18

How can I close/exit my PWA?

I had assumed I'd be able to do this:

window.close();

Unfortunately, that results in the usual error:

Scripts may close only the windows that were opened by it.

Is there any way to programmatically close my PWA?

Brad
  • 159,648
  • 54
  • 349
  • 530

1 Answers1

7

So following MDN documentation on this:

window.close

In the past, when you called the window object's close() method directly, rather than calling close() on a window instance, the browser closed the frontmost window, whether your script created that window or not. This is no longer the case; for security reasons, scripts are no longer allowed to close windows they didn't open.

Following the living standard

If current is null or its is closing is true, then return. (Here is when the problem comes, as browsing context is null unless you opened a new document using the javascript context)

If all the following are true:

  • current is script-closable
  • the incumbent global object's browsing context is familiar with current
  • the incumbent global object's browsing context is allowed to navigate current

Also from the second part the script is going to be considered non-script-closable (Here you have more info)

A browsing context is script-closable if it is an auxiliary browsing context that was created by a script (as opposed to by an action of the user), or if it is a top-level browsing context whose session history contains only one Document.

So I am afraid that in modern browsers is quite impossible to actually close the current tab without having control over who opened it. This also applies to windows opened by iframes. All the info is in the current standard that I linked and the reasons are among the ones I quoted. Back some years there were work arounds using open("", "_self") and similar solutions, but I believe this one have been patched.

In case of PWA, you will be only able to close the application as long as there are no push events in the browse history (this means that window.history.length remains 1 for the whole navigation. In that case your application will be considered a top-level browsing context with only one document, hence window.close() will do. This last affirmation can be tested: Go to twitter.com -> Install Twitter PWA -> Open the PWA -> open console and write window.close(), it will inmediately shut the window. In the moment it navigates into any link within the PWA, the condition of history having only one document is unmeet.

Extra trying to find why was this a security issue: https://security.stackexchange.com/questions/120116/security-feature-preventing-javascript-from-closing-the-window

https://security.stackexchange.com/questions/133744/why-do-browsers-disallow-script-closing-an-opener-window-yet-allow-changing-its

SirPeople
  • 4,248
  • 26
  • 46
  • I'm asking about PWAs, not regular pages. "or if it is a top-level browsing context whose session history contains only one Document" That's exactly what my PWA is. So, sounds to me like a browser bug that it doesn't work in a PWA. – Brad Apr 17 '20 at 15:41
  • You are right (forgot the PWA on the title, my fault), in such case window.close works as intended. The easies way of testing this is , open a incognito window, and execute inmediately window.close... It closes. So probably you need to check if your affirmation is true, I believe that your PWA is not the top level browsing context – SirPeople Apr 17 '20 at 16:01
  • It's opened by a shortcut directly to the PWA... there is no other browsing context in that case. – Brad Apr 17 '20 at 16:02
  • Then it should work, I just tested with the Twitter PWA. Install the PWA, open the application. call window.close in the browser... and it inmediately closes. The easiest way to check is by window.history.length being equal to one – SirPeople Apr 17 '20 at 16:07
  • Ah, got it! Ok, so my PWA uses anchor fragments for its internal navigation... which means that the history length can be greater than 1, even if it's only been on a single origin. Short of a big iframe, know any way around that? – Brad Apr 17 '20 at 16:08
  • I updated the answer with more details. For this specific, I think that the strategy is instead of using anchor links (which push into the history) use the programmatically `window.location.replace("url")` which does not push into the history stack (It is how hash navigation usually works in SPA) – SirPeople Apr 17 '20 at 16:12
  • For example: In the page for react https://github.com/facebook/react you have several anchor text like https://github.com/facebook/react#contributing, if you click the links, it increases the history stack. If instead you use click events and do `window.location.replace("https://github.com/facebook/react#contributing")` navigation to the anchor will work but history will remain 1 – SirPeople Apr 17 '20 at 16:15
  • I think I'll just keep my navigation the way it is. It's useful for people on browsers, where the majority of my users use my PWA. Anyway, thanks for clearing up the history stack mystery! I'll leave this bounty open through the end of it, just to see if there are any other ideas as well. – Brad Apr 17 '20 at 16:17
  • Is there any specific technology/framework you use? Most of them offer alternatives to classical navigation that won't affect your end users. – SirPeople Apr 17 '20 at 16:41
  • No, mostly vanilla JS... I use Riot.js for custom elements, but that's it. I could probably hook `onclick` for links and use `window.location.replace()` if it detects its in PWA mode, but I think it's not worth the hassle for this project. – Brad Apr 17 '20 at 16:42
  • Well, it does not take that much code... adding something like: `document.querySelectorAll("a").forEach(element => element.addEventListener("click", event => { event.preventDefault(); window.location.replace(event.target.href)}))` would make all your links use replace instead :D – SirPeople Apr 17 '20 at 16:56