Promise support for Chrome Extensions API?
Asked Answered
P

5

20

I've been writing some browser extensions in the last few weeks and until today I thought that a WebExtension for Firefox should work pretty much automatically in Chrome. So I tried to write my code according to Mozilla's examples.
But today I realized that there is no mention of Promises in the API documentation for Chrome Extensions.
I have strictly used Promises throughout the code for all my Extensions.

So now my question is, will my code work in Chrome? Or would it work if I add a var browser = chrome declaration at the very top?
Or does Chrome not support Promises on the API at all?
If Chrome doesn't support Promises on the API functions yet, will it support them in the future?

Note, I am aware of this project: https://github.com/mozilla/webextension-polyfill
But I'm not willing to go through the hassle of including that library everywhere. Also, it has annoying bugs in it.

And besides that I don't have Chrome or Chromium and I can't install them for privacy and security reasons.

Edit: They're finally starting to implement support for promises.

Pseudohemophilia answered 12/2, 2017 at 17:46 Comment(4)
Yes, as you can see in the polyfill description, it's required in Chrome. As for the future, the work hasn't started yet. There is an issue on crbug.com though.Asperse
crbug.com/328932Peace
I don't understand why Mozilla's compatibility tables for the WebExtensions APIs list Chrome with full support for something like browser.alarm, when Chrome has no browser object.Plantar
I suppose Chrome supports it through chrome.alarm. Only without promise support.Pseudohemophilia
P
9

Official promise support is finally coming and has already been implemented on some of the APIs. At the time of writing:

On GoogleChrome's Github you can already find a Hello world example using the chrome.tabs API with Promises (await):

chrome.runtime.onInstalled.addListener(async () => {
  let url = chrome.runtime.getURL("hello.html");
  let tab = await chrome.tabs.create({ url });
  console.log(`Created tab ${tab.id}`);
});

As you can see, the promise support has simply been implemented on the chrome object, which now supports both Promises and callbacks. Firefox still uses the browser object for the promise API.

Pseudohemophilia answered 22/2, 2021 at 18:46 Comment(3)
await chrome.storage.local.get() does not seem to work though, it returns undefinedOrpheus
I can confirm that. I don't know why it's happening though. I suppose it is possible that not all of these changes have made it into the latest stable release yet.Pseudohemophilia
Yeah, quite strange. Gotta maintain additional code to just promisify chrome.storage API ☹️Orpheus
S
9

...until today I thought that a WebExtension for Firefox should work pretty much automatically in Chrome.

WebExtensions were created with backward compatibility with Chrome extensions in mind. chrome.* namespace is available for supported APIs. The goal here is to ease porting existing extensions to FF to quickly bootstrap the ecosystem.

However, Mozilla disregards forward compatibility with browser.* namespace. Mozilla decided to go with a promise-based approach for the API, but only for that new namespace.

So now my question is, will my code work in Chrome?
Or would it work if I add a var browser = chrome declaration at the very top?

No; they behave differently and have different signatures. Chrome will reject calls without required explicit callbacks. browser.* variants will emit a Promise instead.

Or does Chrome not support Promises on the API at all?
If Chrome doesn't support Promises on the API functions yet, will it support them in the future?

As mentioned in comments, Promise-based rewrite of the API is considered by Chrome, and it has been implemented, or started to be implemented, for MV3 extensions. However, there exist polyfills, including the one you mentioned. Other than wrapping methods yourself to create your own polyfill, there's no other solution.

And besides that I don't have Chrome or Chromium and I can't install them for privacy and security reasons.

Then you can't properly test your ports anyway; that's not a good approach for your potential users. You're better off not porting in this case.

Spermine answered 2/6, 2017 at 9:32 Comment(1)
There is a polyfill to allow extensions using the firefox apis to run on chrome: github.com/mozilla/webextension-polyfillStebbins
P
9

Official promise support is finally coming and has already been implemented on some of the APIs. At the time of writing:

On GoogleChrome's Github you can already find a Hello world example using the chrome.tabs API with Promises (await):

chrome.runtime.onInstalled.addListener(async () => {
  let url = chrome.runtime.getURL("hello.html");
  let tab = await chrome.tabs.create({ url });
  console.log(`Created tab ${tab.id}`);
});

As you can see, the promise support has simply been implemented on the chrome object, which now supports both Promises and callbacks. Firefox still uses the browser object for the promise API.

Pseudohemophilia answered 22/2, 2021 at 18:46 Comment(3)
await chrome.storage.local.get() does not seem to work though, it returns undefinedOrpheus
I can confirm that. I don't know why it's happening though. I suppose it is possible that not all of these changes have made it into the latest stable release yet.Pseudohemophilia
Yeah, quite strange. Gotta maintain additional code to just promisify chrome.storage API ☹️Orpheus
T
4

I haven't looked at the browser API, so I may be off-base, but if the only difference is that the Firefox APIs return promises instead of using callbacks, the chrome-promise library might be able to help. It wraps all the API calls that require callbacks in functions that return promises instead.

You could then do something like this in Chrome:

var browser = new ChromePromise(); 

And then make promise calls:

browser.storage.local.get(null).then(data => console.log(data));

If you define the browser variable as a global and include that script before any others, you can just use it as a global and not have to include the polyfill in every file where you want to use it.


Edit: the author of chrome-promise is no longer maintaining it, other than fixing bugs. They list some alternative libraries here, including the Mozilla webextension-polyfill rejected by the OP. That library's readme explains how to make it available as a global variable in the various extension contexts where it's needed.

Tuner answered 9/11, 2017 at 3:56 Comment(0)
L
1

I created this library https://github.com/lawlietmester/webextension to make this without one general rule like in webextension-polyfill.

My library is crossbrowser way to make one Browser object without modification of original chrome/browser. Like jQuery in old time.

To use it only one time - import it in background process and make it global for background, then for other imports (from popup for example) use import from

( typeof browser === 'undefined' ? chrome : browser ).extension.getBackgroundPage().Browser

Lorie answered 11/6, 2017 at 13:45 Comment(0)
L
0

My approach on Spellbook has been to turn the necessary methods to return promises and check chrome.runtime.lastError on a wrapped callback.

But now I just go through all suitable chrome API methods, and turn them into promise based methods with the above approach.

Here is a relevant commit on Spellbook.

I am planning to extract this into a small library called Rosegarden, because Mozilla's webextension-polyfill seems too complicated...

Lurie answered 13/2, 2022 at 14:43 Comment(1)
Here is the 1.7kb library called Rosegarden, that I promised: * GitHub - peterhil/rosegarden: Rosegarden gives you the promises for writing cross platform webextensions on Chrome and other browsers missing the promise based API. * Rosegarden: Cross browser Promise based WebExtension development | Hacker NewsLurie

© 2022 - 2024 — McMap. All rights reserved.