w3c / manifest

Manifest for web apps
https://www.w3.org/TR/appmanifest/
Other
653 stars 159 forks source link

Provide a way to not open a link in the PWA, in the moment #989

Open PieterjanDeClippel opened 2 years ago

PieterjanDeClippel commented 2 years ago

Many web apps have a login. This login page is often opened in a new window, in order not to interrupt the already running web application. However when a web app is installed as PWA, window.open() will always open the link in the PWA.

The usual flow in single-page applications is:

But since window.open() always opens the PWA, the external-login flow is interrupted. The bottom line is: a developer MUST BE allowed to have window.open open the url in a new window and prevent it from opening the PWA.

alancutter commented 2 years ago

What is the interruption in "external-login flow is interrupted"?

PieterjanDeClippel commented 2 years ago

Thanks for the reply.

ezgif-2-951fbd944edc

After clicking the "Login using xxx" button, I'm executing the following:

this.authWindow = window.open(`${this.baseUrl}/web/${this.apiVersion}/Account/${this.action}/${medium}/${this.platform}`, '_blank', 'width=600,height=400');
// For example
// this.authWindow = window.open(`https://example.com/web/v3/Account/connect/web/Microsoft`, '_blank', 'width=600,height=400');

Since https://example.com is the root of my PWA, you can see that window.open is opening my PWA instead of just a new tab. After signing in into the Microsoft/Facebook/... login, a script is executed:

<!DOCTYPE html>
<html lang="en">
<head>
    <title>Bezig met verwerken...</title>
    <script type="text/javascript">
        var message = {
            status: @( (int)Model.Status ),
            medium: "@Model.Medium",
            platform: "@Model.Platform",
            error: "@Model.Error",
            errorDescription: "@Model.ErrorDescription"
        };
        window.opener.postMessage(JSON.stringify(message), "https://example.com");
    </script>
</head>
<body>
</body>
</html>

But since this url has been opened in the PWA instead of a browser window, the message doesn't arrive in the opener's window.

opener -> opens https://example.com/signin-microsoft -> PWA is being launched -> Link with opener is broken.

PieterjanDeClippel commented 2 years ago

I should be able to tell window.open to open the url in the browser, and not in the PWA. As stated in this SO thread, I already tried everything.

But still the links open in the PWA. A year ago I worked around this by creating a subdomain hosting the MVC endpoints for the external logins, and that works pretty well. But now I ran into trouble while implementing 2-factor authentication, because the calls to ExternalLoginSigninAsync and TwoFactorAuthenticatorSignInAsync have to be executed from the same domain.

PieterjanDeClippel commented 2 years ago

And I've also already read about Declarative Link Capturing, but it seems that this does not cover the case of forcing window.open to open in the browser and not the PWA.

tomayac commented 2 years ago

And I've also already read about Declarative Link Capturing, but it seems that this does not cover the case of forcing window.open to open in the browser and not the PWA.

Yes, that's correct: "This proposal does not apply if the user is already within the navigation scope (for instance, if the user has a browser tab open that is within scope, and clicks an internal link)."article

tomayac commented 2 years ago

Adding the link to https://github.com/WICG/pwa-url-handler/issues/28.

LuHuangMSFT commented 2 years ago

I suspect this is an Android WebAPK behavior.

When a Progressive Web App is installed on Android, it will register a set of intent filters for all URLs within the scope of the app. When a user clicks on a link that is within the scope of the app, the app will be opened, rather than opening within a browser tab.

https://developers.google.com/web/fundamentals/integration/webapks#android_intent_filters

PieterjanDeClippel commented 2 years ago

Other PWA's

I did a little test, and installed the PWA for all of Chrome, Edge and Firefox (only on android, since the problem is only on android). The window.open behavior is as follows:

Chrome: Opens in the chrome-installed-PWA Edge: Opens in the edge-browser Firefox: Opens in the firefox-browser

On a second take in Edge, when calling window.open I do get a picker menu to choose from either the chrome PWA, and Chrome. Not Edge.

I dismissed the picker popup, and after then, when calling window.open the link forcefully opens in the CHROME PWA.

The Firefox-PWA and browser-app keep working fine however. I can repeatedly open the PWA, call window.open which opens the url in a window within the PWA shell, then go to the site in the Firefox android browser, call window.open which opens the url in a new browser tab. Multiple times.

Change PWA settings

On android, you can always press the pwa (chrome) shortcut icon → tap the (i) information icon → Set as default → Open supported links → choose "In other app". That "would" indeed fix the issue, but is not a fix on itself off course.

alancutter commented 2 years ago

I think filing a crbug.com is the way forward here. This sounds like an incompatibility with how Android Chrome handles web app link capturing especially given Firefox appears to handle it fine.

PieterjanDeClippel commented 2 years ago

Adding backlink to Chromium bugtracker

PieterjanDeClippel commented 2 years ago

This block of code appears to determine whether the PWA should be launched or a new tab should be opened.

Call stack:

PieterjanDeClippel commented 2 years ago

I was hoping that perhaps the draft can be updated, maybe stating something like this:

When a url-open-request is to be processed, the opener must verify if the origin's hoststring matches the hoststring of the requested url. If this is the case, the url must be opened as a new tab within the same browser. If the hoststrings do not match, the opener can call the operating system's handler for urls, which in its turn verifies if any native applications can handle the url.

Then the behavior would be the same in every browser. I don't know how other browsers (safari, opera, ...) are handling this case atm.

marcoscaceres commented 2 years ago

Shouldn't the user agent just respect window.open() tho? That's literally saying open a popup, so it should do that as normal specified in HTML.

PieterjanDeClippel commented 2 years ago

It seems that Firefox For Android doesn't even attempt to open the urls natively. Although I can understand that not all web browsers would opt-in into native behavior (as Chrome does, in this case opening the native app), there appears to be a huge inconsistency in how url-open requests are handled by browsers.

marcoscaceres commented 2 years ago

@PieterjanDeClippel understood.

I think we to distill this down to a simple test case that we can then send around to browser vendors.

I made a super simple PWA test case: https://github.com/marcoscaceres/playground/tree/gh-pages/popup

You can try it here: https://marcoscaceres.github.io/playground/popup/

We can use that if you'd like to file bugs on each individual browser vendor.

(FWIW, Safari seems to do the right thing cross-origin popups at least - it opens an overlay window/browser)

Note that this is still somewhat outside the scope of this specification... although we could provide some guidance around opening auxiliary browsing contexts.

barhemo commented 1 year ago

+1

verekia commented 1 year ago

Maybe another use case could revive this conversation. I am making a web game portal, which is basically simply a list of games that open in a new tab (target="_blank"). The portal itself can be installed as a PWA. I want games to open in the users' default browser, not inside the PWA, because it is a more familiar and full-featured experience for the user.

On desktop (Chrome, Mac), clicking on a game in the PWA opens it in the default browser, which is exactly what I want!

On Android, when opening a game from the PWA portal, it opens it in the in-PWA web view, which is a pretty bad user experience because the user cannot rotate to landscape mode in that view, and cannot use regular browser features in general. This makes the experience of using the PWA portal worse than using the portal in the browser. That's unfortunate.

On iOS, launching the PWA (after adding it to the home screen manually) is a bit better, because the web view allows rotating to landscape mode, but still, I would prefer launching the game in the user's default browser.

My scope is left blank. I tried different scope variations and it didn't change this behavior.

tomayac commented 1 year ago

There's a tester app, courtesy of @alancutter, that you can play with: https://hill-glitter-tree.glitch.me/.

Testing on a Mac (well noting that other OS may behave differently), the following options triggered from the installed PWA open in a new browser tab (nomenclature as per Alan's demo):

The Launch Handler API, which would let you control, for example, that there only ever be just one PWA instance, is still WIP, and, as far as I can tell from my experiments, only works in an origin trial on ChromeOS. The bug https://crbug.com/1231886 shows a number of Windows/Mac/Linux "blocked-on" sub bugs.

There is, so far, no API that could express "open <URL> in <this tab | that tab | a new tab | a new window | a new popup window | the browser | the OS>", as was pointed out by Alan in an internal thread.

verekia commented 1 year ago

@tomayac Thank you for sharing this tester app and for your insights!

For what it's worth, on my Pixel 5, Android 13, Chrome 109, none of the options of the tester app open in the main browser. The 4 options you listed above open in the in-PWA browser.

tomayac commented 1 year ago

@tomayac Thank you for sharing this tester app and for your insights!

For what it's worth, on my Pixel 5, Android 13, Chrome 109, none of the options of the tester app open in the main browser. The 4 options you listed above open in the in-PWA browser.

Yes, that’s the current behavior on Android. We agree it’s not always the ideal behavior.

kurwaBober commented 10 months ago

Is there any update on this issue?

piotr-cz commented 9 months ago

We use PWA with specific route for an OAuth login and we are unable to postMessage back as the window.opener is null. When opening PWA with const popup = window.open(), the popup.postMessage doesn't deliver messages, and popup is marked as closed.

monstermac77 commented 5 months ago

We're hitting this issue as well. We have to declare the scope of the PWA to be our entire domain, but we have certain links that should not be opened in the PWA (in fact many of users seem to be getting into a circular state based on some support and Google Play reviews, where they tap links inside the PWA that open in the PWA even though they should open in the browser, and then on those pages they try to get back to the app which opens the Play Store, and then they find the loop where opening the app from the Play Store just takes them back to the page they were on just before the Play Store). Confusing, I know, but this would be entirely avoided if there was a directive that could be applied, or "target=_blank" was respected, so that PWAs can better replicate native apps (e.g. we haven't heard from any of our native iOS users about this, because the expected behavior occurs where the links open in Safari).

a-type commented 4 months ago

I was hoping to make a suite of separate PWAs which are all tied to the same user account system. The idea was that I could include an affordance for switching between apps, similar to Google's app selector, and those links to other apps would open their respective PWA. Likewise you could open a link to manage your account which would take you back to the browser / "hub" site.

I'm gathering that this isn't feasible on mobile today. If I include a link to another one of my apps within an app, the second app will only open in a webview. That's going to be prohibitively confusing.

I'm not seeing a way to execute on this concept given the functionality available today, which is disappointing. By and large the limitations of PWAs have been avoidable, but once I hit one there's basically no getting by it.

Edit: OK, I misunderstood. After trying the helpful demo app above, I realized I should try installing continuous-harvest-tomato as a PWA as well. When both PWAs are installed, the appropriate PWA is indeed launched upon navigation!

The UX could be better for the account management portion, and opening an app you haven't installed yet will still show a frame (might need to see if I can make a custom UX around that - I'll probably append a query param to inter-app links from PWAs, then if the opened app is not also a PWA it will show a special install prompt) but this is workable for the time being.

I would still love a way to open a new tab from a PWA. For example, in my cooking app https://gnocchi.club, users can open the original recipe page for a web recipe in their collection, but it covers their app view and prevents cross referencing.

Edit 2: detecting the second app is opened inside a frame in the original, rather than as its own PWA, is proving difficult. To detect PWAs, I used to use window.matchMedia('(display-mode: standalone)'), but this evaluates as matching for the in-frame page too, I guess because its frame is rendered inside another PWA?

Knud13 commented 2 months ago

I'm also having this issue. We use the PayPal Buttons SDK on our site which works fine... until run on Android in the PWA. By default 3rd party cookies are blocked (which I'm fine with) but when our customers try and make a payment the PayPal sign in goes into a loop trying to verify identity. Easy I'll just make the payment page open in the browser with target="_blank" but this doesn't work. ahhh. Is there a way to make one page open in the browser and not in the PWA frame?

piotr-cz commented 1 month ago

I wonder if this issue might be mitigated with Tabbed web apps, where new_tab_button.url would point to OAuth login page

monstermac77 commented 1 month ago

I wonder if this issue might be mitigated with Tabbed web apps, where new_tab_button would point to OAuth login page

Definitely not preferred but you're right this would be a really good stopgap until they actually provide us a way to handle this!

piotr-cz commented 1 month ago

Unfortunately my workaround above won't work for OAuth, as

The url member is a string that represents a URL relative to the manifest URL that is within scope of a processed manifest.

Reference: https://wicg.github.io/manifest-incubations/#ref-for-dfn-new_tab_button-2

a-type commented 1 month ago

I'm realizing that the use case I'm looking for (opening the user's default browser / pwa host browser when clicking a link inside the PWA) is different from the original problem cited in this issue (opening a link in the user's browser opens the PWA because it matches the scope).

Given this issue isn't getting any attention, I wonder if it would be worth diverging these two use cases. I think some other commenters are also in my situation. Just want to keep things disambiguated.

For my problem, a few solutions come to mind:

PieterjanDeClippel commented 1 month ago

https://stackoverflow.com/a/59319252/8941307

PieterjanDeClippel commented 1 month ago

3 years on.

Done waiting for a draft.

Did it like this

Here's a demo

a-type commented 1 month ago

@PieterjanDeClippel Not working for me on Android 14, Chrome 124.0.6367.179. Still opens in a frame.

dmurph commented 1 month ago

I don't have any info about changing the behavior on Android here, but we have been working on getting (user controllable) link capturing to work on desktop platforms in Chromium, and our current plan is to only allow links to be captured into app launches if the link is creating a new 'top level transversable' spec, which basically means when there is supposed to be a new frame AND there isn't a shared browser context group with the opener (AKA no window.opener, no window.frames, etc). This makes sure the web platform stuff doesn't break, and from all of the examples we've looked at should match user expectations.

Will this possibly affect Android? I don't know. But I'll poke around to see how engrained that behavior is on the Android platform, and if it's feasible to change in the future. No promises though, this seems like a core thing on the Android platform (but I really have no idea).

monstermac77 commented 1 month ago

@dmurph Thanks so much for your attention on this, Daniel!

a-type commented 1 month ago

@dmurph Speaking as someone lacking technical knowledge of spec and implementations here, is this interpretation of practical outcomes correct?

That would be great, if true. Understood that your scope of work is not necessarily encompassing Android or other mobile OS, but setting some kind of standard for this functionality is a good start. It's a severe limitation today.

dmurph commented 1 month ago

(caveat this behavior is still being fleshed out)

@a-type wrote:

A link in a PWA with rel="noopener" and/or target="_blank" will open in another app - be it browser or other app registered to handle the link, like another PWA

I believe target="_blank" implies 'noopener' now in anchor links, but sadly not in window.open, which requires 'noopener' to be specified manually.

Those link clicks would be 'capturable'. If there is an app that controls that url, then it could be opened in that app. If not, then it would be opened in the browser.

@a-type wrote:

A link in a PWA with rel="opener" will (may?) open inside the PWA as a frame overlay.

This would open in whatever context the navigating-from browser context is in. If we are in an app, we open an app window for that, as it's in the same browser context group. If we're in the browser, we open in browser tab. (assuming we're creating a new browser context - if that target is a named frame that exists or self, then it would never be capturable.

I think we also want to special-case a middle-click on desktop that this is capturable but we force the 'new-client' behavior of launch handling, and we perhaps force the same context (of app or browser). So we still go in the launch queue but we ensure the expected user behavior of opening a new thing.

dmurph commented 1 month ago

Question about the paypal use-case - is this an issue with the paypal native app being installed? Or is this a paypal web app installed? Can you give me more context about that repro case?

Knud13 commented 1 month ago

Question about the paypal use-case -

Our paypal use-case... • chrome browser 3rd party cookies Allowed: works as excpected in both the browser or running out site as a PWA • chrome browser 3rd party cookies disabled: works as excpected If you have 3rd party cookies disabled in your chrome browser and use the paypal buttons the PayPal popup opens itself in a new tab and the payment process works ( this works exactly the same with PayPal app installed or not installed ) • chrome browser 3rd party cookies disabled and our site installed as a PWA BROKEN Here is where the problem occurs. When using the PWA version of our site the PayPal buttons open an in page popup and the PayPal sign in process fails (without an error) and cookies messages keep showing from paypal. This fails both with PayPal app installed or not installed. I tried making a link to our payment page with target="_blank" but this opens in a in-PWA web view and the PayPal process still fails.

I need a way to get the PayPal sign in to work within the PWA (e.g. allow the required PayPal cookies) or a way to open our payment page in the browser.

Cheers