2 回答
TA贡献1893条经验 获得超10个赞
由于内容脚本的注入方式,您的脚本不起作用。
问题
当您(重新)加载扩展程序时,与某些人的预期相反,Chrome 不会将内容脚本注入到与清单中的模式匹配的现有选项卡中。只有在加载扩展后,任何导航都会检查URL以进行匹配,并将注入代码。
那么,时间表:
你打开一些标签。没有内容脚本1。
您加载您的扩展程序。它的顶级代码被执行:它尝试将消息传递给当前选项卡。
由于那里还没有听众,它失败了。(这可能是
chrome://extensions/
页面,你无论如何都不能注入)之后,如果您尝试导航/打开新选项卡,则会注入侦听器,但不再执行顶级代码。
1 - 如果您重新加载扩展程序,也会发生这种情况。如果注入了内容脚本,它将继续处理其事件/不会被卸载,但无法再与扩展进行通信。(详情见最后的附录)
解决方案
解决方案1:您可以先向选项卡询问您是否已准备好发送消息,并在静默时以编程方式注入脚本。考虑:
// Backgroundfunction 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 scriptchrome.runtime.onMessage.addListener(function(request, sender, sendResponse) { if(request.ping) { sendResponse({pong: true}); return; } /* Content script action */});
解决方案2:始终注入脚本,但要确保它只执行一次。
// Backgroundfunction 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 scriptvar 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清理所有内容脚本。但显然事实并非如此; 内容脚本的侦听器未被禁用。但是,任何具有父扩展的消息都将失败。这应该被视为一个错误,并且可能在某些时候被修复。我要打电话给这个州“孤儿”
这在以下两种情况中都不是问题:
内容脚本没有页面上事件的侦听器(例如,只执行一次,或者只侦听来自后台的消息)
内容脚本不对页面执行任何操作,仅发送有关事件的背景消息。
但是,如果情况并非如此,那么您就会遇到一个问题:内容脚本可能正在执行某些操作,但是会失败或干扰另一个非孤立的实例。
解决方法是:
跟踪页面可以触发的所有事件侦听器
在对这些事件采取行动之前,请向背景发送“心跳”消息。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; } /* ... */});
添加回答
举报