This specification defines an API that allows web applications to configure the behaviour of app launches with respect to already open app instances. This API aims to cater to the needs of single instance web apps e.g. music players.

Prerequisites

In order to implement this API, the user agent MUST support [[[appmanifest]]].

Usage Example

A music player app wants to direct app shortcut launches to an existing window without interrupting music playback. This music app would add a [=manifest/launch_handler=] entry to the [[[appmanifest]]], as shown:

      {
        "name": "Music Player",
        "shortcuts": [{
          "name": "Now Playing",
          "url": "/"
        }, {
          "name": "Library",
          "url": "/library"
        }, {
          "name": "Favorites",
          "url": "/favorites"
        }, {
          "name": "Discover",
          "url": "/discover"
        }],
        "launch_handler": {
          "client_mode": "focus-existing"
        }
      }
      

The [=manifest/client_mode=] parameter set to [=client mode/focus-existing=] causes app launches to bring existing app instances (if any) into focus without navigating them away from their current document.

A {{LaunchParams}} will be enqueued on the {{Window/launchQueue}} where the music player can read the {{LaunchParams/targetURL}} in its {{LaunchConsumer}} and handle it in script e.g.:

        window.launchQueue.addConsumer((launchParams) => {
          const url = launchParams.targetURL;
          // If the URL is to one of the app sections, updates the app view to
          // that section without interrupting currently playing music.
          if (maybeFocusAppSection(url)) {
            return;
          }
          // Could not handle the launch in-place, just navigate the page
          // (interrupts any playing music).
          location.href = url;
        });
      

A user, already using the music player app to listen to music, activating the "Library" app shortcut will trigger an app launch to /library which gets routed to the existing app instance, enqueued in the page's {{Window/launchQueue}} which, through the assigned {{LaunchConsumer}}, brings the library section of the music player into focus without affecting the current music playback.

Extension to the Web App Manifest

The following steps are added to the extension point in the steps for processing a manifest:

  1. Run the steps for [=processing the launch_handler member=] given [=ordered map=] |json:ordered map| and [=ordered map=] |manifest:ordered map|.

[=manifest/launch_handler=] member

The `launch_handler` is a dictionary containing configurations for how web app launches should behave.

[=manifest/launch_handler=] is a dictionary despite [=manifest/client_mode=] being the only member. This is to give room for more members to be added should other types of behaviors be needed in the future.

The steps for processing the launch_handler member, given [=ordered map=] |json:ordered map|, [=ordered map=] |manifest:ordered map|, are as follows:

  1. If |json|["launch_handler"] does not [=map/exist=], return.
  2. If the type of |json|["launch_handler"] is not [=ordered map=], return.
  3. Set |manifest|["launch_handler"] to a new [=ordered map=].
  4. [=Process the `client_mode` member=] passing |json|["launch_handler"], |manifest|["launch_handler"].

[=manifest/client_mode=] member

The `client_mode` member of the [=manifest/launch_handler=] is a [=string=] or list of [=strings=] that specify one or more [=client mode targets=]. A [=client mode target=] declares a particular client selection and navigation behaviour to use for web apps launches.

User agents MAY support only a subset of the [=client mode targets=] depending on the constraints of the platform (e.g. mobile devices may not support multiple app instances simultaneously).

The client mode targets are as follows:

auto
The user agent's default launch routing behaviour is used.

The user agent is expected to decide what works best for the platform. e.g., on mobile devices that only support single app instances the user agent may use `navigate-existing`, while on desktop devices that support multiple windows the user agent may use `navigate-new` to avoid data loss.

navigate-new
A new web app client is created to load the launch's target URL.
navigate-existing
If an existing web app client is open it is brought to focus and navigated to the launch's target URL. If there are no existing web app clients the [=client mode/navigate-new=] behaviour is used instead.
focus-existing
If an existing web app client is open it is brought to focus but not navigated to the launch's target URL, instead the target URL is communicated via {{LaunchParams}} . If there are no existing web app clients the [=client mode/navigate-new=] behaviour is used instead.

It is necessary for the page to have a {{LaunchConsumer}} set on {{Window/launchQueue}} to receive the launch's {{LaunchParams}} and do something with it. If no action is taken by the page the user experience of the launch is likely going to appear broken.

To process the `client_mode` member, given [=ordered map=] |json_launch_handler:ordered map|, [=ordered map=] |manifest_launch_handler:ordered map|, run the following:

  1. If |json_launch_handler|["client_mode"] does not [=map/exist=], return.
  2. If the type of |json_launch_handler|["client_mode"] is [=list=]:
    1. [=list/For each=] |entry| of |json_launch_handler|["client_mode"]:
      1. If the type of |entry| is not [=string=], continue.
      2. If |entry| is supported by the user agent, set |manifest_launch_handler|["client_mode"] to |entry| and return.
  3. If |json_launch_handler|["client_mode"] is [=string=] and supported by the user agent, set |manifest_launch_handler|["client_mode"] to |json_launch_handler|["client_mode"] and return.
  4. Set |manifest_launch_handler|["client_mode"] to [=client mode/auto=].

`client_mode` accepts a list of strings to allow sites to specify fallback [=client mode targets=] to use if the preferred [=client mode target=] is not supported by the user agent or platform. The first supported [=client mode target=] entry in the list gets used.

Handling Web App Launches

Launching a Web App

When launching a web app via any means the trigger of the launch should run the steps to [=launch a web app=] with a {{LaunchParams}} containing relevant launch information (e.g. share data in the event of a share target launch).

Examples of different web app launch triggers:

The steps to launch a web app are given by the following algorithm and takes an optional {{LaunchParams}} |params:LaunchParams|.

  1. If |params| is null, set |params| to a new {{LaunchParams}} with {{LaunchParams/targetURL}} set to [=manifest/start_url=].
  2. Set |client| to the result of running the steps to [=prepare a web app launch client=] passing [=manifest/client_mode=] and |params|.{{LaunchParams/targetURL}}.
  3. Append |params| to the [=unconsumed launch params=] of the |client|'s document's {{Window/launchQueue}}.
  4. Run the steps to [=process unconsumed launch params=] on the |client|'s document's {{Window/launchQueue}}.

Preparing a [=Web App Launch Client=]

A web app launch client is a [=service worker client/window client=] associated with the web app.

The exact form of this association is up to the user agent e.g. a dedicated app window separate UI presentation from general hyperlink browsing.

To prepare a web app launch client given a processed manifest |manifest| and |target URL: URL|, run the following steps:

  1. Let [=client mode target=] |client_mode| be |manifest|["launch_handler"]["client_mode"].
  2. If |client_mode| is [=client mode/auto=], set |client_mode| to either [=client mode/navigate-new=] or [=client mode/navigate-existing=] according to the user agent's decision for which is most appropriate.
  3. Switching on |client mode|, run the following substeps:

    [=client mode/navigate-new=]
    1. Return the result of running the steps to [=prepare a new web app launch client=] passing |manifest| and |target URL|.
    [=client mode/navigate-existing=] or [=client mode/focus-existing=]
    1. If there is no [=service worker client/window client=] for the web app, return the result of the result of [=prepare a new web app launch client=] passing |manifest| and |target URL|.
    2. Let |client| be a [=service worker client/window client=] for the web app, the exact selection algorithm is decided by the user agent.
    3. If |client mode| is [=client mode/navigate-existing=], run the steps to [=navigate=] |client| to |target URL|.
    4. Return |client|.

Preparing a new [=Web App Launch Client=]

To prepare a new web app launch client given a processed manifest |manifest| and |target URL:URL|, run the following steps:

  1. Let |client| be the result of creating a new top-level browsing context.
  2. Apply |manifest| to |client|.
  3. Run the steps to [=navigate=] |client| passing |target URL|.
  4. Return |client|.

Processing [=unconsumed launch params=]

The steps to process unconsumed launch params given a {{LaunchQueue}} |queue| are as follows:

  1. If the [=assigned launch consumer=] |consumer| is set on |queue|:
    1. [=list/For each=] |launch_params:LaunchParams| of the |queue|'s [=unconsumed launch params=]:
      1. Invoke |consumer| with |launch_params|.
    2. Set |queue|'s [=unconsumed launch params=] to the empty list.

Script Interfaces to App Launches

LaunchParams interface

          [Exposed=Window] interface LaunchParams {
            readonly attribute DOMString? targetURL;
            readonly attribute FrozenArray<FileSystemHandle> files;
          };
        

{{LaunchParams/targetURL}} represents the [=URL=] of the launch which MUST be [=manifest/within scope=] of the application.

For every |file handle:FileSystemHandle| in {{LaunchParams/files}}, querying the file system permission with {{FileSystemPermissionDescriptor/mode}} set to {{FileSystemPermissionMode/"readwrite"}} MUST return {{PermissionState/"granted"}}.

LaunchConsumer function

          callback LaunchConsumer = any (LaunchParams params);
        

LaunchQueue interface

          partial interface Window {
            readonly attribute LaunchQueue launchQueue;
          };

          [Exposed=Window] interface LaunchQueue {
            undefined setConsumer(LaunchConsumer consumer);
          };
        

{{LaunchQueue}} has an unconsumed launch params which is a [=list=] of {{LaunchParams}} that is initially empty.

{{LaunchQueue}} has an assigned launch consumer which is an optional {{LaunchConsumer}} that is initially null.

setConsumer method

The {{LaunchQueue/setConsumer(consumer)}} method steps are:

  1. Set the [=assigned launch consumer=] to |consumer|.
  2. Run the steps to [=process unconsumed launch params=].

{{LaunchParams}} are passed to the document via a {{LaunchQueue}} instead of via events to avoid a race condition between a launch event firing and page scripts attaching the event listener. In contrast the {{LaunchQueue}} buffers all enqueued {{LaunchParams}} until a {{LaunchConsumer}} has been set.

Accessibility

This specification has no known accessibility considerations.