blob: 22489083f39e2b88d0de39524692bec7614d5026 [file] [view]
# Application Lifecycle
In order to meet common needs of applications running on CE devices, Cobalt
implements a well-defined web application lifecycle, managing resources and
notifying the application as appropriate.
## Summary of changes in Cobalt 22
The application lifecycle has some changes from Cobalt 22:
### States:
* The *Paused* state is renamed to *Blurred*.
* The *Suspended* state is replaced by *Concealed* and *Frozen*.
* The *Preloading* state is removed, and *Concealed* is used instead.
Note: The corresponding attribute value 'prerender' for
document.visibilityState is also removed.
The new *Concealed* state is used for applications that are not visible but may
use CPU or network resources. This state is used to both replace the
*Preloading* state, and as an intermediate state between *Blurred* and
*Frozen*.
The *Frozen* state most closely resembles the previous *Suspended* state,
during which applications do not have network access.
### State Changes:
* The *Pause* event is renamed to *Blur*.
* The *Unpause* event is renamed to *Focus*.
* The *Suspend* event is replaced by *Conceal* and *Freeze*.
* The *Resume* event is replaced by *Unfreeze* and *Reveal*.
Most platforms should only need to replace 'Pause' with 'Blur', 'Unpause' with
'Focus', 'Suspend' with 'Freeze', and 'Resume' with 'Reveal'.
Since there is no longer a special *Preloading* state, applications should no
longer use the *Start* event when a preloaded application is brought to the
foreground. Instead, the same event(s) used for backgrounded applications
(*Concealed* or *Frozen*) should be used.
### Application 'Backgrounding' and 'Foregrounding'.
To signal that the application is being 'backgrounded', the use of *Suspend*
should be replaced with *Freeze*.
To signal that the application is being 'foregrounded', the use of *Unpause*
should be replaced with *Focus*.
Note: If a platform is using *Resume* (*Reveal*) to signal that an application
is being 'foregrounded', then that may result in unexpected application
behavior, unless a subsequent *Unpause* (*Focus*) is also used when the
application receives input focus.
More details about lifecycle states and state changes can be found in
`src/starboard/event.h`.
### Deprecated `SbEventType` values.
The `SbEventType` enum is defined in `src/starboard/event.h`.
* The `kSbEventTypePause` value is renamed to `kSbEventTypeBlur`.
* The `kSbEventTypeUnpause` value is renamed to `kSbEventTypeFocus`.
* The `kSbEventTypeSuspend` value is replaced by `kSbEventTypeConceal` and
`kSbEventTypeFreeze`.
* The `kSbEventTypeResume` value is replaced by `kSbEventTypeUnfreeze` and
`kSbEventTypeReveal`.
The corresponding helper functions in
`starboard::shared::starboard::Application` (implemented in
`starboard/shared/starboard/application.cc`) that inject events with these
values have been updated correspondingly:
* The `Pause()` method is renamed to `Blur()`.
* The `Unpause()` method is renamed to `Focus()`.
* The `Suspend()` method is replaced by `Conceal()` and
`Freeze()`.
* The `Resume()` method is replaced by `Unfreeze()` and
`Reveal()`.
Platforms that inject events themselves should be updated to use renamed event
type values, and platforms that use the helper functions should be updated to
call the corresponding renamed helper functions.
### Deprecated `SbSystemRequest` functions.
The `SbSystemRequest` functions are declared in `src/starboard/system.h`
* The `SbSystemRequestPause` event is renamed to `SbSystemRequestBlur`
* The `SbSystemRequestUnpause` event is renamed to `SbSystemRequestFocus`
* The `SbSystemRequestSuspend` event is replaced by `SbSystemRequestConceal`
and `SbSystemRequestFreeze`
* The `SbSystemRequestResume` event is replaced by `SbSystemRequestUnfreeze`
and `SbSystemRequestReveal`
## Application States
Starboard Application State | Page Visibility State | Window Focused
:-------------------------- | :-------------------- | :-------------
*Started* | visible | true
*Blurred* | visible | false
*Concealed* | hidden | false
*Frozen* | hidden | false
When transitioning between *Concealed* and *Frozen*, the document.onfreeze and
document.onresume events from the Page LifeCycle Web API will be dispatched.
### Started
The application is running, visible, and interactive. The normal foreground
application state. May be the start state, or can be entered from *Blurred*.
May only transition to *Blurred*. In Linux desktop, this happens anytime the
top-level Cobalt X11 window loses focus. Linux transition back to *Started*
when the top-level Cobalt X11 window gains focus again.
### Blurred
The application may be fully visible, partially visible, or completely
obscured, but it has lost input focus, so will receive no input events. It has
been allowed to retain all its resources for a very quick return to *Started*,
and the application is still running. May be entered from or transition to
*Started* or *Concealed* at any time.
### Concealed
The application is not visible and will receive no input, but is running. Can
be entered as the start state. May be entered from or transition to *Blurred*
or *Frozen* at any time. The application may be terminated in this state
without notification.
Upon entering, all graphics resources will be revoked until revealed, so the
application should expect all images to be lost, and all caches to be cleared.
#### Expectations for the web application
The application should **shut down** playback, releasing resources. On resume,
all resources need to be reloaded, and playback should be reinitialized where
it left off, or at the nearest key frame.
### Frozen
The application is not visible and will receive no input, and, once *Frozen*,
will not run any code. May be entered from or transition to *Concealed* at any
time. The application may be terminated in this state without notification.
Upon entering, all graphics and media resources will be revoked until resumed,
so the application should expect all images to be lost, all caches to be
cleared, and all network requests to be aborted.
#### Expectations for the porter
Currently, Cobalt does not manually stop JavaScript execution when it goes into
the *Frozen* state. In Linux desktop, it expects that a `SIGSTOP` will be
raised, causing all the threads not to get any more CPU time until resumed.
This will be fixed in a future version of Cobalt.
### Application Startup Expectations for the porter
The starboard application lifecycle, with descriptions of the states and the
state changes can be found in `src/starboard/event.h`.
For applications that can be preloaded, the platform should send
`kSbEventTypePreload` as the first Starboard event instead of
`kSbEventTypeStart`. Subclasses of
`src/starboard/shared/starboard/application.cc` can opt-in to use the already
implemented support for the `--preload` command-line switch.
If started with `kSbEventTypePreload`, the platform can at any time send
`kSbEventTypeFocus` when the application brought to the foreground.
In Linux desktop (linux-x64x11), this can be done by sending a `SIGCONT` to the
process that is in the *Preloading* state (see
`starboard/shared/signal/suspend_signals.cc`)
If the platform wants to only give applications a certain amount of time to
preload, they can send `SbSystemRequestFreeze` to halt preloading and move to
the *Frozen* state. In Linux desktop, this can be done by sending SIGUSR1 to
the process that is in the *Preloading* state.
## Implementing the Application Lifecycle (for the porter)
The platform Starboard implementation **must always** send events in the
prescribed order - meaning, for example, that it should never send a
`kSbEventTypeConceal` event unless in the *Blurred* state.
Most porters will want to subclass either `starboard::shared::Application` (in
`src/starboard/shared/starboard/application.cc`) or
`starboard::shared::QueueApplication` (in
`src/starboard/shared/starboard/queue_application.cc`), as these are reference
classes that rigorously implement the Starboard application lifecycle. They are
optional, and platforms can directly dispatch events to SbEventHandle(), but it
is then up to them to ensure that events are **always** sent in the correct
state as specified in the Starboard documentation.
`starboard::shared::Application` (in
`starboard/shared/starboard/application.cc`) guarantees the correct ordering by
implementing a small state machine that ignores invalid application state
transitions, and inserts any necessary transitions to make them valid. For
example, you can call `starboard::shared::Application::Conceal()`, and if you
are in *Blurred*, it will just dispatch a `kSbEventTypeConceal` event. But if
you call `Conceal()` in the *Started* state, it will first dispatch
`kSbEventTypeBlur`, followed by a `kSbEventTypeConceal` event. If you call
`Conceal()` in the *Concealed* state, it just does nothing.
This behavior can be ensured by only dispatching events to SbEventHandle()
using `Application::DispatchAndDelete()` either directly, or indirectly such
as by using `Application::RunLoop()` with the default implementation of
`Application::DispatchNextEvent()`.
To control starting up in the *Concealed* state for preloading, `Application`
subclasses must override two functions:
``` c++
class MyApplication : public shared::starboard::QueueApplication {
// [ ... ]
bool IsStartImmediate() override;
bool IsPreloadImmediate() override;
// [ ... ]
}
```
To start up in the *Concealed* state, `IsStartImmediate()` should return
`false` and `IsPreloadImmediate()` should return `true`.
To start up in the *Starting* state (which is the default), `IsStartImmediate()`
should return `true` and `IsPreloadImmediate()` will not be called.
To delay starting up until some later event, `IsStartImmediate()` and
`IsPreloadImmediate()` should both return `false`. No initial event will be
automatically sent to the application, and it is then up to the porter to
dispatch a `kSbEventTypeStart` or `kSbEventTypePreload` event as the first
event. This is useful if you need to wait for an asynchronous system activity to
complete before starting Cobalt.
To support the `--preload` command-line argument:
``` c++
bool IsStartImmediate() override { return !HasPreloadSwitch(); }
bool IsPreloadImmediate() override { return HasPreloadSwitch(); }
```