[whatwg] Proposal to extend registerProtocolHandler

On Tue, 26 Jul 2011, James Kozianski wrote:
> 
> Here are the use cases I had in mind:
> 
> 1. Allow sites to conditionally show UI to promote the advantages of 
> registering the site as a handler.
>
> 2. Allow sites to provide settings screens which allow users to register 
> [and] deregister handlers from within the site.

There are several states a browser could be in:

A + registerFoo() has never been called.
B + registerFoo() has been called, but user was not notified.
  - registerFoo() has been called, user has been notified, and:
C    + user has not responded
D    + user has declined once but is open to future notifications
     - user has accepted (or user agent has implicitly accepted) and:
E       + the handler is a non-default handler, and there is no default
F       + the handler is the default handler
G       + some other handler is the default handler
H       + user since unregistered handler, but is open to readding it
I       + user since unregistered handler permanently (blocked site)
J    + user has declined permanently (blocked site)
K    + site has unregistered itself but user is open to seeing it again
L    + site unregistered itself; user made that permanent (blocked site)

Of these, the page can, without help from the user agent, distinguish
case A from all other cases, and cases K and L from all other cases.

Cases A, K, and L, from the perspective of the page, are more or less
equivalent, and can therefore be treated as identical in the API.

This leaves cases B through J. 

For the use case of determining whether to show a promotional UI,
distinguishing A, K and L, and B through J is sufficient.
Distinguishing A from K and L is possible at the site level (the site
knows if it unregistered or not); if in the API we group those three
and make them separate from B through J, then that use case is
handled.

Moving to the second use case, settings, we see a need for
distinguishing the states where calling the registration method is
useful from the states where calling the method would not help, either
because it's already registered or because it's blocked.

Cases B, E, F, G, I, and J are cases where calling the method again
will have no useful effect:

 B - the UA isn't showing the notification for some reason
 E, F, G - the handler is registered already
 I and J - the user has blocked the site from calling the method

The remaining cases, C, D, and H, are cases where calling the method
again would potentially allow the user to register the handler again:

 C - if the user didn't respond last time, he might respond the next
     time. So you can show the UI again just in case. Note that this
     applies to browsers that show an immediate notification about the
     handler; personally I prefer UIs where the methods always succeed
     but the user doesn't find out about it until the next time they
     try to use a particular scheme or content-type, at which point
     they find a list of handlers for that scheme/type, and they can
     explicitly select one from the list (i.e. the browser is in state
     E after the method call, and the user can switch it to F, G, H,
     or I the next time he would actually use it).

 D - if the user declined once but without blocking the site, then it
     makes sense to offer UI to the user saying that he can still
     register the handler.

 H - the user once accepted then declined; it's a bit like declining
     in the first place, and so is equivalent to D.

So we need to expose the following three states:

1. Handler is not registered and no attempt has yet been made to
   register it: registerFoo() has never been called, or the site has
   since unregistered its handler. The site should consider showing a
   notification that the feature is available.
   Call this "new".

2. Handler is registered or site is blocked: registerFoo() has been
   called, and either the user accepted and the handler is registered,
   or the user blocked the site permanently from registering the
   handler again. In either case, there is no benefit to the site
   trying to register the handler again.
   Call this "registered".

3. Handler is not registered but the user has not blocked the site
   from again offering to register the handler: either the user has
   not responded to previous attempts to register the handler, or the
   user has declined or unregistered the handler in the past but not
   in a permanent fashion.
   Call this "declined".

Since there's two registration methods, this means two discovery methods. 
We also need a mechanism for the site to unregister itself (e.g. when the 
feature has been removed), so that's two unregistration methods also.

Thus:

  void registerProtocolHandler(DOMString scheme, DOMString url, DOMString title);
  void registerContentHandler(DOMString mimeType, DOMString url, DOMString title);
  DOMString isProtocolHandlerRegistered(DOMString scheme, DOMString url);
  DOMString isContentHandlerRegistered(DOMString mimeType, DOMString url);
  void unregisterProtocolHandler(DOMString scheme, DOMString url);
  void unregisterContentHandler(DOMString mimeType, DOMString url);


On Tue, 9 Aug 2011, James Kozianski wrote:
> 
> [1] http://t1.gstatic.com/images?q=tbn:ANd9GcSqpKN5FmbWKiC-6e-ptj6KJ_qbiyPuPj2M1YZO2wwIOAf6qdX8
> [2] http://img.technified.net/tf/Gmail-Desktop-notifications-for_14988/gmail-desktop-notification-settings.png

This kind of thing seems possible with the above methods.


On Mon, 18 Jul 2011, Ben Schwarz wrote:
>
> After recently discovering custom protocol handlers[1], it struck me 
> that it would be difficult to provide a decent user experience for those 
> who used browsers which did not support the feature. [...]
> 
> I want to be able to tell when registration of protocols/schemes has 
> either failed or been successful So that I can provide a fluid user 
> experience.

Do the above methods adequately provide this ability?


> As a web developer, I want to be able to ascertain whether the 
> user-agent supports the registerProtocolHandler feature, So that I can 
> have an alternate experience for the user.

You can easily test this using JS reflection:

   if (navigator.registerProtocolHandler) { ... } else { ... }


> As a web designer / developer, I want to be able to ascertain if another 
> website has registered a custom protocol handler in the user?s 
> browser, So that I can knowingly design and implement an integration 
> experience.

This isn't possible. I can't work out a way to do it that wouldn't be a 
privacy leak (e.g. suppose a porn site registered a handler -- letting 
other sites determine handler registrations cross-site would leak 
information about the user visiting the porn site).


> As a web designer / developer, I want to be able to ascertain whether 
> the user has "installed" a custom protocol handler within their 
> operating system, So that I can knowingly design and implement an 
> integration experience.

This has a similar privacy leak problem, but with native apps instead of 
Web sites.


On Thu, 21 Apr 2011, James Kozianski wrote:
> 
> Currently if a site wants its users to register it as a handler for a 
> given protocol it has two options:
> 
>     a) It can call registerProtocolHandler() on page load. (This 
> approach was suggested in [1])
> 
>     b) It can have a button that the user clicks (or similar) to enact 
> the registration.
> 
> The former is problematic because the call to registerProtocolHandler 
> will cause the browser's UI to notify them of the registration (or 
> prompt them to make a decision), which is redundant if it occurs on 
> every page load.

This is not necessarily true. In fact, I'd go further and say that it 
would be false in good browser implementations. IMHO the 
registerProtocolHandler() method's ideal implementation is to do nothing 
at all, or at most display a minor notification along the lines of:

   "Example Corp Authenticator.
    This site (www.example.com) supports the web+auth: protocol."

The registration would always succeed by default, and the next time the 
user tries to use a link of the appropriate scheme, the user would be 
presented with a dialog saying something like:

   The web+auth: protocol is supported by the following sites:

     www.example.com  Example Corp Authenticator  [Block] [Set as default]
     hostile.invalid  Evil Authenticator          [Block] [Set as default]
     facebook.com     Facebook Login              [Block] [Set as default]
     twitter.com      Your Twitter Identity       [Block] [Set as default]

   Select the site you wish to use this time.          (( Cancel ))

With this kind of approach, sites would always be able to call the 
registration method and there would not be a need for sites to show promos 
to their users. 

But other UIs are equally legitimate, granted.

With the new methods mentioned above, the page can decide whether to show 
the prompt or not, if user agents prefer a more immediate mechanism rather 
than a more passive one as described above.


> 2. Introduce an unregisterProtocolHandler() function.
> 
> Such a function is desirable because it allows sites to remove outdated 
> handlers from clients and would enable clients to provide a UI for 
> managing their registered protocol handlers.

Agreed.


> 3. Require all URL arguments to have the same origin as the page 
> executing the call.
> 
> This would would make the behaviour of this set of functions more 
> consistent (isRegistered() should only work for same-domain queries, to 
> prevent information leaking).

Yeah, that makes sense.


On Fri, 1 Jul 2011, Peter Kasting wrote:
> 
> From my work on the Chrome UI side of this, I would very much like to 
> see something like isRegistered().  This would allow sites to 
> conditionalize requests for the protocol handler.  This is important to 
> me because I would also like to experiment (after that point) with 
> requiring a user gesture for this request (much like browsers typically 
> require user gestures for window.open()), so sites cannot hammer the 
> user with requests outside of some sort of interaction-based workflow.

The great thing about the silent (or quiet) mechanism described above is 
that sites really can't do much in the way of hammering the user. The 
worst they can do is add spam to the dialog the next time it comes up, 
and that's only until the user picks a default handler (after which the 
dialog doesn't show).


On Fri, 1 Jul 2011, Michael Davidson wrote:
>
> From my perspective on Gmail, I would prefer to know if the user hasn't 
> registered because they declined previously or haven't been asked. If 
> they've declined previously, then calling registerProtocolHandler() in 
> today's UAs will not do anything. If I can't detect that state, then 
> they'll keep clicking and I'll keep calling and they'll get frustrated.
> 
> I'd prefer if isRegistered() was something like registeredState() and 
> returned REGISTERED, DECLINED, or NOTASKED. Then I could make a UI that 
> really reflects reality.

Will the methods above adequately handle this?


On Fri, 1 Jul 2011, Michael Davidson wrote:
> 
> FWIW, notifications already exposes this:
> 
> http://dev.w3.org/2006/webapi/WebNotifications/publish/FeaturePermissions.html

That seems like a rather over-engineered API. It also wouldn't really 
handle the case here since we have to pass two arguments to the check in 
addition to the name of hte permission.

Also, it's not a permission we're talking about here. :-)


On Tue, 5 Jul 2011, Rich Tibbett wrote:
> 
> The idea would be to emulate window.open functionality to the point that 
> something happens only if a _user-initiated_ click event occurs. If an 
> event is invoked by a script synthesizing its event via e.g. 
> anchor.click() then that should not invoke any UI stuff. Only if 
> window.open is invoked by a user does the popup blocker not kick-in and 
> the popup pages open. It would be nice to have that same principle work 
> for registerProtocolHandler.

This makes sense if the method fires a dialog, but is that really 
something we ever want to encourage? I would think that if any UI beyond a 
simple notifications appeared at all when the method was called, it would 
at most be an info-bar-style non-modal UI.


> window.open seems a little under-defined when it comes to the 
> pseudo-standard behavior of blocking window.open calls outside of a 
> user-initiated event.

Agreed. I've filed a bug to address this.


On Sun, 3 Jul 2011, timeless wrote:
> 
> I don't look forward to being blackmailed by sites into me allowing them 
> to register for X or else I don't get their content.

Agreed. The way I specced it above, if the user blocks the site it appears 
to the site as being accepted. This seems to me like it would work around 
this problem.


On Mon, 4 Jul 2011, Peter Kasting wrote:
> 
> It's not a problem on the UA side, but the web page side.  Assume we 
> want to limit action on this call to cases where there's a user gesture 
> (to prevent bad sites from annoying you quite as easily, though I admit 
> it is not a huge roadblock).  Now you're Gmail and you want to give the 
> user the ability to register you as a mailto: handler.  So you whip up 
> some button that will make the rPH() call and some UI around it that 
> calls attention to it. Without the ability to check if you're already 
> the default handler, you have to show this UI all the time, which is not 
> at all appealing, or else bury it somewhere, which means it will never 
> get seen by most users.

Do you think we need to expose whether the page is the _default_ handler? 
That seems like a more fine-grained distinction than is strictly 
necessary. A browser may not even have a concept of "default handler".


On Mon, 4 Jul 2011, timeless wrote:
>
> Or we could use a <link> and have the UA only show the option to the 
> user if it feels like it. The UA could choose not to show it if it's 
> already active. It could also choose to only show it if the user has 
> visited repeatedly or whatever.
> 
> This is closer to ATOM discovery, but it's also a deployed style.

We couldn't use a simple rel value, it would have to involve at least two 
new attributes and one of them would have to accept a URL that isn't a URL 
(the %s magic). We could do this, though, it's true. At this stage it 
seems people are more interested in being able to programatically decide 
when to offer the feature; I don't know what to make of that, but if we 
are to address that need, a declarative solution is not particularly 
better than an imperative API.


On Tue, 5 Jul 2011, Michael Davidson wrote:
>
> For rPH, please don't require a user-initiated click for the call. 
> That's one very annoying thing about notifications - it takes users two 
> clicks to enable them, and every app has to find some suitable in-page 
> UI to ask users to make the first click. Since both notifications and 
> rPH are confirmed by the UA, it seems silly to require a user-initiated 
> click. Prompting for permissions is much less annoying than opening a 
> new window, so I don't think the same standards should apply. It's also 
> very easy for users to prevent sites from asking in the future by just 
> denying the permission.

Notifications shouldn't require any clicks at all. They should just work, 
initially constrained to the browser page, with an in-notification UI to 
allow the user to decide where the notifications should appear (e.g. only 
in the browser page, or promote them to desktop level, or promote them to 
"send me an SMS to page me whenever this goes off", or whatever).


On Tue, 5 Jul 2011, Peter Kasting wrote:
> 
> To be truly honest, requiring a user gesture probably doesn't work for 
> rPH() because it doesn't actually work for window.open, or allowing 
> executable files to download, or any other purpose we've keyed off in 
> UAs so far. That's because a click is not at all a good indicator of 
> some sort of of user understanding and intent.  The only thing it buys 
> you is the possibility that a visible UI change happens immediately 
> after a user's click and thus that in theory the user _might_ be able to 
> guess that the click triggered the UI change.

For popups it works ok (not great) because it means that if the user sees 
a popup appear, he can limit the damage by not clicking any more (a 
natural reaction to seeing a popup). This contrasts to the worst-case 
scenario without this restriction, which is a flood of popups that the 
user can do nothing about.

That is to say, the user gesture requirement for popups is a rate limiter, 
not a blocker.


On Wed, 6 Jul 2011, Robert O'Callahan wrote:
>
> I don't think browsers need to prompt for registerProtocolHandler. 
> Instead, I would simply allow any site to register as a protocol handler 
> for almost anything, and remember all such registrations. When the user 
> navigates to a URI whose protocol has had an app newly registered for it 
> since the last such navigation, then prompt the user to choose which app 
> to use. The UI for that prompt would give prominence to the previously- 
> seected app (if any), followed by any newly registered app(s), and would 
> remember the choice.

Hear hear.


> It seems to me that this approach would address the issues James 
> Kozianski raised in part 1 of his email, obviating the need for an 
> isRegistered() function.

I've still added the isFooRegistered() methods because it seems not all 
UAs agree with us here.

Also:

On Wed, 6 Jul 2011, Ojan Vafai wrote:
>
> All the arguments for registering handlers aside, it ought to be 
> possible for a website to provide some UI for deregistering. For 
> example, many users will expect to go into gmail's settings to stop 
> having gmail handle email links. Gmail's help section needing to give 
> users instructions on each browser's UI for deregistering is not a good 
> user experience.
>
> If they're going to have a UI for deregistering, it makes sense for them 
> to be able to show it only when actually registered.

This is a good reason to still have an isFooRegistered() method.


On Wed, 6 Jul 2011, Olli Pettay wrote:
> 
> So all the ad sites (which are embedded via iframe or something) would 
> just register all the possible protocols so that some user might 
> accidentally use their site for protocol handling - especially if the 
> site was the first one to register the protocol. Doesn't sound too good.

On Thu, 7 Jul 2011, Robert O'Callahan wrote:
> 
> I think you could sequester the new app options sufficiently that that 
> would not be worth doing.

Indeed. Also, you could set it up so that blocking a site for one scheme 
or MIME type would block all the site's registrations. We could maybe even 
cause it to make iframes from that site display a roadblock ("You 
previously blocked this site because it registered a handler for the 
'web+auth:' protocol. ((Visit the site anyway...))").


On Thu, 7 Jul 2011, Robert O'Callahan wrote:
> 
> You could also just deny all registerProtocolHandler() attempts from 
> non-toplevel frames.

That seems like a bad idea. It would preclude services from being offered 
by iframes, which seem like a very likely scenario here.


On Thu, 7 Jul 2011, Rich Tibbett wrote:
> 
> For registration, we could allow _auto-registration_ of protocol 
> handlers only if a.) this is the first time the protocol is being 
> registered and b.) when the registration request is coming directly from 
> the top-most window context only (i.e. from a web page that users are 
> actually visiting).
> 
> In all other cases (if you're the nth provider to attempt to register an 
> already registered protocol handler or you're not requesting from 
> window.top) then the UA would ask the user whether they wish to install 
> this handler as the default for the given protocol or not (replacing the 
> previous default handler).
> 
> For usage, the ideal situation would be for the user to click on a link 
> and be auto-directed somewhere without having to select from multiple 
> providers each time.
> 
> When the user wants to override the default protocol handler then the UA 
> could allow e.g. ctrl-shift-click to force show the protocol handler 
> dialog to the user. There the user would be free to select any handler 
> that the UA has come in to contact with during the course of the user's 
> previous browsing sessions (i.e. those handlers that the user decided 
> not to set as defaults at the time).
> 
> Users should be able to easily detach protocol handlers from this list 
> with either [delete] or [delete all handlers for this domain] on this 
> interface.

That could work too.


On Fri, 8 Jul 2011, Jonas Sicking wrote:
>
> I definitely have privacy concerns regarding a isRegistered function. 
> Such a function might be ok in some contexts, but I'd like to avoid it 
> as far as possible.
> 
> For example I don't think we need to think in terms of the, arguably 
> crappy, UI that browsers currently use. One simple improvement that 
> browsers could do is to simply have UI that shows "yes", "not now" and 
> "never". If the user chooses "never" then no UI would be displayed the 
> next time that the site calls registerProtocolHandler.

Right, that's what I mean by "blocked". The proposal above (now in the 
spec) exposes this to the site as a "registered" case, to avoid the 
privacy leak (it makes cases where a user hates the site equivalent to the 
cases where a user likes the site but has no use for the feature).


> Similarly, having a unregisterProtocolHandler without a isRegistered 
> function makes a lot of sense to me. I agree it might not let you create 
> the perfect UI, but you can get pretty far.

Why shouldn't we aim for a more perfect UI?


On Fri, 8 Jul 2011, Jonas Sicking wrote:
> On Fri, Jul 8, 2011 at 9:44 AM, Ojan Vafai <ojan at chromium.org> wrote:
> >
> > Just to be clear, you have privacy concerns for an isRegistered 
> > function that is same-domain restricted?
> 
> Yes.

Could you elaborate on these? A site can presumably tell if it's being 
used or not anyway, so what information does this leak?

-- 
Ian Hickson               U+1047E                )\._.,--....,'``.    fL
http://ln.hixie.ch/       U+263A                /,   _.. \   _\  ;`._ ,.
Things that are impossible just take longer.   `._.-(,_..'--(,_..'`-.;.'

Received on Thursday, 25 August 2011 15:23:55 UTC