From 4069869e736771104006f8f00a2eb8b382b57800 Mon Sep 17 00:00:00 2001 From: Shu-yu Guo Date: Tue, 11 Aug 2020 15:35:06 -0700 Subject: [PATCH] Normative: Add machinery to track the incumbent settings object in HTML for Job callbacks (#2086) In HTML, JS functions that are called asynchronously as part of jobs (like Promise microtasks) are generally associated with a piece of state called an incumbent settings object. The incumbent is stored at the time when the callback is first passed to the API that is responsible for scheduling the job (e.g. Promise.then), and restored when the callback is called. For symmetry, all WebIDL callbacks do this: https://heycam.github.io/webidl/#idl-callback-interface This PR adds the necessary host hook machinery to let HTML store and restore the incumbent settings object. The default behavior is nop. --- spec.html | 127 ++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 110 insertions(+), 17 deletions(-) diff --git a/spec.html b/spec.html index 3be98b186e..c05b8ce5bc 100644 --- a/spec.html +++ b/spec.html @@ -7793,6 +7793,91 @@

Jobs and Host Operations to Enqueue Jobs

Particular kinds of Jobs have additional conformance requirements.

+ +

JobCallback Records

+

A JobCallback Record is a Record value used to store a function object and a host-defined value. Function objects that are invoked via a Job enqueued by the host may have additional host-defined context. To propagate the state, Job Abstract Closures should not capture and call function objects directly. Instead, use HostMakeJobCallback and HostCallJobCallback.

+ +

The WHATWG HTML specification (https://html.spec.whatwg.org/), for example, uses the host-defined value to propagate the incumbent settings object for Promise callbacks.

+
+

JobCallback Records have the fields listed in .

+ + + + + + + + + + + + + + + + + + + +
+ Field Name + + Value + + Meaning +
+ [[Callback]] + + A function object + + The function to invoke when the Job is invoked. +
+ [[HostDefined]] + + Any, default value is ~empty~. + + Field reserved for use by hosts. +
+
+
+ + +

HostMakeJobCallback ( _callback_ )

+

HostMakeJobCallback is a host-defined abstract operation that takes argument _callback_ (a function object).

+

The implementation of HostMakeJobCallback must conform to the following requirements:

+ +

The default implementation of HostMakeJobCallback performs the following steps when called:

+ + 1. Assert: IsCallable(_callback_) is *true*. + 1. Return the JobCallback Record { [[Callback]]: _callback_, [[HostDefined]]: ~empty~ }. + +

ECMAScript hosts that are not web browsers must use the default implementation of HostMakeJobCallback.

+ +

This is called at the time that the callback is passed to the function that is responsible for its being eventually scheduled and run. For example, `promise.then(thenAction)` calls MakeJobCallback on `thenAction` at the time of invoking `Promise.prototype.then`, not at the time of scheduling the reaction Job.

+
+
+ + +

HostCallJobCallback ( _jobCallback_, _V_, _argumentsList_ )

+

HostCallJobCallback is a host-defined abstract operation that takes arguments _jobCallback_ (a JobCallback Record), _V_ (an ECMAScript language value), and _argumentsList_ (a List of ECMAScript language values).

+

The implementation of HostCallJobCallback must conform to the following requirements:

+ + +

This requirement means that hosts cannot change the [[Call]] behaviour of function objects defined in this specification.

+
+

The default implementation of HostCallJobCallback performs the following steps when called:

+ + 1. Assert: IsCallable(_jobCallback_.[[Callback]]) is *true*. + 1. Return ? Call(_jobCallback_.[[Callback]], _V_, _argumentsList_). + +

ECMAScript hosts that are not web browsers must use the default implementation of HostCallJobCallback.

+
+

HostEnqueuePromiseJob ( _job_, _realm_ )

HostEnqueuePromiseJob is a host-defined abstract operation that schedules the Job Abstract Closure _job_ to be performed, at some future time. The Abstract Closures used with this algorithm are intended to be related to the handling of Promises, or otherwise, to be scheduled with equal priority to Promise handling operations.

@@ -22797,7 +22882,7 @@

Script Records

[[HostDefined]] - Any, default value is *undefined*. + Any, default value is ~empty~. Field reserved for use by host environments that need to associate additional information with a script. @@ -40578,7 +40663,7 @@

PromiseReaction Records

~Fulfill~ | ~Reject~ - The [[Type]] is used when [[Handler]] is *undefined* to allow for behaviour specific to the settlement type. + The [[Type]] is used when [[Handler]] is ~empty~ to allow for behaviour specific to the settlement type. @@ -40586,10 +40671,10 @@

PromiseReaction Records

[[Handler]] - A function object or *undefined*. + A JobCallback Record | ~empty~. - The function that should be applied to the incoming value, and whose return value will govern what happens to the derived promise. If [[Handler]] is *undefined*, a function that depends on the value of [[Type]] will be used instead. + The function that should be applied to the incoming value, and whose return value will govern what happens to the derived promise. If [[Handler]] is ~empty~, a function that depends on the value of [[Type]] will be used instead. @@ -40651,7 +40736,8 @@

Promise Resolve Functions

1. Let _thenAction_ be _then_.[[Value]]. 1. If IsCallable(_thenAction_) is *false*, then 1. Return FulfillPromise(_promise_, _resolution_). - 1. Let _job_ be NewPromiseResolveThenableJob(_promise_, _resolution_, _thenAction_). + 1. Let _thenJobCallback_ be HostMakeJobCallback(_thenAction_). + 1. Let _job_ be NewPromiseResolveThenableJob(_promise_, _resolution_, _thenJobCallback_). 1. Perform HostEnqueuePromiseJob(_job_.[[Job]], _job_.[[Realm]]). 1. Return *undefined*. @@ -40738,7 +40824,7 @@

RejectPromise ( _promise_, _reason_ )

TriggerPromiseReactions ( _reactions_, _argument_ )

-

The abstract operation TriggerPromiseReactions takes arguments _reactions_ (a collection of PromiseReaction Records) and _argument_. It enqueues a new Job for each record in _reactions_. Each such Job processes the [[Type]] and [[Handler]] of the PromiseReaction Record, and if the [[Handler]] is a function, calls it passing the given argument. If the [[Handler]] is *undefined*, the behaviour is determined by the [[Type]]. It performs the following steps when called:

+

The abstract operation TriggerPromiseReactions takes arguments _reactions_ (a collection of PromiseReaction Records) and _argument_. It enqueues a new Job for each record in _reactions_. Each such Job processes the [[Type]] and [[Handler]] of the PromiseReaction Record, and if the [[Handler]] is not ~empty~, calls it passing the given argument. If the [[Handler]] is ~empty~, the behaviour is determined by the [[Type]]. It performs the following steps when called:

1. For each _reaction_ in _reactions_, in original insertion order, do 1. Let _job_ be NewPromiseReactionJob(_reaction_, _argument_). @@ -40782,12 +40868,12 @@

NewPromiseReactionJob ( _reaction_, _argument_ )

1. Let _promiseCapability_ be _reaction_.[[Capability]]. 1. Let _type_ be _reaction_.[[Type]]. 1. Let _handler_ be _reaction_.[[Handler]]. - 1. If _handler_ is *undefined*, then + 1. If _handler_ is ~empty~, then 1. If _type_ is ~Fulfill~, let _handlerResult_ be NormalCompletion(_argument_). 1. Else, 1. Assert: _type_ is ~Reject~. 1. Let _handlerResult_ be ThrowCompletion(_argument_). - 1. Else, let _handlerResult_ be Call(_handler_, *undefined*, « _argument_ »). + 1. Else, let _handlerResult_ be HostCallJobCallback(_handler_, *undefined*, « _argument_ »). 1. If _promiseCapability_ is *undefined*, then 1. Assert: _handlerResult_ is not an abrupt completion. 1. Return NormalCompletion(~empty~). @@ -40798,8 +40884,8 @@

NewPromiseReactionJob ( _reaction_, _argument_ )

1. Let _status_ be Call(_promiseCapability_.[[Resolve]], *undefined*, « _handlerResult_.[[Value]] »). 1. Return Completion(_status_). 1. Let _handlerRealm_ be *null*. - 1. If _reaction_.[[Handler]] is not *undefined*, then - 1. Let _getHandlerRealmResult_ be GetFunctionRealm(_reaction_.[[Handler]]). + 1. If _reaction_.[[Handler]] is not ~empty~, then + 1. Let _getHandlerRealmResult_ be GetFunctionRealm(_reaction_.[[Handler]].[[Callback]]). 1. If _getHandlerRealmResult_ is a normal completion, then set _handlerRealm_ to _getHandlerRealmResult_.[[Value]]. 1. Else, set _handlerRealm_ to the current Realm Record. 1. NOTE: _handlerRealm_ is never *null* unless the handler is *undefined*. When the handler is a revoked Proxy and no ECMAScript code runs, _handlerRealm_ is used to create error objects. @@ -40813,15 +40899,15 @@

NewPromiseResolveThenableJob ( _promiseToResolve_, _thenable_, _then_ )

1. Let _job_ be a new Job Abstract Closure with no parameters that captures _promiseToResolve_, _thenable_, and _then_ and performs the following steps when called: 1. Let _resolvingFunctions_ be CreateResolvingFunctions(_promiseToResolve_). - 1. Let _thenCallResult_ be Call(_then_, _thenable_, « _resolvingFunctions_.[[Resolve]], _resolvingFunctions_.[[Reject]] »). + 1. Let _thenCallResult_ be HostCallJobCallback(_then_, _thenable_, « _resolvingFunctions_.[[Resolve]], _resolvingFunctions_.[[Reject]] »). 1. If _thenCallResult_ is an abrupt completion, then 1. Let _status_ be Call(_resolvingFunctions_.[[Reject]], *undefined*, « _thenCallResult_.[[Value]] »). 1. Return Completion(_status_). 1. Return Completion(_thenCallResult_). - 1. Let _getThenRealmResult_ be GetFunctionRealm(_then_). + 1. Let _getThenRealmResult_ be GetFunctionRealm(_then_.[[Callback]]). 1. If _getThenRealmResult_ is a normal completion, then let _thenRealm_ be _getThenRealmResult_.[[Value]]. 1. Else, let _thenRealm_ be the current Realm Record. - 1. NOTE: _thenRealm_ is never *null*. When _then_ is a revoked Proxy and no code runs, _thenRealm_ is used to create error objects. + 1. NOTE: _thenRealm_ is never *null*. When _then_.[[Callback]] is a revoked Proxy and no code runs, _thenRealm_ is used to create error objects. 1. Return the Record { [[Job]]: _job_, [[Realm]]: _thenRealm_ }. @@ -41383,11 +41469,15 @@

PerformPromiseThen ( _promise_, _onFulfilled_, _onRejected_ [ , _resultCapab 1. If _resultCapability_ is not present, then 1. Set _resultCapability_ to *undefined*. 1. If IsCallable(_onFulfilled_) is *false*, then - 1. Set _onFulfilled_ to *undefined*. + 1. Let _onFulfilledJobCallback_ be ~empty~. + 1. Else, + 1. Let _onFulfilledJobCallback_ be HostMakeJobCallback(_onFulfilled_). 1. If IsCallable(_onRejected_) is *false*, then - 1. Set _onRejected_ to *undefined*. - 1. Let _fulfillReaction_ be the PromiseReaction { [[Capability]]: _resultCapability_, [[Type]]: ~Fulfill~, [[Handler]]: _onFulfilled_ }. - 1. Let _rejectReaction_ be the PromiseReaction { [[Capability]]: _resultCapability_, [[Type]]: ~Reject~, [[Handler]]: _onRejected_ }. + 1. Let _onRejectedJobCallback_ be ~empty~. + 1. Else, + 1. Let _onRejectedJobCallback_ be HostMakeJobCallback(_onRejected_). + 1. Let _fulfillReaction_ be the PromiseReaction { [[Capability]]: _resultCapability_, [[Type]]: ~Fulfill~, [[Handler]]: _onFulfilledJobCallback_ }. + 1. Let _rejectReaction_ be the PromiseReaction { [[Capability]]: _resultCapability_, [[Type]]: ~Reject~, [[Handler]]: _onRejectedJobCallback_ }. 1. If _promise_.[[PromiseState]] is ~pending~, then 1. Append _fulfillReaction_ as the last element of the List that is _promise_.[[PromiseFulfillReactions]]. 1. Append _rejectReaction_ as the last element of the List that is _promise_.[[PromiseRejectReactions]]. @@ -44262,6 +44352,7 @@

Host Layering Points

See for the definition of host.

Host Hooks

+

HostCallJobCallback(...)

HostEnqueueFinalizationRegistryCleanupJob(...)

HostEnqueuePromiseJob(...)

HostEnsureCanCompileStrings(...)

@@ -44269,6 +44360,7 @@

Host Hooks

HostGetImportMetaProperties(...)

HostHasSourceTextAvailable(...)

HostImportModuleDynamically(...)

+

HostMakeJobCallback(...)

HostPromiseRejectionTracker(...)

HostResolveImportedModule(...)

InitializeHostDefinedRealm(...)

@@ -44278,6 +44370,7 @@

Host-defined Fields

[[HostDefined]] on Realm Records: See .

[[HostDefined]] on Script Records: See .

[[HostDefined]] on Module Records: See .

+

[[HostDefined]] on JobCallback Records: See .

[[HostSynchronizesWith]] on Candidate Executions: See .

[[IsHTMLDDA]]: See .