diff options
Diffstat (limited to 'files/zh-cn/mozilla/add-ons/sdk/guides/content_scripts/index.html')
| -rw-r--r-- | files/zh-cn/mozilla/add-ons/sdk/guides/content_scripts/index.html | 486 |
1 files changed, 0 insertions, 486 deletions
diff --git a/files/zh-cn/mozilla/add-ons/sdk/guides/content_scripts/index.html b/files/zh-cn/mozilla/add-ons/sdk/guides/content_scripts/index.html deleted file mode 100644 index fa95b15db3..0000000000 --- a/files/zh-cn/mozilla/add-ons/sdk/guides/content_scripts/index.html +++ /dev/null @@ -1,486 +0,0 @@ ---- -title: Content Scripts(内容脚本) -slug: Mozilla/Add-ons/SDK/Guides/Content_Scripts -translation_of: Archive/Add-ons/Add-on_SDK/Guides/Content_Scripts ---- -<article id="wikiArticle">{{AddonSidebar}} -<p><span class="seoSummary">很多 add-ons 需要访问和修改 web 页面的内容。但是 add-on 的主代码不能直接访问 web 内容。替代方案是, SDK add-ons 需要使用一些分散的脚本代理访问 web 内容,这些脚本被称作<em>内容脚本(content scripts)</em>。本页面描述如何开发和部署内容脚本。 </span></p> - -<p>内容脚本是在使用SDK时很令人疑惑的点,但你很有可能不得不使用它们。下面有五个基本原则:</p> - -<ul> - <li>add-on 的主代码,包括"main.js"和其他"lib"下的模块,可以使用 SDK <a href="/zh-CN/docs/Mozilla/Add-ons/SDK/High-Level_APIs">高层次</a>和<a href="/zh-CN/docs/Mozilla/Add-ons/SDK/Low-Level_APIs">低层次</a> APIs,但不能直接访问 web 内容</li> - <li>内容脚本 <a href="/en-US/Add-ons/SDK/Guides/Two_Types_of_Scripts#API_Access_for_Add-on_Code_and_Content_Scripts">不能使用 SDK 的 API</a>(访问不了 globals 的 <code>exports</code>、<code>require</code>),但你可以访问 web 内容</li> - <li>SDK API 可以使用,内容脚本,比如 <a href="/en-US/Add-ons/SDK/High-Level_APIs/page-mod">page-mod</a> 和 <a href="/en-US/Add-ons/SDK/High-Level_APIs/tabs">tabs</a>,提供了一些函数,使得 add-on 的主代码可以将内容脚本载入web页面中。</li> - <li>内容脚本可以作为字符串加载,但是更常见的是分离存储为 add-on 的"data"目录下文件。 jpm 不会默认创建"data"目录,所以你必须添加该目录并把脚本放进去。</li> - <li>一个消息传递 API 允许主代码和内容脚本间相互通信。</li> -</ul> - -<p>这个完整的 add-on 表现出所有的这些原则。它的"main.js"使用 <a href="/en-US/Add-ons/SDK/High-Level_APIs/tabs">tabs</a> 模块附加了一个内容脚本到当前标签页。本例中内容脚本作为字符串传递,内容脚本简单地替换了页面的内容:</p> - -<pre class="brush: js">// main.js -var tabs = require("sdk/tabs"); -var contentScriptString = 'document.body.innerHTML = "<h1>this page has been eaten</h1>";' - -tabs.activeTab.attach({ - contentScript: contentScriptString -});</pre> - -<p>下面的高层次 SDK 模块能使用内容脚本来修改 web 页面:</p> - -<ul> - <li><a href="/en-US/Add-ons/SDK/High-Level_APIs/page-mod">page-mod</a>:使你能附加一个内容脚本到匹配上特定 URL 模式的web页面。</li> - <li><a href="/en-US/Add-ons/SDK/High-Level_APIs/tabs">tabs</a>:导出一个 <code>Tab</code> 对象来处理浏览器标签页。<code>Tab</code> 对象包括了一个 <a href="/en-US/Add-ons/SDK/High-Level_APIs/tabs#attach(options)"><code>attach()</code></a> 函数来附加内容脚本到标签页。</li> - <li><a href="/en-US/Add-ons/SDK/High-Level_APIs/page-worker">page-worker</a>:让你能够恢复一个 web 页面,但不显示它。你可以附加内容脚本到该页面,来访问和操作该页面的 DOM。</li> - <li><a href="/en-US/Add-ons/SDK/High-Level_APIs/context-menu">context-menu</a>:使用内容脚本来和按钮所在的页面交互。</li> -</ul> - -<p>另外,还能使用 HTML 定义了一些 SDK 用户接口组件,并且使用分类的脚本来和这些内容交互。从很多方面来讲,这些脚本就像内容脚本一样,但它们并不是本文的关注点。要学习如何和用户接口模块的内容交互,请参看模块定义文档:<a href="/en-US/Add-ons/SDK/High-Level_APIs/panel">panel</a>、<a href="/en-US/Add-ons/SDK/Low-Level_APIs/ui_sidebar">sidebar</a>、<a href="/en-US/Add-ons/SDK/Low-Level_APIs/ui_frame">frame</a>。</p> - -<p>这篇指南中列出的几乎所有的示例都是完整并且且最小的,可以在 Github 的 <a href="https://github.com/mdn/addon-sdk-content-scripts">addon-sdk-content-scripts repository</a> 页面上获得。</p> - -<h2 id="加载用户脚本">加载用户脚本</h2> - -<article id="wikiArticle"> -<p>你可以声明一个字符串或者指定 <code>contentScript</code> 或 <code>contentScriptFile</code> 选项加载一个单独的脚本。<code>contentScript</code> 选项接受一个作为脚本的字符串:</p> - -<pre class="brush: js">// main.js - -var pageMod = require("sdk/page-mod"); -var contentScriptValue = 'document.body.innerHTML = ' + - ' "<h1>Page matches ruleset</h1>";'; - -pageMod.PageMod({ - include: "*.mozilla.org", - contentScript: contentScriptValue -});</pre> - -<p><code>contentScriptFile</code> 选项接受一个作为 resource:// URL 的字符串,指向一个存储在你的 add-on 的 <code>data</code> 目录中的脚本文件。jpm不会默认创建"data"目录,所以你必须创建该目录并将你的用户脚本放进去。</p> - -<p>本 add-on 提供一个 URL ,指向"content-script.js"文件,存储在 add-on 根目录下的 <code>data</code> 子目录:</p> - -<pre class="brush: js">// main.js - -var data = require("sdk/self").data; -var pageMod = require("sdk/page-mod"); - -pageMod.PageMod({ - include: "*.mozilla.org", - contentScriptFile: data.url("content-script.js") -});</pre> - -<pre class="brush: js">// content-script.js - -document.body.innerHTML = "<h1>Page matches ruleset</h1>";</pre> - -<div class="note"> -<p>从 Firefox 34 开始,你可以使用"./content-script.js"替代 self.data.url("content-script.js")。所以你可以像这样重写:</p> - -<pre class="brush: js">var pageMod = require("sdk/page-mod"); - -pageMod.PageMod({ - include: "*.mozilla.org", - contentScriptFile: "./content-script.js" -}); -</pre> -</div> - -<div class="warning"> -<p>除非你的内容脚本非常简单并且固定是一个静态的字符串,请不要使用 <code>contentScript</code>:否则,你会在从 AMO 获取你的add-on上遇到问题。</p> - -<p>相反,把脚本放到一个单独的文件并用 <code>contentScriptFile</code> 加载它。这回事你的代码更易维护、安全、调试和审核。</p> -</div> - -<p>你可以给 <code>contentScript</code> 或 <code>contentScriptFile</code> 传递字符串数组来加载多个脚本:</p> - -<pre class="brush: js">// main.js - -var tabs = require("sdk/tabs"); - -tabs.on('ready', function(tab) { - tab.attach({ - contentScript: ['document.body.style.border = "5px solid red";', 'window.alert("hi");'] - }); -}); -</pre> - -<pre class="brush: js">// main.js - -var data = require("sdk/self").data; -var pageMod = require("sdk/page-mod"); - -pageMod.PageMod({ - include: "*.mozilla.org", - contentScriptFile: [data.url("jquery.min.js"), data.url("my-content-script.js")] -});</pre> - -<p>如果你这么做,这些脚本之间可以直接交互,就像他们被同一个 web 页面加载一样。</p> - -<p>你也可以把 <code>contentScript</code> 和 <code>contentScriptFile</code> 一起用。如果你这么做,使用 <code>contentScriptFile</code> 定义的脚本会在使用 <code>contentScript</code> 定义的脚本之前加载。这使你能够用 URL 加载比如 jQuery 这样的 JavaScript 库,然后传递一个简单的能够使用jQuery脚本:</p> - -<pre class="brush: js">// main.js - -var data = require("sdk/self").data; -var pageMod = require("sdk/page-mod"); - -var contentScriptString = '$("body").html("<h1>Page matches ruleset</h1>");'; - -pageMod.PageMod({ - include: "*.mozilla.org", - contentScript: contentScriptString, - contentScriptFile: data.url("jquery.js") -});</pre> - -<div class="warning"> -<p>除非你的内容脚本非常简单并且固定是一个静态的字符串,请不要使用 <code>contentScript</code>:否则,在从 AMO 获取你的 add-on 上,你会遇到问题。</p> - -<p>相反,把脚本放到一个单独的文件并用 <code>contentScriptFile</code> 加载它。这回事你的代码更易维护、安全、调试和审核。</p> -</div> - -<h3 id="控制附加脚本的时间">控制附加脚本的时间</h3> - -<p><code>contentScriptWhen</code> 选项指定了什么时候加载内容脚本。从这里选一个:</p> - -<ul> - <li><code>"start"</code>:页面 document 元素插入 DOM 之后,立即加载脚本。这时 DOM 的内容仍未加载,所以脚本不能与其交互。</li> - <li><code>"ready"</code>:页面 DOM 加载完后加载脚本:也就是说,在那个时间点 <a href="https://developer.mozilla.org/en/Gecko-Specific_DOM_Events">DOMContentLoaded</a> 事件触发。这时,内容脚本可以和DOM内容交互,但外部引用的样式表和图片可能还没有完成加载。</li> - <li><code>"end"</code>:页面上所有内容(DOM、JS、CSS、images)加载完后,加载脚本,就是在 <a href="https://developer.mozilla.org/en/DOM/window.onload">window.onload 事件</a>触发的时候</li> -</ul> - -<p>默认值为 <code>"end"</code>。</p> - -<p>注意 <a href="/en-US/Add-ons/SDK/High-Level_APIs/tabs#attach(options)"><code>tab.attach()</code></a> 不支持 contentScriptWhen,因为它原来就是在页面加载页面的时候被调用的。</p> - -<h3 id="传递配置选项">传递配置选项</h3> - -<p><code>contentScriptOptions</code> 是一个作为只读对象暴露给内容脚本的JSON对象,在 <code><a href="/en-US/Add-ons/SDK/Guides/Content_Scripts/self">self</a>.options</code> 的属性里:</p> - -<pre class="brush: js">// main.js - -var tabs = require("sdk/tabs"); - -tabs.on('ready', function(tab) { - tab.attach({ - contentScript: 'window.alert(self.options.message);', - contentScriptOptions: {"message" : "hello world"} - }); -});</pre> - -<p>这里可以使用任何可以转成json的值(object、array、string等等)。</p> - -<h2 id="访问_DOM">访问 DOM</h2> - -<p>内容脚本可以访问页面的 DOM,就像任何页面中加载的脚本(页面脚本)一样。但是内容脚本和页面脚本之间是隔离的:</p> - -<ul> - <li>内容脚本不能看到任何由页面脚本添加到页面的 JavaScript 对象</li> - <li>如果页面脚本重定义了某个 DOM 对象的行为,但内容脚本只会看到原来的那个行为。</li> -</ul> - -<p>相反也是如此:页面脚本不能看到内容脚本添加的 JavaScript 对象。</p> - -<p>例如,假想一个页面用页面脚本添加变量 <code>foo</code> 到 <code>window</code> 对象:</p> - -<pre class="brush: html"><!DOCTYPE html"> -<html> - <head> - <script> - window.foo = "hello from page script" - </script> - </head> -</html></pre> - -<p>在这个脚本后面加载到页面的其他脚本也可以访问 <code>foo</code>。但是内容脚本不能:</p> - -<pre class="brush: js">// main.js - -var tabs = require("sdk/tabs"); -var mod = require("sdk/page-mod"); -var self = require("sdk/self"); - -var pageUrl = self.data.url("page.html") - -var pageMod = mod.PageMod({ - include: pageUrl, - contentScript: "console.log(window.foo);" -}) - -tabs.open(pageUrl);</pre> - -<pre>console.log: my-addon: null -</pre> - -<p>这种隔离策略有着很合理的理由。首先,这意味着内容脚本不会泄露对象给 web 页面,这样可能会打开安全漏洞。第二,这意味着,在内容脚本创建对象的时候,可以不用担心是否会和页面脚本添加的对象相冲突。</p> - -<p>这种隔离意味着,例如,如果一个 web 页面加载了 jQuery 库,那么内容脚本不能够看到由该库添加的 <code>jQuery</code> 对象——但是可以看到内容脚本添加的自己的 <code>jQuery</code> 对象,并且它不会和页面脚本的 jQuery 版本冲突。</p> - -<h3 id="和页面脚本交互">和页面脚本交互</h3> - -<p>一般来说,这种内容脚本和页面脚本的隔离正是你所希望的。但是有时候你也许会希望和页面脚本交互:你想在内容脚本和页面脚本之间共享对象来,来在它们之间发送消息。如果你需要这么做,请阅读<a href="/en-US/Add-ons/SDK/Guides/Content_Scripts/Interacting_with_page_scripts">和页面脚本交互</a>。</p> - -<h3 id="事件监听器">事件监听器</h3> - -<p>你可以监听 DOM 的事件,就像在页面脚本中一样,但是有两个重要的区别:</p> - -<p>第一,如果你向 <a href="https://developer.mozilla.org/en/DOM/element.setAttribute"><code>setAttribute()</code></a> 传递字符串,来定义了事件监听器,那么此监听器被当做是在页面上下文中的,所以它不能访问任何内容脚本中的变量。</p> - -<p>如下,内容脚本会失败报错"theMessage is not defined":</p> - -<pre class="brush: js">var theMessage = "Hello from content script!"; -anElement.setAttribute("onclick", "alert(theMessage);");</pre> - -<p>Second, if you define an event listener by direct assignment to a <a href="/en-US/docs/Web/API/GlobalEventHandlers">global event handler</a> like <code>onclick</code>, then the assignment might be overridden by the page. For example, here's an add-on that tries to add a click handler by assignment to <code>window.onclick</code>:</p> - -<pre class="brush: js">var myScript = "window.onclick = function() {" + - " console.log('unsafewindow.onclick: ' + window.document.title);" + - "}"; - -require("sdk/page-mod").PageMod({ - include: "*", - contentScript: myScript, - contentScriptWhen: "start" -});</pre> - -<p>这个示例会在大多数页面上正常工作,但是会在定义 <code>onclick</code> 的页面上失败:</p> - -<pre class="brush: html"><html> - <head> - </head> - <body> - <script> - window.onclick = function() { - window.alert("it's my click now!"); - } - </script> - </body> -</html></pre> - -<p>由于这些原因,最好还是用 <a href="https://developer.mozilla.org/en/DOM/element.addEventListener"><code>addEventListener()</code> 添加一个事件监听器</a>,定义监听器为一个函数:</p> - -<pre class="brush: js">var theMessage = "Hello from content script!"; - -anElement.onclick = function() { - alert(theMessage); -}; - -anotherElement.addEventListener("click", function() { - alert(theMessage); -});</pre> - -<h2 id="和_add-on_通信">和 add-on 通信</h2> - -<p>为了使 add-on 脚本和内容脚本相互通信,任何一通信端都要访问 <code>port</code> 对象。</p> - -<ul> - <li>要从一头发送消息到另一头,使用 <code>port.emit()</code></li> - <li>要从另一头接收消息,使用 <code>port.on()</code></li> -</ul> - -<p><img alt="" src="https://mdn.mozillademos.org/files/7873/content-scripting-overview.png" style="display: block; margin-left: auto; margin-right: auto;">消息是异步的:也就是说,发送方不会等待接收方的回应,而仅仅是发送消息完后继续处理别的事情。</p> - -<p>这里有一个简单的 add-on 使用 <code>port</code> 发送一个消息到内容脚本:</p> - -<pre class="brush: js">// main.js - -var tabs = require("sdk/tabs"); -var self = require("sdk/self"); - -tabs.on("ready", function(tab) { - var worker = tab.attach({ - contentScriptFile: self.data.url("content-script.js") - }); - worker.port.emit("alert", "Message from the add-on"); -}); - -tabs.open("http://www.mozilla.org");</pre> - -<pre class="brush: js">// content-script.js - -self.port.on("alert", function(message) { - window.alert(message); -});</pre> - -<div class="note"> -<p>context-menu 模块没有使用这里描述的通信模型。了解更多关于使用 context-menu 和内容脚本通信的事情,参看 <a href="/en-US/Add-ons/SDK/High-Level_APIs/context-menu">context-menu documentation</a>。</p> -</div> - -<h3 id="在内容脚本中访问_port"><code>在内容脚本中访问 port</code></h3> - -<p>内容脚本中,<code>port</code> 对象是作为global下 <a href="/en-US/Add-ons/SDK/Guides/Content_Scripts/self"><code>self</code></a> 对象的属性。所以要从内容脚本中发送消息的话:</p> - -<pre class="brush: js">self.port.emit("myContentScriptMessage", myContentScriptMessagePayload);</pre> - -<p>要从 add-on 代码接收消息</p> - -<pre class="brush: js">self.port.on("myAddonMessage", function(myAddonMessagePayload) { - // Handle the message -});</pre> - -<div class="note"> -<p>注意 global下 <a href="/en-US/Add-ons/SDK/Guides/Content_Scripts/self"><code>self</code></a> 对象和 <a href="https://developer.mozilla.org/en-US/Add-ons/SDK/High-Level_APIs/self"><code>self</code> 模块</a>完全不一样,后者提供一个API给 add-on,用来访问它的数据文件和ID。</p> -</div> - -<h3 id="在内容脚本中访问_port_2">在内容脚本中访问 port</h3> - -<p>在 add-on 代码中,联通 add-on 和某一特定内容脚本上下文的通道被封装入 <a href="https://developer.mozilla.org/en-US/Add-ons/SDK/Low-Level_APIs/content_worker"><code>worker</code></a> 对象。所以和内容脚本通信的 <code>port</code> 对象其实是其相对应的 <code>worker</code> 对象的一个属性。</p> - -<p>但是,这个 worker 没有暴露给 add-on 代码,以及同样所有的模块。</p> - -<h4 id="从_page-worker">从 <code>page-worker</code></h4> - -<p><code>page-worker</code> 对象直接整合了 work API。所以要从一个由 <code>page-worker</code> 关联的内容脚本接收消息的话,你可以使用 <code>pageWorker.port.on()</code>:</p> - -<pre class="brush: js">// main.js - -var self = require("sdk/self"); - -var pageWorker = require("sdk/page-worker").Page({ - contentScriptFile: self.data.url("content-script.js"), - contentURL: "http://en.wikipedia.org/wiki/Internet" -}); - -pageWorker.port.on("first-para", function(firstPara) { - console.log(firstPara); -});</pre> - -<p>要从你的 add-on 发送用户定义的消息,你可以只调用 <code>pageWorker.port.emit()</code>:</p> - -<pre class="brush: js">// main.js - -var self = require("sdk/self"); - -var pageWorker = require("sdk/page-worker").Page({ - contentScriptFile: self.data.url("content-script.js"), - contentURL: "http://en.wikipedia.org/wiki/Internet" -}); - -pageWorker.port.on("first-para", function(firstPara) { - console.log(firstPara); -}); - -pageWorker.port.emit("get-first-para");</pre> - -<pre class="brush: js">// content-script.js - -self.port.on("get-first-para", getFirstPara); - -function getFirstPara() { - var paras = document.getElementsByTagName("p"); - if (paras.length > 0) { - var firstPara = paras[0].textContent; - self.port.emit("first-para", firstPara); - } -}</pre> - -<h4 id="从_page-mod">从<code> page-mod</code></h4> - -<p>单个 <code>page-mod</code> 对象可以附加它的脚本到多个页面,每个页面有它自己的上下文来运行内容脚本,所以每个页面都需要相互隔离的通道(worker)。</p> - -<p>所以 <code>page-mod</code> 没有直接整合 worker 的 API。而是在每次内容脚本被附加到页面时,page-mod 发送一个 <code>attach</code> 事件,它的监听器会给对应的上下文传递一个 worker。通过为 <code>attach</code> 提供一个监听器,你可以访问被一个 page-mod 附加到页面上的内容脚本的 <code>port</code> 对象:</p> - -<pre class="brush: js">// main.js - -var pageMods = require("sdk/page-mod"); -var self = require("sdk/self"); - -var pageMod = pageMods.PageMod({ - include: ['*'], - contentScriptFile: self.data.url("content-script.js"), - onAttach: startListening -}); - -function startListening(worker) { - worker.port.on('click', function(html) { - worker.port.emit('warning', 'Do not click this again'); - }); -}</pre> - -<pre class="brush: js">// content-script.js - -window.addEventListener('click', function(event) { - self.port.emit('click', event.target.toString()); - event.stopPropagation(); - event.preventDefault(); -}, false); - -self.port.on('warning', function(message) { - window.alert(message); -}); -</pre> - -<p>上面的 add-on 里有两条消息:</p> - -<ul> - <li>当用户点击页面元素时,<code>click</code> 从 page-mod 被发送到当前 add-on。</li> - <li><code>warning</code> 发送一条傻气的字符串回给page-mod</li> -</ul> - -<h4 id="从_Tab.attach()">从 <code>Tab.attach()</code></h4> - -<p><code>Tab.attach()</code> 方法返回一个 worker,你可以用来和附加的内容脚本通信。</p> - -<p>这个 add-on 添加了一个按钮到Firefox:等用户点击按钮是,这个 add-on 附加一个内容脚本到当前的标签页,发送给内容脚本一条名为 "my-addon-message"的消息,并且监听名为"my-script-response"的响应:</p> - -<pre class="brush: js">//main.js - -var tabs = require("sdk/tabs"); -var buttons = require("sdk/ui/button/action"); -var self = require("sdk/self"); - -buttons.ActionButton({ - id: "attach-script", - label: "Attach the script", - icon: "./icon-16.png", - onClick: attachScript -}); - -function attachScript() { - var worker = tabs.activeTab.attach({ - contentScriptFile: self.data.url("content-script.js") - }); - worker.port.on("my-script-response", function(response) { - console.log(response); - }); - worker.port.emit("my-addon-message", "Message from the add-on"); -} -</pre> - -<pre class="brush: js">// content-script.js - -self.port.on("my-addon-message", handleMessage); - -function handleMessage(message) { - alert(message); - self.port.emit("my-script-response", "Response from content script"); -}</pre> - -<h3 id="port的API">port的API</h3> - -<p>参看 <a href="/en-US/Add-ons/SDK/Guides/Content_Scripts/port"><code>port</code> 对象的参考文档</a>.</p> -</article> - -<h3 id="postMessage的API">postMessage的API</h3> - -<p>在 <code>port</code> 对象加载之前,add-on 代码和内容脚本可以使用另一个 API 通信:</p> - -<ul> - <li>内容脚本调用 <code>self.postMessage()</code> 来发送,并用 <code>self.on()</code> 来接收</li> - <li>内容脚本调用 <code>worker.postMessage()</code> 来发送,并用 <code>worker.on()</code> 来接收</li> -</ul> - -<p>这个API依然可用,并且还有<a href="/en-US/Add-ons/SDK/Guides/Content_Scripts/using_postMessage">文档</a>,但是没有理由替代前文描述的 <code>port</code> API。 例外是 <a href="/en-US/Add-ons/SDK/High-Level_APIs/context-menu">context-menu</a> 模块,它还是使用 postMessage。</p> - -<h3 id="内容脚本的内容脚本">内容脚本的内容脚本</h3> - -<p>内容脚本可用直接和其他同一个上下文中的内容脚本通信。举个例子,如果一次 <code>Tab.attach()</code> 的调用附加了两个脚本,那么他们可用直接相互查看,就像加载在同一页面内的页面脚本一样。但是如果你调用 <code>Tab.attach()</code> 两次,每次附加一个内容脚本,那么这些内容脚本之间不能通信。你必须使用port API 通过 add-on 的主代码来传递消息。</p> - -<h2 id="跨域的内容脚本">跨域的内容脚本</h2> - -<p>默认情况下,内容脚本没有跨域的权限。特别是,它们不能访问在不同 <code>iframe</code> 中的在另外的域名上的内容,也不能发起跨域的 XMLHttpRequests。</p> - -<p>但是,你可以把需要的域名添加到 <a href="/en-US/Add-ons/SDK/Tools/package_json">package.json</a> 中<code>"permissions"</code>键下的 <code>"cross-domain-content"</code>键下,为这些域名打开这些特性。参阅文章<a href="/en-US/Add-ons/SDK/Guides/Content_Scripts/Cross_Domain_Content_Scripts">跨域内容脚本</a>。</p> -</article> - -<div id="xunlei_com_thunder_helper_plugin_d462f475-c18e-46be-bd10-327458d045bd"> </div> - -<div id="xunlei_com_thunder_helper_plugin_d462f475-c18e-46be-bd10-327458d045bd"> </div> |
