<返回更多

从浏览器原理出发聊聊Chrome插件

2023-12-08    OSC开源社区
加入收藏

阿里妹导读

本文从浏览器架构演进、插件运行机制、插件基本介绍和一些常见的插件实现思路几个方向聊聊Chrome插件。

浏览器架构演进

单进程浏览器时代

单进程浏览器是指浏览器的所有功能模块都是运行在同一个进程里,这些模块包含了网络、插件、JAVA 运行环境、渲染引擎和页面等。在 2007 年之前,市面上浏览器都是单进程的。

从浏览器原理出发聊聊Chrome插件

单进程浏览器的架构

很多功能模块运行在一个进程里,是导致单进程浏览器不稳定、不流畅和不安全的一个主要因素。

多进程浏览器时代

早期架构

从浏览器原理出发聊聊Chrome插件

2008 年 Chrome 发布时的进程架构

从图中可以看出,早期的架构已经对浏览器的能力进行了拆分,主要拆分为三类:浏览器进程、插件进程和渲染进程。每个页面是运行在单独的渲染进程中的,同时页面里的插件也是运行在单独的插件进程之中,进程之间是通过 IPC 机制进行通信。

这就解决了单进程时代浏览器的各种问题:

近期架构

相较之前,近期的架构又有了很多新的变化。

从浏览器原理出发聊聊Chrome插件

近期Chrome进程架构

从图中可以看出,最新的 Chrome 浏览器包括:1 个浏览器主进程、1 个 GPU 进程、1 个网络进程、多个渲染进程和多个插件进程。

当前架构

目前Chrome浏览器的架构正在发生一些改变,称为面向服务的架构(SOA),目的是将和浏览器本身(Chrome)相关的部分拆分为一个个不同的服务,服务化之后,这些功能既可以放在不同的进程里面运行也可以合并为一个单独的进程运行。

这样做的主要原因是让Chrome在不同性能的硬件上有不同的表现。当Chrome运行在一些性能比较好的硬件时,浏览器进程相关的服务会被放在不同的进程运行以提高系统的稳定性。相反如果硬件性能不好,这些服务就会被放在同一个进程里面执行来减少内存的占用。

从浏览器原理出发聊聊Chrome插件

面向服务的架构

插件运行机制

在运行机制前,我们先来回顾一下打开页面会发生什么:

打开页面发生了什么

打开插件发生了什么

插件的运行相较于页面会有简化

  1. 我们打开浏览器,新增一个空白tab页

从浏览器原理出发聊聊Chrome插件

  1. tab栏空白处右键,选择任务管理器,打开任务管理器面板

从浏览器原理出发聊聊Chrome插件

  1. 可以看到运行了6个进程,分别是浏览器进程、GPU进程、网络进程、存储进程、渲染进程和扩展进程。

从浏览器原理出发聊聊Chrome插件

插件基本介绍

版本发展

chrome插件存在三个版本,分别是Manifest V1、Manifest V2和Manifest V3

其中MV1版本已经被废弃了,目前市面上存在MV2和MV3版本,以MV2为主流,在被MV3慢慢取代。

时间线:

从浏览器原理出发聊聊Chrome插件

Manifest V2新特性

https://developer.chrome.com/docs/extensions/mv2/manifestVersion/#manifest-v1-changes

Manifest V3新特性 切换MV3会带来的问题 // V2 background.js letsaveUserName = "";

// 其他页面,比如content-或者popup中存储数据chrome.runtime.onMessage.addListener(({ type, name }) =>{if(type=== "set-name") {saveUserName = name;}});

// 点击popup时展示数据chrome.action.onClicked.addListener((tab) =>{// 这里saveUserName可能为空字符串console.log(saveUserName, "saveUserName");});

// V3 service workerchrome.runtime.onMessage.addListener(({ type, name }) =>{if(type=== "set-name") {chrome.storage.local.set({ name });}});

chrome.action.onClicked.addListener(async(tab) => {const{ name } = awAItchrome.storage.local.get(["name"]);chrome.tabs.sendMessage(tab.id, { name });});

为什么切换MV3?

从Manifest V1到Manifest V2,可以看到Chrome想提高插件的隐私和安全,同时也优化了不少API。

而Manifest V3除了安全性更完善外,还在性能上下了功夫。Manifest V3 的核心非常明确,就是限制扩展对系统资源的使用。一直以来高资源占用都是 Chrome 为人诟病的痛点,而且扩展由于在后台运行,如果出现问题,更是难以定位和管理。

虽然增加了诸多限制,但Manifest V3还是有优点的:

这些变化可以让 Chrome 变得更加流畅,对于用户来说是好事。

展示形式

Chrome插件有以下常见的8中展现形式:

browserAction(浏览器右上角)

在浏览器右上角扩展程序一栏显示,包含一个图标、名称和popup

从浏览器原理出发聊聊Chrome插件

山海关插件popup

pageAction(地址栏右侧)

pageAction指的是在当某些特定页面打开才显示的图标。在早些版本的Chrome是将pageAction放在地址栏的最右边,左键单击弹出popup,右键单击则弹出相关默认的选项菜单。而新版的Chrome更改了这一策略,pageAction和普通的browserAction一样也是放在浏览器右上角,只不过没有点亮时是灰色的,点亮了才是彩色的,灰色时无论左键还是右键单击都是弹出选项。

从浏览器原理出发聊聊Chrome插件

右键菜单

通过开发Chrome插件可以自定义浏览器的右键菜单,主要是通过chrome.contextMenus API实现,右键菜单可以出现在不同的上下文,比如普通页面、选中的文字、图片、链接,等等。

从浏览器原理出发聊聊Chrome插件

掘金插件右键菜单

override(覆盖特定页面)

使用override可以将Chrome默认的一些特定页面替换掉,改为使用扩展提供的页面。

扩展可以替代如下页面:

从浏览器原理出发聊聊Chrome插件

掘金插件替换了新标签页

devtools(开发者工具)

Chrome允许插件在开发者工具(devtools)上开发,主要表现在:

从浏览器原理出发聊聊Chrome插件

React Developer Tools

option(选项页)

插件的设置页面,可以在右上角入口右键,有一个选项标签。

从浏览器原理出发聊聊Chrome插件

从浏览器原理出发聊聊Chrome插件

omnibox

omnibox是向用户提供搜索建议的一种方式,可以在搜索栏输入特定的标识然后按Tab进入搜索。

JSON Viewer插件

桌面通知

Chrome提供了一个chrome.notificationsAPI以便插件推送桌面通知,暂未找到chrome.notifications和HTML5自带的Notification的显著区别及优势。

在后台JS中,无论是使用chrome.notifications还是Notification都不需要申请权限(HTML5方式需要申请权限),直接使用即可。

核心介绍

manifest.json

这是一个Chrome插件最重要也是必不可少的文件,用来配置所有和插件相关的配置,必须放在根目录。其中,manifest_version、name、version3个是必不可少的。

Manifest V2

{// 清单文件的版本,这里先使用2演示"manifest_version": 2,// 插件的名称"name": "...",// 插件的版本"version": "1.0.0",// 插件描述"deion": "...",// 图标,一般偷懒全部用一个尺寸的也没问题"icons": {"16": "img/icon.png","48": "img/icon.png","128": "img/icon.png"},// 会一直常驻的后台JS或后台页面"background": {"s": ["js/background.js"]},// 浏览器右上角图标设置,browser_action、page_action、App必须三选一"browser_action": {"default_icon": "img/icon.png","default_title": "...","default_popup": "popup.html"},// 当某些特定页面打开才显示的图标"page_action": {"default_icon": "img/icon.png","default_title": "...","default_popup": "popup.html"},// 需要直接注入页面的JS"content_s": [{"matches": ["<all_urls>"],"js": ["js/content-.js"],"css": ["css/custom.css"],// 代码注入的时机,document_start, document_end, document_idle,默认document_idle"run_at": "document_start"},],// 权限申请"permissions": ["contextMenus", // 右键菜单"tabs", // 标签"notifications", // 通知"webRequest", // web请求"webRequestBlocking","storage", // 插件本地存储"https://*/*"// 可以通过execute或者insertCSS访问的网站],// 普通页面能够直接访问的插件资源列表,如果不设置是无法直接访问的"web_accessible_resources": ["js/inject.js"],"homepage_url": "...", // 插件主页"chrome_url_overrides": { // 覆盖浏览器默认页面"newtab": "newtab.html"},"options_ui": { // 插件选项页"page": "options.html","chrome_style": true},"omnibox": { "keyword": "..."}, // 向地址栏注册一个关键字以提供搜索建议,只能设置一个关键字"default_locale": "zh_CN", // 默认语言"devtools_page": "devtools.html", // devtools页面入口,注意只能指向一个HTML文件,不能是JS文件"content_security_policy": "...", // 安全策略"web_accessible_resources": [ // 可以加载的资源RESOURCE_PATHS]}

Manifest V3(仅展示与V2版本的不同点)

{"manifest_version": 3,"background": {"service_worker": js/background.js"},"action": { //browser_action 和 page_action,统一为 Action"default_icon": "img/icon.png","default_title": "这是一个示例Chrome插件","default_popup": "popup.html"}"content_security_policy": {"extension_pages": "...","sandbox": "..."},"web_accessible_resources": [{"resources": [RESOURCE_PATHS]}]}

content-s

是Chrome插件中向页面注入脚本的一种形式(虽然名为,其实还可以包括css的),借助content-s我们可以实现通过配置的方式轻松向指定页面注入JS和CSS。

content-s和原始页面共享DOM,但不共享JS。如要访问页面JS(例如某个JS变量),只能通过injected js来实现。content-s不能访问绝大部分chrome API,除了下面这4种:

这些API绝大部分时候都够用了,有需要调用其它API的话,可以通过通信让background或service worker来帮忙调用

background

后台是一个常驻的页面,它的生命周期是插件中所有类型页面中最长的,它随着浏览器的打开而打开,随着浏览器的关闭而关闭,所以通常把需要一直运行的、启动就运行的、全局的代码放在background里面。

background的权限非常高,几乎可以调用所有的Chrome扩展API(除了devtools),而且它可以无限制跨域,可以跨域访问任何网站而无需要求对方设置CORS。

background的概念在MV3版本中变为了service worker,区别在于生命周期变短了,service worker是短暂的基于事件的脚本,所以不适合用来保存全局变量。

popup

popup是点击右上角图标时打开的一个小窗口网页,焦点离开网页就立即关闭,一般用来做一些临时性的交互。权限级别和background差不多,就是生命周期比较短。

injected-

chrome插件中其实没有injected-这一概念,这是开发者们在开发过程中衍生出来的一种概念,指的是通过DOM操作的方式向页面注入的一种JS。

因为content-无法访问页面中的JS,虽然可以操作DOM,但是DOM却不能调用它,也就是无法在DOM中通过绑定事件的方式调用content-中的代码。但是在网页中增加一个按钮来调用插件的能力是一个比较常见的需求,所以诞生了injected-。

插件通信机制

讲通信机制之前,先回顾一下插件中存在的脚本类型。

Chrome插件的JS主要可以分为这5类:injected 、content-、popup js、background js和devtools js。

权限对比

JS种类

可访问的API

DOM访问情况

JS访问情况

直接跨域

injected

和普通JS无任何差别,不能访问任何扩展API

可以访问

可以访问

不可以

content

只能访问 extension、runtime等部分API

可以访问

不可以

不可以

popup

可访问绝大部分API,除了devtools系列

不可直接访问

不可以

可以

background

可访问绝大部分API,除了devtools系列

不可直接访问

不可以

可以

devtools

只能访问 devtools、extension、runtime等部分API

可以

可以

不可以

通过权限对比可以看到,每一种脚本在权限上都不相同,所以各种脚本间的相互通信就非常重要,这也是插件能够实现众多功能的基础。

通信概览

 

injected

content

popup

background

injected

-

window.postMessage

-

-

content

window.postMessage

-

chrome.runtime.sendMessage

chrome.runtime.connect

chrome.runtime.sendMessage

chrome.runtime.connect

popup

-

chrome.tabs.sendMessage

chrome.tabs.connect

-

chrome.extension. getBackgroundPage

background

-

chrome.tabs.sendMessage

chrome.tabs.connect

chrome.extension.getViews

-

devtools

chrome.devtools. inspectedWindow.eval

-

chrome.runtime.sendMessage

chrome.runtime.sendMessage

一些常见插件的实现思路

埋点日志检测

一般业务中都会进行一些埋点上报,埋点的本质就是发送一些带特定参数的请求,前端本地调试的时候想实时查看埋点信息通常需要去查看上报接口的入参,或者去对应的埋点平台查看,这样非常不方便。

基于这个,我们可以使用插件来帮助我们快速的可视化查看埋点信息:

从浏览器原理出发聊聊Chrome插件

页面注入小工具

插件的另一个常见用法就是往页面注入一些工具代码,比如去除页面广告工具。

从浏览器原理出发聊聊Chrome插件

总结

参考资料:

《浏览器工作原理与实践》:https://time.geekbang.org/column/intro/100033601?tab=catalog

《Inside look at modern web browser》:https://developer.chrome.com/blog/inside-browser-part1/

《图解浏览器的基本工作原理》:

https://zhuanlan.zhihu.com/p/47407398

《Welcome to Manifest V3》:https://developer.chrome.com/docs/extensions/mv3/intro/

MDN文档:

https://developer.mozilla.org/zh-CN/docs/Web/HTTP/CSP

web_accessible_resources:https://developer.chrome.com/docs/extensions/mv2/manifest/web_accessible_resources/

END

关键词:Chrome      点击(14)
声明:本站部分内容来自互联网,如有版权侵犯或其他问题请与我们联系,我们将立即删除或处理。
▍相关推荐
更多Chrome相关>>>