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.
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:
<install> is a
declarative, user-agent-rendered control that gives
the user agent a stronger signal of the user's intent.
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.
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>
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.
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.
A web install request is a struct with the following [=struct/items=]:
created, resolving target,
awaiting permission, awaiting confirmation,
installing, or complete.
A web install target is a struct with the following [=struct/items=]:
A web install outcome is one of:
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.
To determine whether a {{Document}} document is an eligible installing document, run the following steps:
web-app-installation [=policy-controlled feature=]
is disabled in document, return false.
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.
To resolve a current-document install target for a [=web install request=] request, run the following steps:
resolving target.
id member, return [=invalid install data=].
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]].
To resolve an explicit manifest install target for a [=web install request=] request, run the following steps:
resolving target.
id member, return [=invalid install data=].
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.
To fetch an install manifest from a URL manifestURL, run the following steps:
GET,
[=request/destination=] is manifest,
[=request/credentials mode=] is omit, and
[=request/redirect mode=] is error.
no-referrer.
cors.
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.
To process an install manifest response given a {{Response}} response and URL manifestURL, run the following steps:
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.
To validate a web install target given a [=web install request=] request and [=web install target=] target, run the following steps:
start_url member, return [=invalid install data=].
any, return [=invalid install data=].
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.
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.
To obtain web install permission for a [=web install request=] request, run the following steps:
awaiting permission.
web-app-installation.
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.
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.
To perform web installation given a [=web install request=] request and [=web install target=] target, run the following steps:
awaiting confirmation.
installing.
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.
<install> element
The HTML
<install> element
represents a user-agent-rendered control that lets the user initiate a
Web Install request.
manifest.
manifestid.
oninstallresult.ActivationBlockersMixin.
[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.
{{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:
The {{HTMLInstallElement}} insertion steps are:
The {{HTMLInstallElement}} removing steps are:
The default value of the
tabindex content attribute for
<install> is 0.
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.
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.
The <install>
element's [=EventTarget/activation behavior=] given an {{Event}}
event is:
manifestid attribute but no
manifest attribute,
[=queue an install result=] of invalid_data at
[=this=] and return.
manifest attribute:
invalid_data at
[=this=] and return.
manifestid attribute, set
manifestID to the result of the [=URL parser=] on
that attribute's value with no base URL.
invalid_data at
[=this=] and return.
created.
success at
[=this=] and abort these substeps.
invalid_data at
[=this=] and abort these substeps.
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.
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:
installresult,
{{InstallResultEvent/result}} is result,
{{Event/bubbles}} is true, {{Event/cancelable}} is false, and
{{Event/composed}} is false.
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 attributeThe {{InstallResultEvent/result}} getter returns [=this=].{{InstallResultEvent/[[result]]}}.
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.
This specification adds {{Navigator/install()}},
{{HTMLInstallElement}}, the
<install> element,
and the installresult event to HTML.
{{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.
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}}.
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]].
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:
<install>
element's dimensions, label, style, or state;
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.
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.
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.
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.
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 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.
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.