我正在学习如何创建 Chrome 扩展程序。我刚开始开发一个来捕捉 YouTube 事件。我想将它与 YouTube Flash 播放器一起使用(稍后我将尝试使其与 HTML5 兼容)。
清单.json:
{
"name": "MyExtension",
"version": "1.0",
"description": "Gotta catch Youtube events!",
"permissions": ["tabs", "http://*/*"],
"content_scripts" : [{
"matches" : [ "www.youtube.com/*"],
"js" : ["myScript.js"]
}]
}
myScript.js:
function state() { console.log("State Changed!"); }
var player = document.getElementById("movie_player");
player.addEventListener("onStateChange", "state");
console.log("Started!");
问题是控制台给了我 “开始!” ,但没有 “状态已更改!” 当我播放/暂停 YouTube 视频时。
将此代码放入控制台时,它可以工作。我究竟做错了什么?
原文由 André Alves 发布,翻译遵循 CC BY-SA 4.0 许可协议
根本原因:
内容脚本在 “孤立世界” 环境中执行。
解决方案:
使用 DOM 将代码注入页面 - 该代码将能够 访问 页面上下文(“主世界”)的函数/变量或 将 函数/变量公开给页面上下文(在您的情况下是
state()
方法)。使用 DOM
CustomEvent
处理程序。示例: 一、 二 和 三。chrome
API:由于
chrome.*
API 不能在页面脚本中使用,您必须在内容脚本中使用它们并通过 DOM 消息传递将结果发送到页面脚本(请参阅上面的注释)。安全警告:
页面可能会重新定义或扩充/挂钩内置原型,因此如果页面以不兼容的方式执行此操作,您的公开代码可能会失败。如果您想确保您的公开代码在安全的环境中运行,那么您应该 a) 使用 “run_at”: “document_start” 声明您的内容脚本并使用方法 2-3 而不是 1,或者 b) 提取原始的原生内置 -通过一个空的 iframe 输入 ins, 例如.请注意,使用
document_start
您可能需要在公开代码中使用DOMContentLoaded
事件来等待 DOM。目录
方法 1:注入另一个文件 - ManifestV3 兼容
方法二:注入嵌入式代码——MV2
方法 2b:使用函数 - MV2
方法 3:使用内联事件 - ManifestV3 兼容
方法 4:使用 executeScript 的世界 - 仅限 ManifestV3
注入代码中的动态值
方法一:注入另一个文件(ManifestV3/MV2)
当你有很多代码时特别好。将代码放在扩展程序中的文件中,比如
script.js
。然后将其加载到您的 内容脚本 中,如下所示:js 文件必须在
web_accessible_resources
中公开:如果没有,控制台中会出现以下错误:
方法二:注入嵌入式代码(MV2)
当您想快速运行一小段代码时,此方法很有用。 (另请参阅: 如何使用 Chrome 扩展程序禁用 facebook 热键? )。
注意: 模板文字 仅在 Chrome 41 及更高版本中受支持。如果您希望扩展在 Chrome 40- 中运行,请使用:
方法 2b:使用函数 (MV2)
对于一大段代码,引用字符串是不可行的。可以使用函数而不是使用数组,并对其进行字符串化:
此方法有效,因为字符串和函数上的
+
运算符将所有对象转换为字符串。如果您打算多次使用代码,明智的做法是创建一个函数以避免代码重复。一个实现可能如下所示:注意:由于函数是序列化的,原来的作用域和所有绑定的属性都丢失了!
方法 3:使用内联事件 (ManifestV3/MV2)
有时,您想立即运行一些代码,例如在创建
<head>
元素之前运行一些代码。这可以通过插入带有textContent
的<script>
标记来完成(参见方法 2/2b)。另一种 但不推荐 的方法是使用内联事件。不建议这样做,因为如果页面定义了禁止内联脚本的内容安全策略,则内联事件侦听器将被阻止。另一方面,由扩展注入的内联脚本仍然运行。
如果您仍想使用内联事件,方法如下:
注意:此方法假定没有其他全局事件侦听器处理
reset
事件。如果有,您还可以选择其他全球事件之一。只需打开 JavaScript 控制台 (F12),键入document.documentElement.on
,然后选择可用事件。方法 4:使用 chrome.scripting API
world
(仅限 ManifestV3)Chrome 95 或更新版本,
chrome.scripting.executeScript
withworld: 'MAIN'
Chrome 102 或更新版本
chrome.scripting.registerContentScripts
withworld: 'MAIN'
也允许runAt: 'document_start'
保证页面脚本的早期执行。与其他方法不同,此方法适用于后台脚本或弹出脚本,而不适用于内容脚本。请参阅 文档 和 示例。
注入代码中的动态值 (MV2)
有时,您需要将任意变量传递给注入函数。例如:
要注入此代码,您需要将变量作为参数传递给匿名函数。一定要正确执行!以下将 不起作用:
解决方案是在传递参数之前使用
JSON.stringify
。例子:如果你有很多变量,值得使用
JSON.stringify
一次,以提高可读性,如下:注入代码中的动态值(ManifestV3)
然后 script.js 可以读取它:
args
参数, registerContentScripts 目前没有(希望以后会添加)。