繁体   English   中英

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

[英]Sending message from a background script to a content script, then to a injected script

我正在尝试将消息从后台页面发送到内容脚本,然后将该内容脚本中的消息发送到注入的脚本。 我试过这个,但它没有用。

这是我的代码的样子。

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

background.js

chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
  chrome.tabs.sendMessage(tabs[0].id, {greeting: "hello"}, function(response){});
});

content_script.js

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

injected.js

document.addEventListener('Buffer2Remote', function(e){
    alert(e.todo);
});

消息发送在第一部分background - > content_script中不起作用。 我的代码有什么问题吗?

由于内容脚本的注入方式,您的脚本不起作用。

问题

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

那么,时间表:

  1. 你打开一些标签。 没有内容脚本1
  2. 您加载您的扩展程序。 它的顶级代码被执行:它尝试将消息传递给当前选项卡。
  3. 由于那里还没有听众,它失败了。 (这可能是chrome://extensions/ page,你无论如何都不能注入)
  4. 之后,如果您尝试导航/打开新选项卡,则会注入侦听器,但不再执行顶级代码。

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

// Content script
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
  if(request.ping) { sendResponse({pong: true}); return; }
  /* Content script action */
});

解决方案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);
  });
}

// Content script
var injected;

if(!injected){
  injected = true;
  /* your toplevel code */
}

这更简单,但在扩展重新加载方面存在复杂性。 扩展重新加载后,旧脚本仍然存在1,但它不是“你的”上下文了-所以injected将是不确定的。 注意可能两次执行脚本的副作用。


解决方案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清理所有内容脚本。 但显然事实并非如此; 内容脚本的侦听器未被禁用。 但是,任何具有父扩展的消息都将失败。 这应该被视为一个错误,并且可能在某些时候被修复。 我要打电话给这个州“孤儿”

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

  1. 内容脚本没有页面上事件的侦听器(例如,只执行一次,或者只侦听来自后台的消息)
  2. 内容脚本不对页面执行任何操作,仅发送有关事件的背景消息。

但是,如果情况并非如此,那么您就会遇到一个问题:内容脚本可能正在执行某些操作,但是会失败或干扰另一个非孤立的实例。

解决方法是:

  1. 跟踪页面可以触发的所有事件侦听器
  2. 在对这些事件采取行动之前,请向背景发送“心跳”消息。 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; }
  /* ... */
});    

在我的background.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”之后工作。 所以我无法收到回复。

但是在我补充之后

  1. info.status=="complete"status: "complete"
  2. 我的manifest.json中的"run_at": "document_end"

它工作正常

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM