Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
282 views
in Technique[技术] by (71.8m points)

javascript - Communicate between scripts in the background context (background script, browser action, page action, options page, etc.)

I am running into an issue sending data from my background script to the script for my pageAction. My content script adds an <iframe /> and the JavaScript in the <iframe /> is receiving the data from my background script, but it does not seem to be retrieved in my pageAction.

In my background script I have something like:

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

where senderTab.tab.id is the "sender" in onMessage Listener in my background script.

In the JavaScript loaded by the <iframe /> injected by my content script I have something like:

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

The <iframe /> receives the message exactly as expected.

I put the same JavaScript in my page_action.js, but it does not receive any data from the background script. The pageAction is activated with chrome.pageAction.show(senderTab.tab.id); before I call chrome.tabs.sendMessage(senderTab.tab.id ...

Is the HTML page attached to my pageAction not part of the same tab? Since this tabId enabled me to activate/"show" the icon, I would think the listener in the JavaScript for the pageAction should also receive from chrome.tabs.sendMessage(senderTab.tab.id ...


In my content script I use the following to send data to the background script:

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

When the content script sends the above message, the pageAction JavaScript is picking it up.


How do I get the background script to properly send data to my pageAction? I do not want to have pageAction request/poll, instead I want pageAction to just listen and receive. E.g., if the pageAction HTML it shown, it should be able to update in real time as the background page makes changes.

Question&Answers:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Communicating with a page in the background context

Pages which are open in the background context include:

Using tabs.sendMessage()(MDN) will not send a message to any of them. You would need to use runtime.sendMessage()(MDN) to send a message to them. The scope for any of them, except background pages and event pages, only exists when it is being displayed. Obviously, you can not communicate with the code when it does not exist. When the scope exists, you can communicate with any of them using:

  • Directly
    From the background context, you can directly change variables, or call functions, in another page that is also in the background context (i.e. not content scripts), after having gotten a reference to its global scope, its Window, using extension.getViews()(MDN), extension.getBackgroundPage()(MDN), or other method(MDN).
    For example, you can call a function created with function myFunction in the page of the first returned view by using something like:

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

    It should be noted that in your callback from tabs.create()(MDN) or windows.create()(MDN) the view for the newly opened tab or window will probably not yet exist. You will need to use some methodology to wait for the view to exist.2 See below for recommended ways to communicate with newly opened tabs or windows.

    Directly manipulating values in the other page's scope allows you to communicate any type of data you desire.

  • Messaging
    Receive messages using chrome.runtime.onMessage(MDN), 3 which were sent with chrome.runtime.sendMessage()(MDN). Each time you receive a message in a runtime.onMessage listener, there will be a sendResponse function provided as the third argument which allows you to directly respond to the message. If the original sender has not supplied a callback to receive such a response in their call to chrome.runtime.sendMessage(), then the response is lost. If using Promises (e.g. browser.runtime.sendMessage() in Firefox), the response is passed as an argument when the Promise is fulfilled. If you want to send the response asynchronously, you will need to return true; from your runtime.onMessage listener.

    Ports
    You can also connect ports, using chrome.runtime.connect()(MDN) and chrome.runtime.onConnect(MDN) for longer term messaging.

    Use chrome.tabs.sendMessage() to send to content scripts
    If you want to send from the background context (e.g. background script or popup) to a content script you would use chrome.tabs.sendMessage()/chrome.runtime.onMessage, or connect port(s) using chrome.tabs.connect()(MDN)/chrome.runtime.onConnect.

    JSON-serializable data only
    Using messaging, you can only pass data which is JSON-serializable.

    Messages are received by all scripts in the background, except the sender
    Messages sent to the background context are received by all scripts in the background context which have registered a listener, except the script which sent it.3 There is no way to specify that it is only to be received by a specific script. Thus, if you have multiple potential recipients, you will need to create a way to be sure that the message received was intended for that script. The ways to do so usually rely on specific properties existing in the message (e.g. use a destination or recipient property to indicate what script is to receive it, or define that some type of messages are always for one recipient or another), or to differentiate based on the sender(MDN) supplied to the message handler (e.g. if messages from one sender are always only for a specific recipient). There is no set way to do this, you must choose/create a way to do it for use in your extension.

    For a more detailed discussion of this issue, please see: Messages intended for one script in the background context are received by all

  • Data in a StorageArea
    Store data to a StorageArea(MDN) and be notified of the change in other scripts using chrome.storage.onChanged(MDN). The storage.onChanged event can be listened to in both the background context and content scripts.

    You can only store data which is JSON-serializable into a StorageArea.

Which method is best to use in any particular situation will depends on what you are wanting to communicate (type of data, state change, etc.), and to which portion, or portions, of your extension you are wanting to communicate from and to. For instance, if you want to communicate information which is not JSON-serializable, you would need to do so directly (i.e. not messaging or using a StorageArea). You can use multiple methods in the same extension.

More on popups

None of the popups (e.g. browser action, or page action) are directly associated with the active tab. There is no concept of a shared or separate instance per tab. However, the user can open one popup in each Chrome window. If more than one popup is open (a maximum of one per Chrome window), then each is in a separate instance (separate scope; has its own Window), but are in the same context. When a popup is actually visible, it exists in the background context.

There is only ever one page action or browser action popup open at a time per Chrome window. The HTML file which will be open will be whichever one has been defined fo


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

1.4m articles

1.4m replys

5 comments

57.0k users

...