繁体   English   中英

在后台上下文中的脚本之间进行通信(后台脚本,浏览器操作,页面操作,选项页面等)

[英]Communicate between scripts in the background context (background script, browser action, page action, options page, etc.)

我遇到了将数据从后台脚本发送到pageAction脚本的pageAction 我的内容脚本添加一个<iframe />和JavaScript在<iframe />从我的背景脚本接收数据,但它似乎并没有在我的被检索pageAction

在我的后台脚本中,我有类似以下内容:

chrome.tabs.sendMessage(senderTab.tab.id, 
{
   foo:bar
}); 

其中senderTab.tab.id是我的后台脚本中onMessage Listener中的“发送者”。

在内容脚本注入的<iframe />加载的JavaScript中,我有类似以下内容:

chrome.runtime.onMessage.addListener(
  function(request, sender, sendResponse) {
      console.log("received in iframe:", request);
    }   
});

<iframe />完全按预期接收消息。

我将相同的JavaScript放在了page_action.js ,但它没有从后台脚本接收任何数据。 使用chrome.pageAction.show(senderTab.tab.id);激活chrome.pageAction.show(senderTab.tab.id); 在我调用chrome.tabs.sendMessage(senderTab.tab.id ...

附加到我的pageAction的HTML页面不是同一选项卡的一部分吗? 由于此tabId使我能够激活/“显示”图标,因此我认为pageAction的JavaScript中的侦听器也应从chrome.tabs.sendMessage(senderTab.tab.id ...


在内容脚本中,我使用以下命令将数据发送到后台脚本:

chrome.runtime.sendMessage({
  foo: bar
});  

当内容脚本发送上述消息时,pageAction JavaScript会将其拾取。


如何获取后台脚本以将数据正确发送到pageAction? 我不想有pageAction请求/投票,而是希望pageAction仅监听和接收。 例如,如果显示了pageAction HTML,它应该能够在后台页面进行更改时实时更新。

在后台上下文中与页面进行通信

在后台上下文中打开的页面包括:

使用tabs.sendMessage() MDN不会向任何一个发送消息。 您将需要使用runtime.sendMessage() MDN向他们发送消息。 除背景页面和事件页面外,其中任何一个的作用域仅在显示时存在。 显然,当代码不存在时,您将无法与其进行通信。 存在作用域时,可以使用以下方法与其中任何一个进行通信:


  • 从背景上下文中,您可以在使用extension.getViews()对其全局范围(其Window )进行引用之后,直接在也位于背景上下文中的另一个页面(即,不是内容脚本)中更改变量或调用函数。 extension.getViews() MDNextension.getBackgroundPage() MDN其他方法 MDN
    例如,您可以使用以下类似的方法在返回的第一个视图的页面中调用使用function myFunction创建的function myFunction

     winViews = chrome.extension.getViews(); winViews[0].myFunction(foo); 

    应该注意的是,在您通过tabs.create() MDNwindows.create() MDN )进行的回调中,新打开的选项卡或窗口的视图可能不存在。 您将需要使用某种方法来等待视图存在。 2请参阅以下有关与新打开的选项卡或窗口进行通信的推荐方法。

    在其他页面范围内直接操作值可让您交流所需的任何类型的数据。

  • 讯息传递
    使用chrome.runtime.onMessage MDN接收消息该消息是通过chrome.runtime.sendMessage() MDN )发送的 每次您在runtime.onMessage侦听器中收到消息时,都会提供sendResponse函数作为第三个参数,使您可以直接响应消息。 如果原始发送者在调用chrome.runtime.sendMessage()未提供回调以接收此类响应,则响应将丢失。 如果使用Promises(例如Firefox中的browser.runtime.sendMessage() ),则在实现Promise时,将响应作为参数传递。 如果要异步发送响应,则需要return true;否则, return true; 从您的runtime.onMessage侦听器。

    港口
    您还可以使用chrome.runtime.connect() MDNchrome.runtime.onConnect MDN连接端口以进行长期消息传递。

    使用chrome.tabs.sendMessage()发送到内容脚本
    如果要从后台上下文(例如,后台脚本或弹出窗口) 发送到内容脚本,则可以使用chrome.tabs.sendMessage() / chrome.runtime.onMessage ,或使用chrome.tabs.connect() MDN / chrome.runtime.onConnect

    仅JSON可序列化的数据
    使用消息传递,您只能传递可JSON序列化的数据。

    除发件人外,所有后台脚本均收到消息
    发送到后台上下文的消息将由已注册侦听器的后台上下文中的所有脚本(发送脚本的脚本除外)接收。 3无法指定仅由特定脚本接收。 因此,如果您有多个潜在收件人,则需要创建一种方法来确保收到的邮件是针对该脚本的。 这样做的方式通常取决于消息中存在的特定属性(例如,使用destination属性或recipient属性来指示要接收什么脚本,或者定义某种type的消息始终用于一个或另一个收件人),或者区分基于提供给消息处理程序的sender MDN (例如,如果来自一个发件人的消息始终仅用于特定收件人)。 没有固定的方法来执行此操作,您必须选择/创建一种在扩展程序中使用的方法。

    有关此问题的更详细讨论,请参阅: 所有人都收到了在后台上下文中发送给一个脚本的消息

  • 存储区域中的数据
    将数据存储到StorageArea MDN ),并使用chrome.storage.onChanged MDN通知其他脚本中的更改。 可以在后台上下文和内容脚本中侦听storage.onChanged事件。

    您只能将可JSON序列化的数据存储到StorageArea中。

在任何特定情况下哪种方法最适合使用取决于您要传达的信息(数据类型,状态更改等),以及您要与之通信的扩展的哪个或哪些部分。 。 例如,如果您想传达不可JSON序列化的信息,则需要直接进行传达(即,不发送消息或使用StorageArea)。 您可以在同一扩展中使用多种方法。

有关弹出窗口的更多信息

没有任何弹出窗口(例如,浏览器操作或页面操作)与活动选项卡直接关联。 每个选项卡都没有共享或单独实例的概念。 但是,用户可以在每个Chrome窗口中打开一个弹出窗口。 如果打开了多个弹出窗口(每个Chrome窗口最多可以弹出一个窗口),则每个窗口都在一个单独的实例中(单独的作用域;有自己的Window),但是它们处于相同的上下文中。 当弹出窗口实际上是可见的时,它存在于后台上下文中。

每个Chrome窗口一次只能打开一个页面操作或浏览器操作弹出窗口。 将为打开的HTML文件将是为当前窗口的活动标签定义的文件, 并由用户通过单击页面/浏览器操作按钮来打开 通过使用chrome.browserAction.setPopup() MDNchrome.pageAction.setPopup() MDN并指定tabId ,可以为不同的标签分配不同的HTML文档。 可以/将破坏弹出窗口有多种原因,但是肯定是当另一个选项卡在打开弹出窗口的窗口中变为活动选项卡时。

但是,使用的任何通信方法都只会与当前处于打开状态的通信,而不会与尚未打开的通信。 如果一次打开多个Chrome窗口的弹出窗口,则它们是具有各自范围(即它们自己的Window)的单独实例。 您可以想到类似在多个标签中打开同一网页的感觉

如果您有后台脚本,则后台脚本上下文将在整个Chrome实例中保持不变。 如果您没有后台脚本,则可以在需要时创建上下文(例如,显示弹出窗口),并在不再需要时将其删除。

popups chrome.tabs.sendMessage()无法弹出窗口通信

如上所述,即使弹出窗口确实存在,它也将存在于后台上下文中。 调用chrome.tabs.sendMessage()会将消息发送到注入到tab / frame中的内容脚本 ,而不是发送到背景上下文。 因此,它不会将消息发送到非内容脚本(如弹出窗口)。

操作按钮:启用/禁用(浏览器操作)与显示/隐藏(页面操作)

调用chrome.pageAction.show() MDN只会导致显示页面操作按钮 它不会导致显示任何关联的弹出窗口 如果实际上未显示弹出窗口/选项页面/其他页面(不仅仅是按钮),则其范围不存在。 当它不存在时,显然不能接收任何消息

浏览器动作可以enable()按钮enable() MDNdisable() MDN ,而不是页面动作show() MDNhide() MDN的能力。

通过扩展程序中的HTML以编程方式打开选项卡或窗口

您可以使用tabs.create() MDNwindows.create() MDN从扩展名中打开包含HTML页面的选项卡或窗口。 但是,这两个API调用的回调都在页面DOM存在之前执行,因此在与页面相关联的任何JavaScript之前执行。 因此,您无法立即访问该页面内容创建的DOM,也无法与该页面的JavaScript进行交互。 具体来说:不会添加runtime.onMessage()侦听器,因此新打开的页面不会接收到那时发送的消息。

解决此问题的最佳方法是:

  1. 有可用的数据,以便新打开的页面可以在准备就绪时获取数据。 在开始打开页面之前,请执行以下操作:
    1. 如果源位于后台上下文中:将数据存储在发送页面的全局范围可用的变量中。 然后,打开的页面可以使用chrome.extension.getBackgroundPage()直接读取数据。
    2. 如果数据源在后台上下文或内容脚本中:将数据放入storage.local MDN )中 然后,打开的页面可以在运行JavaScript时读取。 例如,您可以使用名为messageToNewExtensionPage的键。
  2. 如果您使用的是runtime.sendMessage() ,则通过从该页面的代码发送一条消息到数据源(使用runtime.sendMessage()tabs.sendMessage() runtime.sendMessage()来启动从新打开页面的数据传输。 tabs.sendMessage()用于内容脚本源))请求数据。 然后,包含数据的脚本可以使用runtime.onMessage()提供的sendResponse (MDN)函数将数据发送回去。
  3. 等待与新打开的页面进行交互,直到至少有DOM可用为止,否则,直到运行页面的JavaScript之后才进行交互。 尽管可以在不重新打开新页面的情况下提供启动和运行的特定通知的情况下执行此操作,但是这样做更为复杂,并且仅在某些特定情况下有用(例如,您想在运行新页面中的JavaScript之前执行一些操作) 。 2

其他参考

火狐浏览器


  1. 除了一些次要的例外,例如:使用内容脚本将内容插入页面上下文。
  2. 您可以使用多种方法。 哪种方法最好取决于您在做什么(例如,何时需要根据视图中执行的代码访问视图)。 一种简单的方法就是轮询等待视图存在。 以下代码用于打开窗口:

     chrome.windows.create({url: myUrl},function(win){ //Poll for the view of the window ID. Poll every 50ms for a // maximum of 20 times (1 second). Then do a second set of polling to // accommodate slower machines. Testing on a single moderately fast machine // indicated the view was available after, at most, the second 50ms delay. waitForWindowId(win.id,50,20,actOnViewFound,do2ndWaitForWinId); }); function waitForWindowId(id,delay,maxTries,foundCallback,notFoundCallback) { if(maxTries--<=0){ if(typeof notFoundCallback === 'function'){ notFoundCallback(id,foundCallback); } return; } let views = chrome.extension.getViews({windowId:id}); if(views.length > 0){ if(typeof foundCallback === 'function'){ foundCallback(views[0]); } } else { setTimeout(waitForWindowId,delay,id,delay,maxTries,foundCallback ,notFoundCallback); } } function do2ndWaitForWinId(winId,foundCallback){ //Poll for the view of the window ID. Poll every 500ms for max 40 times (20s). waitForWindowId(winId,500,40,foundCallback,windowViewNotFound); } function windowViewNotFound(winId,foundCallback){ //Did not find the view for the window. Do what you want here. // Currently fail quietly. } function actOnViewFound(view){ //What you desire to happen with the view, when it exists. } 
  3. 从MDN

    在版本51之前的Firefox版本中,将为同一脚本发送的消息调用runtime.onMessage侦听器(例如,由后台脚本发送的消息也将由后台脚本接收)。 在那些版本的Firefox中,如果您从runtime.onMessage侦听器中无条件地调用runtime.sendMessage(),则将设置一个无限循环,该循环将最大程度地占用CPU并锁定Firefox。 如果需要从runtime.onMessage内调用runtime.sendMessage(),则需要检查sender.url属性以确认您没有在响应从同一脚本发送的消息时发送消息。 自Firefox 51起,此错误已解决。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM