Skip to content
This repository has been archived by the owner on Jul 18, 2022. It is now read-only.

The spec's definition of hidden is unclear and should be updated to better account for mobile use cases #59

Open
philipwalton opened this issue Jan 24, 2020 · 17 comments

Comments

@philipwalton
Copy link
Member

philipwalton commented Jan 24, 2020

One of the main, initial use cases for the Page Visibility API was to provide developers with a reliable end-of-session signal across both desktop and mobile operating systems.

Since on mobile operating systems it's possible to close a web page when the browser app isn't even running (e.g. via the App Switcher), there's no way the browser can reliably dispatch the pagehide, beforeunload or unload events in such cases. Because of this, the visibilitychange event has long been promoted as the last reliable signal to developers that a user might be leaving your page.

Yet years after the visibilitychange event was introduced and first promoted as a solution to this problem, the cross-browser compatibility story on mobile (and desktop) is still not good.

Part of the problem is due to known browser bugs (which I don't expect to be solved by filing this issue), but I think part of the problem is also due to the spec not always clearly stating what the expectations are and when the event should fire.

When is the visibilitychange event fired today?

The Page Visibility spec says the document's visibility state should return visible if any of the doc's viewport contents are observable to the user.

This text is problematic because it's not clear what "observable" means.

Mobile operating systems and browsers all display a screenshot of the page when entering either the Tab Switcher (in the browser) or the App switcher (via the OS). But is a static screenshot considered observable?

  • If no: then browsers should be firing the visibilitychange event as soon as the user enters the App/Tab switcher
  • If yes: then browsers should NOT fire the visibilitychange event until the page is not visible at all on the screen, but at that point it may be too late.

In my testing, no browser fires the visibilitychange event when first entering the App/Tab Switcher. They (seemingly) attempt to fire it after the user has taken their next action, which means in many cases the event doesn't fire at all.

Here's a breakdown of the current state of the visibilitychange event across desktop and mobile browsers, tested using: https://visibilitychange-logger.glitch.me/

Desktop

Tested on macOS Version 10.15.2 (Build 19C57)

  • Chrome 79.0.3945.130 (Official Build) (64-bit)
  • Firefox 72.0.2 (64-bit)
  • Safari 13.0.4 (15608.4.9.1.3
Action Chrome Firefox Safari
Refresh the page
Navigate away via a link
Navigate via the back/forward buttons
Switch tabs
Close the tab
Close the browser

Mobile

Tested on iPhone X (iOS 13.3) and Pixel 4 (Android 10)

  • Chrome 79.0.3945.136 (Official Build) (32-bit)
  • Firefox 68.4.2
  • Safari 13.3
Action Chrome Firefox Safari
Refresh the page
Navigate away via a link
Navigate via back/forward buttons
Switch tabs
Switch apps
Close the tab from the Tab Switcher
Close the app from the App Switcher

As you can see, Firefox is the only browser to reliably fire the visibilitychange event in all relevant cases, but I suspect they're only able to do that given the hooks Android exposes.

Based on what I know of iOS (someone from Apple please correct me if I'm wrong), a browser would not be able to run JavaScript at the moment it's being dismissed from the App Switcher.

If this is true then I propose we update the spec to make it clear that browsers should consider the hidden state to include cases where:

  • The page is in the background
  • The user is unable to interact with the page
  • The user is unable to see that page

Open bugs:

Bugs have been filed for all of the ❌ marks above, here is the complete list (as far as I am aware):

Next steps / resolution

If the WebPerf WG agrees that the visibilitychange event is the event that developers should be using as the last, reliable end-of-session signal, then we should work with browser vendors to ensure it's possible to fire the event in all the cases I outlined above.

If there isn't agreement on this point (and visibilitychange is just considered a visibility signal), then I'd argue we should work to define/standardize a new event that can cover all these cases (possibly as part of the Page Lifecycle API).

@rniwa
Copy link

rniwa commented Jan 30, 2020

Hm... looks like we're firing pagehide during page navigations bugs not visibilitychange as far as I've audited the code.

@philipwalton
Copy link
Member Author

@rniwa yes, that sounds inline with the results I was seeing in my testing. If I recall correctly, pagehide was being fired when navigating as well as when closing a tab on desktop Safari (here's another test page that logs more than just the visibilitychange event).

I don't think pagehide was being fired when closing Safari from the App Switcher, but I think that's acceptable if visibilitychange can be fired when entering the App/Tab Switcher on mobile operating systems (which this issue is proposing).

@philipwalton
Copy link
Member Author

We discussed this issue on today's WebPerf WG call, and the consensus was to update the Page Visibility spec to clarify that the hidden visibility state should include cases where the tab may be visible (e.g. a preview/screenshot) but it's not fully interactive to the user.

This includes cases such as:

  • A preview/screenshot of the page is shown when a user enters the App Switcher or Tab Switcher on mobile devices
  • A desktop browser introduces a new tab-selection interface that displays a screenshot of each tab open in the current window

In the cases above, despite the fact that a view of the page is visible to the user, these pages are still considered to be in the hidden state because they are not fully in the foreground or interactive to the user (without refocusing).

After making a change to the spec, UAs should update their app/tab-switching logic to fire the visibilitychange event as soon as the user enters the App Switcher or Tab Switcher (today the event is only fired once the user selects a new tab/app to switch to.

@bdekoz does this seem reasonable to you? No one from Mozilla was on the call today, and given that Firefox is the only browser to currently fire visibilitychange in all these cases I tested, I wanted to check and ensure you'd be OK with making such a change.

@yoavweiss
Copy link
Contributor

@bdekoz - friendly ping! :)

@marcoscaceres
Copy link
Member

I wonder if there something we can hook into from HTML about the document becoming inactive (or no longer user interactive)? @domenic, can you suggest if there is anything that is similar to what is described in #59 (comment) in HTML? (related also to #51 (comment))

@domenic
Copy link

domenic commented Jun 12, 2020

Not really. The closest would be the not-well-defined notions of "window having focus". See whatwg/html#5493 for the latest time that came up, which also has pointers to previous bugs. /cc @mustaqahmed.

However I suspect that trying to reuse "window focus" would not give the results you want on desktop browsers, so perhaps this is just an entirely new concept.

@bdekoz
Copy link

bdekoz commented Jul 30, 2020

after thinking about this a bit, in addition to phillipwalton's jan 30 comment, the case where

  • a preview/screenshot is shown in a tab switcher or app switcher
  • any new tab-selection interface

are cases where there is no top-level browsing context (as per july 30, 2020 meeting). If it would clarify visibility wording, this could be explicitly noted, saying it is a top-level internal context or top-level navigation context and not a top-level browsing context? Or some other terminology TBD in https://html.spec.whatwg.org/multipage/browsers.html#top-level-browsing-context

With this or similar clarification, it does seem reasonable for behavior where entering the tab switcher means other pages (even pages that have been active and have screenshots) are marked hidden

@yoavweiss
Copy link
Contributor

@bdekoz - re-reading the definition of top-level browsing context, it doesn't seem like being backgrounded changes it.
Rendering opportunity does take backgrounding into account, but doesn't properly define it.

One thing we can do is to expand determine the visibility state in order to also take into account backgrounding, and properly word it so that a page-preview doesn't count as "visible".

@rniwa
Copy link

rniwa commented Aug 11, 2020

I'm not sure what's being proposed is the right behavior. If you start playing a video on youtube.com on Safari and open app switcher, for example, the video will continue to play. Things like animations, etc... will need to continue to work in this state up until the user switches to another app.

@yoavweiss
Copy link
Contributor

Interesting! Maybe we need some third state (e.g. "preview") where the page would know it might soon be terminated, but needs to continue to display its content?

@rniwa
Copy link

rniwa commented Aug 11, 2020

Maybe what we need is something like "noninteractive", a state to indicate that the page is visible but not currently being interacted / interactive with the user.

@yoavweiss
Copy link
Contributor

Yeah. Added this as a discussion topic for this week's WG call.

@mmocny
Copy link

mmocny commented Oct 6, 2020

Safari has fixed visibilitychange on navigations: https://bugs.webkit.org/show_bug.cgi?id=151234. (I am unable to validate on device)

However, I also tried to re-run the steps from Philips initial post and noticed a few new peculiarities:

  • On Chrome, I am beginning to think there probably is correctly a visibilitychange: hidden event fired every time App switcher is launched, regardless if Chrome is later closed or re-focussed. However, I think maybe there may actually be a problem with saving to localStorage, causing a bug in the test itself. I will try to validate using an alternative method (like sending a beacon/XHR).

  • In Firefox on Android, when launching app switcher and killing FF, indeed you correctly get a visibilitychange: hidden.. but it seems you also get a spurious visibilitychange: visible as well (without a final visibilitychange: hidden). (Speculation: FF is giving JS within tab contents time to unload, but this also unintentionally(?) gives visibility?)

@mmocny
Copy link

mmocny commented Oct 6, 2020

(...for reproducing the FF spurious visibility:hidden, I was actually using the more detailed test page with the beforeunload handler registered. I haven't been able to reproduce again.)

I will say: even in FF, the visibilitychange: hidden event does not always arrive on app close.

@bokand
Copy link

bokand commented Oct 28, 2020

Adding my 2c, it seems undesirable to conflate visibility with a "you might get terminated" signal, the app switcher example is pretty clear why.

A non-interactive state seems to avoid this case but I worry that

  • it still conflates the termination signal with an unrelated app state (if some new exotic state is invented in the future we're back to the same problem)
  • intuitively, a non-interactive state cover basically every state other than "foreground tab" which makes it less useful in other scenarios

Some examples of things we'd want to do in prerendering/portal-type contexts:

  • Avoid recording analytics until page is made active
  • Mark a user as "away" in a chat app
  • Avoid starting a transaction countdown timer on a concert ticketing page until active

If we include background tab in "non-interactive" as would be intuitive it couldn't be used in any of these cases and would require an additional state.

Has anyone explored adding an explicit event for the "you may be terminated without further notice" situation? It sounds like the failure of unload events is that they promise more than the browser can deliver. Such a lastchance event could be worded and fired anytime the page might not get another chance to run: at unload but also at mobile background tab, app switcher, etc.

@bokand
Copy link

bokand commented Nov 13, 2020

FYI: I've expanded on the above in #69 - would appreciate any feedback there. In particular @bdekoz and @rniwa as this relates to preview-like situations which the app switcher examples above fall into.

@noamr
Copy link

noamr commented Jun 20, 2022

I suggest to move this discussion to the HTML spec

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

10 participants