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 |
|
3 | This library allows extensions written for the Promise-based
|
4 | WebExtension/BrowserExt API being standardized by the [W3 Browser
|
5 | Extensions][w3-browserext] group to be used without modification in Google
|
6 | Chrome.
|
7 |
|
8 | [w3-browserext]: https://www.w3.org/community/browserext/
|
9 |
|
10 |
|
11 | Table 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 |
|
21 | To build, assuming you're already installed [node >= 6](https://nodejs.org) and
|
22 | [npm](https://www.npmjs.com/), simply run:
|
23 |
|
24 | ```sh
|
25 | npm install
|
26 | npm run build
|
27 | npm run test
|
28 | ```
|
29 |
|
30 | This will install all the npm dependencies and build both non-minified and minified versions
|
31 | of the final library, and output them to `dist/browser-polyfill.js` and `dist/browser-polyfill.min.js`,
|
32 | respectively, and finally executes the unit tests on the generated dist files.
|
33 |
|
34 | ## Basic Setup
|
35 |
|
36 | In 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
|
38 | content 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 |
|
61 | For HTML documents, such as `browserAction` popups, or tab pages, it must be
|
62 | included 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 |
|
75 | And for dynamically-injected content scripts loaded by `tabs.executeScript`,
|
76 | it must be injected by a separate `executeScript` call, unless it has
|
77 | already been loaded via a `content_scripts` declaration in
|
78 | `manifest.json`:
|
79 |
|
80 | ```javascript
|
81 | browser.tabs.executeScript({file: "browser-polyfill.js"});
|
82 | browser.tabs.executeScript({file: "content.js"}).then(result => {
|
83 | // ...
|
84 | });
|
85 | ```
|
86 |
|
87 | ## Using the Promise-based APIs
|
88 |
|
89 | The Promise-based APIs in the `browser` namespace work, for the most part,
|
90 | very similarly to the callback-based APIs in Chrome's `chrome` namespace.
|
91 | The 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 |
|
113 | The following code will retrieve a list of URLs patterns from the `storage`
|
114 | API, retrieve a list of tabs which match any of them, reload each of those
|
115 | tabs, and notify the user that is has been done:
|
116 |
|
117 | ```javascript
|
118 | browser.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 |
|
136 | Or, using an async function:
|
137 |
|
138 | ```javascript
|
139 | async 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 |
|
161 | It's also possible to use Promises effectively using two-way messaging.
|
162 | Communication between a background page and a tab content script, for example,
|
163 | looks something like this from the background page side:
|
164 |
|
165 | ```javascript
|
166 | browser.tabs.sendMessage("get-ids").then(results => {
|
167 | processResults(results);
|
168 | });
|
169 | ```
|
170 |
|
171 | And like this from the content script:
|
172 |
|
173 | ```javascript
|
174 | browser.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 |
|
184 | or:
|
185 |
|
186 | ```javascript
|
187 | browser.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 |
|
197 | Or vice versa.
|