1. Introduction
With large numbers of web apps (and tabs) running, critical resources such as memory, CPU, battery, network, etc. easily get oversubscribed, leading to a bad end-user experience. Application lifecycle is a key way that modern OSs manage resources.For a platform to support application lifecycle, it needs to:
-
provide developers with signals about transitions between the lifecycle states
-
provide lifecycle-compatible APIs that allow key capabilities to work even when the app is backgrounded or stopped.
This proposal attempts to define what the lifecycle of a web page is and add needed extensions to enable web applications to respond to two important lifecycle events commonly performed by user agents:
-
Tab discarding (for memory saving)
-
CPU suspension (for battery, data, CPU saving)
2. Page Lifecycle States
This spec defines what the lifecycle of a web page is and adds extensions to enable web applications to respond to two important lifecycle events commonly performed by user agents:
-
CPU suspension (for conserving battery, data, CPU)
-
Tab discarding (for memory saving)
This spec formalizes two new lifecycle states to support the above:
-
Frozen: lifecycle state for CPU suspension. This means that the §5.2.2.2 Change frozenness of document algorithm was called on the
Document
's browsing context. Normally HIDDEN pages will be frozen to conserve resources. -
Discarded: means that the discard algorithm was called on the
Document
's browsing context. Normally frozen frames will be moved to discarded state to conserve resources.
TODO(panicker): Insert diagram
3. API
Page Lifecycle involves the following additions:
partial interface Document {attribute EventHandler onfreeze ;attribute EventHandler onresume ;readonly attribute boolean wasDiscarded ; };
The onfreeze
and onresume
attributes are event handler IDL attributes for the freeze
and resume
events, respectively.
The wasDiscarded
attribute’s getter must return the value of this Document
's discarded boolean.
NOTE: these APIs are added on Document
, instead of on Window
, for consistency with the Page Visibility API; we expect these APIs to be used in tandem with that existing one. [PAGE-VISIBILITY]
NOTE: In addition clientId
and discardedClientId
will be added to Window
, to support restoring view state when user revisits a discarded page, causing a reload. We expect those to be used by code that reacts to these events.
3.1. Usage example
Example of handling freeze and resume:
const prepareForFreeze= () => { // Close any open IndexedDB connections. // Release any web locks. // Stop timers or polling. }; const reInitializeApp= () => { // Restore IndexedDB connections. // Re-acquire any needed web locks. // Restart timers or polling. }; document. addEventListener( 'freeze' , prepareForFreeze); document. addEventListener( 'resume' , reInitializeApp);
Example of restoring view state after discard: A user could have multiple tabs open for the same app & URL. If they are both in the background and are both discarded, then the app would need to distinguish between the two tabs to restore the correct state. clientId and lastClientId on the Window can be used for this purpose.
// Persists state to IndexedDB, making sure to set the current value of // `self.clientId` on the record, so it can be retrieved later using // `getPersistedState()` (if the tab has to be reloaded after a discard). const persistState= async( state) => { const record= {... state, cliendId: self. clientId}; // Persist record to IndexedDB or SessionStorage.... } // Retrieves the state record from IndexedDB based on the passed client ID. const getPersistedState= async( clientId) => { // Lookup record in IndexedDB... }; // If the tab was previously discarded, get the persisted state for the // client ID of the discarded tab via `self.lastClientId`. if ( document. wasDiscarded) { getPersistedState( self. lastClientId); }
4. Feature Policies
To improve CPU utilization of nested browsing contexts that are not critical to a page’s usability two feature policies are defined:-
execution-while-not-rendered, which has a default allowlist of *
-
execution-while-out-of-viewport, which has a default allowlist of *
The execution-while-not-rendered policy controls whether freezable tasks should execute for nested browsing contexts whose browsing context container is not being rendered. ie. `display: none`.
The execution-while-out-of-viewport policy controls whether freezable tasks should execute for nested browsing contexts whose browsing context container does not intersect the viewport according to compute the intersection of a target element and the root.
Application of these policies is accomplished by placing the document (and its decendants) in a frozen state when the following conditions have been met:
-
iframe load event steps have been run
-
the policy is disabled for the document
-
relevant policy condition applies (not rendered or scrolled out of view)
If these conditions aren’t met the document will be in the unfrozen state.
// The iframe will be frozen immediately after it is loaded. < iframe allow= "execution-while-not-rendered 'none'" src= "subframe.html" style= "display:none" >< /iframe>
To run the update document frozenness steps for a Document
document,
run the following steps:
-
If document’s browsing context is not a nested browsing context, return.
-
If document’s readiness is not "complete", return.
-
Let element be document’s nested browsing context's browsing context container.
-
Let frozenness be unset.
-
If document is not allowed to use the execution-while-not-rendered feature then:
-
If element’s is not being rendered, set frozenness’s to true.
-
Otherwise, set frozenness to false.
-
-
Otherwise if document is not allowed to use the execution-while-out-of-viewport feature then:
-
If element’s does not intersect the viewport according to compute the intersection of a target element and the root, set frozenness to true.
-
Otherwise, set frozenness to false.
-
-
If frozenness does not equal document’s FROZENNESS state, queue a task to be set document’s FROZENNESS to frozenness.
5. Processing model
5.1. Modifications to the HTML Standard
5.1.1. HTML: task source
Each task source is classified as freezable or unfreezable.
All tasks queued on freezable task source are automatically freezable.
Examples of freezable task sources are:
-
timers: callbacks generated by timers of a Document
-
networking: tasks queued due to loading of resources
-
DOM manipulation, user interaction, history traversal etc.
NOTE: networking task source is freezable with an exception: when a pending fetch keep-alive request in inflight, then networking task source must not be frozen.
Examples of unfreezable task sources are:
-
V8 task sources for garbage collection
-
IPC task sources needed to communicate with browser process
-
other task sources that are shared with the entire process
TODO(domenic): classify all existing task sources in the HTML spec; find out if other specs have defined any relevant task sources.
TODO(panicker): Add note about keeping everything within a site instance that are scriptable (eg. documents that can post-message with each other) in the same lifecycle state.
5.1.2. HTML: Unloading documents and History Traversal
When documents move into and out of bfcache (back forward cache) they will transition its frozenness state to
and
respectively.
-
In the unload a document algorithm, after Step #5, if the
persisted
attribute is true (i.e. we are moving to bfcache), run the §5.2.2.2 Change frozenness of document algorithm, passing in document and
.true -
In the traverse the history algorithm, before Step #4.6.4, if the
persisted
attribute is true (i.e. we are moving out of bfcache), run the §5.2.2.2 Change frozenness of document algorithm, passing in document and
.false
5.1.3. HTML: HTML Event Loop Processing Model
Step #1 currently ignores "tasks whose associated Document
s are not fully active". Modify this clause to also ignore freezable tasks whose associated Document
is in the frozen state.
After Step #11 during the Update the rendering add the following step.
For each fully active Document
in docs, run the update document frozenness steps.
5.1.4. HTML: Discarding browsing context
Rename the "discard" concept, for both browsing contexts and documents, to "destroy". This allows us to use the "discarded" terminology for the user-facing wasDiscarded
attribute.
5.1.5. HTML: Modifications to Initialize the document
Before Step #3 add following:
If the browsing context was previously discarded, then set the Document
's discarded boolean to true.
5.1.6. HTML: Modifications to iframe load event steps
After Step #5 add following:
Run the update document frozenness steps.
5.2. Additions to Page Lifecycle spec
5.2.1. FROZENNESS state
A document can be in one of the following FROZENNESS states:-
true: the document is frozen, any freezeable tasks associated with the document will not run
-
false: the document is unfrozen, tasks associated with the document will run as usual
NOTE: Per the §5.2.2.1 Change frozenness of a top-level document algorithm, when the Document of the top level browsing context changes its frozenness state then all documents of descendant browsing contexts will also change frozenness to the same value (and be consistent with the Document of the top level browsing context).
The UA may choose to execute §5.2.2.1 Change frozenness of a top-level document with
in certain situations.
For instance, if a browsing context is in the background or hidden, and a grace period has elapsed the UA could execute §5.2.2.1 Change frozenness of a top-level document with
to conserve resources and maintain the quality of the (foreground) user experience.
Specific examples:
-
In mobile Chrome, tabs that have been in background for (at least) 5 minutes, may be frozen, to conserve battery and data.
-
In desktop Chrome, background tabs that are not important to the user (not used in some time) may be discarded, to conserve memory
NOTE: background tabs that are actively doing work on behalf of the user (eg. playing audio) are generally not frozen or discarded.
NOTE: For a detailed list of heuristics & exclusions used by Chrome, see this doc.
The UA will typically execute §5.2.2.1 Change frozenness of a top-level document with
when the user revisits that browsing context. In addition, the UA may choose to periodically execute §5.2.2.1 Change frozenness of a top-level document with
in the background, if plentiful resources are available.
5.2.2. Reacting to FROZENNESS state changes
5.2.2.1. Change frozenness of a top-level document
To change the frozenness of a top-level document passing in a document doc and frozenness state x (one oftrue
, false
):
-
Assert: doc’s browsing context is a top-level browsing context.
-
Execute §5.2.2.2 Change frozenness of document passing in doc and x.
-
Let descendants be the list of the descendant browsing contexts of doc.
-
For each browsing context b in descendants:
-
Let d be the active document of b.
-
Execute §5.2.2.2 Change frozenness of document passing in d and x.
-
5.2.2.2. Change frozenness of document
To change the frozenness of a document given a document doc and frozenness state x:-
If x is
:true -
Set doc’s frozenness state to
.true -
Fire an event named
freeze
at doc.
-
-
Otherwise:
-
Fire an event named
resume
at doc. -
Set doc’s frozenness state to
.false
NOTE: it is intentional that the ordering between event-firing and setting the state are different in the two cases above.
-
5.2.3. Discarding
Each Document has a discarded boolean, which is initially false.To discard a browsing context, destroy the browsing context, and make note of the fact that the reason it and any descendant browsing contents were destroyed was because of discarding.
NOTE: Discard is typically done to reclaim system memory, when memory and other resources are running low. On the other hand destroying a browser context is the normal teardown due to user leaving the page etc.
Browsing contexts -- that are in the background and have their documents in VisibilityState hidden -- can be discarded, under resource pressure (eg. low memory). Specific example:
-
In desktop Chrome, background tabs that are not important to the user (not used in some time) may be discarded, to conserve memory
NOTE: background tabs that are actively doing work on behalf of the user (eg. playing audio) are generally not discarded.
NOTE: For a detailed list of heuristics & exclusions used by Chrome, see this doc.
When a top-level browsing context (tab in the browser) is discarded due to resource pressure (or unexpected events eg. process crash), and later the user revisits the tab in the browser, then the Document
's discarded boolean will be true due to §5.1.5 HTML: Modifications to Initialize the document.
6. Acknowledgements
Special thanks to Ojan Vafai, Philip Walton, Fadi Meawad, Olli Pettay, Todd Reifsteck for their technical input and suggestions that led to improvements to this specification.