chrome.runtime.sendMessage 在 运行 正常时第一次点击不工作。它在调试时有效
chrome.runtime.sendMessage not working on the 1st click when running normally. it works while debugging though
我在 context.js 中有一个函数,它加载面板并在最后向 panel.js 发送消息。 panel.js 函数在收到该消息时更新 ui。但它不适用于第一次点击,即它只是正常加载 ui,而不是预期的在收到消息后更新的那个。调试时它工作正常。
manifest.json
"background": {
"scripts": ["background.js"],
"persistent": false
},
"content_scripts": [{
"all_frames": false,
"matches": ["<all_urls>"],
"js":["context.js"]
}],
"permissions": ["activeTab","<all_urls>", "storage","tabs"],
"web_accessible_resources":
"panel.html",
"panel.js"
]
context.js - 代码
fillUI (){
var iframeNode = document.createElement('iframe');
iframeNode.id = "panel"
iframeNode.style.height = "100%";
iframeNode.style.width = "400px";
iframeNode.style.position = "fixed";
iframeNode.style.top = "0px";
iframeNode.style.left = "0px";
iframeNode.style.zIndex = "9000000000000000000";
iframeNode.frameBorder = "none";
iframeNode.src = chrome.extension.getURL("panel.html")
document.body.appendChild(iframeNode);
var dataForUI = "some string data"
chrome.runtime.sendMessage({action: "update UI", results: dataForUI},
(response)=> {
console.log(response.message)
})
}
}
panel.js - 代码
var handleRequest = function(request, sender, cb) {
console.log(request.results)
if (request.action === 'update Not UI') {
//do something
} else if (request.action === 'update UI') {
document.getElementById("displayContent").value = request.results
}
};
chrome.runtime.onMessage.addListener(handleRequest);
background.js
chrome.runtime.onMessage.addListener((request,sender,sendResponse) => {
chrome.tabs.sendMessage(sender.tab.id,request,function(response){
console.log(response)`
});
});
panel.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="panel.css" />
</head>
<body>
<textarea id="displayContent" rows="10" cols="40"></textarea>
</body>
</html>
关于我做错了什么或我可以做什么的任何建议?
一种对我有用的可能方法是使用 setTimeout()
方法中的功能。
在context.js
setTimeout(() => {
chrome.runtime.sendMessage({action: "update UI", results: dataForUI},
(response)=> {
console.log(response.message)
}
)
}, 100);
但我不确定这是否是最好的方法。
具有真实 URL 的 iframe 异步加载,因此其代码在嵌入代码完成后运行 - 因此,您的消息发送得太早而丢失。 URL 在你的例子中指向一个扩展资源,所以它是一个真正的 URL。作为参考,同步加载的 iframe 将有一个虚拟 URL 例如根本没有 src
(或空字符串),或者它会像 about:blank
或 javascript:/*some code here*/
,也可能是 srcdoc
。
解决方案 1:在 iframe 的 onload
事件中发送消息
可能的缺点:所有选项卡中的所有扩展框架都会收到它,包括后台脚本和任何其他打开的扩展页面,如弹出窗口、选项,如果它们也有一个 onMessage 侦听器。
iframeNode.onload = () => {
chrome.runtime.sendMessage('foo', res => { console.log(res); });
};
document.body.appendChild(iframeNode);
解决方案 2:让 iframe 向其嵌入器发送消息
可能的缺点:如果您在一个选项卡中添加多个这样的扩展框架,例如由于浏览器中的错误或优化,第二个比第一个加载得早,可能会发送错误的数据 - 在这种情况下您可能必须使用直接 DOM 消息传递(解决方案 3)。
iframe 脚本 (panel.js):
chrome.tabs.getCurrent(ownTab => {
chrome.tabs.sendMessage(ownTab.id, 'getData', data => {
console.log('frame got data');
// process data here
});
});
内容脚本(context.js):
document.body.appendChild(iframeNode);
chrome.runtime.onMessage.addListener(
function onMessage(msg, sender, sendResponse) {
if (msg === 'getData') {
chrome.runtime.onMessage.removeListener(onMessage)
sendResponse({ action: 'update UI', results: 'foo' });
}
});
解决方案 3:通过 postMessage
直接发送消息
在一个选项卡中有多个扩展框架的情况下使用。
缺点:无法判断消息是由页面还是由其他扩展程序的内容脚本伪造的。
iframe 脚本为 message
事件声明了一个 one-time 侦听器:
window.addEventListener('message', function onMessage(e) {
if (typeof e.data === 'string' && e.data.startsWith(chrome.runtime.id)) {
window.removeEventListener('message', onMessage);
const data = JSON.parse(e.data.slice(chrome.runtime.id.length));
// process data here
}
});
然后,另外,使用以下之一:
如果内容脚本是发起者
iframeNode.onload = () => {
iframeNode.contentWindow.postMessage(
chrome.runtime.id + JSON.stringify({foo: 'data'}), '*');
};
document.body.appendChild(iframeNode);
如果 iframe 是发起者
iframe 脚本:
parent.postMessage('getData', '*');
内容脚本:
document.body.appendChild(iframeNode);
window.addEventListener('message', function onMessage(e) {
if (e.source === iframeNode) {
window.removeEventListener('message', onMessage);
e.source.postMessage(chrome.runtime.id + JSON.stringify({foo: 'data'}), '*');
}
});
我在 context.js 中有一个函数,它加载面板并在最后向 panel.js 发送消息。 panel.js 函数在收到该消息时更新 ui。但它不适用于第一次点击,即它只是正常加载 ui,而不是预期的在收到消息后更新的那个。调试时它工作正常。
manifest.json
"background": {
"scripts": ["background.js"],
"persistent": false
},
"content_scripts": [{
"all_frames": false,
"matches": ["<all_urls>"],
"js":["context.js"]
}],
"permissions": ["activeTab","<all_urls>", "storage","tabs"],
"web_accessible_resources":
"panel.html",
"panel.js"
]
context.js - 代码
fillUI (){
var iframeNode = document.createElement('iframe');
iframeNode.id = "panel"
iframeNode.style.height = "100%";
iframeNode.style.width = "400px";
iframeNode.style.position = "fixed";
iframeNode.style.top = "0px";
iframeNode.style.left = "0px";
iframeNode.style.zIndex = "9000000000000000000";
iframeNode.frameBorder = "none";
iframeNode.src = chrome.extension.getURL("panel.html")
document.body.appendChild(iframeNode);
var dataForUI = "some string data"
chrome.runtime.sendMessage({action: "update UI", results: dataForUI},
(response)=> {
console.log(response.message)
})
}
}
panel.js - 代码
var handleRequest = function(request, sender, cb) {
console.log(request.results)
if (request.action === 'update Not UI') {
//do something
} else if (request.action === 'update UI') {
document.getElementById("displayContent").value = request.results
}
};
chrome.runtime.onMessage.addListener(handleRequest);
background.js
chrome.runtime.onMessage.addListener((request,sender,sendResponse) => {
chrome.tabs.sendMessage(sender.tab.id,request,function(response){
console.log(response)`
});
});
panel.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="panel.css" />
</head>
<body>
<textarea id="displayContent" rows="10" cols="40"></textarea>
</body>
</html>
关于我做错了什么或我可以做什么的任何建议?
一种对我有用的可能方法是使用 setTimeout()
方法中的功能。
在context.js
setTimeout(() => {
chrome.runtime.sendMessage({action: "update UI", results: dataForUI},
(response)=> {
console.log(response.message)
}
)
}, 100);
但我不确定这是否是最好的方法。
具有真实 URL 的 iframe 异步加载,因此其代码在嵌入代码完成后运行 - 因此,您的消息发送得太早而丢失。 URL 在你的例子中指向一个扩展资源,所以它是一个真正的 URL。作为参考,同步加载的 iframe 将有一个虚拟 URL 例如根本没有 src
(或空字符串),或者它会像 about:blank
或 javascript:/*some code here*/
,也可能是 srcdoc
。
解决方案 1:在 iframe 的 onload
事件中发送消息
可能的缺点:所有选项卡中的所有扩展框架都会收到它,包括后台脚本和任何其他打开的扩展页面,如弹出窗口、选项,如果它们也有一个 onMessage 侦听器。
iframeNode.onload = () => {
chrome.runtime.sendMessage('foo', res => { console.log(res); });
};
document.body.appendChild(iframeNode);
解决方案 2:让 iframe 向其嵌入器发送消息
可能的缺点:如果您在一个选项卡中添加多个这样的扩展框架,例如由于浏览器中的错误或优化,第二个比第一个加载得早,可能会发送错误的数据 - 在这种情况下您可能必须使用直接 DOM 消息传递(解决方案 3)。
iframe 脚本 (panel.js):
chrome.tabs.getCurrent(ownTab => {
chrome.tabs.sendMessage(ownTab.id, 'getData', data => {
console.log('frame got data');
// process data here
});
});
内容脚本(context.js):
document.body.appendChild(iframeNode);
chrome.runtime.onMessage.addListener(
function onMessage(msg, sender, sendResponse) {
if (msg === 'getData') {
chrome.runtime.onMessage.removeListener(onMessage)
sendResponse({ action: 'update UI', results: 'foo' });
}
});
解决方案 3:通过 postMessage
直接发送消息
在一个选项卡中有多个扩展框架的情况下使用。
缺点:无法判断消息是由页面还是由其他扩展程序的内容脚本伪造的。
iframe 脚本为 message
事件声明了一个 one-time 侦听器:
window.addEventListener('message', function onMessage(e) {
if (typeof e.data === 'string' && e.data.startsWith(chrome.runtime.id)) {
window.removeEventListener('message', onMessage);
const data = JSON.parse(e.data.slice(chrome.runtime.id.length));
// process data here
}
});
然后,另外,使用以下之一:
如果内容脚本是发起者
iframeNode.onload = () => { iframeNode.contentWindow.postMessage( chrome.runtime.id + JSON.stringify({foo: 'data'}), '*'); }; document.body.appendChild(iframeNode);
如果 iframe 是发起者
iframe 脚本:
parent.postMessage('getData', '*');
内容脚本:
document.body.appendChild(iframeNode); window.addEventListener('message', function onMessage(e) { if (e.source === iframeNode) { window.removeEventListener('message', onMessage); e.source.postMessage(chrome.runtime.id + JSON.stringify({foo: 'data'}), '*'); } });