由于内容脚本是在网页的上下文而不是扩展程序中运行的,因此它们通常需要一些 与扩展程序其余部分的通信方式例如,RSS 阅读器扩展程序可以使用 内容脚本检测网页上是否存在 RSS Feed,然后在 为该网页显示网页操作图标。
扩展程序与其内容脚本之间的通信采用消息传递进行。两者之一 另一端可以监听从另一端发送的消息,并在同一通道上回复。消息可以 包含任何有效的 JSON 对象(null、布尔值、数字、字符串、数组或对象)。这里有一个简单的 适用于一次性请求的 API,还提供了一个更为复杂的 API,允许您长期使用 连接,以在共享的上下文中交换多条消息。还可以将 如果您知道其他附加信息的 ID,则可以向该附加信息发送一条消息,这在跨附加信息中 消息部分。
简单的一次性请求
如果您只需向扩展程序的其他部分发送一条消息(并且可以选择性地接收 响应),则应使用简化的 runtime.sendMessage 或 tabs.sendMessage。 这样,您就可以将一条一次性的 JSON 可序列化消息从内容脚本发送到扩展程序,或者反之。 反之。利用可选的回调参数,您可以处理 (如果有的话)。
从内容脚本发送请求如下所示:
chrome.runtime.sendMessage({greeting: "hello"}, function(response) {
console.log(response.farewell);
});
从扩展程序向内容脚本发送请求的过程非常相似,不同之处在于 指定要发送到哪个标签页。此示例演示如何向内容脚本发送消息 选中的标签页
chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
chrome.tabs.sendMessage(tabs[0].id, {greeting: "hello"}, function(response) {
console.log(response.farewell);
});
});
在接收端,您需要设置 runtime.onMessage 事件监听器来处理 消息。这与内容脚本或扩展程序页面的情况相同。
chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse) {
console.log(sender.tab ?
"from a content script:" + sender.tab.url :
"from the extension");
if (request.greeting == "hello")
sendResponse({farewell: "goodbye"});
}
);
在上面的示例中,sendResponse
是同步调用的。如果您想异步使用
sendResponse
时,请将 return true;
添加到 onMessage
事件处理脚本。
sendResponse
回调仅在同步使用时有效,或者当事件处理脚本返回 true
以表明它将异步响应时。如果没有处理程序返回 true,或者 sendResponse
回调函数被垃圾回收,系统会自动调用 sendMessage
函数的回调函数。长期有效的连接
有时,持续时间超过单个请求和响应的对话十分有用。 在这种情况下,您可以打开一个从内容脚本到扩展程序页面的长期频道,或者 反之亦然,分别使用 runtime.connect 或 tabs.connect。该频道可以 (可选)具有名称,以便区分不同类型的连接。
自动表单填写扩展程序就是一种应用场景。内容脚本可以打开一个频道, 特定登录的扩展程序页面,并在每次输入时向该扩展程序发送消息 元素来请求填写表单数据。共享连接允许该扩展程序 来保持共享状态,以关联来自内容脚本的多条消息。
建立连接时,两端会获得一个 runtime.Port 对象,用于 通过该连接收发邮件。
下面展示了如何通过内容脚本打开频道,以及发送和监听消息:
var port = chrome.runtime.connect({name: "knockknock"});
port.postMessage({joke: "Knock knock"});
port.onMessage.addListener(function(msg) {
if (msg.question == "Who's there?")
port.postMessage({answer: "Madame"});
else if (msg.question == "Madame who?")
port.postMessage({answer: "Madame... Bovary"});
});
从扩展程序向内容脚本发送请求的过程非常相似,不同之处在于 指定要连接的标签页。只需将上述示例中用于连接调用替换为 tabs.connect.
为了处理传入连接,您需要设置 runtime.onConnect 事件 监听器。这与内容脚本或扩展程序页面的情况是一样的。当您的 扩展程序调用“connect()”,则会触发此事件,同时还会触发 runtime.Port 对象 用于通过该连接收发消息。回复邮件的显示效果如下 传入的连接数:
chrome.runtime.onConnect.addListener(function(port) {
console.assert(port.name == "knockknock");
port.onMessage.addListener(function(msg) {
if (msg.joke == "Knock knock")
port.postMessage({question: "Who's there?"});
else if (msg.answer == "Madame")
port.postMessage({question: "Madame who?"});
else if (msg.answer == "Madame... Bovary")
port.postMessage({question: "I don't get it."});
});
});
端口生命周期
端口被设计为扩展程序不同部分之间的双向通信方法,其中 (顶级)帧被视为最小的部分。 调用 tabs.connect、runtime.connect 或 runtime.connectNative(一个端口)后 。此端口可立即用于通过 postMessage。
如果一个标签页中有多个帧,调用 tabs.connect 会导致多次调用 runtime.onConnect 事件(针对标签页中的每个帧一次)。同样,如果 runtime.connect 时,可能会多次触发 onConnect 事件(每次 帧)。
您可能想要了解连接何时关闭,例如,您是否正维护一个单独的 每个开放端口的状态为此,您可以监听 runtime.Port.onDisconnect 事件。本次 事件。这发生在 以下情况:
- 另一端没有 runtime.onConnect 的监听器。
- 包含相应端口的标签页会被卸载(例如,如果已浏览该标签页)。
- 调用
connect
的帧已卸载。 - 收到该端口的所有帧(通过 runtime.onConnect)均已卸载。
- runtime.Port.disconnect 由另一端调用。请注意,如果
connect
调用结果 接收器端的多个端口中,且其中的任何端口都会调用disconnect()
,然后onDisconnect
事件仅在发送方端口触发,不会在其他端口触发。
跨附加信息消息
除了在扩展程序的不同组件之间发送消息外,您还可以使用 Messaging API 与其他扩展程序进行通信。这样,您就可以公开一个 扩展程序可以利用的
监听传入请求和连接的情况与内部情况类似,不同之处在于前者使用 runtime.onMessageExternal 或 runtime.onConnectExternal 方法。这里的 :
// For simple requests:
chrome.runtime.onMessageExternal.addListener(
function(request, sender, sendResponse) {
if (sender.id == blocklistedExtension)
return; // don't allow this extension access
else if (request.getTargetData)
sendResponse({targetData: targetData});
else if (request.activateLasers) {
var success = activateLasers();
sendResponse({activateLasers: success});
}
});
// For long-lived connections:
chrome.runtime.onConnectExternal.addListener(function(port) {
port.onMessage.addListener(function(msg) {
// See other examples for sample onMessage handlers.
});
});
同样,向其他扩展程序发送消息与在扩展程序中发送消息类似。 唯一的区别是,您必须传递要进行通信的扩展程序的 ID。例如:
// The ID of the extension we want to talk to.
var laserExtensionId = "abcdefghijklmnoabcdefhijklmnoabc";
// Make a simple request:
chrome.runtime.sendMessage(laserExtensionId, {getTargetData: true},
function(response) {
if (targetInRange(response.targetData))
chrome.runtime.sendMessage(laserExtensionId, {activateLasers: true});
}
);
// Start a long-running conversation:
var port = chrome.runtime.connect(laserExtensionId);
port.postMessage(...);
从网页发送邮件
与跨附加信息消息功能类似,您的应用或扩展程序可以接收和响应 来自常规网页的邮件。要使用此功能,您必须先在 manifest.json 中指定 和您要与之通信的网站例如:
"externally_connectable": {
"matches": ["*://*.example.com/*"]
}
这样,与您指定的网址格式相匹配的任何网页都会公开 Messaging API。网址 格式必须至少包含一个二级域名,即“*”等主机名格式, “*.com”“*.co.uk”和“*.appspot.com”。在网页上,使用 runtime.sendMessage 或 runtime.connect API:向特定应用发送消息 。例如:
// The ID of the extension we want to talk to.
var editorExtensionId = "abcdefghijklmnoabcdefhijklmnoabc";
// Make a simple request:
chrome.runtime.sendMessage(editorExtensionId, {openUrlInEditor: url},
function(response) {
if (!response.success)
handleError(url);
});
在您的应用或扩展程序中,您可以通过 runtime.onMessageExternal 或 runtime.onConnectExternal API:类似于跨扩展 消息功能。只有网页可以发起连接。示例如下:
chrome.runtime.onMessageExternal.addListener(
function(request, sender, sendResponse) {
if (sender.url == blocklistedWebsite)
return; // don't allow this web page access
if (request.openUrlInEditor)
openUrl(request.openUrlInEditor);
});
原生消息传递
扩展程序和应用可以与注册为 本地消息传递主机。如需详细了解此功能,请参阅原生消息传递。
安全注意事项
内容脚本不可信
内容脚本不如扩展程序后台网页可信(例如,恶意网页) 网页可能会破坏运行内容脚本的渲染器进程)。假设 来自某个内容脚本的消息,因此请确保验证并 清理所有输入。假设发送到内容脚本的任何数据都有可能泄露到网页中。 限制可以由从内容中接收的消息触发的特权操作的范围 脚本。
跨站点脚本攻击
收到来自内容脚本或其他扩展程序的消息时,您的脚本应小心谨慎 避免成为跨站脚本攻击的受害者。此建议适用于在 扩展程序后台网页以及其他网络源内运行的内容脚本。 具体来说,请避免使用危险的 API,例如:
chrome.tabs.sendMessage(tab.id, {greeting: "hello"}, function(response) {
// WARNING! Might be evaluating an evil script!
var resp = eval("(" + response.farewell + ")");
});
chrome.tabs.sendMessage(tab.id, {greeting: "hello"}, function(response) {
// WARNING! Might be injecting a malicious script!
document.getElementById("resp").innerHTML = response.farewell;
});
相反,建议使用不运行脚本且更安全的 API:
chrome.tabs.sendMessage(tab.id, {greeting: "hello"}, function(response) {
// JSON.parse does not evaluate the attacker's scripts.
var resp = JSON.parse(response.farewell);
});
chrome.tabs.sendMessage(tab.id, {greeting: "hello"}, function(response) {
// innerText does not let the attacker inject HTML elements.
document.getElementById("resp").innerText = response.farewell;
});
示例
您可以在 examples/api/messaging 目录。原生消息传递示例演示了 Chrome 应用如何与 构建容器如需查看更多示例以及查看源代码方面的帮助,请参阅示例。