内容脚本(content_scripts)
如下是内容脚本可以实现的一些功能的例子:
- 在网页中找出未链接的URL,并将它们转换为超链接
- 增加字体大小,使文本更具有可读性
- 发现并处理 DOM 中的微格式数据
然而,内容脚本也有一些限制,它们不能 :
- 调用 chrome.* API(除了 chrome.extension 中的一部分)
- 使用所属扩展程序页面中定义的变量或函数
- 使用网页或其他内容脚本中定义的变量或函数
这些限制并不如看上去那么糟糕,内容脚本可以间接地通过与所属扩展程序交换的方式,来使用 chrome.* API、访问扩展程序数据并请求扩展程序完成操作。内容脚本也可以像所属扩展程序一样向拥有主机权限的站点发出跨站 XMLHttpRequest,另外也可以使用共享的 DOM 。有关内容脚本能做什么与不能做什么的更多内部细节,请参见执行环境。
如果你的内容脚本代码每一次都需要插入到网页中,在扩展程序的清单文件中使用 content_scripts 字段注册它,如以下例子所示:
如果只是有时需要插入代码,应该使用 字段,如以编程方式插入部分所述。
使用 content_scripts 字段,扩展程序可以向一个页面中插入多个内容脚本,每个内容脚本可以有多个 JavaScript 和 CSS 文件,content_scripts 数组中的每一个项目可以包含如下属性:
如果页面的 URL 匹配任何 matches 指定的表达式以及任何 include_globs 指定的表达式,并且不匹配 exclude_matches 和 exclude_globs 指定的表达式,则会在页面中插入内容脚本。因为 matches 属性是必需的,exclude_matches、include_globs 和 exclude_globs 只能用来限制受到影响的页面。
例如,假设 matches 为 [“
- 如果 exclude_matches 为 [“*://*/*business*”],那么内容脚本会插入“http://www.nytimes.com/health”,但不会插入“http://www.nytimes.com/business”。
- 如果 include_globs 为 [“*nytimes.com/???s/*“],那么内容脚本会插入“
- 如果 exclude_globs 为 [“*science*“],那么内容脚本会插入“http://www.nytimes.com”,但不会插入“http://science.nytimes.com”或“http://www.nytimes.com/science”。
范围(glob)属性与匹配表达式相比遵循不同并且更灵活的语法。所有含有“通配符”星号和问号的 URL 都是可接受的范围字符串,星号(*)匹配任意长度的字符串(包括空字符串),问号(?)匹配任意单个字符。
例如,范围 ““ 匹配以下任一 URL:
但是不匹配如下 URL:
以编程方式插入
要向页面中插入代码,您的扩展程序必须拥有该页面的,并且还需要用到 chrome.tabs 模块。您可以使用清单文件中的 permissions 字段获得这些权限。
一旦您拥有了相应的权限,您可以通过调用 向页面插入 JavaScript 代码。要插入 CSS 代码,请使用 tabs.insertCSS。
下列代码(来自 例子)当用户单击浏览器按钮时向当前标签页插入并执行 JavaScript 代码。
chrome.tabs.executeScript({
code: 'document.body.style.backgroundColor="red"'
});
});
"permissions": [
"activeTab"
],
当浏览器正在显示一个 HTTP 页面,并且用户单击该扩展程序的浏览器按钮时,扩展程序将页面的 bgcolor 属性设为’red’。结果,除非页面通过 CSS 设置了背景颜色,页面将变成红色。
通常,您不会直接插入代码(如前面的例子所示),而是将代码放在一个文件中。您可以像这样插入文件的内容:
chrome.tabs.executeScript(null, {file: "content_script.js"});
内容脚本在一个称为隔离环境的特殊环境中执行。它们可以访问所在页面的 DOM,但是不能访问当前页面创建的任何 JavaScript 变量或函数。对于每个内容脚本来说,就像没有其他 JavaScript 在当前页面上执行。反过来也是如此:在当前页面运行的 JavaScript 不能调用或访问任何内容脚本定义的函数或变量。
例如,考虑如下简单页面:
假设如下内容脚本插入到了 hello.html 中:
var greeting = "您好啊,";
var button = document.getElementById("mybutton");
button.person_name = "Roberto";
button.addEventListener("click", function() {
alert(greeting + button.person_name + "。");
现在,如果按钮按下,您将会看到两条问候。
隔离环境允许每一个内容脚本更改自己的 JavaScript 环境,而不用担心是否会与页面或其他内容脚本发生冲突。例如,一个内容脚本可以包含 JQuery v1 而页面可以包含 JQuery v2,并且它们互不影响。
网页与扩展程序之间共享的 JavaScript 对象值得注意,例如 window.onload
事件。每一个隔离环境拥有该对象自己的副本,对该对象赋值只影响这一独立的副本。例如,网页和扩展程序都可以给 window.onload
赋值,但是都不能读取另外一方的事件处理器。事件处理器将按照它们赋值的顺序调用。
与嵌入的页面通信
尽管内容脚本的执行环境和所在页面是互相隔离的,但是它们都可以访问页面的 DOM。如果页面想要和内容脚本通信(或者通过内容脚本与扩展程序通信),必须通过共享的 DOM 进行。
可以使用 window.postMessage(或者 window.webkitPostMessage 用于可传输(Transferable)的对象):
var port = chrome.runtime.connect();
window.addEventListener("message", function(event) {
// 我们只接受来自我们自己的消息
if (event.source != window)
if (event.data.type && (event.data.type == "FROM_PAGE")) {
console.log("内容脚本接收到:" + event.data.text);
port.postMessage(event.data.text);
}
document.getElementById("theButton").addEventListener("click",
function() {
window.postMessage({ type: "FROM_PAGE", text: "来自网页的问候!" }, "*");
}, false);
在上面的例子中,example.html(不是扩展程序的一部分)向自己发送消息,由内容脚本截获并检查,然后发送至扩展程序进程。通过这种方式,页面建立了与扩展程序之间的通信,通过类似的方式反过来也是可能的。
当您编写内容脚本时,您应该注意两个安全问题。首先,注意不要向您插入内容脚本的网站引入安全隐患。例如,如果您的内容脚本从另一个网站接收内容(例如通过发出 ),一定要注意把内容插入当前页面前过滤可能的跨站脚本攻击。例如,首选 innerText 而不是 innerHTML 插入内容。当您在 HTTPS 页面上获取来自 HTTP 的内容时要特别小心,因为如果用户在不安全的网络环境中,HTTP 内容可能会因为网络中的而遭到破坏。
第二,尽管在隔离环境中运行您的内容脚本提供了某些保护,但是如果您不加区分地使用来自网页的内容,恶意网页仍然可能攻击您的内容脚本。例如,以下形式是危险的:
var elmt_id = ...
// 警告!elmt_id可能为"); …恶意脚本… //"!
window.setTimeout("animate(" + elmt_id + ")", 200);
因此,改用更安全的不运行脚本的 API:
var data = document.getElementById("json-data")
// JSON.parse 不会运行攻击者的脚本。
var parsed = JSON.parse(data);
var elmt_id = ...
// 闭包形式的 setTimeout 不会运行脚本。
window.setTimeout(function() {
}, 200);
引用扩展程序的文件
使用 获得扩展程序的文件 URL,您可以像任何其他 URL 一样使用获得的结果,如以下代码所示。
一个简单的通过消息通信的例子在Message Timer中,有关以编程方式插入的例子请参见和Email this page (by Google))。
视频
以下视频(英文)讨论了内容脚本的重要概念
第一个视频描述了内容脚本和隔离环境。