Forivin
Forivin

Reputation: 15508

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

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?

Upvotes: 7

Views: 3654

Answers (3)

rsanchez
rsanchez

Reputation: 14657

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.

Upvotes: 2

Forivin
Forivin

Reputation: 15508

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"]
  }
}

Upvotes: 1

Daniel Lane
Daniel Lane

Reputation: 2593

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.

Upvotes: 1

Related Questions