Chrome 扩展程序 - 从网页中检索全局变量

IT技术 javascript html dom google-chrome-extension
2021-01-16 21:10:56

我正在为 Chrome 开发一个扩展程序。我希望解析“原始”Gmail 邮件(当前查看的邮件)的内容。

我尝试使用 jQuery.load() 如下

$(windows).load(function() { alert(GLOBALS); });

并将其放在内容脚本中,但它也不起作用。我正在使用 Chrome 的开发人员工具,它在调用alert(GLOBALS);

未捕获的 ReferenceError:未定义 GLOBALS

尽管在使用开发人员工具的控制台时,在控制台中键入GLOBALS它会返回一个数组。

任何线索如何从内容脚本访问 GLOBALS?

5个回答

内容脚本在隔离环境中运行。要访问任何全局属性(页面的window),您必须注入一个新<script>元素,或使用事件侦听器来传递数据。

例如,请参阅有关在页面上下文中注入元素的答案<script>

例子

contentscript.js"run_at": "document_end"在清单中):

var s = document.createElement('script');
s.src = chrome.extension.getURL('script.js');
(document.head||document.documentElement).appendChild(s);
s.onload = function() {
    s.remove();
};

// Event listener
document.addEventListener('RW759_connectExtension', function(e) {
    // e.detail contains the transferred data (can be anything, ranging
    // from JavaScript objects to strings).
    // Do something, for example:
    alert(e.detail);
});

script.js - 位于扩展目录中,这将被注入到页面本身:

setTimeout(function() {
    /* Example: Send data from the page to your Chrome extension */
    document.dispatchEvent(new CustomEvent('RW759_connectExtension', {
        detail: GLOBALS // Some variable from Gmail.
    }));
}, 0);

由于此文件是通过 DOM 中的 chrome-extension: URL 加载的,因此必须将“script.js”添加到清单文件的 web_accessible_resources 部分。否则 Chrome 将拒绝加载脚本文件。

您应该在网页中运行尽可能少的逻辑,并在内容脚本中处理大部分逻辑。这有多种原因。首先,注入到页面中的任何脚本都在与网页相同的上下文中运行,因此网页可以(有意或无意)修改 JavaScript/DOM 方法,使您的扩展程序停止工作。其次,内容脚本可以访问额外的功能,例如 chrome.* API 和跨域网络请求的有限子集(前提是扩展程序已声明这些权限)。

感谢你的回复。如果我错了,请纠正我,但内容脚本中的源代码是要注入的代码,并且是无缝注入的(由谷歌定义)。否则,我该怎么做(参考会很棒)
2021-03-19 21:10:56
有什么具体的使用理由RW759_connectExtension吗?找不到任何理由,但我见过使用相同事件键的其他示例
2021-03-20 21:10:56
@MrRoth参考:“内容脚本在称为隔离世界的特殊环境中执行。它们可以访问被注入页面的 DOM,但不能访问页面创建的任何 JavaScript 变量或函数。
2021-03-26 21:10:56
@MrRoth 更新并测试了答案。以前,该<script>元素甚至在加载之前就被删除了。现在,<script>元素在加载后被删除。
2021-04-06 21:10:56
@pedrorijo91 这是一个任意选择的事件名称。在撰写本文时是独一无二的,但现在它可能不再那么独特了。
2021-04-07 21:10:56

在 chrome 扩展 content_script 和页面上的 javascript 之间进行通信的一个更现代的解决方案是使用 html5 postMessage API。发送到“window”的任何消息都可以从网页上的 javascript 和扩展程序的 content_script 中看到。

扩展的 content_script.js:

window.addEventListener('message', function(event) {
    console.log('content_script.js got message:', event);
    // check event.type and event.data
});

setTimeout(function () {
    console.log('cs sending message');
    window.postMessage({ type: 'content_script_type',
                         text: 'Hello from content_script.js!'},
                       '*' /* targetOrigin: any */ );
}, 1000);

网页上运行的javascript:

window.addEventListener('message', function(event) {
    console.log('page javascript got message:', event);
});

setTimeout(function() {
    console.log('page javascript sending message');
    window.postMessage({ type: 'page_js_type',
                         text: "Hello from the page's javascript!"},
                       '*' /* targetOrigin: any */);
}, 2000);

另请参阅http://developer.chrome.com/extensions/content_scripts.html#host-page-communication

实际上,它们确实共享相同的 window 对象。结帐developer.mozilla.org/en-US/docs/Web/API/Window/postMessage
2021-03-18 21:10:56
但是,window页面上的全局对象与 中的全局对象不同content_script.js,所以这行不通吗?
2021-03-21 21:10:56
这让我感到困惑,因为我认为孤立的世界意味着所有 env 变量,包括window对象都会不同webpage.jscontent_script.js但这似乎只是部分正确。postMessage并且eventListeners似乎共享同一个window对象,但其他属性不..
2021-03-22 21:10:56
不,它们不一样window,我尝试修改window.openon contentScript,它没有在页面本身上修改。@doublea 但这并不意味着这个答案不起作用,因为我们正在使用postMessageand addEventListener
2021-04-09 21:10:56
在发表此评论时,我确认 MainWorld(非扩展)脚本window.postMessage和 ExtensionWorld-ContentScriptwindow.addEventListener("message")可以相互通信,反之亦然。请参阅此developer.chrome.com/extensions/... 中的官方段落
2021-04-10 21:10:56

有一个新的网页 API 可以安全地与内容脚本进行通信,并且没有任何副作用(window.postMessage 可以有其他侦听器!)。

“从网页中,使用 runtime.sendMessage 或 runtime.connect API 向特定应用程序或扩展程序发送消息”

// The ID of the extension we want to talk to.
var editorExtensionId = "abcdefghijklmnoabcdefhijklmnoabc";

// Make a simple request:
chrome.runtime.sendMessage(editorExtensionId, {openUrlInEditor: url},
  function(response) {
    if (!response.success)
    handleError(url);
});

“从您的应用程序或扩展程序中,您可以通过 runtime.onMessageExternal 或 runtime.onConnectExternal API 收听来自网页的消息,类似于跨扩展程序消息传递。只有网页才能发起连接。[...]”

(来自http://developer.chrome.com/extensions/messaging.html)这仍然只在 chrome 的开发频道中可用,但似乎它会在下一个版本左右。

不要问我这是如何工作的,这似乎非常令人困惑。chrome.runtime 到底是如何在网页上定义的?如果脚本由于某种原因已经定义了该变量怎么办?我也找不到铬错误报告来查看此功能的开发历史。

关于您的最后一段,请参阅code.google.com/p/chromium/issues/detail?id=249080
2021-03-17 21:10:56
@RobW 你好,你是说这行不通吗?因为我不明白这个答案,我已经阅读了你关于在页面中执行脚本的所有答案,我没有问题,我是出于好奇问这个的,谢谢。
2021-03-22 21:10:56
你应该提到(不幸的是)The URL pattern must contain at least a second-level domaindeveloper.chrome.com/extensions/messaging#external-webpage
2021-03-25 21:10:56
@Shayan 最后一段询问如何检测chrome.runtime.sendMessage. chrome.runtime.sendMessage除了重复调用或使用其他方式通知页面(例如,通过内容脚本)之外,没有任何方法可以这样做。crbug.com/249080是关于提供一种让网站发现 API 可用性的方法,但它现在似乎已经关闭。
2021-03-27 21:10:56
今年是 2020 年,我建议不要这样做。文档建议使用postMessage. 请注意文档使用“*”targetOrigin,这是不好的此外,使用runtime.sendMessage嵌入式脚本进行通信意味着您必须使用后台脚本来中继您的所有通信一个精心实施的人postMesage应该这样做。
2021-03-29 21:10:56

根据您的扩​​展程序的可访问 html 页面,我有一个稍微不同的方法。

将您的页面添加到清单:

"web_accessible_resources": ["variables.html"]

创建您的页面(此处为 variables.html)并提取内容数据(即 window.contentVar):

<script type="text/javascript">
  $("#my-var-name").text(window["contentVar"]);
</script>
<div id="my-var-name" style="display: none;"></div>

从扩展程序的 JavaScript 访问:

var myVarName = $("#my-var-name").text();

添加到您的内容脚本:

function executeOnPageSpace(code){

  // create a script tag
  var script = document.createElement('script')
  script.id = 'tmpScript'
  // place the code inside the script. later replace it with execution result.
  script.textContent = 
  'document.getElementById("tmpScript").textContent = JSON.stringify(' + code + ')'
  // attach the script to page
  document.documentElement.appendChild(script)
  // collect execution results
  let result = document.getElementById("tmpScript").textContent
  // remove script from page
  script.remove()
  return JSON.parse(result)

}

现在你可以这样做:

let window = executeOnPageSpace('window')

甚至像这样:

executeOnPageSpace('window.location.href = "http://stackoverflow.com"')

注意:我还没有对长时间运行的代码进行测试。