Re: [w3c/manifest] Add beforeinstallprompt event (#417)

Hi there. I just had a long chat with @dominickng and @benfredwells about this. We have some mixed thoughts.

Firstly, let's start with what I proposed above (a global method rather than a `prompt` method on the `BeforeInstallPromptEvent` object). That is:

- We keep `beforeinstallprompt` event, but remove the `prompt` method from the event. (It keeps its `preventDefault` behaviour.)
- We add a new global method to initiate installation. Instead of calling it `navigator.showInstallPrompt`, let's call it `navigator.installApp` or similar.

OK, let's call that the *base proposal*.

Now you're suggesting we add (essentially) two new things:

1. `permissions.query({name: 'install'})`, to query whether it has been installed, can't yet be installed, or can be installed at the user's discretion, and
2. `permissions.onchange`, to be notified when that state changes.

Let's call that the *permissions proposal*. Notably, it doesn't (and can't) remove anything from the base proposal. In theory, we could drop the BIP event and just use the `change` event, but then that would mean you call `preventDefault` on the `change` event object, and that sounds totally wrong, so we must keep BIP.

### What does permissions add?

So what do those two new capabilities buy us? At first glance (for Chrome's current policy), not much:

1. `query` essentially tells us whether the install has succeeded (`"granted"`), BIP has been triggered (`"prompt"`) or nothing has been allowed yet (`"denied"`). But we can already tell whether it's been installed, and whether BIP has been triggered (just store a boolean variable in response to the `install` and `beforeinstallprompt` events, respectively). So this adds nothing.
2. `change` tells us when that state changes. But again, the BIP and install events already tell us when the user is being prompted and when the app is being installed.

So why do we want this?

The main reason we can think of is the separate BIP and `change` events allows UAs to have some interesting different policy options:
* The "Chrome" policy: "denied" by default. As soon as user engagement threshold is reached, we prompt. We fire the BIP and `change` (to "prompt") events at the same time. The site can either let the BIP go ahead, or cancel it and use the prompt powers later.
* An "always allow" policy: "prompt" by default. Apps can install themselves whenever they want, but there is no automatic prompt. BIP is never fired. The website can query, find the "prompt" status, and show a prompt at its leisure.
* A "silent transition" policy: "denied" by default. As soon as user engagement threshold is reached, the `change` event is fired to transition to the "prompt" state. BIP is never fired, and no prompt is ever automatically shown. The site can automatically prompt upon transition, or hold off until the user requests.
* A mixed policy: after some low engagement threshold, a silent transition to "prompt" (firing a "change" event but not a BIP). After a higher engagement threshold, an automatic prompt and BIP are fired.

The user agents can decide on the above policies and change policies without having to update the spec. So that's kind of cool.

Some other pros:

* No need to store your own global on BIP; can just use permissions.query() to figure out whether you're allowed to attempt to install.
* Kinda fits the permissions model. It helped us think about naming the method "install" rather than "showInstallPrompt" (just as we have "getUserMedia" not "showGetUserMediaPrompt", even though that will likely trigger a prompt as well).
* We may not need an `install` event at all (the `permissions.change` event to "granted" may be sufficient, but see below).

### The problems

The main problem is with what "granted" means. You said:

> "An install attempt was made by showing the dialog. If it succeeded - you should have gotten oninstall event... if you missed it, your fault!" (unless we add oninstallerror, down the road). But it has nothing to do with installation success/fail.

The problem with this is that "granted" is normally an ongoing state. It means you are free to use, say, the microphone or geolocation, without restriction. In the install case, it *should* mean you are free to repeatedly install the app whenever you like (which we don't want to allow).

What it will actually mean in practice is "the app has been installed, and you cannot request it again". Since installation is an action (not a state), the "granted" state will be functionally equivalent to the "denied" state. What happens if you call `navigator.installApp()` from the "granted" state? Does it succeed silently (actually doing nothing)? Does it attempt to re-install the app? (On Android at least, that creates duplicate home screen icons, and Chrome is unable to detect whether one already exists.) Does it reject the promise? (Then in what way is it "granted"?) What if installation fails? Do we still go into a "granted" state which means "you failed to install and cannot try again, sorry"? There are lots of conceptual problems with "granted" meaning what you said it means.

Furthermore, what does `permissions.request` mean for the "install" permission? It's supposed to prompt the user then if they say yes, go into the "granted" state. What does this mean? It wouldn't actually install anything, you'd be in the granted state. Does that mean the app is free to install itself at some point in the future? (That isn't good.) Or is it just a no-op? This is where we think installation doesn't really match up with the permissions system: the permissions system is about granting perpetual access to a resource, not taking a single action once.

Basically, we could write into the spec answers to all of these questions that make it behave the way we want, but it would then feel like it doesn't fit into the permissions system properly.

Some more negatives:

* It makes things more complicated for UA developers and web developers (given that it's a superset of the base proposal).
  * @benfredwells says it will be very awkward to put it in as a permission internally in Chrome.
  * Getting *both* a BIP and change event at the same time could be confusing for developers.
* If we allow different UAs to behave differently (as I outlined above), it means web developers need to correctly accomodate all of those different modes, which could lead to browser-specific bugs on certain sites.
* @dominickng says the permissions API itself is still in flux.

### In closing

OK, so I think we first need to decide whether the pros I listed above are actually desirable. If not, then I don't see a reason to attach this to the permissions system. If they are, then we have two choices: we can extend the beforeinstallprompt APIs to provide those benefits without using the `permissions` API, *or* we can go ahead and use the permissions APIs, after we come up with some good answers for what "granted" and `request` mean.

---
You are receiving this because you are subscribed to this thread.
Reply to this email directly or view it on GitHub:
https://github.com/w3c/manifest/issues/417#issuecomment-229588215

Received on Thursday, 30 June 2016 07:56:13 UTC