Javascript chrome扩展中的内容脚本和背景页面之间的消息传递无法正常工作

Javascript chrome扩展中的内容脚本和背景页面之间的消息传递无法正常工作,javascript,google-chrome-extension,Javascript,Google Chrome Extension,我把代码贴在下面: manifest.json { "manifest_version": 2, "name": "Demo", "description": "all_frames test", "version": "1.0", "background": { "scripts": ["background.js"] }, "content_scripts": [{ "matches": ["*://*/*"],

我把代码贴在下面:

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);
    }

});
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) }
            );
        });
    });
});
chrome.runtime.onMessage.addListener(function(msg, sender, sendResponse) {
    console.log("Received %o from %o, frame", msg, sender.tab, sender.frameId);
    sendResponse("Gotcha!");
});
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');
    }
});
chrome.runtime.sendMessage({text: "hey"}, function(response) {
    console.log("Response: ", response);
});
然后,如果我访问一个包含多个跨源iFrame的站点,例如,您会看到页面中的所有iFrame都从后台页面接收消息,但只有一个iFrame能够响应。这是正常的行为吗


提前感谢您的回答。

您只发送了一条带有直接回调的消息,因此Chrome自然可以只使用此响应回调一次(它是与一个实体的一次性连接,无论是页面还是iframe)

  • 解决方案1:向每个iframe显式发送多条消息:

    manifest.json,其他权限:

    "permissions": [
        "webNavigation"
    ],
    
    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);
        }
    
    });
    
    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) }
                );
            });
        });
    });
    
    chrome.runtime.onMessage.addListener(function(msg, sender, sendResponse) {
        console.log("Received %o from %o, frame", msg, sender.tab, sender.frameId);
        sendResponse("Gotcha!");
    });
    
  • 解决方案2:修改后台脚本逻辑,使内容脚本成为通信的主导,并让它在加载消息后发送消息

    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');
        }
    });
    
    chrome.runtime.sendMessage({text: "hey"}, function(response) {
        console.log("Response: ", response);
    });
    
    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);
        }
    
    });
    
    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) }
                );
            });
        });
    });
    
    chrome.runtime.onMessage.addListener(function(msg, sender, sendResponse) {
        console.log("Received %o from %o, frame", msg, sender.tab, sender.frameId);
        sendResponse("Gotcha!");
    });
    

    • 您可以根据自己的目的使用,而不是发送消息。虽然回调的参数很少使用(我想很多人都不知道它是如何工作的),但它在这里非常完美:

      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
      })
      
      例如,您可以确保最后执行的语句类似于

      // 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.
      
      确保
      script.js
      能够在同一上下文中执行多次

      对于帧标识符,您可以设计自己的算法。也许一个URL就足够了,也许您可以在Chrome扩展中使用。

      在内容脚本和背景页面之间进行通信 背景页的内容脚本 将信息发送到后台页面 从内容脚本接收信息 背景页到内容脚本 将信息发送到内容脚本 从后台页面接收信息
      我不明白您提出的第一个解决方案将如何解决这种情况,因为我只在页面完全加载后才向内容脚本发送消息,这意味着,可以肯定,内容脚本已加载到页面中。顺便说一句,我测试了你提出的解决方案,它没有改变任何东西。关于你的第二个解决方案,我不在乎iFrame加载需要多少时间;实际上,如果您运行问题中发布的示例代码,您永远不会收到回复。否则,你的建议不适合问题中描述的我想要做的事情。这是因为我们希望在内容脚本中执行某些操作,而不是在加载内容脚本时执行,而是在页面加载完成时执行,即,当我们收到tabs.onUpdated status已完成的消息时。这意味着所有iFrame都已加载。@wOxxOm您运行的是哪个版本的chrome?就我而言,我运行的是45.0.2454.99(64位)即使从后台页面向内容脚本发送了多条消息,我也只能在后台控制台中看到一条从内容脚本返回到后台页面的响应,并且我在网页开发人员工具控制台窗口中看到了这些日志。
      getAllFrames
      的签名已更改。现在它需要是:
      chrome.webNavigation.getAllFrames({tabId:tabId},函数(详细信息)…
      。至于这是否是正常情况,请引用文档中的一句话:如果多个页面正在侦听onMessage事件,则只有第一个调用sendResponse()的页面因为一个特定事件将成功发送响应。该事件的所有其他响应都将被忽略。@Xan所以这里我们讨论的是一个页面中的所有iFrame…所以根据文档,所有iFrame都必须响应。否?所以我想知道的是,如果一个iFrame被视为一个页面?不指定帧ID,所有帧都是sepa对消息的“接收者”进行评级。对于每条消息,只能做出一次回复。我们不能将frameId作为帧标识符吗?我认为内容脚本无法以其他Chrome API使用的格式找到
      frameId
      。但我不是100%确定。“内部背景脚本”这些是反向的吗?