Chromium源码阅读:Mojo实战:从浏览器JS API 到blink实现

2024-06-13 17:28

本文主要是介绍Chromium源码阅读:Mojo实战:从浏览器JS API 到blink实现,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!


通过在前面几篇文章,我们粗略梳理了Mojo这套跨进程通信的设计思路和IDL细节。

实际上,Mojo不止是跨进程通信框架,而是跨语言的模块通信自动化系统。

在浏览器暴露的JS API,也是需要通过Mojo这个系统进行桥接,最终到blink的实现上。

我们打开浏览器控制台,可以输入 window.cookieStore.getAll()获取当前默认Frame的Cookie。如下图:
在这里插入图片描述
我们以这个API为例子,看看Chromium是如何实现这个功能的。

确定Web标准

这个API是基于Cookie Store API的Web标准的一部分。首先,我们可以先查找相关的Web标准文档以了解这个API的定义和预期行为。
Cookie Store API

CookieStore 接口的 getAll() 方法返回与传递给它的 name 或 options 匹配的所有 cookie 列表。 语法

getAll(name) getAll(options)

参数 name 可选 记录 cookie 名称的字符串。 或 options 可选 一个包括以下属性的对象:name:记录 cookie
名称的字符串。 url:记录 cookie URL 的字符串。 返回值:一个兑现为与指定的 name 或 options 对象匹配的
cookie 信息对象的 Promise。

寻找IDL 定义

这个API是Web标准API,肯定在blink/render下面,简单一搜就找到了:
在这里插入图片描述
打开third_party\blink\renderer\modules\cookie_store\cookie_store.idl:

// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.// https://wicg.github.io/cookie-store/explainer.html[Exposed=(ServiceWorker,Window),SecureContext
] interface CookieStore : EventTarget {// https://wicg.github.io/cookie-store/explainer.html#the-query-api[CallWith=ScriptState, Measure, RaisesException] Promise<CookieListItem?> get(USVString name);[CallWith=ScriptState, Measure, RaisesException] Promise<CookieListItem?> get(optional CookieStoreGetOptions options = {});[CallWith=ScriptState, Measure, RaisesException] Promise<CookieList> getAll(USVString name);[CallWith=ScriptState, Measure, RaisesException] Promise<CookieList> getAll(optional CookieStoreGetOptions options = {});// https://wicg.github.io/cookie-store/explainer.html#the-modifications-api[CallWith=ScriptState, Measure, RaisesException] Promise<undefined> set(USVString name, USVString value);[CallWith=ScriptState, Measure, RaisesException] Promise<undefined> set(CookieInit cookieInit);[CallWith=ScriptState, ImplementedAs=Delete, Measure, RaisesException]Promise<undefined> delete(USVString name);[CallWith=ScriptState, ImplementedAs=Delete, Measure, RaisesException]Promise<undefined> delete(CookieStoreDeleteOptions options);// https://wicg.github.io/cookie-store/explainer.html#the-change-events-api[Exposed=Window] attribute EventHandler onchange;
};

这个idl不复杂,我们针对其内容,简单做了注释标注,方便读者理解:

// Cookie Store API的解释器文档链接
// https://wicg.github.io/cookie-store/explainer.html// 定义一个名为CookieStore的接口,它在ServiceWorker和Window环境中被暴露,
[Exposed=(ServiceWorker,Window), // 暴露给ServiceWorker和WindowSecureContext // 并且仅在安全上下文中可用(如HTTPS)
] interface CookieStore : EventTarget {// 查询API部分的解释器文档链接// https://wicg.github.io/cookie-store/explainer.html#the-query-api  // ...[CallWith=ScriptState, Measure, RaisesException] Promise<CookieList> getAll(USVString name);// 使用选项获取所有匹配的cookies。返回一个包含cookie信息数组的Promise对象。[CallWith=ScriptState, Measure, RaisesException] Promise<CookieList> getAll(optional CookieStoreGetOptions options = {});// ...
};

[CallWith=ScriptState]表示函数在调用时需要当前的脚本状态,[Measure]表示该函数调用会被性能监控工具记录,而[RaisesException]则表明该函数可能会抛出异常。Promise表示该方法操作成功后会返回一个解决为undefined的Promise对象,Promise<CookieListItem?>表示返回的Promise对象会解决为CookieListItem类型或者null。ImplementedAs属性用于指定C++实现中对应的方法名。

查看对应生成的文件:

所有的mojom生成的文件都会放到out\xxx\gen目录下。根据源文件路径,很容易找到对应的生成文件:
在这里插入图片描述
看起来有点多,别紧张,我们依次进行解释:

C++ 绑定和实现文件

  • cookie_store.mojom.h

    • 这是CookieStore接口的主要C++绑定头文件,包含接口定义和相关类型。
  • cookie_store.mojom.cc

    • 这个文件包含CookieStore接口的主要C++实现,通常与.mojom.h头文件相关联。
  • cookie_store.mojom-blink.h

    • 这是为Blink层生成的CookieStore接口的C++绑定头文件。
  • cookie_store.mojom-blink.cc

    • 包含Blink层CookieStore接口的C++实现,与cookie_store.mojom-blink.h头文件相关联。

内部共享定义和辅助文件

  • cookie_store.mojom-shared-internal.h
    • 这个文件包含CookieStore接口的内部共享定义,例如序列化和反序列化逻辑。
  • cookie_store.mojom-shared.cc
    • 包含CookieStore接口共享代码的C++实现,如序列化和反序列化帮助函数。
  • cookie_store.mojom-params-data.h
    • 包含与CookieStore中的方法参数相关的结构体和序列化/反序列化逻辑。
  • cookie_store.mojom-shared-message-ids.h
    • 包含CookieStore接口中所有消息的唯一标识符(ID),用于IPC通信。

导入和前向声明文件

  • cookie_store.mojom-import-headers.h
    • 这个文件导入所有必需的头文件,用于非Blink代码。
  • cookie_store.mojom-forward.h
    • 这个文件包含公共接口的前向声明,用于非Blink代码。
  • cookie_store.mojom-blink-import-headers.h
    • 这个文件负责引入所有与CookieStore接口相关的头文件,用于Blink层。
  • cookie_store.mojom-blink-forward.h
    • 这个文件包含CookieStore接口的前向声明,专用于Blink代码。

特性控制文件

  • cookie_store.mojom-features.h
    • 这个文件定义了与CookieStore接口相关的特性宏,这些宏可以在编译时启用或禁用接口的特定部分。

JavaScript 绑定文件

  • cookie_store.mojom.js
    • 这个文件包含CookieStore接口的JavaScript绑定,允许在Web页面中通过JavaScript调用接口。
  • cookie_store.mojom.m.js
    • 这是CookieStore的ES模块JavaScript绑定文件,它允许在支持JavaScript模块的环境中使用CookieStore接口。
      cookie_store.mojom-module
    • 这个是直接生成了JavaScript的二进制

v8 虚拟机绑定文件(在另一个目录:src\out\x64_debug\gen\third_party\blink\renderer\bindings\modules\v8\)

  • v8_cookie_store.cc&v8_cookie_store.h
    • 这个文件用于将mojo的C++实现注册到V8虚拟机中

大部分生成文件都比较小,主要关注cookie_store.mojom.h、cookie_store-blink.mojom.h、cookie_store.mojom.js即可。之所以生成这么多个文件,是为了分离关注点。分离关注点是软件工程中的一个核心概念,它可以增强代码的可读性、可维护性和可重用性。通过生成不同的文件来处理不同的需求,开发者可以更容易地管理和维护代码。

阅读生成的源文件

cookie_store.mojom.js

// third_party/blink/public/mojom/cookie_store/cookie_store.mojom.js is auto generated by mojom_bindings_generator.py, do not edit
'use strict';(function() {var mojomId = 'third_party/blink/public/mojom/cookie_store/cookie_store.mojom';if (mojo.internal.isMojomLoaded(mojomId)) {console.warn('The following mojom is loaded multiple times: ' + mojomId);return;}// 省略...var CookieStore = {name: 'blink.mojom.CookieStore',kVersion: 0,ptrClass: CookieStorePtr,proxyClass: CookieStoreProxy,stubClass: CookieStoreStub,validateRequest: validateCookieStoreRequest,validateResponse: validateCookieStoreResponse,};CookieStoreStub.prototype.validator = validateCookieStoreRequest;CookieStoreProxy.prototype.validator = validateCookieStoreResponse;exports.CookieChangeSubscription = CookieChangeSubscription;exports.CookieStore = CookieStore;exports.CookieStorePtr = CookieStorePtr;exports.CookieStoreAssociatedPtr = CookieStoreAssociatedPtr;
})();

上面代码导出了proxyClass、studClass等:

var CookieStore = {
name: ‘blink.mojom.CookieStore’,
kVersion: 0,
ptrClass: CookieStorePtr,
proxyClass: CookieStoreProxy,
stubClass: CookieStoreStub,
validateRequest: validateCookieStoreRequest,
validateResponse: validateCookieStoreResponse,
};

上面代码省略了大量的定义,实际上在这个源文件搜索CookieStore,可以看到一些定义的细节。
在这里插入图片描述
CookieStore通过prototype完成了定义,不在展开。

接下来,看render这边如何收到来自JS的调用

注册到JS 虚拟机

看C++侧代码,打断点是最直接的做法:
找到getAll的注册函数,如下图,下断点(chromium的JS注册是按需注册的,也就是如果在JS里没访问这个对象,那么这里的V8CookieStore::InstallUnconditionalProperties是不会调用的):
在这里插入图片描述
V8怎么知道在需要CookieStore的时候跑过来找V8CookieStore::InstallUnconditionalProperties注册这个对象呢?只需要搜索这个方法,找到注册定义:
在这里插入图片描述
这个定义的对象由V8CookieStore::GetWrapperTypeInfo返回。搜索这个函数的调用,就能找到注册的入口:
在这里插入图片描述
继续往下找这个函数的注册入口:
在这里插入图片描述
终于可以下断点了,在上面截图的地方所在函数( V8Window::Impl::InstallContextDependentProperties)下断点:
在这里插入图片描述
关键注册的流程已经标红,不再冗述。

content.dll!RenderFrameImpl::CommitNavigationWithParams
blink_core.dll!WebLocalFrameImpl::CommitNavigation
blink_modules.dll!V8ContextSnapshotImpl::InstallContextIndependentProps
blink_platform.dll!V8PerContextData::ConstructorForTypeSlowCase
blink_core.dll!V8Window::InstallContextDependentProperties
blink_modules.dll!V8Window::Impl::InstallContextDependentProperties

收到调用

还是通过断点阅读代码:
在这里插入图片描述
JS的调用先到CookieStore::getAll,然后通过成员函数DoRead,把请求转发给HeapMojoRemotenetwork::mojom::blink::RestrictedCookieManager backend_;
在这里插入图片描述
这时,我们要跑去网络进程打断点。根据network::mojom::blink::RestrictedCookieManager的类所在源文件是restricted_cookie_manager.mojom-blink.cc,那么我们去不带blink的源文件对应的函数打上断点:
在这里插入图片描述
核心代码如上图。

总结

从js调用 window.cookieStore.getAll(),总体流程如下图:

编译成
cookie_store.idl
v8_cookie_store.h/cc cookie_store.mojom-blink.h/cc cookie_store.mojom.h/cc等

跨进程`调用
浏览器加载完毕
给V8引起初始化window对象,注册了V8CookieStore的Warper信息
当JS访问V8CookieStore的时候,通过注册信息, 生成CookieStore的JS对象使其可以被调用和访问
JS在访问CookieStore.getAll的时候,转调到对应C++的方法
在getAll方法里,解包JS的参数,转调到实际的网络进程的RestrictedCookieManager去
在getAll方法里,解包JS的参数,转调到实际的网络进程的RestrictedCookieManager去

这篇关于Chromium源码阅读:Mojo实战:从浏览器JS API 到blink实现的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1057947

相关文章

网页解析 lxml 库--实战

lxml库使用流程 lxml 是 Python 的第三方解析库,完全使用 Python 语言编写,它对 XPath表达式提供了良好的支 持,因此能够了高效地解析 HTML/XML 文档。本节讲解如何通过 lxml 库解析 HTML 文档。 pip install lxml lxm| 库提供了一个 etree 模块,该模块专门用来解析 HTML/XML 文档,下面来介绍一下 lxml 库

JS常用组件收集

收集了一些平时遇到的前端比较优秀的组件,方便以后开发的时候查找!!! 函数工具: Lodash 页面固定: stickUp、jQuery.Pin 轮播: unslider、swiper 开关: switch 复选框: icheck 气泡: grumble 隐藏元素: Headroom

hdu1043(八数码问题,广搜 + hash(实现状态压缩) )

利用康拓展开将一个排列映射成一个自然数,然后就变成了普通的广搜题。 #include<iostream>#include<algorithm>#include<string>#include<stack>#include<queue>#include<map>#include<stdio.h>#include<stdlib.h>#include<ctype.h>#inclu

性能分析之MySQL索引实战案例

文章目录 一、前言二、准备三、MySQL索引优化四、MySQL 索引知识回顾五、总结 一、前言 在上一讲性能工具之 JProfiler 简单登录案例分析实战中已经发现SQL没有建立索引问题,本文将一起从代码层去分析为什么没有建立索引? 开源ERP项目地址:https://gitee.com/jishenghua/JSH_ERP 二、准备 打开IDEA找到登录请求资源路径位置

JAVA智听未来一站式有声阅读平台听书系统小程序源码

智听未来,一站式有声阅读平台听书系统 🌟&nbsp;开篇:遇见未来,从“智听”开始 在这个快节奏的时代,你是否渴望在忙碌的间隙,找到一片属于自己的宁静角落?是否梦想着能随时随地,沉浸在知识的海洋,或是故事的奇幻世界里?今天,就让我带你一起探索“智听未来”——这一站式有声阅读平台听书系统,它正悄悄改变着我们的阅读方式,让未来触手可及! 📚&nbsp;第一站:海量资源,应有尽有 走进“智听

【C++】_list常用方法解析及模拟实现

相信自己的力量,只要对自己始终保持信心,尽自己最大努力去完成任何事,就算事情最终结果是失败了,努力了也不留遗憾。💓💓💓 目录   ✨说在前面 🍋知识点一:什么是list? •🌰1.list的定义 •🌰2.list的基本特性 •🌰3.常用接口介绍 🍋知识点二:list常用接口 •🌰1.默认成员函数 🔥构造函数(⭐) 🔥析构函数 •🌰2.list对象

【Prometheus】PromQL向量匹配实现不同标签的向量数据进行运算

✨✨ 欢迎大家来到景天科技苑✨✨ 🎈🎈 养成好习惯,先赞后看哦~🎈🎈 🏆 作者简介:景天科技苑 🏆《头衔》:大厂架构师,华为云开发者社区专家博主,阿里云开发者社区专家博主,CSDN全栈领域优质创作者,掘金优秀博主,51CTO博客专家等。 🏆《博客》:Python全栈,前后端开发,小程序开发,人工智能,js逆向,App逆向,网络系统安全,数据分析,Django,fastapi

让树莓派智能语音助手实现定时提醒功能

最初的时候是想直接在rasa 的chatbot上实现,因为rasa本身是带有remindschedule模块的。不过经过一番折腾后,忽然发现,chatbot上实现的定时,语音助手不一定会有响应。因为,我目前语音助手的代码设置了长时间无应答会结束对话,这样一来,chatbot定时提醒的触发就不会被语音助手获悉。那怎么让语音助手也具有定时提醒功能呢? 我最后选择的方法是用threading.Time

Android实现任意版本设置默认的锁屏壁纸和桌面壁纸(两张壁纸可不一致)

客户有些需求需要设置默认壁纸和锁屏壁纸  在默认情况下 这两个壁纸是相同的  如果需要默认的锁屏壁纸和桌面壁纸不一样 需要额外修改 Android13实现 替换默认桌面壁纸: 将图片文件替换frameworks/base/core/res/res/drawable-nodpi/default_wallpaper.*  (注意不能是bmp格式) 替换默认锁屏壁纸: 将图片资源放入vendo

在JS中的设计模式的单例模式、策略模式、代理模式、原型模式浅讲

1. 单例模式(Singleton Pattern) 确保一个类只有一个实例,并提供一个全局访问点。 示例代码: class Singleton {constructor() {if (Singleton.instance) {return Singleton.instance;}Singleton.instance = this;this.data = [];}addData(value)