当前位置: 首页 > 面试题库 >

将消息从后台脚本发送到内容脚本,然后发送到注入的脚本

张子墨
2023-03-14
问题内容

我正在尝试将消息从后台页面发送到内容脚本,然后将消息从该内容脚本发送到注入的脚本。我已经尝试过了,但是没有用。

这是我的代码的样子。

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

injection.js

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

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


问题答案:

由于内容脚本的注入方式,您的脚本无法使用。

问题

与某些人期望的相反,当您(重新)加载扩展程序时,Chrome 不会将内容脚本注入 与清单中的模式匹配的 现有标签
中。仅在加载扩展名之后,任何导航都将检查URL是否匹配并注入代码。

因此,时间表:

  1. 您打开一些选项卡。那里没有内容脚本1。
  2. 您加载扩展程序。它的顶级代码被执行:它试图将消息传递到当前选项卡。
  3. 由于那里还没有侦听器,因此它将失败。(这可能是该chrome://extensions/页面,您无论如何都无法在其中注入)
  4. 之后,如果您尝试导航/打开一个新选项卡,则将注入侦听器,但不再执行顶层代码。

1-如果您重新加载扩展程序,也会发生这种情况。如果注入了内容脚本,它将继续处理其事件/不会被卸载,但无法再与扩展通信。(有关详细信息,请参阅末尾的附录)

解决方案

解决方案1: 您可以先询问要发送消息的选项卡,以了解消息是否准备就绪 ,然后在静音时以编程方式注入脚本。考虑:

// 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: 始终注入脚本,但要确保脚本仅执行一次。

// 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: 在初始化时不加选择地注入您的内容脚本 。仅当可以安全运行两次相同的内容脚本或在页面完全加载后运行该脚本时,才可以安全地执行此操作。

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清理所有内容脚本。但是显然不是这样。内容脚本的侦听器未禁用。但是,任何带有父扩展名的消息传递都会失败。
这可能应该被认为是一个错误,并且可能在某个时候被修复。 我将这个状态称为“孤立”

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

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


 类似资料:
  • 我正在从Bash脚本启动一个名为 的Java代码。Bash 脚本启动 Java 代码,然后运行 Java 代码。在Java程序结束时,我想发送一个信号回到Bash脚本以终止。请记住,Bash 脚本在 PID = 1 的情况下运行。我必须杀死PID 1过程。 我设置了bash脚本,使其在无限循环中运行,并< code >等待终止信号: 我正在使用Docker实例,信号是< code>sigterm。

  • 问题内容: 我不知道在Flash或Actionscript中进行编程。实际上,我是一名Java EE开发人员。 在Flash文件中,我有以下方法: 此方法会将录制的声音保存到我们将指定的文件夹中的“ recording.wav”中。 我想要做的是通过将录制的声音发送到Java Servlet来更改保存到磁盘的内容。 我找到了这段代码,但是我不知道如何在HTTP请求中发送的params中插入reco

  • 我一直试图从我的Android应用程序向一个URL发送一个POST请求,但我得到的只是一个空的$_POST。下面是我的代码,它运行在AsyncTask中。我在StackOverflow中查看了几个类似的问题,但没有一个能帮助我。 我在AndroidManifest.xml中拥有INTERNET权限。在这个相同的应用程序中,我做了另一个PHP脚本的请求,它按预期工作。 有什么想法吗?

  • 好吧,我不知道出了什么问题。我试图将表单数据从一个简单的jQuery脚本传递到我的php脚本,但由于某种原因,当我试图访问$\u POST数据时,php说$\u POST是空的? 现在开始,我有以下jQuery和php脚本 jQuery PHP var post的控制台日志是这样的 而$U POST的var_dump则说明了这一点 我不知道为什么这会给我带来如此多的问题,所以任何帮助都将不胜感激。

  • 问题内容: 什么是场景 我想从Android向服务器发送多个ArrayList(通常为5个),并将其插入到mysql数据库中。 我成功完成的工作 我已使用JSON成功将Android中的单个值和多个值发送到PHP脚本 我已经使用JSON从mysql数据库到android接收了单条记录和多条记录 这是用于从服务器插入值并从中获取价值的代码 和TeacherAuthen.php脚本 我被困在那里 我没

  • 问题内容: 我当前的部分看起来像这样: …这意味着我可以运行来启动服务器。到现在为止还挺好。 但是,我希望能够运行类似的东西并将参数传递给(例如=> )。这可能吗? 问题答案: 编辑: 可以将args传递给npm2.0.0 语法如下: 注意必要的。需要将传递给命令本身的参数和传递给脚本的参数分开。 所以如果你有 那么以下命令将是等效的: => => 为了读取命名参数,最好使用yargs或[mini