|
| 1 | +# Application Preload |
| 2 | + |
| 3 | +Preloading allows Cobalt to start and initialize in the background without |
| 4 | +displaying any user interface. This enables a "background-to-foreground" |
| 5 | +transition that appears instantaneous to the user when they eventually choose to |
| 6 | +launch the application. |
| 7 | + |
| 8 | +## For Web Designers |
| 9 | + |
| 10 | +When an application is preloaded, it starts in a **hidden** state. Standard Web |
| 11 | +APIs correctly reflect this state: |
| 12 | + |
| 13 | +- `document.visibilityState` will be `"hidden"`. |
| 14 | +- `document.hidden` will be `true`. |
| 15 | + |
| 16 | +### Best Practices |
| 17 | + |
| 18 | +- Avoid starting audio playback or heavy graphical animations while in the |
| 19 | + hidden state. |
| 20 | +- Listen for the `visibilitychange` event on the `document` object to detect |
| 21 | + when the application transitions from preloaded to visible. |
| 22 | + |
| 23 | +## For Device Manufacturers (Starboard Porters) |
| 24 | + |
| 25 | +Preloading is managed through the Starboard lifecycle. |
| 26 | + |
| 27 | +- **Startup:** To start in preload mode, the Starboard implementation should |
| 28 | + send the `kSbEventTypePreload` event instead of `kSbEventTypeStart`. |
| 29 | + - In the shared Starboard application framework (`starboard/shared/starboard/application.cc`), |
| 30 | + the `--preload` command-line flag is recognized via the `kPreloadSwitch` constant. |
| 31 | + - If a platform implementation's `Application::IsPreloadImmediate()` returns true |
| 32 | + (typically by checking `HasPreloadSwitch()`), the application will |
| 33 | + automatically call `DispatchPreload()`. |
| 34 | + - `DispatchPreload()` then creates and dispatches the `kSbEventTypePreload` |
| 35 | + initial event, which is the signal to the application that it should |
| 36 | + initialize in a hidden state. |
| 37 | +- **Revelation:** To bring a preloaded application to the foreground, the |
| 38 | + platform should send a `kSbEventTypeReveal` signal. |
| 39 | + - On Linux-based platforms, this is often triggered by sending a `SIGCONT` |
| 40 | + signal to the process. |
| 41 | + - An example of this mapping can be found in `starboard/shared/signal/suspend_signals.cc`, |
| 42 | + where `SIGCONT` is handled by requesting a focus change. |
| 43 | + - The shared Starboard application logic in `starboard/shared/starboard/application.cc` |
| 44 | + automatically injects a `kSbEventTypeReveal` event if a focus request is |
| 45 | + received while the application is in the preloaded (concealed) state. |
| 46 | +- **Resource Management:** Cobalt defers the creation of the native window and |
| 47 | + associated graphics resources until the first `Reveal` signal is received. |
| 48 | + This minimizes the memory and CPU footprint of the application while it |
| 49 | + resides in the background. |
| 50 | +- **Splash Screen:** The creation of the splash screen's `WebContents` is also |
| 51 | + skipped when the application is preloaded, further reducing the background |
| 52 | + footprint. |
| 53 | + |
| 54 | +## For Cobalt Developers |
| 55 | + |
| 56 | +The "preload" signal is converted into a generic "visibility" state as soon as |
| 57 | +it enters the application layer. |
| 58 | + |
| 59 | +### Implementation Flow |
| 60 | + |
| 61 | +1. **Entry Point:** `SbEventHandle` (in `cobalt/app/cobalt.cc`) receives |
| 62 | + `kSbEventTypePreload`. |
| 63 | +2. **State Propagation:** An `is_visible` boolean (set to `false`) is passed |
| 64 | + through the constructor chain: `CobaltMainDelegate` -> |
| 65 | + `CobaltContentBrowserClient` -> `CobaltBrowserMainParts` -> |
| 66 | + `ShellBrowserMainParts`. |
| 67 | +3. **Initialization:** `ShellBrowserMainParts::PreMainMessageLoopRun` calls |
| 68 | + `Shell::Initialize`, passing the visibility state. |
| 69 | +4. **Splash Screen Skip:** `Shell::CreateNewWindow` checks the visibility state |
| 70 | + and skips creating the splash screen `WebContents` if the application is |
| 71 | + initially hidden. |
| 72 | +5. **Platform Delegate:** `Shell::Initialize` passes the state to |
| 73 | + `ShellPlatformDelegate::Initialize`. Each platform implementation (e.g., |
| 74 | + Aura, Views) stores this in a member variable. |
| 75 | +6. **Deferred Resource Creation:** `CreatePlatformWindow` checks `IsVisible()` |
| 76 | + (or `IsConcealed()`) and defers creating the `NativeWindow` and `Widget` if |
| 77 | + the application is not yet visible. |
| 78 | +7. **Revelation:** When `kSbEventTypeReveal` is received, `Shell::OnReveal()` is |
| 79 | + triggered. This calls `ShellPlatformDelegate::RevealShell`, which creates |
| 80 | + the deferred window resources and calls `WasShown()` on the `WebContents`, |
| 81 | + triggering the `visibilitychange` event for the web application. |
| 82 | + |
| 83 | +### Unit Testing |
| 84 | + |
| 85 | +Application lifecycle and visibility transitions are covered by the following |
| 86 | +unit tests in the `cobalt_unittests` binary: |
| 87 | + |
| 88 | +- **`LifecycleTest`** (`cobalt/shell/browser/lifecycle_unittest.cc`): Verifies |
| 89 | + correct window creation and visibility state propagation during startup, |
| 90 | + revelation, and redundant signals. |
| 91 | +- **`SplashScreenTest`** (`cobalt/shell/browser/splash_screen_unittest.cc`): |
| 92 | + Includes tests for ensuring the splash screen is skipped during preloading. |
| 93 | + |
| 94 | +### Integration Testing |
| 95 | + |
| 96 | +A robust integration test is provided in `cobalt/tools/test_preload.sh`. This |
| 97 | +test: |
| 98 | + |
| 99 | +1. Launches Cobalt in preload mode. |
| 100 | +2. Uses the Chrome DevTools Protocol (CDP) to verify that |
| 101 | + `document.visibilityState` is initially `"hidden"`. |
| 102 | +3. Sends a `SIGCONT` signal to reveal the application. |
| 103 | +4. Verifies via CDP that `document.visibilityState` transitions to `"visible"`. |
| 104 | +5. Sends a `SIGPWR` signal to verify clean shutdown. |
0 commit comments