Skip to content

Commit

Permalink
Add non-"fully active" document handling guide (#317)
Browse files Browse the repository at this point in the history
* Add non-"fully active" document handling guide

As discussed in w3ctag/design-reviews#628, we want to improve support for BFCache/non-fully active documents, as we noticed that a lot of API specifications do not handle it well/explicitly.

This change adds various guides and things to be aware of related to non-"fully active" documents, including gives various examples. We hope this will help future APIs to handle BFCache/on-"fully active" documents correctly by default.

* Rewrite "discard" bit

* Apply suggestions from code review

Co-authored-by: Domenic Denicola <d@domenic.me>

* Apply suggestions from review

* Link to pattern

* Add note about prerender

* Link to discard definition

* Link to Geolocation API's TR page.

* Apply suggestions from code review

Co-authored-by: Marcos Cáceres <marcos@marcosc.com>

* Do not leave document stale

* prerender link

* Use old link for prerender

Co-authored-by: Domenic Denicola <d@domenic.me>
Co-authored-by: Marcos Cáceres <marcos@marcosc.com>
  • Loading branch information
3 people authored Sep 14, 2021
1 parent 6c67d67 commit e28ac9f
Showing 1 changed file with 178 additions and 0 deletions.
178 changes: 178 additions & 0 deletions index.bs
Original file line number Diff line number Diff line change
Expand Up @@ -579,6 +579,184 @@ See also:
* [Security and privacy are essential](https://www.w3.org/2001/tag/doc/ethical-web-principles/#privacy)
* [What data does this specification expose to an origin?](https://www.w3.org/TR/security-privacy-questionnaire/#underlying-platform-data)

<h3 id="support-non-fully-active">Support non-"fully active" documents</h3>

After a user navigated away from a document,
the document might be cached in a non-[=Document/fully active=] state,
and might be reused when the user navigates back to the entry holding the document, which makes navigation fast for users.
In browsers, this is known as the back/forward cache, or "<abbr title="back/forward cache">BFCache</abbr>" for short.
In the past, many APIs have missed specifying support for non-[=Document/fully active=] documents,
making them hard to support in various user agents to cache pages in the BFCache, effectively making the user experience of navigating back and forth less optimal.

To avoid this happening with your API,
you should specify support for non-[=Document/fully active=] documents by following these guidelines:

Note: It is possible for a document to not become [=Document/fully active=] for other reasons not related to
caching, such as when the iframe holding the document gets detached.
Some advices below might not be relevant for those cases,
since the document will never return to [=Document/fully active=] again.

<h4 id="gate-fully-active">Gate actions with [=Document/fully active=] checks</h4>

When performing actions that might update the state of a document,
be aware that the document might not be [=Document/fully active=]
and is considered as "non-existent" from the user's perspective.
This means they should not receive updates or perform actions.

Note: It is possible for a [=Document/fully active=] document to be perceived as "non-existent" by users,
such as when the document is [displaying prerendered content](https://jeremyroman.github.io/alternate-loading-modes/).
These documents might behave differently than non-[=Document/fully active=] documents,
and the guidelines here might not be applicable to them,
as it is written only for handling non-[=Document/fully active=] documents.

In many cases,
anything that happens while the document is not [=Document/fully active=]
should be treated as if it never happened.
If it makes more sense to "update" a document to ensure it does not hold stale information
after it becomes [=Document/fully active=] again, consider the [[#listen-fully-active]] pattern below.
<div class="example">
APIs that periodically send information updates,
such as Geolocation API's {{Geolocation/watchPosition()}}
should not send updates if the document is no longer fully active.
They also should not queue those updates to arrive later,
and only resume sending updates when the document becomes active again,
possibly sending one update with the latest information then.
</div>

<h4 id="listen-fully-active">Listen for changes to [=Document/fully active=] status</h4>

When a document goes from [=Document/fully active=] to non-[=Document/fully active=],
it should be treated similarly to the way discarded documents are treated.
The document must not retain exclusive access to shared resources
and must ensure that no new requests are issued
and that connections that allow for new incoming requests are terminated.
When a document goes from non-[=Document/fully active=] to [=Document/fully active=] again,
it can restore connections if appropriate.

While web authors can manually do cleanup (e.g. release the resources, sever connections)
from within the {{pagehide}} event and restore them from the {{pageshow}} event themselves,
doing this automatically from the API design allows the document to be kept alive after navigation by default,
and is more likely to lead to well-functioning web applications.

<div class="example">
APIs that create live connections can pause/close the connection and possibly resume/reopen it later.
It's also possible to let the connection stay open to complete existing ongoing requests,
and later update the document with the result when it gets restored, if appropriate (e.g.
resource loads).
</div>
<div class="example">
APIs that hold non-exclusive resources
may be able to release the resource when the document becomes not fully active,
and re-acquire them when it becomes [=Document/fully active=] again
(Screen Wake Lock API is already [doing](https://w3c.github.io/screen-wake-lock/#handling-document-loss-of-full-activity) the first part).
</div>

Note: this might not be appropriate for all types of resources,
e.g. if an exclusive lock is held,
we cannot just release it and reacquire when [=Document/fully active=]
since another page could then take that lock.
If there is an API to signal to the page that this has happened,
it may be acceptable but beware that if the only time this happens is with BFCache,
then it's likely many pages are not prepared for it. If it is not possible to support BFCache,
follow the [[#discard]] pattern described below.

Additionally, when a document becomes [=Document/fully active=] again,
it can be useful to update it with the current state of the world,
if anything has changed while it is in the non-[=Document/fully active=] state.
However, care needs to be taken with events that occurred while in the BFCache.
When not [=Document/fully active=], for some cases, all events should be dropped,
in some the latest state should be delivered in a single event,
in others it may be appropriate to queue events or deliver a combined event.
The correct approach is case by case and should consider privacy,
correctness, performance and ergonomics.

Note: Making sure the latest state is sent to a document that becomes
[=Document/fully active=] again is especially important when retrofitting existing APIs.
This is because current users of these APIs expect to always have the latest information.
Dropping state updates can leave the document with stale information,
which can lead to unexpected and hard-to-detect breakage of existing sites.

<div class="example">
The {{gamepadconnected}} event
can be sent to a document that becomes [=Document/fully active=] again
if a gamepad is connected while the document is not [=Document/fully active=].
If the gamepad was repeatedly connected and disconnected,
only the final connected event should be delivered.
(This is not specified yet, see [issue](https://github.com/w3c/gamepad/issues/149))
</div>
<div class="example">
For geolocation or other physical sensors,
no information about what happened while not [=Document/fully active=] should be delivered.
The events should simply resume from when the document became [=Document/fully active=].
However, these APIs should check the state when the document becomes [=Document/fully active=] again,
to determine if a status update should be sent (e.g. is the current location far away from the
location when the document becomes not fully active?), to ensure the document has the latest
information, as guaranteed by the API normally.
</div>
<div class="example">
For network connections or streams,
the data received while not [=Document/fully active=] should be delivered only
when the document becomes [=Document/fully active=] again,
but whereas a stream might have created many events with a small amount of data each,
it could be delivered as smaller number of events with more data in each.
</div>

<h4 id="omit-non-fully-active">Omit non-[=Document/fully active=] documents from APIs that span multiple documents</h4>
Non-[=Document/fully active=] documents should not be observable,
so APIs should treat them as if they no longer exist.
They should not be visible to the "outside world" through document-spanning APIs
(e.g. {{Clients/matchAll()|clients.matchAll()}}, {{Window/opener|window.opener}}).

Note: This should be rare since cross-document-spanning APIs are themselves relatively rare.

<div class="example">
{{BroadcastChannel}} [checks](https://html.spec.whatwg.org/multipage/web-messaging.html#broadcasting-to-other-browsing-contexts:fully-active) for [=Document/fully active=] before sending messages to other browsing contexts.
</div>
<div class="example">
{{Clients/matchAll()|clients.matchAll()}}
currently does not distinguish between [=Document/fully active=]
and non-[=Document/fully active=] clients,
but correct implementations should only return [=Document/fully active=] clients.
(See [issue](https://github.com/w3c/ServiceWorker/issues/1594))
</div>

<h4 id="discard">Discard non-[=Document/fully active=] documents for situations that can't be supported</h4>
If supporting non-[=Document/fully active=] documents is not possible for certain cases,
explicitly specify it by [=discard a document|discarding the document|=] if the situation happens after the user navigated away,
or setting the document's [salvageable](https://html.spec.whatwg.org/multipage/browsing-the-web.html#concept-document-salvageable)
bit to false if the situation happens before or during the navigation away from the document,
to cause it to be automatically discarded after navigation.

Note: this should be rare and probably should only be used when retrofitting old APIs,
as new APIs should always strive to work well with BFCache.

<div class="example">
WebSockets [sets the salvageable bit to false](https://html.spec.whatwg.org/#unloading-documents:concept-document-salvageable-7) during unload.
</div>
<div class="example">
Calling {{Clients/claim()|clients.claim()}}
should not wait for non-[=Document/fully active=] clients,
instead it should cause the non-[=Document/fully active=] client documents to be discarded.
(This is currently not specified, see [issue](https://github.com/w3c/ServiceWorker/issues/1594))
</div>

<h4 id="per-document-state">Be aware that per-document state/data might persist after navigation</h4>
As a document might be reused even after navigation,
be aware that tying something to a document's lifetime
also means reusing it after navigations.
If this is not desirable,
consider listening to changes to the [=Document/fully active=] state
and doing cleanup as necessary (see above).
<div class=example>
[=Sticky activation=] is determined by the "last activation timestamp",
which is tied to a document.
This means after a user triggers activation once on a document,
the document will have sticky activation forever,
even after the user navigated away and back to it again.
Whether this should actually be reset when full activity is lost or not
is still [under discussion](https://github.com/whatwg/html/issues/6588).
</div>

<h2 id="html">HTML</h2>

This section details design principles for features which are exposed via HTML.
Expand Down

0 comments on commit e28ac9f

Please sign in to comment.