Skip to content

Commit

Permalink
Feat(events): add BeforeInstallPromptEvent (#520)
Browse files Browse the repository at this point in the history
* Feat(events): add BeforeInstallPromptEvent (closes #417)
* 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.
  • Loading branch information
Marcos Cáceres authored May 9, 2017
1 parent edaf7b1 commit b0ad114
Showing 1 changed file with 166 additions and 20 deletions.
186 changes: 166 additions & 20 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -435,22 +435,32 @@ <h2>
<a>Queue a task</a> on the <a>application life-cycle task
source</a> to do the following:
<ol>
<li>Let <var>event</var> be a <a data-lt=
"construct a BeforeInstallPromptEvent">newly constructed</a> <a>
BeforeInstallPromptEvent</a> named
"<a>beforeinstallprompt</a>", which is cancelable.
<li>Let <var>event</var> be a newly constructed
<a>BeforeInstallPromptEvent</a> named
<code>beforeinstallprompt</code>, with its
<code>cancelable</code> attribute initialized to true.
</li>
<li>
<a>Fire</a> <var>event</var> at the <a>Window</a> object of the
<a>top-level browsing context</a>.
<li>Let <var>showPrompt</var> be the result of <a>firing</a>
<var>event</var> at the <a>Window</a> object of the <a>top-level
browsing context</a>.
</li>
<li>If <var>event</var>'s <a>canceled flag</a> is not set, then,
<a>in parallel</a>, <a>request to present an install prompt</a>
with <var>event</var>.
<li>If <var>showPrompt</var> is true, then, <a>in parallel</a>,
<a>request to present an install prompt</a> with
<var>event</var>.
</li>
</ol>
</li>
</ol>
<div class="issue">
Implementations may wish to show a prompt if, and only if, the site
explicitly requests it via
<a><code>BeforeInstallPromptEvent.prompt()</code></a>, 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".)
</div>
</section>
<section>
<h3 id="installation-sec">
Expand Down Expand Up @@ -526,14 +536,150 @@ <h2>
DOM events <a>fired</a> by this specification use the <dfn>application
life-cycle task source</dfn>.
</p>
<section data-dfn-for="BeforeInstallPromptEvent">
<h3>
<code>BeforeInstallPromptEvent</code> Interface
</h3>
<pre class="idl">
[Constructor]
interface BeforeInstallPromptEvent : Event {
Promise&lt;PromptResponseObject&gt; prompt();
};
dictionary PromptResponseObject {
AppBannerPromptOutcome userChoice;
};
</pre>
<p>
The <dfn>BeforeInstallPromptEvent</dfn> is dispatched prior to
activating an <a>automated install prompt</a>, allowing a developer
to prevent the default action for an install prompt.
</p>
<div class="note">
The default action of the <a>BeforeInstallPromptEvent</a> is to
<a data-lt="presents an install prompt">present an automated install
prompt</a> to the end-user. Canceling the default action (via
<a data-cite=
"DOM#dom-event-preventdefault"><code>preventDefault</code></a>)
prevents the user agent from <a data-lt=
"presents an install prompt">presenting an automated install
prompt</a>. The user agent is free to run <a>steps to notify before
an automated install prompt</a> again at a later time.
</div>
<p>
The <dfn>PromptResponseObject</dfn> contains the result of calling
<a for="BeforeInstallPromptEvent"><code>prompt()</code></a>. It
contains one member, <dfn for=
"PromptResponseObject">userChoice</dfn>, which states the user's
chosen outcome.
</p>
<p>
An instance of a <a>BeforeInstallPromptEvent</a> has the following
internal slots:
</p>
<dl>
<dt>
<dfn>[[\didPrompt]]</dfn>
</dt>
<dd>
A boolean, initially <code>false</code>. Represents whether this
event was used to <a>present an install prompt</a> to the end-user.
</dd>
<dt>
<dfn>[[\userResponsePromise]]</dfn>
</dt>
<dd>
A promise that represents the outcome of <a>presenting an install
prompt</a>.
</dd>
</dl>
<section>
<h4>
<code>prompt()</code> method
</h4>
<p>
The <dfn>prompt</dfn> method, when called, runs the following
steps:
</p>
<ol>
<li>If <var>this</var>.<a>[[\userResponsePromise]]</a> is pending:
<ol>
<li>If this event's <a data-cite=
"!DOM#dom-event-istrusted"><code>isTrusted</code></a> attribute
is <code>false</code>, reject
<var>this</var>.<a>[[\userResponsePromise]]</a> with
<a>NotAllowedError</a>, optionally informing the developer that
untrusted events can't call <code>prompt()</code>.
</li>
<li>Else if <var>this</var>.<a>[[\didPrompt]]</a> is
<code>false</code>, set <var>this</var>.<a>[[\didPrompt]]</a>
to <code>true</code>, then <a>in parallel</a>, <a>request to
present an install prompt</a> with this event. Wait, possibly
indefinitely, for the end-user to make a choice.
</li>
</ol>
</li>
<li>Return <var>this</var>.<a>[[\userResponsePromise]]</a>.
</li>
</ol>
<p>
To <dfn>request to present an install prompt</dfn> with
<a>BeforeInstallPromptEvent</a> <var>event</var>:
</p>
<ol>
<li>
<a>Present an install prompt</a> and let <var>outcome</var> be
the result.
</li>
<li>Resolve <var>event</var>.<a>[[\userResponsePromise]]</a> with a
newly created <a>PromptResponseObject</a> whose <a for=
"PromptResponseObject">userChoice</a> member is the value of <var>
outcome</var>.
</li>
</ol>
</section>
<section class="informative">
<h4>
Usage example
</h4>
<p>
This example shows how one might prevent an automated install
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.
</p>
<pre class="example" title=
"Using beforeinstallprompt to present an install button">
window.addEventListener("beforeinstallprompt", event =&gt; {
// Suppress automatic prompting.
event.preventDefault();

// 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 =&gt; {
// 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}`);
});
});
</pre>
</section>
</section>
<section>
<h3>
Extensions to the <code>Window</code> object
</h3>
<p>
The following extensions to the <dfn><code>Window</code></dfn> object
specify the <a>event handler attributes</a> on which events relating
to the <a>installation</a> of a web application are <a>fired</a>.
The following extensions to the <dfn data-cite=
"!HTML#window"><code>Window</code></dfn> object specify the <a>event
handler attributes</a> on which events relating to the
<a>installation</a> of a web application are <a>fired</a>.
</p>
<pre class="idl">
partial interface Window {
Expand Down Expand Up @@ -573,16 +719,16 @@ <h4>
application</a>).
</p>
</section>
<section>
<section data-dfn-for="Window">
<h4>
<code>onbeforeinstallprompt</code> attribute
</h4>
<p>
The <dfn data-dfn-for="Window">onbeforeinstallprompt</dfn> is an
<a>event handler IDL attribute</a> for the
"<dfn>beforeinstallprompt</dfn>" event type. The interface used for
these events is the <a>BeforeInstallPromptEvent</a> interface (see
the <a>steps to notify before an automated install prompt</a>).
The <dfn>onbeforeinstallprompt</dfn> is an <a>event handler IDL
attribute</a> for the "<dfn>beforeinstallprompt</dfn>" event type.
The interface used for these events is the
<a>BeforeInstallPromptEvent</a> interface (see the <a>steps to
notify before an automated install prompt</a>).
</p>
</section>
</section>
Expand Down Expand Up @@ -3510,7 +3656,7 @@ <h2>
<li>
<a href=
"https://dom.spec.whatwg.org/#concept-event-fire"><dfn data-lt=
"fire|fired">Fire an event</dfn></a>
"fire|fired|firing">Fire an event</dfn></a>
</li>
</ul>
<p>
Expand Down

0 comments on commit b0ad114

Please sign in to comment.