This is an archived snapshot of W3C's public bugzilla bug tracker, decommissioned in April 2019. Please see the home page for more details.

Bug 25457 - Adding "AutocompleteError" to error names table
Summary: Adding "AutocompleteError" to error names table
Status: RESOLVED LATER
Alias: None
Product: WebAppsWG
Classification: Unclassified
Component: WebIDL (show other bugs)
Version: unspecified
Hardware: All All
: P2 normal
Target Milestone: ---
Assignee: Cameron McCormack
QA Contact: public-webapps-bugzilla
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2014-04-25 04:02 UTC by Dan Beam
Modified: 2014-09-17 15:39 UTC (History)
11 users (show)

See Also:


Attachments

Description Dan Beam 2014-04-25 04:02:32 UTC
Hi,

I'm implementing requestAutocomplete()[1] in Blink/Chrome.  I'd like to make requestAutocomplete() return a Promise[2].

The W3C TAG's guide to promises[3] recommends that promises be rejected with a DOMException, but not all of requestAutocomplete()'s failure methods map cleanly to the existing error names[4].  Adding new names for these specific failure categories doesn't seem useful to the platform.  Additionally, many potential failure causes may have the same reason property[5] ("disabled" => SecurityError, WrongDocumentError?, InvalidAccessError?)** and the terminology differs slightly ("cancel" => AbortError).

Because this would be confusing to web authors, I'd like to add a new name of "AutocompleteError" with a code of 0 (and if a message is required: "An attempt to autocomplete a form failed." or something along these lines).

I'm posting all of this to the whatwg@ thread[2] after filing this bug.

Thanks!

--
Dan Beam
dbeam@chromium.org

[1] http://www.whatwg.org/specs/web-apps/current-work/multipage/association-of-controls-and-forms.html#dom-form-requestautocomplete
[2] http://lists.whatwg.org/htdig.cgi/whatwg-whatwg.org/2014-April/254142.html
[3] https://github.com/w3ctag/promises-guide#rejection-reasons-should-be-errors
[4] http://w3c.github.io/dom/#error-names-0
[5] http://www.whatwg.org/specs/web-apps/current-work/multipage/association-of-controls-and-forms.html#autocompleteerrorevent

** best guesses at existing DOMException names, though they're not great equivalents
Comment 1 Dan Beam 2014-04-25 04:12:57 UTC
whatwg@ thread update is here:
http://lists.whatwg.org/htdig.cgi/whatwg-whatwg.org/2014-April/254172.html
Comment 2 Anne 2014-04-30 13:39:18 UTC
This whole section needs to move to IDL.
Comment 3 Dan Beam 2014-05-03 03:38:06 UTC
Boris Zbarsky asked that I check with public-script-coord@ (CC'd) regarding minting new DOMExceptions[1].  I’m proposing a new AutocompleteError type that’d be used when rejecting a Promise returned by `form.requestAutocomplete()`[2][3].

To quote the WebIDL spec[4]:

"""
Authors of future Web platform specifications should not mint new IDL exceptions but instead should use DOMException and distinguish different kinds of exceptions by their type and not by an integer code. The type will be exposed in ECMAScript using a "name" property on the exception object. One valid reason to mint new IDL exceptions is if additional exception fields are required. If a predefined exception matches the meaning of the exception to be thrown, it should be used in preference to minting an exception type.
"""

My proposed AutocompleteError would have a new exception field and look something like this (excuse the [probably wrong] IDL):

  exception AutocompleteError : DOMException {
    // code = 0
    // name = "AutocompleteError"
    const DOMString reason;
  };

The reason field would be the same as the AutocompleteErrorEvent.reason[5] dispatched before the Promise is rejected.  Current reason values are:

  "disabled" => flow was not run (generally because of developer error)
  "cancel" => flow was started but the user cancelled
  "invalid" => flow succeeded but invalid data was received

These values are already spec'd in HTML, implemented in Chrome (Firefox in progress[6]), and used by some sites.


Conversely, here's my best attempt at reusing existing error names[7] for Chrome's present implementation:

  Case: user cancels flow
  Reason: "cancel"
  Name: AbortError

  Case: invoked on an insecure page (e.g. non-HTTPS)
  Reason: "disabled"
  Name: SecurityError

  Case: invoked without processing a user gesture
  Reason: "disabled"
  Name: InvalidStateError? 

  Case: no payment-specific [autocomplete] types were found in form controls
  Reason: "disabled"
  Name: NotSupportedError?

  Case: invoked on <form autocomplete="off">
  Reason: "disabled"
  Name: NoModificationAllowedError?

  Case: invoked on <form> not in a frame (e.g. in a document fragment)
  Reason: "disabled"
  Name: WrongDocumentError?

  Case: a <form> control fails static validation after being filled
  Reason: "invalid"
  Name: InvalidModificationError or InvalidStateError?

I believe that reusing existing terminology could make the API more convoluted (e.g. does a "cancel" reason mean the same thing as an AbortError?).  Chrome already logs detailed failure messages to the developer console for testing (as encouraged by the spec), so I think differentiating failures at runtime wont be very useful (most errors are likely to occur only while adding this feature to a site).

Thoughts?

--
Dan Beam
dbeam@chromium.org

[1] http://lists.whatwg.org/htdig.cgi/whatwg-whatwg.org/2014-April/254173.html
[2] http://www.whatwg.org/specs/web-apps/current-work/multipage/association-of-controls-and-forms.html#dom-form-requestautocomplete
[3] http://lists.whatwg.org/htdig.cgi/whatwg-whatwg.org/2014-April/254142.html
[4] http://www.w3.org/TR/WebIDL/#idl-exceptions
[5] http://www.whatwg.org/specs/web-apps/current-work/multipage/association-of-controls-and-forms.html#autocompleteerrorevent
[6] https://bugzilla.mozilla.org/show_bug.cgi?id=939351
[7] http://w3c.github.io/dom/#error-names-0
Comment 4 Anne 2014-05-06 12:58:01 UTC
Are all of those truly exceptional? Note that a promise should only reject for reasons where a synchronous function would throw.

In any event, picking three existing names that approximate seems better than minting a whole new exception object.
Comment 5 Dan Beam 2014-05-07 18:25:20 UTC
(In reply to Anne from comment #4)
> Are all of those truly exceptional? Note that a promise should only reject
> for reasons where a synchronous function would throw.

Probably not, as cancel is a common case.

Does this means that the promise would be resolved in cases requestAutocomplete doesn't succeed (e.g. cancel)?  I'm not sure Promises are a good fit for rAc if that's the case (at least it doesn't seem intuitive to me).
Comment 6 Domenic Denicola 2014-05-07 18:34:25 UTC
Let's pretend you were writing in a language that primarily used synchronous code for this sort of thing. E.g. Java or C#, before their recent shifts to async, or Ruby and Python, where async is a minority.

If you called requestAutocomplete(), and the user cancelled, what would you expect? A return value of `false`? A thrown exception? Something else?

That should guide the answer here.
Comment 7 Dan Beam 2014-05-07 19:02:33 UTC
(In reply to Domenic Denicola from comment #6)
> Let's pretend you were writing in a language that primarily used synchronous
> code for this sort of thing. E.g. Java or C#, before their recent shifts to
> async, or Ruby and Python, where async is a minority.
> 
> If you called requestAutocomplete(), and the user cancelled, what would you
> expect? A return value of `false`? A thrown exception? Something else?
> 
> That should guide the answer here.

I kinda think exceptions are an anti-pattern, so I'll go with "return something to signal/explain the error".
Comment 8 Domenic Denicola 2014-05-07 19:04:50 UTC
In that case, the promise should be resolved with something that would signal/explain the error.
Comment 9 Joshua Bell 2014-05-08 01:07:18 UTC
I'm not familiar with the feature, but looking at the reasons listed in #c3, most of them do look to me like cases for which an exception would be thrown if the API were synchronous:

  Case: invoked on an insecure page (e.g. non-HTTPS)
  Case: invoked without processing a user gesture
  Case: no payment-specific [autocomplete] types were found in form controls
  Case: invoked on <form autocomplete="off">
  Case: invoked on <form> not in a frame (e.g. in a document fragment)

i.e. the developer didn't read the fine print, and the page has bugs. Best thing to do is log those errors back to the server and fix the page.

Not sure about this one:

  Case: a <form> control fails static validation after being filled

The last one is interesting:

  Case: user cancels flow

i.e. user denied permission. We should probably be very consistent across the web platform for that case for requestXXX() APIs.

I don't see "user agent policy" on the list e.g. "feature disabled"; at least for Service Worker we're treating that as a rejection.
Comment 10 Dan Beam 2014-05-13 02:32:26 UTC
I don't think requestAutocomplete returning a Promise is a win given the current restrictions on how Promises work.

I'm more than happy to hear an argument for how using Promises would improve this API in the future.
Comment 11 Domenic Denicola 2014-05-13 04:31:42 UTC
I don't understand. requestAutocomplete is clearly an asynchronous operation. Making it use the uniform promise API that all other aynchronous operations are tending toward is a big win for composability, usability, and developer experience. What restrictions are you talking about?
Comment 12 Dan Beam 2014-05-13 05:24:37 UTC
(In reply to Domenic Denicola from comment #11)
> I don't understand. requestAutocomplete is clearly an asynchronous
> operation. Making it use the uniform promise API that all other aynchronous
> operations are tending toward is a big win for composability, usability, and
> developer experience. What restrictions are you talking about?

The restriction that rejection arguments must be an exception[al case].  rAc often fails for a very non-exceptional reason (a user cancel).  Error handling in a success callback is not an improvement.
Comment 13 Domenic Denicola 2014-05-13 14:19:19 UTC
I'm sorry, I still don't understand.

Functions can either return values or throw exceptions. Async functions can either return fulfilled promises or return rejected promises.

When you throw an exception, you can throw anything, e.g. `throw 5`. This is bad practice though, and you generally try to throw things that are `instanceof Error` so that e.g. consumers can get stack traces.

Similarly, when you reject a promise with an exception, you can reject with anything, e.g. `return Promise.reject(5)`. Again this is bad practice, and you should generally reject with things that are `instanceof Error`.

There are no "restrictions" in the promise case that do not appear for every function, ever: your function can either succeed or fail, and if it fails, it _should_ do so using Error instances.

Why are these semantics---which are fairly basic to JavaScript, and indeed to most imperative languages---too restrictive to implement requestAutocomplete?
Comment 14 Dan Beam 2014-05-13 19:39:07 UTC
(In reply to Domenic Denicola from comment #13)
> I'm sorry, I still don't understand.
> 
> Functions can either return values or throw exceptions. Async functions can
> either return fulfilled promises or return rejected promises.
> 
> When you throw an exception, you can throw anything, e.g. `throw 5`. This is
> bad practice though, and you generally try to throw things that are
> `instanceof Error` so that e.g. consumers can get stack traces.
> 
> Similarly, when you reject a promise with an exception, you can reject with
> anything, e.g. `return Promise.reject(5)`. Again this is bad practice, and
> you should generally reject with things that are `instanceof Error`.
> 
> There are no "restrictions" in the promise case that do not appear for every
> function, ever: your function can either succeed or fail, and if it fails,
> it _should_ do so using Error instances.
> 
> Why are these semantics---which are fairly basic to JavaScript, and indeed
> to most imperative languages---too restrictive to implement
> requestAutocomplete?

In this analogy, requestAutocomplete never throws and therefore never rejects.  We'd basically just be using Promises as a callback (which one can already do with events).

Though I did not expect rAc to be resolved if the form was not filled (see my original email[1]), perhaps my understanding of Promise semantics was bad.  I do think authors would be mildly confused as to when they'd ever need a .catch() (just like authors are unaware of when to use try {} catch() {} without some strong way of knowing an interface throws [like in Java]).

esprehn@ worked pretty hard to streamline the default success case (e.g. static validation + "invalid" to omit checkValidity() from success callback), so adding a way to invoke rAc like this:

  promise.then(function(result) {
    if (result == 'success') {
      // success! submit form or fancy AJAX
    } else if (result == 'cancel') {
      // user canceled, maybe redirect?
    } else if (result == 'invalid') {
      // 1+ inputs are invalid, fix this somehow
    } else if (result == 'disabled') {
      // something bad happened, run for the hills
    }
  });

is about as (or less) appealing to me than the current code:

  form.onautocomplete = function() { /* success! submit form or fancy AJAX */ };

if the author only cares about the success case, and

  form.onautocompleteerror = function(error) {
    if (error.reason == 'cancel') {
      // user canceled, possibly redirect or something
    } else if (error.reason == 'invalid') {
      // 1+ inputs are invalid, fix this somehow
    } else if (error.reason == 'disabled') {
      // something bad happened, run for the hills
    }
  };

if they care about errors (in some cases, e.g. donations, they may not care about errors).

esprehn@: thoughts on the new Promises code example?


(In reply to Joshua Bell from comment #9)
> I'm not familiar with the feature, but looking at the reasons listed in #c3,
> most of them do look to me like cases for which an exception would be thrown
> if the API were synchronous:
> 
>   Case: invoked on an insecure page (e.g. non-HTTPS)
>   Case: invoked without processing a user gesture
>   Case: no payment-specific [autocomplete] types were found in form controls
>   Case: invoked on <form autocomplete="off">
>   Case: invoked on <form> not in a frame (e.g. in a document fragment)
> 
> i.e. the developer didn't read the fine print, and the page has bugs. Best
> thing to do is log those errors back to the server and fix the page.

Most "disabled" cases are developer error, but not all.

The spec allows failure with a "disabled" reason if "the user has disabled this feature for this form's Document's origin".  Chrome does not currently do this, but has in the past.  We used to also deny invocation from a frame.

Also, some extensions add autocomplete="off" to <form>s (basically attempting to turn off Autofill), which can cause this error.
Comment 15 Dan Beam 2014-05-13 19:41:05 UTC
(In reply to Dan Beam from comment #14)
>     if (result == 'success') {

FYI: this doesn't currently exist and would need to be added to HTML but seemed more explicit than a param `error` == "" to indicate success.
Comment 16 Elliott Sprehn 2014-05-19 20:48:47 UTC
What Error do new APIs throw when a user disabled an API or dismisses the permission prompt? How do developers correctly take action?

We either need to add a bunch of new Error types or we need to add a bunch of Result objects and make developers put error handling in _both_ the resolve and reject branches.

It seems really unfortunate for the correct use of an API to require error handling on both sides.

slightlyoff@ What are we supposed to do here?
Comment 17 Ian 'Hixie' Hickson 2014-09-17 15:39:47 UTC
(In reply to Domenic Denicola from comment #13)
> 
> Functions can either return values or throw exceptions. Async functions can
> either return fulfilled promises or return rejected promises.

Functions can return values, throw exceptions, and queue events to report success or failure. requestAutoComplete() returns void, has no way to throw an exception (it doesn't even have arguments), and it queues either an 'autocomplete' event or it fires a bunch of 'invalid' events (at various form controls) followed by an 'autocompleteerror' event, the latter of which has a reason field with three codes.

I don't think it makes sense for requestAutocomplete() to reject with an exception, nor do I think it makes sense for it to resolve in the case it would have fired 'autocompleteerror'.