mozilla / webextension-polyfill

A lightweight polyfill library for Promise-based WebExtension APIs in Chrome
Mozilla Public License 2.0
2.66k stars 214 forks source link

Support Chrome manifest v3 APIs #329

Open avalanche1 opened 3 years ago

avalanche1 commented 3 years ago

Is this lib compatible with Chrome manifest v3? I haven't found any mentions in the docs.

avalanche1 commented 3 years ago

If not - are you planning to implement the support?

Rob--W commented 3 years ago

Chrome will support promises at the chrome. namespace in browser, and still support the callback-based APIs.

The polyfill invokes methods in the chrome. namespace using callbacks and returns a Promise, for methods that are explicitly supported. When a method is not explicitly recognized, the parameters to the API call are passed as-is to the underlying chrome. namespace. In manifest v3, Chrome's APIs support promises too, so the return values would still be promises. In either case, the polyfill will provide support for the APIs as expected.

We'll keep this bug open, and close it once we've added integration tests.

AhsanAyaz commented 2 years ago

Chrome will support promises at the chrome. namespace in browser, and still support the callback-based APIs.

The polyfill invokes methods in the chrome. namespace using callbacks and returns a Promise, for methods that are explicitly supported. When a method is not explicitly recognized, the parameters to the API call are passed as-is to the underlying chrome. namespace. In manifest v3, Chrome's APIs support promises too, so the return values would still be promises. In either case, the polyfill will provide support for the APIs as expected.

We'll keep this bug open, and close it once we've added integration tests.

Is there any update on this? It seems like the extensions break with servicewoker+background script combo in manifest v3.

avalanche1 commented 2 years ago

@AhsanAyaz you mean servicewoker+**content script** combo in manifest v3?

AhsanAyaz commented 2 years ago

@avalanche1 . No I mean using the background.js or any file as a service worker with webextension-polyfill doesn't work because of the window being undefined.

Rob--W commented 2 years ago

@avalanche1 . No I mean using the background.js or any file as a service worker with webextension-polyfill doesn't work because of the window being undefined.

The polyfill does not use window, but globalThis. Are you using the latest version of the polyfill?

getify commented 2 years ago

I'm attempting to convert my working v2 extension to v3... but experiencing some issues. I am using importScripts(..) in my formerly-background-now-service-worker script to bring in this polyfill.

In particular, since v3 consolidates pageAction and browserAction into just action, I changed my calls from browser.pageAction.show(..) to browser.action.show(..), but that's throwing the exception: "browser.action.show is not a function". I then tried just directly chrome.action.show(..), but that's the same exception. And I then I tried the old browser.pageAction.show(..), but that gives an exception indicating that browser.pageAction is undefined (so thus the show property access fails).

Is this a problem with chrome's v3 manifest having changed or deprecated that function name? Or is this a problem with this polyfill? I can't seem to get properly updated info on the interplay between the v3 changes and this polyfill.

getify commented 2 years ago

Yes I'm sure I'm in manifest v3, because my other changes (including changes in the manifest, like "service_worker") are working as expected. Moreover, chrome.action is present and valid.

Specifically, the error I'm getting is that chrome.action (or browser.action) do not have a show(..) function. In manifest-v2 browser.pageAction.show(..) works great, but I can't seem to get the equivalent to work in manifest-v3 via chrome.action.show(..) or browser.action.show(..) -- they both throw an exception for show(..) not being a function (it's undefined).

meandavejustice commented 2 years ago

@getify chrome.action.show Is not in mv3. You may be looking for setPopup

https://developer.chrome.com/docs/extensions/reference/action/

getify commented 2 years ago

They didn't list throwing away show(..) in the breaking changes from manifest v2 to v3... but it does seem like setPopup(..) is roughly equivalent... though now I have to pass the URL for the HTML page whereas in v2 that URL is specified in the manifest itself.

In any case, I've now passed that error, and encountered a variety of others. For example, my extension (obviously) has a popup, and I used to be able to call browser.extension.getViews({ type: "popup" }) to get references to all instances of my extension's popup, so that I could get call postMessage(..) on each reference to send them all messages.

getViews(..) is another one which apparently was discarded from v2 to v3. :( A similar (but not equivalent or equivalent) seems to be action.getPopup(..). Sadly, that function doesn't get the popup window reference itself, it just gets the URL of the popup... which isn't at all helpful since I already manually provided that URL.

In any case, sorry to be rambling here... it seems that the conversion from v2 to v3 is NOT particularly straightforward the way the migration guide makes it seem. There's quite a bit of breaking change behavior where they apparently decided people didn't need (or shouldn't need) to do things that were perfectly valid/useful in v2.

I don't guess that my problems right now are particularly related to this polyfill, so I don't need to keep creating noise here. Sorry!

If anyone has any suggested resources on how to navigate all these breaking changes, I'd certainly love to read it. I feel like I'm flailing in the dark here, because just about everything I google search about reveals stuff about v2 and nobody seems to have much concrete answers on v3.

Side note: it's annoying that Chrome is already warning (with logged errors) about manifest v2 extensions going away, but the v3 stuff we're supposed to convert to is not even fully shipped, and the docs for it are painfully lacking. Maybe I'm just in the pain because I decided to be proactive when I saw the error, and perhaps I should just sleep on this for about 6 months while more of this bakes fully.

XjSv commented 2 years ago

I came across this: https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Build_a_cross_browser_extension and it seems like things are still up in the air. The note specifically mentions that the webextension-polyfill is specifically for Manifest V2.

Building a cross-browser extension
Rob--W commented 2 years ago

They didn't list throwing away show(..) in the breaking changes from manifest v2 to v3... but it does seem like setPopup(..) is roughly equivalent... though now I have to pass the URL for the HTML page whereas in v2 that URL is specified in the manifest itself.

.show is NOT replaced with setPopup, the two are different. action.show is the replacement for pageAction.show and browserAction.enable.

In any case, I've now passed that error, and encountered a variety of others. For example, my extension (obviously) has a popup, and I used to be able to call browser.extension.getViews({ type: "popup" }) to get references to all instances of my extension's popup, so that I could get call postMessage(..) on each reference to send them all messages.

getViews(..) is another one which apparently was discarded from v2 to v3. :(

This is not the case. Extension pages can still use extension.getViews(). What you may be observing is the absence of extension.getViews() in a background Service Worker. That's because service workers run off the main thread, where they cannot access DOM APIs, including entire documents/windows that would be exposed via getViews().

A similar (but not equivalent or equivalent) seems to be action.getPopup(..). Sadly, that function doesn't get the popup window reference itself, it just gets the URL of the popup... which isn't at all helpful since I already manually provided that URL.

getPopup() never returned the window reference itself.

getify commented 2 years ago

@Rob--W

action.show is the replacement for pageAction.show and browserAction.enable.

Well, I wish that was the case, but... did you read this from me earlier in the thread?

I changed my calls from browser.pageAction.show(..) to browser.action.show(..), but that's throwing the exception: "browser.action.show is not a function". I then tried just directly chrome.action.show(..), but that's the same exception. And I then I tried the old browser.pageAction.show(..), but that gives an exception indicating that browser.pageAction is undefined (so thus the show property access fails).

So it's not (or wasn't at time of writing) in Chrome. Moreover, it's not (or wasn't at time of writing) in the MV3 docs.

And when I came here with my problem, here was the first response:

getify chrome.action.show Is not in mv3. You may be looking for setPopup

So... shrugs. It's clearly in a state of confusion and flux.

How am I supposed to migrate when faced with such mixed signals?

getify commented 2 years ago

@Rob--W

What you may be observing is the absence of extension.getViews() in a background Service Worker.

Well, what's my reasonable workaround then?

My MV2 extension sends messages from browser page to the background page, which then finds the extension popup to relay the message to. The background page (MV2) had no trouble doing that. If SW (MV3) cannot do so, how can I get these messages routed?

Rob--W commented 2 years ago

@Rob--W

action.show is the replacement for pageAction.show and browserAction.enable.

Well, I wish that was the case, but... did you read this from me earlier in the thread?

I changed my calls from browser.pageAction.show(..) to browser.action.show(..), but that's throwing the exception: "browser.action.show is not a function". I then tried just directly chrome.action.show(..), but that's the same exception. And I then I tried the old browser.pageAction.show(..), but that gives an exception indicating that browser.pageAction is undefined (so thus the show property access fails).

The API is only available if action is declared in manifest.json.

getify chrome.action.show Is not in mv3. You may be looking for setPopup

So... shrugs. It's clearly in a state of confusion and flux.

How am I supposed to migrate when faced with such mixed signals?

Read official documentation instead of relying on a comment from a random third party.

@Rob--W

What you may be observing is the absence of extension.getViews() in a background Service Worker.

Well, what's my reasonable workaround then?

My MV2 extension sends messages from browser page to the background page, which then finds the extension popup to relay the message to. The background page (MV2) had no trouble doing that. If SW (MV3) cannot do so, how can I get these messages routed?

The extension page or even a content script can directly communicate with the popup, e.g. with runtime.sendMessage.

getify commented 2 years ago

@Rob--W

The API is only available if action is declared in manifest.json.

This tone is almost deliberately condescending and hostile. It's like stating "the sky is blue" as if someone doesn't know that obviously, the sky is blue.

Of course I have "action" declared in the manifest. I was using the equivalent of it (browser.pageAction) in MV2. I couldn't even get a running MV3 extension (the manifest validation would have failed) without having first converted the pageAction manifest entry to action. I did so following the best information in the migration notes and the "official documentation".

As I stated above, I have other action.* methods in my code that are working just fine, but as I've now asserted multiple times, action.show(..) is NOT working, it shows as undefined and not a function, even though other action.* methods are present and working. Moreover, this "official documentation" doesn't even list show under action anymore -- or at least didn't at the time of my writing these messages.

Please refrain from insulting me like I have no idea what I'm doing. I have a perfectly good and workable MV2 extension that has worked for years. Migrating to MV3 has been a horrible nightmare, and it's the fault of the developers building the changes as well as those responsible for the poor state of the "official documentation".

Read official documentation instead of relying on a comment from a random third party.

First off, how on earth am I supposed to know who's who? Maybe you're the "random third party" for all I know.

[ Edit: these comments are off-topic ] The "official documentation" for MV3 is confusing and incomplete, at best.

But as I stated multiple different ways above in the thread, I HAVE READ THE DOCUMENTATION.

[ Edit: these comments are off-topic ] The documentation is wrong and broken.

Moreover, your dismissive and condescending tone here furthers the off-putting nature of this whole situation.

[ Edit: these comments are off-topic ] Google should be ashamed for the current state of this rollout. Why would anyone care to write or maintain extensions when they are treated as being ignorant and at fault when such a huge change is absurdly difficult to migrate, accompanied by poor and incomplete documentation.

Rob--W commented 2 years ago

@Rob--W

The API is only available if action is declared in manifest.json.

This tone is almost deliberately condescending and hostile. It's like stating "the sky is blue" as if someone doesn't know that obviously, the sky is blue.

That was not intended. I misread the error message that you pasted, and thought that the error indicated the absence of the action API in your extension, hence the question of whether "action" was declared.

Secondly, I made a typo in an earlier comment. The method is action.enable (not action.show). If you wonder why, it's historical: In Chrome, pageAction has been visually indistinguishable from browserAction for many years now (it wasn't the case in the past), hence Chrome decided to merge it with the browserAction API, followed by renaming it to action.

Read official documentation instead of relying on a comment from a random third party.

First off, how on earth am I supposed to know who's who? Maybe you're the "random third party" for all I know.

It seems that you believe that this is the place for feedback to Google, judging by your expressed frustration directed towards Google and Chrome. This is not the place for that.

You are currently posting questions and complaints about Google's MV3 implementation, in the issue tracker of a library that offers a Promise-based API layer over whatever the execution environment offers. The only overlap is "MV3" as subject, but other than that your comments are off-topic. Your questions about the API would fit better on a Q&A site or a forum for extension developers.

The "official documentation" for MV3 is confusing and incomplete, at best. But as I stated multiple different ways above in the thread, I HAVE READ THE DOCUMENTATION. The documentation is wrong and broken.

The API references match the browsers' implementations. If something is missing or inaccurate, file a bug report in the right places: https://github.com/mdn/content for MDN (Firefox, Safari and other browsers), and https://github.com/GoogleChrome/developer.chrome.com/ for Chrome-specific docs.

getify commented 2 years ago

@Rob--W

The method is action.enable

I read the docs for enable(..), but it sure doesn't seem to me semantically to be a replacement for show(..). I don't think of "enable" as "make visible" (which is what I'm intending). But if that's indeed the consolidation that occurred, and we're supposed to know that, fine. I am not sure I ever would have come to the conclusion (from the docs) that "enable" means "make the popup visible", but having learned that now, I'm glad for the clarification.

It is still the case that the migration guide/breaking-changes of those "official docs" does not list this "change" from show(..) to enable(..). If it had, I probably would never have come here.

That's not the fault of this polyfill, of course. So that complaint indeed belongs elsewhere. But it is nonetheless fact that bears on how this thread has unfolded.

the issue tracker of a library that offers a Promise-based API layer over whatever the execution environment offers

I'm only on this thread because when I initially encountered the errors in migration, it wasn't at all clear if any/all of them might be coming from this polyfill not being MV3 compliant (the OP's topic). The fact that the issue was still open led me to believe that the question was still undecided.

After a few messages exchanged here, it seemed clear that my issues likely weren't caused by the polyfill. So 2 months ago my "last" message said that I didn't want to continue creating undue noise in this repo/thread, and intended to take my support questions (and complaints) to a different channel (at some future date).

It was your belated comment (with mistake/typo) contradicting earlier facts/claims that restarted the conversation and brought me back into the mix here.

I'll again assert that, my intent is for THIS to be my last message here on the subject, and that my support questions and complaints will go elsewhere.

Side note: the OP had asked "is this polyfill MV3 compliant?" If it is, now -- and I think so, but I'm not in a position to know for sure -- then it seems like this thread is ready to be closed with an affirmative answer to that question. That would probably help clear up any future readers of this thread being confused by the unfortunate detours.

I misread the error message that you pasted... Secondly, I made a typo in an earlier comment

We all make mistakes. For the record, my tone escalated to significant frustration not from the first message you posted (with the mistake/typo), but from the second reply, where you doubled down with comments I interpreted as suggesting that the fault was entirely mine for not RTFM.

Moreover, I interpreted your defense of the "official" documentation, and your dismissal of another thread commenter as "random third-party", as positioning yourself instead as "official". That made your mistake/typo claims all the more infuriating. I don't have any idea who you work for, but it seemed to me like you were defending Google there, perhaps even as a Google employee might, so that's what spurred my Google-specific complaints.

You are currently posting questions and complaints about Google's MV3 implementation... your comments are off-topi

Agreed. I edited my earlier comment to strikeout the parts that were off-topic complaints about Google. My apologies for the noise.

fregante commented 2 years ago

I think that the polyfill isn't useful after MV3:

Firefox doesn't support MV3 at all at the moment so using this "Firefox polyfill" on a Chrome-only MV3 extension makes no sense.

Once Firefox supports MV3, you'll just be able to load your natively-promisified "Chrome-only" chrome-*-based MV3 extension in Firefox without a any polyfills.

Surely there will be some API discrepancies going forward, but the polyfill isn't really built with that in mind and has its own downsides/bugs.

Juraj-Masiar commented 1 year ago

So, if I want to keep using "browser" namespace in my MV3 Chromium extension, I could "simplify" this polyfill to just this:

if (!('browser' in self)) {
  self.browser = self.chrome;
}

Right? Or is the polyfill doing something more?

james-cnz commented 9 months ago

@Juraj-Masiar: Thanks, this helped me. I did run into an issue, though, that Chrome still doesn't support returning a promise from a message listener. Instead you have to start a promise that will call a callback, and immediately return true. See; https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/runtime/onMessage (Also, Chrome seems to have deprecated a bunch of functionality, so I'll have to work around that.) EDIT: One of the things Chrome's deprecated is the ability to specify multiple JavaScript files for a background script. You can specify a background script as a JavaScript module, that can import things from other JavaScript modules that export them, but you can't specify a content script as a JavaScript module, so if you want to have a JavaScript file that's shared between the background script and a content script, you need a workaround for that: https://stackoverflow.com/questions/48104433/how-to-import-es6-modules-in-content-script-for-chrome-extension EDIT 2: FYI, other things Chrome's deprecated are getBackgroundPage in popup scripts, and using DOMParser in the background script. So now, instead of the popup script being able to call getBackgroundPage and interact with the background script directly, it has to send messages, same as between the background script and content scripts. And instead of background scripts being able to use DOMParser directly, DOMParser has to be used from an "offscreen document". So more messages, I guess. It'll be like Old MacDonald's Farm, but with messages: Here a message, there a message, everywhere a message message.