1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
|
---
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>
|