diff --git a/source b/source index 3b4afdeec14..c31bc70f7e2 100644 --- a/source +++ b/source @@ -2896,6 +2896,7 @@ a.setAttribute('href', 'http://example.com/'); // change the content attribute d
The steps to prepare to run script with - an environment settings object settings are as follows:
+The steps to prepare to run script with an environment settings object + settings are as follows:
The steps to clean up after running - script with an environment settings object settings are as - follows:
+The steps to clean up after running script with an environment settings + object settings are as follows:
Assert: settings's realm execution context is the running JavaScript execution context.
Decrement settings's realm execution context's entrance - counter by one.
Remove settings's realm execution context from the JavaScript execution context stack.
Decrement settings's realm execution context's entrance + counter by one.
If the JavaScript execution context stack is now empty, run the global script clean-up jobs. (These cannot run scripts.)
In this specification, all JavaScript
- realms are initialized with are initialised with global objects that are either Window
or
WorkerGlobalScope
objects.
The incumbent settings object is determined as follows:
+All JavaScript execution contexts must + contain, as part of their code evaluation state, a skip-when-determining-incumbent + counter value, which is initially zero. In the process of preparing to run a callback and cleaning up after running a callback, this value will be incremented and + decremented.
+ +Every event loop has an associated backup incumbent settings object + stack, initially empty. Roughly speaking, it is used to determine the incumbent + settings object when no author code is on the stack, but author code is responsible for the + current algorithm having been run in some way. The process of preparing to run a callback and cleaning up after running a callback manipulate this stack.
+ +When Web IDL is used to invoke author + code, or when EnqueueJob invokes a promise job, they use the following algorithms to + track relevant data for determining the incumbent settings object:
+ +To prepare to run a callback with an environment settings object + settings:
+ +Push settings onto the backup incumbent settings object + stack.
Let context be the topmost script-having execution + context.
If context is not null, increment context's + skip-when-determining-incumbent counter.
To clean up after running a callback with an environment settings object + settings:
+ +Let context be the topmost script-having execution context.
+ +This will be the same as the topmost script-having execution + context inside the corresponding invocation of prepare to run a + callback.
+If context is not null, decrement context's + skip-when-determining-incumbent counter.
Assert: the topmost entry of the backup incumbent settings object stack is + settings.
Remove settings from the backup incumbent settings object + stack.
Here, the topmost script-having execution context is the topmost entry of the + JavaScript execution context stack that has a non-null ScriptOrModule component, or + null if there is no such entry in the JavaScript execution context stack.
+ +With all this in place, the incumbent settings object is determined as follows:
Let scriptOrModule be the result of JavaScript's GetActiveScriptOrModule() abstract - operation.
If scriptOrModule is null, abort these steps; there is no - incumbent settings object.
Return the settings object of the script in - scriptOrModule's [[HostDefined]] field.
Let context be the topmost script-having execution + context.
If context is null, or if context's + skip-when-determining-incumbent counter is greater than zero, then:
+ +Assert: the backup incumbent settings object stack is not empty.
+ +This assert would fail if you try to obtain the incumbent settings + object from inside an algorithm that was triggered neither by calling scripts nor by Web IDL invoking a callback. For example, it would + trigger if you tried to obtain the incumbent settings object inside an algorithm + that ran periodically as part of the event loop, with no involvement of author + code. In such cases the incumbent concept + cannot be used.
+Return the topmost entry of the backup incumbent settings object + stack.
Return context's Realm component's settings obect.
Then, the incumbent Realm is the NavigatorOnLine { global object of the incumbent settings object.
+The following series of examples is intended to make it clear how all of the different + mechanisms contribute to the definition of the incumbent concept:
+ +Consider the following very simple example:
+ +<!DOCTYPE html> +<iframe></iframe> +<script> + new frames[0].Worker('worker.js'); +</script>+ +
When the Worker()
constructor looks up the incumbent
+ settings object to use for various parts of its algorithm, the topmost script-having
+ execution context will be that corresponding to the script
element: it was
+ pushed onto the JavaScript execution context stack as part of ScriptEvaluation during the run a classic script
+ algorithm. Since there are no Web IDL callback invocations involved, the context's
+ skip-when-determining-incumbent counter is zero, so it is used to determine the
+ incumbent settings object; the result is the environment settings
+ object of window
.
(In this example, the environment settings object of frames[0]
is not involved at all. It is the current settings
+ object, but the Worker()
constructor cares only about the
+ incumbent, not current.)
Consider the following more complicated example:
+ +<!DOCTYPE html> +<iframe></iframe> +<script> + const bound = frames[0].postMessage.bind(frames[0], "some data", "*"); + window.setTimeout(bound); +</script>+ +
There are two interesting environment settings
+ objects here: that of window
, and that of frames[0]
. Our concern is: what is the incumbent settings object at
+ the time that the algorithm for postMessage()
+ executes?
It should be that of window
, to capture the intuitive notion that the
+ author script responsible for causing the algorithm to happen is executing in window
, not frames[0]
. Another way of capturing the
+ intuition here is that invoking algorithms asynchronously (in this case via setTimeout()
) should not change the incumbent concept.
Let us now explain how the steps given above give us our intuitively-desired result of window
's relevant settings object.
When bound
is converted to a
+ Web IDL callback type, the incumbent settings object is that corresponding to window
(in the same manner as in our simple example above). Web IDL stores this
+ as the resulting callback value's callback context.
When the task posted by setTimeout()
executes, the algorithm for that task uses Web IDL to
+ invoke the stored callback value. Web IDL in
+ turn calls the above prepare to run a callback algorithm. This pushes the stored
+ callback context onto the backup incumbent settings object stack. At
+ this time (inside the timer task) there is no author code on the stack, so the topmost
+ script-having execution context is null, and nothing gets its
+ skip-when-determining-incumbent counter incremented.
Invoking the callback then calls bound
, which in turn calls
+ the postMessage()
method of frames[0]
. When the postMessage()
+ algorithm looks up the incumbent settings object, there is still no author code on
+ the stack, since the bound function just directly calls the built-in method. So the
+ topmost script-having execution context will be null: the JavaScript execution
+ context stack only contains an execution context corresponding to postMessage()
, with no ScriptEvaluation context or similar below it.
This is where we fall back to the backup incumbent settings object stack. As
+ noted above, it will contain as its topmost entry the relevant settings object of
+ window
. So that is what is used as the incumbent settings
+ object while executing the postMessage()
+ algorithm.
Consider this final, even more convoluted example:
+ +<!-- a.html --> +<!DOCTYPE html> +<button>click me</button> +<iframe></iframe> +<script> +const bound = frames[0].location.assign.bind(frames[0].location, "https://example.com/"); +document.querySelector("button").addEventListener("click", bound); +</script> + +<!-- b.html --> +<!DOCTYPE html> +<iframe src="a.html"></iframe> +<script> + const iframe = document.querySelector("iframe"); + iframe.onload = function onLoad() { + iframe.contentWindow.document.querySelector("button").click(); + }; +</script>+ +
Again there are two interesting environment
+ settings objects in play: that of a.html
, and that of b.html
. When the location.assign()
+ method triggers the Location
-object navigate algorithm, what will be
+ the incumbent settings object? As before, it should intuitively be that of a.html
: the click
listener was originally
+ scheduled by a.html
, so even if something involving b.html
causes the listener to fire, the incumbent responsible is that of a.html
.
The callback setup is similar to the previous example: when bound
is
+ converted to a Web IDL callback type, the
+ incumbent settings object is that corresponding to a.html
,
+ which is stored as the callback's callback context.
When the click()
method is called inside b.html
, it dispatches a click
event on the button that is inside a.html
. This time, when the prepare to run a callback algorithm
+ executes as part of event dispatch, there is author code on the stack; the topmost
+ script-having execution context is that of the onLoad
function,
+ whose skip-when-determining-incumbent counter gets incremented. Additionally, a.html
's environment settings object (stored as the
+ EventHandler
's callback context) is pushed onto the
+ backup incumbent settings object stack.
Now, when the Location
-object navigate algorithm looks up the
+ incumbent settings object, the topmost script-having execution
+ context is still that of the onLoad
function (due to the fact we
+ are using a bound function as the callback). Its skip-when-determining-incumbent
+ counter value is one, however, so we fall back to the backup incumbent settings
+ object stack. This gives us the environment settings object of a.html
, as expected.
Note that this means that even though it is the iframe
inside a.html
that navigates, it is a.html
itself that is used
+ as the source browsing context, which determines among other things the request client. This is perhaps the only justifiable use
+ of the incumbent concept on the web platform; in all other cases the consequences of using it
+ are simply confusing and we hope to one day switch them to use current or relevant as
+ appropriate.
The JavaScript specification defines the current Realm Record, sometimes @@ -87103,19 +87338,39 @@ interface NavigatorOnLine {
Assert: queueName is "PromiseJobs"
. ("ScriptJobs"
must not be used by user agents.)
Let settings be the settings - object of job.[[Realm]].
Queue a microtask, on settings's responsible event
+ Let job settings be some appropriate environment settings object. It is not yet clear how to specify the environment settings
+ object that should be used here. In practice, this means that the entry concept is not correctly specified while executing a job. See
+ discussion in issue
+ #1189.
Let incumbent settings be the incumbent settings object.
Queue a microtask, on job settings's responsible event loop, to perform the following steps:
Check if we can run script with settings. If this returns "do - not run" then abort these steps.
Prepare to run script with settings.
Check if we can run script with job settings. If this returns + "do not run" then abort these steps.
Prepare to run script with job settings.
Prepare to run a callback with incumbent settings.
Let result be the result of performing the abstract operation specified by job, using the elements of arguments as its arguments.
Clean up after running script with settings.
Clean up after running a callback with incumbent + settings.
Clean up after running script with job settings.
If result is an abrupt completion, report the exception given by result.[[Value]].