将消息从后台脚本发送到内容脚本,然后发送到注入的脚本
我试图从后台页面发送消息到内容脚本,然后从内容脚本发送消息到注入的脚本。 我试过这个,但是不起作用。
这是我的代码的样子。
的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 。
- 你加载你的扩展。 它的顶级代码被执行:它试图将消息传递给当前选项卡。
- 因为那里还没有听众,所以失败了。 (这可能是
chrome://extensions/
页面,你不能注入那里) - 如果之后尝试导航/打开新选项卡,则监听器将被注入,但是顶级代码不会再被执行。
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清理所有内容脚本。 但显然情况并非如此; 内容脚本的侦听器没有被禁用。 但是,使用父扩展的任何消息都将失败。 这应该可能被认为是一个错误,并可能在某些时候被固定。 我打算把这个状态称为“孤儿”
这在两种情况下都不是问题:
- 内容脚本没有页面上事件的监听器(例如,只执行一次,或只监听来自后台的消息)
- 内容脚本不会对页面做任何事情,只会发送关于事件的背景信息。
但是,如果情况并非如此,那么您就遇到了一个问题:内容脚本可能会做某些事情,但是会失败或者干扰另一个非孤立的本身实例。
解决这个问题的方法是:
- 跟踪可以由页面触发的所有事件侦听器
- 在处理这些事件之前,请将“心跳”消息发送到后台。 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”之后工作。 所以我不能收到回应。
但是,我加了之后
-
info.status=="complete"
,status: "complete"
- 在我的manifest.json中的
"run_at": "document_end"
它工作正常