From 6fa3d47c6e99550742f7260b3e9160871e9d28b6 Mon Sep 17 00:00:00 2001 From: Marcos Caceres Date: Mon, 7 Nov 2016 20:39:39 +1100 Subject: [PATCH 1/2] Feat(events): add BeforeInstallPromptEvent (closes #417) --- index.html | 140 ++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 129 insertions(+), 11 deletions(-) diff --git a/index.html b/index.html index 139e6b10c..aa9e8e2d6 100644 --- a/index.html +++ b/index.html @@ -435,18 +435,17 @@

Queue a task on the application life-cycle task source to do the following:
    -
  1. Let event be a newly constructed - BeforeInstallPromptEvent named - "beforeinstallprompt", which is cancelable. +
  2. Let target be at Window object of the + top-level browsing context.
  3. -
  4. - Fire event at the Window object of the - top-level browsing context. +
  5. Let showPrompt be the result of firing an + event (event) named "beforeinstallprompt", using + BeforeInstallPromptEvent at target with its + cancelable attribute initialized to true.
  6. -
  7. If event's canceled flag is not set, then, - in parallel, request to present an install prompt - with event. +
  8. If showPrompt is true, then, in parallel, + request to present an install prompt with + event.
@@ -526,6 +525,125 @@

DOM events fired by this specification use the application life-cycle task source.

+
+

+ BeforeInstallPromptEvent Interface +

+
+          [Constructor]
+          interface BeforeInstallPromptEvent : Event {
+              Promise<PromptResponseObject> prompt();
+          };
+          dictionary PromptResponseObject {
+            AppBannerPromptOutcome userChoice;
+          };
+        
+

+ The BeforeInstallPromptEvent is dispatched prior to + activating an automated install prompt, allowing a developer + to prevent the default action for an install prompt. +

+

+ Thus, the default action of the BeforeInstallPromptEvent is to + present an automated install + prompt to the end-user. Canceling the default action (via + .preventDefault()) prevents the user agent from + presenting an automated + install prompt until a later time (see + BeforeInstallPromptEvent.prompt() method). +

+

+ An instance of a BeforeInstallPromptEvent has the following + internal slots: +

+
+
+ [[\didPrompt]] +
+
+ A boolean, initially false. Represents if this event + was used to present an install prompt to the end-user. +
+
+ [[\userResponsePromise]] +
+
+ A promise that represents the outcome of presenting an install + prompt. +
+
+
+

+ prompt() method +

+

+ The prompt method, when called, runs the following + steps: +

+
    +
  1. Let p be a newly created promise. +
  2. +
  3. Resolve p with + this.[[\userResponsePromise]]. +
  4. +
  5. If this.[[\userResponsePromise]] is pending: +
      +
    1. If this event's isTrusted attribute is + false, reject + this.[[\userResponsePromise]] with + NotAllowedError, optionally informing the developer + that untrusted events can't call prompt(). +
    2. +
    3. Else if this.[[\didPrompt]] is + false, then, in parallel, request to + present an install prompt with this event. Wait, possibly + indefinitely, for the end-user to make a choice. +
    4. +
    +
  6. +
  7. Return p. +
  8. +
+

+ To request to present an install prompt with + BeforeInstallPromptEvent event: +

+
    +
  1. + Present an install prompt and let outcome be + the result. +
  2. +
  3. Let responseObj be a newly created + PromptResponseObject whose userChoice member is + the value of outcome. +
  4. +
  5. Resolve event.[[\userResponsePromise]] with + responseObj. +
  6. +
+
+
+

+ Usage example +

+

+ This example shows how one might prevent an automated install + prompt from showing until the user has finished a set of tasks. + Those tasks are represented as an array of promises, which the + application "awaits" to finish before an install prompt is + presented to the end-user. +

+
+            window.addEventListener("beforeinstallprompt", async (event) => {
+              event.preventDefault();
+              // Wait for e.g., the user to request installation from inside the app.
+              await Promise.all(tasksThatPreventsInstallation);
+              const { userChoice } = await event.prompt();
+              console.info(`user choice was: ${userChoice}`);
+            });
+          
+
+

Extensions to the Window object @@ -3507,7 +3625,7 @@

  • Fire an event + "fire|fired|firing an event">Fire an event
  • From 27dcb3cbf090b098688393d4673ecf38d5296809 Mon Sep 17 00:00:00 2001 From: Matt Giuca Date: Mon, 24 Apr 2017 13:59:58 +1000 Subject: [PATCH 2/2] Rework beforeinstallprompt sections. - Fixed links. - Simplified logic and fixed bugs in the algorithm. - Reworked usage example. - Rewrote some text. - Added an issue box about install prompts. - Addressed other review comments. --- index.html | 132 ++++++++++++++++++++++++++++++++--------------------- 1 file changed, 80 insertions(+), 52 deletions(-) diff --git a/index.html b/index.html index aa9e8e2d6..519801e37 100644 --- a/index.html +++ b/index.html @@ -435,14 +435,15 @@

    Queue a task on the application life-cycle task source to do the following:
      -
    1. Let target be at Window object of the - top-level browsing context. -
    2. -
    3. Let showPrompt be the result of firing an - event (event) named "beforeinstallprompt", using - BeforeInstallPromptEvent at target with its +
    4. Let event be a newly constructed + BeforeInstallPromptEvent named + beforeinstallprompt, with its cancelable attribute initialized to true.
    5. +
    6. Let showPrompt be the result of firing + event at the Window object of the top-level + browsing context. +
    7. If showPrompt is true, then, in parallel, request to present an install prompt with event. @@ -450,6 +451,16 @@

    +
    + Implementations may wish to show a prompt if, and only if, the site + explicitly requests it via + BeforeInstallPromptEvent.prompt(), but not + automatically without the site's approval. Is this something we want + to leave to the discretion of the user agent? (This would require + changing the language here, as "steps to notify before an automated + install prompt" would become "steps to notify that an install prompt + is available".) +

    @@ -543,14 +554,23 @@

    activating an automated install prompt, allowing a developer to prevent the default action for an install prompt.

    -

    - Thus, the default action of the BeforeInstallPromptEvent is to +

    + The default action of the BeforeInstallPromptEvent is to present an automated install prompt to the end-user. Canceling the default action (via - .preventDefault()) prevents the user agent from - presenting an automated - install prompt until a later time (see - BeforeInstallPromptEvent.prompt() method). + preventDefault) + prevents the user agent from presenting an automated install + prompt. The user agent is free to run steps to notify before + an automated install prompt again at a later time. +
    +

    + The PromptResponseObject contains the result of calling + prompt(). It + contains one member, userChoice, which states the user's + chosen outcome.

    An instance of a BeforeInstallPromptEvent has the following @@ -561,8 +581,8 @@

    [[\didPrompt]]
    - A boolean, initially false. Represents if this event - was used to present an install prompt to the end-user. + A boolean, initially false. Represents whether this + event was used to present an install prompt to the end-user.
    [[\userResponsePromise]] @@ -581,27 +601,24 @@

    steps:

      -
    1. Let p be a newly created promise. -
    2. -
    3. Resolve p with - this.[[\userResponsePromise]]. -
    4. If this.[[\userResponsePromise]] is pending:
        -
      1. If this event's isTrusted attribute is - false, reject - this.[[\userResponsePromise]] with - NotAllowedError, optionally informing the developer - that untrusted events can't call prompt(). +
      2. If this event's isTrusted attribute + is false, reject + this.[[\userResponsePromise]] with + NotAllowedError, optionally informing the developer that + untrusted events can't call prompt().
      3. Else if this.[[\didPrompt]] is - false, then, in parallel, request to + false, set this.[[\didPrompt]] + to true, then in parallel, request to present an install prompt with this event. Wait, possibly indefinitely, for the end-user to make a choice.
    5. -
    6. Return p. +
    7. Return this.[[\userResponsePromise]].

    @@ -613,12 +630,10 @@

    Present an install prompt and let outcome be the result. -
  • Let responseObj be a newly created - PromptResponseObject whose userChoice member is - the value of outcome. -
  • -
  • Resolve event.[[\userResponsePromise]] with - responseObj. +
  • Resolve event.[[\userResponsePromise]] with a + newly created PromptResponseObject whose userChoice member is the value of + outcome.
  • @@ -628,18 +643,30 @@

    This example shows how one might prevent an automated install - prompt from showing until the user has finished a set of tasks. - Those tasks are represented as an array of promises, which the - application "awaits" to finish before an install prompt is - presented to the end-user. + prompt from showing until the user clicks a button to install the + app. In this way, the site can leave installation at the user's + discretion (rather than prompting at an arbitrary time), whilst + still providing a prominent UI to do so.

    -
    -            window.addEventListener("beforeinstallprompt", async (event) => {
    +          
    +            window.addEventListener("beforeinstallprompt", event => {
    +              // Suppress automatic prompting.
                   event.preventDefault();
    -              // Wait for e.g., the user to request installation from inside the app.
    -              await Promise.all(tasksThatPreventsInstallation);
    -              const { userChoice } = await event.prompt();
    -              console.info(`user choice was: ${userChoice}`);
    +
    +              // Show the (disabled-by-default) install button. This button
    +              // resolves the installButtonClicked promise when clicked.
    +              installButton.disabled = false;
    +
    +              // Wait for the user to click the button.
    +              installButton.addEventListener("click", async e => {
    +                // The prompt() method can only be used once.
    +                installButton.disabled = true;
    +
    +                // Show the prompt.
    +                const { userChoice } = await event.prompt();
    +                console.info(`user choice was: ${userChoice}`);
    +              });
                 });
               
    @@ -649,9 +676,10 @@

    Extensions to the Window object

    - The following extensions to the Window object - specify the event handler attributes on which events relating - to the installation of a web application are fired. + The following extensions to the Window object specify the event + handler attributes on which events relating to the + installation of a web application are fired.

               partial interface Window {
    @@ -691,16 +719,16 @@ 

    application).

    -
    +

    onbeforeinstallprompt attribute

    - The onbeforeinstallprompt is an - event handler IDL attribute for the - "beforeinstallprompt" event type. The interface used for - these events is the BeforeInstallPromptEvent interface (see - the steps to notify before an automated install prompt). + The onbeforeinstallprompt is an event handler IDL + attribute for the "beforeinstallprompt" event type. + The interface used for these events is the + BeforeInstallPromptEvent interface (see the steps to + notify before an automated install prompt).

    @@ -3625,7 +3653,7 @@

  • Fire an event + "fire|fired|firing">Fire an event