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 25472 - autocomplete: return a promise for requestAutocomplete()
Summary: autocomplete: return a promise for requestAutocomplete()
Status: RESOLVED WONTFIX
Alias: None
Product: WHATWG
Classification: Unclassified
Component: HTML (show other bugs)
Version: unspecified
Hardware: Other other
: P3 enhancement
Target Milestone: 2017 Q1
Assignee: Ian 'Hixie' Hickson
QA Contact: contributor
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2014-04-25 23:49 UTC by Ian 'Hixie' Hickson
Modified: 2016-06-16 15:50 UTC (History)
9 users (show)

See Also:


Attachments

Description Ian 'Hixie' Hickson 2014-04-25 23:49:23 UTC

    
Comment 1 Ian 'Hixie' Hickson 2014-05-06 16:23:31 UTC
Before I can do this, I need a concrete proposal for how exactly it should work. I'm not familiar enough with how promises are supposed to work to design it.
Comment 2 Ian 'Hixie' Hickson 2014-09-12 18:29:22 UTC
I've learnt enough about promises that I can probably make a proposal here. The question now is just: does anyone want to implement this, or is the event API sufficient?
Comment 3 Brian Nicholson 2014-09-12 18:49:12 UTC
I would be happy to implement this for Firefox.
Comment 4 Dan Beam 2014-09-12 21:06:00 UTC
Ian or Brian: could you write an example code snippet of how web authors would use requestAutocomplete() with a Promise (so I can visualize it)?

I've previously looked into this; see [1] and [2] for more context.

I prototyped my proposal [3] five months ago and would be happy to resume work on this if we can agree on an API (that was the previous blocker).

[1] http://lists.whatwg.org/htdig.cgi/whatwg-whatwg.org/2014-April/254142.html
[2] https://www.w3.org/Bugs/Public/show_bug.cgi?id=25457
[3] https://codereview.chromium.org/228783007/
Comment 5 Brian Nicholson 2014-09-16 18:52:39 UTC
I initially anticipated this API to resolve only on success and reject for all failures, including user cancellation. Intuitively, that seems more straightforward to me as it allows the reject handler to iterate through all errors versus splitting different kinds of errors between resolve and reject.

I agree, though, that user cancellation isn't an exceptional event, so that muddies things a bit. Is there an established precedent for cancellation (and other non-exceptional failures) from other APIs using promises?
Comment 6 Ian 'Hixie' Hickson 2014-09-17 15:32:51 UTC
I would just have anything that fires 'autocomplete' now also result in the promise being resolved with the form as its value, and anything that results in 'autocompleteerror' now result in the promise being rejected with the reason string as its value. (I wouldn't reject with an exception since failure here is not exceptional.)
Comment 7 Ian 'Hixie' Hickson 2014-09-17 15:40:14 UTC
(see also my comment on bug 25457 comment 17)
Comment 8 Anne 2014-09-17 15:40:28 UTC
That sound wrong. We'll get declarative syntax for promises. At that point they're the same as a function call. Their either return a value or throw. So that's how we should model this. E.g. how would you model this assuming the operation was blocking:

  var r
  try {
    r = obj.requestAutoComplete()
  } catch(e) {
    r = e
  }
  console.log(r)
Comment 9 Ian 'Hixie' Hickson 2014-09-17 20:06:31 UTC
If it was blocking I would model it as:

   if (obj.requestAutocomplete()) {
     // success
   } else {
     switch (obj.rejectReason) {
       // ...
     }
   }

...or some such (in a sane language with pass-by-reference arguments, I would just have an "out" argument where the reject reason would put stuffed).

There's no exceptions here. The user canceling the dialog, for instance, isn't an exceptional situation. Modeling it with exceptions would make no sense IMHO.
Comment 10 Dan Beam 2014-09-20 00:24:44 UTC
Cancel will probably happen more than success (for now, maybe always?) and therefore would probably never throw (nor reject).


This means that if rAc returns a Promise:

 ↪ If any other error would throw/reject:
  ↪ authors will have to handle failures in both resolution/rejection

 ↪ If no errors would throw/reject:
  ↪ the returned Promise is a fancy callback (and rejection logic is superfluous)


If rAc was changed to throw when invoked:
- from an insecure page
- from within <iframe>
- without a user gesture

the sync code would look possibly like this:

  try {
    form.requestAutocomplete();
    // determine whether user cancelled or filled inputs are valid here
    // maybe by returning something or adding an attribute to the form?
  } catch (e) {
    // figure out what failed (maybe e.reason tells you?)
  }

and the Promise version would be:

  form.requestAutocomplete().then(function() {
    // differentiate success from cancel/invalid somehow
  }, function() {
    // figure out what failed (maybe arguments[0] is 'disabled'?)
  });


If rAc never throws, I'd imagine the sync code looking like this:

  // this could easily be changed to a boolean like Ian's example
  var result = form.requestAutocomplete();
  if (result == 'success') {
    // 'success' is new and more explicit than an empty failure reason
  } else {
    // determine what happened (is result 'cancel', 'invalid', or 'disabled'?)
  }

and Promise-y would be:

  form.requestAutocomplete().then(function() {
    // maybe arguments[0] is 'success', 'cancel', 'invalid', or 'disabled'?
  });


The second alternative is better, IMO, but I'd prefer more flexibility with what is passed to the resolver (if we ever need to provide more than just a reason).


tl;dr - I'd still use the event-based API on my site as rAc's majority case is a valid failure (cancel).
Comment 11 Ian 'Hixie' Hickson 2014-09-22 16:26:26 UTC
Maybe we should just not use a promise here.
Comment 12 Domenic Denicola 2014-09-22 17:58:46 UTC
Promises are perfect for requestAutocomplete. I find it baffling that this is a point of contention at all. It does seem to stem from some misunderstanding of promises as "fancy callbacks" or not understanding what they give you over events.

In terms of design: exceptional cases (i.e. cases which the author often does not want to write code to handle, but instead wants to allow to bubble up through the async call stack) should reject. So rejecting on insecure page/within <iframe>/without user gesture sounds reasonable to me.

Concrete design as requested in comment #1:

- requestAutocomplete returns a promise for a boolean. The promise is resolved with true if the autocomplete succeeds, and false if the user cancels. If the autocomplete fails for another reason, the promise is rejected.
- For the rejection, either:
  - AutocompleteError subclasses DOMException with reason \in { "insecure origin", "within iframe", "no user gesture", "invalid form" }.
  - You mint several new .name values for DOMException corresponding to each of those cases.
Comment 13 Ian 'Hixie' Hickson 2014-09-23 18:21:40 UTC
If you do that then you end up with error-checking logic in both paths. That doesn't make much sense — it would make the current event API more usable.
Comment 14 Domenic Denicola 2014-09-23 18:27:09 UTC
(In reply to Ian 'Hixie' Hickson from comment #13)
> If you do that then you end up with error-checking logic in both paths. That
> doesn't make much sense — it would make the current event API more usable.

I attempted to divide the "errors" into "things you want to handle" (cancel) and "things you don't want to handle but instead let bubble up through the async scope chain" (environmental factors outside your control). Thus you would not even include the error-checking logic in most cases, instead letting it bubble.

Do you think my division is incorrect?
Comment 15 Ian 'Hixie' Hickson 2014-09-24 17:07:59 UTC
Why would you not want to handle the case of the browser not showing the dialog? That would leave your page unusable.
Comment 16 Evan Stade 2014-09-24 21:51:38 UTC
> Why would you not want to handle the case of the browser not showing the
> dialog? That would leave your page unusable.

I agree you should handle the case of the browser not showing the dialog because it's spec'd as "the dialog may fail to show for any old reason". That said, Chrome aims to always show the dialog, although what exactly is inside the dialog may depend on user settings. Even if they've disabled Autofill, for example, the dialog still appears; they just have to type in everything afresh and it won't be saved. Of course, the dialog may not appear on an http:// site, may not appear if you don't request the right kind of fields, etc., but that's a programming error as opposed to a result of a user setting or state.
Comment 17 Ian 'Hixie' Hickson 2014-09-25 17:22:18 UTC
Since we allow the browser to decide what fields it supports, not showing it because the fields aren't supported it not caused by a programmatic error on the author's behalf.
Comment 18 Brian Nicholson 2014-09-29 20:45:02 UTC
(In reply to Ian 'Hixie' Hickson from comment #15)
> Why would you not want to handle the case of the browser not showing the
> dialog? That would leave your page unusable.

I think that what Dominic was suggesting is that rather than having the rAc error checking intimately tied to requestAutocomplete() call, authors may want errors to bubble so they can be handled at a more general level. This is one of features that make promises so compelling to begin with: they can be used seamlessly with exceptions and other promise-based APIs.

Consider the following (using ES6 generators, allowing for linear asynchronous code):

function* handleCheckout() {
  try {
    // synchronous; may throw exception
    mySite.ensureCartNotEmpty();

    // async; rejects if items not in stock
    yield mySite.ensureItemsInStock();

    // async; rejects on error and returns false on cancel
    var success = yield form.requestAutocomplete();
    if (success) {
      form.submit();
    }
  } catch (e) {
    console.log("An error has occurred during checkout");
    if (e instanceof AutocompleteError) {
      mySite.showStandardForm();
    }
    mySite.sendErrorToServer(e);
  }
}
Comment 19 Ian 'Hixie' Hickson 2014-09-30 02:04:42 UTC
That could work...
Comment 20 Dan Beam 2014-10-02 00:15:47 UTC
Would we need to change requestAutocomplete to throw when it rejects (it doesn't currently)?  I assume comment 18 implies this, but just double-checking.

Because rAc can fail for reasons unknown to the page (decided by the user agent[1]), all rAc calls would need a try {...} catch(e) {...} wrapper.  This is kind of arduous...

Also relevant: http://lists.w3.org/Archives/Public/public-whatwg-archive/2014Oct/0011.html

[1] https://html.spec.whatwg.org/#dom-form-requestautocomplete step #2
(i.e. "the user agent does not support this form's fields", "the user has disabled this feature for this form's node document's origin")
Comment 21 Matthew Noorenberghe 2014-10-02 00:45:48 UTC
(In reply to Dan Beam from comment #20)
> Would we need to change requestAutocomplete to throw when it rejects (it
> doesn't currently)?  I assume comment 18 implies this, but just
> double-checking.

I don't believe comment 18 implies this as my understanding is that a promise rejection gets converted into an exception when one uses yield on the promise.
Comment 22 Dan Beam 2014-10-02 00:49:04 UTC
(In reply to Matthew Noorenberghe from comment #21)
> (In reply to Dan Beam from comment #20)
> > Would we need to change requestAutocomplete to throw when it rejects (it
> > doesn't currently)?  I assume comment 18 implies this, but just
> > double-checking.
> 
> I don't believe comment 18 implies this as my understanding is that a
> promise rejection gets converted into an exception when one uses yield on
> the promise.

Right, I meant for the synchronous cases (e.g. http:, no user gesture, from frame, etc.).
Comment 23 Brian Nicholson 2014-10-03 20:25:18 UTC
(In reply to Dan Beam from comment #22)
> Right, I meant for the synchronous cases (e.g. http:, no user gesture, from
> frame, etc.).

Why does it matter if these are synchronous or asynchronous? If requestAutocomplete() returned a promise, it would be rejected in either case to indicate the error, and it should make no difference to clients using the promise.
Comment 24 Dan Beam 2014-10-04 03:22:42 UTC
(In reply to Brian Nicholson from comment #23)
> (In reply to Dan Beam from comment #22)
> > Right, I meant for the synchronous cases (e.g. http:, no user gesture, from
> > frame, etc.).
> 
> Why does it matter if these are synchronous or asynchronous? If
> requestAutocomplete() returned a promise, it would be rejected in either
> case to indicate the error, and it should make no difference to clients
> using the promise.

I'm talking about the Promise-less API.

As I understand it, Promise semantics are:
 - reject if the code would throw
 - otherwise resolve with what the code would return

Chrome's implementation knows enough to throw in some cases it'd reject, e.g.,

  try {
    // if the page is insecure, not processing a user gesture, form.autocomplete == "off", or in
    // a document fragment, throw right now as the promise will be rejected later.
    form.requestAutocomplete();
  } catch(e) {
    // examine e.reason
  }

I assume we *don't* want to do this, right?  Am I misunderstanding how Promise semantics?
Comment 25 Dan Beam 2014-10-04 03:24:21 UTC
(In reply to Dan Beam from comment #24)
> I assume we *don't* want to do this, right?  Am I misunderstanding how
> Promise semantics?

misunderstanding Promise semantics**
Comment 26 Anne 2014-10-04 07:32:47 UTC
Dan, you're correct. From the author of the promises specification: http://domenic.me/2012/10/14/youre-missing-the-point-of-promises/
Comment 27 Brian Nicholson 2014-10-08 00:31:56 UTC
(In reply to Dan Beam from comment #24)
> I'm talking about the Promise-less API.

Apologize for the confusion earlier. Let's back up a bit...

By promise-less API, you're talking about the existing API, correct? As in, once this bug lands, the promise-less API will no longer exist, and since there's little value in changing the existing API in the interim, I assume your question is entirely academic?

Going on this assumption, I understand your argument as the following:
1) There is a 1:1 conversion between synchronous exceptions and promise rejections.
2) The existing API doesn't (and shouldn't) throw exceptions.
3) Therefore, we should not use promises.

My take: given an API with only synchronous failures, I do think that API should throw exceptions for exceptional situations. However, rAc also has asynchronous failures. There isn't a direct conversion from the existing API to promises because the existing API is partly async. I think an error callback was the right call for the sake of consistency; it's not that exceptions don't fit, but rather that we simply can't throw exceptions for async operations.

Of course, that's changing with the new generator syntax I used above, and why I feel promises are an improvement to the existing API.

(In reply to Anne from comment #26)
> Dan, you're correct. From the author of the promises specification:
> http://domenic.me/2012/10/14/youre-missing-the-point-of-promises/

Note that Domenic, the author of that article, is the same person who proposed this new API back in comment 12 :)
Comment 28 Domenic Denicola 2014-10-09 18:44:00 UTC
I think things got a little confused about sync vs. async. When we talked about "if it was sync," that was meant as an analogy so that people got the right frame of mind for how to translate the API to promises. But somehow that got spun into saying that rAc should have sync behavior like throwing.

To be clear, it definitely should not: rAc should always reject and never throw. When a function becomes async (i.e. promise-returning), it switches from returning and throwing as a paradigm to fulfilling and rejecting its returned promise. So yeah, lack of user gesture etc. should reject, not throw.
Comment 29 Ian 'Hixie' Hickson 2014-10-15 00:19:24 UTC
So the problem with comment 18's suggestion is it doesn't work when you combine promises. You can't take this promise, combine it with another promise for "network is up again", and have it submit the form, because you don't know if the autocomplete promise in this scenario was canceled or not.
Comment 30 Anne 2016-03-28 13:17:54 UTC
1. Is this still desired functionality?
2. Will the existence of cancelable promises influence the design?
Comment 31 Domenic Denicola 2016-06-16 15:50:59 UTC
requestAutocomplete is gone