UNPKG

5.48 kBMarkdownView Raw
1# WebExtension `browser` API Polyfill [![Build Status](https://travis-ci.org/mozilla/webextension-polyfill.svg?branch=master)](https://travis-ci.org/mozilla/webextension-polyfill)
2
3This library allows extensions written for the Promise-based
4WebExtension/BrowserExt API being standardized by the [W3 Browser
5Extensions][w3-browserext] group to be used without modification in Google
6Chrome.
7
8[w3-browserext]: https://www.w3.org/community/browserext/
9
10
11Table of contents
12=================
13
14* [Building](#building)
15* [Basic Setup](#basic-setup)
16* [Using the Promise-based APIs](#using-the-promise-based-apis)
17* [Examples](#examples)
18
19## Building
20
21To build, assuming you're already installed [node >= 6](https://nodejs.org) and
22[npm](https://www.npmjs.com/), simply run:
23
24```sh
25npm install
26npm run build
27npm run test
28```
29
30This will install all the npm dependencies and build both non-minified and minified versions
31of the final library, and output them to `dist/browser-polyfill.js` and `dist/browser-polyfill.min.js`,
32respectively, and finally executes the unit tests on the generated dist files.
33
34## Basic Setup
35
36In order to use the polyfill, it must be loaded into any context where
37`browser` APIs are accessed. The most common cases are background and
38content scripts, which can be specified in `manifest.json`:
39
40```javascript
41{
42 // ...
43
44 "background": {
45 "scripts": [
46 "browser-polyfill.js",
47 "background.js"
48 ]
49 },
50
51 "content_scripts": [{
52 // ...
53 "js": [
54 "browser-polyfill.js",
55 "content.js"
56 ]
57 }]
58}
59```
60
61For HTML documents, such as `browserAction` popups, or tab pages, it must be
62included more explicitly:
63
64```html
65<!DOCTYPE html>
66<html>
67 <head>
68 <script type="application/javascript" src="browser-polyfill.js"></script>
69 <script type="application/javascript" src="popup.js"></script>
70 </head>
71 <!-- ... -->
72</html>
73```
74
75And for dynamically-injected content scripts loaded by `tabs.executeScript`,
76it must be injected by a separate `executeScript` call, unless it has
77already been loaded via a `content_scripts` declaration in
78`manifest.json`:
79
80```javascript
81browser.tabs.executeScript({file: "browser-polyfill.js"});
82browser.tabs.executeScript({file: "content.js"}).then(result => {
83 // ...
84});
85```
86
87## Using the Promise-based APIs
88
89The Promise-based APIs in the `browser` namespace work, for the most part,
90very similarly to the callback-based APIs in Chrome's `chrome` namespace.
91The major differences are:
92
93* Rather than receiving a callback argument, every async function returns a
94 `Promise` object, which resolves or rejects when the operation completes.
95
96* Rather than checking the `chrome.runtime.lastError` property from every
97 callback, code which needs to explicitly deal with errors registers a
98 separate Promise rejection handler.
99
100* Rather than receiving a `sendResponse` callback to send a response,
101 `onMessage` listeners simply return a Promise whose resolution value is
102 used as a reply.
103
104* Rather than nesting callbacks when a sequence of operations depend on each
105 other, Promise chaining is generally used instead.
106
107* For users of an ES7 transpiler, such as Babel, the resulting Promises are
108 generally used with `async` and `await`, rather than dealt with
109 directly.
110
111## Examples
112
113The following code will retrieve a list of URLs patterns from the `storage`
114API, retrieve a list of tabs which match any of them, reload each of those
115tabs, and notify the user that is has been done:
116
117```javascript
118browser.storage.get("urls").then(({urls}) => {
119 return browser.tabs.query({url: urls});
120}).then(tabs => {
121 return Promise.all(
122 Array.from(tabs, tab => browser.tabs.reload(tab.id)));
123 );
124}).then(() => {
125 return browser.notifications.create({
126 type: "basic",
127 iconUrl: "icon.png",
128 title: "Tabs reloaded",
129 message: "Your tabs have been reloaded",
130 });
131}).catch(error => {
132 console.error(`An error occurred while reloading tabs: ${error.message}`);
133});
134```
135
136Or, using an async function:
137
138```javascript
139async function reloadTabs() {
140 try {
141 let {urls} = await browser.storage.get("urls");
142
143 let tabs = await browser.tabs.query({url: urls});
144
145 await Promise.all(
146 Array.from(tabs, tab => browser.tabs.reload(tab.id)));
147 );
148
149 await browser.notifications.create({
150 type: "basic",
151 iconUrl: "icon.png",
152 title: "Tabs reloaded",
153 message: "Your tabs have been reloaded",
154 });
155 } catch (error) {
156 console.error(`An error occurred while reloading tabs: ${error.message}`);
157 }
158}
159```
160
161It's also possible to use Promises effectively using two-way messaging.
162Communication between a background page and a tab content script, for example,
163looks something like this from the background page side:
164
165```javascript
166browser.tabs.sendMessage("get-ids").then(results => {
167 processResults(results);
168});
169```
170
171And like this from the content script:
172
173```javascript
174browser.runtime.onMessage.addListener(msg => {
175 if (msg == "get-ids") {
176 return browser.storage.get("idPattern").then(({idPattern}) => {
177 return Array.from(document.querySelectorAll(idPattern),
178 elem => elem.textContent);
179 });
180 }
181});
182```
183
184or:
185
186```javascript
187browser.runtime.onMessage.addListener(async function(msg) {
188 if (msg == "get-ids") {
189 let {idPattern} = await browser.storage.get("idPattern");
190
191 return Array.from(document.querySelectorAll(idPattern),
192 elem => elem.textContent);
193 }
194});
195```
196
197Or vice versa.