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
  • perform a security check
  • platform object
  • global environment associated with a platform object
  • +
  • callback context
  • frozen array
  • read only (when applied to arrays)
  • callback this value @@ -3028,7 +3029,6 @@ a.setAttribute('href', 'http://example.com/'); // change the content attribute d
  • The EnqueueJob abstract operation
  • The FunctionCreate abstract operation
  • The Get abstract operation
  • -
  • The GetActiveScriptOrModule abstract operation
  • The HasOwnProperty abstract operation
  • The HostEnsureCanCompileStrings abstract operation
  • The HostPromiseRejectionTracker abstract operation
  • @@ -86682,8 +86682,8 @@ interface NavigatorOnLine { -

    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:

      @@ -86696,21 +86696,20 @@ interface NavigatorOnLine {
    -

    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:

    1. Assert: settings's realm execution context is the running JavaScript execution context.

    2. -
    3. Decrement settings's realm execution context's entrance - counter by one.

    4. -
    5. Remove settings's realm execution context from the JavaScript execution context stack.

    6. +
    7. Decrement settings's realm execution context's entrance + counter by one.

    8. +
    9. If the JavaScript execution context stack is now empty, run the global script clean-up jobs. (These cannot run scripts.)

    10. @@ -86744,7 +86743,7 @@ interface NavigatorOnLine { JavaScript realm.

      In this specification, all JavaScript - realms are initialized with are initialised with global objects that are either Window or WorkerGlobalScope objects.

      @@ -86791,7 +86790,8 @@ interface NavigatorOnLine { the function or script that the user agent called into when it called into author code.
      Incumbent
      -
      This corresponds to the most-recently-entered author function or script on the stack.
      +
      This corresponds to the most-recently-entered author function or script on the stack, or the + author function or script that originally scheduled the currently-running callback.
      Current
      This corresponds to the currently-running function object, including built-in user-agent @@ -86964,16 +86964,96 @@ interface NavigatorOnLine {
      Incumbent
      -

      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:

      + +
        +
      1. Push settings onto the backup incumbent settings object + stack.

      2. + +
      3. Let context be the topmost script-having execution + context.

      4. + +
      5. If context is not null, increment context's + skip-when-determining-incumbent counter.

      6. +
      + +

      To clean up after running a callback with an environment settings object + settings:

      + +
        +
      1. +

        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.

        +
      2. + +
      3. If context is not null, decrement context's + skip-when-determining-incumbent counter.

      4. + +
      5. Assert: the topmost entry of the backup incumbent settings object stack is + settings.

      6. + +
      7. Remove settings from the backup incumbent settings object + stack.

      8. +
      + +

      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:

        -
      1. Let scriptOrModule be the result of JavaScript's GetActiveScriptOrModule() abstract - operation.

      2. -
      3. If scriptOrModule is null, abort these steps; there is no - incumbent settings object.

      4. -
      5. Return the settings object of the script in - scriptOrModule's [[HostDefined]] field.

      6. +
      7. Let context be the topmost script-having execution + context.

      8. + +
      9. +

        If context is null, or if context's + skip-when-determining-incumbent counter is greater than zero, then:

        + +
          +
        1. +

          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.

          +
        2. + +
        3. Return the topmost entry of the backup incumbent settings object + stack.

        4. +
        +
      10. + +
      11. 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.

      +
      +
      Current

      The JavaScript specification defines the current Realm Record, sometimes @@ -87103,19 +87338,39 @@ interface NavigatorOnLine {

      1. Assert: queueName is "PromiseJobs". ("ScriptJobs" must not be used by user agents.)

      2. -
      3. Let settings be the settings - object of job.[[Realm]].

      4. +
      5. -

        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.

        +
      6. + +
      7. Let incumbent settings be the incumbent settings object.

      8. + +
      9. +

        Queue a microtask, on job settings's responsible event loop, to perform the following steps:

          -
        1. Check if we can run script with settings. If this returns "do - not run" then abort these steps.

        2. -
        3. Prepare to run script with settings.

        4. +
        5. Check if we can run script with job settings. If this returns + "do not run" then abort these steps.

        6. + +
        7. Prepare to run script with job settings.

        8. + +
        9. Prepare to run a callback with incumbent settings.

        10. +
        11. Let result be the result of performing the abstract operation specified by job, using the elements of arguments as its arguments.

        12. -
        13. Clean up after running script with settings.

        14. + +
        15. Clean up after running a callback with incumbent + settings.

        16. + +
        17. Clean up after running script with job settings.

        18. +
        19. If result is an abrupt completion, report the exception given by result.[[Value]].