이 명세는 웹 애플리케이션이 이미 열려 있는 앱 인스턴스와 관련하여 앱 실행의 동작을 구성할 수 있게 하는 API를 정의한다. 이 API는 음악 플레이어와 같은 단일 인스턴스 웹 앱의 요구를 충족하는 것을 목표로 한다.

전제 조건

이 API를 구현하려면, 사용자 에이전트는 [[[appmanifest]]]를 지원해야 한다.

사용 예제

음악 플레이어 앱은 음악 재생을 중단하지 않고 앱 바로 가기 실행을 기존 창으로 전달하려고 한다. 이 음악 앱은 다음과 같이 [[[appmanifest]]]에 [=manifest/launch_handler=] 항목을 추가한다:

      {
        "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"
        }
      }
      

[=manifest/client_mode=] 매개변수를 [=client mode/focus-existing=]로 설정하면, 앱 실행 시 기존 앱 인스턴스가 있는 경우 해당 인스턴스를 현재 문서에서 벗어나도록 탐색시키지 않고 포커스로 가져온다.

{{LaunchParams}}가 {{Window/launchQueue}}에 큐에 추가되며, 음악 플레이어는 {{LaunchConsumer}}에서 {{LaunchParams/targetURL}}을 읽고 스크립트에서 이를 처리할 수 있다. 예:

        window.launchQueue.setConsumer((launchParams) => {
          const url = launchParams.targetURL;
          // URL이 앱 섹션 중 하나를 가리키면, 현재 재생 중인 음악을
          // 중단하지 않고 앱 뷰를 해당 섹션으로 갱신한다.
          if (maybeFocusAppSection(url)) {
            return;
          }
          // 실행을 제자리에서 처리할 수 없으므로, 페이지를 탐색한다
          // (재생 중인 음악이 중단된다).
          location.href = url;
        });
      

이미 음악 플레이어 앱에서 음악을 듣고 있는 사용자가 "Library" 앱 바로 가기를 활성화하면, /library로의 앱 실행이 트리거되고 이것이 기존 앱 인스턴스로 라우팅되어 페이지의 {{Window/launchQueue}}에 큐에 추가되며, 할당된 {{LaunchConsumer}}를 통해 현재 음악 재생에 영향을 주지 않고 음악 플레이어의 라이브러리 섹션을 포커스로 가져온다.

Web App Manifest에 대한 확장

다음 단계들이 확장 지점, 즉 manifest 처리 단계에 추가된다:

  1. [=ordered map=] |json:ordered map| 및 [=ordered map=] |manifest:ordered map|이 주어졌을 때 [=processing the launch_handler member=] 단계를 실행한다.

[=manifest/launch_handler=] 멤버

`launch_handler`는 웹 앱 실행이 어떻게 동작해야 하는지에 대한 구성을 담는 dictionary이다.

[=manifest/launch_handler=]는 [=manifest/client_mode=]가 유일한 멤버임에도 dictionary이다. 이는 향후 다른 유형의 동작이 필요해질 경우 더 많은 멤버를 추가할 여지를 두기 위한 것이다.

[=ordered map=] |json:ordered map|, [=ordered map=] |manifest:ordered map|이 주어졌을 때 processing the launch_handler member 단계는 다음과 같다:

  1. |json|["launch_handler"]가 [=map/exist=]하지 않으면 반환한다.
  2. |json|["launch_handler"]의 타입이 [=ordered map=]이 아니면 반환한다.
  3. |manifest|["launch_handler"]를 새로운 [=ordered map=]으로 설정한다.
  4. |json|["launch_handler"], |manifest|["launch_handler"]를 전달하여 [=Process the `client_mode` member=]를 수행한다.

[=manifest/client_mode=] 멤버

[=manifest/launch_handler=]의 `client_mode` 멤버는 하나 이상의 [=client mode targets=]를 지정하는 [=string=] 또는 [=strings=]의 리스트이다. [=client mode target=]은 웹 앱 실행에 사용할 특정 클라이언트 선택 및 탐색 동작을 선언한다.

사용자 에이전트는 플랫폼의 제약(예: 모바일 기기가 여러 앱 인스턴스를 동시에 지원하지 않을 수 있음)에 따라 [=client mode targets=]의 일부만 지원할 수 있다.

client mode targets는 다음과 같다:

auto
사용자 에이전트의 기본 실행 라우팅 동작이 사용된다.

사용자 에이전트는 플랫폼에 가장 적합한 방식을 결정할 것으로 기대된다. 예를 들어, 단일 앱 인스턴스만 지원하는 모바일 기기에서는 사용자 에이전트가 `navigate-existing`을 사용할 수 있고, 여러 창을 지원하는 데스크톱 기기에서는 데이터 손실을 피하기 위해 `navigate-new`를 사용할 수 있다.

navigate-new
실행의 대상 URL을 로드하기 위해 새 웹 앱 클라이언트가 생성된다.
navigate-existing
기존 웹 앱 클라이언트가 열려 있으면 포커스로 가져오고 실행의 대상 URL로 탐색한다. 기존 웹 앱 클라이언트가 없으면 대신 [=client mode/navigate-new=] 동작이 사용된다.
focus-existing
기존 웹 앱 클라이언트가 열려 있으면 포커스로 가져오지만 실행의 대상 URL로 탐색하지 않고, 대신 대상 URL이 {{LaunchParams}}를 통해 전달된다. 기존 웹 앱 클라이언트가 없으면 대신 [=client mode/navigate-new=] 동작이 사용된다.

페이지가 실행의 {{LaunchParams}}를 수신하고 이를 처리하려면 {{Window/launchQueue}}에 {{LaunchConsumer}}가 설정되어 있어야 한다. 페이지가 아무 동작도 취하지 않으면 실행의 사용자 경험은 깨진 것처럼 보일 가능성이 높다.

[=ordered map=] |json_launch_handler:ordered map|, [=ordered map=] |manifest_launch_handler:ordered map|이 주어졌을 때 process the `client_mode` member하려면, 다음을 실행한다:

  1. |json_launch_handler|["client_mode"]가 [=map/exist=]하지 않으면 반환한다.
  2. |json_launch_handler|["client_mode"]의 타입이 [=list=]이면:
    1. |json_launch_handler|["client_mode"]의 각 |entry|에 대해 [=list/For each=]:
      1. |entry|의 타입이 [=string=]이 아니면 계속한다.
      2. |entry|가 사용자 에이전트에 의해 지원되면, |manifest_launch_handler|["client_mode"]를 |entry|로 설정하고 반환한다.
  3. |json_launch_handler|["client_mode"]가 [=string=]이고 사용자 에이전트에 의해 지원되면, |manifest_launch_handler|["client_mode"]를 |json_launch_handler|["client_mode"]로 설정하고 반환한다.
  4. |manifest_launch_handler|["client_mode"]를 [=client mode/auto=]로 설정한다.

`client_mode`는 사이트가 선호하는 [=client mode target=]이 사용자 에이전트나 플랫폼에 의해 지원되지 않는 경우 사용할 대체 [=client mode targets=]를 지정할 수 있도록 문자열 리스트를 허용한다. 리스트에서 처음으로 지원되는 [=client mode target=] 항목이 사용된다.

웹 앱 실행 처리

처리를 수반한 웹 앱 실행

이 명세는 [=manifest/launch_handler=]의 동작을 포함하도록 기존의 [=launch a web application=] 알고리즘을 [=launch a web application with handling=] 단계로 대체한다.

launch a web application with handling 단계는 다음 알고리즘으로 주어진다. 이 알고리즘은 [=Document/processed manifest=] |manifest:processed manifest|, 선택적 [=URL=] 또는 {{LaunchParams}} |params|, 선택적 [=POST resource=] |POST resource|를 취하고 [=application context=]를 반환한다.

  1. |params|가 주어지지 않았으면, |params|를 |manifest|.[=manifest/start_url=]로 설정한다.
  2. |params|가 [=URL=]이면, {{LaunchParams/targetURL}}이 |params|로 설정된 새 {{LaunchParams}}로 |params|를 설정한다.
  3. Assert: |params|.{{LaunchParams/targetURL}}는 |manifest|의 [=manifest/within scope=]이다.
  4. |manifest|, |params| 및 |POST resource|를 전달하여 [=prepare an application context=] 단계를 실행한 결과로 |application context|를 설정한다.
  5. |params|를 |application context|의 document의 {{Window/launchQueue}}의 [=unconsumed launch params=]에 추가한다.
  6. |application context|의 [=navigable/active document=]의 {{Window/launchQueue}}에서 [=process unconsumed launch params=] 단계를 실행한다.

    |application context|는 [=assigned launch consumer=]가 있는 기존 인스턴스일 수 있으므로, 새로 추가된 {{LaunchParams}}를 처리할 필요가 있다.

prepare an application context 단계는 다음 알고리즘으로 주어진다. 이 알고리즘은 [=Document/processed manifest=] |manifest:processed manifest|, {{LaunchParams}} |launch params|, 선택적 [=POST resource=] |POST resource|를 취하고 [=application context=]를 반환한다.

  1. [=client mode target=] |client_mode|를 |manifest|.[=manifest/launch_handler=].[=manifest/client_mode=]로 둔다.
  2. |client_mode|가 [=client mode/auto=]이면, 사용자 에이전트가 가장 적절하다고 판단한 결정에 따라 |client_mode|를 [=client mode/navigate-new=] 또는 [=client mode/navigate-existing=] 중 하나로 설정한다.
  3. |client mode|에 대해 switch하여 다음 하위 단계를 실행한다:

    [=client mode/navigate-new=]
    1. |manifest|, |launch params|.{{LaunchParams/targetURL}} 및 |POST resource|를 전달하여 [=create a new application context=] 단계를 실행한 결과를 반환한다.
    [=client mode/navigate-existing=] 또는 [=client mode/focus-existing=]
    1. |manifest|가 [=applied=]된 [=application context=]가 없으면:
      1. |manifest|, |launch params|.{{LaunchParams/targetURL}} 및 |POST resource|를 전달하여 [=create a new application context=] 단계를 실행한 결과를 반환한다.
    2. |application context|를 |manifest|가 [=applied=]된 [=application context=]로 둔다. 여러 개가 있는 경우 사용자 에이전트가 가장 적절한 것을 선택한다.

      적절한 선택 전략은 가장 최근에 포커스된 것을 선택하는 것일 수 있다.

    3. |client mode|가 [=client mode/focus-existing=]이고 |application context|의 현재 세션 기록 항목URL이 |manifest|의 [=manifest/within scope=]이면:
      1. |application context|의 viewport를 포커스로 가져온다.
      2. |application context|를 반환한다.
    4. |application context|를 |launch params|.{{LaunchParams/targetURL}}로 [=Navigate=]하고 |POST resource|를 전달한다.
    5. |application context|를 반환한다.

[=unconsumed launch params=] 처리

{{LaunchQueue}} |queue|가 주어졌을 때 process unconsumed launch params 단계는 다음과 같다:

  1. [=assigned launch consumer=] |consumer|가 |queue|에 설정되어 있으면:
    1. |queue|의 [=unconsumed launch params=]의 각 |launch_params:LaunchParams|에 대해 [=list/For each=]:
      1. |consumer|를 |launch_params|와 함께 호출한다.
    2. |queue|의 [=unconsumed launch params=]를 빈 리스트로 설정한다.

앱 실행에 대한 스크립트 인터페이스

LaunchParams 인터페이스

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

{{LaunchParams/targetURL}}는 애플리케이션의 [=manifest/within scope=]여야 하는 실행의 [=URL=]을 나타낸다.

{{LaunchParams/files}}의 모든 |file handle:FileSystemHandle|에 대해, {{FileSystemPermissionDescriptor/mode}}를 {{FileSystemPermissionMode/"readwrite"}}로 설정하여 파일 시스템 권한을 질의하면 {{PermissionState/"granted"}}를 반환해야 한다.

LaunchConsumer 함수

          callback LaunchConsumer = any (LaunchParams params);
        

LaunchQueue 인터페이스

          partial interface Window {
            readonly attribute LaunchQueue launchQueue;
          };

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

{{LaunchQueue}}는 초기에 비어 있는 {{LaunchParams}}의 [=list=]인 unconsumed launch params를 가진다.

{{LaunchQueue}}는 초기에 null인 선택적 {{LaunchConsumer}}인 assigned launch consumer를 가진다.

setConsumer 메서드

{{LaunchQueue/setConsumer(consumer)}} 메서드 단계는 다음과 같다:

  1. [=assigned launch consumer=]를 |consumer|로 설정한다.
  2. [=process unconsumed launch params=] 단계를 실행한다.

{{LaunchParams}}는 이벤트가 아니라 {{LaunchQueue}}를 통해 document에 전달된다. 이는 launch 이벤트가 발생하는 것과 페이지 스크립트가 이벤트 리스너를 연결하는 것 사이의 race condition을 피하기 위한 것이다. 반대로 {{LaunchQueue}}는 {{LaunchConsumer}}가 설정될 때까지 큐에 추가된 모든 {{LaunchParams}}를 버퍼링한다.

접근성

이 명세에는 알려진 접근성 고려 사항이 없다.

개인정보 및 보안 고려 사항

[=manifest/client_mode=]가 [=client mode/focus-existing=]인 실행에 대해 [=launching a web application with handling=]할 때 구현은 주의를 기울여야 한다. 이러한 실행은 [=manifest/navigation scope=] 밖으로 URL을 누설해서는 안 된다. 이는 [=Document/processed manifest=] |manifest|가 주어졌을 때 양방향 모두에 적용된다: