aboutsummaryrefslogtreecommitdiff
path: root/files/ru/mozilla/add-ons/sdk/guides/скрипты_содержимого/index.html
blob: 59832331e8afd34ea805ee60e755808a097f5d7a (plain)
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
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
---
title: Скрипты Content Scripts
slug: Mozilla/Add-ons/SDK/Guides/Скрипты_содержимого
tags:
  - Content script
  - Дополнение
translation_of: Archive/Add-ons/Add-on_SDK/Guides/Content_Scripts
---
<article id="wikiArticle">
<p>{{AddonSidebar}}</p>

<div class="blockIndicator warning">
<p>Support for extensions using XUL/XPCOM or the Add-on SDK was removed in Firefox 57, released November 2017. As there is no supported version of Firefox enabling these technologies, this page will be removed by December 2020.</p>
</div>

<p>{{LegacyAddonsNotice}}</p>

<p>Многим дополнениям (add-on) необходим доступ к веб-страницам и возможность их изменения. Но основной код дополнения не имеет прямого доступа к веб-содержимому. Взамен, SDK-дополнений необходим способ в коде, который даст доступ к веб-содержимому в отдельных скриптах, которые называются <code>content scripts</code> (скрипты содержимого). Эта страница описывает как разрабатывать и реализовывать <code>content scripts</code>.</p>

<p>Скрипты <code>content scripts</code>, вероятно, один из наиболее сбивающих с толку аспектов при работе с SDK, но вам они скорее всего будут нужны. Существуют пять основных принципов:</p>

<ul>
 <li>расширения основного кода, включая "main.js" и другие модули в "lib", могут использовать SDK <a href="/en-US/Add-ons/SDK/High-Level_APIs">верхнего-уровня</a> и <a href="/en-US/Add-ons/SDK/Low-Level_APIs">нижнего-уровня</a> API, но не имеют доступа к веб-содержимому напрямую;</li>
 <li>скрипты <code>content scripts </code><a href="/en-US/Add-ons/SDK/Guides/Two_Types_of_Scripts#API_Access_for_Add-on_Code_and_Content_Scripts">не могут использовать API в SDK</a> (нет доступа к глобальным <code>exports</code>, <code>require</code>) но есть доступ к веб-содержимому;</li>
 <li>API в SDK которые используют <code>content scripts</code>, например <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>, предоставляют функции, которые позволяют коду расширения загружать скрипты содержимого в веб-страницы;</li>
 <li>скрипты <code>content scripts </code>могут быть загружены как строки, но чаще они хранятся как отдельные файлы в папке "data". jpm не создаёт каталог "data" по умолчанию, поэтому вы должны создать его и положить туда ваши скрипты;</li>
 <li>API передачи сообщений позволяет основному коду и скриптам <code>content scripts </code>взаимодействовать друг с другом.</li>
</ul>

<p>Следующее дополнение (полностью завершённое) показывает эти принципы. "main.js" прикрепляет <code>content scripts</code> к текущей вкладке, используя модуль <a href="/en-US/Add-ons/SDK/High-Level_APIs/tabs">tabs</a>. В этом случае, <code>content scripts</code> передаётся, как строка. Скрипт <code>content scripts</code> просто заменяет содержимое страницы:</p>

<pre class="brush: js">// main.js
var tabs = require("sdk/tabs");
var contentScriptString = 'document.body.innerHTML = "&lt;h1&gt;this page has been eaten&lt;/h1&gt;";'

tabs.activeTab.attach({
  contentScript: contentScriptString
});</pre>

<p>Следующие высокоуровневые SDK-модули, могут использовать скрипты <code>content scripts </code>для изменения веб-страниц:</p>

<ul>
 <li><a href="/en-US/Add-ons/SDK/High-Level_APIs/page-mod">page-mod</a>: позволяет вам прикреплять <code>content scripts</code> к веб-страницам, которые соответствуют заданному URL шаблону.</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>, которая позволяет прикрепить <code>content scripts</code> ко вкладке.</li>
 <li><a href="/en-US/Add-ons/SDK/High-Level_APIs/page-worker">page-worker</a>: позволяет вам получить страницу, без отображения её. Вы можете прикрепить <code>content scripts</code> к странице, чтобы иметь доступ и возможность изменять DOM страницы.</li>
 <li><a href="/en-US/Add-ons/SDK/High-Level_APIs/context-menu">context-menu</a>: использует <code>content scripts</code> для взаимодействия со страницей, в которой вызывается меню.</li>
</ul>

<p>В дополнение к этому, некоторые SDK компоненты пользовательского интерфейса - <code>panel, sidebar, frame</code> - заданы в помощью HTML, и необходимо использовать отдельные скрипты для взаимодействия с их контентом. В большинстве случаев они похожи на скрипты <code>content scripts</code>, но в данной статье они не описываются. Для изучения способов взаимодействия с данными модулями пользовательского интерфейса обратитесь к документации: <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="Загрузка_content_scripts">Загрузка content scripts</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 = ' +
                         ' "&lt;h1&gt;Page matches ruleset&lt;/h1&gt;";';

pageMod.PageMod({
  include: "*.mozilla.org",
  contentScript: contentScriptValue
});</pre>

<p>Атрибут <code>contentScriptFile</code> определяет строковое значение как путь к ресурсу<code>://<em>URL-путь к скрипту, который находится в подкаталоге вашего дополнения</em>.</code> jpm не создаёт папку "data" по умолчанию, поэтому вы должны добавить её и положить внутрь файл <code>content scripts</code>.</p>

<p>Следующее дополнение использует URL для ссылки на файл "content-script.js", находящийся в папке <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 = "&lt;h1&gt;Page matches ruleset&lt;/h1&gt;";</pre>

<div class="note">
<p>Начиная с Firefox 34 и далее , вы можете использовать "./content-script.js" как синоним для self.data.url("content-script.js"). Поэтому можно переписать код main.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 (<span class="st">addons.<em>mozilla</em>.org</span>).</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> and <code>contentScriptFile </code>одновременно. В таком случае скрипты, загружаемые <code>contentScriptFile</code>  загрузятся до <code>contentScript. </code>Это похволяет загружать библиотеки JavaScript, такие как jQuery по URL, а затем использвать их в простом скрипте, загруженном через <code>contentScript</code>:</p>

<pre class="brush: js">// main.js

var data = require("sdk/self").data;
var pageMod = require("sdk/page-mod");

var contentScriptString = '$("body").html("&lt;h1&gt;Page matches ruleset&lt;/h1&gt;");';

pageMod.PageMod({
  include: "*.mozilla.org",
  contentScript: contentScriptString,
  contentScriptFile: data.url("jquery.js")
});</pre>

<div class="warning">
<p>Настоятельно рекоммендуется использовать  <code>contentScript </code>только для очень простых скриптов или статичных строк: если это не так, то могут возникнуть проблемы с принятием Вашего дополнения на AMO (<span class="st">addons.<em>mozilla</em>.org</span>).</p>

<p>Содержите ваши скрипты в отдельном файле и загружайте их, используя <code>contentScriptFile</code>. Это сделает ваш код проще в поддержке, отладке, безопаснее, удобочитаемее.</p>
</div>

<h3 id="Определение_момента_(времени)_подключения_скрипта">Определение момента (времени) подключения скрипта</h3>

<p>Опция <code>contentScriptWhen </code>определяет момент, когда <code>content script</code> должен быть загружен. Возможные варианты:</p>

<ul>
 <li><code>"start"</code>: загрузить сразу после того, как элемент документа страницы вставляется в DOM. В таком случае DOM-контент ещё пока не загружен, поэтому скрипт не может работать с ним.</li>
 <li><code>"ready"</code>: загрузить скрипт после того, как DOM страницы загружен: то есть в точке активации событий <a href="https://developer.mozilla.org/en/Gecko-Specific_DOM_Events">DOMContentLoaded</a>. В этот момент content scripts уже могут взаимодействовать с DOM-контентом, но загрузка внешних CSS и картинок ещё могла не завершиться.</li>
 <li><code>"end"</code>: загрузить скрипт после завершения загрузки всего контента (DOM, JS, CSS, картинки), в то время, как активируется событие <a href="https://developer.mozilla.org/en/DOM/window.onload">window.onload event</a>.</li>
</ul>

<p>Значение по умолчанию <code>"<strong>end</strong>"</code>.</p>

<p>Обратите внимание, что <a href="/en-US/Add-ons/SDK/High-Level_APIs/tabs#attach(options)"><code>tab.attach()</code></a> не имеет параметра <code>contentScriptWhen</code>, потому что он обычно вызывается после загрузки страницы.<a href="/en-US/Add-ons/SDK/High-Level_APIs/tabs#attach(options)"><code> </code></a></p>

<h3 id="Передача_конфигурационных_опций">Передача конфигурационных опций</h3>

<p><code>Атрибут contentScriptOptions</code> это JSON-объект, который используется скриптом как read-only значение доступное через свойство <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, etc.).</p>

<h2 id="Доступ_к_DOM">Доступ к DOM</h2>

<p>Скрипты <code>content scripts</code> могут иметь доступ к DOM страницы, конечно, только те скрипты, которые уже загрузились на странице. При этом скрипты content scripts изолированы от скриптов web-страницы:</p>

<ul>
 <li>content scripts не видят объектов JavaScript, добавленных скриптами web-страницы.</li>
 <li>Если скриты web-страницы переопределят поведения каких-либо DOM-объектов, то скрипты <code>content script</code> обнаружат исходное поведение.</li>
</ul>

<p>То же происходит в обратную сторону: скрипты web-страницы не увидят объектов JavaScript, добавленных скриптами <code>content scripts</code>.</p>

<p>Например, рассмотрим страницу, где скрипты web-страницы создают переменную <code>foo </code>в объекте <code>window</code>:</p>

<pre class="brush: html">&lt;!DOCTYPE html"&gt;
&lt;html&gt;
  &lt;head&gt;
    &lt;script&gt;
    window.foo = "hello from page script"
    &lt;/script&gt;
  &lt;/head&gt;
&lt;/html&gt;</pre>

<p>Другой скрипт (но тоже page-script), загруженный на страницу после этого скрипта (указанного выше), будет иметь доступ к foo. Но скрипт <code>content script</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>Есть веские причины для изоляции. Во-первых, из <code>content script</code> не утекают объекты в web-страницу, что потенциально является дырой в безопасности. Во-вторых, <code>content scripts</code> могут не беспокоиться о пересечении объектов с объектами, созданных скриптами web-страницы.</p>

<p>Такая изоляция необходима, например, в случае, если web-страница загружает библиотеку jQuery, но <code>content script</code> не увидит объектов, созданных этой библиотекой. В этом случае content script может добавить свою собственный jQuery-объект, который не пересечётся со страничным объектом.</p>

<h3 id="Взаимодействие_со_скриптами_web-страницы">Взаимодействие со скриптами web-страницы</h3>

<p>Обычно изоляция content scripts и page scripts (скрипты web-страницы) необходима. Но иногда вы захотите наладить такое взаимодействие: вы можете захотеть иметь общие объекты между <code>content scripts</code> и <code>page scripts</code> или передевать между ними сообщения. Если появится такая необходимость, то прочтите о <a href="/en-US/Add-ons/SDK/Guides/Content_Scripts/Interacting_with_page_scripts">взаимодействии со скриптами web-страницы</a> (<a href="/en-US/Add-ons/SDK/Guides/Content_Scripts/Interacting_with_page_scripts">interacting with page scripts</a>).</p>

<h3 id="Прослушивание_событий">Прослушивание событий</h3>

<p>Вы можете прослушивать события DOM в скриптах <code>content scripts</code> также, как в обычных скриптах web-страницы. Но есть два важных отличия:</p>

<p>Первое. Если вы определите слушатель через передачу строки в функцию<a href="https://developer.mozilla.org/en/DOM/element.setAttribute"><code> setAttribute()</code></a>, то слушатель будет вызываться в контексте web-страницы, поэтому он не будет иметь доступа ни к каким переменным, определённым в <code>content script</code>.</p>

<p>Например, при выполнении в данном <code>content script</code> появится ошибка "theMessage is not defined":</p>

<pre class="brush: js">var theMessage = "Hello from content script!";
anElement.setAttribute("onclick", "alert(theMessage);");</pre>

<p>Второе. Если вы определите слушатель напрямую через <a href="/en-US/docs/Web/API/GlobalEventHandlers">GlobalEventHandlers</a>, например на <code>onclick</code>, то такое определение может быть переопределено на web-странице. Например, здесь представлен add-on, который пытается добавить обработчик click-события при помощи присвоения <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">&lt;html&gt;
  &lt;head&gt;
  &lt;/head&gt;
  &lt;body&gt;
    &lt;script&gt;
    window.onclick = function() {
      window.alert("it's my click now!");
    }
    &lt;/script&gt;
  &lt;/body&gt;
&lt;/html&gt;</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>Для организации взаимодействия друг с другом скрипта дополнения (<code>add-on script</code>) и скрипта <code>content script</code> нужно обоим дать доступ к объекту <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>Вот пример простого дополнения, которое отправляет сообщение скрипту <code>content script, используя 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>Модуль <code>context-menu</code> не использует данную модель коммуникации. Для изучения варианта взаимодействия скриптов <code>content scripts</code>, загруженных с использованием <code>context-menu</code>, смотрите <a href="/en-US/Add-ons/SDK/High-Level_APIs/context-menu">context-menu documentation</a></p>
</div>

<h3 id="Доступ_к_порту_в_content_script">Доступ к порту в content script</h3>

<p>В скрипте <code>content script</code> объект <code>port </code>доступен через свойство глобального объекта <a href="/en-US/Add-ons/SDK/Guides/Content_Scripts/self"><code>self</code></a>.  Чтобы послать сообщение из <code>content script</code>:</p>

<pre class="brush: js">self.port.emit("myContentScriptMessage", myContentScriptMessagePayload);</pre>

<p>Чтобы получить сообщение из кода дополнения:</p>

<pre class="brush: js">self.port.on("myAddonMessage", function(myAddonMessagePayload) {
  // Handle the message
});</pre>

<div class="note">
<p><span>Учтите, что глобальный объект <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> module</a>, предоставляющим API дополнению для доступа к его файлам и ID.</span></p>
</div>

<h3 id="Доступ_к_порту_в_скрипте_дополнения_(add-on_script)">Доступ к порту в скрипте дополнения (add-on script)</h3>

<p>В коде дополнения канал взаимодействия между дополнением и конкретным <code>content script</code> инкапсулируется посредством объекта <a href="https://developer.mozilla.org/en-US/Add-ons/SDK/Low-Level_APIs/content_worker"><code>worker</code></a>. Поэтому объект <code>port </code>для для связи со скриптом content script это свойство связанного worker.</p>

<p>Тем не менее, объект worker не расширен на код дополнения так же, как в других модулях.</p>

<h4 id="Сообщения_из_page-worker">Сообщения из<code> page-worker</code></h4>

<p>Объект <code>page-worker</code> интегрирует в себе <code>worker API</code>. Поэтому для получения сообщений от скрипта content script, ассоциированного с <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>Для отправки пользовательских сообщений их дополнения нужно вызвать <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 &gt; 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> может привязать свои скрипты к нескольким страницам, каждая из них со своим контекстом, в котором запускаются <code>content scripts</code>. Поэтому для каждой страницы необходим отдельный канал (<code>worker</code>) связи.</p>

<p><code>page-mod</code> не интегрирует в себе <code>worker API напрямую</code>. Вместо этого, когда скрипт <code>content script</code> привязывается к странице, <code>page-mod</code> бросает событие <code>attach</code> тому слушателю, который связан с worker. Создавая слушатель для события <code>attach</code>, вы можете получить доступ через объект <code>port </code>к тому скрипту <code>content scripts</code>, который связан с нужной страницей (через <code>page-mod)</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>В дополнении, описанном выше, есть два сообщения:</p>

<ul>
 <li><code>click</code> отправляется из <code>page-mod</code> в дополнение, когда пользователь кликает на элемент на web-странице</li>
 <li><code>warning</code> отправляет прикольную строчку обратно в объект <code>page-mod</code></li>
</ul>

<h4 id="Сообщения_из_Tab.attach()">Сообщения из <code>Tab.attach()</code></h4>

<p>Функция <code>Tab.attach()</code> возвращает <code>worker</code>, который можно использовать для связи со скриптом content script(s).</p>

<p>Следующее дополнение добавляет кнопку в Firefox: когда пользователь надимает её, то дополнение привязывает скрипт <code>content script</code> к активной вкладке, отправляет этому скрипту сообщение "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">reference page for the <code>port</code> object</a>.</p>
</article>

<h3 id="Описание_postMessage_API">Описание postMessage API</h3>

<p>До того, как был введён объект port, дополнения и <code>content scripts </code>общались следующим образом, используя различные API:</p>

<ul>
 <li>скрипт content script <code>вызывал self.postMessage()</code> для отправки и <code>self.on()</code> для получения</li>
 <li>дополнение (add-on) вызывал <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 API</code>, описанный здесь выше. Исключением является модуль <a href="/en-US/Add-ons/SDK/High-Level_APIs/context-menu">context-menu</a>, который ещё использует <code>postMessage</code>.</p>

<h3 id="Взаимодействие_скриптов_content_script_со_скриптами_content_script">Взаимодействие скриптов content script со скриптами content script</h3>

<p>Скрипты <code>content scripts</code> могут взаимодействовать друг с другом напрямую если они загружены в одном контексте. Например, если один вызов <code>Tab.attach()</code> привязывает два скрипта <code>content scripts</code>, то они видят друг друга напрямую, как если два скрипта загружены на одну страницу. Но если вызвать <code>Tab.attach()</code> дважды, привязывая <code>content scripts</code> каждый раз, то они уже не будут загружены в одном контексте, и дожны взаимодействовать способами как скрипты из разных контекстов. Один из вариантом это пересылать сообщения через основной код дополнения, используя port API с передачей сообщения другим скриптам <code>context script</code>. Этои вариант будет работать независимо от контекста, в котором загружен скрипт <code>content script</code>.</p>

<p>В отдельном случае, когда два скрипта загружены на одной странице, существует возможность для обоих скриптов <code>content scripts</code> взаимодействовать друг с другом, используя<a href="https://developer.mozilla.org/en-US/Add-ons/SDK/Guides/Content_Scripts/Communicating_With_Other_Scripts#Using_the_DOM_postMessage_API"> DOM postMessage() API</a> или <a href="https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent">CustomEvent</a>. Следующее дополнение показывает как скрипт <code>content script</code>, добавленный через <code>page-mod</code>, получает событие CustomEvent, отправленное из <a href="https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/SDK/High-Level_APIs/context-menu">context-menu</a>, когда элемент меню был кликнут. Скрипт <code>page-mod</code> будет отображать алерт с URL той ссылки, по которой было отображено контекстное меню. URL передан в скрипт <code>page-mod</code> с использованием CustomEvent.</p>

<pre><code>var pageMod = require("sdk/page-mod");
pageMod.PageMod({
  include: "*.mozilla.org",
  contentScript: 'function contextMenuAlert(href) {'
               + '    window.alert("The context menu was clicked on URL:\\n" + href);'
               + '};'
               + 'window.addEventListener("myAddonId-contextMenu-clicked",'
               + '    function(event){contextMenuAlert(event.detail);});'
});

let cm = require("sdk/context-menu");
cm.Item({
    label: "Alert URL",
    context: [
        cm.URLContext(["*.mozilla.org"]),
        cm.SelectorContext("a[href]")
    ],
    contentScript: 'self.on("click", function (node, data) {'
                 + '    var event = new CustomEvent("myAddonId-contextMenu-clicked",'
                 + '                                {detail:node.href});'
                 + '    window.dispatchEvent(event);'
                 + '});'
});</code></pre>

<h2 id="Междоменные_скрипты_content_script">Междоменные скрипты <code>content script</code></h2>

<p>По умолчанию скрипты <code>content script </code>не имеют никаких междоменных привилегий. В частности, они не имеют доступа к содержимому в <code>iframe</code>, если содержимое получено из другого домена, или выполняются междоменные XMLHttpRequests.</p>

<p>Однако, вы можете разрешить эти функции для заданных доменов, путём добавления их в <a href="/en-US/Add-ons/SDK/Tools/package_json">package.json</a> дополнения в ключе <code>"cross-domain-content"</code>, который расположен в ключе <code>"permissions"</code>. Смотрите статью <a href="/en-US/Add-ons/SDK/Guides/Content_Scripts/Cross_Domain_Content_Scripts">междоменные скрипты содержимого</a>.</p>
</article>