修改 web 页面
浏览器附加组件 ( add-on ) 常被用于修改网页。例如更改页面的样式,隐藏特定的 DOM 节点或把 DOM 节点注入到页面中。
使用 WebExtensions 有两种方式:
- 声明方式:定义一个网址格式,用来匹配特定的网址,然后加载脚本到对应的网页中。
- 编程方式:使用 JavaScript 接口,将脚本加载到一个指定标签页所承载的页面中
无论使用上面何种方式,它们都被称为内容脚本,与其他脚本的区别:
- 只能使用一部分的 webextension API。
- 能读取加载了内容脚本的网页。
- 通过使用消息 API 与其他的 webextension 通信。
在本文中,我们将看下加载脚本的两种方式。
修改匹配 URL 的页面
首先,创建一个名为“modify-page”的文件夹,并在目录下创建“manifest.json”文件,内容如下:
{
"manifest_version": 2,
"name": "modify-page",
"version": "1.0",
"content_scripts": [
{
"matches": ["https://developer.mozilla.org/*"],
"js": ["page-eater.js"]
}
]
}
"content_scripts"指出符合 URL 格式的页面地址,然后让浏览器加载脚本 (“page-eater.js“) 到匹配的 URL 页面 (https://developer.mozilla.org/ )。
备注:
由于 content_scripts
的 "js"
属性是一个数组,因此可以使用它将多个脚本注入匹配的页面。如果这样做,页面将按照数组中列出的顺序加载多个脚本。
备注: content_scripts
键还具有一个 "css"
属性,可以使用它来注入 CSS 样式表。
在 "modify-page" 文件夹下创建“page-eater.js”文件,内容如下:
document.body.textContent = "";
var header = document.createElement("h1");
header.textContent = "This page has been eaten";
document.body.appendChild(header);
现在安装这个WebExtension, 然后浏览 https://developer.mozilla.org/:
备注: 请注意,虽然此视频显示在 addons.mozilla.org 工作的 content scripts,但目前该网站已禁止 content scripts。
通过程序修改页面
如何修改程序使其在用户要求时才吞页面。现在修改上面的例子,在点击右键菜单项时才注入内容脚本。
修改 "manifest.json" 内容如下:
{
"manifest_version": 2,
"name": "modify-page",
"version": "1.0",
"permissions": ["activeTab", "contextMenus"],
"background": {
"scripts": ["background.js"]
}
}
这里我们要移除"content_scripts
"键值,并添加两个键:
permissions
:要向页面中注入脚本,就需要拥有修改页面对应的权限。activeTab
可以临时获得修改当前活动标签所加载的页面的权限。另外还通过 contextmenus 来获取添加右键菜单项的权限。background
: 加载名为 "background.js" 的 "background script" (长期有效的后台脚本),在该脚本中,我们将设置注入右键菜单的内容脚本。
在 "modify-page" 文件夹下创建名为 "background.js"的新文件,内容如下:
browser.contextMenus.create({
id: "eat-page",
title: "Eat this page",
});
browser.contextMenus.onClicked.addListener(function (info, tab) {
if (info.menuItemId == "eat-page") {
browser.tabs.executeScript({
file: "page-eater.js",
});
}
});
在该脚本中我们创建了一个右键菜单项,给了它一个具体的 id 和标题 (将在菜单中显示的文本)。然后又设置了一个事件侦听器,当用户点击菜单项时,检查该菜单项是否就是我们的吞页菜单项。如果是,就通过tabs.executeScript()
接口,把"page-eater.js" 注入到活动标签页中。这个接口用标签 ID 做为参数:如果省略标签 ID 参数,就默认把脚本注入当前活动标签。
现在,附加组件看起来像这样:
modify-page/ background.js manifest.json page-eater.js
重新加载WebExtension, 打开页面 (这次可以是任何一个页面) 激活右键菜单,然后选择 "Eat this page":
备注: 请注意,虽然此视频显示在 addons.mozilla.org 工作的 content scripts,但目前该网站已禁止 content scripts。
消息
内容脚本和后台脚本不能直接相互访问,但可以通过发送消息进行通信。当一端设置一个消息侦听器时,另一个端就可以发送消息了。下面的表格总结了通信时的 api 接口:
在内容脚本中 | 在后台脚本中 | |
---|---|---|
发送消息 |
browser.runtime.sendMessage()
|
browser.tabs.sendMessage()
|
接收消息 |
browser.runtime.onMessage
|
browser.runtime.onMessage
|
修改上面的示例,使得可以通过后台脚本来发送消息。
首先,修改 "background.js" 如下:
browser.contextMenus.create({
id: "eat-page",
title: "Eat this page",
});
function messageTab(tabs) {
browser.tabs.sendMessage(tabs[0].id, {
replacement: "Message from the add-on!",
});
}
browser.contextMenus.onClicked.addListener(function (info, tab) {
if (info.menuItemId == "eat-page") {
browser.tabs.executeScript({
file: "page-eater.js",
});
var querying = browser.tabs.query({
active: true,
currentWindow: true,
});
querying.then(messageTab);
}
});
注入 "page-eater.js"后,通过使用 tabs.query()
获取当前活动标签页,然后使用tabs.sendMessage()
将消息发送到该标签页中的内容脚本。该消息的内容 {replacement: "Message from the add-on!"}
。
接下来,修改 "page-eater.js" 如下:
function eatPage(request, sender, sendResponse) {
document.body.textContent = "";
var header = document.createElement("h1");
header.textContent = request.replacement;
document.body.appendChild(header);
}
browser.runtime.onMessage.addListener(eatPage);
现在,不再立即执行吞页,内容脚本将先通过使用 runtime.onMessage
来监听消息。当监听到消息时,内容脚本才开始运作,除了来自request.replacement
的替换文本不一样以外,其他的脚本运作本质上与之前的相同。
如果我们想将消息从内容脚本发送到后台页面,除了在内容脚本中使用 runtime.sendMessage()
,其他与上面的过程相反。
备注:
这些例子注入的都是 JavaScript; 想注入 CSS 可以使用 tabs.insertCSS()
函数。
了解更多
-
content_scripts
manifest key -
permissions
manifest key -
使用
content_scripts
的例子: -
使用
tabs.executeScript()
的例子: