This specification defines Web Install, a user-mediated capability that lets a website ask the user agent to install a web application. It defines two entry points: the {{Navigator/install()}} method and the HTML <install> element. Both entry points resolve a target application manifest, validate the target application, obtain user consent, and invoke the same installation processing model.

This document is an incubation draft. It intentionally records open interoperability, privacy, and specification-ownership questions inline. Those issues are part of the draft and do not indicate implementer consensus.

Introduction

User agents can install web applications, but websites do not have a consistent way to offer that action. Existing installation affordances vary by user agent and platform, and script-driven installation mechanisms are generally limited to the application associated with the current document.

Web Install provides a common operation with two complementary entry points:

Either entry point can target the web application associated with the current document, or a web application identified by the URL of its manifest. A user agent always remains responsible for presenting trustworthy confirmation UI and for deciding how installation maps to the host platform.

Goals

Non-goals

Examples

A site can offer installation of the web application associated with its current document:

await navigator.install();
      
<install>
  <a href="/install-help">How to install this app</a>
</install>
      

A catalog can offer installation of an application identified by its manifest URL:

await navigator.install({
  manifest: "https://music.example/manifest.webmanifest",
  manifestId: "https://music.example/app"
});
      
<install
  manifest="https://music.example/manifest.webmanifest"
  manifestid="https://music.example/app">
  <a href="https://music.example/">Open Music Example</a>
</install>
      

Dependencies

This specification extends [[!HTML]] with {{Navigator/install()}} and the <install> element. It reuses manifest concepts and processing from [[!appmanifest]], permission concepts from [[!permissions]], policy control from [[!permissions-policy]], and request processing from [[!FETCH]].

The <install> element provisionally reuses activation blockers and their rendering protections from [[!CAPABILITY-ELEMENTS]].

Capability Elements ownership is unsettled. The reusable activation-blocker algorithms currently live inside the Geolocation Element draft, while Chromium's <install> implementation uses an older InPagePermissionMixin that also exposes permission-state and prompt event members that <install> does not use. This draft references only the narrower ActivationBlockersMixin. Before advancement, that mixin should have a maintained, independently referenceable specification home, or the required behavior must be defined here.

[[MANIFEST-INCUBATIONS]] is an informative dependency at present. If this specification later reuses an incubating manifest member or algorithm, that dependency must be made normative at the exact point of use.

Terminology

A user agent that does not support web application installation on a particular platform or profile is not required to expose these entry points. Platform support is implementation status, not a desktop-specific conformance requirement. If an entry point is exposed, its observable behavior MUST conform to this specification.

An installing document is the {{Document}} whose {{Navigator}} or <install> element initiated a Web Install request.

An entry-point kind is either navigator or install element.

A target mode is either current document or explicit manifest.

A manifest ID assertion is an optional URL supplied by the caller for comparison with the processed manifest id.

Web Install model

A web install request is a struct with the following [=struct/items=]:

document
The [=installing document=].
entry point
The request's [=entry-point kind=].
target mode
The request's [=target mode=].
manifest URL
Null for [=current document=] requests; otherwise the parsed manifest URL.
manifest ID
The request's optional [=manifest ID assertion=].
source element
The initiating {{HTMLInstallElement}} for an [=install element=] request; otherwise null.
state
One of created, resolving target, awaiting permission, awaiting confirmation, installing, or complete.

A web install target is a struct with the following [=struct/items=]:

manifest
A processed manifest.
manifest URL
The URL from which the manifest was obtained.
identity
The processed manifest id.
start URL
The processed start URL.

A web install outcome is one of:

installation succeeded
The user agent completed installation, or completed a user-approved action for an already-installed application.
invalid install data
The supplied URL, manifest, identity assertion, or required installation metadata was invalid or unavailable.
user aborted installation
The user dismissed or declined user-agent installation UI.
installation not allowed
Permission, policy, security context, or anti-abuse processing disallowed the request.
installation state invalid
The installing document or its browsing context could not initiate or continue the operation.
installation not supported
The user agent or host platform could not install the target in the current profile or environment.
installation operation aborted
The request ended for another reason, including a concurrent operation, navigation, destruction, or internal failure.

The internal outcome vocabulary is intentionally more detailed than either public result surface. Each entry point maps it to a privacy-limited public result.

Every [=top-level traversable=] has an active web install request, which is null or a [=web install request=] and is initially null.

The public outcome taxonomy remains unresolved. Current proposal text and Chromium expose success, invalid data, and a broad aborted result. Developer feedback asks for user cancellation to be distinguishable from internal failure, while privacy review prefers exposing less cross-origin information. The algorithms below provisionally retain the coarse current behavior and mark the entry-point mappings explicitly.

Shared Web Install processing

To finish a web install request given a [=web install request=] request and [=web install outcome=] outcome, run the following steps:

  1. Set request's [=web install request/state=] to complete.
  2. Let traversable be request's [=web install request/document=]'s [=top-level traversable=], if it has one; otherwise null.
  3. If traversable is not null and its [=top-level traversable/active web install request=] is request, set it to null.
  4. Return outcome.

To run a web install request given a [=web install request=] request, run the following steps:

  1. Let document be request's [=web install request/document=].
  2. If document is not an [=eligible installing document=], return the result of [=finish a web install request=] with request and [=installation state invalid=].
  3. Let traversable be document's [=top-level traversable=].
  4. If traversable's [=top-level traversable/active web install request=] is not null, return the result of [=finish a web install request=] with request and [=installation operation aborted=].
  5. Set traversable's [=top-level traversable/active web install request=] to request.
  6. If request's [=web install request/target mode=] is [=current document=], let targetResult be the result of [=resolve a current-document install target=] for request. Otherwise, let targetResult be the result of [=resolve an explicit manifest install target=] for request.
  7. If targetResult is a [=web install outcome=], return the result of [=finish a web install request=] with request and targetResult.
  8. Let target be targetResult.
  9. If document is no longer an [=eligible installing document=]:
    1. If traversable's [=top-level traversable/active web install request=] is request, set it to null.
    2. Return the result of [=finish a web install request=] with request and [=installation state invalid=].
  10. Let permissionGranted be the result of [=obtain web install permission=] for request.
  11. If permissionGranted is false, return the result of [=finish a web install request=] with request and [=installation not allowed=].
  12. Let outcome be the result of [=perform web installation=] with request and target.
  13. Return the result of [=finish a web install request=] with request and outcome.

The network request currently precedes permission. The algorithm matches Chromium by fetching and validating an explicit manifest before checking installation permission and profile support. This helps coarsen private-profile failures behind data errors, but it lets a target origin observe a request before the installing user grants permission. The final ordering requires privacy and implementer review.

Concurrency scope is provisional. Chromium has document-service and WebContents-level guards, while the capability-element implementation also caps registered install elements. This draft defines one active operation per top-level traversable because that is the visible dialog scope. Per-profile, per-origin, cooldown, and quota controls remain user-agent anti-abuse policy.

If the [=installing document=] navigates, is destroyed, ceases to be fully active, or ceases to be the active document of its top-level traversable before processing completes, the user agent MUST cancel associated user interface and complete the request with [=installation state invalid=].

Navigation and BFCache lifetime need exact HTML integration. Chromium cancels on every cross-document navigation, including same-origin navigation and BFCache transitions. This draft preserves the active-document invariant but does not yet define whether a BFCache restoration can resume an operation.

Common request preconditions

To determine whether a {{Document}} document is an eligible installing document, run the following steps:

  1. If document is not [=Document/fully active=], return false.
  2. If document is not the active document of a [=top-level traversable=], return false.
  3. If document is a fenced frame document, return false.
  4. If document's active sandboxing flag set is not empty, return false.
  5. If the web-app-installation [=policy-controlled feature=] is disabled in document, return false.
  6. Return true.

Frame and sandbox scope needs multi-implementer review. Chromium currently requires the primary top-level document and rejects every sandboxed context. Proposal text leaves same-origin frames and a future sandbox opt-in open. This draft preserves Chromium's restrictive behavior until a safe delegation model is designed.

Secure-context exposure is inconsistent. Chromium's IDL lacks [SecureContext], but browser bindings and manifest URL checks effectively restrict use to trustworthy HTTP(S) contexts. This draft checks trustworthy contexts in processing rather than changing interface exposure. The final API should decide whether secure-context gating belongs in WebIDL.

Resolving an installation target

Current-document target

To resolve a current-document install target for a [=web install request=] request, run the following steps:

  1. Set request's [=web install request/state=] to resolving target.
  2. Let document be request's [=web install request/document=].
  3. Let manifest be document's processed manifest.
  4. If manifest is null, return [=invalid install data=].
  5. Let manifestURL be manifest's manifest URL.
  6. Let identity be manifest's manifest id.
  7. If the manifest did not explicitly contain an id member, return [=invalid install data=].
  8. If identity is not [=same origin=] with document's [=Document/URL=], return [=invalid install data=].
  9. Let target be a new [=web install target=] whose [=web install target/manifest=] is manifest, [=web install target/manifest URL=] is manifestURL, [=web install target/identity=] is identity, and [=web install target/start URL=] is manifest's start URL.
  10. Return the result of [=validate a web install target=] with request and target.

Manifest readiness is underspecified. The no-argument API can be activated before the document's linked manifest finishes loading. Chromium reads the current document's processed manifest without waiting for a service worker. The final algorithm must define whether it waits for an in-flight manifest, fails immediately, or reuses the installation-prompt discovery processing currently incubated in [[MANIFEST-INCUBATIONS]].

Explicit manifest target

To resolve an explicit manifest install target for a [=web install request=] request, run the following steps:

  1. Set request's [=web install request/state=] to resolving target.
  2. Let manifestURL be request's [=web install request/manifest URL=].
  3. If manifestURL is not an HTTP(S) URL whose [=url/origin=] is not potentially trustworthy, return [=invalid install data=].
  4. Let response be the result of [=fetch an install manifest=] from manifestURL.
  5. If response is failure, return [=invalid install data=].
  6. Let manifest be the result of [=process an install manifest response=] with response and manifestURL.
  7. If manifest is failure, return [=invalid install data=].
  8. Let identity be manifest's manifest id.
  9. If request's [=web install request/manifest ID=] is not null, and it is not equal to identity, return [=invalid install data=].
  10. If request's [=web install request/manifest ID=] is null, and the fetched manifest did not explicitly contain an id member, return [=invalid install data=].
  11. Let target be a new [=web install target=] whose [=web install target/manifest=] is manifest, [=web install target/manifest URL=] is manifestURL, [=web install target/identity=] is identity, and [=web install target/start URL=] is manifest's start URL.
  12. Return the result of [=validate a web install target=] with request and target.

Relative manifest ID assertions are unresolved. Proposal examples use absolute URLs and Chromium currently rejects relative assertions. HTML URL-valued attributes normally resolve relative values. This draft provisionally requires an absolute manifest ID assertion for both entry points so their comparison semantics remain aligned.

Fetching and processing a manifest

Fetching an install manifest

To fetch an install manifest from a URL manifestURL, run the following steps:

  1. Let request be a new {{Request}} whose [=request/URL=] is manifestURL, [=request/method=] is GET, [=request/destination=] is manifest, [=request/credentials mode=] is omit, and [=request/redirect mode=] is error.
  2. Set request's [=request/referrer=] to no-referrer.
  3. Set request's [=request/mode=] to cors.
  4. Let response be the result of [=fetching=] request.
  5. If response is a [=network error=], return failure.
  6. If response's [=response/status=] is not an [=ok status=], return failure.
  7. Return response.

The Fetch mode is a blocking design question. Chromium performs a browser-process, credentialless fetch without explicitly setting a Fetch mode. A cors request requires the target server to opt in, which may conflict with the proposal's goal that any catalog can offer an application. A no-cors request normally yields an opaque response that cannot be parsed by web content. The provisional cors step above must be replaced by an agreed Fetch integration before this algorithm is implementable across engines.

Redirect and MIME behavior need agreement. Chromium rejects every redirect and parses successful bodies without checking MIME type. The explainers do not fully specify either behavior. This draft provisionally rejects redirects and does not add a MIME requirement, matching current implementation.

A user agent MAY impose implementation-defined response-size, retry, cache, and resource limits. Such limits MUST NOT expose installed state or private-browsing state.

If the user agent fetches icons or other target resources before the user accepts installation, those requests MUST omit credentials and caller-specific authentication state. Their Fetch mode, referrer, cache behavior, and cross-origin requirements need the same explicit review as the manifest request.

Processing an install manifest response

To process an install manifest response given a {{Response}} response and URL manifestURL, run the following steps:

  1. Let bodyBytes be the result of fully reading response's [=response/body=].
  2. If fully reading the body failed, return failure.
  3. Let manifest be the result of running process a manifest with bodyBytes, manifestURL as both the manifest URL and document URL, and null as the client.
  4. If processing the manifest failed, return failure.
  5. Return manifest.

This is an explicit Web App Manifest integration. Chromium passes the manifest URL as both the document URL and manifest URL so relative members resolve against the manifest. Web App Manifest permits processing without a client but recommends evidence that a document was historically associated with the manifest. Direct installation deliberately has no loaded target document. WebApps WG should decide whether this call is a supported parameterization or a monkeypatch requiring a new processing entry point.

Source-member presence must survive processing. Both target modes need to distinguish an explicitly declared id or start_url from Web App Manifest's computed defaults. The current processed-manifest model does not clearly export those presence bits. This specification needs either an enriched processing result or an explicit pre-processing step that records them.

Validating a web install target

To validate a web install target given a [=web install request=] request and [=web install target=] target, run the following steps:

  1. Let manifest be target's [=web install target/manifest=].
  2. If the source manifest did not explicitly contain a valid start_url member, return [=invalid install data=].
  3. If neither name nor short name is a non-empty string, return [=invalid install data=].
  4. If manifest's icons list does not contain at least one valid image resource whose purpose contains any, return [=invalid install data=].
  5. If request's [=web install request/target mode=] is [=explicit manifest=], and target's [=web install target/start URL=] is not [=same origin=] with target's [=web install target/manifest URL=], return [=invalid install data=].
  6. Return target.

Installation eligibility differs between current paths. Chromium's current-document path requires a suitable any icon and ignores display mode, while its explicit manifest path initially checks only for a non-empty icon list before downloading icons. The common requirements above are provisional and need alignment with Web App Manifest's deliberately broad definition of an installable web application.

Missing-icon failure classification differs. Chromium can classify an explicit target with no icons as a broad abort while other malformed manifest data becomes DataError. The provisional validation steps treat missing required icon data as [=invalid install data=] for both target modes.

Related applications and overlapping scopes are unresolved. The draft does not hand installation to related_applications, nor does it reject a target because another installed app controls overlapping scope. Both behaviors affect application identity and should be decided with Web App Manifest rather than inherited from one product's installability heuristics.

This specification does not require a service worker, offline capability, or a particular display mode. A user agent MAY use those signals when deciding how to present or integrate an application, but it MUST NOT report their absence as [=invalid install data=] unless a future version of this specification makes them requirements.

Permission and user consent

The web-app-installation powerful feature

This specification defines a [=powerful feature=] identified by the permission name web-app-installation permission. Its [=permission state=] is associated with the [=installing document=]'s [=origin=].

This specification also defines a [=policy-controlled feature=] named web-app-installation policy-controlled feature with a default allowlist of self.

Permission scope and applicability conflict across evidence. Some proposal text describes the permission as cross-origin-only, other proposal text requires it for every explicitly targeted manifest, and Chromium currently checks it for every {{Navigator/install()}} explicit-manifest request. The algorithm below provisionally follows Chromium: the permission is required for the imperative explicit-manifest form, regardless of whether caller and target are same-origin.

The default permission state differs between products. Chromium can prompt for this permission, while current Edge product policy can treat the initial state as allowed. The web specification should define the permission's semantics and leave enterprise or product grants to user-agent policy without changing the public result taxonomy.

Obtaining install permission

To obtain web install permission for a [=web install request=] request, run the following steps:

  1. If request's [=web install request/entry point=] is [=install element=], return true.
  2. If request's [=web install request/target mode=] is [=current document=], return true.
  3. Set request's [=web install request/state=] to awaiting permission.
  4. Let descriptor be a new {{PermissionDescriptor}} whose {{PermissionDescriptor/name}} is web-app-installation.
  5. Let state be the result of [=request permission to use=] the powerful feature described by descriptor.
  6. If state is not {{PermissionState/"granted"}}, return false.
  7. Return true.

Trusted element activation currently bypasses stored denial. Chromium treats a validated click on <install> as sufficient intent and skips the persistent permission check, even if the stored state is denied. This draft provisionally matches that behavior. The final design must decide whether the element bypasses only a prompt, or also an explicit prior denial.

Installation confirmation

To confirm web installation given a [=web install request=] request and [=web install target=] target, the user agent MUST present user-controlled UI that:

The exact layout, wording, platform integration, and number of UI surfaces are user-agent-defined.

Return true if the user accepts the operation. Otherwise, return false.

Performing installation

To perform web installation given a [=web install request=] request and [=web install target=] target, run the following steps:

  1. Set request's [=web install request/state=] to awaiting confirmation.
  2. If the user agent determines that an installed web application with target's [=web install target/identity=] already exists:
    1. Present user-controlled UI that offers a reasonable action for the existing application, such as launching it.
    2. If the user declines or dismisses that UI, return [=user aborted installation=].
    3. Perform the accepted action and return [=installation succeeded=].
  3. Let accepted be the result of [=confirm web installation=] with request and target.
  4. If accepted is false, return [=user aborted installation=].
  5. Set request's [=web install request/state=] to installing.
  6. Ask the user agent to install an installed web application whose application manifest is target's [=web install target/manifest=].
  7. If the user agent cannot complete installation, return [=installation operation aborted=].
  8. Return [=installation succeeded=].

A user agent MAY launch the application after installation and MAY integrate it with host-platform surfaces. Such behavior does not change the target application's origin, permissions, or storage partition.

The success point is not settled. Chromium reports success after installation, or after the user accepts a launch action for an already-installed app. A race can cause success even if the installed app disappears before a queued launch. The final specification must decide whether success means completed installation, accepted user intent, or a broader "reasonable action" by the user agent.

Display mode and OS integration are user-agent policy. Chromium currently forces a standalone display mode, requests OS integration, and launches new installations. Those choices are not stated as interoperable requirements in the proposal and are not required by this draft.

The <install> element

The HTML <install> element represents a user-agent-rendered control that lets the user initiate a Web Install request.

Categories:
[=Flow content=].
[=Phrasing content=].
[=Interactive content=].
[=Palpable content=].
Contexts in which this element can be used:
Where [=phrasing content=] is expected.
Content model:
[=Phrasing content=].
Content attributes:
Global attributes.
manifest.
manifestid.
oninstallresult.
Attributes defined by ActivationBlockersMixin.
Accessibility considerations:
For authors: none.
For implementers: see [[HTML-AAM]] and Accessibility considerations.
DOM interface:
{{HTMLInstallElement}}.
[Exposed=Window]
interface HTMLInstallElement : HTMLElement {
  [HTMLConstructor] constructor();

  [CEReactions] attribute USVString? manifest;
  [CEReactions] attribute USVString? manifestId;
  attribute EventHandler oninstallresult;
};

interface mixin ActivationBlockersMixin {
  readonly attribute boolean isValid;
  readonly attribute DOMString invalidReason;
  attribute EventHandler onvalidationstatuschange;
};
HTMLInstallElement includes ActivationBlockersMixin;

enum InstallResult {
  "success",
  "aborted",
  "invalid_data"
};

[Exposed=Window]
interface InstallResultEvent : Event {
  constructor(DOMString type,
              optional InstallResultEventInit eventInitDict = {});
  readonly attribute InstallResult result;
};

dictionary InstallResultEventInit : EventInit {
  InstallResult result = "aborted";
};
      

The event initializer default differs from Chromium. Chromium's dictionary has no default and a script-constructed event can have an empty internal result. This draft defaults to aborted so every conforming event has a valid enum value. Alternatively, the member could be required.

The content model is provisional. Capability-element drafts vary between flow and phrasing fallback content. A fallback link is a key use case, but nesting interactive content inside a supported interactive element can create activation and accessibility ambiguity. This draft provisionally allows phrasing content and suppresses author children when the native control renders.

The <install> element is not form-associated. It does not define a disabled content attribute; activation blockers represent whether the native control can currently be activated.

An author-controlled disabled state may be useful. A standard disabled attribute could let authors remove the control from interaction and accessibility navigation while application state changes. The current proposal and Chromium implementation do not expose one.

The {{ActivationBlockersMixin/isValid}}, {{ActivationBlockersMixin/invalidReason}}, and {{ActivationBlockersMixin/onvalidationstatuschange}} members behave as defined by [[CAPABILITY-ELEMENTS]]. The copy of their WebIDL above is a temporary dependency bridge and does not redefine their processing.

Internal state and lifecycle

{{HTMLInstallElement}} uses the internal state defined by ActivationBlockersMixin. It does not store a persistent installation result; each result belongs to one {{InstallResultEvent}}.

The {{HTMLInstallElement/constructor()}} steps are:

  1. Run ActivationBlockersMixin's initialization steps.

The {{HTMLInstallElement}} insertion steps are:

  1. Run ActivationBlockersMixin's insertion steps.

The {{HTMLInstallElement}} removing steps are:

  1. Run ActivationBlockersMixin's removing steps.

The default value of the tabindex content attribute for <install> is 0.

Attributes

The manifest content attribute gives the URL of the target application manifest. The {{HTMLInstallElement/manifest}} IDL attribute reflects the manifest content attribute.

The manifestid content attribute gives an optional absolute [=manifest ID assertion=]. The {{HTMLInstallElement/manifestId}} IDL attribute reflects the manifestid content attribute.

Omitting both attributes selects [=current document=] mode. Supplying manifestid without manifest is invalid install data.

URL reflection needs alignment with HTML conventions. Chromium exposes nullable URL-reflecting attributes and currently requires an absolute manifest ID. The final IDL should decide whether missing attributes return null or the empty string, and whether relative values follow normal HTML URL reflection.

Lifecycle and rendering

The <install> element uses ActivationBlockersMixin's initialization, insertion, removing, visibility, occlusion, and style-validation processing.

A supporting user agent MUST render a user-agent-controlled control whose accessible purpose is installation. The user agent controls the control's text and iconography and MUST ensure the control remains distinguishable, legible, focusable, and activatable using keyboard and platform accessibility input.

While the native control is rendered, author children are fallback content and are not rendered. A user agent that does not support <install> treats it as an {{HTMLUnknownElement}}, allowing its children to provide ordinary HTML fallback.

The native control's rendering, intrinsic dimensions, accessible properties, DOM-observable state, and supported pseudo-classes MUST NOT vary based on whether the target application is installed.

Installed-state rendering conflicts across evidence. The explainer permits an optional "Launch" appearance. Chromium contains that appearance but disables it because width differences reveal installed state. This draft provisionally requires installed-state-independent rendering. Any future launch appearance needs a complete side-channel design covering layout, CSS, canvas, timing, and accessibility APIs.

A user agent MAY apply anti-clickjacking and anti-spam limits in addition to the capability-element requirements. Numeric cooldowns, per-page control limits, and reputation checks are user-agent policy and are not interoperable state exposed by this specification.

Activation behavior

The <install> element's [=EventTarget/activation behavior=] given an {{Event}} event is:

  1. If event.{{Event/isTrusted}} is false, return.
  2. If [=this=].{{ActivationBlockersMixin/isValid}} is false, return.
  3. Let document be [=this=]'s node document.
  4. If document is not an [=eligible installing document=], return.
  5. Let manifestURL be null.
  6. Let manifestID be null.
  7. If [=this=] has a manifestid attribute but no manifest attribute, [=queue an install result=] of invalid_data at [=this=] and return.
  8. If [=this=] has a manifest attribute:
    1. Set manifestURL to the result of the [=URL parser=] on the attribute's value with document's [=Document/URL=] as base.
    2. If manifestURL is failure, [=queue an install result=] of invalid_data at [=this=] and return.
    3. If [=this=] has a manifestid attribute, set manifestID to the result of the [=URL parser=] on that attribute's value with no base URL.
    4. If manifestID is failure, [=queue an install result=] of invalid_data at [=this=] and return.
  9. Let request be a new [=web install request=] whose [=web install request/document=] is document, [=web install request/entry point=] is [=install element=], [=web install request/target mode=] is [=current document=] if manifestURL is null and [=explicit manifest=] otherwise, [=web install request/manifest URL=] is manifestURL, [=web install request/manifest ID=] is manifestID, [=web install request/source element=] is [=this=], and [=web install request/state=] is created.
  10. Run the following steps [=in parallel=]:
    1. Let outcome be the result of [=run a web install request=] with request.
    2. If outcome is [=installation succeeded=], [=queue an install result=] of success at [=this=] and abort these substeps.
    3. If outcome is [=invalid install data=], [=queue an install result=] of invalid_data at [=this=] and abort these substeps.
    4. [=Queue an install result=] of aborted at [=this=].

Blocked activation currently has no result event. Chromium silently ignores untrusted, visually invalid, or unregistered activation. That is consistent with capability-element validation, but authors may expect every user activation to produce installresult. The final text should preserve silence or define a separate pre-activation signal deliberately.

The values used by an in-flight request are snapshotted during activation. Later mutations to manifest or manifestid affect only later activations.

Detached-element result delivery is unresolved. The implementation snapshots options, but focused tests do not define whether removing or reinserting the element cancels an in-flight installation or whether a queued result fires on a detached element.

The installresult event

Value Meaning
success Installation, or a user-approved action for an existing application, succeeded.
invalid_data The manifest URL, manifest contents, identity assertion, or required installation metadata was invalid or unavailable.
aborted The operation did not complete for a non-data reason.

An {{InstallResultEvent}} has a [[\result]] internal slot. Its constructor initializes {{InstallResultEvent/[[result]]}} to the value of {{InstallResultEventInit/result}} in eventInitDict.

To queue an install result of {{InstallResult}} result at an {{HTMLInstallElement}} element, [=queue an element task=] on the [=user interaction task source=] with element to run these steps:

  1. Let event be a new {{InstallResultEvent}} whose {{Event/type}} is installresult, {{InstallResultEvent/result}} is result, {{Event/bubbles}} is true, {{Event/cancelable}} is false, and {{Event/composed}} is false.
  2. [=Dispatch=] event at element.

The following are the event handlers and their corresponding event handler event types that user agents MUST support:

Event handler Event handler event type
oninstallresult installresult

Event ordering and flags need WPT coverage. Chromium queues the event on the user interaction task source, bubbles it, and leaves it non-cancelable and non-composed. Tests do not yet pin ordering relative to dialog closure, application launch, navigation, or promise microtasks, nor the composed/cancelable flags.

result attribute

The {{InstallResultEvent/result}} getter returns [=this=].{{InstallResultEvent/[[result]]}}.

Host-specification integrations

Web App Manifest

This specification supplies the manifest URL as both manifest URL and document URL, and null as client, when it invokes process a manifest for an [=explicit manifest=] target.

This specification additionally requires an explicitly declared id when the caller did not supply a [=manifest ID assertion=], even though Web App Manifest normally computes a fallback identity.

WHATWG HTML

This specification adds {{Navigator/install()}}, {{HTMLInstallElement}}, the <install> element, and the installresult event to HTML.

Capability Elements

{{HTMLInstallElement}} includes ActivationBlockersMixin and invokes that mixin's initialization, insertion, removal, and activation-validation processing. It does not observe or expose a capability permission state through that mixin.

Permissions and Permissions Policy

This specification registers the web-app-installation powerful feature and the same-named policy-controlled feature. The Permissions API descriptor requires no members beyond {{PermissionDescriptor/name}}.

Launch Handler

If an accepted action for an already-installed application launches it, the user agent SHOULD apply the application's launch handling behavior as defined by [[LAUNCH-HANDLER]].

Privacy considerations

Web Install creates potential cross-origin information channels because an installing origin can name another application's manifest. User agents MUST NOT expose the target application's installed state directly through this API.

In particular, installed state MUST NOT be distinguishable through:

Manifest and icon requests for explicit targets MUST omit credentials and MUST NOT disclose cookies or other caller-specific authentication state to the target origin.

Success means that the user accepted an installation or an action for an existing application. It does not reveal which case occurred. A later uninstall is not observable through this API.

User agents that do not support installation in private, guest, managed, or other profile types SHOULD make that failure indistinguishable from other non-data failures. They SHOULD avoid reliable timing differences that reveal private-browsing state.

Some side channels remain outside the explicit result. Focus and blur events, the presence and duration of browser UI, network timing, and application launch can reveal partial information. The final privacy design must state which channels are mitigated normatively and which are acknowledged limitations.

Security considerations

The user agent is the authority that validates installation input, attributes the request, obtains user consent, and performs privileged platform integration. Renderer- or script-side checks do not replace authoritative validation before installation.

The confirmation UI MUST clearly distinguish the installing origin from the target application's origin. Target names and icons MUST be treated as untrusted presentation data and displayed so that they cannot obscure or impersonate origin information.

The <install> element's trusted rendering, visibility, occlusion, style validation, focus behavior, and activation cooldown protect its click as a signal of user intent. A user agent MAY apply stricter clickjacking, reputation, rate-limit, or prompt-abuse defenses.

Direct manifest installation does not execute script from the target application before consent. Manifest and resource parsing MUST be bounded and MUST treat every URL and metadata field as untrusted.

Target authority for CDN-hosted manifests remains open. This draft requires the processed start URL to be same-origin with the manifest URL. That excludes common CDN deployment patterns but avoids a manifest on one origin claiming an unrelated application on another. Supporting CDN manifests requires a non-gatekeeping proof of authority.

Accessibility considerations

A supporting <install> element maps to the accessibility semantics of a button. Its accessible name comes from user-agent-controlled localized content, not from untrusted author fallback content.

The element MUST be reachable and activatable using sequential focus, keyboard activation, and platform accessibility input. Visual validation and anti-clickjacking checks MUST work with forced colors, high contrast, zoom, text scaling, and other user preferences.

Blocked-state accessibility is underspecified. Chromium exposes a button role and default focusability, but focused tests do not define how temporary visual blockers or policy failure map to disabled state in the accessibility tree. The native control must not appear actionable to assistive technology when activation is impossible.

Authors SHOULD provide accessible status feedback when reacting to installresult; dispatching the event does not itself create an assistive-technology announcement.

Internationalization considerations

The user agent uses the lang and dir attributes applicable to <install> to localize and order the native control's own label. It uses Web App Manifest's localization processing when displaying target application metadata.

User agents MUST isolate bidirectional app names, URLs, and origins from surrounding UI and MUST safely elide or wrap long localized strings without hiding security-relevant origin information.

Control and manifest locale selection can diverge. Chromium localizes the control from the inherited document language, while target metadata selection can follow user-agent locale preferences. The final specification should define this as intentional or choose one locale negotiation model.

Testing considerations

Interoperable tests should cover both entry points against the same target and outcome matrix. In addition to WebIDL and feature-detection tests, the suite should cover:

Existing Chromium tests cover many product behaviors, but they do not by themselves establish interoperability requirements. Every normative requirement in this draft should map to a Web Platform Test or to a documented reason that cross-browser automation is not possible.

Open issues index

Open issues are recorded inline beside the affected definitions and algorithms. Before advancement, editors should ensure each inline issue is linked to a public repository issue and that its resolution updates the associated Web Platform Tests.

Acknowledgements

The editors thank Mike West, Diego González, Daniel Murphy, Alex Russell, Arthur Sonzogni, and participants in WICG, WebApps WG, TAG, browser standards-position discussions, and early developer trials for feedback that informed this draft.