从 NPAPI、PPAPI 到 Extension,再到 IE 兼容模式:浏览器内核架构是如何演进的(结合 Chromium 理解)

好的,这是一份基于你提供的框架,进行了深度扩充和润色的技术演进文档。我保留了你的原意和核心结构,同时补充了技术细节、历史背景和逻辑衔接,使其更像一篇正式的技术博客或内部培训材料。


浏览器能力演进史:从 NPAPI 到 Mojo,从插件到平台

做浏览器内核开发时,经常会看到这些名字:NPAPI、PPAPI、Extension、content::、Mojo、MSHTML、IE Mode。

刚接触时很容易混淆:NPAPI 和 PPAPI 到底是什么关系?为什么浏览器后来不再推荐插件?现代浏览器为什么转向 Extension?为什么还有厂商保留 NPAPI?为什么还有浏览器保留 IE 内核?Chromium 里的 content:: 又是什么?

本文把这些概念串成一条完整的演进链,揭示浏览器如何从一个"网页加载器"演进成一个"完整的应用运行平台"。

一、浏览器最早为什么需要插件

最初,浏览器的能力非常有限。它本质上是一个声明式文档查看器,只能处理:

  • HTML:描述文档结构
  • CSS:定义视觉样式
  • JS:提供基本的页面交互

然而,用户的需求远比"查看文档"复杂得多。随着互联网的商业化,涌现出大量超出浏览器原生能力的需求:

  • 富媒体播放:视频、音频、流媒体
  • 文档处理:PDF 阅读、Office 文档预览
  • 高级交互:3D 在线游戏、复杂的文件上传与下载管理
  • 安全与硬件交互:网银 U 盾、USB Key、摄像头、麦克风、数字签名

浏览器自身的标准制定和功能实现速度,远远跟不上这些需求的爆发。为了让网页能够承载这些"重型"应用,浏览器必须开放一个"能力后门",允许第三方原生代码在浏览器中运行。这就是插件诞生的背景。

其核心架构非常简单直接:

复制代码
网页 (HTML/JS)
  ↓ 调用
插件 (NPAPI/PPAPI)
  ↓ 直接调用
操作系统 (Windows/macOS/Linux)

这个时代最早也是最成功的标准,就是 NPAPI

二、NPAPI:浏览器与 DLL 直接通信的"野蛮时代"

NPAPI,全称 Netscape Plugin Application Programming Interface ,始于 Netscape 浏览器。它的设计哲学简单而粗暴:将浏览器进程变成一个原生代码的宿主

架构模型

复制代码
chrome.exe (浏览器进程)
 ├─ Browser (界面)
 ├─ Renderer (网页渲染)
 └─ plugin.dll (插件,作为动态库直接加载进进程空间)

插件本质上就是一个操作系统级别的动态链接库(Windows 上是 .dll,Linux 上是 .so,macOS 上是 .plugin)。

生命周期与调用链

  1. 页面嵌入 :网页通过 <object><embed> 标签声明插件内容。
  2. 加载 :浏览器解析到标签,通过 LoadLibrary()dlopen() 将插件 DLL 加载到自己的内存空间。
  3. 初始化 :调用 NP_Initialize() 交换浏览器和插件的函数指针表,然后调用 NPP_New() 创建插件实例。
  4. 运行与交互 :事件循环触发 NPP_HandleEvent(),脚本调用触发 NPObject 的调用。
  5. 销毁 :页面关闭时调用 NPP_Destroy(),最终卸载 DLL。

完整的调用链是:

复制代码
HTML <object>/<embed>
  ↓
PluginDocument (浏览器内部的插件文档节点)
  ↓
WebPlugin (浏览器对插件的封装层)
  ↓
NPAPI (C接口层)
  ↓
DLL (插件原生代码)

NPAPI 的"超能力"

因为插件和浏览器共享同一个进程空间,插件代码拥有与浏览器本身完全同等的权限。它可以做任何事:

  • CreateFile() / ReadFile():随意读写用户磁盘
  • OpenProcess() / ReadProcessMemory():操作其他进程
  • EnumWindows():枚举系统所有窗口
  • send() / recv():发起任意网络连接
  • 加载其他 DLL,Hook 系统 API

这种能力模型虽然极其强大,但也埋下了灾难的种子。

NPAPI 的致命问题

这种"无边界"的架构导致了严重且系统性的问题。假设一个插件里写了 Sleep(30000),整个调用链会如何?

复制代码
插件执行 Sleep(30000)
  ↓ 阻塞
消息循环卡死
  ↓
浏览器UI线程阻塞
  ↓
整个浏览器窗口失去响应,无法操作、无法滚动、无法关闭

这只是一个缩影,典型问题如下表所示:

问题 结果 原因
插件崩溃 整个浏览器崩溃 插件和浏览器在同一进程,一个内存越界(野指针)直接破坏整个进程。
死锁/死循环 UI 卡死、无响应 插件占用了浏览器的主线程(UI线程),导致事件循环无法处理用户输入。
内存泄漏 浏览器内存持续飙高,最终崩溃 插件内部忘记释放内存,且该内存属于浏览器进程的堆。
严重安全风险 用户系统被完全控制 插件可以执行任意系统调用、安装恶意软件、窃取文件,浏览器沙箱对其完全无效。
跨平台困难 一个插件只能用于特定OS DLL 是二进制文件,内含 x86/x64 机器码和特定OS的系统调用,必须为每个平台单独编译。

随着浏览器在性能和稳定性上的要求越来越高,这种"插件亡、浏览器亡"的共生关系变得不可接受。浏览器厂商在维护崩溃报告时,发现绝大多数崩溃都指向第三方插件。变革势在必行。

三、PPAPI:插件进程隔离与沙箱化时代

Google 在接手 Chromium 项目后,深刻认识到多进程架构是浏览器的未来。他们提出了一项革命性原则:插件不能再和浏览器共进程 。这个原则催生了 PPAPI (Pepper Plugin API)

核心理念 :将插件从浏览器主进程中剥离,放入一个独立、低权限的沙箱进程 。浏览器和插件之间通过高效的 IPC (进程间通信) 机制进行交互。

架构模型

复制代码
Browser Process (浏览器主进程)
  ↓ 发现 Pepper 插件
  ↓ 启动
Plugin Process (独立的插件进程)
  ↓ 加载并运行
pepflashplayer.dll (Pepper 插件)

调用链

复制代码
网页 JS/HTML
  ↓
Renderer Process (渲染进程,处理普通网页内容)
  ↓
Pepper Host (在渲染进程或浏览器进程中,封装PPAPI的C++接口)
  ↓ IPC (通过 Mojo 或 Chromium IPC 通道)
Plugin Process (插件进程)
  ↓
PPAPI 实现 (处理 IPC 调用,执行沙箱允许的操作)

PPAPI 的能力边界

PPAPI 不再提供"无限制的系统调用"。相反,它提供了一系列封装的、受控的接口,所有能力必须通过IPC向浏览器申请和代理执行。

插件的能力从:

  • CreateFile()send()

变成了:

  • PPB_URLLoader:发起网络请求(由浏览器网络栈代理)
  • PPB_FileIO:受限的文件读写(只能访问沙箱临时目录或用户指定的文件)
  • PPB_Graphics2D / PPB_Graphics3D:使用 GPU 加速的图形渲染(通过 CommandBuffer 提交给 GPU 进程)
  • PPB_Audio:播放音频(由浏览器音频栈代理)
  • PPB_Instance:管理插件实例的生命周期

PPAPI 带来的进步与代价

优点:

  1. 稳定性隔离:插件崩溃,仅其自身的 Plugin Process 终止,浏览器主程序和其标签页毫发无伤。用户可以刷新页面重载插件。
  2. 安全沙箱:插件进程运行在高度受限的环境中,无法直接访问文件系统、注册表或其他进程。所有敏感操作必须经过浏览器审核和代理。
  3. GPU 硬件加速 :通过 Graphics3D API,Pepper Flash 可以高效使用 OpenGL/DirectX 进行视频解码和渲染,性能远超 NPAPI。
  4. 真正的多进程:浏览器架构变得更加健壮和模块化。

缺点:

  1. IPC 性能开销:每次绘图调用、文件读写都需要跨进程通信,对延迟敏感的应用(如游戏)有一定影响。
  2. 生命周期与状态同步极其复杂:浏览器崩溃了,插件进程怎么办?插件进程无响应,浏览器如何判断和重启?多个标签页共享一个插件进程时如何处理?这些边界情况使得实现异常困难。
  3. API 设计耦合度高:PPAPI 的 C++ API 与 Chromium 内部机制(如 Compositor、CommandBuffer)绑定较深,版本兼容性差。

其最著名、也几乎是唯一的典型应用,就是 Pepper Flash。Google 与 Adobe 合作,将 Flash Player 移植到 PPAPI 之上,使其能在 Chrome 的安全沙箱中运行。

四、为什么 PPAPI 最终也消失了

PPAPI 的消失,并非因为它是一个失败的技术,恰恰相反,它在过渡期完美地完成了历史使命。它退役的根本原因,是浏览器自身变得足够强大,吞噬了插件的核心应用场景

这场"功能吞噬"的演进路径非常清晰:

  • 视频播放Flash VideoHTML5 <video> 标签。浏览器原生支持 H.264/VP9 编解码。
  • 矢量图形与动画Flash SWFCSS3 Animations / SVG
  • 2D/3D 游戏Stage3DWebGL / WebGL 2.0。浏览器直接封装 OpenGL ES,提供原生级 GPU 渲染。
  • 高性能计算C++ 插件计算WebAssembly (WASM)。允许在浏览器中安全地运行接近原生速度的二进制代码。
  • PDF 阅读Adobe Reader 插件内建 PDF 引擎 (Chrome/Edge 用 PDFium)。
  • 实时通信RTMP via FlashWebRTC。浏览器原生支持点对点音视频和数据通信。

整个能力替代路线图:

复制代码
NPAPI 插件
  ↓ 隔离 + 沙箱
PPAPI 插件
  ↓ 浏览器原生功能替代
扩展 (Extensions)
  ↓ 深化与开放
功能强大的 Web API (W3C标准)
  ↓ 将浏览器内核服务化
Mojo 服务化架构

当 Web 平台本身可以做几乎所有事情,并且做得更安全、更流畅时,一个需要用户额外安装、存在安全风险、难以跨平台维护的第三方插件框架,退出历史舞台就是必然。

五、现代浏览器为什么转向 Extension

现代浏览器的扩展(Extension)模型,其设计哲学与插件截然不同。插件是在浏览器上"开洞",而扩展是在浏览器之上"添砖加瓦" 。它不是一个二进制程序的加载器,而是一个能力受限的标准化 Web 应用

现代模型

复制代码
网页 (Web Content)
  ↓ 隔离
扩展 (Extension) - 由 HTML/JS/CSS 组成
  ↓ 通过受限 API 调用
Browser (浏览器核心) - 作为能力的唯一中介和代理
  ↓ 通过内部机制访问
操作系统能力 (读写文件、管理下载、网络请求等)

扩展的能力边界:声明与权限

扩展通过 manifest.json 声明自己需要的权限,用户在安装时进行授权。

  • chrome.tabs:访问标签页信息
  • chrome.storage:同步/本地存储数据
  • chrome.cookies:访问和管理 Cookie
  • chrome.downloads:管理文件下载
  • chrome.webRequest:拦截和修改网络请求
  • nativeMessaging唯一的"后门",允许扩展与一个用户预先安装的原生程序进行标准输入输出通信。

内部调用链路

一个看似简单的扩展 API 调用,背后是复杂的进程间通信。例如:

chrome.tabs.query({active: true, currentWindow: true})

其内部链路是:

复制代码
Extension Context (扩展的JS代码运行环境)
  ↓ 调用 chrome.tabs.query API
Extensions API (C++ 实现,运行在 Browser Process)
  ↓ 访问内部数据结构
Browser (TabStripModel, Window管理等)
  ↓ 通过 Mojo 与渲染进程通信
Renderer (WebContents,查找对应标签页状态)
  ↓ 返回结果
最终将 Tab 对象返回给扩展的 JS 代码

在这个模型中,浏览器成为了所有系统能力的统一、受控的入口。它不再是简单地加载插件 DLL,而是精心的在扩展和系统之间加了一层代理和审计。

六、既然淘汰了,为什么还有厂商保留 NPAPI

尽管主流浏览器(Chrome, Firefox, Edge, Safari)早已全面禁用 NPAPI,但在某些特定领域,尤其在中国,仍有大量基于 Chromium 的国产浏览器保留了 NPAPI 支持。这通常不是技术优劣的问题,而是无法逃避的历史兼容成本

这些系统往往是多年前基于 ActiveX (IE) 或 NPAPI (Firefox/Chrome) 开发的关键业务系统。

典型的遗留系统依赖链

复制代码
业务网页
  ↓ 调用
plugin.Sign("合同数据") 或 plugin.ReadUSB()
  ↓ 插件通过
中间件 DLL (PKI 库、驱动接口)
  ↓ 与
硬件证书 (U盾) / 系统驱动
  ↓ 绑定
后台业务逻辑

受影响的核心行业:

  • 银行:企业网银、银企直连
  • 政务:电子税务局、工商注册、政府采购平台
  • OA 办公:公文处理、电子签章(如iSignature)、红头文件
  • 工控与医疗:基于浏览器的 SCADA 监控界面、PACS 医学影像系统
  • 教育:老旧的在线考试系统、虚拟实验平台

如果贸然删除 NPAPI 支持,结果是灾难性的:

  • 功能直接失效:登录按钮点击无反应、签章无法调用、U盾无法识别、文件无法上传。
  • 业务停摆:企业每月报税、发送公文、处理订单的基础操作都无法完成。

厂商的常见兼容方案

为了在升级 Chromium 内核的同时保证业务连续性,厂商通常采用以下几种方案:

方案一:内部维护 NPAPI 层(硬扛)

直接在 Chromium 代码中恢复被删除的 NPAPI 基础设施。

  • 需要恢复的组件PluginServicePluginProcessHostWebPluginNPObject 绑定等。
  • 代价:带来极高的 Chromium 升级成本,每次合并上游代码都会发生剧烈冲突,并需要自行承担安全风险。

方案二:伪插件(Native Messaging 桥接)

对外表现像一个插件,但内部是扩展 + 原生程序的组合。

复制代码
业务网页 (仍可调用 window.plugin.method())
  ↓ 通过页面注入JS进行拦截和转发
Content Script (Extension)
  ↓ chrome.runtime.connectNative()
Native Host (一个独立的 host.exe)
  ↓ 加载并调用
旧插件的核心 DLL (usbkey.dll)
  • 优点:符合现代安全模型,不需要维护整个 NPAPI 栈。
  • 缺点 :需要分发和安装一个额外的 host.exe,并且页面需要适配JS桥接层。

方案三:双内核切换(最普遍的中国方案)

这也是我们下一节要重点讨论的内容。

复制代码
对于 *.bank.com, *.gov.cn
  ↓ 自动切换
使用 IE 内核(MSHTML)加载,原生支持 ActiveX/NPAPI
  ↓
对于其他标准网站
  ↓
使用 Chromium 内核加载

这完美地引出了下一个问题。

七、很多浏览器说支持 IE 内核,本质上到底做了什么

很多人可能简单地理解为:LoadLibrary("mshtml.dll") 就完成了。实际上,这背后的工程复杂度堪比在飞机飞行时更换引擎

7.1 IE 浏览器 ≠ IE 内核

IE 浏览器(iexplore.exe)只是一个外壳,它真正的核心是一系列以 mshtml.dll 为首的 COM 组件集合。

真正的 IE 内核组件:

核心 DLL 职责
mshtml.dll 排版与渲染引擎,负责 HTML/CSS 解析、DOM 树构建、布局和绘制。
jscript.dll JavaScript 引擎 (非 Chakra 模式),负责解析和执行 ES3 标准的 JS。
vbscript.dll VBScript 引擎,负责解析和执行 VBS,这是很多老式 ASP 系统的核心。
urlmon.dll URL 监视器与安全域管理,负责 URL 解析、安全区域判断、异步可插拔协议。
wininet.dll 网络与缓存,负责 HTTP/HTTPS 请求和响应,以及 Cookie 和缓存管理。
ole32.dll COM 基础结构,所有组件都以 COM 形式交互,是 IE 内核的"神经系统"。

浏览器通常通过以下 COM 调用获取 IE 的核心能力:

cpp 复制代码
// 创建 WebBrowser 控件的实例
CoCreateInstance(CLSID_WebBrowser, NULL, CLSCTX_INPROC_SERVER, IID_IWebBrowser2, (void**)&pBrowser);
// 控制其导航到指定URL
pBrowser->Navigate(L"http://old-system.com");

7.2 浏览器真正做的是构建一个"IE 容器"

现代浏览器并不是"变成"IE,而是 在自己的一个标签页或区域中,作为容器宿主(Host)去承载一个 IE 内核的实例

宏观架构

复制代码
现代浏览器 (Browser Process)
  ├─ Chromium 标签页栈
  └─ IE 容器宿主
        ├─ IEHostView (一个自定义的窗口类)
        │    ↓ 创建子窗口,管理布局和消息
        └─ WebBrowser Control (ActiveX 控件实例)
              ↓ 加载和运行
              ├─ mshtml.dll, jscript.dll ...
              └─ 业务 ActiveX 控件 (网银U盾)

容器内部的典型实现:

cpp 复制代码
class IEHostView {
public:
  // 创建一个可以嵌入 ActiveX 控件的容器窗口
  HWND Create(HWND parent) {
    // AtlAxCreateControl 会加载并初始化 "Shell.Explorer" (即WebBrowser控件)
    AtlAxCreateControl(L"Shell.Explorer", parent, NULL, &m_spBrowser);
    // 获取 IWebBrowser2 接口进行操作
    m_spBrowser->Navigate(url);
  }
private:
  CComPtr<IWebBrowser2> m_spBrowser;
};

7.3 真正困难的是"统一体验"

加载 IE 内核只是第一步,最困难的是让用户感觉不到他在使用两个完全不同的引擎。这需要在多个层面进行深度同步。

(1) 统一的 Tab 生命周期管理

需要一个抽象基类来封装所有类型的网页宿主:

cpp 复制代码
class IWebPage {
public:
  virtual void LoadURL(const GURL& url) = 0;
  virtual void Close() = 0;
  virtual std::string GetTitle() = 0;
  // ...
};

// 一个实现是标准 Chromium 页面
class ChromeView : public IWebPage { /* 包含 content::WebContents */ };
// 另一个实现是 IE 容器页面
class IEView : public IWebPage { /* 包含 IEHostView */ };

(2) 同步导航状态

监听 IE 的一系列 COM 事件,并转换成浏览器统一的状态。

  • NavigateComplete2 -> 更新地址栏 URL
  • DocumentComplete -> 通知 Tab 页面加载完毕,停止刷新动画
  • TitleChange -> 更新 Tab 标题
  • CommandStateChange -> 更新前进后退按钮的可用状态

(3) 同步 Cookie 与登录态

这是最棘手的问题之一。许多旧系统使用 wininet.dll 的 Cookie 容器,而 Chromium 使用自己的 CookieMonster。两者默认是隔离的。

  • 登录丢失问题:用户在 IE 内核中登录银行,切换到 Chrome 内核又变成未登录。
  • 解决方案 :需要实现一个 CookieSyncService,通过 InternetGetCookieEx 等 API 定期将 WinINET Cookie 与 Chromium 的 Cookie 数据库进行双向同步,或者强制所有请求走同一套网络栈。

(4) 接管下载

IE 弹出原生的下载对话框会破坏统一体验。

  • 实现 :监听 DownloadBeginFileDownload 等事件,取消 IE 的原生下载,获取文件的 URL、Cookie等信息,传递给浏览器自己的 DownloadManager 处理。

(5) JS 与浏览器通信桥接

老旧系统大量使用 window.external 作为 JS 与浏览器外壳通信的渠道。

  • window.external.Login("user", "pass")
  • 实现 :浏览器需要实现一个 IDispatch 接口的 COM 对象,并将其挂载到 IE 容器的 window.external 上。这样,IE 内的 JS COM 调用就能被转换成 C++ 函数调用,进入浏览器的逻辑。

(6) 智能切核(自动切换规则)

不可能让用户手动选择哪个网站用哪个内核。需要一套复杂的规则引擎。

  • 规则示例*.bank.com*.gov.cn*.taobao.com(仅限某些特定路径)。
  • 逻辑 :当一个导航请求发起时,先匹配规则引擎。
    • 命中 IE 规则:CreateViewForIE() -> 创建 IEView
    • 未命中:CreateViewForChrome() -> 创建 ChromeView

7.4 为什么越来越少浏览器保留 IE 了

这个"IE 容器"方案的维护成本是天文数字:

  • Chromium 代码库本身已经极其庞大和复杂。
  • MSHTML + 同步层 + 兼容层,这是一套对稳定性、性能和安全性要求都极高的胶水代码。
  • 微软的态度:微软已放弃 IE,转向基于 Chromium 的 Edge。Windows 11 中已移除 IE。继续依赖一个不再更新的系统组件,风险极高。

现代替代方案(微软主推):

复制代码
Chromium 内核浏览器
  ↓ 如果遇到老旧系统
开启 IE 模式标签页 (实际上就是之前的"容器"方案,但由微软官方维护)
  ↓ 或者
使用 WebView2,引导客户将系统前端逐步重构为现代 Web 技术

八、那 Chromium 的 content:: API 又是什么?

当你在 Chromium 源码中看到满屏的 content:: 时,它不是指"网页内容"。它是 Chromium 项目最核心、最基础的**"浏览器抽象层"**。

可以这样理解 Chromium 的分层架构:

复制代码
+-----------------+  <-- 产品层(面向用户)
|  chrome/        |      Chrome, Edge, Opera 等浏览器的个性化功能 (书签, 同步, 历史, UI主题)
+-----------------+
        ^
        |
+-----------------+  <-- 内容层(核心抽象)
|   content/      |      一个多进程的、沙箱化的 Web 平台托管层。它是所有浏览器功能的基础。
+-----------------+      暴露 content:: 公共 API。
        ^
        |
+-----------------+  <-- 引擎层
|  blink/ & v8/   |      渲染引擎 (Blink) 和 JavaScript 引擎 (V8)。
+-----------------+
        ^
        |
+-----------------+  <-- 系统层
|   //base, //net |      基础库、网络栈、图形、GPU 命令缓冲等。
+-----------------+

content:: 层的核心职责:

  • 多进程架构的实现:管理 Browser, GPU, Utility, Plugin, Renderer 等各种进程的启动、通信和生命周期。
  • 沙箱隔离:为每个渲染进程以及其他子进程配置沙箱策略。
  • 站点隔离:实现现代浏览器的站点隔离(Site Isolation)安全特性。
  • 核心 Web 平台功能:提供一个最小化的集合来加载、渲染和交互网页,并暴露必要的 API 供上层使用。

content:: 命名空间下的核心类

核心类 职责与代表意义
content::WebContents 一个网页页面实例的抽象 。它是 content/ 层最核心的类。代表了从导航开始,到渲染、显示、事件处理的全过程。
content::RenderProcessHost 渲染进程的宿主。负责管理一个独立的渲染器进程的生命周期和通信。
content::RenderFrameHost 一个 RenderFrame 的宿主 。一个 WebContents 可能包含多个 frame(主 frame + 子 iframe),每个都由一个 RenderFrameHost 代理。
content::BrowserContext 浏览器上下文。通常对应一个用户 Profile,管理该用户下的 Cookie、缓存、存储权限等数据。
content::NavigationHandle 一次导航请求的处理对象。从发起 URL 请求到提交渲染的全过程。

当你在 chrome/ 层想要打开一个新标签页时,最终会调用 content::WebContents::Create() 等方法,在 content/ 层创建并管理这个页面实例。

九、现代 Chromium 的真实能力链路

今天浏览器内部的能力调用,早已不是一条单向直线,而是一个服务化的、多层解耦的复杂系统

能力链路全景图:

复制代码
网页 JavaScript
  ↓ 调用
标准 Web API (如 navigator.geolocation, navigator.mediaDevices)
  ↓ 实现于
Blink 渲染引擎 (在沙箱化的 Renderer Process 中运行)
  ↓ 如果该 API 需要系统权限,则通过 Mojo 接口发起 IPC 调用
Browser Process 中的对应 Mojo 服务实现 (如 GeolocationServiceImpl)
  ↓ 该服务验证权限、管理生命周期,并调用真正的系统接口
操作系统 API (如 Windows Location API, CoreAudio, Win32 file I/O)

对比过去的 NPAPI 链路:

复制代码
网页 JS
  ↓ 直接调用
Plugin DLL (在浏览器进程中)
  ↓ 直接调用
Windows API

核心变化在于

  1. 调用从直接函数调用变为跨进程 IPC:保证了安全与稳定。
  2. 能力提供者从第三方 DLL 变为浏览器自己的服务:浏览器从平台的使用者,变成了平台能力的定义者和提供者。
  3. 一切皆服务化(Mojo):地理定位、文件系统、网络、音频、GPU 等都变成了独立的 Mojo 服务。这使得架构极度模块化,可以独立开发、测试甚至替换。

十、总结:浏览器能力的四个时代

浏览器能力模型的演进,本质上是一段不断强化安全与规范,逐步收回"系统权限后门" 的历史。

第一代:NPAPI 时代(野蛮共生)

复制代码
网页
  ↓ 无限制调用
NPAPI 插件 (DLL)
  ↓ 直接调用
操作系统
  • 关键词:高性能、无权限控制、稳定性极差、严重安全风险。
  • 代表:Flash, Silverlight, 各种网银控件。

第二代:PPAPI 时代(隔离沙箱)

复制代码
网页
  ↓ 声明式调用
PPAPI 插件进程 (沙箱运行)
  ↓ 通过代理访问
操作系统
  • 关键词:进程隔离、沙箱安全、过渡技术。
  • 代表:Pepper Flash。

第三代:Extension 时代(能力封装)

复制代码
网页 (受限环境)
  ↓ 通过 Event/API 通信
扩展 (Extension)
  ↓ 通过受限的 JS API 调用
浏览器核心 (权限代理)
  • 关键词:跨平台、权限声明、浏览器即中介。
  • 代表:Chrome Web Store 中的各类扩展。

第四代:Web API + Mojo 服务化时代(平台即服务)

复制代码
网页 (JS)
  ↓ 调用标准 API
Web 平台 (Blink + Mojo 服务)
  ↓ 高度模块化、可审计
操作系统能力
  • 关键词:标准化、服务化、高模块化、安全纵深防御。
  • 代表:现代浏览器本身。

如果你今天还在 Chromium 代码里看到 PluginServicePluginProcessHostNPObjectWebPlugin 等代码,它们通常意味着:

  • 历史兼容:为了不破坏某些极端边缘的旧页面。
  • 企业定制:某些发行版(如企业浏览器或国产浏览器)为特定业务保留的能力。
  • 代码遗迹:在彻底移除前,暂时保留的僵尸代码。

现代浏览器的真正主路径已经全面迁移到了:

  • content/:负责核心的多进程 Web 平台托管。
  • extensions/:提供安全、跨平台的扩展系统。
  • services/:网络、设备、媒体等核心系统服务的集合。
  • components/:可复用的浏览器功能组件,如自动填充、密码管理。

浏览器,已经不再是那个简单的"网页加载器"。它已经蜕变成一个功能强大、安全严密的通用应用运行平台,是事实上的新一代操作系统用户态。