Javascript 将消息从后台脚本发送到内容脚本,然后再发送到注入脚本

Javascript 将消息从后台脚本发送到内容脚本,然后再发送到注入脚本,javascript,google-chrome-extension,Javascript,Google Chrome Extension,我试图将消息从后台页面发送到内容脚本,然后将消息从该内容脚本发送到注入脚本。我试过这个,但不起作用 下面是我的代码 manifest.json { "manifest_version": 2, "name": "NAME", "description": ":D", "version": "0.0", "permissions": [ "tabs","<all_urls>" ], "content_scripts": [ {

我试图将消息从后台页面发送到内容脚本,然后将消息从该内容脚本发送到注入脚本。我试过这个,但不起作用

下面是我的代码

manifest.json

{
  "manifest_version": 2,

  "name": "NAME",
  "description": ":D",
  "version": "0.0",
  "permissions": [
    "tabs","<all_urls>"
  ],
  "content_scripts": [
    {
      "matches": ["<all_urls>"],
      "js": ["content_script.js"]
    }
  ],
  "web_accessible_resources": [
      "injected.js"
  ],
  "background":{
      "scripts":["background.js"]
  }
}
content\u script.js

chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
  chrome.tabs.sendMessage(tabs[0].id, {greeting: "hello"}, function(response){});
});
var s = document.createElement('script');
s.src = chrome.extension.getURL('injected.js');
s.onload = function(){ 
        this.parentNode.removeChild(this);
};
(document.head||document.documentElement).appendChild(s);


chrome.runtime.onMessage.addListener(
  function(request, sender, sendResponse) {
    document.dispatchEvent(new CustomEvent('Buffer2Remote', {todo: "LOL"}));
});
document.addEventListener('Buffer2Remote', function(e){
    alert(e.todo);
});
injected.js

chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
  chrome.tabs.sendMessage(tabs[0].id, {greeting: "hello"}, function(response){});
});
var s = document.createElement('script');
s.src = chrome.extension.getURL('injected.js');
s.onload = function(){ 
        this.parentNode.removeChild(this);
};
(document.head||document.documentElement).appendChild(s);


chrome.runtime.onMessage.addListener(
  function(request, sender, sendResponse) {
    document.dispatchEvent(new CustomEvent('Buffer2Remote', {todo: "LOL"}));
});
document.addEventListener('Buffer2Remote', function(e){
    alert(e.todo);
});
从第一部分“背景->内容”脚本发送消息不起作用。
我的代码有什么问题吗?

由于内容脚本的注入方式,您的脚本无法工作

问题 当您(重新)加载扩展时,与一些人期望的相反,Chrome不会将内容脚本注入到与清单中的模式匹配的现有选项卡中。只有在加载扩展后,任何导航都将检查URL是否匹配,并将插入代码

因此,时间表:

  • 你可以打开一些标签。没有内容脚本1
  • 你加载你的分机。它的顶层代码被执行:它试图将消息传递给当前选项卡
  • 因为那里还没有侦听器,所以它失败了。(这可能是
    chrome://extensions/
    页面,但无论如何都不能在那里注入)
  • 如果之后您尝试导航/打开一个新选项卡,侦听器将被注入,但顶级代码将不再执行
  • 1-如果重新加载扩展,也会发生这种情况。如果注入了内容脚本,它将继续处理其事件/不会卸载,但无法再与扩展通信。(详情见末尾的附录)

    解决 解决方案1:您可以首先询问要向其发送消息的选项卡是否准备就绪,然后在沉默时以编程方式注入脚本。考虑:

    // Background
    function ensureSendMessage(tabId, message, callback){
      chrome.tabs.sendMessage(tabId, {ping: true}, function(response){
        if(response && response.pong) { // Content script ready
          chrome.tabs.sendMessage(tabId, message, callback);
        } else { // No listener on the other end
          chrome.tabs.executeScript(tabId, {file: "content_script.js"}, function(){
            if(chrome.runtime.lastError) {
              console.error(chrome.runtime.lastError);
              throw Error("Unable to inject script into tab " + tabId);
            }
            // OK, now it's injected and ready
            chrome.tabs.sendMessage(tabId, message, callback);
          });
        }
      });
    }
    
    chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
      ensureSendMessage(tabs[0].id, {greeting: "hello"});
    });
    


    解决方案2:始终注入脚本,但确保它只执行一次。

    // Background
    function ensureSendMessage(tabId, message, callback){
      chrome.tabs.executeScript(tabId, {file: "content_script.js"}, function(){
        if(chrome.runtime.lastError) {
          console.error(chrome.runtime.lastError);
          throw Error("Unable to inject script into tab " + tabId);
        }
        // OK, now it's injected and ready
        chrome.tabs.sendMessage(tabId, message, callback);
      });
    }
    

    这比较简单,但在扩展重新加载时会比较复杂。重新加载扩展后,旧脚本仍然存在,但它不再是“您的”上下文-因此
    注入的
    将是未定义的。小心可能执行脚本两次的副作用


    解决方案3:只需在初始化时不加区分地注入内容脚本即可。只有在安全地运行同一内容脚本两次,或者在页面完全加载后运行它时,才可以安全地执行此操作

    chrome.tabs.query({}, function(tabs) {
      for(var i in tabs) {
        // Filter by url if needed; that would require "tabs" permission
        // Note that injection will simply fail for tabs that you don't have permissions for
        chrome.tabs.executeScript(tabs[i].id, {file: "content_script.js"}, function() {
          // Now you can use normal messaging
        });
      }
    }); 
    

    我还怀疑您希望它在某些操作上运行,而不是在扩展负载上运行。例如,您可以使用一个函数,并将代码包装在
    chrome.browserAction.onClicked
    侦听器中


    关于孤立内容脚本的增编 当一个扩展被重新加载时,人们会期望Chrome清理所有的内容脚本。但显然情况并非如此;未禁用内容脚本的侦听器。但是,任何带有父扩展名的消息传递都将失败。这可能会被视为一个bug,并可能在某个时候得到修复。我要称这个州为“孤儿”

    在以下两种情况下,这都不是问题:

  • 内容脚本没有页面上事件的侦听器(例如,仅执行一次,或仅侦听来自后台的消息)
  • 内容脚本对页面没有任何作用,只向背景发送有关事件的消息 然而,如果不是这样的话,那么您就有一个问题:内容脚本可能正在做一些事情,但失败或干扰了自身的另一个非孤立实例

    解决办法是:

  • 跟踪页面可以触发的所有事件侦听器
  • 在对这些事件采取行动之前,向后台发送“心跳”消息。 3a。如果背景有反应,我们很好,应该执行这个动作。 3b。如果消息传递失败,我们就成了孤儿,应该停止;忽略事件并取消注册所有侦听器
  • 代码、内容脚本:

    function heartbeat(success, failure) {
      chrome.runtime.sendMessage({heartbeat: true}, function(reply){
        if(chrome.runtime.lastError){
          failure();
        } else {
          success();
        }
      });
    }
    
    function handler() {
      heartbeat(
        function(){ // hearbeat success
          /* Do stuff */
        }, 
        function(){ // hearbeat failure
          someEvent.removeListener(handler);
          console.log("Goodbye, cruel world!");
        }
      );
    }
    someEvent.addListener(handler);
    
    背景脚本:

    chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
      if(request.heartbeat) { sendResponse(request); return; }
      /* ... */
    });    
    
    在mybackground.js中

    chrome.tabs.onUpdated.addListener(function(tabId, info, tab) {
      if (tab.url !== undefined && info.status == "complete") {
    
        chrome.tabs.query({active: true, currentWindow: true, status: "complete"}, function (tabs) {
          console.log(tabs);
          chrome.tabs.sendMessage(tabs[0].id, {greeting: "hello"}, function (response) {
            console.log(response.farewell);
          });
        });
      }
    });
    
    我的manifest.json

    "content_scripts": [
    {
      "matches": ["http://*/*", "https://*/*"],
      "js": [
        "content_script.js"
      ],
      "run_at": "document_end"
    }
    
    我的“content_sciprt.js”在“background.js”之后工作。所以我无法收到回复

    但是在我加上

  • info.status==“完成”
    状态:“完成”
  • “run\u at”:“document\u end”
    在my manifest.json中

  • 它工作正常

    您的清单中有一个输入错误,
    content\u脚本中有一个额外的逗号
    @Xan我修复了它,但仍然不起作用。实际上,现在我想我遇到了您提到的关于杀死content\u脚本上的侦听器的问题。你能解释一下吗?Chrome不允许通过executeScript“无法访问Chrome://URL”注入内容脚本。有没有其他方法注入它们?@parats不在
    chrome://
    页面中,不。你不能触摸这些。哦,所以在其他页面中,应该没有错误。对吗?如果是孤儿院,您可以简单地检查
    chrome.runtime.id
    而不是检查心跳,它将是
    未定义的。if(chrome.runtime.id==未定义)返回;