从 chrome 扩展注入的 js 访问全局 js 变量 [英] Access global js variables from js injected by a chrome extension

查看:21
本文介绍了从 chrome 扩展注入的 js 访问全局 js 变量的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试创建一个带有侧面板的扩展程序.此侧面板将包含可根据主机页面状态执行操作的按钮.

I am trying to create an extension that will have a side panel. This side panel will have buttons that will perform actions based on the host page state.

我按照这个例子注入侧板我可以连接一个按钮 onClick 侦听器.但是,我无法访问全局 js 变量.在开发人员控制台中,在主机页面的范围内,我能够看到我所追求的变量(变量名称 - 配置).但是当我进入侧面板 (popup.html) 的上下文时,我收到以下错误 -
VM523:1 未捕获的 ReferenceError:未定义配置.似乎 popup.html 也在一个单独的线程中运行.

I followed this example to inject the side panel and I am able to wire up a button onClick listener. However, I am unable to access the global js variable. In developer console, in the scope of the host page I am able to see the variable (name of variable - config) that I am after. but when I which to the context of the sidepanel (popup.html) I get the following error -
VM523:1 Uncaught ReferenceError: config is not defined. It seems like popup.html also runs in a separate thread.

如何访问按钮的 onClick 处理程序的全局 js 变量?

How can I access the global js variable for the onClick handler of my button?

我的代码:

ma​​nifest.json

{
    "manifest_version": 2,

    "name": "Hello World",
    "description": "This extension to test html injection",
    "version": "1.0",
    "content_scripts": [{
        "run_at": "document_end",
        "matches": [
            "https://*/*",
            "http://*/*"
        ],
        "js": ["content-script.js"]
    }],
    "browser_action": {
        "default_icon": "icon.png"
    },
    "background": {
        "scripts":["background.js"]
    },
    "permissions": [
        "activeTab"
    ],
    "web_accessible_resources": [
        "popup.html",
        "popup.js"
    ]
}

background.js

chrome.browserAction.onClicked.addListener(function(){
    chrome.tabs.query({active: true, currentWindow: true}, function(tabs){
        chrome.tabs.sendMessage(tabs[0].id,"toggle");
    })
});

content-script.js

chrome.runtime.onMessage.addListener(function(msg, sender){
    if(msg == "toggle"){
        toggle();
    }
})

var iframe = document.createElement('iframe'); 
iframe.style.background = "green";
iframe.style.height = "100%";
iframe.style.width = "0px";
iframe.style.position = "fixed";
iframe.style.top = "0px";
iframe.style.right = "0px";
iframe.style.zIndex = "9000000000000000000";
iframe.frameBorder = "none"; 
iframe.src = chrome.extension.getURL("popup.html")

document.body.appendChild(iframe);

function toggle(){
    if(iframe.style.width == "0px"){
        iframe.style.width="400px";
    }
    else{
        iframe.style.width="0px";
    }
}

popup.html

<head>
<script src="popup.js"> </script>
</head>
<body>
<h1>Hello World</h1>
<button name="toggle" id="toggle" >on</button>
</body>

popup.js

document.addEventListener('DOMContentLoaded', function() {
  document.getElementById("toggle").addEventListener("click", handler);
});

function handler() {
  console.log("Hello");
  console.log(config);
}

推荐答案

由于内容脚本在孤立的世界"中运行页面的JS变量不能直接从扩展中访问,需要script元素中插入代码 以便它在页面上下文中运行.它将提取变量并通过 DOM 消息将其发送到内容脚本中.然后内容脚本可以将消息中继到 iframe 或弹出/后台页面中的扩展脚本.

Since content scripts run in an "isolated world" the JS variables of the page cannot be directly accessed from an extension, you need to insert code in script element so that it runs in the page context. It'll extract the variable and send it into the content script via DOM messaging. Then the content script can relay the message to the extension script in iframe or popup/background pages.

  • ManifestV2 内容脚本:

const evtToPage = chrome.runtime.id;
const evtFromPage = chrome.runtime.id + '-response';

// this creates a script element with the function's code and passes event names
const script = document.createElement('script');
script.textContent = `(${inPageContext})("${evtToPage}", "${evtFromPage}")`;
document.documentElement.appendChild(script);
script.remove();

// this function runs in page context and registers a listener
function inPageContext(listenTo, respondWith) {
  addEventListener(listenTo, () => {
    dispatchEvent(new CustomEvent(respondWith, {
      detail: window.config,
    }));
  });
}

chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => {
  if (msg === 'getConfig') {
    // DOM messaging is synchronous so we don't need `return true` in onMessage
    addEventListener(evtFromPage, e => sendResponse(e.detail), {once: true});
    dispatchEvent(new Event(evtToPage));
  }
});

  • ManifestV3 需要两个单独的文件.

  • ManifestV3 needs two separate files.

    内容脚本:

    const evtToPage = chrome.runtime.id;
    const evtFromPage = chrome.runtime.id + '-response';
    
    chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => {
      if (msg === 'getConfig') {
        // DOM messaging is synchronous so we don't need `return true` in onMessage
        addEventListener(evtFromPage, e => {
          sendResponse(JSON.parse(e.detail));
        }, {once: true});
        dispatchEvent(new Event(evtToPage));
      }
    });
    
    // Run the script in page context and pass event names
    const script = document.createElement('script');
    script.src = chrome.runtime.getURL('page-context.js');
    script.dataset.args = JSON.stringify({evtToPage, evtFromPage});
    document.documentElement.appendChild(script);
    

    page-context.js 应该在 manifest.json 的 web_accessible_resourcesexample 中公开.>

    page-context.js should be exposed in manifest.json's web_accessible_resources, example.

    // This script runs in page context and registers a listener.
    // Note that the page may override/hook things like addEventListener... 
    (() => {
      const el = document.currentScript;
      const {evtToPage, evtFromPage} = JSON.parse(el.dataset.args);
      el.remove();
      addEventListener(evtToPage, () => {
        dispatchEvent(new CustomEvent(evtFromPage, {
          // stringifying strips nontranferable things like functions or DOM elements
          detail: JSON.stringify(window.config),
        }));
      });
    })();
    

  • 同一标签页中扩展 iframe 脚本的使用示例:

  • usage example for extension iframe script in the same tab:

    function handler() {
      chrome.tabs.getCurrent(tab => {
        chrome.tabs.sendMessage(tab.id, 'getConfig', config => {
          console.log(config);
          // do something with config
        });
      });  
    }
    

  • 弹出脚本或后台脚本的使用示例:

  • usage example for popup script or background script:

    function handler() {
      chrome.tabs.query({active: true, currentWindow: true}, tabs => {
        chrome.tabs.sendMessage(tabs[0].id, 'getConfig', config => {
          console.log(config);
          // do something with config
        });
      });  
    }
    

  • 所以,基本上:

    1. iframe 脚本获取自己的标签 ID(或弹出/后台脚本获取活动标签 ID)并向内容脚本发送消息
    2. 内容脚本向之前插入的页面脚本发送 DOM 消息
    3. 页面脚本侦听该 DOM 消息并将另一条 DOM 消息发送回内容脚本
    4. 内容脚本将其作为响应发送回扩展脚本.

    这篇关于从 chrome 扩展注入的 js 访问全局 js 变量的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

    查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆