chromium 常用函数

0, 各种对外的delegate,client:

MouseWheelEventQueue 的 client_ 是 InputRouterImpl 

InputRouterImpl 的 client_ 是 RenderWidgetHostImpl 

RenderWidgetHostImpl 的 delegate_ 是 WebContentsImpl 

WebContentsImpl 的 delegate_ 是 Browser (src\chrome\browser\ui\browser.cc)

1,从ResourceId类获取整型的id值

auto myindex = quad->resource_id().GetUnsafeValue();

默认的页面style:
third_party\blink\renderer\core\html\resources\下

forced_colors.css

html.css

由 third_party\blink\public\blink_resources.grd 引用。最终由 third_party\blink\renderer\core\css\css_default_style_sheets.cc 中用。

2,blink中父类子类来回转换 IsA   To DynamicTo

  • DCHECK(IsA<HTMLVideoElement>(media_element));
  • To<HTMLVideoElement>(media_element) 动态强制转向子类
  • 转向具体类,子类。判断是否成功:
if (auto* anchor = DynamicTo<HTMLAnchorElement>(element))
return anchor->VisitedLinkHash();
//---------------------------------------------------------------------
const WebInputElement input_element = element.DynamicTo<WebInputElement>();
if (input_element.IsNull() && !form_util::IsTextAreaElement(element))
return;
Image* HitTestResult::GetImage(const Node* node) {
if (!node)
return nullptr;
LayoutObject* layout_object = node->GetLayoutObject();
if (layout_object && layout_object->IsImage()) {
auto* image = To<LayoutImage>(layout_object);
if (image->CachedImage() && !image->CachedImage()->ErrorOccurred())
return image->CachedImage()->GetImage();
}
return nullptr;
}

判断是否是form的text输入框:

form_util::IsTextAreaElementOrTextInput

 
void PasswordAutofillAgent::FocusedNodeHasChanged(const blink::WebNode& node) {
  if (!node.IsElementNode()) {
    focus_state_notifier_.FocusedInputChanged(FieldRendererId(),
                                              FocusedFieldType::kUnknown);
    return;
  }
 
  auto element = node.To<WebElement>();
  if (element.IsFormControlElement() &&
      form_util::IsTextAreaElement(element.To<WebFormControlElement>())) {
    FieldRendererId textarea_id(
        element.To<WebFormControlElement>().UniqueRendererFormControlId());
    focus_state_notifier_.FocusedInputChanged(
        textarea_id, FocusedFieldType::kFillableTextArea);
    return;
  }
 
  WebInputElement input_element = element.DynamicTo<WebInputElement>();
  if (input_element.IsNull()) {
    focus_state_notifier_.FocusedInputChanged(
        FieldRendererId(), FocusedFieldType::kUnfillableElement);
    return;
  }
 
  auto focused_field_type = FocusedFieldType::kUnfillableElement;
  if (input_element.IsTextField() && IsElementEditable(input_element)) {
    focused_input_element_ = input_element;
 
    WebString type = input_element.GetAttribute("type");
    if (!type.IsNull() && type == "search")
      focused_field_type = FocusedFieldType::kFillableSearchField;
    else if (input_element.IsPasswordFieldForAutofill())
      focused_field_type = FocusedFieldType::kFillablePasswordField;
    else if (base::Contains(web_input_to_password_info_, input_element))
      focused_field_type = FocusedFieldType::kFillableUsernameField; 确认是个密码框对应的用户名文本框
    else
      focused_field_type = FocusedFieldType::kFillableNonSearchField;
  }
 
  const FieldRendererId input_id(input_element.UniqueRendererFormControlId());
  focus_state_notifier_.FocusedInputChanged(input_id, focused_field_type);
  field_data_manager_->UpdateFieldDataMapWithNullValue(
      input_id, FieldPropertiesFlags::kHadFocus);
}

 

是否url访问会放到历史记录

bool LocalFrame::NavigationShouldReplaceCurrentHistoryEntry

 

3. skia的使用

3.1 api各种示例,可以直接在网页编写调试。

网址:https://fiddle.skia.org/named/  

wasm或者skia层的调试时,可以用skDebug输出来查看日志 别忘了字串加上最后的 换行符,否则看不到输出!!!!

SkDebugf("matrix %c= nearlyEqual\n", matrix == nearlyEqual ? '=' : '!');

类的dump函数会打印出对象信息:

   SkMatrix nearlyEqual;
    nearlyEqual.setAll(0.7071f, -0.7071f, 0,   0.7071f, 0.7071f, 0,   0, 0, 1);
    nearlyEqual.dump();

输出:[ 0.7071 -0.7071 0.0000][ 0.7071 0.7071 0.0000][ 0.0000 0.0000 1.0000]

 

4. log输出:

#include "base/logging.h"

LOG(INFO)<<"HELLO";

DLOG(ERROR) << __func__ << ": {code=" << error->code();

-----------------------------------------------------------

调试输出字串 内核中是ToString , skia的dump
LayerImpl::ToString()
gfx::transform::ToString()
raster_source->GetSize().ToString();

request.GetResourceRequest().Url().GetString().Utf8()

---------------------------------------------------------

拼接string concat splice

方法a:

  std::string dump_name =
        base::StringPrintf("cc/resource_memory/provider_%d/resource_%u",
                           tracing_id_, resource_entry.first.GetUnsafeValue());

对类加个tostring()输出:

std::string SoftwareImageDecodeCacheUtils::CacheKey::ToString() const {
std::ostringstream str;
str << "frame_key[" << frame_key_.ToString() << "]\ntype[";
switch (type_) {
case kOriginal:
str << "Original";
break;
case kSubrectOriginal:
str << "SubrectOriginal";
break;
case kSubrectAndScale:
str << "SubrectAndScale";
break;
}
str << "]\nis_nearest_neightbor[" << is_nearest_neighbor_ << "]\nsrc_rect["
<< src_rect_.ToString() << "]\ntarget_size[" << target_size_.ToString()
<< "]\ntarget_color_params[" << target_color_params_.ToString()
<< "]\nhash[" << hash_ << "]";
return str.str();
}

json: base::Value 对象是一个通用的数据容器,可以包含多种类型的数据,如字典、列表、字符串、整数等。要打印输出 base::Value 对象的内容,可以将其转换为 JSON 字符串,然后打印该字符串。

// Returns parameters associated with the start of a HTTP stream job.
base::Value NetLogHttpStreamJobParams(const NetLogSource& source,
const GURL& original_url,
const GURL& url,
bool expect_spdy,
bool using_quic,
HttpStreamFactory::JobType job_type,
RequestPriority priority) {
base::Value::Dict dict;
if (source.IsValid())
source.AddToEventParameters(dict);
dict.Set("original_url", original_url.DeprecatedGetOriginAsURL().spec());
dict.Set("url", url.DeprecatedGetOriginAsURL().spec());
dict.Set("expect_spdy", expect_spdy);
dict.Set("using_quic", using_quic);
dict.Set("priority", RequestPriorityToString(priority));
dict.Set("type", NetLogHttpStreamJobType(job_type));
return base::Value(std::move(dict));
}
{
std::string json_string;
base::JSONWriter::Write(base::Value xxx, &json_string);
std::cout << json_string << std::endl;
}

5. gfx等库的值想看的话用 ToString

std::cout<<LayerImpl::ToString()
gfx::transform::ToString()
 raster_source->GetSize().ToString();
 

gfx 与 skia的转化:ui/gfx/geometry/skia_conversions.h

SkM44 TransformToSkM44(const Transform& tranform);
Transform SkMatrixToTransform(const SkMatrix& matrix);
SkIRect RectToSkIRect(const Rect& rect);
SkIRectToRect
SizeToSkISize

6. 字符集转换

1
2
3
4
5
6
base::string16 utf16_output;
//对应未知编码,先转成utf16,在转成utf8输出。字符集charset没有设置,就用系统当前默认的。windows为GBK。windows10可以自己把默认字符集改为UTF8。
if (!referrer_charset.empty() &&
          ConvertToUTF16(encoded_word, referrer_charset.c_str(),
                         &utf16_output)) {
        *output = base::UTF16ToUTF8(utf16_output);

 

WebString FilePathToWebString(const base::FilePath& path) {
if (path.empty())
return WebString();
#if BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
return WebString::FromUTF8(path.value());
#else
return WebString::FromUTF16(path.AsUTF16Unsafe());
#endif
}

 

 7. 用net库输出二进制内容

chromium打印二进制函数:
#include "net/base/hex_utils.h"
auto sss = base::StringPiece(info.content_disposition);
LOG(INFO) << net::HexDump(sss);
// Print binary data to stdout as hex.
void print_data(const SkData* data, const char* name) {
if (data) {
SkDebugf("\nxxd -r -p > %s << EOF", name);
size_t s = data->size();
const uint8_t* d = data->bytes();
for (size_t i = 0; i < s; ++i) {
if (i % 40 == 0) { SkDebugf("\n"); }
SkDebugf("%02x", d[i]);
}
SkDebugf("\nEOF\n\n");
}
}

8. 单例示例,需要c++17

复制代码
sk_sp<SkFontMgr> SkFontMgr::RefDefault() {
    static SkOnce once;
    static sk_sp<SkFontMgr> singleton;

    once([]{
        sk_sp<SkFontMgr> fm = gSkFontMgr_DefaultFactory ? gSkFontMgr_DefaultFactory()
                                                        : SkFontMgr::Factory();
        singleton = fm ? std::move(fm) : sk_make_sp<SkEmptyFontMgr>();
    });
    return singleton;
}
复制代码

SkOnce的实现:

查看代码
 /*
* Copyright 2013 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef SkOnce_DEFINED
#define SkOnce_DEFINED
#include "include/private/SkThreadAnnotations.h"
#include <atomic>
#include <utility>
// SkOnce provides call-once guarantees for Skia, much like std::once_flag/std::call_once().
//
// There should be no particularly error-prone gotcha use cases when using SkOnce.
// It works correctly as a class member, a local, a global, a function-scoped static, whatever.
class SkOnce {
public:
constexpr SkOnce() = default;
template <typename Fn, typename... Args>
void operator()(Fn&& fn, Args&&... args) {
auto state = fState.load(std::memory_order_acquire);
if (state == Done) {
return;
}
// If it looks like no one has started calling fn(), try to claim that job.
if (state == NotStarted && fState.compare_exchange_strong(state, Claimed,
std::memory_order_relaxed,
std::memory_order_relaxed)) {
// Great! We'll run fn() then notify the other threads by releasing Done into fState.
fn(std::forward<Args>(args)...);
return fState.store(Done, std::memory_order_release);
}
// Some other thread is calling fn().
// We'll just spin here acquiring until it releases Done into fState.
SK_POTENTIALLY_BLOCKING_REGION_BEGIN;
while (fState.load(std::memory_order_acquire) != Done) { /*spin*/ }
SK_POTENTIALLY_BLOCKING_REGION_END;
}
private:
enum State : uint8_t { NotStarted, Claimed, Done};
std::atomic<uint8_t> fState{NotStarted};
};
#endif // SkOnce_DEFINED

 

// static
BookmarkModel* BookmarkModelFactory::GetForBrowserContext(
content::BrowserContext* context) {
return static_cast<BookmarkModel*>(
GetInstance()->GetServiceForBrowserContext(context, true));
}
// static
BookmarkModelFactory* BookmarkModelFactory::GetInstance() {
return base::Singleton<BookmarkModelFactory>::get();
}

内核中单例

封在文件内部
namespace {
using RenderFrameDevToolsMap =
std::map<FrameTreeNode*, RenderFrameDevToolsAgentHost*>;
base::LazyInstance<RenderFrameDevToolsMap>::Leaky g_agent_host_instances =
LAZY_INSTANCE_INITIALIZER;
RenderFrameDevToolsAgentHost* FindAgentHost(FrameTreeNode* frame_tree_node) {
if (!g_agent_host_instances.IsCreated())
return nullptr;
auto it = g_agent_host_instances.Get().find(frame_tree_node);
return it == g_agent_host_instances.Get().end() ? nullptr : it->second;
}
}
使用:
g_agent_host_instances.Get().erase(frame_tree_node_);
g_agent_host_instances.Get()[frame_tree_node] = this;

 

9. wtf::String 到 std::string 转换

static inline std::string fromWTFString(const WTF::String& wtf_string) {

  //if (wtf_string.IsNull())
  // return std::string();
  return std::string(wtf_string.Utf8().data(), wtf_string.Utf8().size());
}

E:\dev\chromium104\src\third_party\blink\renderer\platform\wtf\text\wtf_string.h 使用 header

std::string to wtf::String: explicit String(const std::string& s) : String(s.c_str(), s.length()) {}

10,计算耗时:

10.1 用 base 库:

base::TimeDelta g_fallback_delay = base::Seconds(3);

//#include "base/time/time.h"
#include "base/timer/elapsed_timer.h"
//metrics_time_delta_ = base::TimeDelta();
base::ElapsedTimer timer;
Decode(index);
LOG(INFO)<<timer.Elapsed();
//输出: 20.8946 s
//metrics_time_delta_ += timer.Elapsed();

 

const base::TimeTicks start = base::TimeTicks::Now();
。。。。。。。。
const int64_t elapsed_us = (base::TimeTicks::Now() - start).InMicroseconds();
printf("%" PRIu64 " MB/s,\telapsed = %" PRIu64 " source=%d dest=%d\n",
static_cast<uint64_t>(elapsed_us == 0 ? 0 : num_bytes / elapsed_us),
static_cast<uint64_t>(elapsed_us), GetBitmapSize(&source),
GetBitmapSize(&dest));
return true;
}

 

// For other cases, only update at |kFlushDelay| intervals. This
// throttles how frequently we update |m_image| and how frequently we
// inform the clients which causes an invalidation of this image. In other
// words, we only invalidate this image every |kFlushDelay| seconds
// while loading.
if (!is_pending_flushing_) {
scoped_refptr<base::SingleThreadTaskRunner> task_runner =
Loader()->GetLoadingTaskRunner();
base::TimeTicks now = base::TimeTicks::Now();
if (last_flush_time_.is_null())
last_flush_time_ = now;
DCHECK_LE(last_flush_time_, now);
base::TimeDelta flush_delay =
std::max(base::TimeDelta(), last_flush_time_ - now + kFlushDelay);
task_runner->PostDelayedTask(FROM_HERE,
WTF::Bind(&ImageResource::FlushImageIfNeeded,
WrapWeakPersistent(this)),
flush_delay);
is_pending_flushing_ = true;
#include "base/time/time.h"
base::Time start_time_ = base::Time::Now();
base::TimeDelta delta = base::TimeTicks::Now() - start_time_;
 base::Time plusone = base::Time::Now() + base::Days(1);
  double real_wall_time = base::Time::Now().ToDoubleT();
  double delta.InSecondsF()
 int64_t  (base::Time::Now() - base::Time::UnixEpoch()).InMilliseconds();
base::TimeDelta duration
int InDays() const;
int InDaysFloored() const;
constexpr int InHours() const;
constexpr int InMinutes() const;
constexpr double InSecondsF() const;
constexpr int64_t InSeconds() const;
double InMillisecondsF() const;
int64_t InMilliseconds() const;
int64_t InMillisecondsRoundedUp() const;
constexpr int64_t InMicroseconds() const { return delta_; }
double InMicrosecondsF() const;
constexpr int64_t InNanoseconds() const;
base::TimeDelta kSlowDuration = base::Seconds(1);
base::TimeDelta kFastDuration = base::Milliseconds(1);

 

class SimpleURLLoaderImpl{
// The timer that triggers a timeout when a request takes too long.
base::OneShotTimer timeout_timer_;
// How long |timeout_timer_| should wait before timing out a request. A value
// of zero means do not set a timeout.
base::TimeDelta timeout_duration_ = base::TimeDelta();
}
bool NavigationURLLoaderImpl::SetNavigationTimeout(base::TimeDelta timeout) {
// If the timer has already been started, don't change it.
if (timeout_timer_.IsRunning())
return false;
// Fail the navigation with error code ERR_TIMED_OUT if the timer triggers
// before the navigation commits.
timeout_timer_.Start(
FROM_HERE, timeout,
base::BindOnce(&NavigationURLLoaderImpl::NotifyRequestFailed,
base::Unretained(this),
network::URLLoaderCompletionStatus(net::ERR_TIMED_OUT)));
return true;
}

10.2 std库计时

#include <chrono>
class Timer {
public:
void start() {
fStart = std::chrono::high_resolution_clock::now();
}
void stop() {
auto end = std::chrono::high_resolution_clock::now();
fElapsedSeconds += end - fStart;
}
double elapsedSeconds() {
return fElapsedSeconds.count();
}
private:
decltype(std::chrono::high_resolution_clock::now()) fStart;
std::chrono::duration<double> fElapsedSeconds{0.0};
};
Timer drawTime;
drawTime.start();
。。。。。。。。。
drawTime.stop();
fprintf(stderr, "%s use GPU %s elapsed time %8.6f s\n", gSkpName.c_str(),
gUseGpu ? "true" : "false", drawTime.elapsedSeconds());

12 文件输出

内核base

#include "base/files/file_util.h"
WriteFile(base::FilePath(FILE_PATH_LITERAL("c:/myskp/select-auto.html")), data, size);

文件权限判断

// Determine if the certain permissions have been granted to a file.
bool HasPermissionsForFile(const base::FilePath& file, int permissions) {
#if BUILDFLAG(IS_ANDROID)
if (file.IsContentUri())
return HasPermissionsForContentUri(file, permissions);
#endif
if (!permissions || file.empty() || !file.IsAbsolute())
return false;
base::FilePath current_path = file.StripTrailingSeparators();
base::FilePath last_path;
int skip = 0;
while (current_path != last_path) {
base::FilePath base_name = current_path.BaseName();
if (base_name.value() == base::FilePath::kParentDirectory) {
++skip;
} else if (skip > 0) {
if (base_name.value() != base::FilePath::kCurrentDirectory)
--skip;
} else {
FileMap::const_iterator it = file_permissions_.find(current_path);
if (it != file_permissions_.end())
return (it->second & permissions) == permissions;
}
last_path = current_path;
current_path = current_path.DirName();
}
return false;
}

12.1 root layer 到 picture 到 bitmap 到 png 编码到 文件输出

const cc::Layer* InspectorLayerTreeAgent::RootLayer() {
return inspected_frames_->Root()->View()->RootCcLayer();
}
获取picture后输出:
{
auto* rLayer = MainFrame().View()->RootCcLayer();
for (const auto& layer : rLayer->children()) {
if (!layer->update_rect().IsEmpty()) {
if (!layer->draws_content())
return ;
//获取 picture。因为sk_sp<const SkPicture>转成 sk_sp<SkPicture>,传入 MakeFromPicture。
//所以复制了一份picture
sk_sp<const SkPicture> picture0 =layer->GetPicture();
sk_sp<SkData> skp = picture0->serialize();
sk_sp<SkPicture> picture = SkPicture::MakeFromData(skp.get());
//sk_sp<SkPicture>& picture = const_cast<sk_sp<SkPicture>&>(picture0);
auto image=SkImage::MakeFromPicture(picture, SkISize::Make(layer->bounds().width(), layer->bounds().height()),
nullptr, nullptr, SkImage::BitDepth::kU8,
SkColorSpace::MakeSRGB());
sk_sp<SkData> data = image->encodeToData();//SkEncodedImageFormat::kPNG, 100
base::FilePath path =
base::FilePath(FILE_PATH_LITERAL("c:\\mySkp"));
char filename[128];
base::Time::Exploded exploded;
base::Time::NowFromSystemTime().LocalExplode(&exploded);
std::snprintf(filename, 128, "%03d-id-%03d-%02d%02d%02d%03d-%s.png",
111, 111, exploded.hour, exploded.minute,
exploded.second, exploded.millisecond, "");
path = path.Append(base::FilePath::FromUTF8Unsafe(filename));
SkFILEWStream out(path.AsUTF8Unsafe().c_str());
out.write(data->data(), data->size());
}
}
}

skia库 文件,关于创建目录

文件,关于创建目录,文件访问权限的:

放着base那块儿
#include "base/files/file_path.h"
#include "base/files/file_util.h"
放到那块儿
#include "third_party/skia/include/core/SkStream.h"
base::FilePath dirpath = base::FilePath::FromUTF8Unsafe("e:\\tmp");
if (!base::CreateDirectory(dirpath) || !base::PathIsWritable(dirpath)) {
std::string msg("Path is not writable: ");
msg.append(dirpath.MaybeAsASCII());
LOG(ERROR) << "=======LayerTreeHost SKPicture create path error or no privillege:"<<msg;
return;
}
std::string filename =
"layer_" + base::NumberToString(layer_id_++) + ".skp";
std::string filepath = dirpath.AppendASCII(filename).MaybeAsASCII();
DCHECK(!filepath.empty());
SkFILEWStream file(filepath.c_str());
DCHECK(file.isValid());
auto data = picture->serialize();
file.write(data->data(), data->size());
file.fsync();
static void PrintDocumentTofile(v8::Isolate* isolate,
const std::string& filename,
sk_sp<SkDocument> (*make_doc)(SkWStream*),
RenderFrameImpl* render_frame) {
GpuBenchmarkingContext context(render_frame);
base::FilePath path = base::FilePath::FromUTF8Unsafe(filename);
if (!base::PathIsWritable(path.DirName())) {
}
SkFILEWStream wStream(path.MaybeAsASCII().c_str());
void write_png(const std::string& filename, const SkBitmap& bitmap) {
auto data = SkEncodeBitmap(bitmap, SkEncodedImageFormat::kPNG, 0);
SkFILEWStream w{filename.c_str()};
w.write(data->data(), data->size());
w.fsync();
}

编码图片,直接

#include "include/core/SkBitmap.h"
#include "include/core/SkEncodedImageFormat.h"
#include "include/core/SkImageEncoder.h"
#include "base/files/file.h"
#include "base/files/file_path.h"
base::FilePath path =
base::FilePath(FILE_PATH_LITERAL("c:\\mySkp"));
char filename[128];
base::Time::Exploded exploded;
base::Time::NowFromSystemTime().LocalExplode(&exploded);
std::snprintf(filename, 128, "%03d-id-%03d-%02d%02d%02d%03d-%s",
111, 111, exploded.hour, exploded.minute,
exploded.second, exploded.millisecond, ".png" );
path = path.Append(base::FilePath::FromUTF8Unsafe(filename));
SkFILEWStream out(path.AsUTF8Unsafe().c_str());
SkEncodeImage(&out, fRasterized, SkEncodedImageFormat::kPNG, 100);

目录处理:

scoped_refptr<SharedBuffer> ReadFile(const char* dir, const char* file_name) {
StringBuilder file_path;
if (strncmp(dir, "web_tests/", 10) == 0) {
file_path.Append(test::BlinkWebTestsDir());
file_path.Append('/');
file_path.Append(dir + 10);
} else {
file_path.Append(test::BlinkRootDir());
file_path.Append('/');
file_path.Append(dir);
}
file_path.Append('/');
file_path.Append(file_name);
return test::ReadFromFile(file_path.ToString());
}
scoped_refptr<SharedBuffer> ReadFromFile(const String& path) {
base::FilePath file_path = blink::WebStringToFilePath(path);
std::string buffer;
base::ReadFileToString(file_path, &buffer);
return SharedBuffer::Create(buffer.data(), buffer.size());
}

 

base::ScopedTempDir temp_dir_;
ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
history_dir_ = temp_dir_.GetPath().AppendASCII("VisitedLinkTest");
ASSERT_TRUE(base::CreateDirectory(history_dir_));
visited_file_ = history_dir_.Append(FILE_PATH_LITERAL("VisitedLinks"));
ASSERT_TRUE(temp_dir_.Delete());

 

jpg解码只允许按照8的倍数缩放????????      

    // we'll only allow downscaling an image if both dimensions fit a
          // whole number of MCUs or if decoding to the original size would
          // cause us to exceed memory limits. The latter case is detected by
          // checking the |max_numerator| returned by DesiredScaleNumerator():
          // this method will return either |g_scale_denominator| if decoding to
          // the original size won't exceed the memory limit (see
          // |max_decoded_bytes_| in ImageDecoder) or something less than
          // |g_scale_denominator| otherwise to ensure the image is downscaled.

// static
unsigned JPEGImageDecoder::DesiredScaleNumerator(wtf_size_t max_decoded_bytes,
wtf_size_t original_bytes,
unsigned scale_denominator) {
if (original_bytes <= max_decoded_bytes)
return scale_denominator;//无限大,就返回 8
// Downsample according to the maximum decoded size.
return static_cast<unsigned>(floor(sqrt(
// MSVC needs explicit parameter type for sqrt().
static_cast<float>(max_decoded_bytes) / original_bytes *
scale_denominator * scale_denominator)));
// max_decoded_bytes/original_bytes * 8 * 8
}

13. 全局变量

13.1

static bool g_should_use_external_popup_menus = false;
void WebView::SetUseExternalPopupMenus(bool use_external_popup_menus) {
g_should_use_external_popup_menus = use_external_popup_menus;
}
bool WebViewImpl::UseExternalPopupMenus() {
return g_should_use_external_popup_menus;
}

13.2

#include "base/no_destructor.h"
// static
Clipboard::ClipboardMap* Clipboard::ClipboardMapPtr() {
static base::NoDestructor<ClipboardMap> clipboard_map;
return clipboard_map.get();
}

使用:

// static
void Clipboard::SetClipboardForCurrentThread(
    std::unique_ptr<Clipboard> platform_clipboard) {
  base::AutoLock lock(ClipboardMapLock());
  base::PlatformThreadId id = Clipboard::GetAndValidateThreadID();

 ClipboardMap* clipboard_map = ClipboardMapPtr();
  // This shouldn't happen. The clipboard should not already exist.
  DCHECK(!base::Contains(*clipboard_map, id));
  clipboard_map->insert({id, std::move(platform_clipboard)});
}

14, 文件保存。从blink调用了skia的库.

PaintRecord to SkPicture

#include <string>
#include "base/files/file_path.h"
#include "third_party/skia/include/core/SkStream.h"
#include "third_party/skia/include/core/SkPicture.h"
{
sk_sp<cc::PaintRecord> record = canvas2d_bridge_->getLastRecord();
cc::PlaybackParams::CustomDataRasterCallback callback;
auto bound = SkRect::MakeXYWH(0, 0, 800, 800);
sk_sp<const SkPicture> picture =
cc::ToSkPicture(record, bound, nullptr, callback);
if (picture) {
sk_sp<SkData> data = picture->serialize();
base::FilePath path =
base::FilePath(FILE_PATH_LITERAL("D:\\mySkp"));
char filename[128];
base::Time::Exploded exploded;
base::Time::NowFromSystemTime().LocalExplode(&exploded);
std::snprintf(filename, 128, "%03d-id-%03d-%02d%02d%02d%03d-%s",
111, 111, exploded.hour, exploded.minute,
exploded.second, exploded.millisecond, "");
path = path.Append(base::FilePath::FromUTF8Unsafe(filename));
SkFILEWStream out(path.AsUTF8Unsafe().c_str());
out.write(data->data(), data->size());
}
}

SkPicture to Skimage

SkImage::MakeFromPicture(picture, SkISize::Make(width, height),
                                  nullptr, nullptr, SkImage::BitDepth::kU8,
                                  SkColorSpace::MakeSRGB());

或者 

SkPicture保存成图片png

查看代码
 Vector<uint8_t> PictureSnapshot::Replay(unsigned from_step,
unsigned to_step,
double scale) const {
const SkIRect bounds = picture_->cullRect().roundOut();
int width = ceil(scale * bounds.width());
int height = ceil(scale * bounds.height());
// TODO(fmalita): convert this to SkSurface/SkImage, drop the intermediate
// SkBitmap.
SkBitmap bitmap;
bitmap.allocPixels(SkImageInfo::MakeN32Premul(width, height));
bitmap.eraseARGB(0, 0, 0, 0);
{
ReplayingCanvas canvas(bitmap, from_step, to_step);
// Disable LCD text preemptively, because the picture opacity is unknown.
// The canonical API involves SkSurface props, but since we're not
// SkSurface-based at this point (see TODO above) we (ab)use saveLayer for
// this purpose.
SkAutoCanvasRestore auto_restore(&canvas, false);
canvas.saveLayer(nullptr, nullptr);
canvas.scale(scale, scale);
canvas.ResetStepCount();
picture_->playback(&canvas, &canvas);
}
Vector<uint8_t> encoded_image;
SkPixmap src;
bool peekResult = bitmap.peekPixels(&src);
DCHECK(peekResult);
SkPngEncoder::Options options;
options.fFilterFlags = SkPngEncoder::FilterFlag::kSub;
options.fZLibLevel = 3;
if (!ImageEncoder::Encode(&encoded_image, src, options))
return Vector<uint8_t>();
return encoded_image;
}

picture编码成image,用 surface

auto pic = SkPicture::MakeFromData(picData, &procs);
auto cullRect = pic->cullRect();
auto r = cullRect.round();
auto s = SkSurface::MakeRasterN32Premul(r.width(), r.height());
auto c = s->getCanvas();
c->drawPicture(pic);
auto i = s->makeImageSnapshot();
auto data = i->encodeToData();
SkFILEWStream f(outFilename.c_str());
f.write(data->data(), data->size());

 

15. 生成 DisplayItemList,layer_tree_host

cc\trees\layer_tree_host_unittest_capture_content.cc

scoped_refptr<DisplayItemList> PaintContentsToDisplayList() override {
auto display_list = base::MakeRefCounted<DisplayItemList>();
for (auto& holder : holders_) {
display_list->StartPaint();
display_list->push<DrawTextBlobOp>(
SkTextBlob::MakeFromString(holder.text().data(), SkFont()),
static_cast<float>(holder.rect().x()),
static_cast<float>(holder.rect().y()), holder.node_id(),
PaintFlags());
display_list->EndPaintOfUnpaired(holder.rect());
}
display_list->Finalize();
return display_list;
}
void SetupRootPictureLayer(const gfx::Size& size) {
scoped_refptr<Layer> root = Layer::Create();
root->SetBounds(size);
client_.set_bounds(size);
root_picture_layer_ = FakePictureLayer::Create(&client_);
root_picture_layer_->SetBounds(size);
root->AddChild(root_picture_layer_);
layer_tree_host()->SetRootLayer(root);
layer_tree_host()->SetVisualDeviceViewportIntersectionRect(
gfx::Rect(device_bounds_));
}

16. id序列生成大法

#include "base/atomic_sequence_num.h"
namespace cc {
namespace {
// Tracing ID sequence for use in CacheEntry.
base::AtomicSequenceNumber g_next_tracing_id_;
uint64_t tracing_id_= g_next_tracing_id_.GetNext();

17, 互相访问,互转 localFrame 换到 WebLocalFrameImpl 到 WebLocalFrameClient 

  1.  LocalFrame 到 WebLocalFrameImpl

LocalFrame* frame

WebLocalFrameImpl* web_frame = WebLocalFrameImpl::FromFrame(frame);

web_frame->Client()->ScriptedPrint();

  1. WebLocalFrameImpl利用client取到 RenderFrameImpl.

core中 LocalDOMWindow 这些html往外接口是 chromeClient,调研client的具体方法时走到实现类chromeClientImpl

  1. webLocalFrame 到 localFrame

WebLocalFrameImpl* popup = FocusedWebLocalFrameInWidget();
LocalFrame* popup_frame= popup->GetFrame();

blink::WebLocalFrame* web_frame;

web_frame->GetDocument().GetFrame() 返回就是WebDocument,LocalFrame* XXX 返回是 WebLocalFrame

WebLocalFrame* WebDocument::GetFrame() const {
return WebLocalFrameImpl::FromFrame(ConstUnwrap<Document>()->GetFrame());
}

Document* LocalFrame::GetDocument()通过lf可以返回Document* 

这个强制将dom_window转成localDomWindow:

const LocalDOMWindow* LocalFrame::DomWindow() const {
  return To<LocalDOMWindow>(dom_window_.Get());
}

WebDocument存放着Document: WebDocument(Document* elem)

获取缩放: WebLocalFrameImpl::FromFrame(local_frame_)
                                    ->LocalRootFrameWidget()
                                    ->GetEmulatorScale();

LocalFrame 到 page 到 chromeClient

LocalFrame* frame = owner_element.GetDocument().GetFrame();
const Page* page = frame ? frame->GetPage() : nullptr;
dpr = page->GetChromeClient().GetScreenInfo(*frame).device_scale_factor;

 

  LocalFrame* opener_frame = client->OwnerElement().GetDocument().GetFrame();
  

owner_document.GetStyleEngine().GetFontSelector()  src\third_party\blink\renderer\core\css\style_engine.h

  1. RenderFrameHost 访问 localframe

// Holder of Mojo connection with the LocalFrame in Blink.
  mojo::AssociatedRemote<blink::mojom::LocalFrame> local_frame_;

  1. RenderFrameImpl 和RenderFrameHostImpl 互调

RenderFrameImpl.GetFrameHost()获取、 RenderFrameHostImpl.GetMojomFrameInRenderer()

  1.   renderFrame 到 localFrame

      blink::WebLocalFrame* myWebLocalFrame = render_frame()->GetWebFrame();
      blink::WebLocalFrameImpl* frame_impl = DynamicTo<blink::WebLocalFrameImpl>(myWebLocalFrame);
      blink::LayoutView* layout_view = frame_impl->GetFrame()->GetDocument()->GetLayoutView();
      blink::LocalFrame* local_frame = frame_impl ->GetFrame();

localFrame获取远程的localFrameHost

local_frame->GetLocalFrameHostRemote()

RenderFrameHostImpl 获取 RenderWidgetHostImpl

RenderFrameHostImpl* host_;

RenderWidgetHostImpl* widget_host =
    host_ ? host_->GetRenderWidgetHost() : nullptr;

从 RenderWidgetHostView 获取到 RenderWidgetHostImpl

RenderWidgetHostImpl* render_widget_host_impl = widget_host_view->host();

Frame to localFrame

auto* local_frame = DynamicTo(frame);
 Frame* FocusController::FocusedOrMainFrame() const {
if (LocalFrame* frame = FocusedFrame())
return frame;
// TODO(dcheng, alexmos): https://crbug.com/820786: This is a temporary hack
// to ensure that we return a LocalFrame, even when the mainFrame is remote.
// FocusController needs to be refactored to deal with RemoteFrames
// cross-process focus transfers.
for (Frame* frame = &page_->MainFrame()->Tree().Top(); frame;
frame = frame->Tree().TraverseNext()) {
auto* local_frame = DynamicTo<LocalFrame>(frame);
if (local_frame)
return frame;
}
return page_->MainFrame();
}

【渲染】webkit内部的WebView,page, WebFrame, Frame, document, frameView, RenderView, RenderFrame,Widget的层级关联关系以及构建过程 - Bigben - 博客园 (cnblogs.com) 乱

17.1 从子renderWidget找到父

std::unique_ptr<RenderWidgetHostIterator>
RenderWidgetHostImpl::GetEmbeddedRenderWidgetHosts() {
  // This iterates over all RenderWidgetHosts and returns those whose Views
  // are children of this host's View.
  auto hosts = std::make_unique<RenderWidgetHostIteratorImpl>();
  auto* parent_view = static_cast<RenderWidgetHostViewBase*>(GetView());
  for (auto& it : g_routing_id_widget_map.Get()) {
    RenderWidgetHost* widget = it.second;

    auto* view = static_cast<RenderWidgetHostViewBase*>(widget->GetView());
    if (view && view->IsRenderWidgetHostViewChildFrame() &&
        static_cast<RenderWidgetHostViewChildFrame*>(view)->GetParentView() ==
            parent_view) {
      hosts->Add(widget);
    }
  }

  return std::move(hosts);
}

18. sharedbuffer 转成 string

scoped_refptr<SharedBuffer> data = SharedBuffer::Create();
popup_client_->WriteDocument(data.get());
const char* myhtml = data->Data();
size_t size = data->size();
LOG(ERROR)<<" ==== MYHTML SELECTION:"<< base::StringPiece(myhtml, size);
// Create a StringPiece to view the UTF-8 encoded data without copying
base::StringPiece stringPiece(bufferData, bufferSize);
// Convert the StringPiece to a std::string by copying the data
std::string utf8String(stringPiece.data(), stringPiece.size());
// Now utf8String contains the UTF-8 encoded data as a C++ string
// Print out the UTF-8 string
std::cout << "UTF-8 String: " << utf8String << std::endl;

19, css 序列化成字符串

ItemIterationContext context(*owner_element_->GetComputedStyle(), data.get());
context.SerializeBaseStyle();
参考
 void InternalPopupMenu::Update(bool force_update) {
scoped_refptr<SharedBuffer> data = SharedBuffer::Create();
PagePopupClient::AddString("window.updateData = {\n", data.get());
PagePopupClient::AddString("type: \"update\",\n", data.get());
ItemIterationContext context(*owner_element_->GetComputedStyle(), data.get());
context.SerializeBaseStyle();
PagePopupClient::AddString("children: [", data.get());
const HeapVector<Member<HTMLElement>>& items = owner_element_->GetListItems();
for (; context.list_index_ < items.size(); ++context.list_index_) {
Element& child = *items[context.list_index_];
if (!IsA<HTMLOptGroupElement>(child.parentNode()))
context.FinishGroupIfNecessary();
if (auto* option = DynamicTo<HTMLOptionElement>(child))
AddOption(context, *option);
else if (auto* optgroup = DynamicTo<HTMLOptGroupElement>(child))
AddOptGroup(context, *optgroup);
else if (auto* hr = DynamicTo<HTMLHRElement>(child))
AddSeparator(context, *hr);
}
context.FinishGroupIfNecessary();
PagePopupClient::AddString("],\n", data.get());
gfx::Rect anchor_rect_in_screen = chrome_client_->LocalRootToScreenDIPs(
owner_element_->VisibleBoundsInLocalRoot(),
OwnerElement().GetDocument().View());
AddProperty("anchorRectInScreen", anchor_rect_in_screen, data.get());
PagePopupClient::AddString("}\n", data.get());
#if 1//zhibin:select log html update
LOG(INFO) << " SLECT UPDATE HTML: " << String::FromUTF8(data->Data(), data->size());
#endif
popup_->PostMessageToPopup(String::FromUTF8(data->Data(), data->size()));
}
  • 字符串从base的wtf库到平台比如win字符 | 从文件后缀取到mime type
#include "net/base/platform_mime_util.h"
#include <string>
#include "base/strings/utf_string_conversions.h"
#include "base/win/registry.h"
#include <windows.h>
namespace net {
bool MimeUtil::GetMimeTypeFromFile(const base::FilePath& file_path,
string* result) const {
base::FilePath::StringType file_name_str = file_path.Extension();
if (file_name_str.empty())
return false;
return GetMimeTypeFromExtension(file_name_str.substr(1), result);
}
bool PlatformMimeUtil::GetPlatformMimeTypeFromExtension(
const base::FilePath::StringType& ext, std::string* result) const {
// check windows registry for file extension's mime type (registry key
// names are not case-sensitive).
base::FilePath::StringType value, key = FILE_PATH_LITERAL(".") + ext;
base::win::RegKey(HKEY_CLASSES_ROOT, key.c_str(), KEY_READ)
.ReadValue(L"Content Type", &value);
if (!value.empty()) {
*result = base::WideToUTF8(value);
return true;
}
return false;
}

20. 内核访问content层

 blink::WebLocalFrame* frame = render_frame()->GetWebFrame();

访问到glue层。再通过

21.  调用进入 WaitForLoad,即正常执行被终止,延迟2s后,重新返回入口函数。流程再来一遍,不满足条件时继续延迟。直到通过,继续执行。

WaitForLoad
void PrintRenderFrameHelper::RequestPrintPreview(PrintPreviewRequestType type,
bool already_notified_frame) {
。。。。。。
is_scripted_preview_delayed_ = true;
if (is_loading_) {
// Wait for DidStopLoading, for two reasons:
// * To give the document time to finish loading any pending resources
/// that are desired for printing.
// * Plugins may not know the correct|is_modifiable| value until they
// are fully loaded, which occurs when DidStopLoading() is called.
// Defer showing the preview until then.
WaitForLoad(type);
return;//这里正常执行被返回。后续由WaitForLoad里面的postTask任务继续接力。
}
void PrintRenderFrameHelper::WaitForLoad(PrintPreviewRequestType type) {
static constexpr base::TimeDelta kLoadEventTimeout = base::Seconds(2);
on_stop_loading_closure_ =
base::BindOnce(&PrintRenderFrameHelper::RequestPrintPreview,
weak_ptr_factory_.GetWeakPtr(), type, true);
base::SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask(
FROM_HERE,
base::BindOnce(&PrintRenderFrameHelper::DidFinishLoadForPrinting,
weak_ptr_factory_.GetWeakPtr()),
kLoadEventTimeout);//接力棒是2秒后执行 DidFinishLoadForPrinting
}
void PrintRenderFrameHelper::DidFinishLoad/DidFinishLoadForPrinting() {
is_loading_ = false;
if (!on_stop_loading_closure_.is_null())
std::move(on_stop_loading_closure_).Run();
}
运行后Run :RequestPrintPreview 再次执行WaitForLoad流程。即不停循环,直到满足。

 

创建共享内存

// Initializes the shared memory structure. The salt should already be filled
// in so that it can be written to the shared memory
bool VisitedLinkWriter::CreateURLTable(int32_t num_entries) {
base::MappedReadOnlyRegion table_memory;
if (CreateApartURLTable(num_entries, salt_, &table_memory)) {
mapped_table_memory_ = std::move(table_memory);
hash_table_ = GetHashTableFromMapping(mapped_table_memory_.mapping);
table_length_ = num_entries;
used_items_ = 0;
return true;
}
// static
bool VisitedLinkWriter::CreateApartURLTable(
int32_t num_entries,
const uint8_t salt[LINK_SALT_LENGTH],
base::MappedReadOnlyRegion* memory) {
DCHECK(salt);
DCHECK(memory);
// The table is the size of the table followed by the entries.
uint32_t alloc_size =
num_entries * sizeof(Fingerprint) + sizeof(SharedHeader);
// Create the shared memory object.
*memory = base::ReadOnlySharedMemoryRegion::Create(alloc_size);
if (!memory->IsValid())
return false;
memset(memory->mapping.memory(), 0, alloc_size);
// Save the header for other processes to read.
SharedHeader* header = static_cast<SharedHeader*>(memory->mapping.memory());
header->length = num_entries;
memcpy(header->salt, salt, LINK_SALT_LENGTH);
return true;
}

22, 异步调用时回调中对象可能被释放问题

1,利用 独立指针;另外由于 chromium的base::BindOnce里面的lamba只能是最简的,才能转成函数指针,不能带状态。可以利用bind参数,将状态传入。

base::RunLoo run_loop; //没用。只是说明按引用传递,在模板中。   

std::unique_ptr<network::SimpleURLLoader> url_loader =
        network::SimpleURLLoader::Create(std::move(resource_request),
            kRBELogUploaderTrafficAnnotation);

    url_loader->AttachStringForUpload(upload_data, MimeContentType());

    auto* loader_ptr = url_loader.get();
    loader_ptr->DownloadToString(url_load_factory.get(),
        base::BindOnce(
            [](base::RunLoop& run_loop, std::unique_ptr<network::SimpleURLLoader> loader, std::unique_ptr<std::string> response_body) {
                LOG(ERROR) << "==== OnSimpleLoaderComplete";
                //run_loop.Quit();  // 退出消息循环
                loader.reset(); },

         std::ref(run_loop), std::move(url_loader) //前面的lamda绑定了两个参数. 第三个参数response_body,才是真正的DownloadToString第二个参数的原型:base::OnceCallback<void(std::unique_ptr<std::string> response_body)>
                ), //bindOnce结束,将一个三个参数的lamda,绑定了前两个参数。只剩一个参数。与DownloadToString要求的第二个参数一致。
        10000);

23, 启动参数

"out\Default\chrome1.exe" --remote-debugging-address=0.0.0.0 --remote-debugging-port=9222 --process-per-site --disable-dev-shm-usage --flag-switches-begin --enable-features=BackForwardCache:TimeToLiveInBackForwardCacheInSeconds/3000/should_ignore_blocklists/true,BackForwardCacheNoTimeEviction,CacheControlNoStoreEnterBackForwardCache:level/store-and-evict,TimeoutTcpConnectAttempt:TimeoutTcpConnectAttemptMin/3s/TimeoutTcpConnectAttemptMax/5s/TimeoutTcpConnectAttemptRTTMultiplier/2,NetworkQualityEstimator --flag-switches-end --no-sandbox --log-net-log="C:\mylogs\netlog.json" --enable-logging --v=0 --vmodule=*surface_layer*=2,render_frame_host_impl=0 --flag-switches-begin --flag-switches-end http://192.168.11.1/

24 element bound 到 screen space 转换

void ChromeAutofillClient::ShowAutofillPopup(
    const autofill::AutofillClient::PopupOpenArgs& open_args,
    base::WeakPtr<AutofillPopupDelegate> delegate) {
 
 
  // Convert element_bounds to be in screen space.
  gfx::Rect client_area = web_contents()->GetContainerBounds();
  gfx::RectF element_bounds_in_screen_space =
      open_args.element_bounds + client_area.OffsetFromOrigin();

 

25,win上的实用,hook:src\base\win\win_util.cc

void __cdecl ForceCrashOnSigAbort(int) {
  *((volatile int*)nullptr) = 0x1337;
}

26 实现第一个类的打印,obj.ToString 类似

利用json
 
#include "components/password_manager/core/browser/password_form.h"
#include <algorithm>
#include <ostream>
#include <sstream>
#include <string>
#include "base/json/json_writer.h"
#include "base/json/values_util.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/values.h"
std::string ToString(PasswordForm::Scheme scheme) {
switch (scheme) {
case PasswordForm::Scheme::kHtml:
return "HTML";
case PasswordForm::Scheme::kBasic:
return "Basic";
case PasswordForm::Scheme::kDigest:
return "Digest";
case PasswordForm::Scheme::kOther:
return "Other";
case PasswordForm::Scheme::kUsernameOnly:
return "UsernameOnly";
}
NOTREACHED();
return std::string();
}
// Utility function that creates a std::string from an object supporting the
// ostream operator<<.
template <typename T>
std::string ToString(const T& obj) {
std::ostringstream ostream;
ostream << obj;
return ostream.str();
}
std::u16string ValueElementVectorToString(
const ValueElementVector& value_element_pairs) {
std::vector<std::u16string> pairs(value_element_pairs.size());
std::transform(
value_element_pairs.begin(), value_element_pairs.end(), pairs.begin(),
[](const ValueElementPair& p) { return p.first + u"+" + p.second; });
return base::JoinString(pairs, u", ");
}
// Serializes a PasswordForm to a JSON object. Used only for logging in tests.
void PasswordFormToJSON(const PasswordForm& form, base::Value::Dict& target) {
target.Set("primary_key",
form.primary_key.has_value()
? base::NumberToString(form.primary_key.value().value())
: "PRIMARY KEY IS MISSING");
target.Set("scheme", ToString(form.scheme));
target.Set("signon_realm", form.signon_realm);
target.Set("is_public_suffix_match", form.is_public_suffix_match);
target.Set("is_affiliation_based_match", form.is_affiliation_based_match);
target.Set("url", form.url.possibly_invalid_spec());
target.Set("action", form.action.possibly_invalid_spec());
target.Set("submit_element", form.submit_element);
target.Set("username_element", form.username_element);
target.Set("username_element_renderer_id",
base::NumberToString(form.username_element_renderer_id.value()));
target.Set("username_value", form.username_value);
target.Set("password_element", form.password_element);
target.Set("password_value", form.password_value);
target.Set("password_element_renderer_id",
base::NumberToString(form.password_element_renderer_id.value()));
............
target.Set("in_store", ToString(form.in_store));
std::vector<std::string> hashes;
hashes.reserve(form.moving_blocked_for_list.size());
for (const auto& gaia_id_hash : form.moving_blocked_for_list) {
hashes.push_back(gaia_id_hash.ToBase64());
}
target.Set("moving_blocked_for_list", base::JoinString(hashes, ", "));
base::Value::List password_issues;
password_issues.reserve(form.password_issues.size());
for (const auto& issue : form.password_issues) {
base::Value::Dict issue_value;
issue_value.Set("insecurity_type", ToString(issue.first));
issue_value.Set("create_time", base::TimeToValue(issue.second.create_time));
issue_value.Set("is_muted", static_cast<bool>(issue.second.is_muted));
password_issues.Append(std::move(issue_value));
}
target.Set("password_issues ", std::move(password_issues));
base::Value::List password_notes;
password_notes.reserve(form.notes.size());
for (const auto& note : form.notes) {
base::Value::Dict note_dict;
note_dict.Set("unique_display_name", note.unique_display_name);
note_dict.Set("value", note.value);
note_dict.Set("date_created", base::TimeToValue(note.date_created));
note_dict.Set("hide_by_default", note.hide_by_default);
password_notes.Append(std::move(note_dict));
}
target.Set("notes", std::move(password_notes));
target.Set("previously_associated_sync_account_email",
form.previously_associated_sync_account_email);
}
absl::optional<std::u16string> PasswordForm::GetNoteWithEmptyUniqueDisplayName()
const {
const auto& note_itr = base::ranges::find_if(
notes, &std::u16string::empty, &PasswordNote::unique_display_name);
return note_itr != notes.end() ? absl::make_optional(note_itr->value)
: absl::nullopt;
}
std::ostream& operator<<(std::ostream& os, PasswordForm::Scheme scheme) {
return os << ToString(scheme);利用 ToString模板转换
}
中间实现方法
std::ostream& operator<<(std::ostream& os, const PasswordForm& form) {
base::Value::Dict form_json;
PasswordFormToJSON(form, form_json);生成一个具体的对象json。
// Serialize the default PasswordForm, and remove values from the result that
// are equal to this to make the results more concise.
base::Value::Dict default_form_json;
PasswordFormToJSON(PasswordForm(), default_form_json);生成一个默认值的对象json。
for (auto it_default_key_values : default_form_json) {
比较具体对象值与默认值的差异,捡出具体对象的东西。为了观看显著的,不看默认值,没意义。
const base::Value* actual_value =
form_json.Find(it_default_key_values.first);
if (actual_value != nullptr &&
it_default_key_values.second == *actual_value) {
form_json.Remove(it_default_key_values.first);
}
}
std::string form_as_string;
base::JSONWriter::WriteWithOptions(
form_json, base::JSONWriter::OPTIONS_PRETTY_PRINT, &form_as_string);
base::TrimWhitespaceASCII(form_as_string, base::TRIM_ALL, &form_as_string);
return os << "PasswordForm(" << form_as_string << ")";
}
打印一个 PasswordForm 时调用
std::ostream& operator<<(std::ostream& os, PasswordForm* form) {
return os << "&" << *form;
}
头文件里面:
// For testing.
#if defined(UNIT_TEST)
std::ostream& operator<<(std::ostream& os, PasswordForm::Scheme scheme);
std::ostream& operator<<(std::ostream& os, const PasswordForm& form);
std::ostream& operator<<(std::ostream& os, PasswordForm* form);
#endif
posted @   Bigben  阅读(649)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
历史上的今天:
2020-07-23 chromium 处理 addEventListener 事件
2019-07-23 Elasticsearch SQL用法详解
2019-07-23 MySQL重要知识点
2018-07-23 如何刻录cd音乐
点击右上角即可分享
微信分享提示