消息传递

由于内容脚本是在网页的上下文而不是扩展程序中运行的,因此它们通常需要一些 与扩展程序其余部分的通信方式例如,RSS 阅读器扩展程序可以使用 内容脚本检测网页上是否存在 RSS Feed,然后在 为该网页显示网页操作图标。

扩展程序与其内容脚本之间的通信采用消息传递进行。两者之一 另一端可以监听从另一端发送的消息,并在同一通道上回复。消息可以 包含任何有效的 JSON 对象(null、布尔值、数字、字符串、数组或对象)。这里有一个简单的 适用于一次性请求的 API,还提供了一个更为复杂的 API,允许您长期使用 连接,以在共享的上下文中交换多条消息。还可以将 如果您知道其他附加信息的 ID,则可以向该附加信息发送一条消息,这在跨附加信息中 消息部分。

简单的一次性请求

如果您只需向扩展程序的其他部分发送一条消息(并且可以选择性地接收 响应),则应使用简化的 runtime.sendMessagetabs.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 事件处理脚本。

注意:如果多个页面都监听 onMessage 事件,则只有第一个针对特定事件调用 sendResponse() 的页面会成功发送响应。系统将忽略针对该事件的所有其他响应。
注意sendResponse 回调仅在同步使用时有效,或者当事件处理脚本返回 true 以表明它将异步响应时。如果没有处理程序返回 true,或者 sendResponse 回调函数被垃圾回收,系统会自动调用 sendMessage 函数的回调函数。

长期有效的连接

有时,持续时间超过单个请求和响应的对话十分有用。 在这种情况下,您可以打开一个从内容脚本到扩展程序页面的长期频道,或者 反之亦然,分别使用 runtime.connecttabs.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.connectruntime.connectruntime.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.onMessageExternalruntime.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.sendMessageruntime.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.onMessageExternalruntime.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 应用如何与 构建容器如需查看更多示例以及查看源代码方面的帮助,请参阅示例