Browser extensions: Send messages (with response) between browser-action-popup and background-script
Asked Answered
S

3

7

First of all I'm not looking for Long Lived Connections. I'm specifically looking for a way to send messages and send direct responses to these messages.

When you send messages between a content-script and a background-script it's pretty straight forward, as you use the chrome.tabs API to send/receive messages from/to the content-script. And to send/receive messages from/to the background-script you use the chrome.runtime API.

But with browser-action-popups it is a bit different because both are running in a background-context. So I suppose they both have to use the chrome.runtime API.

But that would mean I'd have to listen to chrome.runtime.onMessage in both my browser-action-popup and in my background-script. So basically I would receive message sent from the popup in the background-script, but also in the popup itself. And the other way around it would be the same.

So yeah, this wouldn't really work:

/////////////background-script//////////////
//Send message from background-script to browser-action-popup
chrome.runtime.sendMessage({msg:"This is a message sent from the background-script to the browser-action-popup"})
  .then(response => { //Receive response from the browser-action-popup
      console.log(response.msg)
  })

//Receive messages from browser-action-popup
chrome.runtime.onMessage.addListener(function(message,sender,sendResponse) {
    sendResponse({msg:"This is a response message sent from the background-script"})
    return true
})

...

///////////browser-action-popup/////////////
//Send message from browser-action-popup to background-script
chrome.runtime.sendMessage({msg:"This is a message sent from the browser-action-popup to the background-script"})
  .then(response => { //Receive response from the background-script
      console.log(response.msg)
  })

//Receive message from background-script
chrome.runtime.onMessage.addListener(function(message,sender,sendResponse) {
    sendResponse({msg:"This is a response message sent from the browser-action-popup"})
    return true
})

But since they both run in a background context I was also wondering is there isn't a simpler way than sending messages: Would it be possible to share variables between the two or are they running completely isolated?

Scarron answered 27/1, 2017 at 13:55 Comment(6)
Possible duplicate of Communicate between scripts in the background context (background script, browser action, page action, options page, etc.)Moritz
There is a bug in (now older versions of) Firefox that results in runtime.sendMessage() is triggering its own runtime.onMessage listener. This bug exists in Firefox 50.1.0 (now old). In my testing, this does not happen in Firefox 51.0.1 (now the current release version of Firefox).Moritz
FYI: I have updated the runtime.onMessage to include a note regarding this bug and the fact that it can result in locking up Firefox (if you unconditionally call runtime.sendMessage() from within a runtime.onMessage listener). You may need to reload the page using Ctrl-F5 to see the change.Moritz
How is this not a duplicate of the proposed dup-target? You claim that it is because you are "not looking for Long Lived Connections", but that question/answer only mentions connect/onConnect as one of the various methods of communicating, and mostly concentrated on the multiple other methods using exactly the same methods described in this question/answer (sendMessage()/onMessage). In other words, that question/answer is a superset of what you are asking here. It covers all the methods of communicating in the background context, not the limited subset discussed here.Moritz
Oh, you are right, I confused that question with another one that only had an answer containing information on long lived connections.Scarron
Possible duplicate of Communicate between scripts in the background context (background script, browser action, page action, options page, etc.)Scarron
R
2

From the docs:

If sending to your extension, the runtime.onMessage event will be fired in every frame of your extension (except for the sender's frame)

So you don't have to worry for a message from the popup triggering the onMessage event on the popup.

You also ask for another mean of communication by sharing variables. From the popup you can call chrome.runtime.getBackgroundPage, that to get the JavaScript window object for the background page. And from the background page you can call chrome.extension.getViews with {type: 'popup'} to access the window object for the popup, if it is open.

The Firefox documentation for getBackgroundPage states:

This provides a convenient way for other privileged add-on scripts to get direct access to the background script's scope. This enables them to access variables or call functions defined in that scope. "Privileged script" here includes scripts running in options pages, or scripts running in browser action or page action popups, but does not include content scripts.

Remunerative answered 27/1, 2017 at 15:19 Comment(4)
Well, the docs are definitely wrong then. At least in Firefox. Both the popup and the background script are triggering their own onMessage. From what I remember it does work in content scripts though, but I'm not 100% sure.Scarron
@Scarron The docs I cited are for Chrome, I didn't see the firefox tag. Now you know a difference between both :) For the second part of the answer, getBackgroundPage and getViews seems to exist on Firefox too, according to the docsRemunerative
getViews and getBackgroundPage would only allow me to communicate through the DOM though, right?Scarron
Okay that's amazing! This will make everything soo much easier.Scarron
S
1

This is an easy one, just give your messages a type or similar, some kind of message identifier. I've called it type in this example.

/////////////background-script//////////////
//Send message from background-script to browser-action-popup
var msg = {
    data : "This is a message sent from the background-script to the browser-action-popup",
    type : "notifyPopupOfMessage"
};

chrome.runtime.sendMessage(msg)
.then(response =  > { //Receive response from the browser-action-popup
        console.log(response.data);
    })

//Receive messages from browser-action-popup
chrome.runtime.onMessage.addListener(function (message, sender, sendResponse) {
    if (message.type === 'notifyBackgroundOfMessage') {
        var msg = {
            data : "This is a response message sent from the background-script",
            type: 'notifyPopupOfMessage'
        };

        sendResponse(msg);
    }

    return true
});

.....

///////////browser-action-popup/////////////
//Send message from browser-action-popup to background-script

var msg = {
    data: 'This is a message sent from the browser-action-popup to the background-script',
    type: 'notifyBackgroundOfMessage'
};

chrome.runtime.sendMessage(msg)
  .then(response => { //Receive response from the background-script
      console.log(response.data);
  });

//Receive message from background-script
chrome.runtime.onMessage.addListener(function(message,sender,sendResponse) {
    if(message.type === 'notifyPopupOfMessage') {
        var msg = {
            data:"This is a response message sent from the browser-action-popup",
            type: 'notifyBackgroundOfMessage'
        };
        sendResponse(msg);
    }

    return true
});

Hope that helps.

Saw answered 27/1, 2017 at 14:47 Comment(1)
You could also detect the sender by checking sender.url. Note that this is detecting the sender, not the intended recipient.Moritz
S
1

Although Daniel Lane's answer would theoretically work, I'm not a fan of using the msg object to identify the sender. I actually found a neat way to filter out messages sent from the same "page" (backgorund, popup, etc).
I've done it by identifying the current page url and comparing it to the sender.url . Here is my whole code:

popup.js

const THIS_PAGE_URL = chrome.runtime.getURL('popup.html')

//Send message from browser-action-popup to background-script
setTimeout(function(){

    chrome.runtime.sendMessage({msg:"This is a message sent from the browser-action-popup to the background-script"})
      .then(response => { //Receive response from the background-script
          if (!response) {
              console.log("Popup sent a msg and received no response.")
              return
          }
          document.body.innerHTML += "<br>Popup sent a msg and received a response: " + response.msg
      })

}, 3000)


//Receive message from background-script
chrome.runtime.onMessage.addListener(function(message,sender,sendResponse) {
    if (sender.url === THIS_PAGE_URL)
        return
    document.body.innerHTML += "<br>Popup received a new msg: " + message.msg
    sendResponse({msg:"This is a response message sent from the browser-action-popup"})
    return true
})

background-script.js

const THIS_PAGE_URL = chrome.runtime.getURL('_generated_background_page.html')

//Send message from background-script to browser-action-popup
setTimeout(function(){

    chrome.runtime.sendMessage({msg:"This is a message sent from the background-script to the browser-action-popup"})
      .then(response => { //Receive response from the browser-action-popup
          if (!response) {
              console.log("Background-script sent a msg and received no response.")
              return
          }
          console.log("Background-script sent a msg and received a response: " + response.msg)
      })

},3000)


//Receive messages from browser-action-popup
chrome.runtime.onMessage.addListener(function(message,sender,sendResponse) {
    if (sender.url === THIS_PAGE_URL)
        return
    console.log("Background-script received a new msg: " + message.msg)
    sendResponse({msg:"This is a response message sent from the background-script"})
    return true
})

And for those who are interested here are the remaining files:

popup.html

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
  </head>
  <body>
    <script src="popup.js"></script>
  </body>
</html>

manifest.json

{
  "manifest_version": 2,
  "name": "popup-background-msg-example",
  "version": "1.0",

  "browser_action": {
    "browser_style": true,
    "default_title": "popup-background-msg-example",
    "default_popup": "popup.html"
  },

  "background": {
    "scripts": ["background-script.js"]
  }
}
Scarron answered 27/1, 2017 at 15:21 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.