ThePerplexedOne ThePerplexedOne - 1 month ago 6
HTML Question

Send message to background page, update the html, and then show it in a tab

I have a chrome extension where a user inputs some information and it generates a report. Now of course, this report will be different each time based on what the user has entered.

What I want to achieve, is for my extension to say:


Hey, Mr. Background page. Here's the information you need, now build some html based off what I've given you and show it to the user.


Here is the
manifest.json
that I'm using:

{
"manifest_version": 2,

"name": "XXXX",
"description": "XXXX",
"version": "1.0.0",
"permissions": ["storage", "tabs"],
"options_page": "settings.html",

"background":
{
"page": "background.html"
},

"content_scripts":
[
{
"matches": ["<all_urls>"],
"js": ["js/jquery-3.1.1.min.js", "js/bootstrap.min.js", "js/main.js", "js/background.js"],
"css": ["css/bootstrap.min.css", "css/font-awesome.min.css"]
}
],

"browser_action": {
"default_icon": "icon.png",
"default_popup": "popup.html"
},
"icons": { "128": "logo.png" }
}


Here is my
background.html


<html>
<body>
<script src="js/jquery-3.1.1.min.js"></script>
<script src="js/bootstrap.min.js"></script>
<script src="js/background.js"></script>
</body>
</html>


And here is my
background.js


$(document).ready(function() {
chrome.runtime.onMessage.addListener(function(msg, sender, sendResponse) {
$("body").html(msg.message);
console.log("Message from: " + msg.message);
});
});


Now, when the user clicks a button on my extension, I use the following code to send message:

$("#generate").click(function() {
chrome.tabs.create({url: 'background.html'});
chrome.runtime.sendMessage({
message: "Sample message."
});
});


Now what I expected to happen, was for my
background.html
page to open and then have the body of the page change based on what message was sent, but this does not work.

Any ideas?

Xan Xan
Answer

You are making a huge conceptual mistake by trying to use the same page for the background and to display something. This can lead to very unpredictable behavior.

You are essentially trying to open a tab with background.html and somehow expect it to be "that same" background page. It doesn't work like that - you're opening a new instance of the same document. Think opening the same web form in two tabs - you don't expect them to mirror text entered into fields.

On top of that, a lot of mistakes are piled on in respect to popup pages' interaction with opening tabs.

So, the plan of action:

  1. If you really need for a piece of code to execute in a (permanently invisible) background page, call it idiomatically background.js and switch to scripts-style background page definition:

    "background": {
      "scripts": ["background.js"]
    }
    

    Also, consider using Event pages.

  2. Whatever you use for report-showing should not be called background for your sanity's sake. Rename it report.html / report.js.

  3. In your popup code, your mistake #1 is timing. You open a page that ought to listen for your message, but do not wait for it to become ready to listen. You should use the callback of tabs.create if you want to make sure the page is actually open and ready.

    chrome.tabs.create({url: "report.html"}, function(tab) {
      // Okay, now it's ready, right? Right?..
      chrome.runtime.sendMessage(/*...*/);
    });
    
  4. However, doing that won't solve the problem, since opening a new tab by default focuses it (which is something you want, presumably), which immediately forces the popup to close. There is nothing you can do to prevent it as soon as a tab is focused. But that means your message is not going to be sent: popup will be destroyed before it happens.

    So, don't rely on messaging. Presumably, you can store whatever data your report is based on in storage. So, set it first, then open the report page, where you read the data and build the report.

    // Popup
    chrome.storage.local.set({foo: bar}, function() {
      // Storage is updated, it's showtime!
      chrome.tabs.create({url: "report.html"});
      // And now, we die, inevitably. Goodbye, cruel world.
    });
    
    // Report
    chrome.storage.local.get("foo", function(data) {
      // Oh, what joy is to have data.foo in here!
    });