Chrome 插件 V3 版本 Manifest.json 中的内容脚本(Content Scripts)解析
内容脚本(Content Scripts)
指定在用户打开某些网页时要使用的
JavaScript或CSS文件。
内容脚本是在网页环境中运行的文件。通过使用标准文档对象模型 (
DOM),开发者能够读取浏览器所访问网页的详情、更改这些网页,并将信息传递给其父级扩展程序。
一、内容脚本功能
内容脚本在声明扩展程序文件为可通过网络访问的资源后,便可访问扩展程序文件。他们可以直接访问以下扩展程序 API:
domi18nstorageruntime.connect()runtime.getManifest()runtime.getURL()runtime.idruntime.onConnectruntime.onMessageruntime.sendMessage()
内容脚本无法直接访问其他 API。但用户可以通过与扩展程序的其他部分交换消息来间接访问这些扩展程序。
二、隔离环境
隔离世界是一种私有执行环境,页面或其他扩展程序无法访问。这种隔离带来的实际结果是,扩展程序内容脚本中的
JavaScript变量对托管页面或其他扩展程序的内容脚本不可见。此概念最初是在Chrome首次发布时引入的,用于隔离浏览器标签页。
不仅每个扩展程序都会在各自的独立世界中运行,内容脚本和网页也会在这方面运行。这意味着它们(网页、内容脚本和任何正在运行的扩展程序)都无法访问其他项的上下文和变量。
内容脚本位于一个独立的环境中,这使得内容脚本可以对其 JavaScript 环境进行更改,而不会与网页或其他扩展程序的内容脚本发生冲突。
1. 示例
插件在类似于以下示例的网页中运行。
webPage.html
<html>
<button id="mybutton">click me</button>
<script>
var greeting = "hello, ";
var button = document.getElementById("mybutton");
button.person_name = "Bob";
button.addEventListener(
"click", () => alert(greeting + button.person_name + "."), false);
</script>
</html>
该扩展程序可以使用注入脚本部分中所述的方法之一注入以下内容脚本。
content-script.js
var greeting = "hola, ";
var button = document.getElementById("mybutton");
button.person_name = "Roberto";
button.addEventListener("click", () => alert(greeting + button.person_name + "."), false);
进行此项更改后,用户点击按钮时,系统会按顺序显示两个提醒。
三、注入脚本(Inject scripts)
1. 使用静态声明进行注入
使用 manifest.json 中的静态内容脚本声明。
静态声明的脚本在清单中的 "content_scripts" 键下注册。可以包含 JavaScript 文件和/ CSS 文件。所有自动运行的内容脚本都必须指定匹配模式。
manifest.json
{
"name": "My extension",
"content_scripts": [
{
"matches": ["https://*.nytimes.com/*"],
"css": ["my-styles.css"],
"js": ["content-script.js"]
}
],
}
| 名称 | 类型 | 说明 |
|---|---|---|
matches |
字符串数组 string[] |
必需。指定将此内容脚本注入到哪些网页。 |
css |
字符串数组 string[] |
可选。要注入到匹配页面的 CSS 文件列表。这些代码会按照它们在此数组中出现的顺序进行注入,然后为网页构建或显示任何 DOM。 |
js |
字符串数组 string[] |
可选。要注入到匹配页面的 JavaScript 文件的列表。系统会按照文件在此数组中出现的顺序注入文件。此列表中的每个字符串都必须包含扩展程序根目录中某项资源的相对路径。前导斜杠(“/”)会自动剪除。 |
run_at |
RunAt |
可选。指定应将脚本注入网页的时间。默认为 document_idle。 |
match_about_blank |
布尔值 boolean |
可选。脚本是否应注入到 about:blank 帧中,其中父帧或起始帧与 matches 中声明的模式之一匹配。默认值为 false。 |
match_origin_as_fallback |
布尔值 boolean |
可选。脚本是否应在由匹配的来源创建但其网址或来源可能与模式不直接匹配的帧中注入。其中包括采用不同架构的帧,例如 about:、data:、blob: 和 filesystem:。 |
world |
ExecutionWorld |
可选。要在其中执行脚本的 JavaScript 环境。默认值为 ISOLATED |
1.1 RunAt
document_startDOM仍在加载。
document_end- 网页的资源仍在加载
document_idle- DOM 和资源已加载完毕。这是默认值。
1.2 ExecutionWorld
ISOLATED- 指定独立的世界,这是扩展程序独有的执行环境。
MAIN- 指定
DOM的主环境,即与托管网页的JavaScript共享的执行环境。
- 指定
2. 使用动态声明进行注入
如果内容脚本的匹配模式并不为人所知,或者内容脚本不应总是注入已知主机上,就需要使用动态进行注入。
内容脚本对象是使用 chrome.scripting 命名空间(而不是 manifest.json)中的方法在 Chrome 中注册的。
Scripting API 还允许扩展程序开发者执行以下操作:
动态声明可以包含 JavaScript 文件和或 CSS 文件。
service-worker.js
2.1. 注册
chrome.scripting
.registerContentScripts([{
id: "session-script",
js: ["content.js"],
persistAcrossSessions: false,
matches: ["*://example.com/*"],
runAt: "document_start",
}])
.then(() => console.log("registration complete"))
.catch((err) => console.warn("unexpected error", err))
2.2. 更新
chrome.scripting
.updateContentScripts([{
id: "session-script",
excludeMatches: ["*://admin.example.com/*"],
}])
.then(() => console.log("registration updated"));
2.3. 获取
chrome.scripting
.getRegisteredContentScripts()
.then(scripts => console.log("registered content scripts", scripts));
2.4. 移除
chrome.scripting
.unregisterContentScripts({ ids: ["session-script"] })
.then(() => console.log("un-registration complete"));
3. 以编程方式注入
对于需要为了响应事件或在特定情况下运行的内容脚本,使用程序化注入。
如需以编程方式注入内容脚本,扩展程序需要对要尝试注入脚本的页面拥有主机权限。可以通过在扩展程序清单中请求这些权限来授予主机权限,也可以通过 activeTab 暂时授予主机权限。
3.1. 内容文件作为脚本进行注入
manifest.json
{
"name": "My extension",
"permissions": [
"activeTab",
"scripting"
],
"background": {
"service_worker": "background.js"
},
"action": {
"default_title": "Action Button"
}
}
content-script.js
document.body.style.backgroundColor = "orange";
service-worker.js
chrome.action.onClicked.addListener((tab) => {
chrome.scripting.executeScript({
target: { tabId: tab.id },
files: ["content-script.js"]
});
});
3.2. 注入函数正文,并将其作为内容脚本执行。
service-worker.js
function injectedFunction() {
document.body.style.backgroundColor = "orange";
}
chrome.action.onClicked.addListener((tab) => {
chrome.scripting.executeScript({
target : {tabId : tab.id},
func : injectedFunction,
});
});
3.3 注入函数时,可以传递参数
service-worker.js
function injectedFunction(color) {
document.body.style.backgroundColor = color;
}
chrome.action.onClicked.addListener((tab) => {
chrome.scripting.executeScript({
target : {tabId : tab.id},
func : injectedFunction,
args : [ "orange" ],
});
});
四、排除匹配项和 glob
如需自定义指定的网页匹配,请在声明式注册中添加以下字段
| 名称 | 类型 | 说明 |
|---|---|---|
exclude_matches |
字符串数组 string[] |
可选。不包括此内容脚本将被注入的网页。 |
include_globs |
字符串数组 string[] |
可选。在 matches 之后应用,以仅包含也与此 glob 匹配的网址。 |
exclude_globs |
字符串数组 string[] |
可选。在 matches 之后应用,以排除与此 glob 匹配的网址。 |
1. 排除/包含匹配
如果同时满足以下两个条件,内容脚本将会注入到网页中:
- 其网址与任何
matches格式和include_globs格式匹配。 - 该网址也不符合
exclude_matches或exclude_globs格式。由于matches属性是必需的,因此exclude_matches、include_globs和exclude_globs只能用于限制哪些页面会受到影响。
1.1. 示例
以下扩展程序会将内容脚本注入 https://www.nytimes.com/health,但不会注入 https://www.nytimes.com/business。
manifest.json
{
"name": "My extension",
"content_scripts": [
{
"matches": ["https://*.nytimes.com/*"],
"exclude_matches": ["*://*/*business*"],
"js": ["contentScript.js"]
}
],
}
service-worker.js
chrome.scripting.registerContentScripts([{
id : "test",
matches : [ "https://*.nytimes.com/*" ],
excludeMatches : [ "*://*/*business*" ],
js : [ "contentScript.js" ],
}]);
2. Glob 匹配
Glob 属性遵循与匹配模式不同且更灵活的语法。可接受的 glob 字符串是指可能包含通配符和问号的网址。
- 星号 (
*) 匹配任何长度的字符串,包括空字符串 - 问号 (
?) 匹配任何单个字符。
例如,glob https://???.example.com/foo/* 与以下任何一项匹配:
https://www.example.com/foo/barhttps://the.example.com/foo/
不过,它与以下内容不匹配:
https://my.example.com/foo/barhttps://example.com/foo/https://www.example.com/foo
2.1 示例
- 此扩展程序会将内容脚本注入
https://www.nytimes.com/arts/index.html和https://www.nytimes.com/jobs/index.htm*,但不会注入https://www.nytimes.com/sports/index.html:
manifest.json
{
"name": "My extension",
"content_scripts": [
{
"matches": ["https://*.nytimes.com/*"],
"include_globs": ["*nytimes.com/???s/*"],
"js": ["contentScript.js"]
}
],
}
- 此扩展程序会将内容脚本注入
https://history.nytimes.com和https://.nytimes.com/history,但不会注入https://science.nytimes.com或https://www.nytimes.com/science:
manifest.json
{
"name": "My extension",
"content_scripts": [
{
"matches": ["https://*.nytimes.com/*"],
"exclude_globs": ["*science*"],
"js": ["contentScript.js"]
}
],
}
- 全部参数都加上
此扩展程序会将内容脚本注入 https://www.example.com/arts/index.html
和 https://.example.com/jobs/index.html,但不会注入 https://science.example.com、https://www.example.com/jobs/business 和 https://www.example.com/science
manifest.json
{
"content_scripts": [
{
"matches": ["https://*.example.com/*"],
"exclude_matches": ["*://*/*business*"],
"include_globs": ["*example.com/???s/*"],
"exclude_globs": ["*science*"],
"js": ["content-script.js"]
}
],
}
五、运行时间
run_at 字段用于控制何时将 JavaScript 文件注入网页。首选值为 "document_idle"。
manifest.json
{
"name": "My extension",
"content_scripts": [
{
"matches": ["https://*.nytimes.com/*"],
"run_at": "document_idle",
"js": ["contentScript.js"]
}
],
}
service-worker.js
chrome.scripting.registerContentScripts([{
id : "test",
matches : [ "https://*.nytimes.com/*" ],
runAt : "document_idle",
js : [ "contentScript.js" ],
}]);
六、允许运行的 iframe
"all_frames" 字段允许该扩展程序指定将 JavaScript 和 CSS 文件注入到符合指定网址要求的所有框架中,还是仅注入标签页中最顶层的框架。
manifest.json
{
"name": "My extension",
...
"content_scripts": [
{
"matches": ["https://*.nytimes.com/*"],
"all_frames": true,
"js": ["contentScript.js"]
}
],
...
}
service-worker.js
chrome.scripting.registerContentScripts([{
id: "test",
matches : [ "https://*.nytimes.com/*" ],
allFrames : true,
js : [ "contentScript.js" ],
}]);
| 名称 | 类型 | 说明 |
|---|---|---|
all_frames |
布尔值 boolean |
可选。默认为 false,表示仅匹配顶部帧。 如果指定 true,所有帧都将注入到,即使帧不是标签页中的最顶层帧也是如此。系统会单独检查每个帧是否符合网址要求。如果不符合网址要求,则不会注入子框架。 |
七、通信
虽然内容脚本的执行环境和托管它们的页面彼此隔离,但它们共享对页面 DOM 的访问权限。如果网页希望通过内容脚本与内容脚本或扩展程序进行通信,则必须通过共享 DOM 来实现。
可以使用 window.postMessage()
content-script.js
var port = chrome.runtime.connect();
window.addEventListener("message", (event) => {
// We only accept messages from ourselves
if (event.source !== window) {
return;
}
if (event.data.type && (event.data.type === "FROM_PAGE")) {
console.log("Content script received: " + event.data.text);
port.postMessage(event.data.text);
}
}, false);
example.js
document.getElementById("theButton").addEventListener("click", () => {
window.postMessage(
{type : "FROM_PAGE", text : "Hello from the webpage!"}, "*");
}, false);
非扩展程序网页 example.html 向自身发布消息。内容脚本拦截和检查此消息,然后发布到扩展程序进程。通过这种方式,页面就能与扩展进程建立通信连接。反之亦然。
八、访问扩展程序文件
如需从内容脚本访问扩展程序文件,可以调用 chrome.runtime.getURL() 来获取扩展程序资源的绝对网址。
content-script.js
let image = chrome.runtime.getURL("images/my_image.png")
如需在 CSS 文件中使用字体或图片,可以使用 @@extension_id 构建网址,如以下示例所示 (content.css):
content.css
body {
background-image:url('chrome-extension://__MSG_@@extension_id__/background.png');
}
@font-face {
font-family: 'Stint Ultra Expanded';
font-style: normal;
font-weight: 400;
src: url('chrome-extension://__MSG_@@extension_id__/fonts/Stint Ultra Expanded.woff') format('woff');
}
所有资源都必须在 manifest.json 文件中声明为网络可访问资源:
manifest.json
{
"web_accessible_resources": [
{
"resources": [ "images/*.png" ],
"matches": [ "https://example.com/*" ]
},
{
"resources": [ "fonts/*.woff" ],
"matches": [ "https://example.com/*" ]
}
],
}
九、禁止事项
1. Eval()
以下为 Manifest V3 禁止使用案例
content-script.js
const data = document.getElementById("json-data");
// WARNING! Might be evaluating an evil script!
const parsed = eval("(" + data + ")");
2. 字符串拼接函数
以下为 Manifest V3 禁止使用案例
content-script.js
const elmt_id = "...";
window.setTimeout("animate(" + elmt_id + ")", 200);
引用
Chrome 插件 V3 版本 Manifest.json 中的内容脚本(Content Scripts)解析的更多相关文章
- Chrome插件开发之manifest.json
广而告之: Chrome插件之一键保存网页为PDF1.1发布 http://www.cnblogs.com/bdstjk/p/3179543.html 最近做“一键保存网页为PDF”过程中,对Chro ...
- maven-bundle-plugin 2.4.0以下版本导出META-INF中的内容到MANIFEST.MF中
今天终于把maven-bundle-plugin不能导出META-INF中的内容到Export-Package中的问题解决了,因为用到的第三方JAR包需要加载META-INF/XX/XX.xml这个内 ...
- Chrome插件(Extensions)开发攻略
本文将从个人经验出发,讲述为什么需要Chrome插件,如何开发,如何调试,到哪里找资料,会遇到怎样的问题以及如何解决等,同时给出一个个人认为的比较典型的例子——获取网页内容,和服务器交互,再把信息反馈 ...
- 【干货】Chrome插件(扩展)开发全攻略(不点进来看看你肯定后悔)
写在前面 我花了将近一个多月的时间断断续续写下这篇博文,并精心写下完整demo,写博客的辛苦大家懂的,所以转载务必保留出处.本文所有涉及到的大部分代码均在这个demo里面:https://github ...
- 【干货】Chrome插件(扩展)开发全攻略
写在前面 我花了将近一个多月的时间断断续续写下这篇博文,并精心写下完整demo,写博客的辛苦大家懂的,所以转载务必保留出处.本文所有涉及到的大部分代码均在这个demo里面:https://github ...
- chrome插件编写基本入门
chrome插件编写基本入门 http://igeekbar.com/igeekbar/post/331.htm #精选JAVASCRIPTCHROME 作为一名程序猿,怎么能不会写chrome插件 ...
- Chrome插件(扩展)
[干货]Chrome插件(扩展)开发全攻略 写在前面 我花了将近一个多月的时间断断续续写下这篇博文,并精心写下完整demo,写博客的辛苦大家懂的,所以转载务必保留出处.本文所有涉及到的大部分代码均 ...
- 【干货】Chrome插件(扩展)开发全攻略(转)
写在前面 我花了将近一个多月的时间断断续续写下这篇博文,并精心写下完整demo,写博客的辛苦大家懂的,所以转载务必保留出处.本文所有涉及到的大部分代码均在这个demo里面:https://github ...
- 能说会道爱办公——“别人家的”Chrome插件到底怎么做
根据相关数据显示,谷歌的Chrome浏览器目前已达近七成的市场占有率,成为浏览器的"霸主".大家选择Chrome,除了是因为性能的优越以及强大的兼容性之外,Chrome充足的扩展插 ...
- 利用chrome插件批量读取浏览器页面内容并写入数据库
试想一下,如果每天要收集100页网页数据甚至更多.如果采用人工收集会吐血,用程序去收集也就成为一个不二的选择.首先肯定会想到说用java.php.C#等高级语言,但这偏偏又有个登陆和验证码,搞到无所适 ...
随机推荐
- #Raney引理,圆排列#洛谷 6672 [清华集训2016] 你的生命已如风中残烛
题目 分析 转化一下条件,就是 \(\sum{w_i}\geq i\),将所有牌权值减一,那就是 \(\sum{w'_i}\geq 0\) 根据Raney引理,总和为 1 的数列,在循环移位时,只有一 ...
- 赵海鹏:如何进行 OpenHarmony 音频特性架构设计和开发工作
编者按:在 OpenHarmony 生态发展过程中,涌现了大批优秀的代码贡献者,本专题旨在表彰贡献.分享经验,文中内容来自嘉宾访谈,不代表 OpenHarmony 工作委员会观点. 赵海鹏 江苏润和软 ...
- Numpy随机数组(random)
numpy.random()模块补充了Python内置random模块的一些功能,用于高效/高速生成一些概率分布的样本数组数据. In [1]: import numpy as np In [2]: ...
- 驾考宝典携手HMS Core统一扫码服务,构建复杂场景中的流畅扫码体验
"驾考宝典"是一款颇具人气的互联网综合驾照考试学习应用,通过强大的驾考功能,在手机移动端为学车学员提供从报名.学习到拿本的全方位驾考服务.作为一个专业的驾培平台,"驾考宝 ...
- 使用谷歌浏览器打开PDF文件,怎么关闭缩略图
我们在使用谷歌浏览器浏览PDF文件时,总是会出现章节预览缩略图和工具栏,我们可以使用 参数来控制浏览器不显示出工具栏 #scrollbars=0&toolbar=0&statusbar ...
- GAN的一些问题
GAN为什么难以训练? 大多深度模型的训练都使用优化算法寻找损失函数比较低的值.优化算法通常是个可靠的"下山"过程.生成对抗神经网络要求双方在博弈的过程中达到势均力敌(均衡).每个 ...
- 涂色-【BFS】
涂色 有一幅以二维整数数组表示的图画,每一个整数表示该图画的像素值大小,数值在 0 到 65535 之间.给你一个坐标 (sr, sc) 表示图像渲染开始的像素值(行 ,列)和一个新的颜色值 newC ...
- allure常用
overview:概述页面:categories:分类目录:suites:按套件和类分组的已执行测试的标准结构:graphs:图表:timeline:时间线:behaviors:可根据epic.fea ...
- React 逃离闭包陷阱
众所周知,JavaScript 中的闭包(Closures)一定是这种语言最可怕的特性之一,即使是无所不知的 ChatGPT 也是这样说的.另外它可能也是最隐蔽的语言特性之一,我们在编写 React ...
- 大屏小程序探索实践 | Cube 技术解读
简介: 支付宝客户端有极强的动态化诉求,不论 iOS 还是 Android 平台,重新分发软件包从时间上,效率上难以满足产品运营的要求,因此客户端动态化技术应运而生. Cube 起源于 Native ...