diff options
Diffstat (limited to 'files/zh-cn/mozilla/add-ons/webextensions/content_scripts/index.html')
-rw-r--r-- | files/zh-cn/mozilla/add-ons/webextensions/content_scripts/index.html | 575 |
1 files changed, 575 insertions, 0 deletions
diff --git a/files/zh-cn/mozilla/add-ons/webextensions/content_scripts/index.html b/files/zh-cn/mozilla/add-ons/webextensions/content_scripts/index.html new file mode 100644 index 0000000000..4fe6a88055 --- /dev/null +++ b/files/zh-cn/mozilla/add-ons/webextensions/content_scripts/index.html @@ -0,0 +1,575 @@ +--- +title: Content scripts +slug: Mozilla/Add-ons/WebExtensions/Content_scripts +translation_of: Mozilla/Add-ons/WebExtensions/Content_scripts +--- +<div>{{AddonSidebar}}</div> + +<p>Content script 是你扩展的一部分,运行于一个特定的网页环境(而并不是后台脚本,后台脚本是扩展的一部分,也不是该网页利用 {{HTMLElement("script")}} 加载的一个脚本,{{HTMLElement("script")}} 加载的脚本是网页的一部分)。</p> + +<p>后台脚本可以访问所有WebExtension JavaScript APIS,但是他们不能直接访问网页的内容,所以如果你需要Content Scripts来做到这点。</p> + +<p>就像通常的网页加载的脚本一样,Content Scripts 可以使用standard DOM APIS 读取和修改页面内容。</p> + +<p>Content Script 只能访问WebExtension APIS 的一个小的子集,但是它们可以使用通信系统与后台脚本进行通信,从而间接的访问WebExtension APIS。</p> + +<div class="note"> +<p>注意content scripts 在addons.mozilla.org现在已被禁止,如果你在这个域名尝试插入一个Content script将会失败而这个页面会LOG一个CSP错误。</p> +</div> + +<h2 id="加载Content_scripts">加载Content scripts</h2> + +<p>你可以通过两种方法之一在一个页面加载Content script:</p> + +<ul> + <li><strong>声明式</strong>: 在你的manifest.json中使用content_scripts关键字,你可以要求浏览器每当加载一个与指定正则表达式匹配的网页时加载一个Content Script。</li> + <li><strong>程序式</strong>: 使用 <code><a href="/en-US/Add-ons/WebExtensions/API/Tabs/executeScript">tabs.executeScript()</a></code> API, 你可以在任何你想要的时候加载一个Content script 到一个指定的标签:比如,作为用户点击事件的回应。</li> +</ul> + +<p>在每一个extension的每一个frame中,只有一个全局作用域。所以在一个content script中的变量可以被另外的content script访问到,而与content script如何被加载无关。</p> + +<h2 id="Content_script_环境">Content script 环境</h2> + +<h3 id="DOM_访问">DOM 访问</h3> + +<p>Content scripts 可以访问和修改页面的DOM,就像普通的页面脚本一样。他们也可以察觉页面脚本对页面做出的任何修改。</p> + +<p>不过,content scripts 得到的是一个“干净的DOM视图”,这意味着:</p> + +<ul> + <li>content scripts 不能看见页面脚本定义的javascript 变量。</li> + <li>如果一个页面脚本重定义了一个DOM内置属性,content scripts将获取到这个属性的原始版本,而不是重定义版本。</li> +</ul> + +<p>在 Gecko(译者注:Gecko是由 Mozilla 工程开发出的布局引擎的名字), 这种行为被称为射线视觉。</p> + +<p>举个例子,考虑一个网页如下:</p> + +<pre class="brush: html"><!DOCTYPE html> +<html> + <head> + <meta http-equiv="content-type" content="text/html; charset=utf-8" /> + </head> + + <body> + <script src="page-scripts/page-script.js"></script> + </body> +</html></pre> + +<p>脚本文件“page-script.js”如下:</p> + +<pre class="brush: js">// page-script.js + +// add a new element to the DOM +var p = document.createElement("p"); +p.textContent = "This paragraph was added by a page script."; +p.setAttribute("id", "page-script-para"); +document.body.appendChild(p); + +// define a new property on the window +window.foo = "This global variable was added by a page script"; + +// redefine the built-in window.confirm() function +window.confirm = function() { + alert("The page script has also redefined 'confirm'"); +}</pre> + +<p>现在一个扩展插入一个content script 如下:</p> + +<pre class="brush: js">// content-script.js + +// can access and modify the DOM +var pageScriptPara = document.getElementById("page-script-para"); +pageScriptPara.style.backgroundColor = "blue"; + +// can't see page-script-added properties +console.log(window.foo); // undefined + +// sees the original form of redefined properties +window.confirm("Are you sure?"); // calls the original window.confirm()</pre> + +<p>相反的情况也是成立的:页面脚本不能察觉到通过content scripts 添加的JavaScript 属性。</p> + +<p>这意味着content script 可以依靠DOM属性获取可预期的行为</p> + +<p>这种行为造成的一个结果是content script不能获取任何通过页面加载的Javascript 库。所以,如果这个页面包含JQuery,content script 将不会在意它。</p> + +<p>如果一个content script 想要使用Javascript库,这个库本身就必须像一个content script一样在这个content script旁被插入:</p> + +<pre class="brush: json">"content_scripts": [ + { + "matches": ["*://*.mozilla.org/*"], + "js": ["jquery.js", "content-script.js"] + } +]</pre> + +<h3 id="WebExtension_APIs">WebExtension APIs</h3> + +<p>除了standard DOM APIS,content script还能使用以下WebExtension APIS:</p> + +<p>From <code><a href="/en-US/Add-ons/WebExtensions/API/extension">extension</a></code>:</p> + +<ul> + <li><code><a href="/en-US/Add-ons/WebExtensions/API/extension#getURL()">getURL()</a></code></li> + <li><code><a href="/en-US/Add-ons/WebExtensions/API/extension#inIncognitoContext">inIncognitoContext</a></code></li> +</ul> + +<p>From <code><a href="/en-US/Add-ons/WebExtensions/API/runtime">runtime</a></code>:</p> + +<ul> + <li><code><a href="/en-US/Add-ons/WebExtensions/API/runtime#connect()">connect()</a></code></li> + <li><code><a href="/en-US/Add-ons/WebExtensions/API/runtime#getManifest()">getManifest()</a></code></li> + <li><code><a href="/en-US/Add-ons/WebExtensions/API/runtime#getURL()">getURL()</a></code></li> + <li><code><a href="/en-US/Add-ons/WebExtensions/API/runtime#onConnect">onConnect</a></code></li> + <li><code><a href="/en-US/Add-ons/WebExtensions/API/runtime#onMessage">onMessage</a></code></li> + <li><code><a href="/en-US/Add-ons/WebExtensions/API/runtime#sendMessage()">sendMessage()</a></code></li> +</ul> + +<p>From <code><a href="/en-US/docs/Mozilla/Add-ons/WebExtensions/API/i18n">i18n</a></code>:</p> + +<ul> + <li><code><a href="/en-US/docs/Mozilla/Add-ons/WebExtensions/API/i18n/getMessage">getMessage()</a></code></li> + <li><code><a href="/en-US/docs/Mozilla/Add-ons/WebExtensions/API/i18n/getAcceptLanguages">getAcceptLanguages()</a></code></li> + <li><code><a href="/en-US/docs/Mozilla/Add-ons/WebExtensions/API/i18n/getUILanguage">getUILanguage()</a></code></li> + <li><code><a href="/en-US/docs/Mozilla/Add-ons/WebExtensions/API/i18n/detectLanguage">detectLanguage()</a></code></li> +</ul> + +<p>所有 <code><a href="/en-US/docs/Mozilla/Add-ons/WebExtensions/API/storage">storage</a></code>.</p> + +<h3 id="跨域名权限">跨域名权限</h3> + +<p>content scripts 拥有与扩展剩余部分一致的权限:所以如果这个扩展已在manifest.json文件中使用permission关键字请求跨域权限,其content script将能很好获取某些跨域权限。</p> + +<h2 id="后台脚本通信">后台脚本通信</h2> + +<p>尽管content scripts 不能直接使用大部分WebExtension APIS,但他们可以通过使用messaging APIS 与扩展的后台脚本通信,然后便能够间接地调用所有的后台脚本能够调用的APIS。</p> + +<p>在background script和content script中有两种基本的通讯方式:你可以发送带有可选回复的一次性的消息,或者在两者之间建立一个长期活动的连接并用这个连接来进行信息交换。</p> + +<h3 id="一次性消息">一次性消息</h3> + +<p>为了发送一个带有可选回复选项的一次性消息,你能使用以下APIS:</p> + +<table class="fullwidth-table standard-table"> + <thead> + <tr> + <th scope="row"> </th> + <th scope="col">In content script</th> + <th scope="col">In background script</th> + </tr> + </thead> + <tbody> + <tr> + <th scope="row">Send a message</th> + <td><code><a href="/en-US/Add-ons/WebExtensions/API/runtime#sendMessage()">browser.runtime.sendMessage()</a></code></td> + <td><code><a href="/en-US/Add-ons/WebExtensions/API/Tabs/sendMessage">browser.tabs.sendMessage()</a></code></td> + </tr> + <tr> + <th scope="row">Receive a message</th> + <td><code><a href="/en-US/Add-ons/WebExtensions/API/runtime/onMessage">browser.runtime.onMessage</a></code></td> + <td><code><a href="/en-US/Add-ons/WebExtensions/API/runtime#onMessage">browser.runtime.onMessage</a></code></td> + </tr> + </tbody> +</table> + +<p>举例,这里是一个监听点击事件的content script,如果点击发生在一个链接上,他将会将该链接的地址发送给后台脚本:</p> + +<pre class="brush: js">// content-script.js + +window.addEventListener("click", notifyExtension); + +function notifyExtension(e) { + if (e.target.tagName != "A") { + return; + } + browser.runtime.sendMessage({"url": e.target.href}); +}</pre> + +<p>后台脚本监听这个消息然后使用<code><a href="/en-US/docs/Mozilla/Add-ons/WebExtensions/API/notifications">notifications</a></code> API 显示一个通知:</p> + +<pre class="brush: js">// background-script.js + +browser.runtime.onMessage.addListener(notify); + +function notify(message) { + browser.notifications.create({ + "type": "basic", + "iconUrl": browser.extension.getURL("link.png"), + "title": "You clicked a link!", + "message": message.url + }); +} +</pre> + +<p>这个示范代码从Github上的 <a href="https://github.com/mdn/webextensions-examples/tree/master/notify-link-clicks-i18n">notify-link-clicks-i18n</a> 例子 修改而来.</p> + +<h3 id="Connection-based_messaging">Connection-based messaging</h3> + +<p>如果你将在一个content script 和 后台脚本间交换大量的消息,一次性消息会变得笨重而缓慢。所以一个更好的方案是在两个脚本间建立一个长久连接,然后使用该连接交换消息。</p> + +<p>每个脚本都有一个 <code><a href="/en-US/docs/Mozilla/Add-ons/WebExtensions/API/runtime/Port">runtime.Port</a></code> 对象用以交换消息。</p> + +<p>建立过程::</p> + +<ul> + <li> + <p>在一个脚本中使用 <code><a href="/en-US/docs/Mozilla/Add-ons/WebExtensions/API/runtime/onConnect">runtime.onConnect</a></code> 监听连接</p> + </li> + <li>另一个脚本中调用 <code><a href="/en-US/docs/Mozilla/Add-ons/WebExtensions/API/tabs/connect">tabs.connect()</a></code> (如果连接 content script) or <code><a href="/en-US/docs/Mozilla/Add-ons/WebExtensions/API/runtime/connect">runtime.connect()</a></code> (如果连接后台脚本). 这会返回一个 <code><a href="/en-US/docs/Mozilla/Add-ons/WebExtensions/API/runtime/Port">runtime.Port</a></code> 对象.</li> + <li> <code><a href="/en-US/docs/Mozilla/Add-ons/WebExtensions/API/runtime/onConnect">runtime.onConnect</a></code> 传递它自己的 <code><a href="/en-US/docs/Mozilla/Add-ons/WebExtensions/API/runtime/Port">runtime.Port</a></code> 对象.</li> +</ul> + +<p>每个脚本都拥有一个port,两个脚本可以使用runtime.Port.postMessage()来发送消息,runtime.Port.onMessage 来接收消息</p> + +<p>比如,当加载该content script时:</p> + +<ul> + <li>连接到后台脚本,存取Port对象至 <code>myPort</code></li> + <li>监听myPort上的消息,并记录。</li> + <li>当用户点击网页是发送消息至后台脚本。</li> +</ul> + +<pre class="brush: js">// content-script.js + +var myPort = browser.runtime.connect({name:"port-from-cs"}); +myPort.postMessage({greeting: "hello from content script"}); + +myPort.onMessage.addListener(function(m) { + console.log("In content script, received message from background script: "); + console.log(m.greeting); +}); + +document.body.addEventListener("click", function() { + myPort.postMessage({greeting: "they clicked the page!"}); +});</pre> + +<p>对应的后台脚本:</p> + +<ul> + <li>监听content script 的所有连接企图。</li> + <li>当收到连接请求后: + <ul> + <li>存贮Port对象至 <code>portFromCS</code></li> + <li>使用portFromCS发送一个消息到content script</li> + <li>开始监听消息并记录它们。</li> + </ul> + </li> + <li>当用户点击浏览器的某些扩展按钮或动作后,发送一个消息到content script。</li> +</ul> + +<pre class="brush: js">// background-script.js + +var portFromCS; + +function connected(p) { + portFromCS = p; + portFromCS.postMessage({greeting: "hi there content script!"}); + portFromCS.onMessage.addListener(function(m) { + console.log("In background script, received message from content script") + console.log(m.greeting); + }); +} + +browser.runtime.onConnect.addListener(connected); + +browser.browserAction.onClicked.addListener(function() { + portFromCS.postMessage({greeting: "they clicked the button!"}); +}); +</pre> + +<p> <a href="https://github.com/mdn/webextensions-examples/tree/master/inpage-toolbar-ui">inpage-toolbar-ui</a> 例子使用了 connection-based messaging.</p> + +<ul> +</ul> + +<h2 id="网页通信">网页通信</h2> + +<p>尽管content script 通常不能获取由网页脚本创建的对象,但他们可以通过 <code><a href="/en-US/docs/Web/API/Window/postMessage">window.postMessage</a></code> 和 <code><a href="/en-US/docs/Web/API/EventTarget/addEventListener">window.addEventListener</a></code> APIs 与网页脚本进行通信.</p> + +<p>比如:</p> + +<pre class="brush: js">// page-script.js + +var messenger = document.getElementById("from-page-script"); + +messenger.addEventListener("click", messageContentScript); + +function messageContentScript() { + window.postMessage({ + direction: "from-page-script", + message: "Message from the page" + }, "*");</pre> + +<pre class="brush: js">// content-script.js + +window.addEventListener("message", function(event) { + if (event.source == window && + event.data.direction && + event.data.direction == "from-page-script") { + alert("Content script received message: \"" + event.data.message + "\""); + } +});</pre> + +<p>完整的例子请访问该链接, <a href="https://mdn.github.io/webextensions-examples/content-script-page-script-messaging.html">visit the demo page on GitHub</a> 并且观看以下介绍.</p> + +<div class="warning"> +<p>需要注意的是当你用该方法与一些不被信任的网页进行交互式需要特别小心。WebExtensions拥有高等级权限,而一些恶意页面可以很轻松的获取这些权限。</p> + +<p>做一个微小的示范,假定有如下content script 代码:</p> + +<pre class="brush: js">// content-script.js + +window.addEventListener("message", function(event) { + if (event.source == window && + event.data.direction && + event.data.direction == "from-page-script") { + eval(event.data.message); + } +});</pre> + +<p>现在网页脚本可以在content script 权限范围内运行任何代码。</p> +</div> + +<h2 id="与页面脚本共享对象">与页面脚本共享对象</h2> + +<div class="note"> +<p>这个部分的技术描述只适用于49版本后的Firefox</p> +</div> + +<div class="warning"> +<p>作为一个插件开发者你必须考虑脚本运行在任何伺机偷取用户个人隐私,破坏他们的电脑,或者使用其他方式攻击的网页上。</p> + +<p>隔离content script 和 页面脚本 便是为了使恶意网页的攻击变得更加困难。</p> + +<p>这部分的技术打破了这个隔离,它们从根本上是危险的而应该被谨慎使用。</p> +</div> + +<p>我们在 <a href="/en-US/Add-ons/WebExtensions/Content_scripts#DOM_access">DOM access</a> 中看到content scripts不会察觉到通过网页脚本修改的某些属性. 这意味着,如果一个网页加载了一个库比如JQuery,content script 将不会使用它,而不得不加载它自己的一个复制。相反的,网页加载的脚本也不能获知content script的修改。</p> + +<p>然而,Firefox提供了一些APIS 可以使得content script能够:</p> + +<ul> + <li>访问页面脚本创建的Javascript 对象</li> + <li>暴露他们自己的JavaScript对象给页面脚本.</li> +</ul> + +<h3 id="Xray_vision_in_Firefox">Xray vision in Firefox</h3> + +<p>在Firefox中,隔离content script 和页面脚本通过使用一种称为“Xray vision”的功能实现。 当一个处于更高权限的脚本访问一个被定义于一个更低权限版本的域中时,它将只能看见这个对象的原始版本。</p> + +<p>任何 <a href="/en-US/docs/Glossary/Expando">expando</a> 属性都是不可见得, 而且如果对象的任何属性被重定义,他也只能能看见原始的实现而不是重定义的实现。</p> + +<p>这个功能的目的是为了让低权限的脚本不至于因为重定义原始对象属性而使高权限脚本行为异常。</p> + +<p>让我们来举个例子,当一个content script 访问一个页面的 <a href="/en-US/docs/Web/API/Window">window</a> 类,他不会看见任何该页面脚本对这个window 添加的任何属性, 如果页面脚本重定义了任何已存在的属性,content script将只能看见该属性的原始版本。</p> + +<p>更多信心请查看 <a href="/en-US/docs/Mozilla/Tech/Xray_vision">Xray vision</a> 和 <a href="/en-US/docs/Mozilla/Gecko/Script_security">Script security</a>.</p> + +<h3 id="从content_script_中访问_页面脚本对象">从content script 中访问 页面脚本对象</h3> + +<p>在Firefox中,content script中的DOM对象会获得一个额外的属性 wrappedJSObject。这是一个会包含任何由页面脚本所造成修改的”未包裹“对象。</p> + +<p>让我们来看一个简单的例子,假定一个页面载入脚本如下:</p> + +<pre class="brush: html"><!DOCTYPE html> +<html> + <head> + <meta charset="UTF-8"> + </head> + <body> + <script type="text/javascript" src="main.js"></script> + </body> +</html></pre> + +<p><code>这个脚本添加一个全局的属性到全局window</code>:</p> + +<pre class="brush: js">// main.js + +var foo = "I'm defined in a page script!";</pre> + +<p>Xray vision 意味着 如果一个content script 尝试访问foo,它将是未定义的:</p> + +<pre class="brush: js">// content-script.js + +console.log(window.foo); // undefined</pre> + +<p>在Firefox,content script 可以使用window.wrappedJSObject来看见全局属性:</p> + +<pre class="brush: js">// content-script.js + +console.log(window.wrappedJSObject.foo); // "I'm defined in a page script!"</pre> + +<p>注意因为这个原因,你最好不在依赖该对象的任何属性或方法 建立或执行某些操作,你所期望的,它们,甚至setter和getter 都可能被不被信任的代码重定义。</p> + +<p>同时注意unwarapping是及物的:当你使用wrappedJSObject,该未包裹对象的任何属性都是未包裹的(同时都是不可靠的),所以 一个好的建议是只在你需要时获取这个对象,重新包裹他,你能这样做:</p> + +<pre class="bz_comment_text" id="comment_text_38">XPCNativeWrapper(window.wrappedJSObject.foo);</pre> + +<p>查看 <a href="/en-US/docs/Mozilla/Tech/Xray_vision">Xray vision </a> 文档获取更多.</p> + +<h3 id="与页面脚本共享content_script_对象">与页面脚本共享content script 对象</h3> + +<p>Firefox 同样提供APIS允许content scripts 是对象对于页面脚本可用。这里是两个主要的APIS:</p> + +<ul> + <li><code><a href="/en-US/Add-ons/WebExtensions/Content_scripts#exportFunction">exportFunction()</a></code>: 导出一个函数至页面脚本</li> + <li><code><a href="/en-US/Add-ons/WebExtensions/Content_scripts#cloneInto">cloneInto()</a></code>: 导出一个对象至页面脚本</li> +</ul> + +<h4 id="exportFunction">exportFunction</h4> + +<p>给予一个定义于content script中的方法,exportFunction()导出他至页面脚本域,然后脚本可以调用它。</p> + +<p>比如,让我们考虑一个WebExtension有一个后台脚本如下:</p> + +<pre class="brush: js">/* +Execute content script in the active tab. +*/ +function loadContentScript() { + browser.tabs.executeScript({ + file: "/content_scripts/export.js" + }); +} + +/* +Add loadContentScript() as a listener to clicks +on the browser action. +*/ +browser.browserAction.onClicked.addListener(loadContentScript); + +/* +Show a notification when we get messages from +the content script. +*/ +browser.runtime.onMessage.addListener((message) => { + browser.notifications.create({ + type: "basic", + title: "Message from the page", + message: message.content + }); +});</pre> + +<p>该脚本做了两件事:</p> + +<ul> + <li>当用户点击浏览器按钮时,在当前标签执行一个content script 。</li> + <li>监听从content script 传递的消息,并在消息到达时显示一个通知。</li> +</ul> + +<p>content script 如下:</p> + +<pre class="brush: js">/* +Define a function in the content script's scope, then export it +into the page script's scope. +*/ +function notify(message) { + browser.runtime.sendMessage({content: "Function call: " + message}); +} + +exportFunction(notify, window, {defineAs:'notify'});</pre> + +<p>该脚本定义了一个函数notify()用以发送其参数到后台脚本,而后他导出了这个函数至页面脚本域。现在页面脚本可以调用该函数:</p> + +<pre class="brush: js">window.notify("Message from the page script!");</pre> + +<p>更详细的信息请看, <code><a href="/en-US/docs/Mozilla/Tech/XPCOM/Language_Bindings/Components.utils.exportFunction">Components.utils.exportFunction</a></code>.</p> + +<h4 id="cloneInto">cloneInto</h4> + +<p>给予一个定义于content script的对象, 该技术可以创建该对象的一个复制到页面脚本域,从而使该复制可以被页面脚本访问.通常使用 <a href="/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm">structured clone algorithm</a> 复制对象, 这意味着该对象中的方法不会被复制为了复制方法,需要传递cloneFunction选项.</p> + +<p>比如,这里有一个content script 定义了一个包含方法的对象,然后复制他们至页面脚本域:</p> + +<pre class="brush: js">/* +Create an object that contains functions in +the content script's scope, then clone it +into the page script's scope. + +Because the object contains functions, +the cloneInto call must include +the `cloneFunctions` option. +*/ +var messenger = { + notify: function(message) { + browser.runtime.sendMessage({ + content: "Object method call: " + message + }); + } +}; + +window.wrappedJSObject.messenger = cloneInto( + messenger, + window, + {cloneFunctions: true});</pre> + +<p><code>现在页面脚本将看到新的含有notify方法的属性</code>:</p> + +<pre class="brush: js">window.messenger.notify("Message from the page script!");</pre> + +<p>详情请看 <code><a href="/en-US/docs/Mozilla/Tech/XPCOM/Language_Bindings/Components.utils.cloneInto">Components.utils.cloneInto</a></code>.</p> + +<h2 id="在content_script中使用_eval()">在content script中使用 eval()</h2> + +<p>在 Chrome中,<code>eval()</code> 总是在content script的上下文环境中运行, 而不是在页面的上下文环境中运行.</p> + +<p>在Firefox中:</p> + +<ul> + <li>如果你调用<code>eval()</code>,它在content script的上下文中运行</li> + <li>如果你调用<code>window.eval()</code>,它在页面的上下文中运行</li> +</ul> + +<p>比如,有一个content script如下:</p> + +<pre class="brush: js line-numbers language-js"><code class="language-js"><span class="comment token">// content-script.js</span> + +window<span class="punctuation token">.</span><span class="function token">eval</span><span class="punctuation token">(</span><span class="string token">'window.x = 1;'</span><span class="punctuation token">)</span><span class="punctuation token">;</span> +<span class="function token">eval</span><span class="punctuation token">(</span><span class="string token">'window.y = 2'</span><span class="punctuation token">)</span><span class="punctuation token">;</span> + +console<span class="punctuation token">.</span><span class="function token">log</span><span class="punctuation token">(</span><span class="template-string token"><span class="string token">`In content script, window.x: </span><span class="interpolation token"><span class="interpolation-punctuation punctuation token">${</span>window<span class="punctuation token">.</span>x<span class="interpolation-punctuation punctuation token">}</span></span><span class="string token">`</span></span><span class="punctuation token">)</span><span class="punctuation token">;</span> +console<span class="punctuation token">.</span><span class="function token">log</span><span class="punctuation token">(</span><span class="template-string token"><span class="string token">`In content script, window.y: </span><span class="interpolation token"><span class="interpolation-punctuation punctuation token">${</span>window<span class="punctuation token">.</span>y<span class="interpolation-punctuation punctuation token">}</span></span><span class="string token">`</span></span><span class="punctuation token">)</span><span class="punctuation token">;</span> + +window<span class="punctuation token">.</span><span class="function token">postMessage</span><span class="punctuation token">(</span><span class="punctuation token">{</span> + message<span class="punctuation token">:</span> <span class="string token">"check"</span> +<span class="punctuation token">}</span><span class="punctuation token">,</span> <span class="string token">"*"</span><span class="punctuation token">)</span><span class="punctuation token">;</span></code></pre> + +<p>这段代码仅仅通过调用<code>window.eval()</code> 和 <code>eval()</code>创建了变量x和y。然后记录它们的值并通知页面更新.</p> + +<p>接收到消息后页面的脚本记录下这些变量:</p> + +<pre class="brush: js line-numbers language-js"><code class="language-js">window<span class="punctuation token">.</span><span class="function token">addEventListener</span><span class="punctuation token">(</span><span class="string token">"message"</span><span class="punctuation token">,</span> <span class="keyword token">function</span><span class="punctuation token">(</span>event<span class="punctuation token">)</span> <span class="punctuation token">{</span> + <span class="keyword token">if</span> <span class="punctuation token">(</span>event<span class="punctuation token">.</span>source <span class="operator token">===</span> window <span class="operator token">&&</span> event<span class="punctuation token">.</span>data <span class="operator token">&&</span> event<span class="punctuation token">.</span>data<span class="punctuation token">.</span>message <span class="operator token">===</span> <span class="string token">"check"</span><span class="punctuation token">)</span> <span class="punctuation token">{</span> + console<span class="punctuation token">.</span><span class="function token">log</span><span class="punctuation token">(</span><span class="template-string token"><span class="string token">`In page script, window.x: </span><span class="interpolation token"><span class="interpolation-punctuation punctuation token">${</span>window<span class="punctuation token">.</span>x<span class="interpolation-punctuation punctuation token">}</span></span><span class="string token">`</span></span><span class="punctuation token">)</span><span class="punctuation token">;</span> + console<span class="punctuation token">.</span><span class="function token">log</span><span class="punctuation token">(</span><span class="template-string token"><span class="string token">`In page script, window.y: </span><span class="interpolation token"><span class="interpolation-punctuation punctuation token">${</span>window<span class="punctuation token">.</span>y<span class="interpolation-punctuation punctuation token">}</span></span><span class="string token">`</span></span><span class="punctuation token">)</span><span class="punctuation token">;</span> + <span class="punctuation token">}</span> +<span class="punctuation token">}</span><span class="punctuation token">)</span><span class="punctuation token">;</span></code></pre> + +<p>在Chrome中,输出类似下面所示:</p> + +<pre class="line-numbers language-html"><code class="language-html">In content script, window.x: 1 +In content script, window.y: 2 +In page script, window.x: undefined +In page script, window.y: undefined</code></pre> + +<p>在Firefox中,输出类似下面所示:</p> + +<pre class="line-numbers language-html"><code class="language-html">In content script, window.x: undefined +In content script, window.y: 2 +In page script, window.x: 1 +In page script, window.y: undefined</code></pre> + +<p>上述内容同样适用于 <code><a href="https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setTimeout">setTimeout()</a></code>, <code><a href="https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setInterval">setInterval()</a></code>, and <code><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function">Function()</a></code>.</p> + +<p>当在页面的上下文中运行代码时, 适用于上面所提到的"<a href="https://developer.mozilla.org/en-US/Add-ons/WebExtensions/Content_scripts#Sharing_objects_with_page_scripts">Sharing content script objects with page scripts</a>" 这一部分的警告: 页面的环境可能会被恶意的网页所控制,这可能会导致你所交互的对象会有意想不到的行为:</p> + +<pre class="brush: js line-numbers language-js"><code class="language-js"><span class="comment token">// page.js redefines console.log</span> + +<span class="keyword token">var</span> original <span class="operator token">=</span> console<span class="punctuation token">.</span>log<span class="punctuation token">;</span> + +console<span class="punctuation token">.</span>log <span class="operator token">=</span> <span class="keyword token">function</span><span class="punctuation token">(</span><span class="punctuation token">)</span> <span class="punctuation token">{</span> + <span class="function token">original</span><span class="punctuation token">(</span><span class="keyword token">true</span><span class="punctuation token">)</span><span class="punctuation token">;</span> +<span class="punctuation token">}</span></code></pre> + +<pre class="brush: js line-numbers language-js"><code class="language-js"><span class="comment token">// content-script.js calls the redefined version</span> + +window<span class="punctuation token">.</span><span class="function token">eval</span><span class="punctuation token">(</span><span class="string token">'console.log(false)'</span><span class="punctuation token">)</span><span class="punctuation token">;</span></code></pre> |