Chromium 架构中的 ContentClient / ContentBrowserClient 设计原理全解析

C++中的Thunk技术和它的使用场景 阅读详情

一、前言

在阅读 Chromium 源码时,很多人会对这样一段调用产生疑惑:

bool BrowserMainLoop::AudioServiceOutOfProcess() const { return base::FeatureList::IsEnabled(features::kAudioServiceOutOfProcess) && !GetContentClient()->browser()->OverridesAudioManager(); } 

细心的同学会问:GetContentClient()->browser() 为什么没有进行判空?这段代码是否有潜在的空指针风险?

要回答这个问题,就必须深入理解 Chromium 架构中的 ContentClient / ContentBrowserClient 体系。这是 Chromium 为了解耦 Content 内核框架与上层浏览器产品(如 Chrome、360 浏览器、Edge 等)而设计的一套 嵌入式架构接口。

本文将从以下几个维度系统剖析这一设计:

  1. 架构动机 —— 为什么需要 ContentClient 体系

  2. 类的职责划分 —— ContentClient、ContentBrowserClient、ContentRendererClient 等如何协作

  3. 生命周期管理 —— 为什么调用时不需要判空

  4. Invariant(不变量) —— 保证接口使用安全性的核心机制

  5. 安全性与可扩展性策略 —— 如何确保第三方嵌入不会破坏 Content 内核的稳定性

  6. 典型使用模式与案例分析 —— 以 AudioServiceOutOfProcess 为例

  7. 总结与最佳实践建议


二、架构动机:解耦内核与产品

Chromium 的 Content 模块可以理解为一个“浏览器内核框架”,它提供了渲染、网络、进程管理、IPC、调度等底层能力,但它本身并不是一个浏览器。

不同的浏览器厂商(Google Chrome、Edge、360 浏览器、Samsung Internet 等)都希望在 Content 基础上 添加自定义逻辑:

  • 注入自定义的 UI 交互逻辑

  • 替换默认的音视频管理(AudioManager)

  • 修改进程模型(单进程 / 多进程 / 沙箱策略)

  • 自定义崩溃上报、数据统计、隐私策略

如果 Content 直接硬编码这些逻辑,那么:

  • 可维护性差:Chrome 的定制逻辑和内核耦合,第三方难以移植。

  • 可扩展性差:不同厂商需求冲突,代码膨胀。

为此,Chromium 引入了 Client 接口体系:

  • ContentClient:顶层单例入口,持有 Browser / Renderer / Utility 三类 Client。

  • ContentBrowserClient:浏览器端扩展点。

  • ContentRendererClient:渲染进程扩展点。

  • ContentUtilityClient:Utility 子进程扩展点。

这种设计模式类似 依赖反转原则 (DIP):Content 定义接口,Embedder(上层浏览器)实现接口,内核反向调用。


三、类的职责划分

  1. ContentClient

    • 全局唯一实例

    • 提供 browser() / renderer() / utility() 三个方法获取对应的 Client

    • 内核调用 GetContentClient() 获取当前 embedder 提供的实现

  2. ContentBrowserClient

    • 提供浏览器侧的所有扩展点(核心)

    • 示例接口:

      • CreateBrowserMainParts() → 定制 Browser 主循环组件

      • OverrideWebPreferences() → 修改默认 Web 偏好设置

      • OverridesAudioManager() → 替换音频管理器

      • IsPluginAllowed() → 插件安全策略

  3. ContentRendererClient

    • 渲染器侧定制点,例如:脚本注入、V8 设置、资源加载控制。

  4. ContentUtilityClient

    • 用于 Utility 子进程的扩展逻辑,比如解码器、数据转换。

设计哲学:

  • ContentClient = 总控入口

  • 各子 Client = 按进程维度划分的扩展接口


四、生命周期管理:为什么可以不判空

我们回到开头的疑问:

GetContentClient()->browser()->OverridesAudioManager(); 

为什么 browser() 不需要判空?

原因在于 生命周期保证:

  1. ContentMainRunner::Initialize 阶段,Embedder 会调用:

SetContentClient(embedder_content_client); 

这里的 embedder_content_client 是浏览器实现的全局实例,整个进程生命周期内始终存在。

  1. 在 ContentMain 初始化流程中,ContentBrowserClient 会被提前构建,并绑定到 ContentClient 中:

content_client->set_browser(embedder_browser_client); 
  1. Invariant:

    • 在任何使用 GetContentClient()->browser() 的时机,必然保证已经完成初始化。

    • 如果没有设置,程序就是初始化不完整,整个浏览器无法正常启动。

因此,判空是没有意义的:

  • 如果为 null,那说明架构初始化就失败了,继续运行毫无意义。

  • 不判空,反而能让 bug 立即暴露,而不是隐性进入异常状态。


五、Invariant(不变量)在设计中的作用

Invariant(不变量)是系统设计中的一个重要概念:

  • 指某个条件在系统生命周期中始终成立。

  • 违反 Invariant 意味着系统进入未定义状态。

在 ContentClient 体系中,典型的不变量包括:

  1. GetContentClient() 在任何时候都不为 null。

  2. GetContentClient()->browser() 在 BrowserMainLoop 阶段必然已初始化。

  3. 每个进程只能有一个对应的 Client 实例,不允许多重注册。

这种设计带来的好处:

  • 性能优化:调用处省去了重复的判空开销。

  • 代码简洁:避免到处写防御性代码。

  • 安全性:一旦不变量被破坏,系统快速崩溃,开发者能立即发现问题。


六、安全性与可扩展性策略

Chromium 的安全模型要求 embedder 只能通过 Client 接口扩展,而不能直接修改 Content 内部逻辑。

  1. 接口白名单:

    • 所有可扩展点都通过 ContentBrowserClient 提供。

    • 内核核心逻辑(IPC、调度、沙箱)不对外开放。

  2. 沙箱化设计:

    • 即使 embedder 覆盖了某些策略,仍然运行在沙箱约束下,无法突破安全边界。

  3. 动态特性开关:

    • 与 base::FeatureList 结合,embedder 可以在运行时选择是否启用某些服务(如 AudioServiceOutOfProcess)。

  4. 防御性检查:

    • 内核内部仍然有 CHECK 或 DCHECK 确认 invariant,不依赖外部调用者的防御性代码。


七、典型使用模式与案例分析

回到 AudioServiceOutOfProcess:

bool BrowserMainLoop::AudioServiceOutOfProcess() const { return base::FeatureList::IsEnabled(features::kAudioServiceOutOfProcess) && !GetContentClient()->browser()->OverridesAudioManager(); } 

这里体现了典型的 内核 + embedder 协作模式:

  1. 内核通过 FeatureList 控制是否允许 out-of-process AudioService。

  2. Embedder 通过 ContentBrowserClient::OverridesAudioManager() 声明是否使用自定义 AudioManager。

  3. 两者共同决定最终行为。

如果 embedder 没有覆盖:

  • 使用内核默认 AudioManager,可能运行在独立进程。

如果 embedder 覆盖:

  • 内核必须尊重 embedder 的决策,使用自定义 AudioManager。

这种模式下:

  • Content 保持通用性和独立性。

  • Embedder 保持灵活性和可定制性。


八、总结与最佳实践

通过分析可以得出几个关键结论:

  1. ContentClient / ContentBrowserClient 是 Chromium 插件化架构的基石。

    • 它们解耦了内核框架与产品逻辑。

    • 提供了清晰的扩展边界。

  2. 生命周期与 invariant 保证了调用安全性。

    • GetContentClient()->browser() 不判空是合理的设计选择。

    • 空指针意味着系统初始化失败,应立即暴露。

  3. 安全与可扩展性并存。

    • 内核只暴露白名单接口。

    • Embedder 定制逻辑必须在沙箱和安全策略下运行。

  4. 最佳实践:

    • 在 embedder 中必须确保尽早正确设置 ContentClient。

    • 实现 ContentBrowserClient 时应遵循最小化覆盖原则,只修改必要逻辑。

    • 避免滥用扩展点,保持内核升级兼容性。


九、后记

如果说 Blink、V8 是 Chromium 的“心脏与大脑”,那么 ContentClient 体系就是神经系统。
它让 Chromium 内核成为一个真正可复用、可嵌入的浏览器框架,而不仅仅是为 Chrome 专门打造的引擎。

理解了这一点,我们就能更清晰地看到:

  • 为什么一些调用“不判空”反而是正确的。

  • 为什么 invariant 在架构中比 if 判空更重要。

  • 为什么 Chromium 能支撑多个不同厂商的浏览器产品。

深入解析 Chromium Mojo IPC:跨进程通信原理与源码实战 本文深入解析Chromium浏览器中的Mojo IPC机制,这一高性能跨进程通信框架支撑着现代浏览器多进程架构的运行。文章从Mojo的五个核心概念(MessagePipe、Handle、Interface、Receiver/Remote、Bindings)切入,详细剖析了其数据流、线程模型和异步机制。通过源码分析展示了MessagePipe的底层实现、SimpleWatcher的事件监听机制,以及接口自动生成类的运作原理。最后提供了实战示例和性能优化建议,包括使用SharedBuffer减少拷贝、合理绑定线 阅读详情

相关推荐

深入理解浏览器渲染引擎:底层机制与性能优化实战

本文深入探讨了现代浏览器渲染引擎的工作原理,特别是Blink和V8引擎的协作机制。文章首先概述了浏览器的多进程架构,包括浏览器进程、渲染进程、GPU进程和网络进程的功能。接着,详细解析了渲染引擎的核心步骤,从HTML和CSS的解析到DOM和CSSOM的构建,再到渲染树的形成、布局、绘制和最终的合成阶段。文章还探讨了JavaScript引擎与渲染引擎的交互方式,以及如何通过优化策略减少重排和重绘,提升页面渲染性能。最后,提供了Chromium源码的调试路径和实用建议,帮助开发者更好地理解和优化渲染流程。

John_ToStr的博客 1777

chromium之ContentBrowserClient

chromium中的ContentBrowserClient类

蜗牛想飞翔 1932

实战解析:如何利用MDFEND与Weibo21数据集构建多领域假新闻检测系统

本文实战解析了如何利用MDFEND模型与Weibo21数据集构建多领域假新闻检测系统。文章深入剖析了MDFEND的领域门控与专家混合机制,并提供了从数据预处理、模型理解到训练部署的完整指南,旨在解决通用模型在不同领域新闻检测中的性能衰减问题,提升检测的准确性与适应性。

vulkan6gpu的博客 579

Chrominum的技术架构

Chromium采用多进程架构提升性能与安全性,主要包含浏览器进程(全局控制)、渲染进程(Blink+V8引擎处理页面)、GPU进程(图形加速)、网络进程(独立处理请求)等核心模块。通过Mojo IPC实现跨进程通信,采用沙箱机制隔离风险。其渲染流程从HTML解析到GPU合成显示形成完整管线,结合多线程优化(如主线程、合成线程等)。关键组件包括Blink引擎、V8 JavaScript引擎、Skia绘图库等,实现硬件加速、快速响应等目标。整体架构体现模块化、并行化设计,平衡功能扩展与安全隔离。

编程改变世界。想做一些改变世界的产品。感谢一键三连。 2630

使用Hyper-V使用下载下来的macOS安装引导安装macos

然后关闭工具返回安装界面选择第二个reinstall,选择119g那个硬盘。我的资源上传了:macOS Monterey on Hyper-V.7z。设置固件的启动顺序,引导安装盘放到第一位,mac安装110g放第二。选择disk utility格式化那个110g。下载解压的macos引导挂载到虚拟硬盘。其他设置安全禁用,检查禁用,集成都选中。新建一个虚拟硬盘装mac,110g。

viviliving的专栏 3023

【上海大学计算机组成原理实验报告】六、内存系统实验

学习内存访问机制//理解代码和数据的分区存放原理和技术//手动方式把立即数`33H`写入内存`D1H`单元//手动方式把`D1H`单元的内容读出,再送入`ST`单元//在`CP226`汇编语言程序集成开发环境下编写程序//既然有`ORG`微指令,为什么集成开发环境下载到实验箱的目标程序的第一条执行语句最好存放在0号地址?//上海大学计算机组成原理实验//内存系统实验

HackerKevon的博客 3128

Content模块设计意图

Content 模块概述 “content”模块放在src \content里面,并使用多进程浏览器沙盒模块来呈现页面所需的核心代码。它包括所有的网络平台功能(如HTML5)和GPU加速。它不包括Chrome浏览器的功能,即扩展/自动填充/拼写等。它的目标是,任何嵌入者或者说使用者应该能够用它来开始建立一个浏览器,然后从中挑选Chrome功能。 动机是什么? 由于Ch

不积跬步无以至千里,不积小流无以成江海 1580

kettle从入门到精通 第九十二课 ETL之kettle 使用Kettle的Carte对外发布读写接口

场景:使用kettle实现将查询结果返回给客户端,也就是说kettle暴露查询接口供外围系统调用。前提必须是使用carte服务才可以提供接口供外部系统调用。具体实操方法如下:1、设计转换根据具体需求设计转换,主要用到的步骤有获取变量(从请求参数中获取变量)、json输入(解析入参),json 输出将结果转换为json格式输出到客户端。如下图所示:2、接收参数使用获取变量步骤来接收接口请求参数,变量通过${}方法进行获取,如下图${PARAM}则是获取PARAM参数,将参数赋值为req字段。

zhangjin1222的博客 954

SharePoint 2010 Client Browser 客户端浏览器

SharePoint 2010 Client Browser 客户端浏览器,为了加快开发速度,发现隐藏的列表/项目/文档,发现结构或特定属性。

NetSuite 张老师 3167

015:机载三维激光扫描技术相关知识

机载三维激光扫描系统(LiDAR)是集激光、GPS、INS等尖端技术于一体的三维航空遥感技术,能高效准确获取地表三维信息。该系统由GPS、INS、激光扫描测距系统、成像装置和工作平台组成,各部件协同工作以获取精确的空间定位、运动状态、地表三维坐标及真实色彩信息。通过制定飞行计划、安置GPS基站、激光扫描测量、惯性测量及获取高清数码影像等步骤,机载LiDAR技术能实时获取大范围高精度三维数据。该技术具有扫描速度快、精度高、全数字特征等优势,广泛应用于测绘、工程、环境监测和城市建设等领域,如城市三维模型重建、灾

qq_31762031的博客 1725

【专栏导引贴】100+智能优化算法:从背景原理到代码实战(持续更新)

本专栏系统整理100+种现代优化算法,涵盖粒子群优化(PSO)、遗传算法(GA)、鲸鱼优化(WOA)等经典方法,以及白鲸优化(BWO)、非洲秃鹫优化(AVOA)等新兴算法。每篇详解包含生物启发原理、数学模型、代码实现及工程应用,采用统一编程风格,提供可直接运行的实例代码。专栏持续更新,旨在为研究者提供系统化的智能优化算法参考资料,实现从理论到实践的完整知识链。目前已收录23种算法,包括蚁群系统(ACS)、蝙蝠算法(BA)、天牛须搜索(BAS)等自然启发式优化方法。

qq_42212808的博客 3643

chromium之content_shell源代码分析(二)

http://blog.youkuaiyun.com/chromium_webkit/article/details/8666005 上一篇介绍到了Shell_browser_main.cc的中调用的BrowserMainRunnerImpl的Initialize和Run函数,现在来介绍具体的过程和特点;在BrowserMainRunnerImpl的Initialize中会看到如下代码:

dzhj11的专栏 1844

Pytorch入门教程

Pytorch学习总结:1.张量Tensor 张量是一种特殊的数据结构,与数组和矩阵非常相似。在PyTorch中,我们使用张量对模型的输入和输出以及模型的参数进行编码。 张量类似于NumPy的ndarray,除了张量可以在 GPU 或其他硬件加速器上运行。事实上,张量和NumPy数组通常可以共享相同的底层内存,从而无需复制数据。...

专注大数据与人工智能技术分享,欢迎私信加群互相学习! 4万+

Chromium之Browser进程启动

Chromium Browser进程启动分析

remygo的专栏 4932

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

理解WebKit和Chromium: Content API和CEF3

转载请注明原文地址:http://blog.youkuaiyun.com/milado_nju/article/details/7455373 # Content API及CEF3 ## 概述 相信你一定看过下面这张图(没看过的话去上官网阅读一下“how chromium displays web pages”)。 这是一幅介绍页面如果被渲染和显示的概括性的层次结构图。Render...

weixin_34090562的博客 490

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

贪心算法——最大整数问题详解

贪心算法——最大整数问题详解: [最大整数]设有n个正整数,将它们连接成一排,组成一个最大的多位整数。 例如:n=3时,3个整数13,312,343,连成的最大整数为34331213。 又如:n=4时,4个整数7,13,4,246,连成的最大整数为7424613。 输入:n N个数 输出:连成的多位数 算法分析:此题很容易想到使用贪心法,在考试时有很多

John_ToStr的博客 7773

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

Windows Hook案例分析与技术探索

Hook是Windows中提供的一种用以替换DOS下“中断“的系统机制,中文译为“挂钩”或“钩子”。在对 特定的系统事件进行Hook后,一旦发生已Hook事件,对该事件进行Hook的程序就会收到系统的通知, 这时程序就能在第一时间对该事件做出响应。 钩子实际上是一个处理消息的程序段,通过系统调用,把它挂入系统。每当特定的消息发出,在没有 到达目的程序前,钩子程序就先捕获该消息,亦即钩子函数先得到控制权。这时钩子函数即可以加工处理 (改变)该消息,也可以不作处理而继续传递该消息,还可以强制结束消息的传递。..

John_ToStr的博客 7128

浏览器无痕模式机制解析:它与正常模式究竟有何不同?

在日常浏览网页时,很多用户会注意到浏览器提供了“无痕模式”、“隐私窗口”或“InPrivate模式”等选项。这个功能常常被认为是“匿名上网”,但从技术实现角度来看,无痕模式到底和普通浏览模式有什么不同?它真的能完全不留痕迹吗?又有哪些场景下开发者要注意两种模式的行为差异? 本文将从浏览器实现机制出发,系统地讲解无痕模式的原理、与普通模式的区别、以及它在开发调试中的表现差异和典型使用场景。

John_ToStr的博客 6027

CPU 占用升高 ≠ 卡顿:浏览器硬件加速的真正价值

浏览器开启硬件加速后CPU占用升高但运行更流畅的现象属于正常优化:硬件加速将图形任务从CPU主线程转移到GPU处理,虽然GPU调度会消耗部分CPU资源,但显著减轻了主线程负担,带来更流畅的体验。建议多数设备开启该功能,仅在驱动不兼容或低功耗场景下关闭。通过chrome://gpu可验证加速是否生效,开发者还能追踪关键渲染事件进行深度优化。

John_ToStr的博客 2134

揭秘浏览器中 Cookie 的真正作用与性能影响

本文深入解析了Cookie在Web开发中的真实作用与性能影响。文章首先澄清了"Cookie能加速请求"的常见误解,指出Cookie不仅不会提升性能,反而可能因增加请求体积和降低缓存命中率而拖慢加载速度。通过分析Cookie的定义、核心作用(状态保持、个性化、行为跟踪)和现代浏览器对其的限制(SameSite、分区存储等),作者强调Cookie是状态管理工具而非性能优化手段。文章提供了实用建议:合理设置作用域、避免存储大数据、为静态资源使用独立域名等,并推荐采用现代缓存策略替代过度依赖Co

John_ToStr的博客 1627

关于回调函数跟this指针的一些理解

在C里面,经常需要提供一个函数地址,注册到结构里,然后在程序执行到特定阶段时,回调该函数。创建线程,注册线程运行的主函数就是一个典型的例子。这里以简单的回调实例,说明C++中回调函数为成员函数时有关this指针的问题。由于C++对C的继承关系,C++没有自己的线程封装技术,一般而言我们创建线程时,还是用C的回调函数机制。类似的例子也挺多的。在Java等纯粹的面向对象语言,则不一样,不光有自己的独立

John_ToStr的博客 1543

从 base 出发:深入理解 Chromium 自研基础库的体系结构与设计哲学(兼谈为什么不直接用 STL)

Chromium基础库base解析:浏览器内核的底层基石 作为Chromium内核最底层的公共库,base库为整个浏览器架构提供了跨平台一致性的基础支撑。它通过重新封装智能指针、线程模型、容器等核心组件,解决了标准C++库在大型跨平台项目中面临的性能不可控、行为不一致等问题。base库包含内存管理(RefCounted/WeakPtr)、任务调度(TaskRunner/ThreadPool)、事件循环(MessageLoop)、自研容器(flat_map)等核心模块,采用统一抽象层确保不同操作系统表现一致。

John_ToStr的博客 1523

背包问题——动态规划算法详解与实例

动态规划算法详解与实例 一、基本概念     动态规划过程是:每次决策依赖于当前状态,又随即引起状态的转移。一个决策序列就是在变化的状态中产生出来的,所以,这种多阶段最优化决策解决问题的过程就称为动态规划。 二、基本思想与策略     基本思想与分治法类似,也是将待求解的问题分解为若干个子问题(阶段),按顺序求解子阶段,前一子问题的解,为后一子问题的求解提供了有用的信息

John_ToStr的博客 1512

make_shared、shared_ptr、enable_shared_from_this

make_shared 和 正常的 shared_ptr 构造函数异同!

John_ToStr的博客 1480

Release 与 Debug 的区别及 Release 版本调试指南

摘要: Debug与Release模式在软件开发中存在显著差异:Debug关闭优化、保留调试信息,便于调试但性能差;Release开启优化、去除冗余检查,性能好但调试困难。Release版本易因未初始化变量、越界访问等问题出现Bug,尤其在线程竞争下更明显。调试Release版本需保留符号信息(如.pdb文件),临时禁用优化,利用日志、Dump文件及工具(如VS/WinDbg/gdb)定位问题。开发者应兼顾Debug阶段的严格测试与Release版本的符号保留,以快速修复线上问题。

John_ToStr的博客 1473
上一篇: 深入解析 Chromium ThreadPool、Task Cancellation 与 ScopedClosureRunner:源码机制、任务生命周期与安全管理
下一篇: 从一次Crash分析Chromium浏览器的悬空指针检测机制:raw_ref与BackupRefPtr揭秘
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ปรัชญา แค้วคำมูล

你的鼓励将是我创作的最大动力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值