Chrome 扩展 - 从网页中检索全局变量
Posted
技术标签:
【中文标题】Chrome 扩展 - 从网页中检索全局变量【英文标题】:Chrome extension - retrieving global variable from webpage 【发布时间】:2012-03-25 00:18:40 【问题描述】:我正在开发 Chrome 的扩展程序。我希望解析“原始”Gmail 邮件(当前查看的邮件)的内容。
我尝试如下使用 jQuery.load()
$(windows).load(function() alert(GLOBALS); );
并将其放在内容脚本中,但它也不起作用。我正在使用 Chrome 的开发人员工具,它在调用 alert(GLOBALS);
时返回以下错误
Uncaught ReferenceError: GLOBALS is not defined
虽然,在使用开发者工具的控制台时,在控制台中输入GLOBALS
会返回一个数组。
任何线索如何从内容脚本访问 GLOBALS?
【问题讨论】:
【参考方案1】:Content scripts 在隔离环境中运行。要访问任何全局属性(页面的window
),您必须注入一个新的<script>
元素,或者使用事件侦听器来传递数据。
参见this answer,例如在页面上下文中注入<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);
由于此文件是通过 chrome-extension: URL 从 DOM 中加载的,因此必须将“script.js”添加到清单文件的 web_accessible_resources 部分。否则 Chrome 将拒绝加载脚本文件。
您应该在网页中运行尽可能少的逻辑,并在内容脚本中处理大部分逻辑。这有多种原因。首先,任何注入页面的脚本都在与网页相同的上下文中运行,因此网页可以(有意或无意地)修改 JavaScript/DOM 方法,从而使您的扩展程序停止工作。其次,内容脚本可以访问额外的功能,例如 chrome.* API 的有限子集和跨域网络请求(前提是扩展程序已声明这些权限)。
【讨论】:
感谢您的回复。如果我错了,请纠正我,但内容脚本中的源代码是要注入的代码,并且是无缝注入的(由谷歌定义)。否则,我该怎么做(参考会很棒) @MrRoth Reference:“内容脚本在称为隔离世界的特殊环境中执行。它们可以访问它们被注入的页面的 DOM,但 不能访问任何 JavaScript 变量或页面创建的函数。" @MrRoth 更新和测试的答案。以前,<script>
元素在加载之前就被删除了。现在,<script>
元素在加载后被移除。
使用RW759_connectExtension
有什么具体原因吗?找不到任何理由,但我见过使用相同事件键的其他示例
@pedrorijo91 这是一个任意选择的事件名称。在撰写本文时是独一无二的,但现在可能不再那么独特了。【参考方案2】:
在 chrome 扩展 content_script 和页面上的 javascript 之间进行通信的更现代的解决方案是使用 html5 postMessage API。任何发送到“窗口”的消息都可以从网页上的 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
对象与content_script.js
中的对象不同,所以这不起作用?
实际上,它们共享同一个窗口对象。结帐developer.mozilla.org/en-US/docs/Web/API/Window/postMessage
这让我很困惑,因为我认为孤立的世界意味着所有环境变量,包括 window
对象对于 webpage.js
和 content_script.js
会有所不同,但这似乎只是部分正确。 postMessage
和 eventListeners
似乎共享相同的 window
对象,但其他属性却没有..
不,它们不一样window
,我尝试在contentScript
上修改window.open
,它没有在页面本身上修改。 @doublea 但这并不意味着这个答案不起作用,因为我们使用的是postMessage
和addEventListener
。
在发表此评论时,我确实确认 MainWorld(非扩展)脚本 window.postMessage
和 ExtensionWorld-ContentScript window.addEventListener("message")
可以相互通信,反之亦然。看这个developer.chrome.com/extensions/…的官方段落@【参考方案3】:
有一个新的网页 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 你应该提到(不幸的是)The URL pattern must contain at least a second-level domain
见developer.chrome.com/extensions/messaging#external-webpage
@RobW 你好,你是说这行不通吗?因为我不明白这个答案,所以我已经阅读了你关于在页面中执行脚本的所有答案,我没有任何问题,我是出于好奇才问这个的,谢谢。
@Shayan 最后一段询问如何检测chrome.runtime.sendMessage
的可用性。除了重复调用chrome.runtime.sendMessage
或使用其他方式通知页面(例如通过内容脚本),没有办法这样做。 crbug.com/249080 是关于提供一种让网站发现 API 可用性的方法,但它现在似乎已经关闭了。
今年是 2020 年,我建议不要这样做。 documentation 推荐使用postMessage
。注意文档使用“*”targetOrigin,这是bad。此外,使用runtime.sendMessage
与嵌入式脚本通信意味着您必须使用后台脚本来中继您的所有communication。一个精心实现的postMesage
应该这样做。【参考方案4】:
根据您的扩展程序可访问的 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();
【讨论】:
【参考方案5】:添加到您的内容脚本:
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://***.com"')
注意:我没有测试过长时间运行的代码。
【讨论】:
以上是关于Chrome 扩展 - 从网页中检索全局变量的主要内容,如果未能解决你的问题,请参考以下文章
如何从C扩展名(PHP源)读取和写入PHP超全局变量? [处于保留状态]
Linux编程 11(shell全局环境变量与局变环境变量)