webidl
webidl copied to clipboard
Promise handling algorithms are not very clear about what happens if type conversion fails
https://heycam.github.io/webidl/#dfn-perform-steps-once-promise-is-settled
What if the type is Promise<Node>
and someone passes Promise.resolve(3)
?
I think the best we can do is go down the rejection path? Although that makes using "upon fulfillment" in isolation pretty dangerous for non-any
_T_s.
I am curious if implementations actually pay attention to the T at all... Do they have counterpart algorithms?
Gecko pays attention to the T in the following ways:
- IDL parse fails if
Promise<T>
is the return type of an operation or attribute but T is not exposed in all the globals where the operation or attribute is exposed. There is no corresponding requirement in the spec, as far as I can tell (also for the situation where some type is returned directly, without Promise being involved), but fundamentally it doesn't make sense to have this setup. - Something involving which C++ headers to include in generated binding files. I'm not even convinced that code needs to be there.
- The equality operator on types in the IDL parser treats
Promise<T>
as equal toPromise<U>
if and only ifT == U
. This equality operator is used when coalescing identical unions across IDL files. It could probably ignore the parameter type and just treat appPromise
as equal in practice...
There are no other places that are aware of the T in Gecko as far as I can tell.
How does respondWith()
work in Gecko? Is there a manual type check?
In the spec as written, it'll return a rejected promise and the success steps won't be run.
Let's take a look at what APIs take a Promise argument anyway:
-
ExtendableEvent.waitUntil
: takesPromise<any>
-
FetchEvent.respondWith
: takesPromise<Response>
; if the conversion fails, it wants to do the same as with a rejected promise. -
WebAssembly.compileStreaming
andWebAssembly.instantiateStreaming
takePromise<Response>
and want the currently specced behavior (implemented in Fx, Chrome & Edge per wpt.fyi). Rejection steps just pass the rejection value through. -
MerchantValidationEvent.complete
: takesPromise<any>
-
PaymentRequestUpdateEvent.updateWith
takesPromise<PaymentDetailsUpdate>
; it wants to do nearly the same thing as when it gets a rejected promise, except that it drops the rejection value in a rejected promise in favour of an AbortError, while it passes along the TypeError from the conversion failure.
This suggests to me that we should run the rejection steps on conversion failure.
How does
respondWith()
work in Gecko? Is there a manual type check?
Yes. See https://searchfox.org/mozilla-central/rev/7088fc958db5935eba24b413b1f16d6ab7bd13ea/dom/serviceworkers/ServiceWorkerEvents.cpp#576,588-591,595-597,605-608 (which checks that the resolution value is an object, then checks that it's a Response.
In the spec as written, it'll return a rejected promise and the success steps won't be run.
Ah, I guess because "converting" will throw, and PerformPromiseThen handles it. That makes sense.
This suggests to me that we should run the rejection steps on conversion failure.
Sounds good to me.