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]], content restrictions from [[!CSP3]], and request processing from [[!FETCH]]. Explicit-manifest retrieval is also subject to [[!LOCAL-NETWORK-ACCESS]].

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 initiating document is the {{Document}} whose {{Navigator}} or <install> element initiated a Web Install request.

An entry-point kind is either imperative, for a request initiated by {{Navigator/install()}}, or declarative, for a request initiated by the <install> element's [=EventTarget/activation behavior=].

A application source is either current document or explicit manifest.

A manifest ID assertion is an optional URL supplied by the caller representing the expected processed manifest id.

Web Install model

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

document
The [=initiating document=].
entry point
The request's [=entry-point kind=].
application source
The request's [=application source=].
manifest URL
The manifest URL for an [=explicit manifest=] request; otherwise null.
manifest ID
The request's optional [=manifest ID assertion=].

A web install candidate 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 processing 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.
installation aborted
The request did not complete for a reason other than [=invalid install data=], including user dismissal, permission or policy denial, an invalid initiating document, an unsupported environment, a concurrent operation, navigation, destruction, or internal failure.

The outcome vocabulary is shared by both entry points. Each entry point maps an outcome to its corresponding 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. Let traversable be request's [=web install request/document=]'s [=top-level traversable=], if it has one; otherwise null.
  2. If traversable is not null and its [=top-level traversable/active web install request=] is request, set it to null.
  3. 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 initiating document=], return the result of [=finish a web install request=] with request and [=installation aborted=].
  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 aborted=].
  5. Set traversable's [=top-level traversable/active web install request=] to request.
  6. If request's [=web install request/application source=] is [=current document=], let candidateResult be the result of [=resolve a web install candidate from the current document=] for request. Otherwise, let candidateResult be the result of [=resolve a web install candidate from an explicit manifest=] for request.
  7. If candidateResult is a [=web install outcome=], return the result of [=finish a web install request=] with request and candidateResult.
  8. Let candidate be candidateResult.
  9. If document is no longer an [=eligible initiating 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 aborted=].
  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 aborted=].
  12. Let outcome be the result of [=perform a Web Install action=] with request and candidate.
  13. Return the result of [=finish a web install request=] with request and outcome.

For explicit-manifest requests, manifest retrieval and validation precede installation-permission and unsupported-profile handling. This ensures that invalid manifest data produces the same [=invalid install data=] outcome regardless of permission or profile state. Consequently, the target origin may observe a manifest request before installation permission is granted; the request omits credentials and referrer information.

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 [=initiating 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 aborted=].

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 initiating 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 a web install candidate

Resolving from the current document

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

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

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]].

Resolving from an explicit manifest

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

  1. Let manifestURL be request's [=web install request/manifest URL=].
  2. If manifestURL is not an HTTP(S) URL whose [=url/origin=] is potentially trustworthy, return [=invalid install data=].
  3. Let bodyBytes be the result of [=fetch an install manifest=] from manifestURL.
  4. If bodyBytes is failure, return [=invalid install data=].
  5. Let manifest be the result of [=process install manifest bytes=] with bodyBytes and manifestURL.
  6. If manifest is failure, return [=invalid install data=].
  7. Let identity be manifest's manifest id.
  8. If request's [=web install request/manifest ID=] is not null, and it is not equal to identity, return [=invalid install data=].
  9. 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=].
  10. Let candidate be a new [=web install candidate=] whose [=web install candidate/manifest=] is manifest, [=web install candidate/manifest URL=] is manifestURL, [=web install candidate/identity=] is identity, and [=web install candidate/start URL=] is manifest's start URL.
  11. Return the result of [=validate a web install candidate=] with request and candidate.

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 response be the result of the user agent internally retrieving manifestURL using GET.
  2. The retrieval MUST omit credentials and referrer information.
  3. The retrieval MUST NOT require a CORS check to succeed.
  4. The retrieval MUST comply with the manifest-src directive of the Content Security Policy applicable to the [=initiating document=].
  5. The retrieval MUST NOT be intercepted by a service worker.
  6. The retrieval MUST be subject to Local Network Access checks as if it were initiated by the [=initiating document=].
  7. If the retrieval uses cached data, it MUST use the [=network partition key=] associated with the [=initiating document=]'s [=relevant settings object=].
  8. The information observable by the [=initiating document=] as a result of this retrieval is limited as described in Privacy considerations.
  9. If the retrieval encounters a redirect, return failure.
  10. If response is a [=network error=], return failure.
  11. If response's [=response/status=] is not an [=ok status=], return failure.
  12. If response's Content-Type metadata is not a JSON MIME type, return failure.
  13. Let bodyBytes be the result of fully reading response's [=response/body=].
  14. If fully reading the body failed, return failure.
  15. The user agent MUST NOT expose response or bodyBytes to the [=initiating document=].
  16. Return bodyBytes.

Exact Fetch integration remains unresolved. The steps above describe the intended network and exposure behavior directly. Chromium implements them using an internal browser-process request. This draft does not yet define the request destination, or response tainting model. These details require coordination with Fetch before advancement.

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 referrer, cache behavior, and cross-origin requirements need the same integration review as the manifest retrieval.

Processing an install manifest

To process install manifest bytes given a byte sequence bodyBytes and URL manifestURL, run the following steps:

  1. 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.
  2. If processing the manifest failed, return failure.
  3. 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 application sources 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 candidate

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

  1. Let manifest be candidate's [=web install candidate/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/application source=] is [=explicit manifest=], and candidate's [=web install candidate/start URL=] is not [=same origin=] with candidate's [=web install candidate/manifest URL=], return [=invalid install data=].
  6. Return candidate.

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-manifest candidate 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 application sources.

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 [=initiating 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 [=entry-point kind/declarative=], return true.
  2. If request's [=web install request/application source=] is [=current document=], return true.
  3. Let descriptor be a new {{PermissionDescriptor}} whose {{PermissionDescriptor/name}} is web-app-installation.
  4. Let state be the result of [=request permission to use=] the powerful feature described by descriptor.
  5. If state is not {{PermissionState/"granted"}}, return false.
  6. 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 candidate=] candidate, 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 a Web Install action given a [=web install request=] request and [=web install candidate=] candidate, run the following steps:

  1. If the user agent determines that an installed web application with candidate's [=web install candidate/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 [=installation aborted=].
    3. Perform or initiate the accepted action, then return [=installation succeeded=]. The observable outcome MUST NOT reveal anything about the installation state of the candidate application.
  2. Let accepted be the result of [=confirm web installation=] with request and candidate.
  3. If accepted is false, return [=installation aborted=].
  4. Ask the user agent to install an installed web application whose application manifest is candidate's [=web install candidate/manifest=].
  5. If the user agent cannot complete installation, return [=installation aborted=].
  6. 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=] as the [=application source=]. 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 initiating 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 [=entry-point kind/declarative=], [=web install request/application source=] 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.
  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 completed, or processing of a user-approved action for an existing application completed.
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=] [=application source=].

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.

The user agent MUST ensure that the initiating document cannot distinguish whether the candidate application was newly installed or was already installed through:

User agents SHOULD minimize distinguishable timing, focus, visibility, user-interface, and application-launch behavior between those cases.

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.

Explicit-manifest retrieval does not require the target origin to opt in through CORS. The [=initiating document=] MUST NOT receive the response, manifest contents, or individual results of retrieval, parsing, identity, or metadata validation. It may observe only that processing produced [=invalid install data=], or that the candidate passed validation and processing continued to a user-mediated step.

[=Invalid install data=] MUST NOT distinguish among a network failure, an unsuccessful HTTP response, a redirect, a body-read failure, a manifest-processing failure, an identity failure, or a required-metadata validation failure. If processing continues, the [=initiating document=] may infer that the manifest was processed as a valid [=web install candidate=] and that any supplied [=manifest ID assertion=] matched.

Success means that installation completed, or that the user accepted an action for an existing application and the user agent completed processing that action. 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 MUST process invalid installation data consistently with supported profiles. After installation data has been validated, they MUST return [=installation aborted=]. They SHOULD avoid reliable timing differences that distinguish an unsupported profile from other reasons for [=installation aborted=].

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.