我正在开发Chrome扩展程序。我没有使用manifest.json
来匹配所有网址的内容脚本,而是在用户点击扩展程序图标时,通过调用chrome.tabs.executeScript
来懒惰地注入内容脚本。
我正在尝试避免多次执行脚本。所以我在我的内容脚本中有以下代码:
if (!window.ALREADY_INJECTED_FLAG) {
window.ALREADY_INJECTED_FLAG = true
init() // <- All side effects go here
}
问题#1 ,每次点击扩展程序图标时,这是否足以安全地呼叫chrome.tabs.executeScript
?换句话说,这是幂等的吗?
问题#2 ,chrome.tabs.insertCSS
有类似的方法吗?
似乎无法在backgroud脚本中检查内容脚本注入状态,因为它无法访问网页的DOM。我已经尝试了ping / pong方法来检查任何内容脚本实例是否存活。但这会带来设计ping超时的开销和复杂性。
问题#3 ,是后台脚本检查内容脚本注入状态的更好方法,所以每次用户点击图标时我都可以阻止调用chrome.tabs.executeScript
?
提前致谢!
答案 0 :(得分:9)
这足够安全,可以天真地每次呼叫
chrome.tabs.executeScript
点击扩展图标的时间?换句话说,就是这样 幂等?
chrome.tabs.insertCSS
是否有类似的方法?
chrome.tabs.insertCSS
没有任何包容性保护。但是再次插入相同的样式表并不会改变页面的外观,因为所有规则都具有相同的CSS特性,并且在这种情况下最后一个样式表优先。但是如果样式表与您的扩展名紧密耦合,那么您可以使用executeScript简单地注入脚本,检查它是否是第一次注入,如果是,请插入样式表(参见下面的示例)。任何更好的后台脚本方法来检查注入状态 内容脚本,所以我可以阻止调用 每次用户点击图标时都会
chrome.tabs.executeScript
chrome.tabs.sendMessage
)发送消息,如果您没有收到回复,请假设选项卡中没有内容脚本并插入内容脚本。在弹出/后台脚本中:
chrome.tabs.executeScript(tabId, {
file: 'contentscript.js',
}, function(results) {
if (chrome.runtime.lastError || !results || !results.length) {
return; // Permission error, tab closed, etc.
}
if (results[0] !== true) {
// Not already inserted before, do your thing, e.g. add your CSS:
chrome.tabs.insertCSS(tabId, { file: 'yourstylesheet.css' });
}
});
contentscript.js:
// Wrapping in a function to not leak/modify variables if the script
// was already inserted before.
(function() {
if (window.hasRun === true)
return true; // Will ultimately be passed back to executeScript
window.hasRun = true;
// rest of code ...
// No return value here, so the return value is "undefined" (without quotes).
})(); // <-- Invoke function. The return value is passed back to executeScript
注意,明确检查window.hasRun
的值(上例中的true
)非常重要,否则它可以是id="hasRun"
的DOM元素的自动创建的全局变量属性,请参阅Is there a spec that the id of elements should be made global variable?
答案 1 :(得分:4)
<强> background.js 强>
chrome.tabs.onActivated.addListener(function(activeInfo){
tabId = activeInfo.tabId
chrome.tabs.sendMessage(tabId, {text: "are_you_there_content_script?"}, function(msg) {
msg = msg || {};
if (msg.status != 'yes') {
chrome.tabs.insertCSS(tabId, {file: "css/mystyle.css"});
chrome.tabs.executeScript(tabId, {file: "js/content.js"});
}
});
});
<强> content.js 强>
chrome.runtime.onMessage.addListener(function (msg, sender, sendResponse) {
if (msg.text === 'are_you_there_content_script?') {
sendResponse({status: "yes"});
}
});
答案 2 :(得分:1)
只是对Rob的好回答的旁注。
我发现Pocket的Chrome扩展程序使用了类似的方法。在动态注入的脚本中:
if (window.thePKT_BM)
window.thePKT_BM.save();
else {
var PKT_BM_OVERLAY = function(a) {
// ... tons of code
},
$(document).ready(function() {
if (!window.thePKT_BM) {
var a = new PKT_BM;
window.thePKT_BM = a,
a.init()
}
window.thePKT_BM.save()
}
)
}