chrome 扩展的浏览器动作、后台脚本和内容脚本之间通信的上下文和方法?

Posted

技术标签:

【中文标题】chrome 扩展的浏览器动作、后台脚本和内容脚本之间通信的上下文和方法?【英文标题】:Contexts and methods for communication between the browser action, background scripts, and content scripts of chrome extensions? 【发布时间】:2013-06-22 00:28:11 【问题描述】:

我认为 chrome 扩展程序总体上非常简单且非常强大,但总是让我感到困惑的一件事是尝试在可能运行代码的各种脚本之间进行通信。当从“default_popup”页面引用时运行的代码浏览器操作、“背景”的“脚本”属性中的代码和内容脚本。

这些类别中的脚本在什么上下文中运行,每个脚本如何与其他脚本通信?

【问题讨论】:

相关:Please Explain Background Communication with Google Chrome Extensions 【参考方案1】:

三种不同的上下文

作为 Chrome 扩展开发者,您可以区分三种不同的环境。

    扩展代码,运行在你的 Chrome 扩展进程中 Background/event页面 Browser action / page action 弹出窗口 info bar 中的页面。 其***框架是您的扩展程序中的文档的选项卡,例如options page。 Content scripts,在标签的进程中运行。 非扩展代码在标签的进程中运行 (injected by content scripts)。

请注意,非扩展页面中的 <iframe src="chrome-extension://EXTENSIONID/page.htm"> 曾经被视为案例 2(内容脚本),因为框架是在非特权选项卡进程中加载​​的。由于 out-of-process iframes 是在 Chrome 56 中为扩展程序启动的,因此这些页面由扩展程序处理,因此它们可能使用相同的全套扩展程序 API。这个change in behavior (allowing extension frames to use privileged extension APIs) is intentional。

在扩展进程中访问window 对象

因为所有扩展代码都运行在同一个进程中,所以它们可以互相访问全局window对象。这一特性并不为人所知,但它允许在同一个扩展进程中直接操作 javascript 和 DOM 对象。通常最好不要使用这种方法,而是使用message passing API。

// To access the `window` of a background page, use
var bgWindowObject = @987654332@;
// To access the `window` of an event or background page, use:
@987654333@(function(bgWindowObject) 
    // Do something with `bgWindow` if you want
);

// To access the `window` of the badge's popup page (only if it's open!!!), use
var popupWindowObject = @987654334@;

// To access the `window` of the options page (called /options.html), use
var allWindowObjects = @987654335@;
var popupWindowObjects = allWindowObjects.filter(function(windowObject) 
    return windowObject.location.pathname == '/options.html';
);
// Example: Get the `window` object of the first options page:
var popupWindowObject = popupWindowObjects[0];

为了使本节简短,我有意将代码示例限制为访问其他全局window 对象的演示。您可以使用这些方法来定义全局方法、设置全局变量、调用全局函数等。 ...只要页面是打开的。某人thought 弹出窗口的window 始终可用。这不是真的,当弹窗关闭时,全局对象就被释放了!

message passing通讯

消息通道总是有两端:发送者和接收者。 要成为接收者,请使用chrome.runtime.onMessage.addListener 方法绑定事件侦听器。这可以通过扩展代码和内容脚本来完成。

要在扩展程序中传递消息,请使用chrome.runtime.sendMessage。如果您想向另一个选项卡发送消息,请致电chrome.tabs.sendMessage。目标选项卡通过包含一个整数 (tabId) 作为其第一个参数来指定。请注意,背景页面只能向一个选项卡发送消息。要访问所有选项卡,必须为每个选项卡调用该方法。例如:

@987654341@(, function(tabs) 
    for (var i=0; i<tabs.length; i++) 
        @987654342@(tabs[i].id, "some message");
    
);

内容脚本只能调用chrome.runtime.sendMessage 向扩展代码发送消息。如果您想从一个内容脚本向另一个内容脚本发送消息,则需要一个背景/事件页面,它接收消息并将其发送到所需的选项卡。示例见this answer。

sendMessage 方法接受一个可选函数,该函数作为onMessage 事件的第三个参数接收。

chrome.runtime.onMessage.addListener(function(message, sender, sendResponse) 
    if (message === 'message') sendResponse('the response');
);
chrome.runtime.sendMessage('message', function(response) 
    console('sendResponse was called with: ' + response);
);

前面的例子显示了明显的行为。当您想要异步发送响应时,事情会变得更加棘手,例如,如果您想要执行 AJAX 请求来获取一些数据。当onMessage 函数在没有调用sendResponse 的情况下返回时,Chrome 将立即调用sendResponse。由于sendResponse只能调用一次,你会收到如下错误:

无法发送响应:如果您想在侦听器返回后发送响应,则 chrome.runtime.onMessage 侦听器必须返回 true(消息由扩展程序 EXTENSION ID HERE 发送)

按照错误提示进行操作,在 onMessage 事件监听器中添加 return true;

chrome.runtime.onMessage.addListener(function(message, sender, sendResponse) 
    setTimeout(function()  // Example: asynchronous invocation of sendResponse
        sendResponse('async response');
    , 200);
    return true;
);

我已经在本节中解释了简单的一次性消息传递的实际应用。如果您想了解更多关于长期消息通道或跨扩展消息的信息,请阅读tutorial from the official documentation。

消息传递 API 经历了多次名称更改。如果您阅读旧示例,请记住这一点。历史和兼容性说明可以在here找到。

内容脚本和页面之间的通信

可以与页面进行通信。 Apsillers 创建了一个很好的答案,解释了如何在(非扩展)页面和内容脚本之间建立通信通道。在Can a site invoke a browser extension?阅读他的回答。

与one from the documentation 相比,apsiller 方法的优势在于使用了自定义事件。文档使用window.postMessage 向页面发送消息,但这可能会导致与不期望消息事件的错误编码页面发生冲突。

【讨论】:

哦,伙计,这个答案值得接受和更多的支持..解释得很好。【参考方案2】:

Google 文档应有尽有,但很难将所有信息汇总在一起。有两种主要类型的脚本: 1.后台脚本可以完全访问Chrome api,但不能与目标网页交互。 2. 内容脚本可以相互交互,也可以与网页的 DOM(但不能与它的脚本相互隔离),但只能有限地访问 Chrome api。 两者都会在您加载新页面时运行(除非您使用“匹配”来限制内容脚本的运行位置)。

您可以通过message passing 在两者之间进行通信。内容脚本比后台脚本更容易做到这一点,因为您需要知道后者的选项卡 ID。

其他脚本(browserAction.jspageAction.jsoptionsPage.js)仅在打开相应的 html 页面时运行(就像您在浏览器窗口中打开网页一样,这就是您真正在做的事情)。它们在限制和能力上类似于背景脚本。

尽量避免与页面脚本交互的需要。我知道的最好方法是通过共享 DOM 进行交互(字面意思是在 html cmets 中编写 javascript 代码)。但是您的扩展程序的目标不是为此而设计的,因此您必须在网页中包含您自己的脚本来执行此操作。使用内容脚本将脚本元素写入文档(其srcchrome.extension.getURL("myscript.js"), 你需要有"web_accessible_resources": ["myscript.js"] 在您的清单中。

【讨论】:

【参考方案3】:

我已经有一段时间没有处理 chrome 的扩展了。我记得在我了解事情的运作方式之前,这是一场相当大的斗争。为了让您的扩展程序与浏览器进行通信,您可以轻松地使用您的 javascript/background 文件,并与您需要使用 chrome.tabs.executeScript 的网页进行通信,但这真的很棘手,而且可能会让人头疼。我建议您使用google's tour on extensions 并给他们的 api 一个非常好的俯瞰,一切都在那里!祝你好运,希望这个答案对你有所帮助! :P

【讨论】:

以上是关于chrome 扩展的浏览器动作、后台脚本和内容脚本之间通信的上下文和方法?的主要内容,如果未能解决你的问题,请参考以下文章

Chrome 扩展:在浏览器中存储文件的最佳方式(非磁盘)

在 iFrame 中激活 Zurb Foundation 的 Joyride,来自 Chrome 扩展内容脚本

Chrome 扩展:检查后台脚本是不是正在运行

chrome.runtime.onMessage多次调用

Chrome 扩展后台脚本如何与网页服务工作者通信?

chrome扩展拒绝执行内联脚本