我正在开发 Chrome 扩展程序。我希望解析“原始”Gmail 邮件(当前查看的邮件)的内容。
我尝试使用 jQuery.load() 如下
$(windows).load(function() { alert(GLOBALS); });
并将其放置在内容脚本中,但它也不起作用。我正在使用 Chrome 的开发者工具,它在调用
alert(GLOBALS);
时返回以下错误
未捕获的引用错误:GLOBALS 未定义
尽管在使用开发人员工具的控制台时,在控制台中输入
GLOBALS
,它会返回一个数组。
知道如何从内容脚本访问 GLOBALS 吗?
内容脚本在隔离环境中运行。要访问(页面的
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);
由于此文件是通过 chrome-extension: URL 从 DOM 加载的,因此必须将“script.js”添加到清单文件的 web_accessible_resources 部分。否则 Chrome 将拒绝加载脚本文件。
您应该在网页中运行尽可能少的逻辑,并在内容脚本中处理大部分逻辑。这有多种原因。首先也是最重要的,注入到页面中的任何脚本都在与网页相同的上下文中运行,因此网页可以(有意或无意)修改 JavaScript/DOM 方法,从而导致您的扩展停止工作。其次,内容脚本可以访问额外的功能,例如 chrome.* API 的有限子集和跨源网络请求(前提是扩展程序已声明这些权限)。
在 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
网页有一个新的 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 到底是如何在网页上定义的?如果脚本由于某种原因已经定义了该变量怎么办?我也找不到 chromium bug 报告来查看这个功能的开发历史。
根据您的扩展程序的可访问 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"')
注意:我还没有测试过长时间运行的代码。
从 Chrome 95 开始,最好的方法是使用
chrome.scripting
API,它使扩展能够在选项卡的主世界中运行脚本并将结果返回到后台脚本。
https://developer.chrome.com/docs/extensions/reference/api/scripting
// Inject a content script into the tab's main world when the browser action button is clicked.
chrome.action.onClicked.addListener(async (tab) => {
// content.js will execute in the main world and have access to all global variables
const result = await chrome.scripting.executeScript({
target: { tabId: tab.id },
files: ["content.js"],
world: "MAIN",
});
// myGlobals will contain the data returned from the content.js script
const myGlobals = result[0].result;
});