从chrome扩展注入的js访问全局js变量

时间:2017-10-22 01:50:34

标签: google-chrome-extension

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

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

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

我的代码:

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

内容-的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);
}

1 个答案:

答案 0 :(得分:9)

由于无法从扩展中直接访问页面JS变量,因此需要insert code in script element so that it runs in the page context并通过DOM消息传递将变量中继到内容脚本中。然后内容脚本可以将消息中继到iframe。

  • 内容脚本:

    runInPage(initConfigExtractor);
    
    chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => {
      switch (msg) {
        case 'toggle':
          toggle();
          return;
        case 'getConfig':
          window.addEventListener(chrome.runtime.id + '-config', event => {
            sendResponse(event.detail);
          }, {once: true});
          window.dispatchEvent(new Event(chrome.runtime.id));
          return true;
      }
    })
    
    function initConfigExtractor(extensionId) {
      window.addEventListener(extensionId, () => {
        window.dispatchEvent(new CustomEvent(extensionId + '-config', {
          detail: window.config,
        }));
      });
    }
    
    function runInPage(fn) {
      const script = document.createElement('script');
      document.head.appendChild(script).text =
        '((...args) => (' + fn + ')(...args))(' + JSON.stringify(chrome.runtime.id) + ')';
      script.remove();
    }
    
  • iframe脚本:

    function handler() {
      chrome.tabs.getCurrent(tab => {
        chrome.tabs.sendMessage(tab.id, 'getConfig', config => {
          console.log(config);
          // do something with config
        });
      });  
    }
    
  • 替代案例 - 弹出脚本:

    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并向内容脚本发送消息
  2. 内容脚本将DOM消息发送到先前插入的页面脚本
  3. 页面脚本侦听该DOM消息并将另一个DOM消息发送回内容脚本
  4. 内容脚本将其在响应中发送回iframe脚本。
  5. 整个过程是异步的,因此onMessage监听器需要return true来保持消息通道的打开。