w3c / page-visibility

Page Visibility
https://www.w3.org/TR/page-visibility-2/
Other
35 stars 23 forks source link

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

Open philipwalton opened 4 years ago

philipwalton commented 4 years ago

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?

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)

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)

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:

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 commented 4 years ago

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

philipwalton commented 4 years ago

@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 commented 4 years ago

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:

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 commented 4 years ago

@bdekoz - friendly ping! :)

marcoscaceres commented 4 years ago

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 https://github.com/w3c/page-visibility/issues/59#issuecomment-580446312 in HTML? (related also to https://github.com/w3c/page-visibility/issues/51#issuecomment-529269709)

domenic commented 4 years ago

Not really. The closest would be the not-well-defined notions of "window having focus". See https://github.com/whatwg/html/issues/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 commented 4 years ago

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

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 commented 4 years ago

@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 commented 4 years ago

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 commented 4 years ago

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 commented 4 years ago

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 commented 4 years ago

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

mmocny commented 4 years ago

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:

mmocny commented 4 years ago

(...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 commented 4 years ago

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

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

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 commented 4 years ago

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 commented 2 years ago

I suggest to move this discussion to the HTML spec