chromium 常用函数

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. 字符集转换

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

blink::WebLocalFrame* web_frame;

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

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();
  WebLocalFrameImpl* web_opener_frame =
      WebLocalFrameImpl::FromFrame(opener_frame);

 

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()

 

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

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 @ 2021-07-23 11:07  Bigben  阅读(572)  评论(0编辑  收藏  举报