blob: 834125bb3215e1913f54896b15add5a7547c73c7 (
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
|
---
title: Embedded WebExtensions
slug: Mozilla/Add-ons/WebExtensions/Embedded_WebExtensions
translation_of: Archive/Add-ons/Embedded_WebExtensions
---
<div>{{AddonSidebar}}</div>
<div class="note">
<p>嵌入一个 WebExtension 需要使用 Firefox 51 或更高版本。在 SDK 附加组件中嵌入一个 WebExtension 还需要 <a href="https://www.npmjs.com/package/jpm">jpm 1.2.0</a>。</p>
</div>
<p>从 Firefox 51 开始,你可以在传统附加组件类型中嵌入一个 WebExtension。</p>
<p>传统附加组件可以是经典的<a href="/en-US/docs/Mozilla/Add-ons/Bootstrapped_extensions">自举扩展</a>或者<a href="/en-US/docs/Mozilla/Add-ons/SDK">Add-on SDK</a> 附加组件。嵌入式 WebExtension 的文件打包在传统附加组件中。嵌入式 WebExtension 并不直接与嵌入的附加组件共享范围,但可以使用 {{WebExtAPIRef("runtime")}} API 中定义的消息函数交换消息。</p>
<p><img alt="" src="https://mdn.mozillademos.org/files/13895/embedded-we.png" style="display: block; height: 522px; margin-left: auto; margin-right: auto; width: 429px;"></p>
<p>这意味着您可以一次性迁移传统附加组件到 WebExtensions,并且在此期间附加组件的功能完全保留。尤其是它可以让您从旧版附加组件<a href="/en-US/Add-ons/WebExtensions/Embedded_WebExtensions#Migrating_data_from_legacy_add-ons">迁移存储数据</a>到 WebExtension,通过撰写一个中间的混合式附加组件,使用旧版 API 读取数据(例如 <a href="/en-US/docs/Mozilla/Add-ons/SDK/High-Level_APIs/simple-prefs">simple-prefs</a> 或 preferences <a href="/en-US/docs/Mozilla/JavaScript_code_modules/Services.jsm">服务</a>)并使用 WebExtension API 写入它(例如 {{WebExtAPIRef("storage")}})。</p>
<p>连同本指南,我们撰写了两个例子展示如何使用嵌入式 WebExtensions 来帮助从传统附加组件迁移。<a href="https://github.com/mdn/webextensions-examples/tree/master/embedded-webextension-bootstrapped">如何从自举式附加组件迁移</a>以及<a href="https://github.com/mdn/webextensions-examples/tree/master/embedded-webextension-sdk">如何从 SDK 附加组件迁移</a>。</p>
<h2 id="嵌入_WebExtension">嵌入 WebExtension</h2>
<p>如果传统附加组件是一个带有<a href="/en-US/Add-ons/Install_Manifests">install.rdf</a> 的自举式扩展,在该 RDF 中加入 "hasEmbeddedWebExtension" 并设为 "true":</p>
<pre><<span class="pl-ent">em</span><span class="pl-ent">:</span><span class="pl-ent">hasEmbeddedWebExtension</span>>true</<span class="pl-ent">em</span><span class="pl-ent">:</span><span class="pl-ent">hasEmbeddedWebExtension</span>></pre>
<div>如果旧式附加组件是一个 SDK 附加组件,在 package.json 中包含 "hasEmbeddedWebExtension" 并设为 <code>true</code>:</div>
<div> </div>
<pre class="brush: json"><span class="pl-s"><span class="pl-pds">"</span>hasEmbeddedWebExtension<span class="pl-pds">"</span></span>: <span class="pl-c1">true</span>
</pre>
<div>WebExtension 本身放在附加组件中的 "webextension" 顶层目录。例如:</div>
<div> </div>
<pre>my-boostrapped-addon/
chrome/
webextension/
manifest.json
background.js
...
bootstrap.js
chrome.manifest
install.rdf</pre>
<div> </div>
<div>
<pre>my-sdk-addon/
index.js
package.json
webextension/
manifest.json
background.js
...</pre>
</div>
<p>Firefox 不将嵌入式 WebExtension 视为一个独立的附加组件。因此,您不应该为它指定一个<a href="/en-US/docs/Mozilla/Add-ons/WebExtensions/WebExtensions_and_the_Add-on_ID">附加组件 ID</a>。如果你这样做,那只会被忽略。</p>
<p>但是,在您完成附加组件的迁移并移除旧式嵌入代码后,您必须添加一个 <a href="/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/applications">applications</a> 键,设置 ID 为旧式附加组件的 ID。通过此方式,<a href="https://addons.mozilla.org/en-US/firefox/">addons.mozilla.org</a> 可以识别 WebExtension 是旧式附加组件的一个更新。</p>
<h2 id="启动_WebExtension">启动 WebExtension</h2>
<p>嵌入式 WebExtension 必须由被嵌入的附加组件明确启动。</p>
<p>如果被嵌入的附加组件是一个自举式附加组件,那么传递到自举式扩展的 <code><a href="/en-US/Add-ons/Bootstrapped_extensions#startup">startup()</a></code> 函数的 <code>data</code> 将获得一个明确的 <code>webExtension</code>:</p>
<pre class="brush: js">// bootstrapped add-on
<span class="pl-k">function</span> <span class="pl-en">startup</span>({webExtension}) {
...</pre>
<p>如果被嵌入的附加组件是一个 SDK 附加组件,它可以使用 <code>sdk/webextension</code> 模块访问一个 WebExtension 对象:</p>
<pre class="brush: js"><span class="pl-k">// SDK add-on
const</span> <span class="pl-c1">webExtension</span> <span class="pl-k">=</span> <span class="pl-c1">require</span>(<span class="pl-s"><span class="pl-pds">"</span>sdk/webextension<span class="pl-pds">"</span></span>);</pre>
<p>无论哪种方式,此对象都有一个 <code>startup()</code> 函数,它返回一个 <code><a href="/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise">Promise</a></code>。该 promise 使用一个 <code>browser</code> 属性解决一个对象:这包括 {{WebExtAPIRef("runtime")}} API,被嵌入的附加组件可以用它来与嵌入式 WebExtension 交换消息:</p>
<ul>
<li>{{WebExtAPIRef("runtime.onConnect")}}</li>
<li>{{WebExtAPIRef("runtime.onMessage")}}</li>
</ul>
<p>例如:</p>
<pre class="brush: js">// bootstrapped add-on
function startup({webExtension}) {
webExtension.startup().then(api => {
const {browser} = api;
browser.runtime.onMessage.addListener(handleMessage);
});
}</pre>
<pre class="brush: js"><span class="pl-k">// SDK add-on</span>
const webExtension = require("sdk/webextension");
webExtension.startup().then(api => {
const {browser} = api;
browser.runtime.onMessage.addListener(handleMessage);
});
</pre>
<p>应注意的是,嵌入的附加组件不能启动通信,它可以使用 <code>onMessage</code> 接收(并可选响应)一次性消息,并可以使用 <code>onConnect</code> 接受连接请求。</p>
<p>如果嵌入式 WebExtension 缺少一个 manifest,或者如果 manifest 无效,该 promise 会被拒绝。这种情况下,您可以在<a href="/en-US/Add-ons/WebExtensions/Debugging#Viewing_log_output">浏览器工具箱的控制台</a>中看到更多细节。</p>
<h2 id="交换消息">交换消息</h2>
<p>一旦嵌入式 WebExtension 处在运行,它可以使用 {{WebExtAPIRef("runtime")}} API 的子集与旧式附加组件交换消息:</p>
<ul>
<li>它可以使用 {{WebExtAPIRef("runtime.sendMessage()")}} 发送一次性消息。</li>
<li>它可以使用 {{WebExtAPIRef("runtime.connect()")}} 建立一个连接。</li>
</ul>
<h3 id="无需连接的消息">无需连接的消息</h3>
<p>要发送一条消息,WebExtension 可以使用 {{WebExtAPIRef("runtime.sendMessage()")}}。您可以省略 <code>extensionId</code> 参数,因为浏览器认为嵌入式 WebExtension 是被嵌入附加组件的一部分:</p>
<pre class="brush: js">browser.runtime.sendMessage("message-from-webextension").then(reply => {
if (reply) {
console.log("response from legacy add-on: " + reply.content);
}
});</pre>
<p>被嵌入的附加组件可以使用 {{WebExtAPIRef("runtime.onMessage")}} 对象接收消息(并可选响应):</p>
<pre class="brush: js">// bootstrapped add-on
function startup({webExtension}) {
// Start the embedded webextension.
webExtension.startup().then(api => {
const {browser} = api;
browser.runtime.onMessage.addListener((msg, sender, sendReply) => {
if (msg == "message-from-webextension") {
sendReply({
content: "reply from legacy add-on"
});
}
});
});
}</pre>
<h3 id="基于连接的消息">基于连接的消息</h3>
<p>要在 WebExtension 与传统附加组件间设置一个长寿命连接,WebExtension 可以使用 {{WebExtAPIRef("runtime.connect()")}}。</p>
<pre class="brush: js">var port = browser.runtime.connect({name: "connection-to-legacy"});
port.onMessage.addListener(function(message) {
console.log("Message from legacy add-on: " + message.content);
});
</pre>
<p>旧式附加组件可以使用 {{WebExtAPIRef("runtime.onConnect")}} 监听连接尝试,双方可以使用得到的 {{webExtAPIRef("runtime.Port")}} 来交换消息:</p>
<pre class="brush: js">function startup({webExtension}) {
// Start the embedded webextension.
webExtension.startup().then(api => {
const {browser} = api;
browser.runtime.onConnect.addListener((port) => {
port.postMessage({
content: "content from legacy add-on"
});
});
});
}</pre>
<h2 id="从传统附加组件迁移数据">从传统附加组件迁移数据</h2>
<p>嵌入式 WebExtensions 的一项主要用途是迁移附加组件的存储数据。</p>
<p>人们从旧式附加组件类型迁移的一个主要问题是存储数据,因为旧式附加组件不能使用 WebExtension 存储 API,WebExtensions 也不能使用旧式存储 API。例如,如果一个 SDK 附加组件使用了 SDK 的 <a href="/en-US/docs/Mozilla/Add-ons/SDK/High-Level_APIs/simple-prefs">simple-prefs</a> API 来存储首选项,WebExtension 版本不可能访问这项数据。</p>
<p>使用嵌入式 WebExtensions,您可以创建一个嵌入了 WebExtension 的附加组件中间版本来迁移数据。中间版本使用旧式 API 读取存储的数据,然后使用 WebExtension API 写入数据。</p>
<ul>
<li>在初始版本中,基于 SDK 的附加组件使用 <a href="/en-US/docs/Mozilla/Add-ons/SDK/High-Level_APIs/simple-prefs">simple-prefs</a> API 读取和写入附加组件首选项。</li>
<li>
<p>在中间版本中,SDK 附加组件启动嵌入式 WebExtension。WebExtension 要求 SDK 附加组件用 simple-prefs 检索存储的数据。WebExtension 然后使用 {{WebExtAPIRef("storage")}} API 存储数据。</p>
<div class="note">
<p>在某些情况下,中间版本必须在初始导入后保持数据同步。例如,<a href="/en-US/Add-ons/WebExtensions/Embedded_WebExtensions#Add-on_preferences_UI">附加组件的首选项界面仍然使用旧系统</a>,所以如果用户在这里修改了设置,它修改的是旧数据。中间的附加组件必须监听这些更改并将新数据发送到嵌入式 WebExtension。</p>
<p>有个 <a href="https://github.com/mdn/webextensions-examples/tree/master/embedded-webextension-sdk">"embedded-webextension-sdk"</a> 示例说明了这一点。</p>
</div>
</li>
<li>在最终版本中,附加组件只是一个 WebExtension,并且只使用存储 API。</li>
</ul>
<p>我们提供了两个例子说明此模式:<a href="https://github.com/mdn/webextensions-examples/tree/master/embedded-webextension-bootstrapped">"embedded-webextension-bootstrapped"</a> 展示了从一个自举式附加组件迁移,而 <a href="https://github.com/mdn/webextensions-examples/tree/master/embedded-webextension-sdk">"embedded-webextension-sdk"</a> 展示了从一个 SDK 附加组件迁移。</p>
<h2 id="限制">限制</h2>
<h3 id="调试">调试</h3>
<p>如果您有一个嵌入了 WebExtension 的旧式附加组件,您不能使用新的附加组件调试器来调试它。您必须使用基于浏览器工具箱的<a href="/en-US/Add-ons/WebExtensions/Debugging_(before_Firefox_50)">旧版调试工具</a>。</p>
|