This is an archived snapshot of W3C's public bugzilla bug tracker, decommissioned in April 2019. Please see the home page for more details.
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.
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?
I would be happy to implement this for Firefox.
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/
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?
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.)
(see also my comment on bug 25457 comment 17)
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)
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.
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).
Maybe we should just not use a promise here.
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.
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.
(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?
Why would you not want to handle the case of the browser not showing the dialog? That would leave your page unusable.
> 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.
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.
(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); } }
That could work...
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")
(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.
(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.).
(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.
(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?
(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**
Dan, you're correct. From the author of the promises specification: http://domenic.me/2012/10/14/youre-missing-the-point-of-promises/
(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 :)
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.
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.
1. Is this still desired functionality? 2. Will the existence of cancelable promises influence the design?
requestAutocomplete is gone