发布更新后,我正在尝试为Chrome扩展程序的用户提供流畅的体验。
我在应用程序更新时重新注入了内容脚本,即使用户在扩展程序更新后未刷新的页面上继续使用我的扩展程序,我的功能也起作用。刷新页面是理想的选择,但是我不想强迫用户使用它。
但是,在扩展程序更新之后,但在代码尝试从内容脚本/页面发送消息到后台时,页面刷新之前,我在内容脚本控制台(将内容脚本插入的页面)中收到以下错误页面:
未捕获的错误:扩展上下文无效。
是否有一种“重建”连接的方法?我尝试了长期使用的端口和常规消息传递-更新扩展具有相同的效果。
赞赏任何想法/方向。
这是我的简单消息传递代码(在内容脚本中),引发错误...但是可以与后台脚本进行通信:
chrome.runtime.sendMessage({ msg: "recordFeedback", obj: commentObj, source: source}, function(response){
console.log('Sent to DB...response was: ', response, response.recordFeedbackResponse);
});
答案 0 :(得分:4)
卸载扩展程序时,现有的内容脚本将失去与扩展程序其余部分的连接,即端口关闭,它们将无法使用runtime.sendMessage()
-但是内容脚本本身仍然可以继续工作,因为它们已被注入到页面中。
重新加载扩展程序时是相同的:那些现有的内容脚本仍将存在,但是无法发送消息;尝试这样做会导致类似您遇到的错误的错误。此外,扩展程序通常在加载时将其内容脚本注入到现有标签中(在Chrome上,Firefox会自动执行),因此最终会在给定的标签中运行多个内容脚本副本:原来的(现在已断开连接),而当前的已断开连接。
如果出现以下情况,可能会出现问题:(1)您的原始内容脚本仍在尝试与扩展的其余部分进行通信,或者(2)您的原始内容脚本执行了诸如修改DOM之类的操作,因为最终可能会导致这些更改做不止一次。
这也许就是为什么即使重新注入内容脚本后仍然出现错误:错误来自仍然存在但已断开连接的原始内容脚本。
这是some helpful background information that I found when previously researching this problem的措辞。请注意,Firefox会自动卸载内容脚本(稍后会详细介绍)。
如果您没有自动重新注入内容脚本,则可以按照上面try to get the existing script to reconnect on upgrade的评论wOxxOm。
这里有一些进一步的信息和建议,如果您注入新的内容脚本并想disable the original, disconnected, one when a new one is injected,则特别有用。
无论您是想尝试使用现有内容脚本重新连接,还是要停止对原始内容脚本进行进一步的更改并注入新的脚本,都需要检测是否已发生卸载。您可以使用端口的disconnect
处理程序来捕获扩展程序何时卸载。如果您的扩展名仅使用简单的一次性消息传递,而不使用端口,则该代码就像在您的内容脚本启动时运行以下代码一样简单(我从lydell的一些代码中首次看到了此技术)。 / p>
browser.runtime.connect().onDisconnect.addListener(function() {
// clean up when content script gets disconnected
})
On Firefox, the port's disconnect
is not fired,因为内容脚本在接收之前就已删除。这样做的优点是不需要检测(手动内容脚本注入也不需要,因为内容脚本也会自动注入),但这确实意味着没有机会重置内容脚本对DOM所做的任何更改正在运行。
答案 1 :(得分:1)
if(typeof chrome.app.isInstalled!=='undefined'){
chrome.runtime.sendMessage()
}
答案 2 :(得分:1)
在后台脚本中尝试。现在已淘汰了许多旧方法,因此我重构了代码。对于我来说,我只安装单个content_script文件。如果需要,您可以遍历chrome.runtime.getManifest().content_scripts
数组以获取所有.js文件。
chrome.runtime.onInstalled.addListener(installScript);
function installScript(details){
// console.log('Installing content script in all tabs.');
let params = {
currentWindow: true
};
chrome.tabs.query(params, function gotTabs(tabs){
let contentjsFile = chrome.runtime.getManifest().content_scripts[0].js[0];
for (let index = 0; index < tabs.length; index++) {
chrome.tabs.executeScript(tabs[index].id, {
file: contentjsFile
},
result => {
const lastErr = chrome.runtime.lastError;
if (lastErr) {
console.error('tab: ' + tabs[index].id + ' lastError: ' + JSON.stringify(lastErr));
}
})
}
});
}
答案 3 :(得分:1)
我花了几个小时阅读文档,SO,铬漏洞报告,最终才解决了所有与“扩展程序重新加载后重新连接到运行时”问题相关的错误。
此解决方案在更新/重新加载扩展程序后重新安装内容脚本,并确保旧的内容脚本不再与无效的运行时通信。
这是整个相关代码:
manifest.json
来自content_scripts.matches
的所有URL也必须包含在permissions
数组中。这是chrome.tabs.executeScript()
工作所必需的。
实际上,您可以删除content_scripts
对象(因为其唯一原因是自动注入内容脚本)并亲自处理background.js中的一切(请参见docs: "Inject Scripts")。但是我保留并使用它是出于更好的概述和“兼容性”的原因。
{
"permissions": [
"tabs",
"http://example.org",
],
"background": {
"scripts": ["background.js"],
"persistent": false
},
"content_scripts": [
{
"matches": ["http://example.org/*"],
"js": ["contentScript.js"]
}
]
}
请注意,content_scripts
和content_scripts[n].js
都是数组。
background.js
const manifest = chrome.runtime.getManifest();
function installContentScript() {
// iterate over all content_script definitions from manifest
// and install all their js files to the corresponding hosts.
let contentScripts = manifest.content_scripts;
for (let i = 0; i < contentScripts.length; i++) {
let contScript = contentScripts[i];
chrome.tabs.query({ url: contScript.matches }, function(foundTabs) {
for (let j = 0; j < foundTabs.length; j++) {
let javaScripts = contScript.js;
for (let k = 0; k < javaScripts.length; k++) {
chrome.tabs.executeScript(foundTabs[j].id, {
file: javaScripts[k]
});
}
}
});
}
}
chrome.runtime.onInstalled.addListener(installContentScript);
contentScript.js
var chromeRuntimePort = chrome.runtime.connect();
chromeRuntimePort.onDisconnect.addListener(() => {
chromeRuntimePort = undefined;
});
// when using the port, always check if valid/connected
function postToPort(msg) {
if (chromeRuntimePort) {
chromeRuntimePort.postMessage(msg);
}
}
// or
chromeRuntimePort?.postMessage('Hey, finally no errors');
答案 4 :(得分:1)
最佳答案在 Chrome 中对我不起作用。它不会在重新加载扩展程序时运行,而是在每个页面上运行,甚至在刷新时也是如此。
这是我决定实施的(在 Chrome 和 Firefox 中测试)。这会检查错误(英文字符串)是否是由在开发中重新加载扩展引起的。如果这是真的,那么它会禁止函数通过全局变量在扩展中执行导致错误的代码。它记录所有其他错误。
function isReloadExtErr(err) {
if (err.toString().includes("Extension context invalidated")) {
console.log("Extension has been reloaded, set global var");
extensionActive = false;
return true;
}
return false;
}
然后在刷新后会导致错误的函数内部(使用浏览器 API 的函数)
function someFunction() {
try {
if (!extensionActive) return;
/* code */
} catch (err) {
if (!isReloadExtErr(err)) console.error(err);
}
}
答案 5 :(得分:-5)
关闭浏览器标签并重新开始。为我工作。