gpt4 book ai didi

javascript - 将消息从后台脚本发送到内容脚本,然后再发送到注入(inject)脚本

转载 作者:可可西里 更新时间:2023-11-01 01:45:21 27 4
gpt4 key购买 nike

我正在尝试从后台页面向内容脚本发送消息,然后从该内容脚本向注入(inject)脚本发送消息。我试过这个,但没有用。

这是我的代码的样子。

list .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"}));
});

注入(inject).js

document.addEventListener('Buffer2Remote', function(e){
alert(e.todo);
});

消息发送从第一部分开始不起作用,background -> content_script。我的代码有什么问题吗?

最佳答案

由于内容脚本的注入(inject)方式,您的脚本无法正常工作。

问题

当您(重新)加载您的扩展程序时,与某些人的预期相反,Chrome 不会将内容脚本注入(inject)现有标签页以匹配 list 中的模式。只有在加载扩展后,任何导航才会检查匹配的 URL 并注入(inject)代码。

所以,时间轴:

  1. 你打开了一些标签。那里没有内容脚本1
  2. 加载您的扩展程序。它的顶级代码得到执行:它尝试将消息传递到当前选项卡。
  3. 由于那里还没有监听器,所以它失败了。 (这可能是 chrome://extensions/ 页面,无论如何你不能在那里注入(inject))
  4. 如果之后您尝试导航/打开新选项卡,则会注入(inject)监听器,但您的顶级代码将不再执行。

1 - 如果您重新加载您的扩展,也会发生这种情况。如果注入(inject)了内容脚本,它会继续处理其事件/不会被卸载,但无法再与扩展通信。 (详情见文末附录)

解决方案

解决方案 1:您可以先询问要向其发送消息的选项卡是否准备就绪,然后在保持沉默后以编程方式注入(inject)脚本。考虑:

// 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 */
});

解决方案 2:始终注入(inject)脚本,但确保它只执行一次。

// 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 将是未定义的。当心可能执行脚本两次的副作用。


解决方案 3:只是在初始化时不加选择地注入(inject)您的内容脚本。只有在两次运行相同的内容脚本或在页面完全加载后运行它是安全的情况下,才可以安全地执行此操作。

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
});
}
});

我还怀疑您希望它在执行某些操作时运行,而不是在扩展加载时运行。例如,您可以雇用 Browser Action并将您的代码包装在 chrome.browserAction.onClicked 监听器中。


关于孤立内容脚本的附录

当扩展程序重新加载时,人们会期望 Chrome 清理所有内容脚本。但显然情况并非如此;内容脚本的监听器未被禁用。但是,任何带有父分机的消息传递都将失败。 这可能应该被认为是一个错误,可能会在某个时候被修复。我将这个状态称为“孤儿”

这在两种情况下都不是问题:

  1. 内容脚本没有页面事件的监听器(例如只执行一次,或者只监听来自后台的消息)
  2. 内容脚本不对页面做任何事情,只向后台发送有关事件的消息。

但是,如果不是这种情况,您就会遇到问题:内容脚本可能正在做某事但失败或干扰了自身的另一个非孤立实例。

对此的解决方案是:

  1. 跟踪页面可以触发的所有事件监听器
  2. 在对这些事件采取行动之前,向后台发送“心跳”消息。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; }
/* ... */
});

关于javascript - 将消息从后台脚本发送到内容脚本,然后再发送到注入(inject)脚本,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23895377/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com