Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

messaging between content script and background page in a chrome extension is not working as it is supposed to be

I post the code below:

manifest.json

{
  "manifest_version": 2,

  "name": "Demo",
  "description": "all_frames test",
  "version": "1.0",

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

  "content_scripts": [{
        "matches":    ["*://*/*"],
        "js":         ["content.js"],
        "all_frames": true
    }],

  "permissions": [
          "tabs",
          "*://*/*"
   ]
}

background.js

chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab) {
    var tabStatus = changeInfo.status;

    if (tabStatus == 'complete') {

        function return_msg_callback() {
            console.log('Got a msg from cs...')
        }

        chrome.tabs.sendMessage(tabId, {
            text: 'hey_cs'
        }, return_msg_callback);
    }

});

content.js

/* Listen for messages */
chrome.runtime.onMessage.addListener(function(msg, sender, sendResponse) {
    /* If the received message has the expected format... */
    if (msg.text && (msg.text == 'hey_cs')) {
        console.log('Received a msg from bp...')
        sendResponse('hey_bp');
    }
});

Then, if I go to a site that includes multiples cross-origin iFrames, e.g., http://www.sport.es/ you would see that all the iFrames within the page receive the message from the background page but only one of them is able to response back. Is this a normal behavior?

Thanks in advance for your answer.

like image 722
jack Avatar asked Sep 25 '15 07:09

jack


People also ask

How can background scripts and content scripts communicate?

Communication between extensions and their content scripts works by using message passing. Either side can listen for messages sent from the other end, and respond on the same channel.

How do I send data between Chrome extension scripts?

When sending a message to the content script, we need to specify which tab to send it to. Therefore, we need to retrieve the active tab information first, and then use tabs. sendMessage . To use the tabs API and to have access to the active tab, you need to add tabs and activeTab under permissions in your manifest.

How do you send a message from the background script to a popup script?

tabs. sendMessage( tab.id, youtPayload, function (response) { // do something here if you want } ); }); }); That's it!

What is the difference between content script and background script?

Background Script - Provides persistence and handles background events. Content Script - Scripts that run in isolation in the context of the web page. Injected Script - Scripts that are programmatically injected into the web page.


3 Answers

You send just one message with a direct callback so naturally Chrome can use this response callback just one time (it's a one-time connection to one entity, be it a page or an iframe).

  • Solution 1: send multiple messages to each iframe explicitly:

    manifest.json, additional permissions:

    "permissions": [
        "webNavigation"
    ],
    

    background.js

    chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab) {
        .............
        // before Chrome 49 it was chrome.webNavigation.getAllFrames(tabId, .....
        // starting with Chrome 49 tabId is passed inside an object
        chrome.webNavigation.getAllFrames({tabId: tabId}, function(details) {
            details.forEach(function(frame) {
                chrome.tabs.sendMessage(
                    tabId,
                    {text: 'hey_cs'},
                    {frameId: frame.frameId},
                    function(response) { console.log(response) }
                );
            });
        });
    });
    
  • Solution 2: rework your background script logic so that the content script is the lead in communication and let it send the message once it's loaded.

    content.js

    chrome.runtime.sendMessage({text: "hey"}, function(response) {
        console.log("Response: ", response);
    });
    

    background.js

    chrome.runtime.onMessage.addListener(function(msg, sender, sendResponse) {
        console.log("Received %o from %o, frame", msg, sender.tab, sender.frameId);
        sendResponse("Gotcha!");
    });
    
like image 88
wOxxOm Avatar answered Oct 26 '22 09:10

wOxxOm


Communicating between a content script and the background page in a Chrome extension

Content script to background page

Send info to background page

chrome.extension.sendRequest({message: contentScriptMessage});

Receive info from content script

chrome.extension.onRequest.addListener(function(request, sender) {
  console.log(request.message);
});

Background page to content script

Send info to content script

chrome.tabs.getSelected(null, function(tab) {
  chrome.tabs.sendMessage(tab.id, { message: "TEST" });
});
 

Receive info from background page

 chrome.runtime.onMessage.addListener(function(request, sender) {
  console.log(request.message);
});
like image 36
V. Sambor Avatar answered Oct 26 '22 11:10

V. Sambor


Instead of messaging, you can use executeScript for your purposes. While the callback's argument is rarely used (and I don't think many know how it works), it's perfect here:

chrome.tabs.executeScript(tabId, {file: "script.js"}, function(results) {
  // Whichever is returned by the last executed statement of script.js
  //   is considered a result.
  // "results" is an Array of all results - collected from all frames
})

You can make sure, for instance, that the last executed statement is something like

// script.js
/* ... */
result = { someFrameIdentifier: ..., data: ...};
// Note: you shouldn't do a "return" statement - it'll be an error,
//   since it's not a function call. It just needs to evaluate to what you want.

Make sure you make script.js able to execute more than once on the same context.

For a frame identifier, you can devise your own algorithm. Perhaps a URL is enough, perhaps you can use the frame's position in the hierarchy.

like image 24
Xan Avatar answered Oct 26 '22 10:10

Xan