网站搜索

javascript - Chrome 扩展在已定义的变量上抛出“未定义”


我正在尝试通过 Chrome 扩展访问本地变量。当在页面脚本中尝试 console.info(myVar) 时,我收到 myVar 未定义 的错误

然而,当使用 Chrome 开发者工具并在调试控制台中执行相同的代码片段时,我得到的是 myVar 的全部内容。

尝试访问 window.myVar 时表现相同,通过 Chrome 扩展打印时,它只是 undefined。

将一个脚本标签注入到 body 中,通过开发者工具和页面脚本使用以下片段,会导致完全相同的行为。

   $("body").append($("<script />", {
      html: "console.info(myVar);"
   }));

在开发工具中执行时变量会被打印,但在页面脚本中会出现 JavaScript 错误

答案#1

你看到的是预期的行为。

内容脚本在一个称为隔离世界的特殊环境中执行。它们可以访问它们被注入的页面的DOM,但不能访问页面创建的任何JavaScript变量或函数。对于每个内容脚本来说,看起来好像页面上没有其他JavaScript在执行。反之亦然:页面上运行的JavaScript无法调用内容脚本定义的任何函数或访问其变量。

http://developer.chrome.com/extensions/content_scripts.html#execution-environment

当你从页面运行 console.info(myVar);,无论是在 Chrome 开发者工具的控制台还是从注入的脚本中,它都是在页面的上下文中运行的,因此可以看到该变量已被定义。

然而,当从你的扩展代码内部运行相同的代码时,你是在一个完全不同的环境中操作,所以 myVar 是未定义的。

你可以尝试使用共享 DOM 来解决这个问题。
http://developer.chrome.com/extensions/content_scripts.html#host-page-communication

编辑

除非有人纠正我,下面的 console.info(myVar); 代码是在仍然在扩展程序的上下文中执行时,被注入到 script 元素中的:

$("<script />", {
    html: "console.info(myVar);" // myVar points to extension's myVar, not page's
});

一个简单的测试可以通过记录 chrome.extension 对象或在内容脚本中声明 myVar 来运行——chrome.extension 将是可用的,并且 myVar 将会按内容脚本中定义的方式被记录。

建议使用外部脚本(这对于模块化也更好),将其添加到web_accessible_resources,并创建一个script元素,其src属性指向该外部脚本。

下面的示例扩展代码在启用扩展时加载 test.html 页面会弹出“test”。

manifest.json

{
    "name": "Test",
    "version": "0.1.0",
    "manifest_version": 2,
    "description": "Just a test.",
    "content_scripts": [{
        "matches": ["<all_urls>"],
        "js": ["contentscript.js"]
    }],
    "web_accessible_resources": [
        "myscript.js"
    ]
}

test.html(访问的页面)

<!DOCTYPE html>
<html>
<head>
    <script>
        window.test = 'test';
    </script>
</head>
<body>
</body>
</html>

contentscript.js

var script = document.createElement('script');
script.type = 'text/javascript';
script.src = chrome.extension.getURL('myscript.js');
document.getElementsByTagName('head')[0].appendChild(script);

myscript.js(这是将在页面上下文中运行的外部脚本)

alert(window.test);

答案#2

奥列格和我在一次聊天中找到了问题的根源。

我尝试使用以下代码片段注入脚本:

   $("body").append($("<script />", {
      html: "console.info(myVar);"
   }));

正如Oleg在他的回答中写的,看起来console.info部分在到达页面主体之前就被执行了,因此在这里有扩展上下文。不过非jQuery版本工作正常:

var g = document.createElement('script'); 
var s = document.getElementsByTagName('script')[0]; 
g.text = "console.info(myVar);" 
s.parentNode.insertBefore(g, s);