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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/17246133/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-27 07:37:01  来源:igfitidea点击:

Contexts and methods for communication between the browser action, background scripts, and content scripts of chrome extensions?

javascriptgoogle-chromegoogle-chrome-extension

提问by mowwwalker

I think chrome extensions are overall quite simple and very powerful, but one thing that always confuses me is trying to communicate between the various script that code may run in. There's the code that runs when referenced from the "default_popup" page of the browser action, the code in the "scripts" property of "background" and the content scripts.

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

In what context are scripts in these categories run, and how can each communicate with the others?

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

回答by Rob W

Three different contexts

三种不同的语境

As a Chrome extension developer, you can distinguish three different environments.

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

  1. Extension code, runs in the process of your Chrome extension
  2. Content scripts, running in the tab's process.
  3. Non-extension coderunning in the tab's process (injected by content scripts).
  1. 扩展代码,在你的 Chrome 扩展进程中运行
  2. 内容脚本,在选项卡的进程中运行。
  3. 在选项卡进程中运行的非扩展代码由内容脚本注入)。

Note that <iframe src="chrome-extension://EXTENSIONID/page.htm">in non-extension pages used to be treated like case 2 (content scripts), because the frame was loaded in an unprivileged tab process. Since out-of-process iframeswas launched for extensions in Chrome 56, these pages are handled by the extension process, and therefore they may use the same full set of extension APIs. This change in behavior (allowing extension frames to use privileged extension APIs) is intentional.

请注意,<iframe src="chrome-extension://EXTENSIONID/page.htm">在非扩展页面中,过去被视为案例 2(内容脚本),因为框架是在非特权选项卡进程中加载​​的。由于在 Chrome 56 中为扩展启动了进程外 iframe,这些页面由扩展进程处理,因此它们可能使用相同的全套扩展 API。这种行为变化(允许扩展框架使用特权扩展 API)是有意的

Accessing the windowobject within an extension process

window在扩展进程中访问对象

Because all extension code runs in the same process, they can access each other global windowobject. This feature is not well-known, but allows one to directly manipulate JavaScript and DOM objects within the same extension process. It's generally better to not use this method, but use the message passingAPIs instead.

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

// To access the `window` of a background page, use
var bgWindowObject = chrome.extension.getBackgroundPage();
// To access the `window` of an event or background page, use:
chrome.runtime.getBackgroundPage(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 = chrome.extension.getViews({type:'popup'})[0];

// To access the `window` of the options page (called /options.html), use
var allWindowObjects = chrome.extension.getViews({type:'tab'});
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];

To keep this section short, I have intentionally limited the code example to a demonstration of accessing other global windowobjects. You could use these methods to define a global method, set a global variable, call a global function, etc.
... provided that the page is open. Someone thoughtthat the popup's windowis always available. This is not true, when the popup is closed, the global object is disposed!

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

Communication by message passing

通过消息传递进行通信

A message channel always has two ends: The sender and a receiver.
To become a receiver, bind an event listener using the chrome.runtime.onMessage.addListenermethod. This can be done by extension code and content scripts.

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

To pass messages within the extension, use chrome.runtime.sendMessage. If you want to send a message to another tab, call chrome.tabs.sendMessage. The target tab is specified by including an integer (tabId) as its first argument. Note that a background page can only send a message to one tab. To reach all tabs, the method has to be called for every tab. For instance:

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

chrome.tabs.query({}, function(tabs) {
    for (var i=0; i<tabs.length; i++) {
        chrome.tabs.sendMessage(tabs[i].id, "some message");
    }
});

Content scripts can only call chrome.runtime.sendMessageto send a message to extension code. If you want to send a message from a content script to another content script, a background / event page should is needed, which takes a message and sends it to the desired tab. See this answerfor an example.

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

The sendMessagemethods accept an optional function, which is received as a third argument to the onMessageevent.

这些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);
});

The previous example shows obvious behaviour. Things get trickier when you want to send a response asynchronously, for instance if you want to perform an AJAX request to fetch some data. When the onMessagefunction returns without having called sendResponse, Chrome will immediately invoke sendResponse. Since sendResponsecan be called only once, you will receive the following error:

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

Could not send response: The chrome.runtime.onMessage listener must return true if you want to send a response after the listener returns (message was sent by extension EXTENSION ID HERE)

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

Do as the error suggest, add return true;inside your onMessage event listener:

按照错误提示,return true;在您的 onMessage 事件侦听器中添加:

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

I've explained the practical application of simple one-time message passing in this section. If you want to know more about long-lived message channels or cross-extension messaging, read the tutorial from the official documentation.

在本节中,我已经解释了简单的一次性消息传递的实际应用。如果您想了解更多有关长寿命消息通道或跨扩展消息的信息,请阅读官方文档中教程

The message passing API has undergone several name changes. Keep this in mind if you read old examples. The history and compatibility notes can be found here.

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

Communication between a content script and the page

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

It's possible to communicate with the page. Apsillers has created an excellent answer which explains how to set up a communication channel between a (non-extension) page and a content script. Read his answer at Can a site invoke a browser extension?.

可以与页面进行通信。Apsillers 创建了一个很好的答案,它解释了如何在(非扩展)页面和内容脚本之间建立通信渠道。阅读他在站点可以调用浏览器扩展吗?.

The advantage of apsiller's method over the one from the documentationis that a custom event is used. The documentation uses window.postMessageto send a message to the page, but this could cause conflict with badly coded pages which do not expect the message events.

apsiller的在方法的优势从文档中一个是用于自定义事件。该文档用于window.postMessage向页面发送消息,但这可能会导致与不期望消息事件的错误编码页面发生冲突。

回答by Teepeemm

The Google documentation has everything, but it's hard to pull all the information together. There are two main types of scripts:
1. Background scripts have full access to the Chrome api's, but can't interact with the target webpage.
2. Content scripts can interact with each other and with the webpage's DOM (but not its scripts, from which it's isolated), but have only limited access to the Chrome api's.
Both run whenever you load a new page (unless you've used "matches" to restrict where the content script runs).

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

You can communicate between the two via message passing. This is more easily done from content scripts than from background scripts because you need to know the tab's id for the latter.

您可以通过消息传递在两者之间进行通信。这比从后台脚本更容易从内容脚本完成,因为您需要知道后者的选项卡的 id。

Other scripts (browserAction.js, pageAction.js, optionsPage.js) only run when their corresponding html page is opened (as if you are opening the webpage in your browser window, which is what you're really doing). They are similar to background scripts in restrictions and abilities.

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

Try to avoid the need to interact with a page's scripts. The best way I know of is to interact through the shared DOM (literally writing javascript code inside html comments). But the target of your extension isn't designed for this, so you'll have to include you're own script that does this into the webpage. Use a content script to write the script element into the document (its srcis
chrome.extension.getURL("myscript.js"),
and you'll need to have
"web_accessible_resources": ["myscript.js"]
in your manifest.

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

回答by php_nub_qq

It's been some time since I have had to deal with chrome's extensions. I remember it was quite a struggle before I got how things worked. In order for your extension to communicate with the browser it's easy you use your javascript/background file, and to communicate with the webpage you need to use chrome.tabs.executeScriptbut that's really tricky and can be a real pain in the butt. I suggest you take google's tour on extensionsand give their api a really good overlook, everything's in there! I wish you good luck and I hope this answer has helped you! :P

自从我不得不处理 chrome 的扩展已经有一段时间了。我记得在我了解事情的运作方式之前,这是一场相当大的斗争。为了让您的扩展程序与浏览器进行通信,您可以轻松地使用 javascript/背景文件,并与您需要使用的网页进行通信,chrome.tabs.executeScript但这真的很棘手,而且可能会很麻烦。我建议你参加谷歌的扩展之旅,并给他们的 api 一个很好的忽视,一切都在那里!祝你好运,希望这个回答对你有帮助!:P