MSEdgeExplainers

Web Install API (background document)

Authors: Diego Gonzalez

Participate

Table of contents

Status of this Document

This document is a starting point for engaging the community and standards bodies in developing collaborative solutions fit for standardization. As the solutions to problems described in this document progress along the standards-track, we will retain this document as an archive and use this section to keep the community up-to-date with the most current standards venue and content location of future work and discussions.

Introduction

The Web Install API provides a way to democratise and decentralise web application acquisition, by enabling "do-it-yourself" end users and developers to have control over the application discovery and distribution process. It provides the tools needed to allow a web site to install a web app.

Note: The Web Install API can be used to install a background web app, as explained in this document. It can also be used to install the current web app the user has open. Refer to this document for the current document functionality.

User-facing problem

End users don't have a standard, cross-platform way to acquire applications. Users are usually faced with inconsistent, hidden and proprietary mechanisms (custom protocols, stores) to acquire applications.

The Web Install API aims to fix this issue by creating an open, ergonomic, standardized, and cross-platform supported way of acquiring applications.

The feature aims to benefit users that:

Goals

Non-goals

Use cases

Installation of a background document is when the install API is invoked to install a web application that is different from the one currenlty loaded in UA's navigation context. This can be another web app in the same origin, or it can be in a different origin. This enables the following use case (among others):

Suite of Web Apps

An example would be a family of software applications, like a productivity or photography suite, where each application is accessed from a different web page. The developer is in control and can effectively advertise and control their applications, without having to redirect users to platform-specific proprietary repositories, which is what happens now.

SERP app install

Developers of Search Engines could use the API to include a way to directly install an origin that is an application. A new feature could be offered by search engines that could see them facilitating a frictionless way to acquire an app that a user is searching for. This could also aid discovery as a user might not be aware that a specific origin has an associated web application they could acquire.

Creation of online catalogs

Another potential use case for the API is related to the creation of online catalogs. A web site/app can list and install web apps. A unique aspect of this approach is that since the applications that are installed are web apps, this could enable a new set of true cross-device, cross-platform app repositories.

Install flow from an app repository

Proposed approach

As exemplified in the use case, an end user would be able to acquire an app from a suite or family of apps directly from one single page. We propose an open, ergonomic and standard way of acquiring web applications that enables end users to easily and safely install content on their devices. The proposed approach is a new API in the shape of promise-based method navigator.install([<install_url>[, <manifest_id>[, <params>]]]);. This promise will:

Signatures of the install method

The Web Install API consists of the extension to the navigator interface with an install method. This method has 2 different signatures that can be used to install background documents. The possible parameters it may receive are:

The manifest_id is the what to install, the install_url is the where to find it.

Unless the UA decides to gate this functionality behind installation, the behaviour between calling the install method on a tab or on an installed application should not differ.

One parameter navigator.install(<install_url>)

This signature can be used to install the current or a background document.

Note: If installing the current document the install_url points to itself.

Requirements:

Two parameters navigator.install(<install_url>, <manifest_id>)

This signature is intended to install background documents that don't necessarily have an explicit id value defined in the manifest file.

Requirements:

Note: according to the manifest spec, if there is no id member present, the processed string resolves to that of the start_url.

Note: We acknowledge that only around 4% (as of 2024) of web apps have defined ids in their manifest. We also know that ids are a crucial part to support to avoid situations of multiple same applications with no path to being updated. For apps that have an id defined in their manifest, the 1 param signature is useful. For apps that do not define the id field, they can be installed with the 2 parameter signature.

Steps to install the app

To install an application with the Web Install API, the process is as follows:

Background Document (1 param)

  1. User gesture activates code that calls the install(<install_url>) method.
  2. If the <install_url> is not the current document, the UA asks for permission to perform installations (it not previously granted). Else reject with AbortError.
  3. UA tries to fetch the background document present at the <install_url> and its manifest file.
  4. If fetched document has a manifest file, continue. Else reject with DataError.
  5. If manifest file linked to the fetched document has an id field defined, continue. Else reject with DataError.
  6. UA shows the acquisition/installation confirmation UX (prompt/dialog). If the user accepts, continue. Else reject with AbortError.
  7. Promise resolves with processed id of installed app and application follows the platform's post-install UX (adds to Dock/opens app/adds to mobile app drawer).

Background Document (2 param)

  1. User gesture activates code that calls the install(<install_url>, <manifest_id>) method.
  2. If the <install_url> is not the current document, the UA asks for permission to perform installations (if not previously granted). Else reject with AbortError.
  3. UA tries to fetch the background document present at the <install_url> and its manifest file.
  4. If fetched document has a manifest file, continue. Else reject with DataError.
  5. If <manifest_id> matches the processed id from the manifest of the fetched document, continue. Else reject with DataError.
  6. UA shows the acquisition confirmation UX (prompt/dialog). If the user accepts, continue. Else reject with AbortError.
  7. Promise resolves with processed id of installed app and application follows the platform's post-install UX (adds to Dock/opens app/adds to mobile app drawer).

Note: if an application is installed, the UA can choose to display UX to launch this application. The behaviour for this case is the same as if the app is not installed: The promise would resolve if the application opens, and rejects otherwise, with an AbortError.

There is an open PR to add this to the Manifest spec.

Sample code

/* tries to install a background document (app) */

const installApp = async (install_url, manifest_id) => {
    if (!navigator.install) return; // api not supported
    try {
        await navigator.install(install_url, manifest_id);
    } catch(err) {
        switch(err.name){
            case 'AbortError':
                /* Operation was aborted*/
                break;
            case 'DataError':
                /*issue with manifest file or id*/
                break;
        }
    }
};

Web application acquisition

As defined in the non-goals section, the concept of what "installation" mean can vary per browser and platform. These are the current behaviours on some browsers that can represent acquiring an app or "installing" it:

The main benefit of "installation" for the end user is that they can experience their web applications with better UX, including easier access through icons/app drawer/dock integrations and better/advanced platform integrations like sharing, notifications and others.

Alternatives considered

Declarative install

An alternate solution is to have a declarative way to install web apps. This can be achieved by allowing a new target type of _install to the HTML anchor tag. It can also use the rel attribute to hint to the UA that the url in the link should be installed.

<a href="https://airhorner.com" target="_install">honk</a>

<a href="https://airhorner.com" rel="install">honk</a>

Pros:

Cons:

We believe that a declarative implementation is a simple and effective solution, and a future entry point for the API. It should be considered for a v2 of the capability. For the current solution, we've decided to go with an imperative implementation since it allows more control over the overall installation UX:

PEPC version of the API

A version of Web Install that switches the permission prompt for a PEPC element is considered. While we remain enthusiastic about PEPC, we prefer to be cautious in its adoption and support until the feature ships and there is commitment to ship from more browser engines.

The main benefit from this alternative would be a clearer user intent on a UX surface controlled by the UA. This can mitigate spoofing and spamming with a dedicated DOM element to install web apps, and we are observing its development and considering it for future work.

Accessibility, Privacy, and Security Considerations

Same-origin policy

Note: any application installed with the install method will have to ask for permissions (if any) when the user opens and interacts with the application.

Preventing installation prompt spamming from third parties

A website that wants to install apps will require this new permission and will only be able to prompt the user for this in a period of time defined by the implementer. This will avoid spam from websites constantly asking for a permission to install apps, and will force websites to only prompt when there is a meaningful user intent to install apps.

The installation permission for an origin should be time-limited and expire after a period of time defined by the UA. After the permission expires the UA will prompt again for permission from the user.

New "installation" Permission for origin

A new "installation" permission is required if the content that is installing is not the current document. This permission is associated to the origin.

This results in a new integration with the Permissions API. The install API will make available the "installation" PermissionDescriptor as a new powerful feature. This would make it possible to know programmatically if install would be blocked.

/* example of querying for the state of an installation permission using the Permission API  */

const { state } = await navigator.permissions.query({
  name: "installation"
});
switch (state) {
  case "granted":
    navigator.install('https://productivitysuite.com');
    break;
  case "prompt":
    //shows the install button in the web UI
    showInstallButton();
    break;
  case "denied":
    redirectToApp();
    break;
}

Note: For background documents, a permission prompt will appear for origins that do not have the capability to install apps. Even if the installation is of a "background document" in the same origin, for consistency the origin must have the permission to install apps. The only cases that will not prompt for the permission are the installation of the "current document" or a "background document" in an origin that already has installation permissions.

Example:

The app located in https://productivitysuite.com displays in its homepage 3 buttons that aim to install 3 different apps (notice all apps are in the same origin):

The end user goes to the homepage in the https://productivitysuite.com's origin and clicks on the button to install the presentation application. As this is a background document (not the current document the user is interacting with) and the origin does not have permission to install apps, a permission prompt will appear. If this permission is granted for the origin, it can now install apps. After this permission prompt, the second prompt where the user confirms the installation appears.

The end user then tries to install the text processor, and since the origin has been granted the permission, then the UA will skip the permission prompt and skip directly to confirm installation with a prompt indicating that "productivity suite wants to install text processor". The installation permission is bound to an origin.

If the user were to deny the permission to install for the origin, they could browse to the app itself and once there, they could install the application. In this case, there wouldn't be any permission prompt required as this would now be a current document installation.

Rejecting promise with limited existing DOMException names

To protect the user's privacy, the API does not create any new error names for the DOMException, instead it uses common existing names: AbortError, DataError, NotAllowError and InvalidStateError. This makes it harder for the developer to know if an installation failed because of a mismatch in id values, a wrong manifest file URL or if there is no id defined in the manifest.

The promise will reject with an AbortError if:

The promise will reject with a DataError if:

The promise will reject with an NotAllowedError if:

The promise will reject with an InvalidStateError if:

Example: combining errors to mitigate private data leaking

A bad actor could try to determine if a user is logged into a dating website. This dating web site could provide install UX after a user is logged in (the dating website will likely have a page that serves a manifest, but it requires authentication). The bad actor could deceive the user to provide a user gesture allowing them to silently call navigator.install _intentionally+ with the wrong manifest id. Their hope would be to get an error indicating a manifest id mismatch, meaning that the user had access to retrieve the manifest (and was thus logged-in), or an error indicating that the manifest could not be retrieved (meaning that they weren't logged-in).

The benefit of the defined error handling for this feature is that the invoking call doesn't know if the DataError is because: i. manifest file was not accessible (user not logged-in) or ii. there was a mismatch between the id field and the provided 'wrong' parameter (user is logged-in).

Note: Using less verbose errors by grouping them into existing ones reduces leakage of information. This is the reason why we avoid using multiple errors or creating new ones, like a previously proposed ManifestIdMismatch and NoIdInManifest.

Gating capability behind installation

A UA may choose to gate the navigator.install capability behind a requirement that the installation origin itself is installed. This would serve as an additional trust signal from the user towards enabling the functionality.

Showing try-before-you-buy UX

The install UX can show a try-before-you-buy prompt. The UA may decide to show a prompt, some sort of rich-install dialog with additional information found in the manifest file, or load a preview of the app with the install confirmation. This is an implementation detail completely up to the UA.

Feature does not work on Incognito or private mode

The install capability should not work on incognito, and the promise should always reject if called in a private browsing context.

The user gesture, the new origin permission, the final installation confirmation (current default behaviour in the browser before installing an app) and the optional gated capability work together to minimize the risk of origins spamming the user for unrequested installations, give developers flexibility to control the distribution of their apps and end users the facility to discover and acquire content in a frictionless way.

Future Work

Stakeholder Feedback / Opposition / FAQs

Refer to this document for stakeholder feedback for the Web Install API.

References & acknowledgements

This explainer takes on a reimagination of a capability previously published by PEConn.

Throughout the development of this capability we've revceived a lot of feedback and support from lots of people. We've like to give a special thanks to Daniel Appelquist, Amanda Baker, Marcos Cáceres, Lu Huang, Kristin Lee, Daniel Murphy, Alex Russell and Howard Wolosky for their input.

Feedback

For suggestions and comments, please file an issue.

Web Install logo