通过在前面几篇文章,我们粗略梳理了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)下断点:
关键注册的流程已经标红,不再冗述。
收到调用
还是通过断点阅读代码:
JS的调用先到CookieStore::getAll,然后通过成员函数DoRead,把请求转发给HeapMojoRemotenetwork::mojom::blink::RestrictedCookieManager backend_;
这时,我们要跑去网络进程打断点。根据network::mojom::blink::RestrictedCookieManager的类所在源文件是restricted_cookie_manager.mojom-blink.cc,那么我们去不带blink的源文件对应的函数打上断点:
核心代码如上图。
总结
从js调用 window.cookieStore.getAll()
,总体流程如下图: