2017-08-20 11:35:53 +00:00
|
|
|
# WebExtension `browser` API Polyfill [![Build Status](https://travis-ci.org/mozilla/webextension-polyfill.svg?branch=master)](https://travis-ci.org/mozilla/webextension-polyfill)
|
2016-10-07 01:20:37 +00:00
|
|
|
|
|
|
|
This library allows extensions written for the Promise-based
|
|
|
|
WebExtension/BrowserExt API being standardized by the [W3 Browser
|
|
|
|
Extensions][w3-browserext] group to be used without modification in Google
|
|
|
|
Chrome.
|
|
|
|
|
|
|
|
[w3-browserext]: https://www.w3.org/community/browserext/
|
|
|
|
|
2017-10-01 10:47:50 +00:00
|
|
|
|
|
|
|
Table of contents
|
|
|
|
=================
|
|
|
|
|
|
|
|
* [Building](#building)
|
|
|
|
* [Basic Setup](#basic-setup)
|
|
|
|
* [Using the Promise-based APIs](#using-the-promise-based-apis)
|
|
|
|
* [Examples](#examples)
|
|
|
|
|
2016-11-06 22:45:10 +00:00
|
|
|
## Building
|
|
|
|
|
2017-04-11 11:50:22 +00:00
|
|
|
To build, assuming you're already installed [node >= 6](https://nodejs.org) and
|
|
|
|
[npm](https://www.npmjs.com/), simply run:
|
2016-11-06 22:45:10 +00:00
|
|
|
|
|
|
|
```sh
|
|
|
|
npm install
|
2017-04-11 11:50:22 +00:00
|
|
|
npm run build
|
|
|
|
npm run test
|
2016-11-06 22:45:10 +00:00
|
|
|
```
|
|
|
|
|
2017-04-11 11:50:22 +00:00
|
|
|
This will install all the npm dependencies and build both non-minified and minified versions
|
|
|
|
of the final library, and output them to `dist/browser-polyfill.js` and `dist/browser-polyfill.min.js`,
|
|
|
|
respectively, and finally executes the unit tests on the generated dist files.
|
2016-11-06 22:45:10 +00:00
|
|
|
|
2016-10-07 01:20:37 +00:00
|
|
|
## Basic Setup
|
|
|
|
|
|
|
|
In order to use the polyfill, it must be loaded into any context where
|
|
|
|
`browser` APIs are accessed. The most common cases are background and
|
|
|
|
content scripts, which can be specified in `manifest.json`:
|
|
|
|
|
|
|
|
```javascript
|
|
|
|
{
|
|
|
|
// ...
|
|
|
|
|
|
|
|
"background": {
|
|
|
|
"scripts": [
|
|
|
|
"browser-polyfill.js",
|
|
|
|
"background.js"
|
|
|
|
]
|
|
|
|
},
|
|
|
|
|
|
|
|
"content_scripts": [{
|
|
|
|
// ...
|
|
|
|
"js": [
|
|
|
|
"browser-polyfill.js",
|
|
|
|
"content.js"
|
|
|
|
]
|
|
|
|
}]
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
For HTML documents, such as `browserAction` popups, or tab pages, it must be
|
|
|
|
included more explicitly:
|
|
|
|
|
|
|
|
```html
|
|
|
|
<!DOCTYPE html>
|
|
|
|
<html>
|
|
|
|
<head>
|
|
|
|
<script type="application/javascript" src="browser-polyfill.js"></script>
|
|
|
|
<script type="application/javascript" src="popup.js"></script>
|
|
|
|
</head>
|
|
|
|
<!-- ... -->
|
|
|
|
</html>
|
|
|
|
```
|
|
|
|
|
|
|
|
And for dynamically-injected content scripts loaded by `tabs.executeScript`,
|
|
|
|
it must be injected by a separate `executeScript` call, unless it has
|
|
|
|
already been loaded via a `content_scripts` declaration in
|
|
|
|
`manifest.json`:
|
|
|
|
|
|
|
|
```javascript
|
|
|
|
browser.tabs.executeScript({file: "browser-polyfill.js"});
|
|
|
|
browser.tabs.executeScript({file: "content.js"}).then(result => {
|
|
|
|
// ...
|
|
|
|
});
|
|
|
|
```
|
|
|
|
|
|
|
|
## Using the Promise-based APIs
|
|
|
|
|
|
|
|
The Promise-based APIs in the `browser` namespace work, for the most part,
|
|
|
|
very similarly to the callback-based APIs in Chrome's `chrome` namespace.
|
|
|
|
The major differences are:
|
|
|
|
|
|
|
|
* Rather than receiving a callback argument, every async function returns a
|
|
|
|
`Promise` object, which resolves or rejects when the operation completes.
|
|
|
|
|
|
|
|
* Rather than checking the `chrome.runtime.lastError` property from every
|
|
|
|
callback, code which needs to explicitly deal with errors registers a
|
|
|
|
separate Promise rejection handler.
|
|
|
|
|
|
|
|
* Rather than receiving a `sendResponse` callback to send a response,
|
|
|
|
`onMessage` listeners simply return a Promise whose resolution value is
|
|
|
|
used as a reply.
|
|
|
|
|
|
|
|
* Rather than nesting callbacks when a sequence of operations depend on each
|
|
|
|
other, Promise chaining is generally used instead.
|
|
|
|
|
2018-04-09 17:24:15 +00:00
|
|
|
* The resulting Promises can be also used with `async` and `await`, rather
|
|
|
|
than dealt with directly.
|
2016-10-07 01:20:37 +00:00
|
|
|
|
|
|
|
## Examples
|
|
|
|
|
|
|
|
The following code will retrieve a list of URLs patterns from the `storage`
|
|
|
|
API, retrieve a list of tabs which match any of them, reload each of those
|
|
|
|
tabs, and notify the user that is has been done:
|
|
|
|
|
|
|
|
```javascript
|
2018-03-19 14:42:40 +00:00
|
|
|
browser.storage.local.get("urls").then(({urls}) => {
|
2016-10-07 01:20:37 +00:00
|
|
|
return browser.tabs.query({url: urls});
|
|
|
|
}).then(tabs => {
|
|
|
|
return Promise.all(
|
2018-03-19 14:42:40 +00:00
|
|
|
Array.from(tabs, tab => browser.tabs.reload(tab.id))
|
2016-10-07 01:20:37 +00:00
|
|
|
);
|
|
|
|
}).then(() => {
|
|
|
|
return browser.notifications.create({
|
|
|
|
type: "basic",
|
|
|
|
iconUrl: "icon.png",
|
|
|
|
title: "Tabs reloaded",
|
|
|
|
message: "Your tabs have been reloaded",
|
|
|
|
});
|
|
|
|
}).catch(error => {
|
|
|
|
console.error(`An error occurred while reloading tabs: ${error.message}`);
|
|
|
|
});
|
|
|
|
```
|
|
|
|
|
|
|
|
Or, using an async function:
|
|
|
|
|
|
|
|
```javascript
|
|
|
|
async function reloadTabs() {
|
|
|
|
try {
|
2018-03-19 14:42:40 +00:00
|
|
|
let {urls} = await browser.storage.local.get("urls");
|
2016-10-07 01:20:37 +00:00
|
|
|
|
|
|
|
let tabs = await browser.tabs.query({url: urls});
|
|
|
|
|
|
|
|
await Promise.all(
|
2018-03-19 14:42:40 +00:00
|
|
|
Array.from(tabs, tab => browser.tabs.reload(tab.id))
|
2016-10-07 01:20:37 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
await browser.notifications.create({
|
|
|
|
type: "basic",
|
|
|
|
iconUrl: "icon.png",
|
|
|
|
title: "Tabs reloaded",
|
|
|
|
message: "Your tabs have been reloaded",
|
|
|
|
});
|
|
|
|
} catch (error) {
|
|
|
|
console.error(`An error occurred while reloading tabs: ${error.message}`);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
It's also possible to use Promises effectively using two-way messaging.
|
|
|
|
Communication between a background page and a tab content script, for example,
|
|
|
|
looks something like this from the background page side:
|
|
|
|
|
|
|
|
```javascript
|
|
|
|
browser.tabs.sendMessage("get-ids").then(results => {
|
|
|
|
processResults(results);
|
|
|
|
});
|
|
|
|
```
|
|
|
|
|
|
|
|
And like this from the content script:
|
|
|
|
|
|
|
|
```javascript
|
|
|
|
browser.runtime.onMessage.addListener(msg => {
|
|
|
|
if (msg == "get-ids") {
|
2018-03-19 14:42:40 +00:00
|
|
|
return browser.storage.local.get("idPattern").then(({idPattern}) => {
|
2016-10-07 01:20:37 +00:00
|
|
|
return Array.from(document.querySelectorAll(idPattern),
|
|
|
|
elem => elem.textContent);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
});
|
|
|
|
```
|
|
|
|
|
2016-10-10 21:02:56 +00:00
|
|
|
or:
|
|
|
|
|
|
|
|
```javascript
|
|
|
|
browser.runtime.onMessage.addListener(async function(msg) {
|
|
|
|
if (msg == "get-ids") {
|
2018-03-19 14:42:40 +00:00
|
|
|
let {idPattern} = await browser.storage.local.get("idPattern");
|
2016-10-10 21:02:56 +00:00
|
|
|
|
|
|
|
return Array.from(document.querySelectorAll(idPattern),
|
|
|
|
elem => elem.textContent);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
```
|
|
|
|
|
2016-10-07 01:20:37 +00:00
|
|
|
Or vice versa.
|