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

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

这是我的代码的样子。

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

消息发送不起作用,从第一部分,背景 – > content_script。 我的代码有什么问题吗?

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

问题

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

所以,时间表:

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

1 – 如果您重新加载您的分机也会发生这种情况。 如果注入了内容脚本,它将继续处理其事件/不会被卸载,但不能再与扩展进行通信。 (详情见最后附录)

解决scheme

解决scheme1: 您可以首先询问您发送消息的选项卡是否已准备就绪 ,并在静默时以编程方式注入脚本。 考虑:

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

解决scheme2: 总是注入一个脚本,但确保它只执行一次。

 // 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将是不确定的。 小心可能执行两次脚本的副作用。


解决scheme3: 只是在初始化时不加区分地注入内容脚本 。 如果可以安全地运行两次相同的内容脚本,或者在页面完全加载后运行,则这样做是安全的。

 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"

它工作正常