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
|
---
title: 你的第二個 WebExtension
slug: Mozilla/Add-ons/WebExtensions/Your_second_WebExtension
translation_of: Mozilla/Add-ons/WebExtensions/Your_second_WebExtension
---
<p>{{AddonSidebar}}</p>
<p>假如你已經讀過了 <a href="https://developer.mozilla.org/zh-TW/Add-ons/WebExtensions/Your_first_WebExtension">你的第一個 WebExtension</a>,你也已經知道該如何寫一個 extension(外掛),在這篇文章中我們將會教你寫一個稍微複雜一點的 extension,來 demo 一些 API 的使用。</p>
<p>在這個 extension 中,將會新增一個按鈕到 Firefox 的工具列上,當使用者按下按鈕後,將會顯示一個彈出視窗 (pop-up) 並可選擇一個動物。當使用者選擇了一個動物後,將會在當前的網頁中顯示使用者所選的動物圖片。</p>
<p>為了實作這個,我們將需要:</p>
<ul>
<li><strong>定義一個 <a href="/en-US/docs/Mozilla/Add-ons/WebExtensions/Browser_action">browser action</a> 給新增於 Firefox 工具列的按鈕。</strong><br>
這個按鈕,我們將提供以下功能:
<ul>
<li>按鈕的 icon,命名為 "beasts-32.png"</li>
<li>當按下按鈕時顯示一個彈出視窗 (pop-up),這個 pop-up將會包含 HTML, CSS 和 JavaScript。</li>
</ul>
</li>
<li><strong>定義一個 extension 用的 icon,</strong> 命名為 "beasts-48.png". 此 icon 將會顯示於 Add-ons Manager.</li>
<li><strong>寫一個內容腳本 "beastify.js" ,該檔案會被當前網頁讀取。</strong><br>
讓網頁顯示所選的動物圖片的程式碼會寫在這裡。</li>
<li><strong>打包所需要的動物圖片,此圖片是用來顯示按下按鈕後顯示於網頁上的。</strong><br>
為了讓網頁可以取用圖片,我們將會讓這些圖片變成可讓 "網頁存取的資源"。</li>
</ul>
<p>下面是這次 extension 的流程圖:</p>
<p><img alt="" src="https://mdn.mozillademos.org/files/13671/Untitled-1.png" style="display: block; height: 1200px; margin-left: auto; margin-right: auto; width: 860px;"></p>
<p>這個一個簡單的 extension,但是會教你許多基本的 WebExtensions API 的概念:</p>
<ul>
<li>新增一個按鈕到工具列</li>
<li>利用 HTML, CSS 和 JavaScript 去定義一個 pop-up</li>
<li>讀取內容腳本到網頁</li>
<li>內容腳本與整個 extension 間的溝通</li>
<li>打包 extension 所需的資源,讓網頁可以存取</li>
</ul>
<p>也可以在 GitHub 上找到範例的原始碼: <a href="https://github.com/mdn/webextensions-examples/tree/master/beastify">https://github.com/mdn/webextensions-examples/tree/master/beastify</a></p>
<p>實作這個 extension 前,請先確認你的 Firefox 有 45.0 或更新的版本。</p>
<h2 id="實作_extension">實作 extension</h2>
<p>新增一個資料夾,然後進去:</p>
<pre class="brush: bash notranslate">mkdir beastify
cd beastify</pre>
<h3 id="manifest.json">manifest.json</h3>
<p>在資料夾 "beastify" 下新增一個檔案,並命名為 "manifest.json",然後撰寫以下程式碼。</p>
<pre class="brush: json notranslate">{
"manifest_version": 2,
"name": "Beastify",
"version": "1.0",
"description": "Adds a browser action icon to the toolbar. Click the button to choose a beast. The active tab's body content is then replaced with a picture of the chosen beast. See https://developer.mozilla.org/en-US/Add-ons/WebExtensions/Examples#beastify",
"homepage_url": "https://github.com/mdn/webextensions-examples/tree/master/beastify",
"icons": {
"48": "icons/beasts-48.png"
},
"permissions": [
"activeTab"
],
"browser_action": {
"default_icon": "icons/beasts-32.png",
"default_title": "Beastify",
"default_popup": "popup/choose_beast.html"
},
"web_accessible_resources": [
"beasts/frog.jpg",
"beasts/turtle.jpg",
"beasts/snake.jpg"
]
}
</pre>
<ul>
<li>最前面的三個 key:<code><a href="https://developer.mozilla.org/zh-TW/Add-ons/WebExtensions/manifest.json/manifest_version">manifest_version</a></code>、<code><a href="https://developer.mozilla.org/zh-TW/Add-ons/WebExtensions/manifest.json/name">name</a></code>、<code><a href="https://developer.mozilla.org/zh-TW/Add-ons/WebExtensions/manifest.json/version">version</a></code> 必須寫進去,它包含了附加元件的基本詮釋資料(metadata)。</li>
<li><code><a href="/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/description">description</a></code> 和 <code><a href="/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/homepage_url">homepage_url</a></code> 為非必要但建議加上:主要在說明該 extension。</li>
<li><code><a href="/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/icons">icons</a></code> 為非必要但建議加上:它允許附加元件指定圖示、也會在附加元件的管理員顯示</li>
<li><code><a href="/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/permissions">permissions</a></code> 列出了該 extension 所需要的權限。這邊我們只會要求 <a href="/en-US/Add-ons/WebExtensions/manifest.json/permissions#activeTab_permission"><code>activeTab</code> permission</a> 。</li>
<li><code><a href="/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/browser_action">browser_action</a></code> 定義工具列的按鈕,在這邊我們將會提供三種 key:
<ul>
<li><code>default_icon</code> 為必要的:告訴 button 該使用的 icon 為何</li>
<li><code>default_title</code>為非必要的:該 value 會顯示在 button 的 tip 裡</li>
<li><code>default_popup</code> 如果想要顯示 pop-up,此為必要的 key:此教學中有使用到 pop-up 故為必要的,並將 HTML 檔案指給他。</li>
</ul>
</li>
<li><code><a href="/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/web_accessible_resources">web_accessible_resources</a></code> 列出所有希望讓網頁可以存取的檔案。</li>
</ul>
<p>所有的路徑都會關連到 manifest.json 。</p>
<h3 id="The_icon">The icon</h3>
<p>一個 extension 應該要有一個 icon。icon 將會顯示在 Add-ons Manager 的 extension 列表中。(在 Firefox 網址列輸入 "about:addons" 開啟 Add-ons Manager)。</p>
<p>在 beastify 下建立一個名為 "icons" 的資料夾,並準備一個命名為 "beasts-48.png" 的 icon並存在 "beastify/icons" 的資料夾中(可以使用我們的<a href="https://github.com/mdn/webextensions-examples/blob/master/beastify/icons/beasts-48.png">範例圖檔</a>,圖檔來源:<a href="https://www.iconfinder.com/iconsets/free-retina-icon-set">Free Retina Icon Set</a>,遵照<a href="http://www.aha-soft.com/free-icons/free-retina-icon-set/">使用條款</a>來使用)。並在 manifest.json 裡告訴他要使用 "icons/beasts-48.png" 路徑下的 icon。</p>
<p>如果你想要使用自己的 icon,icon 大小必須是 48x48 pixels,另外也可使用 96x96 pixel 來支援較高解析度的顯示。</p>
<pre class="brush: json line-numbers language-json notranslate"><code class="language-json"><span class="key token">"icons":</span> <span class="punctuation token">{</span>
<span class="key token">"48":</span> <span class="string token">"icons/beasts-48.png"</span><span class="punctuation token">,</span>
<span class="key token">"96":</span> <span class="string token">"icons/beasts-96.png"</span>
<span class="punctuation token">}</span></code></pre>
<h3 id="工具列按鈕The_toolbar_button">工具列按鈕(The toolbar button)</h3>
<p>工具列按鈕也需要一個 icon,在 manifest.json 裡 "browser_action" 物件中的 "default_icon" 中告訴他要使用 "icons/beasts-32.png" 路徑下的 icon。。</p>
<p>準備一個命名為 "beasts-32.png" 的 icon,並存在 "beastify/icons" 資料夾中(你可以使用<a href="https://github.com/mdn/webextensions-examples/blob/master/beastify/icons/beasts-32.png">範例圖檔</a>,圖檔來源:<a href="http://www.iconbeast.com/free">IconBeast Lite icon set</a>,遵守<a href="http://www.iconbeast.com/faq/">使用條款</a>來使用)。 </p>
<p>假設你不使用 pop-up,當按下按鈕的時候就會觸發事件。假如使用 pop-up ,當按下按鈕時並不會觸發事件,取而代之會打開 pop-up。不過這邊我們想要用 pop-up,所以接來下會教你如何新增他。</p>
<h3 id="The_popup">The popup</h3>
<p>pop-up 的方法主要是讓使用者可以選擇三個動物中的其中一個。</p>
<p>在 beastify 下 新增一個名為 "popup" 的資料夾,該資料夾中會包含以下三個檔案:</p>
<ul>
<li><strong><code>choose_beast.html</code></strong> 定義 pop-up 的顯示的內容文字</li>
<li><strong><code>choose_beast.css</code></strong> 定義 html 裡的 styles </li>
<li><strong><code>choose_beast.js</code></strong> 當使用者選擇動物後實行的腳本內容</li>
</ul>
<pre class="brush: bash line-numbers language-bash notranslate"><code class="language-bash">mkdir popup
cd popup
touch choose_beast.html choose_beast.css choose_beast.js</code></pre>
<h4 id="choose_beast.html">choose_beast.html</h4>
<p>HTML 內容長得像這樣:</p>
<pre class="brush: html notranslate"><!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="choose_beast.css"/>
</head>
<body>
<div id="popup-content">
<div class="button beast">Frog</div>
<div class="button beast">Turtle</div>
<div class="button beast">Snake</div>
<div class="button reset">Reset</div>
</div>
<div id="error-content" class="hidden">
<p>Can't beastify this web page.</p><p>Try a different page.</p>
</div>
<script src="choose_beast.js"></script>
</body>
</html></pre>
<p>我們在 ID 為 <code>"popup-content"</code> 的 <code><a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/div"><div></a></code> 元件裡建立了一個包含每種動物選項的元件。當載入 popup 發生問題時,用另外一個 ID 為 <code>"error-content"</code> 且類別定義為 <code>"hidden"</code> 的 <code><a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/div"><div></a></code> 元件來處理。</p>
<p>值得注意的是我們在這個檔案裡引用了 CSS 與 JS 檔案,就如同一般網頁。</p>
<h4 id="choose_beast.css">choose_beast.css</h4>
<p>CSS 定義了 pop-up 的大小,並確保三個選項有填滿整個 pop-up,並給他們幾個基本的 style:</p>
<pre class="brush: css notranslate">html, body {
width: 100px;
}
.button {
margin: 3% auto;
padding: 4px;
text-align: center;
font-size: 1.5em;
cursor: pointer;
}
.beast:hover {
background-color: #CFF2F2;
}
.beast {
background-color: #E5F2F2;
}
.clear {
background-color: #FBFBC9;
}
.clear:hover {
background-color: #EAEAC9;
}
</pre>
<h4 id="choose_beast.js">choose_beast.js</h4>
<p>在 pop-up 的 JavaScript 中,我們監控著 click 事件。當按下其中一個選項後,將會讀取 js 檔到當前的瀏覽器分頁(active_tab)中,當內容腳本被讀取後,將會發送一個訊息告訴他該選擇哪一張圖片。</p>
<pre class="brush: js notranslate">/*
Given the name of a beast, get the URL to the corresponding image.
*/
function beastNameToURL(beastName) {
switch (beastName) {
case "Frog":
return browser.extension.getURL("beasts/frog.jpg");
case "Snake":
return browser.extension.getURL("beasts/snake.jpg");
case "Turtle":
return browser.extension.getURL("beasts/turtle.jpg");
}
}
/*
Listen for clicks in the popup.
If the click is on one of the beasts:
Inject the "beastify.js" content script in the active tab.
Then get the active tab and send "beastify.js" a message
containing the URL to the chosen beast's image.
If it's on a button which contains class "clear":
Reload the page.
Close the popup. This is needed, as the content script malfunctions after page reloads.
*/
document.addEventListener("click", (e) => {
if (e.target.classList.contains("beast")) {
var chosenBeast = e.target.textContent;
var chosenBeastURL = beastNameToURL(chosenBeast);
browser.tabs.executeScript(null, {
file: "/content_scripts/beastify.js"
});
var gettingActiveTab = browser.tabs.query({active: true, currentWindow: true});
gettingActiveTab.then((tabs) => {
browser.tabs.sendMessage(tabs[0].id, {beastURL: chosenBeastURL});
});
}
else if (e.target.classList.contains("clear")) {
browser.tabs.reload();
window.close();
}
});
</pre>
<p>這邊使用了三個 WebExtensions API 的方法:</p>
<ul>
<li><code><a href="/en-US/docs/Mozilla/Add-ons/WebExtensions/API/tabs/executeScript">browser.tabs.executeScript</a></code> 讀取內容腳本 "content_scripts/beastify.js" 到當前的瀏覽器分頁裡面</li>
<li><code><a href="/en-US/docs/Mozilla/Add-ons/WebExtensions/API/tabs/query">browser.tabs.query</a></code> 取得當前的瀏覽器分頁</li>
<li><code><a href="/en-US/docs/Mozilla/Add-ons/WebExtensions/API/tabs/sendMessage">browser.tabs.sendMessage</a></code> 送訊息到當前的瀏覽器分頁中正在執行的內容腳本裡(beastify.js)。訊息包含了所選的動物的 URL</li>
</ul>
<h3 id="內容腳本The_content_script">內容腳本(The content script)</h3>
<p>在 beastify 下建立一個名為 "content_scripts" 的資料夾,並新增一個命名為 "beastify.js" 的檔案,檔案裡的內容:</p>
<pre class="brush: js notranslate">/*
beastify():
* removes every node in the document.body,
* then inserts the chosen beast
* then removes itself as a listener
*/
function beastify(request, sender, sendResponse) {
removeEverything();
insertBeast(request.beastURL);
browser.runtime.onMessage.removeListener(beastify);
}
/*
Remove every node under document.body
*/
function removeEverything() {
while (document.body.firstChild) {
document.body.firstChild.remove();
}
}
/*
Given a URL to a beast image, create and style an IMG node pointing to
that image, then insert the node into the document.
*/
function insertBeast(beastURL) {
var beastImage = document.createElement("img");
beastImage.setAttribute("src", beastURL);
beastImage.setAttribute("style", "width: 100vw");
beastImage.setAttribute("style", "height: 100vh");
document.body.appendChild(beastImage);
}
/*
Assign beastify() as a listener for messages from the extension.
*/
browser.runtime.onMessage.addListener(beastify);
</pre>
<p>內容腳本中新增了一個 listener ,使其從 extension 可傳送訊息。(具體來說是從 "choose_beast.js" 這邊) ,在 listener 中做了:</p>
<ul>
<li>removeEverything():移除 <code>document.body</code> 中所有的 element ()</li>
<li>insertBeast(beastURL):新增一個 <code><img></code> element 並告訴它圖片的 URL,並插入到文件中</li>
<li>removeListener(beastify):刪除訊息 listener</li>
</ul>
<h3 id="The_beasts">The beasts</h3>
<p>最後,我們需要將動物的照片放進來</p>
<p>新增一個名為 "beasts" 的資料夾,並把三張動物的圖片放進此資料夾中,請取相對應的檔名。可以使用<a href="https://github.com/mdn/webextensions-examples/tree/master/beastify/beasts">範例圖片</a> ,或從這邊下載:</p>
<p><img alt="" src="https://mdn.mozillademos.org/files/11459/frog.jpg" style="display: inline-block; height: 200px; margin: 20px; width: 200px;"><img alt="" src="https://mdn.mozillademos.org/files/11461/snake.jpg" style="display: inline-block; height: 200px; margin: 20px; width: 200px;"><img alt="" src="https://mdn.mozillademos.org/files/11463/turtle.jpg" style="display: inline-block; height: 200px; margin: 20px; width: 200px;"></p>
<h2 id="Testing_it_out">Testing it out</h2>
<p>首先,請再三的確認檔案有放到相對應的資料夾中:</p>
<pre class="notranslate">beastify/
beasts/
frog.jpg
snake.jpg
turtle.jpg
content_scripts/
beastify.js
icons/
beasts-32.png
beasts-48.png
popup/
choose_beast.css
choose_beast.html
choose_beast.js
manifest.json</pre>
<p>開啟 Firefox 45.0,並安裝本地的 extensive 到瀏覽器裡。</p>
<p>在 Firefox 網址列輸入 "about:debugging" ,點選 "Load Temporary Add-on",然後選擇你的 "manifest.json" 檔案。然後應該就會看到 extensive 的 icon 出現在工具列上了:</p>
<p>{{EmbedYouTube("sAM78GU4P34")}}</p>
<p>打開一個網頁,點選 icon,選擇一個動物的名字,將會看到網頁內容被動物的圖片取代了:</p>
<p>{{EmbedYouTube("YMQXyAQSiE8")}}</p>
<h2 id="透過命令行佈署">透過命令行佈署</h2>
<p>你可以利用<a href="/en-US/Add-ons/WebExtensions/Getting_started_with_web-ext">web-ext</a>自動化暫時載入。<br>
試試看:</p>
<pre class="brush: bash notranslate">cd beastify
web-ext run</pre>
|