From 49f9e1eabcb5376d66d444cf5e2bb95700d5ee3c Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Tue, 2 May 2023 15:22:42 +0300 Subject: [PATCH 001/102] Deferred fetching Add a JS-exposed function to request a deferred fetch. A deferred fetch would be invoked in one of two scenarios: - The document is destroyed (the fetch group is terminated) - The document is backgrounded (the fetch group is deactivated) and not restored after a certain time. A few constraints: - Deferred fetch body sizes are limited to 64KB per origin. Exceeding this would immediately reject with a QuotaExceeded. - Request body streams are not allowed. A request body, if exists, has to be a byte sequence. The JS function is called `requestDeferredFetch` but that's bikesheddable. See https://github.com/WICG/pending-beacon/issues/70 --- fetch.bs | 215 +++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 208 insertions(+), 7 deletions(-) diff --git a/fetch.bs b/fetch.bs index 4b1f0c2ca..89e939e20 100644 --- a/fetch.bs +++ b/fetch.bs @@ -2731,6 +2731,9 @@ functionality.

A fetch group holds an ordered list of fetch records. +

A fetch group holds an ordered list of +deferred fetch records. +

A fetch record has an associated request (a request). @@ -2739,16 +2742,103 @@ functionality. controller (a fetch controller or null). +

A deferred fetch record is a struct used to maintain state needed +to invoke a fetch at a later time, e.g. when a Document is unloaded or backgrounded. It +has the following items: + +

+
request +
A request + +
background timeout (default null) +
Null or a duration + +
pending steps (default null) +
invoked callback (default null) +
Null or an algortihm accepting nothing + +
invoked (default false) +
A boolean +
+ +
-

When a fetch group is -terminated, for each associated -fetch record whose fetch record's -controller is non-null, and whose request's -done flag is unset or keepalive is false, -terminate the fetch record's -controller. +

When a fetch group fetchGroup is +terminated: + +

    +
  1. +

    For each deferred fetch record + deferredRecord in fetchGroup's + deferred fetch records whose invoked is + false: + +

      +
    1. If deferredRecord's pending steps is not + null then abort deferredRecord's + pending steps. + +

    2. fetch deferredRecord's request. +

    + +
  2. For each associated fetch record record, + if record's controller is non-null and + record's request's done flag is unset or + keepalive is false, terminate record's + controller. +

+ +

When a fetch group fetchGroup is +activated: +for each deferred fetch record deferredRecord in +fetchGroup's deferred fetch records: + +

    +
  1. +

    If deferredRecord's invoked is true then: +

      +
    1. If deferredRecord's invoked callback is not + null then call deferredRecord's invoked callback. + +

    2. Remove deferredRecord from fetchGroup's + deferred fetch records. +

    + +
  2. Otherwise, if deferredRecord's + pending steps is not null, then abort + deferredRecord's pending steps and set + deferredRecord's pending steps to null. +

+ +

When a fetch group fetchGroup is +deactivated: + +

    +
  1. +

    For each deferred fetch record deferredRecord in + fetchGroup's deferred fetch records whose + background timeout is not null: set deferredRecord's + pending steps to running the following steps in parallel: + +

      +
    1. Wait until deferredRecord's + background timeout have passed. + +

    2. +

      Queue a fetch task to run the following steps with + request's client's + global object: +

        +
      1. Fetch record's request. + +

      2. Set deferredRecord invoked to true. +

      +
    3. +
    +
  2. +

Resolving domains

@@ -8633,6 +8723,117 @@ fetch("https://www.example.com/") +

Deferred fetching

+ +

Deferred fetches allow callers to request that a fetch is invoked at the latest possible moment, +when a fetch group is terminated, or after a timeout after it is +deactivated. + +

Requesting a deferred fetch

+ +
+

To request a deferred fetch given a +request request and a null-or-{{DOMHighResTimeStamp}} +backgroundTimeout (default null): + +

    +
  1. Assert: request's client is an + environment settings object. + +

  2. Let totalScheduledDeferredBytesForOrigin be 0. + +
  3. +

    If request's body is not null then: + +

      +
    1. If request's + body's length is null, then throw a {{TypeError}}. + +

    2. Set totalScheduledDeferredBytesForOrigin to request's + body's length. +

    +
  4. + +
  5. For each deferred fetch record deferredRecord in + request's client's fetch group's + deferred fetch records: if deferredRecord's + request's body is not null and + deferredRecord's request's URL's + origin is same origin with request's URL's + origin, then increment totalScheduledDeferredBytesForOrigin by + deferredRecord's request's body's + length. + +

  6. If totalScheduledDeferredBytesForOrigin is greater than 64 kilobytes, then + throw a {{QuotaExceededError}}. + +

  7. Let deferredRecord be a new deferred fetch record whose + request is request. + +

  8. Set deferredRecord's background timeout to + backgroundTimeout. + +

  9. Append deferredRecord to request's + client's fetch group's + deferred fetch records. + +

  10. Return deferredRecord. +

+
+ +

RequestDeferredFetch method

+ +
+
+dictionary DeferredRequestInit : RequestInit {
+  DOMHighResTimeStamp? backgroundTimeout;
+};
+
+partial interface mixin WindowOrWorkerGlobalScope {
+  [NewObject] Promise<Response> requestDeferredFetch(RequestInfo input, optional DeferredRequestInit init = {});
+};
+
+ +
+

The +requestDeferredFetch(input, init) +method steps are: + +

    +
  1. Let promise be a new promise. + +

  2. Let requestObject be the result of invoking the initial value of {{Request}} as + constructor with input and init as arguments. If that threw an exception, + reject promise with that exception and return promise. + +

  3. If requestObject's signal is aborted, + then reject promise with requestObject's + signal's abort reason and return promise. + +

  4. Let request be requestObject's request. + +

  5. Let backgroundTimeout be null. + +

  6. If init is given and init["backgroundTimeout"] + exists then set backgroundTimeout to + init["backgroundTimeout"]. + +

  7. If backgroundTimeout is not a {{DOMHighResTimeStamp}} then throw a {{TypeError}}. + +

  8. Let deferredRecord be the result of calling + request a deferred fetch given request and backgroundTimeout. If that + threw an exception, reject promise with that exception and return + promise. + +

  9. Set deferredRecord's invoke callback to + resolve promise. + +

  10. Add the following abort steps to requestObject's + signal: remove deferredRecord from + request's client's fetch group's + deferred fetch records. +

+

data: URLs

For an informative description of data: URLs, see RFC 2397. This section replaces From a9d0b245f633fb01cc869c736428e7ee5515ebec Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Thu, 4 May 2023 12:50:47 +0300 Subject: [PATCH 002/102] nits --- fetch.bs | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/fetch.bs b/fetch.bs index 89e939e20..fcdf321d5 100644 --- a/fetch.bs +++ b/fetch.bs @@ -2750,7 +2750,7 @@ has the following items:

request
A request -
background timeout (default null) +
inactive timeout (default null)
Null or a duration
pending steps (default null) @@ -2818,12 +2818,12 @@ has the following items:
  • For each deferred fetch record deferredRecord in fetchGroup's deferred fetch records whose - background timeout is not null: set deferredRecord's + inactive timeout is not null: set deferredRecord's pending steps to running the following steps in parallel:

    1. Wait until deferredRecord's - background timeout have passed. + inactive timeout have passed.

    2. Queue a fetch task to run the following steps with @@ -8734,7 +8734,7 @@ when a fetch group is terminated, or after a tim

      To request a deferred fetch given a request request and a null-or-{{DOMHighResTimeStamp}} -backgroundTimeout (default null): +inactiveTimeout (default null):

      1. Assert: request's client is an @@ -8770,8 +8770,8 @@ when a fetch group is terminated, or after a tim

      2. Let deferredRecord be a new deferred fetch record whose request is request. -

      3. Set deferredRecord's background timeout to - backgroundTimeout. +

      4. Set deferredRecord's inactive timeout to + inactiveTimeout.

      5. Append deferredRecord to request's client's fetch group's @@ -8786,7 +8786,7 @@ when a fetch group is terminated, or after a tim

         
         dictionary DeferredRequestInit : RequestInit {
        -  DOMHighResTimeStamp? backgroundTimeout;
        +  DOMHighResTimeStamp? inactiveTimeout;
         };
         
         partial interface mixin WindowOrWorkerGlobalScope {
        @@ -8812,16 +8812,16 @@ method steps are:
         
          
      6. Let request be requestObject's request. -

      7. Let backgroundTimeout be null. +

      8. Let inactiveTimeout be null. -

      9. If init is given and init["backgroundTimeout"] - exists then set backgroundTimeout to - init["backgroundTimeout"]. +

      10. If init is given and init["inactiveTimeout"] + exists then set inactiveTimeout to + init["inactiveTimeout"]. -

      11. If backgroundTimeout is not a {{DOMHighResTimeStamp}} then throw a {{TypeError}}. +

      12. If inactiveTimeout is not a {{DOMHighResTimeStamp}} then throw a {{TypeError}}.

      13. Let deferredRecord be the result of calling - request a deferred fetch given request and backgroundTimeout. If that + request a deferred fetch given request and inactiveTimeout. If that threw an exception, reject promise with that exception and return promise. From 3eb155d123ffaf86c4606071becf35be9e1c5a10 Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Mon, 8 May 2023 13:50:34 +0300 Subject: [PATCH 003/102] Update fetch.bs Co-authored-by: Anne van Kesteren --- fetch.bs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fetch.bs b/fetch.bs index fcdf321d5..dab4aa72f 100644 --- a/fetch.bs +++ b/fetch.bs @@ -2743,7 +2743,7 @@ functionality. fetch controller or null).

        A deferred fetch record is a struct used to maintain state needed -to invoke a fetch at a later time, e.g. when a Document is unloaded or backgrounded. It +to invoke a fetch at a later time, e.g., when a Document is unloaded or backgrounded. It has the following items:

        From f78e5e052374b1e8f1b8b1f2587b2f76affe00d5 Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Mon, 8 May 2023 14:00:32 +0300 Subject: [PATCH 004/102] Move sections around --- fetch.bs | 223 +++++++++++++++++++++++++++---------------------------- 1 file changed, 111 insertions(+), 112 deletions(-) diff --git a/fetch.bs b/fetch.bs index dab4aa72f..0b860e878 100644 --- a/fetch.bs +++ b/fetch.bs @@ -6804,6 +6804,62 @@ agent's CORS-preflight cache for which there is a cache entry match
      +

      Deferred fetching

      + +

      Deferred fetches allow callers to request that a fetch is invoked at the latest possible moment, +when a fetch group is terminated, or after a timeout after it is +deactivated. + +

      +

      To request a deferred fetch given a +request request and a null-or-{{DOMHighResTimeStamp}} +inactiveTimeout (default null): + +

        +
      1. Assert: request's client is an + environment settings object. + +

      2. Let totalScheduledDeferredBytesForOrigin be 0. + +
      3. +

        If request's body is not null then: + +

          +
        1. If request's + body's length is null, then throw a {{TypeError}}. + +

        2. Set totalScheduledDeferredBytesForOrigin to request's + body's length. +

        +
      4. + +
      5. For each deferred fetch record deferredRecord in + request's client's fetch group's + deferred fetch records: if deferredRecord's + request's body is not null and + deferredRecord's request's URL's + origin is same origin with request's URL's + origin, then increment totalScheduledDeferredBytesForOrigin by + deferredRecord's request's body's + length. + +

      6. If totalScheduledDeferredBytesForOrigin is greater than 64 kilobytes, then + throw a {{QuotaExceededError}}. + +

      7. Let deferredRecord be a new deferred fetch record whose + request is request. + +

      8. Set deferredRecord's inactive timeout to + inactiveTimeout. + +

      9. Append deferredRecord to request's + client's fetch group's + deferred fetch records. + +

      10. Return deferredRecord. +

      +
      +

      Fetch API

      @@ -8527,7 +8583,7 @@ otherwise false.
  • -

    Fetch method

    +

    Fetch methods

     partial interface mixin WindowOrWorkerGlobalScope {
    @@ -8667,6 +8723,60 @@ with a promise, request, responseObject, and an
     
     
     
    +

    RequestDeferredFetch method

    + +
    +
    +dictionary DeferredRequestInit : RequestInit {
    +  DOMHighResTimeStamp inactiveTimeout;
    +};
    +
    +partial interface mixin WindowOrWorkerGlobalScope {
    +  [NewObject] Promise<Response> requestDeferredFetch(RequestInfo input, optional DeferredRequestInit init = {});
    +};
    +
    + +
    +

    The +requestDeferredFetch(input, init) +method steps are: + +

      +
    1. Let promise be a new promise. + +

    2. Let requestObject be the result of invoking the initial value of {{Request}} as + constructor with input and init as arguments. If that threw an exception, + reject promise with that exception and return promise. + +

    3. If requestObject's signal is aborted, + then reject promise with requestObject's + signal's abort reason and return promise. + +

    4. Let request be requestObject's request. + +

    5. Let inactiveTimeout be null. + +

    6. If init is given and init["inactiveTimeout"] + exists then set inactiveTimeout to + init["inactiveTimeout"]. + +

    7. If inactiveTimeout is not a {{DOMHighResTimeStamp}} then throw a {{TypeError}}. + +

    8. Let deferredRecord be the result of calling + request a deferred fetch given request and inactiveTimeout. If that + threw an exception, reject promise with that exception and return + promise. + +

    9. Set deferredRecord's invoke callback to + resolve promise. + +

    10. Add the following abort steps to requestObject's + signal: remove deferredRecord from + request's client's fetch group's + deferred fetch records. +

    + +

    Garbage collection

    The user agent may terminate an ongoing fetch if that termination @@ -8723,117 +8833,6 @@ fetch("https://www.example.com/") -

    Deferred fetching

    - -

    Deferred fetches allow callers to request that a fetch is invoked at the latest possible moment, -when a fetch group is terminated, or after a timeout after it is -deactivated. - -

    Requesting a deferred fetch

    - -
    -

    To request a deferred fetch given a -request request and a null-or-{{DOMHighResTimeStamp}} -inactiveTimeout (default null): - -

      -
    1. Assert: request's client is an - environment settings object. - -

    2. Let totalScheduledDeferredBytesForOrigin be 0. - -
    3. -

      If request's body is not null then: - -

        -
      1. If request's - body's length is null, then throw a {{TypeError}}. - -

      2. Set totalScheduledDeferredBytesForOrigin to request's - body's length. -

      -
    4. - -
    5. For each deferred fetch record deferredRecord in - request's client's fetch group's - deferred fetch records: if deferredRecord's - request's body is not null and - deferredRecord's request's URL's - origin is same origin with request's URL's - origin, then increment totalScheduledDeferredBytesForOrigin by - deferredRecord's request's body's - length. - -

    6. If totalScheduledDeferredBytesForOrigin is greater than 64 kilobytes, then - throw a {{QuotaExceededError}}. - -

    7. Let deferredRecord be a new deferred fetch record whose - request is request. - -

    8. Set deferredRecord's inactive timeout to - inactiveTimeout. - -

    9. Append deferredRecord to request's - client's fetch group's - deferred fetch records. - -

    10. Return deferredRecord. -

    -
    - -

    RequestDeferredFetch method

    - -
    -
    -dictionary DeferredRequestInit : RequestInit {
    -  DOMHighResTimeStamp? inactiveTimeout;
    -};
    -
    -partial interface mixin WindowOrWorkerGlobalScope {
    -  [NewObject] Promise<Response> requestDeferredFetch(RequestInfo input, optional DeferredRequestInit init = {});
    -};
    -
    - -
    -

    The -requestDeferredFetch(input, init) -method steps are: - -

      -
    1. Let promise be a new promise. - -

    2. Let requestObject be the result of invoking the initial value of {{Request}} as - constructor with input and init as arguments. If that threw an exception, - reject promise with that exception and return promise. - -

    3. If requestObject's signal is aborted, - then reject promise with requestObject's - signal's abort reason and return promise. - -

    4. Let request be requestObject's request. - -

    5. Let inactiveTimeout be null. - -

    6. If init is given and init["inactiveTimeout"] - exists then set inactiveTimeout to - init["inactiveTimeout"]. - -

    7. If inactiveTimeout is not a {{DOMHighResTimeStamp}} then throw a {{TypeError}}. - -

    8. Let deferredRecord be the result of calling - request a deferred fetch given request and inactiveTimeout. If that - threw an exception, reject promise with that exception and return - promise. - -

    9. Set deferredRecord's invoke callback to - resolve promise. - -

    10. Add the following abort steps to requestObject's - signal: remove deferredRecord from - request's client's fetch group's - deferred fetch records. -

    -

    data: URLs

    For an informative description of data: URLs, see RFC 2397. This section replaces From 30fcf5ef72331ff4d9986b370cf2cb5765e84cc0 Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Mon, 8 May 2023 14:06:13 +0300 Subject: [PATCH 005/102] Rename to fetchLater and backgroundTimeout --- fetch.bs | 37 +++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/fetch.bs b/fetch.bs index 0b860e878..77a14635b 100644 --- a/fetch.bs +++ b/fetch.bs @@ -2750,7 +2750,7 @@ has the following items:

    request
    A request -
    inactive timeout (default null) +
    inactivity deferred delay (default null)
    Null or a duration
    pending steps (default null) @@ -2818,12 +2818,13 @@ has the following items:
  • For each deferred fetch record deferredRecord in fetchGroup's deferred fetch records whose - inactive timeout is not null: set deferredRecord's - pending steps to running the following steps in parallel: + inactivity deferred delay is not null: set + deferredRecord's pending steps to running the + following steps in parallel:

    1. Wait until deferredRecord's - inactive timeout have passed. + inactivity deferred delay have passed.

    2. Queue a fetch task to run the following steps with @@ -6813,7 +6814,7 @@ when a fetch group is terminated, or after a tim

      To request a deferred fetch given a request request and a null-or-{{DOMHighResTimeStamp}} -inactiveTimeout (default null): +inactivityDeferredDelay (default null):

      1. Assert: request's client is an @@ -6849,8 +6850,8 @@ when a fetch group is terminated, or after a tim

      2. Let deferredRecord be a new deferred fetch record whose request is request. -

      3. Set deferredRecord's inactive timeout to - inactiveTimeout. +

      4. Set deferredRecord's inactivity deferred delay + to inactivityDeferredDelay.

      5. Append deferredRecord to request's client's fetch group's @@ -8723,22 +8724,22 @@ with a promise, request, responseObject, and an

      -

      RequestDeferredFetch method

      +

      FetchLater method

       
       dictionary DeferredRequestInit : RequestInit {
      -  DOMHighResTimeStamp inactiveTimeout;
      +  DOMHighResTimeStamp backgroundTimeout;
       };
       
       partial interface mixin WindowOrWorkerGlobalScope {
      -  [NewObject] Promise<Response> requestDeferredFetch(RequestInfo input, optional DeferredRequestInit init = {});
      +  [NewObject] Promise<Response> fetchLater(RequestInfo input, optional DeferredRequestInit init = {});
       };
       
      -
      +

      The -requestDeferredFetch(input, init) +fetchLater(input, init) method steps are:

        @@ -8754,16 +8755,16 @@ method steps are:
      1. Let request be requestObject's request. -

      2. Let inactiveTimeout be null. +

      3. Let backgroundTimeout be null. -

      4. If init is given and init["inactiveTimeout"] - exists then set inactiveTimeout to - init["inactiveTimeout"]. +

      5. If init is given and init["backgroundTimeout"] + exists then set backgroundTimeout to + init["backgroundTimeout"]. -

      6. If inactiveTimeout is not a {{DOMHighResTimeStamp}} then throw a {{TypeError}}. +

      7. If backgroundTimeout is not a {{DOMHighResTimeStamp}} then throw a {{TypeError}}.

      8. Let deferredRecord be the result of calling - request a deferred fetch given request and inactiveTimeout. If that + request a deferred fetch given request and backgroundTimeout. If that threw an exception, reject promise with that exception and return promise. From a38a86fe180946102df008cccc06389b9643ac88 Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Mon, 8 May 2023 18:48:39 +0300 Subject: [PATCH 006/102] Fix hierarchy --- fetch.bs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fetch.bs b/fetch.bs index 77a14635b..f6f0fae49 100644 --- a/fetch.bs +++ b/fetch.bs @@ -8584,8 +8584,9 @@ otherwise false.

      -

      Fetch methods

      +

      Fetch methods

      +

      Fetch Method

       partial interface mixin WindowOrWorkerGlobalScope {
         [NewObject] Promise<Response> fetch(RequestInfo input, optional RequestInit init = {});
      
      From b602f6b429c9c8a17d5bbfbe491ef934c720cf42 Mon Sep 17 00:00:00 2001
      From: Noam Rosenthal 
      Date: Sun, 11 Jun 2023 16:21:12 +0300
      Subject: [PATCH 007/102] Restructure to expose a boolean instead of a promise
      
      ---
       fetch.bs | 143 ++++++++++++++++++++++++++++---------------------------
       1 file changed, 72 insertions(+), 71 deletions(-)
      
      diff --git a/fetch.bs b/fetch.bs
      index f6f0fae49..f976b0d73 100644
      --- a/fetch.bs
      +++ b/fetch.bs
      @@ -2753,12 +2753,15 @@ has the following items:
        
      inactivity deferred delay (default null)
      Null or a duration -
      pending steps (default null) -
      invoked callback (default null) -
      Null or an algortihm accepting nothing - -
      invoked (default false) +
      sent (default false)
      A boolean + +
      invoke state (default "deferred") +
      +

      "deferred", "scheduled", "terminated", + "aborted", or "sent" + +

      This value can be modified in parallel. @@ -2768,79 +2771,60 @@ has the following items: terminated:

        -
      1. -

        For each deferred fetch record - deferredRecord in fetchGroup's - deferred fetch records whose invoked is - false: - -

          -
        1. If deferredRecord's pending steps is not - null then abort deferredRecord's - pending steps. - -

        2. fetch deferredRecord's request. -

        -
      2. For each associated fetch record record, if record's controller is non-null and record's request's done flag is unset or keepalive is false, terminate record's controller. -

      - -

      When a fetch group fetchGroup is -activated: -for each deferred fetch record deferredRecord in -fetchGroup's deferred fetch records: - -

        -
      1. -

        If deferredRecord's invoked is true then: -

          -
        1. If deferredRecord's invoked callback is not - null then call deferredRecord's invoked callback. - -

        2. Remove deferredRecord from fetchGroup's - deferred fetch records. -

        -
      2. Otherwise, if deferredRecord's - pending steps is not null, then abort - deferredRecord's pending steps and set - deferredRecord's pending steps to null. +

      3. For each deferred fetch record + deferredRecord in fetchGroup's + deferred fetch records: If the result of atomically exchanging the value + of deferredRecord's invoke state with + "terminated" is not "sent", then the user agent should + fetch deferredRecord's request. The + exact time in which the fetch takes place is implementation-defined.

      When a fetch group fetchGroup is deactivated: +For each deferred fetch record deferredRecord in +fetchGroup's deferred fetch records whose +inactivity deferred delay is not null:

        +
      1. Set deferredRecord's invoke state to + "scheduled". +

      2. -

        For each deferred fetch record deferredRecord in - fetchGroup's deferred fetch records whose - inactivity deferred delay is not null: set - deferredRecord's pending steps to running the - following steps in parallel: +

        Run the following steps in parallel:

          -
        1. Wait until deferredRecord's - inactivity deferred delay have passed. - -

        2. -

          Queue a fetch task to run the following steps with - request's client's - global object: - -

            -
          1. Fetch record's request. - -

          2. Set deferredRecord invoked to true. -

          -
        3. +
        4. The user agent should wait until deferredRecord's + inactivity deferred delay have passed or until + deferredRecord's invoke state is not + "scheduled". The user agent may wait for a longer or shorter period time, e.g., to + optimize batching of deferred fetches. + +

        5. If the result of atomically exchanging the value of deferredRecord's + invoke state with "sent" is + "scheduled", then fetch record's + request.

      +

      When a fetch group fetchGroup is +reactivated: +For each deferred fetch record deferredRecord in +fetchGroup's deferred fetch records: If the result of atomically +exchanging the value of deferredRecord's invoke state +with "deferred" is "sent", then remove +deferredRecord from fetchGroup's +deferred fetch records and set deferredRecord's +sent to true. + +

      Resolving domains

      @@ -8733,19 +8717,29 @@ dictionary DeferredRequestInit : RequestInit { DOMHighResTimeStamp backgroundTimeout; }; +interface FetchLaterResult { + readonly attribute boolean sent; +}; + partial interface mixin WindowOrWorkerGlobalScope { - [NewObject] Promise<Response> fetchLater(RequestInfo input, optional DeferredRequestInit init = {}); + [NewObject] FetchLaterResult fetchLater(RequestInfo input, optional DeferredRequestInit init = {}); };
      +

      A {{FetchLaterResult}} has an associated deferred fetch record +deferred record. + +

      +

      The sent getter steps are to return +this's deferred record's sent. +

      +

      The fetchLater(input, init) method steps are:

        -
      1. Let promise be a new promise. -

      2. Let requestObject be the result of invoking the initial value of {{Request}} as constructor with input and init as arguments. If that threw an exception, reject promise with that exception and return promise. @@ -8765,17 +8759,24 @@ method steps are:

      3. If backgroundTimeout is not a {{DOMHighResTimeStamp}} then throw a {{TypeError}}.

      4. Let deferredRecord be the result of calling - request a deferred fetch given request and backgroundTimeout. If that - threw an exception, reject promise with that exception and return - promise. + request a deferred fetch given request and backgroundTimeout. This + may throw an exception. -

      5. Set deferredRecord's invoke callback to - resolve promise. +

      6. +

        Add the following abort steps to requestObject's + signal: -

      7. Add the following abort steps to requestObject's - signal: remove deferredRecord from - request's client's fetch group's - deferred fetch records. +

          +
        1. Set deferredRecord's invoke state to + "aborted". + +

        2. Remove deferredRecord from + request's client's fetch group's + deferred fetch records. +

        + +
      8. Return a new {{FetchLaterResult}} whose deferred record is + deferredRecord.

      From b4bae0eacc91537faf6b4c909201ca65644d6951 Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Sun, 11 Jun 2023 16:25:03 +0300 Subject: [PATCH 008/102] nit --- fetch.bs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fetch.bs b/fetch.bs index f976b0d73..b6898701d 100644 --- a/fetch.bs +++ b/fetch.bs @@ -6792,8 +6792,8 @@ agent's CORS-preflight cache for which there is a cache entry matchDeferred fetching

      Deferred fetches allow callers to request that a fetch is invoked at the latest possible moment, -when a fetch group is terminated, or after a timeout after it is -deactivated. +i.e. when a fetch group is terminated, or after a timeout after it +is deactivated.

      To request a deferred fetch given a From 9a0180ecc490b1fd8737e5832c8c5cf5ddbb08cf Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Sun, 18 Jun 2023 13:20:34 +0300 Subject: [PATCH 009/102] or/and --- fetch.bs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fetch.bs b/fetch.bs index b6898701d..31d4343b1 100644 --- a/fetch.bs +++ b/fetch.bs @@ -2773,7 +2773,7 @@ has the following items:

      1. For each associated fetch record record, if record's controller is non-null and - record's request's done flag is unset or + record's request's done flag is unset and keepalive is false, terminate record's controller. From 84c794b6a9c869268148e579e6948176de073b5e Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Thu, 29 Jun 2023 08:55:37 +0300 Subject: [PATCH 010/102] Remove spurious promise --- fetch.bs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/fetch.bs b/fetch.bs index 31d4343b1..365a1ecc4 100644 --- a/fetch.bs +++ b/fetch.bs @@ -2761,7 +2761,9 @@ has the following items:

        "deferred", "scheduled", "terminated", "aborted", or "sent" -

        This value can be modified in parallel. +

        This value can be modified in parallel. To set it safely, use atomic + operations, e.g. atomically exchange the value (set it to the new value and return the new value + in one operation). @@ -8741,12 +8743,10 @@ method steps are:

        1. Let requestObject be the result of invoking the initial value of {{Request}} as - constructor with input and init as arguments. If that threw an exception, - reject promise with that exception and return promise. + constructor with input and init as arguments. This may throw an exception.

        2. If requestObject's signal is aborted, - then reject promise with requestObject's - signal's abort reason and return promise. + then throw signal's abort reason.

        3. Let request be requestObject's request. From 9e234b475fd941b3a614f0f34ef5ee098a9affa5 Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Thu, 29 Jun 2023 11:42:26 +0300 Subject: [PATCH 011/102] Rename backgrounded --- fetch.bs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fetch.bs b/fetch.bs index 365a1ecc4..4f45cf68d 100644 --- a/fetch.bs +++ b/fetch.bs @@ -2743,8 +2743,8 @@ functionality. fetch controller or null).

          A deferred fetch record is a struct used to maintain state needed -to invoke a fetch at a later time, e.g., when a Document is unloaded or backgrounded. It -has the following items: +to invoke a fetch at a later time, e.g., when a Document is unloaded or becomes +not fully active. It has the following items:

          request From e301c55732c21da1b1d989cb9988ddef7fe7a0c0 Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Tue, 4 Jul 2023 14:17:53 +0300 Subject: [PATCH 012/102] Throw a few more errors --- fetch.bs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/fetch.bs b/fetch.bs index 4f45cf68d..9cab1da52 100644 --- a/fetch.bs +++ b/fetch.bs @@ -8742,6 +8742,14 @@ partial interface mixin WindowOrWorkerGlobalScope { method steps are:
            +
          1. +

            If the user-agent has determined that deferred fetching is not allowed in this context, then + throw a {{NotAllowedError}}. + +

            User-agents can, for example, decide that deferred fetching is not allowed for + subframes or gated behind some permission. +

          2. +
          3. Let requestObject be the result of invoking the initial value of {{Request}} as constructor with input and init as arguments. This may throw an exception. @@ -8750,6 +8758,12 @@ method steps are:

          4. Let request be requestObject's request. +

          5. If request's URL's scheme is not an + HTTP(S) scheme then throw a {{TypeError}}. + +

          6. If request's URL is not a + [=potentially trustworthy url=], then throw a {{SecurityError}}. +

          7. Let backgroundTimeout be null.

          8. If init is given and init["backgroundTimeout"] From dabea5256db4619ee56dafaa8ac8d7fb86f4763b Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Wed, 5 Jul 2023 10:57:59 +0300 Subject: [PATCH 013/102] Always set keepalive --- fetch.bs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fetch.bs b/fetch.bs index 9cab1da52..dd603a0e2 100644 --- a/fetch.bs +++ b/fetch.bs @@ -6833,6 +6833,8 @@ is deactivated.

          9. If totalScheduledDeferredBytesForOrigin is greater than 64 kilobytes, then throw a {{QuotaExceededError}}. +

          10. Set request's keepalive to true. +

          11. Let deferredRecord be a new deferred fetch record whose request is request. From 25efd01b2efa328c12a1d06dcfe91a9f7a1dc126 Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Sun, 20 Aug 2023 12:45:38 +0300 Subject: [PATCH 014/102] Nits --- fetch.bs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/fetch.bs b/fetch.bs index dd603a0e2..0bf7f3f2d 100644 --- a/fetch.bs +++ b/fetch.bs @@ -2743,23 +2743,23 @@ functionality. fetch controller or null).

            A deferred fetch record is a struct used to maintain state needed -to invoke a fetch at a later time, e.g., when a Document is unloaded or becomes +to invoke a fetch at a later time, e.g., when a Document object is unloaded or becomes not fully active. It has the following items:

            request -
            A request +
            A request.
            inactivity deferred delay (default null) -
            Null or a duration +
            Null or a duration.
            sent (default false) -
            A boolean +
            A boolean.
            invoke state (default "deferred")

            "deferred", "scheduled", "terminated", - "aborted", or "sent" + "aborted", or "sent".

            This value can be modified in parallel. To set it safely, use atomic operations, e.g. atomically exchange the value (set it to the new value and return the new value @@ -2773,11 +2773,11 @@ not fully active. It has the following itemsterminated:

              -
            1. For each associated fetch record record, - if record's controller is non-null and - record's request's done flag is unset and - keepalive is false, terminate record's - controller. +

            2. For each fetch record record in fetchGroup's + fetch records, if record's controller + is non-null and record's request's done flag is unset + and keepalive is false, terminate + record's controller.

            3. For each deferred fetch record deferredRecord in fetchGroup's From a0dc674854f0a663edc87a5b812880b93fe3996c Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Sun, 20 Aug 2023 13:17:34 +0300 Subject: [PATCH 015/102] Remove atomics, leave thread safety to implementation --- fetch.bs | 84 +++++++++++++++++++++++++++----------------------------- 1 file changed, 41 insertions(+), 43 deletions(-) diff --git a/fetch.bs b/fetch.bs index 0bf7f3f2d..285eac32c 100644 --- a/fetch.bs +++ b/fetch.bs @@ -2750,20 +2750,18 @@ not fully active. It has the following itemsrequest

              A request. -
              inactivity deferred delay (default null) +
              inactivity delay (default null)
              Null or a duration. -
              sent (default false) -
              A boolean. -
              invoke state (default "deferred")

              "deferred", "scheduled", "terminated", - "aborted", or "sent". + "aborted", or "activated". -

              This value can be modified in parallel. To set it safely, use atomic - operations, e.g. atomically exchange the value (set it to the new value and return the new value - in one operation). +

              This value can be modified in parallel. There could be a race condition where + the Document object's event loop might change it to + "deferred" at the same time that it is changed to "activated". UAs can + mitigate this race condition in an implementation-defined manner.

            @@ -2773,26 +2771,33 @@ not fully active. It has the following itemsterminated:
              -
            1. For each fetch record record in fetchGroup's - fetch records, if record's controller - is non-null and record's request's done flag is unset - and keepalive is false, terminate - record's controller. - -

            2. For each deferred fetch record - deferredRecord in fetchGroup's - deferred fetch records: If the result of atomically exchanging the value - of deferredRecord's invoke state with - "terminated" is not "sent", then the user agent should - fetch deferredRecord's request. The - exact time in which the fetch takes place is implementation-defined. +

            3. For each fetch record record of + fetchGroup's fetch records, if record's + controller is non-null and record's + request's done flag is unset and keepalive is + false, terminate record's + controller. + +

            4. +

              For each deferred fetch record + deferredRecord of fetchGroup's + deferred fetch records: + +

                +
              1. Set deferredRecord's invoke state to + "terminated". + +

              2. Fetch deferredRecord's + request. +

              +

            When a fetch group fetchGroup is deactivated: -For each deferred fetch record deferredRecord in +For each deferred fetch record deferredRecord of fetchGroup's deferred fetch records whose -inactivity deferred delay is not null: +inactivity delay is not null:

            1. Set deferredRecord's invoke state to @@ -2803,28 +2808,20 @@ not fully active. It has the following items

            2. The user agent should wait until deferredRecord's - inactivity deferred delay have passed or until + inactivity delay have passed or until deferredRecord's invoke state is not "scheduled". The user agent may wait for a longer or shorter period time, e.g., to optimize batching of deferred fetches. -

            3. If the result of atomically exchanging the value of deferredRecord's - invoke state with "sent" is - "scheduled", then fetch record's - request. +

            4. If deferredRecord's is not "scheduled", abort these steps. + +

            5. Set deferredRecord's invoke state to + "activated". + +

            6. Fetch record's request.

            -
          -

          When a fetch group fetchGroup is -reactivated: -For each deferred fetch record deferredRecord in -fetchGroup's deferred fetch records: If the result of atomically -exchanging the value of deferredRecord's invoke state -with "deferred" is "sent", then remove -deferredRecord from fetchGroup's -deferred fetch records and set deferredRecord's -sent to true.

          Resolving domains

          @@ -6838,8 +6835,8 @@ is deactivated.
        4. Let deferredRecord be a new deferred fetch record whose request is request. -

        5. Set deferredRecord's inactivity deferred delay - to inactivityDeferredDelay. +

        6. Set deferredRecord's inactivity delay to + inactivityDeferredDelay.

        7. Append deferredRecord to request's client's fetch group's @@ -8722,7 +8719,7 @@ dictionary DeferredRequestInit : RequestInit { }; interface FetchLaterResult { - readonly attribute boolean sent; + readonly attribute boolean activated; }; partial interface mixin WindowOrWorkerGlobalScope { @@ -8734,8 +8731,9 @@ partial interface mixin WindowOrWorkerGlobalScope { deferred record.

          -

          The sent getter steps are to return -this's deferred record's sent. +

          The activated getter steps are to return +true if this's deferred record's +invoke state is "activated"; Otherwise false.

          From 742e55b1242d870aeb65f540d1215133b38d7601 Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Wed, 23 Aug 2023 11:04:34 +0300 Subject: [PATCH 016/102] Throw RangeError when backgroundTimeout is negative --- fetch.bs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fetch.bs b/fetch.bs index 285eac32c..65f0ee028 100644 --- a/fetch.bs +++ b/fetch.bs @@ -8772,6 +8772,8 @@ method steps are:
        8. If backgroundTimeout is not a {{DOMHighResTimeStamp}} then throw a {{TypeError}}. +

        9. If backgroundTimeout is less than 0 then throw a {{RangeError}}. +

        10. Let deferredRecord be the result of calling request a deferred fetch given request and backgroundTimeout. This may throw an exception. From 5b75fa2f7badbfe1541ffee73bdff4046506a851 Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Tue, 19 Sep 2023 13:00:22 +0300 Subject: [PATCH 017/102] Refactor out backgroundTimeout, use activationTimeout --- fetch.bs | 124 +++++++++++++++++++++++++++---------------------------- 1 file changed, 60 insertions(+), 64 deletions(-) diff --git a/fetch.bs b/fetch.bs index 65f0ee028..8e1a8c1e1 100644 --- a/fetch.bs +++ b/fetch.bs @@ -2731,7 +2731,7 @@ functionality.

          A fetch group holds an ordered list of fetch records. -

          A fetch group holds an ordered list of +

          A fetch group holds an list of deferred fetch records.

          A fetch record has an associated @@ -2750,16 +2750,12 @@ not fully active. It has the following itemsrequest

          A request. -
          inactivity delay (default null) -
          Null or a duration. -
          invoke state (default "deferred")
          -

          "deferred", "scheduled", "terminated", - "aborted", or "activated". +

          "deferred", "aborted", or "activated".

          This value can be modified in parallel. There could be a race condition where - the Document object's event loop might change it to + the Document object's event loop might read it as "deferred" at the same time that it is changed to "activated". UAs can mitigate this race condition in an implementation-defined manner.

        11. @@ -2778,48 +2774,7 @@ not fully active. It has the following itemsterminate record's controller. -
        12. -

          For each deferred fetch record - deferredRecord of fetchGroup's - deferred fetch records: - -

            -
          1. Set deferredRecord's invoke state to - "terminated". - -

          2. Fetch deferredRecord's - request. -

          -
        13. -
        - -

        When a fetch group fetchGroup is -deactivated: -For each deferred fetch record deferredRecord of -fetchGroup's deferred fetch records whose -inactivity delay is not null: - -

          -
        1. Set deferredRecord's invoke state to - "scheduled". - -

        2. -

          Run the following steps in parallel: - -

            -
          1. The user agent should wait until deferredRecord's - inactivity delay have passed or until - deferredRecord's invoke state is not - "scheduled". The user agent may wait for a longer or shorter period time, e.g., to - optimize batching of deferred fetches. - -

          2. If deferredRecord's is not "scheduled", abort these steps. - -

          3. Set deferredRecord's invoke state to - "activated". - -

          4. Fetch record's request. -

          +
        3. process deferred fetches for fetchGroup.

        @@ -6791,13 +6746,12 @@ agent's CORS-preflight cache for which there is a cache entry matchDeferred fetching

        Deferred fetches allow callers to request that a fetch is invoked at the latest possible moment, -i.e. when a fetch group is terminated, or after a timeout after it -is deactivated. +i.e. when a fetch group is terminated, or after a timeout.

        To request a deferred fetch given a -request request and a null-or-{{DOMHighResTimeStamp}} -inactivityDeferredDelay (default null): +request request and a null or {{DOMHighResTimeStamp}} +activationTimeout (default null):

        1. Assert: request's client is an @@ -6835,17 +6789,59 @@ is deactivated.

        2. Let deferredRecord be a new deferred fetch record whose request is request. -

        3. Set deferredRecord's inactivity delay to - inactivityDeferredDelay. -

        4. Append deferredRecord to request's client's fetch group's deferred fetch records. +

        5. +

          If activationTimeout is not null, then run the following steps in parallel:

          + +
            +
          1. The user agent should wait until activationTimeout milliseconds have passed. + The user agent may wait for a longer or shorter period time, e.g., to optimize batching of + deferred fetches. + +

          2. Process a deferred fetch given deferredRecord. +

          +
        6. +
        7. Return deferredRecord.

        +
        +

        To process deferred fetches given a fetch group fetchGroup: + +

          +
        1. Let deferredFetchRecords be fetchGroup's + deferred fetch records. + +

        2. Let fetchGroup's + deferred fetch records be « ». + +

        3. For each deferred fetch record + deferredRecord of deferredFetchRecords, process a deferred fetch given + deferredRecord. +

        +
        + +
        +

        To process a deferred fetch deferredRecord: +

          +
        1. If deferredRecord's invoke state is not + "deferred", then return. + +

        2. Set deferredRecord's invoke state to + "activated". + +

        3. Fetch deferredRecord's + request. +

        +
        + +

        The user agent may process deferred fetches at any given moment, for example when the +browser is about to terminate or lose the ability to execute code. +

        Fetch API

        @@ -8715,7 +8711,7 @@ with a promise, request, responseObject, and an
         
         dictionary DeferredRequestInit : RequestInit {
        -  DOMHighResTimeStamp backgroundTimeout;
        +  DOMHighResTimeStamp activationTimeout;
         };
         
         interface FetchLaterResult {
        @@ -8764,18 +8760,18 @@ method steps are:
          
      2. If request's URL is not a [=potentially trustworthy url=], then throw a {{SecurityError}}. -

      3. Let backgroundTimeout be null. +

      4. Let activationTimeout be null. -

      5. If init is given and init["backgroundTimeout"] - exists then set backgroundTimeout to - init["backgroundTimeout"]. +

      6. If init is given and init["activationTimeout"] + exists then set activationTimeout to + init["activationTimeout"]. -

      7. If backgroundTimeout is not a {{DOMHighResTimeStamp}} then throw a {{TypeError}}. +

      8. If activationTimeout is not a {{DOMHighResTimeStamp}} then throw a {{TypeError}}. -

      9. If backgroundTimeout is less than 0 then throw a {{RangeError}}. +

      10. If activationTimeout is less than 0 then throw a {{RangeError}}.

      11. Let deferredRecord be the result of calling - request a deferred fetch given request and backgroundTimeout. This + request a deferred fetch given request and activationTimeout. This may throw an exception.

      12. From e34decdb4c7a37a15b6e32aa606aa69b257d2270 Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Tue, 19 Sep 2023 13:11:23 +0300 Subject: [PATCH 018/102] Throw on ReadableStream --- fetch.bs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/fetch.bs b/fetch.bs index 8e1a8c1e1..7116104c6 100644 --- a/fetch.bs +++ b/fetch.bs @@ -8760,6 +8760,12 @@ method steps are:
      13. If request's URL is not a [=potentially trustworthy url=], then throw a {{SecurityError}}. +

      14. +

        If request's body is not null and request's + body's source is null, then throw a {{TypeError}}. + +

        This disallows sending deferred fetches with a live {{ReadableStream}}. +

      15. Let activationTimeout be null.

      16. If init is given and init["activationTimeout"] From a05a071364d0084d407dec102f21f99856a5b80a Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Tue, 19 Sep 2023 15:01:56 +0300 Subject: [PATCH 019/102] Fix grammar --- fetch.bs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fetch.bs b/fetch.bs index 7116104c6..163af0c2b 100644 --- a/fetch.bs +++ b/fetch.bs @@ -2731,7 +2731,7 @@ functionality.

        A fetch group holds an ordered list of fetch records. -

        A fetch group holds an list of +

        A fetch group holds a list of deferred fetch records.

        A fetch record has an associated From 110cb8d4acb6f0ed0777f02e78656b8b3f5be07d Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Mon, 2 Oct 2023 21:18:43 +0300 Subject: [PATCH 020/102] Several editorial changes, renamed to activationDeadline --- fetch.bs | 53 ++++++++++++++++++++++++++++------------------------- 1 file changed, 28 insertions(+), 25 deletions(-) diff --git a/fetch.bs b/fetch.bs index 163af0c2b..603767dd4 100644 --- a/fetch.bs +++ b/fetch.bs @@ -2742,6 +2742,9 @@ functionality. controller (a fetch controller or null). + +


        +

        A deferred fetch record is a struct used to maintain state needed to invoke a fetch at a later time, e.g., when a Document object is unloaded or becomes not fully active. It has the following items: @@ -2774,7 +2777,7 @@ not fully active. It has the following itemsterminate record's controller. -

      17. process deferred fetches for fetchGroup. +

      18. Process deferred fetches for fetchGroup.

      @@ -6751,7 +6754,7 @@ i.e. when a fetch group is terminated, or after

      To request a deferred fetch given a request request and a null or {{DOMHighResTimeStamp}} -activationTimeout (default null): +activationDeadline (default null):

      1. Assert: request's client is an @@ -6794,12 +6797,16 @@ i.e. when a fetch group is terminated, or after deferred fetch records.

      2. -

        If activationTimeout is not null, then run the following steps in parallel:

        +

        If activationDeadline is not null, then run the following steps in parallel:

          -
        1. The user agent should wait until activationTimeout milliseconds have passed. - The user agent may wait for a longer or shorter period time, e.g., to optimize batching of - deferred fetches. +

        2. The user agent should wait until activationDeadline milliseconds have passed, + or until the user agent has a reason to believe that it is about to lose the opportunity to + execute scripts, e.g. when the browser is moved to the background. + +

        3. The user agent may wait for a further implementation-defined duration, e.g. + in order to batch several requests together or to wait until keepalive + requests are complete.

        4. Process a deferred fetch given deferredRecord.

        @@ -6839,10 +6846,6 @@ i.e. when a fetch group is terminated, or after
      -

      The user agent may process deferred fetches at any given moment, for example when the -browser is about to terminate or lose the ability to execute code. - -

      Fetch API

      @@ -8567,7 +8570,7 @@ otherwise false.

      Fetch methods

      -

      Fetch Method

      +

      Fetch Methods

       partial interface mixin WindowOrWorkerGlobalScope {
         [NewObject] Promise<Response> fetch(RequestInfo input, optional RequestInit init = {});
      @@ -8705,15 +8708,13 @@ with a promise, request, responseObject, and an
       
  • - -

    FetchLater method

    -
     
     dictionary DeferredRequestInit : RequestInit {
    -  DOMHighResTimeStamp activationTimeout;
    +  DOMHighResTimeStamp activationDeadline;
     };
     
    +[Exposed=Window,Worker]
     interface FetchLaterResult {
       readonly attribute boolean activated;
     };
    @@ -8739,15 +8740,15 @@ method steps are:
     
     
    1. -

      If the user-agent has determined that deferred fetching is not allowed in this context, then +

      If the user agent has determined that deferred fetching is not allowed in this context, then throw a {{NotAllowedError}}. -

      User-agents can, for example, decide that deferred fetching is not allowed for +

      user agents can, for example, decide that deferred fetching is not allowed for subframes or gated behind some permission.

    2. Let requestObject be the result of invoking the initial value of {{Request}} as - constructor with input and init as arguments. This may throw an exception. + constructor with input and init as arguments.

    3. If requestObject's signal is aborted, then throw signal's abort reason. @@ -8760,24 +8761,26 @@ method steps are:

    4. If request's URL is not a [=potentially trustworthy url=], then throw a {{SecurityError}}. +

    5. Set request's service-workers mode to "none". +

    6. If request's body is not null and request's body's source is null, then throw a {{TypeError}}.

      This disallows sending deferred fetches with a live {{ReadableStream}}. -

    7. Let activationTimeout be null. +

    8. Let activationDeadline be null. -

    9. If init is given and init["activationTimeout"] - exists then set activationTimeout to - init["activationTimeout"]. +

    10. If init is given and init["activationDeadline"] + exists, then set activationDeadline to + init["activationDeadline"]. -

    11. If activationTimeout is not a {{DOMHighResTimeStamp}} then throw a {{TypeError}}. +

    12. If activationDeadline is not a {{DOMHighResTimeStamp}}, then throw a {{TypeError}}. -

    13. If activationTimeout is less than 0 then throw a {{RangeError}}. +

    14. If activationDeadline is less than 0, then throw a {{RangeError}}.

    15. Let deferredRecord be the result of calling - request a deferred fetch given request and activationTimeout. This + request a deferred fetch given request and activationDeadline. This may throw an exception.

    16. From aa68e95cdfce02a002903a2240321ddf7bbdd9f9 Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Tue, 3 Oct 2023 14:14:54 +0300 Subject: [PATCH 021/102] Handle CR nits --- fetch.bs | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/fetch.bs b/fetch.bs index 603767dd4..d90ec9ce5 100644 --- a/fetch.bs +++ b/fetch.bs @@ -6760,6 +6760,20 @@ i.e. when a fetch group is terminated, or after
    17. Assert: request's client is an environment settings object. +

    18. If request's URL's scheme is not an + HTTP(S) scheme then throw a {{TypeError}}. + +

    19. If request's URL is not a + [=potentially trustworthy url=], then throw a "{{SecurityError}}" {{DOMException}}. + +

    20. Set request's service-workers mode to "none". + +

    21. +

      If request's body is not null and request's + body's source is null, then throw a {{TypeError}}. + +

      This disallows sending deferred fetches with a live {{ReadableStream}}. +

    22. Let totalScheduledDeferredBytesForOrigin be 0.
    23. @@ -6785,7 +6799,7 @@ i.e. when a fetch group is terminated, or after length.
    24. If totalScheduledDeferredBytesForOrigin is greater than 64 kilobytes, then - throw a {{QuotaExceededError}}. + throw a "{{QuotaExceededError}}" {{DOMException}}.

    25. Set request's keepalive to true. @@ -8755,20 +8769,6 @@ method steps are:

    26. Let request be requestObject's request. -

    27. If request's URL's scheme is not an - HTTP(S) scheme then throw a {{TypeError}}. - -

    28. If request's URL is not a - [=potentially trustworthy url=], then throw a {{SecurityError}}. - -

    29. Set request's service-workers mode to "none". - -

    30. -

      If request's body is not null and request's - body's source is null, then throw a {{TypeError}}. - -

      This disallows sending deferred fetches with a live {{ReadableStream}}. -

    31. Let activationDeadline be null.

    32. If init is given and init["activationDeadline"] @@ -8780,8 +8780,7 @@ method steps are:

    33. If activationDeadline is less than 0, then throw a {{RangeError}}.

    34. Let deferredRecord be the result of calling - request a deferred fetch given request and activationDeadline. This - may throw an exception. + request a deferred fetch given request and activationDeadline.

    35. Add the following abort steps to requestObject's From a77984d3709c7bb3251c28f8e53b0ba039bf130e Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Tue, 3 Oct 2023 15:44:38 +0300 Subject: [PATCH 022/102] Remove h2 --- fetch.bs | 2 -- 1 file changed, 2 deletions(-) diff --git a/fetch.bs b/fetch.bs index d90ec9ce5..0e8653f2d 100644 --- a/fetch.bs +++ b/fetch.bs @@ -8582,8 +8582,6 @@ otherwise false.

    -

    Fetch methods

    -

    Fetch Methods

     partial interface mixin WindowOrWorkerGlobalScope {
    
    From cb46ef21f5d05daabff7e0394574ab7c2194f5af Mon Sep 17 00:00:00 2001
    From: Noam Rosenthal 
    Date: Tue, 3 Oct 2023 15:45:10 +0300
    Subject: [PATCH 023/102] Bring back line break
    
    ---
     fetch.bs | 1 +
     1 file changed, 1 insertion(+)
    
    diff --git a/fetch.bs b/fetch.bs
    index 0e8653f2d..6e3d04ebb 100644
    --- a/fetch.bs
    +++ b/fetch.bs
    @@ -8583,6 +8583,7 @@ otherwise false.
     
     
     

    Fetch Methods

    +
     partial interface mixin WindowOrWorkerGlobalScope {
       [NewObject] Promise<Response> fetch(RequestInfo input, optional RequestInit init = {});
    
    From 34bf2933edac731cddd1394499edd51c4c677569 Mon Sep 17 00:00:00 2001
    From: Noam Rosenthal 
    Date: Tue, 3 Oct 2023 15:45:34 +0300
    Subject: [PATCH 024/102] nit
    
    ---
     fetch.bs | 2 +-
     1 file changed, 1 insertion(+), 1 deletion(-)
    
    diff --git a/fetch.bs b/fetch.bs
    index 6e3d04ebb..66fcc1a86 100644
    --- a/fetch.bs
    +++ b/fetch.bs
    @@ -8582,7 +8582,7 @@ otherwise false.
     
     
     
    -

    Fetch Methods

    +

    Fetch methods

     partial interface mixin WindowOrWorkerGlobalScope {
    
    From 308cb1bb47be0c7adad3f1d4c2fd27b794386e52 Mon Sep 17 00:00:00 2001
    From: Noam Rosenthal 
    Date: Tue, 3 Oct 2023 15:45:46 +0300
    Subject: [PATCH 025/102] nit
    
    ---
     fetch.bs | 2 +-
     1 file changed, 1 insertion(+), 1 deletion(-)
    
    diff --git a/fetch.bs b/fetch.bs
    index 66fcc1a86..33bed79c3 100644
    --- a/fetch.bs
    +++ b/fetch.bs
    @@ -8582,7 +8582,7 @@ otherwise false.
     
     
     
    -

    Fetch methods

    +

    Fetch methods

     partial interface mixin WindowOrWorkerGlobalScope {
    
    From ba49a997a1f24a148fcfaec123e0dc134541b3d0 Mon Sep 17 00:00:00 2001
    From: Noam Rosenthal 
    Date: Tue, 3 Oct 2023 15:47:27 +0300
    Subject: [PATCH 026/102] nit
    
    ---
     fetch.bs | 2 +-
     1 file changed, 1 insertion(+), 1 deletion(-)
    
    diff --git a/fetch.bs b/fetch.bs
    index 33bed79c3..3260150dc 100644
    --- a/fetch.bs
    +++ b/fetch.bs
    @@ -8582,7 +8582,7 @@ otherwise false.
     
     
     
    -

    Fetch methods

    +

    Fetch methods

     partial interface mixin WindowOrWorkerGlobalScope {
    
    From 64d5a73d96aee7309ec3f4b05d39e5bc753c4821 Mon Sep 17 00:00:00 2001
    From: Noam Rosenthal 
    Date: Mon, 9 Oct 2023 19:07:27 +0300
    Subject: [PATCH 027/102] - Rename activationDeadline to activateAfter -
     Clarify the conditions under which a deferred fetch   should be activated
     early.
    
    ---
     fetch.bs | 26 ++++++++++++++------------
     1 file changed, 14 insertions(+), 12 deletions(-)
    
    diff --git a/fetch.bs b/fetch.bs
    index 3260150dc..457a5e35a 100644
    --- a/fetch.bs
    +++ b/fetch.bs
    @@ -6754,7 +6754,7 @@ i.e. when a fetch group is terminated, or after
     

    To request a deferred fetch given a request request and a null or {{DOMHighResTimeStamp}} -activationDeadline (default null): +activateAfter (default null):

    1. Assert: request's client is an @@ -6811,12 +6811,14 @@ i.e. when a fetch group is terminated, or after deferred fetch records.

    2. -

      If activationDeadline is not null, then run the following steps in parallel:

      +

      If activateAfter is not null, then run the following steps in parallel:

        -
      1. The user agent should wait until activationDeadline milliseconds have passed, +

      2. The user agent should wait until activateAfter milliseconds have passed, or until the user agent has a reason to believe that it is about to lose the opportunity to - execute scripts, e.g. when the browser is moved to the background. + execute scripts, e.g. when the browser is moved to the background, or when request's + client is a Document that had a "hidden" + visibility state for a long period of time.

      3. The user agent may wait for a further implementation-defined duration, e.g. in order to batch several requests together or to wait until keepalive @@ -8724,7 +8726,7 @@ with a promise, request, responseObject, and an

         
         dictionary DeferredRequestInit : RequestInit {
        -  DOMHighResTimeStamp activationDeadline;
        +  DOMHighResTimeStamp activateAfter;
         };
         
         [Exposed=Window,Worker]
        @@ -8768,18 +8770,18 @@ method steps are:
         
          
      4. Let request be requestObject's request. -

      5. Let activationDeadline be null. +

      6. Let activateAfter be null. -

      7. If init is given and init["activationDeadline"] - exists, then set activationDeadline to - init["activationDeadline"]. +

      8. If init is given and init["activateAfter"] + exists, then set activateAfter to + init["activateAfter"]. -

      9. If activationDeadline is not a {{DOMHighResTimeStamp}}, then throw a {{TypeError}}. +

      10. If activateAfter is not a {{DOMHighResTimeStamp}}, then throw a {{TypeError}}. -

      11. If activationDeadline is less than 0, then throw a {{RangeError}}. +

      12. If activateAfter is less than 0, then throw a {{RangeError}}.

      13. Let deferredRecord be the result of calling - request a deferred fetch given request and activationDeadline. + request a deferred fetch given request and activateAfter.

      14. Add the following abort steps to requestObject's From cf5503cc816590ba79e724b8bc372ca1b852d265 Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Wed, 25 Oct 2023 10:19:14 +0100 Subject: [PATCH 028/102] Throw a NotAllowedError for 3p envs --- fetch.bs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/fetch.bs b/fetch.bs index 457a5e35a..b09570017 100644 --- a/fetch.bs +++ b/fetch.bs @@ -8754,13 +8754,10 @@ true if this's deferred record's method steps are:

          -
        1. -

          If the user agent has determined that deferred fetching is not allowed in this context, then - throw a {{NotAllowedError}}. - -

          user agents can, for example, decide that deferred fetching is not allowed for - subframes or gated behind some permission. -

        2. +
        3. If requestObject's relevant settings object's + origin is not same origin with + requestObject's relevant settings object's + top-level origin, then throw a {{NotAllowedError}}.

        4. Let requestObject be the result of invoking the initial value of {{Request}} as constructor with input and init as arguments. From d356198ab6e50e8dee55ee786b37830c181800e4 Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Tue, 12 Mar 2024 18:00:58 +0000 Subject: [PATCH 029/102] Add top-level + per-sink quota --- fetch.bs | 114 +++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 94 insertions(+), 20 deletions(-) diff --git a/fetch.bs b/fetch.bs index b09570017..329d65b6e 100644 --- a/fetch.bs +++ b/fetch.bs @@ -6766,40 +6766,90 @@ i.e. when a fetch group is terminated, or after

        5. If request's URL is not a [=potentially trustworthy url=], then throw a "{{SecurityError}}" {{DOMException}}. +

        6. If request's body is null then throw a {{TypeError}}. + +

        7. If request's + body's length is null, then throw a {{TypeError}}. +

        8. Set request's service-workers mode to "none".

        9. -

          If request's body is not null and request's - body's source is null, then throw a {{TypeError}}. +

          If request's body's source is null, then throw a + {{TypeError}}.

          This disallows sending deferred fetches with a live {{ReadableStream}}. -

        10. Let totalScheduledDeferredBytesForOrigin be 0. +
        11. +

          Let quotaRelevantTopLevelNavigables be an the + quota-relevant top-level navigables given request's client. + +

          In most cases this will list will contain a single {{Document}}. The exception would + be when the request comes from a {{SharedWorker}}, in which case all the documents that directly + or indirectly obtain this worker would have to check their quota.

        12. -

          If request's body is not null then: +

          For each topLevelNavigable in + quotaRelevantTopLevelNavigables: + +

          This algorithm asserts that this deferred fetch doesn't exceed two quotas: one for + the top-level document (640kb), and one for the reporting origin (64kb). The larger quota ensures + that the top-level {{Document}} and its subresources don't continue using an unlimited amount of + bandwidth after being destroyed. The smaller quota ensures that a single reporting sink doesn't + reserve the whole quota to itself.

            -
          1. If request's - body's length is null, then throw a {{TypeError}}. +

          2. Let totalScheduledDeferredBytesForTopLevelDocument be + request's body's length. -

          3. Set totalScheduledDeferredBytesForOrigin to request's +

          4. Let totalScheduledDeferredBytesForOrigin be request's body's length. -

          -
        13. -
        14. For each deferred fetch record deferredRecord in - request's client's fetch group's - deferred fetch records: if deferredRecord's - request's body is not null and - deferredRecord's request's URL's - origin is same origin with request's URL's - origin, then increment totalScheduledDeferredBytesForOrigin by - deferredRecord's request's body's - length. +

        15. Let participatingClients be an empty set. + +

        16. For each navigable of topLevelNavigable's + active document's inclusive descendant navigables, + append navigable's active document to + participatingClients. + +

        17. +

          For each {{WorkerGlobalScope}} worker: + +

            +
          1. For each owner of worker's owner set: if + participatingClients contains owner, then + append worker to participatingClients. +

          + +
        18. +

          For each client of participatingClients: +

            +
          1. +

            For each deferred fetch record deferredRecord in + client's fetch group's + deferred fetch records:

            + +
              +
            1. Let length be deferredRecord's + request's body's + length. -

            2. If totalScheduledDeferredBytesForOrigin is greater than 64 kilobytes, then - throw a "{{QuotaExceededError}}" {{DOMException}}. +

            3. Increment totalScheduledDeferredBytesForTopLevelDocument by length. + +

            4. If totalScheduledDeferredBytesForTopLevelDocument is greater than 640 + kilobytes, then throw a "{{QuotaExceededError}}" {{DOMException}}. + +

            5. If deferredRecord's request's + URL's origin is same origin with + request's origin, then increment + totalScheduledDeferredBytesForOrigin by length. + +

            6. If totalScheduledDeferredBytesForOrigin is greater than 64 kilobytes, then + throw a "{{QuotaExceededError}}" {{DOMException}}. +

            +
          2. +
          +
        19. +
      15. Set request's keepalive to true. @@ -6832,6 +6882,30 @@ i.e. when a fetch group is terminated, or after

    +
    +

    To retrieve the quota-relevant top-level navigables given an +environment settings object client:

    + +
      +
    1. +

      If client is a {{Document}}, then: + +

        +
      1. If client is fully active then return + « client's inclusive ancestor navigables[0] ». + +

      2. Return « ». +

      + +
    2. Let topLevelNavigables be « ». + +

    3. For each owner in client's + owner set, extend + topLevelNavigables with owner's quota-relevant top-level navigables. + +

    4. Return topLevelNavigables. +

    +

    To process deferred fetches given a fetch group fetchGroup: From 547599021b16511f4cda9fbd3b30cad8eb29ec7b Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Tue, 12 Mar 2024 20:29:16 +0000 Subject: [PATCH 030/102] Check permissions policy --- fetch.bs | 47 ++++++++++++++++++++++++++++++++--------------- 1 file changed, 32 insertions(+), 15 deletions(-) diff --git a/fetch.bs b/fetch.bs index 329d65b6e..7dfd469b0 100644 --- a/fetch.bs +++ b/fetch.bs @@ -50,6 +50,10 @@ urlPrefix:https://w3c.github.io/hr-time/#;spec:hr-time url:dfn-unsafe-shared-current-time;text:unsafe shared current time type:typedef;url:dom-domhighrestimestamp;text:DOMHighResTimeStamp +urlPrefix:https://w3c.github.io/webappsec-permissions-policy;spec:permissions-policy + type:dfn + url:is-feature-enabled;text:is feature enabled in document for origin? + urlPrefix:https://tc39.es/ecma262/#;type:dfn;spec:ecma-262 url:realm;text:realm url:sec-list-and-record-specification-type;text:Record @@ -6780,16 +6784,30 @@ i.e. when a fetch group is terminated, or after

    This disallows sending deferred fetches with a live {{ReadableStream}}.

  • -

    Let quotaRelevantTopLevelNavigables be an the - quota-relevant top-level navigables given request's client. +

    Let quotaRelevantDocuments be the + quota-relevant documents given request's client.

    In most cases this will list will contain a single {{Document}}. The exception would be when the request comes from a {{SharedWorker}}, in which case all the documents that directly or indirectly obtain this worker would have to check their quota. +

  • Let topLevelDocuments be an empty set. + +

  • +

    For each doc in quotaRelevantDocuments: + +

      +
    1. If the result of calling is feature enabled in document for origin? given + "deferred-fetch", doc, and request's origin + is Disabled, then throw a {{NotAllowedError}}. + +

    2. Append doc's + inclusive ancestor navigables[0]'s active document to + topLevelDocuments. +

    +
  • -

    For each topLevelNavigable in - quotaRelevantTopLevelNavigables: +

    For each topLevelDocument in topLevelDocuments:

    This algorithm asserts that this deferred fetch doesn't exceed two quotas: one for the top-level document (640kb), and one for the reporting origin (64kb). The larger quota ensures @@ -6806,10 +6824,9 @@ i.e. when a fetch group is terminated, or after

  • Let participatingClients be an empty set. -

  • For each navigable of topLevelNavigable's - active document's inclusive descendant navigables, - append navigable's active document to - participatingClients. +

  • For each navigable of topLevelDocument's + inclusive descendant navigables, append navigable's + active document to participatingClients.

  • For each {{WorkerGlobalScope}} worker: @@ -6882,9 +6899,9 @@ i.e. when a fetch group is terminated, or after

  • -
    -

    To retrieve the quota-relevant top-level navigables given an -environment settings object client:

    +
    +

    To retrieve the quota-relevant documents given an environment settings object +client:

    1. @@ -6892,18 +6909,18 @@ i.e. when a fetch group is terminated, or after
      1. If client is fully active then return - « client's inclusive ancestor navigables[0] ». + « client ».

      2. Return « ».

      -
    2. Let topLevelNavigables be « ». +

    3. Let documents be « ».

    4. For each owner in client's owner set, extend - topLevelNavigables with owner's quota-relevant top-level navigables. + documents with owner's quota-relevant documents. -

    5. Return topLevelNavigables. +

    6. Return documents.

    From 842f23cd22bed512cd54f0feb9d0a935aed1e106 Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Wed, 13 Mar 2024 08:27:12 +0000 Subject: [PATCH 031/102] Narrow scope to Window --- fetch.bs | 121 ++++++++++++++----------------------------------------- 1 file changed, 31 insertions(+), 90 deletions(-) diff --git a/fetch.bs b/fetch.bs index 7dfd469b0..a39451541 100644 --- a/fetch.bs +++ b/fetch.bs @@ -6761,8 +6761,10 @@ i.e. when a fetch group is terminated, or after activateAfter (default null):
      -
    1. Assert: request's client is an - environment settings object. +

    2. Assert: request's client is a fully active + {{Document}}. + +

    3. Let document be request's client.

    4. If request's URL's scheme is not an HTTP(S) scheme then throw a {{TypeError}}. @@ -6783,31 +6785,20 @@ i.e. when a fetch group is terminated, or after

      This disallows sending deferred fetches with a live {{ReadableStream}}. -

    5. -

      Let quotaRelevantDocuments be the - quota-relevant documents given request's client. +

    6. If the result of calling is feature enabled in document for origin? given + "deferred-fetch", document, and request's + origin is Disabled, then throw a {{NotAllowedError}}. -

      In most cases this will list will contain a single {{Document}}. The exception would - be when the request comes from a {{SharedWorker}}, in which case all the documents that directly - or indirectly obtain this worker would have to check their quota. +

    7. Let totalScheduledDeferredBytesForTopLevelDocument be + request's body's length. -

    8. Let topLevelDocuments be an empty set. +

    9. Let totalScheduledDeferredBytesForOrigin be request's + body's length.

    10. -

      For each doc in quotaRelevantDocuments: - -

        -
      1. If the result of calling is feature enabled in document for origin? given - "deferred-fetch", doc, and request's origin - is Disabled, then throw a {{NotAllowedError}}. - -

      2. Append doc's - inclusive ancestor navigables[0]'s active document to - topLevelDocuments. -

      - -
    11. -

      For each topLevelDocument in topLevelDocuments: +

      For each navigable of document's + node navigable's top-level traversable's + inclusive descendant navigables:

      This algorithm asserts that this deferred fetch doesn't exceed two quotas: one for the top-level document (640kb), and one for the reporting origin (64kb). The larger quota ensures @@ -6816,54 +6807,28 @@ i.e. when a fetch group is terminated, or after reserve the whole quota to itself.

        -
      1. Let totalScheduledDeferredBytesForTopLevelDocument be - request's body's length. - -

      2. Let totalScheduledDeferredBytesForOrigin be request's - body's length. - -

      3. Let participatingClients be an empty set. - -

      4. For each navigable of topLevelDocument's - inclusive descendant navigables, append navigable's - active document to participatingClients. -

      5. -

        For each {{WorkerGlobalScope}} worker: +

        For each deferred fetch record deferredRecord in + navigable's active document's fetch group's + deferred fetch records:

          -
        1. For each owner of worker's owner set: if - participatingClients contains owner, then - append worker to participatingClients. -

        +
      6. Let length be deferredRecord's + request's body's + length. -

      7. -

        For each client of participatingClients: -

          -
        1. -

          For each deferred fetch record deferredRecord in - client's fetch group's - deferred fetch records:

          +
        2. Increment totalScheduledDeferredBytesForTopLevelDocument by length. -

            -
          1. Let length be deferredRecord's - request's body's - length. +

          2. If totalScheduledDeferredBytesForTopLevelDocument is greater than 640 + kilobytes, then throw a "{{QuotaExceededError}}" {{DOMException}}. -

          3. Increment totalScheduledDeferredBytesForTopLevelDocument by length. +

          4. If deferredRecord's request's + URL's origin is same origin with + request's origin, then increment + totalScheduledDeferredBytesForOrigin by length. -

          5. If totalScheduledDeferredBytesForTopLevelDocument is greater than 640 - kilobytes, then throw a "{{QuotaExceededError}}" {{DOMException}}. - -

          6. If deferredRecord's request's - URL's origin is same origin with - request's origin, then increment - totalScheduledDeferredBytesForOrigin by length. - -

          7. If totalScheduledDeferredBytesForOrigin is greater than 64 kilobytes, then - throw a "{{QuotaExceededError}}" {{DOMException}}. -

          -
        3. +
        4. If totalScheduledDeferredBytesForOrigin is greater than 64 kilobytes, then + throw a "{{QuotaExceededError}}" {{DOMException}}.

      @@ -6899,30 +6864,6 @@ i.e. when a fetch group is terminated, or after
    -
    -

    To retrieve the quota-relevant documents given an environment settings object -client:

    - -
      -
    1. -

      If client is a {{Document}}, then: - -

        -
      1. If client is fully active then return - « client ». - -

      2. Return « ». -

      - -
    2. Let documents be « ». - -

    3. For each owner in client's - owner set, extend - documents with owner's quota-relevant documents. - -

    4. Return documents. -

    -

    To process deferred fetches given a fetch group fetchGroup: @@ -8820,12 +8761,12 @@ dictionary DeferredRequestInit : RequestInit { DOMHighResTimeStamp activateAfter; }; -[Exposed=Window,Worker] +[Exposed=Window] interface FetchLaterResult { readonly attribute boolean activated; }; -partial interface mixin WindowOrWorkerGlobalScope { +partial interface mixin Window { [NewObject] FetchLaterResult fetchLater(RequestInfo input, optional DeferredRequestInit init = {}); };

    From 4009bcf1caff07b7723eaf93a2813da9b64bd368 Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Wed, 13 Mar 2024 12:01:31 +0000 Subject: [PATCH 032/102] Remove existing NotAllowedError --- fetch.bs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/fetch.bs b/fetch.bs index a39451541..5d6a09359 100644 --- a/fetch.bs +++ b/fetch.bs @@ -6767,7 +6767,7 @@ i.e. when a fetch group is terminated, or after
  • Let document be request's client.

  • If request's URL's scheme is not an - HTTP(S) scheme then throw a {{TypeError}}. + HTTP(S) scheme, then throw a {{TypeError}}.

  • If request's URL is not a [=potentially trustworthy url=], then throw a "{{SecurityError}}" {{DOMException}}. @@ -8786,11 +8786,6 @@ true if this's deferred record's method steps are:

      -
    1. If requestObject's relevant settings object's - origin is not same origin with - requestObject's relevant settings object's - top-level origin, then throw a {{NotAllowedError}}. -

    2. Let requestObject be the result of invoking the initial value of {{Request}} as constructor with input and init as arguments. From a1f2cc794120c826b937b22925876ea1a1b39082 Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Wed, 13 Mar 2024 12:30:20 +0000 Subject: [PATCH 033/102] Fix null-body issues --- fetch.bs | 88 +++++++++++++++++++++++++++++--------------------------- 1 file changed, 46 insertions(+), 42 deletions(-) diff --git a/fetch.bs b/fetch.bs index 5d6a09359..204233723 100644 --- a/fetch.bs +++ b/fetch.bs @@ -6772,67 +6772,71 @@ i.e. when a fetch group is terminated, or after

    3. If request's URL is not a [=potentially trustworthy url=], then throw a "{{SecurityError}}" {{DOMException}}. -

    4. If request's body is null then throw a {{TypeError}}. - -

    5. If request's - body's length is null, then throw a {{TypeError}}. - -

    6. Set request's service-workers mode to "none". - -

    7. -

      If request's body's source is null, then throw a - {{TypeError}}. - -

      This disallows sending deferred fetches with a live {{ReadableStream}}. -

    8. If the result of calling is feature enabled in document for origin? given "deferred-fetch", document, and request's origin is Disabled, then throw a {{NotAllowedError}}. -

    9. Let totalScheduledDeferredBytesForTopLevelDocument be - request's body's length. +

    10. +

      If request's body is not null then: -

    11. Let totalScheduledDeferredBytesForOrigin be request's - body's length. +

        +
      1. If request's + body's length is null, then throw a {{TypeError}}. -

      2. -

        For each navigable of document's - node navigable's top-level traversable's - inclusive descendant navigables: +

      3. +

        If request's body's source is null, then throw a + {{TypeError}}. -

        This algorithm asserts that this deferred fetch doesn't exceed two quotas: one for - the top-level document (640kb), and one for the reporting origin (64kb). The larger quota ensures - that the top-level {{Document}} and its subresources don't continue using an unlimited amount of - bandwidth after being destroyed. The smaller quota ensures that a single reporting sink doesn't - reserve the whole quota to itself. +

        This disallows sending deferred fetches with a live {{ReadableStream}}. + +

      4. Let totalScheduledDeferredBytesForTopLevelDocument be + request's body's length. + +

      5. Let totalScheduledDeferredBytesForOrigin be request's + body's length. -

        1. -

          For each deferred fetch record deferredRecord in - navigable's active document's fetch group's - deferred fetch records:

          +

          For each navigable of document's + node navigable's top-level traversable's + inclusive descendant navigables: + +

          This algorithm asserts that this deferred fetch doesn't exceed two quotas: one for + the top-level document (640kb), and one for the reporting origin (64kb). The larger quota ensures + that the top-level {{Document}} and its subresources don't continue using an unlimited amount of + bandwidth after being destroyed. The smaller quota ensures that a single reporting sink doesn't + reserve the whole quota to itself.

            -
          1. Let length be deferredRecord's - request's body's - length. +

          2. +

            For each deferred fetch record deferredRecord in + navigable's active document's fetch group's + deferred fetch records:

            -
          3. Increment totalScheduledDeferredBytesForTopLevelDocument by length. +

              +
            1. Let length be deferredRecord's + request's body's + length. + +

            2. Increment totalScheduledDeferredBytesForTopLevelDocument by length. -

            3. If totalScheduledDeferredBytesForTopLevelDocument is greater than 640 - kilobytes, then throw a "{{QuotaExceededError}}" {{DOMException}}. +

            4. If totalScheduledDeferredBytesForTopLevelDocument is greater than 640 + kilobytes, then throw a "{{QuotaExceededError}}" {{DOMException}}. -

            5. If deferredRecord's request's - URL's origin is same origin with - request's origin, then increment - totalScheduledDeferredBytesForOrigin by length. +

            6. If deferredRecord's request's + URL's origin is same origin with + request's origin, then increment + totalScheduledDeferredBytesForOrigin by length. -

            7. If totalScheduledDeferredBytesForOrigin is greater than 64 kilobytes, then - throw a "{{QuotaExceededError}}" {{DOMException}}. +

            8. If totalScheduledDeferredBytesForOrigin is greater than 64 kilobytes, then + throw a "{{QuotaExceededError}}" {{DOMException}}. +

            +
        +
      6. Set request's service-workers mode to "none". +

      7. Set request's keepalive to true.

      8. Let deferredRecord be a new deferred fetch record whose From 3ed435f5d7f612dedaeacca6327170e9b84dacfa Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Mon, 25 Mar 2024 16:49:32 +0000 Subject: [PATCH 034/102] Revise based on nits --- fetch.bs | 91 ++++++++++++++++++++++++++++---------------------------- 1 file changed, 45 insertions(+), 46 deletions(-) diff --git a/fetch.bs b/fetch.bs index 204233723..45d1bb98a 100644 --- a/fetch.bs +++ b/fetch.bs @@ -2732,20 +2732,22 @@ functionality.

        Each environment settings object has an associated fetch group. -

        A fetch group holds an ordered list of -fetch records. +

        A fetch group has an associated +fetch records, +a list of fetch records. -

        A fetch group holds a list of -deferred fetch records. +

        A fetch group has an associated +deferred fetch records, +a list of deferred fetch records. -

        A fetch record has an associated -request (a -request). +

        A fetch record is a [=struct=]. It has the following items: -

        A fetch record has an associated -controller (a -fetch controller or null). +

        +
        request +
        A request. +
        controller +
        A fetch controller or null.
        @@ -2763,8 +2765,8 @@ not fully active. It has the following itemsThis value can be modified in parallel. There could be a race condition where the Document object's event loop might read it as - "deferred" at the same time that it is changed to "activated". UAs can - mitigate this race condition in an implementation-defined manner. + "deferred" at the same time that it is changed to "activated". User + agents can mitigate this race condition in an implementation-defined manner.
        @@ -6753,9 +6755,9 @@ agent's CORS-preflight cache for which there is a cache entry matchDeferred fetching

        Deferred fetches allow callers to request that a fetch is invoked at the latest possible moment, -i.e. when a fetch group is terminated, or after a timeout. +i.e., when a fetch group is terminated, or after a timeout. -

        +

        To request a deferred fetch given a request request and a null or {{DOMHighResTimeStamp}} activateAfter (default null): @@ -6770,14 +6772,14 @@ i.e. when a fetch group is terminated, or after HTTP(S) scheme, then throw a {{TypeError}}.

      9. If request's URL is not a - [=potentially trustworthy url=], then throw a "{{SecurityError}}" {{DOMException}}. + potentially trustworthy url, then throw a "{{SecurityError}}" {{DOMException}}. -

      10. If the result of calling is feature enabled in document for origin? given +

      11. If the result of calling is feature enabled in document for origin? given "deferred-fetch", document, and request's origin is Disabled, then throw a {{NotAllowedError}}.

      12. -

        If request's body is not null then: +

        If request's body is non-null then:

        1. If request's @@ -6801,10 +6803,10 @@ i.e. when a fetch group is terminated, or after inclusive descendant navigables:

          This algorithm asserts that this deferred fetch doesn't exceed two quotas: one for - the top-level document (640kb), and one for the reporting origin (64kb). The larger quota ensures - that the top-level {{Document}} and its subresources don't continue using an unlimited amount of - bandwidth after being destroyed. The smaller quota ensures that a single reporting sink doesn't - reserve the whole quota to itself. + the top-level document (640 kibibytes), and one for the reporting origin (64 kibibytes). The + larger quota ensures that the top-level {{Document}} and its subresources don't continue using + an unlimited amount of bandwidth after being destroyed. The smaller quota ensures that a single + reporting sink doesn't reserve the whole quota to itself.

          1. @@ -6820,14 +6822,14 @@ i.e. when a fetch group is terminated, or after
          2. Increment totalScheduledDeferredBytesForTopLevelDocument by length.

          3. If totalScheduledDeferredBytesForTopLevelDocument is greater than 640 - kilobytes, then throw a "{{QuotaExceededError}}" {{DOMException}}. + kibibytes, then throw a "{{QuotaExceededError}}" {{DOMException}}.

          4. If deferredRecord's request's URL's origin is same origin with request's origin, then increment totalScheduledDeferredBytesForOrigin by length. -

          5. If totalScheduledDeferredBytesForOrigin is greater than 64 kilobytes, then +

          6. If totalScheduledDeferredBytesForOrigin is greater than 64 kibibytes, then throw a "{{QuotaExceededError}}" {{DOMException}}.

        2. @@ -6847,16 +6849,16 @@ i.e. when a fetch group is terminated, or after deferred fetch records.
        3. -

          If activateAfter is not null, then run the following steps in parallel:

          +

          If activateAfter is non-null, then run the following steps in parallel:

          1. The user agent should wait until activateAfter milliseconds have passed, or until the user agent has a reason to believe that it is about to lose the opportunity to - execute scripts, e.g. when the browser is moved to the background, or when request's + execute scripts, e.g., when the browser is moved to the background, or when request's client is a Document that had a "hidden" visibility state for a long period of time. -

          2. The user agent may wait for a further implementation-defined duration, e.g. +

          3. The user agent may wait for a further implementation-defined duration, e.g., in order to batch several requests together or to wait until keepalive requests are complete. @@ -6868,7 +6870,7 @@ i.e. when a fetch group is terminated, or after

          -
          +

          To process deferred fetches given a fetch group fetchGroup:

            @@ -6884,7 +6886,7 @@ i.e. when a fetch group is terminated, or after
          -
          +

          To process a deferred fetch deferredRecord:

          1. If deferredRecord's invoke state is not @@ -6899,6 +6901,7 @@ i.e. when a fetch group is terminated, or after

          +

          Fetch API

          The fetch() method is relatively low-level API for @@ -8626,6 +8629,20 @@ otherwise false. partial interface mixin WindowOrWorkerGlobalScope { [NewObject] Promise<Response> fetch(RequestInfo input, optional RequestInit init = {}); }; + + +dictionary DeferredRequestInit : RequestInit { + DOMHighResTimeStamp activateAfter; +}; + +[Exposed=Window] +interface FetchLaterResult { + readonly attribute boolean activated; +}; + +partial interface mixin Window { + [NewObject] FetchLaterResult fetchLater(RequestInfo input, optional DeferredRequestInit init = {}); +};

  • @@ -8759,22 +8776,6 @@ with a promise, request, responseObject, and an
    -
    -
    -dictionary DeferredRequestInit : RequestInit {
    -  DOMHighResTimeStamp activateAfter;
    -};
    -
    -[Exposed=Window]
    -interface FetchLaterResult {
    -  readonly attribute boolean activated;
    -};
    -
    -partial interface mixin Window {
    -  [NewObject] FetchLaterResult fetchLater(RequestInfo input, optional DeferredRequestInit init = {});
    -};
    -
    -

    A {{FetchLaterResult}} has an associated deferred fetch record deferred record. @@ -8804,8 +8805,6 @@ method steps are: exists, then set activateAfter to init["activateAfter"]. -

  • If activateAfter is not a {{DOMHighResTimeStamp}}, then throw a {{TypeError}}. -

  • If activateAfter is less than 0, then throw a {{RangeError}}.

  • Let deferredRecord be the result of calling From 693f569d50ba243663c09d03836a89197ab4789d Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Mon, 25 Mar 2024 16:54:20 +0000 Subject: [PATCH 035/102] Use correct dfn --- fetch.bs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/fetch.bs b/fetch.bs index 45d1bb98a..1c5ccf723 100644 --- a/fetch.bs +++ b/fetch.bs @@ -1822,7 +1822,8 @@ not always relevant and might require different behavior. connect-src navigator.sendBeacon(), {{EventSource}}, HTML's <a ping=""> and <area ping="">, - fetch(), {{XMLHttpRequest}}, {{WebSocket}}, Cache API + fetch(), fetchLater(), {{XMLHttpRequest}}, + {{WebSocket}}, Cache API "object" object-src @@ -8787,7 +8788,7 @@ true if this's deferred record's

    The -fetchLater(input, init) +fetchLater(input, init) method steps are:

      From 92b605dbda243bca30734323c52dd217a449072a Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Mon, 25 Mar 2024 16:58:40 +0000 Subject: [PATCH 036/102] Remove spurious whitespace --- fetch.bs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fetch.bs b/fetch.bs index 1c5ccf723..4cfae2313 100644 --- a/fetch.bs +++ b/fetch.bs @@ -6775,7 +6775,7 @@ i.e., when a fetch group is terminated, or after
    1. If request's URL is not a potentially trustworthy url, then throw a "{{SecurityError}}" {{DOMException}}. -

    2. If the result of calling is feature enabled in document for origin? given +

    3. If the result of calling is feature enabled in document for origin? given "deferred-fetch", document, and request's origin is Disabled, then throw a {{NotAllowedError}}. From afca31d6e2c6cf801f9d7701712eeca15998d260 Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Mon, 25 Mar 2024 17:34:03 +0000 Subject: [PATCH 037/102] Use $ --- fetch.bs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/fetch.bs b/fetch.bs index 4cfae2313..fcef62e26 100644 --- a/fetch.bs +++ b/fetch.bs @@ -50,10 +50,6 @@ urlPrefix:https://w3c.github.io/hr-time/#;spec:hr-time url:dfn-unsafe-shared-current-time;text:unsafe shared current time type:typedef;url:dom-domhighrestimestamp;text:DOMHighResTimeStamp -urlPrefix:https://w3c.github.io/webappsec-permissions-policy;spec:permissions-policy - type:dfn - url:is-feature-enabled;text:is feature enabled in document for origin? - urlPrefix:https://tc39.es/ecma262/#;type:dfn;spec:ecma-262 url:realm;text:realm url:sec-list-and-record-specification-type;text:Record @@ -6775,7 +6771,7 @@ i.e., when a fetch group is terminated, or after

    4. If request's URL is not a potentially trustworthy url, then throw a "{{SecurityError}}" {{DOMException}}. -

    5. If the result of calling is feature enabled in document for origin? given +

    6. If the result of calling [$Is feature enabled in document for origin?$] given "deferred-fetch", document, and request's origin is Disabled, then throw a {{NotAllowedError}}. From 5da508edbf9819f5a9f35b2507c3b8f8452a239e Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Thu, 11 Apr 2024 14:34:59 +0100 Subject: [PATCH 038/102] Use new quota algorithm See https://github.com/WICG/pending-beacon/issues/87#issuecomment-2031368286 --- fetch.bs | 175 ++++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 128 insertions(+), 47 deletions(-) diff --git a/fetch.bs b/fetch.bs index fcef62e26..8d1e7943d 100644 --- a/fetch.bs +++ b/fetch.bs @@ -2737,6 +2737,13 @@ a list of fetch records. deferred fetch records, a list of deferred fetch records. +

      A fetch group has an associated boolean +is eligible for deferred fetching. + +To initialize a fetch group for a given environment settings object +settings, set its is eligible for deferred fetching to the +result of calling check deferred fetching eligibility given settings. +

      A fetch record is a [=struct=]. It has the following items:

      @@ -6771,9 +6778,9 @@ i.e., when a fetch group is terminated, or after
    7. If request's URL is not a potentially trustworthy url, then throw a "{{SecurityError}}" {{DOMException}}. -

    8. If the result of calling [$Is feature enabled in document for origin?$] given - "deferred-fetch", document, and request's - origin is Disabled, then throw a {{NotAllowedError}}. +

    9. If request's client's fetch group + is eligible for deferred fetching is false, then throw a + "{{NotAllowedError}}" {{DOMException}}.

    10. If request's body is non-null then: @@ -6788,50 +6795,11 @@ i.e., when a fetch group is terminated, or after

      This disallows sending deferred fetches with a live {{ReadableStream}}. -

    11. Let totalScheduledDeferredBytesForTopLevelDocument be - request's body's length. - -

    12. Let totalScheduledDeferredBytesForOrigin be request's - body's length. - -

    13. -

      For each navigable of document's - node navigable's top-level traversable's - inclusive descendant navigables: - -

      This algorithm asserts that this deferred fetch doesn't exceed two quotas: one for - the top-level document (640 kibibytes), and one for the reporting origin (64 kibibytes). The - larger quota ensures that the top-level {{Document}} and its subresources don't continue using - an unlimited amount of bandwidth after being destroyed. The smaller quota ensures that a single - reporting sink doesn't reserve the whole quota to itself. - -

        -
      1. -

        For each deferred fetch record deferredRecord in - navigable's active document's fetch group's - deferred fetch records:

        - -
          -
        1. Let length be deferredRecord's - request's body's - length. - -

        2. Increment totalScheduledDeferredBytesForTopLevelDocument by length. - -

        3. If totalScheduledDeferredBytesForTopLevelDocument is greater than 640 - kibibytes, then throw a "{{QuotaExceededError}}" {{DOMException}}. - -

        4. If deferredRecord's request's - URL's origin is same origin with - request's origin, then increment - totalScheduledDeferredBytesForOrigin by length. - -

        5. If totalScheduledDeferredBytesForOrigin is greater than 64 kibibytes, then - throw a "{{QuotaExceededError}}" {{DOMException}}. -

        -
      2. -
      -
    14. +
    15. If the + available deferred fetching quota given document and request's + URL's origin is less than request's + body's length, then throw a "{{QuotaExceededError}}" + {{DOMException}}.

  • Set request's service-workers mode to "none". @@ -6867,6 +6835,119 @@ i.e., when a fetch group is terminated, or after +

    +

    To get the available deferred fetching quota given a Document +document and an origin-or-null origin: + +

    This algorithm asserts that this deferred fetch doesn't exceed two quotas: one for the +top-level document (640 kibibytes), and one for the reporting origin (64 kibibytes). The larger +quota ensures that the top-level {{Document}} and its subresources don't continue using an unlimited +amount of bandwidth after being destroyed. The smaller quota ensures that a single reporting sink +doesn't reserve the whole quota to itself. + +

      +
    1. Let closestSameOriginInclusiveAncestor be document's + node navigable. + +

    2. While closestSameOriginInclusiveAncestor's parent is a + navigable whose active document's origin is same origin + with document's origin, set + closestSameOriginInclusiveAncestor to closestSameOriginInclusiveAncestor's + parent. + +

    3. Let directRelativeSameOriginNavigables be the + inclusive direct same origin descendants of closestSameOriginInclusiveAncestor. + +

    4. Let remainingTotalQuota be 640 kibibytes if + directRelativeSameOriginNavigables includes document's + node navigable's top-level traversable, and 64 kibibytes otherwise. + +

    5. Let remainingQuotaForOrigin be 64 kibibytes. + +

    6. +

      For each navigable in + directRelativeSameOriginNavigables: + +

        +
      1. +

        For each deferred fetch record deferredRecord in + navigable's active document's fetch group's + deferred fetch records:

        + +
          +
        1. Let length be deferredRecord's + request's body's + length. + +

        2. Decrement remainingTotalQuota by length. + +

        3. If origin is not null, and deferredRecord's + request's URL's origin is + same origin with origin, then decrement remainingQuotaForOrigin by + length. +

        +
      2. + +
      3. Let navigableContainers be a list of all shadow-including descendants of + navigable's active document that are navigable containers. + +

      4. +

        For each childNavigable of navigableContainers: + +

          +
        1. If childNavigable's active document's origin is + not same origin with navigable's active document's + origin and childNavigable's active document's + fetch group's is eligible for deferred fetching is true, + then decrement remainingTotalQuota by 64 kibibytes. +

        +
      5. +
      +
    7. + +
    8. If remainingTotalQuota is less than remainingQuotaForOrigin, then + return remainingTotalQuota. + +

    9. Return remainingQuotaForOrigin. +

    +
    + +
    +

    To get the inclusive direct same origin descendants of a navigable +root:

    + +
      +
    1. Let descendants be « root ». + +

    2. Let navigableContainers be a list of all shadow-including descendants + of root's active document that are navigable containers. + +

    3. For each childNavigable of navigableContainers + whose active document's origin is same origin with + navigable's active document's origin, extend + descendants with childNavigable's + inclusive direct same origin descendants. + +

    4. Return descendants. +

    +
    + +
    +To check deferred fetching eligibility given an environment settings object +settings: + +
      +
    1. If settings is not a Document, return false. + +

    2. If the result of calling [$Is feature enabled in document for origin?$] given + settings is Disabled, return false. + +

    3. If the available deferred fetching quota given settings and null is less + than 64 kibibytes, return false. + +

    4. Return true. +

    +

    To process deferred fetches given a fetch group fetchGroup: From 7e101e49b1546e1f86d519404da100d4169dbac3 Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Thu, 11 Apr 2024 14:46:46 +0100 Subject: [PATCH 039/102] nits --- fetch.bs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/fetch.bs b/fetch.bs index 8d1e7943d..32134eac0 100644 --- a/fetch.bs +++ b/fetch.bs @@ -2752,7 +2752,7 @@ result of calling check deferred fetching eligibility given settings

    controller
    A fetch controller or null. - +

    A deferred fetch record is a struct used to maintain state needed @@ -6767,8 +6767,8 @@ i.e., when a fetch group is terminated, or after activateAfter (default null):

      -
    1. Assert: request's client is a fully active - {{Document}}. +

    2. If request's client is not a fully active + {{Document}}, then throw an "{{InvalidStateError}}" {{DOMException}}.

    3. Let document be request's client. @@ -6942,6 +6942,10 @@ To check deferred fetching eligibility given an environment settin

    4. If the result of calling [$Is feature enabled in document for origin?$] given settings is Disabled, return false. +

    5. If settings's node navigable's parent is a + navigable whose active document's fetch group's + is eligible for deferred fetching is true, then return true. +

    6. If the available deferred fetching quota given settings and null is less than 64 kibibytes, return false. From dc22a768ba802750e8855079e81a2af540ef9cbf Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Fri, 26 Apr 2024 15:11:02 +0100 Subject: [PATCH 040/102] Include url+headers in the quota --- fetch.bs | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/fetch.bs b/fetch.bs index 32134eac0..7ed04e1b0 100644 --- a/fetch.bs +++ b/fetch.bs @@ -6770,8 +6770,6 @@ i.e., when a fetch group is terminated, or after

    7. If request's client is not a fully active {{Document}}, then throw an "{{InvalidStateError}}" {{DOMException}}. -

    8. Let document be request's client. -

    9. If request's URL's scheme is not an HTTP(S) scheme, then throw a {{TypeError}}. @@ -6782,6 +6780,14 @@ i.e., when a fetch group is terminated, or after is eligible for deferred fetching is false, then throw a "{{NotAllowedError}}" {{DOMException}}. +

    10. Let totalRequestLength be the [=string/length=] of request's + URL, serialized with + [=URL serializer/exclude fragment=] set to true. + +

    11. For each (name, value) in + header list, increment totalRequestLength by name's + [=byte sequence/length=] + value's [=byte sequence/length=]. +

    12. If request's body is non-null then: @@ -6795,13 +6801,14 @@ i.e., when a fetch group is terminated, or after

      This disallows sending deferred fetches with a live {{ReadableStream}}. -

    13. If the - available deferred fetching quota given document and request's - URL's origin is less than request's - body's length, then throw a "{{QuotaExceededError}}" - {{DOMException}}. +

    14. Increment totalRequestLength by request's + body's length.

    +
  • If the available deferred fetching quota given request's + client and request's URL's origin + is less than totalRequestLength, then throw a "{{QuotaExceededError}}" {{DOMException}}. +

  • Set request's service-workers mode to "none".

  • Set request's keepalive to true. From 02637d22b721d9a82d61262c01f5b5282070d480 Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Thu, 5 Sep 2024 10:34:39 +0100 Subject: [PATCH 041/102] Allow 16kb (UA-specific) even for 3p contexts --- fetch.bs | 61 ++++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 42 insertions(+), 19 deletions(-) diff --git a/fetch.bs b/fetch.bs index 7ed04e1b0..a5f275027 100644 --- a/fetch.bs +++ b/fetch.bs @@ -2738,11 +2738,12 @@ a list of fetch records. a list of deferred fetch records.

    A fetch group has an associated boolean -is eligible for deferred fetching. +is eligible for full-quota deferred fetching. To initialize a fetch group for a given environment settings object -settings, set its is eligible for deferred fetching to the -result of calling check deferred fetching eligibility given settings. +settings, set its is eligible for full-quota deferred fetching +to the result of calling check full-quota deferred fetching eligibility given +settings.

    A fetch record is a [=struct=]. It has the following items: @@ -6776,10 +6777,6 @@ i.e., when a fetch group is terminated, or after

  • If request's URL is not a potentially trustworthy url, then throw a "{{SecurityError}}" {{DOMException}}. -

  • If request's client's fetch group - is eligible for deferred fetching is false, then throw a - "{{NotAllowedError}}" {{DOMException}}. -

  • Let totalRequestLength be the [=string/length=] of request's URL, serialized with [=URL serializer/exclude fragment=] set to true. @@ -6853,6 +6850,16 @@ amount of bandwidth after being destroyed. The smaller quota ensures that a sing doesn't reserve the whole quota to itself.

      +
    1. +

      If request's client's fetch group + is eligible for full-quota deferred fetching is false, the user agent may + return 0. + +

      This allows implementations to prevent 3rd party environments from taking up + non-abortable bandwidth without the consent of the top-level document. Without this constraint, + those environments can still take up a small amount of bandwidth (16 kibibytes) for deferred + fetching. +

    2. Let closestSameOriginInclusiveAncestor be document's node navigable. @@ -6865,11 +6872,22 @@ doesn't reserve the whole quota to itself.

    3. Let directRelativeSameOriginNavigables be the inclusive direct same origin descendants of closestSameOriginInclusiveAncestor. -

    4. Let remainingTotalQuota be 640 kibibytes if - directRelativeSameOriginNavigables includes document's - node navigable's top-level traversable, and 64 kibibytes otherwise. +

    5. +

      Let remainingTotalQuota be the result of the first matching statement: + +

      +
      directRelativeSameOriginNavigables includes document's + node navigable's top-level traversable +
      640 kibibytes -
    6. Let remainingQuotaForOrigin be 64 kibibytes. +

      request's client's fetch group + is eligible for full-quota deferred fetching is true +
      64 kibibytes + + +
      Otherwise +
      16 kibibytes +
    7. For each navigable in @@ -6905,7 +6923,8 @@ doesn't reserve the whole quota to itself.

    8. If childNavigable's active document's origin is not same origin with navigable's active document's origin and childNavigable's active document's - fetch group's is eligible for deferred fetching is true, + fetch group's + is eligible for full-quota deferred fetching is true, then decrement remainingTotalQuota by 64 kibibytes.

  • @@ -6940,8 +6959,8 @@ doesn't reserve the whole quota to itself.
    -To check deferred fetching eligibility given an environment settings object -settings: +To check full-quota deferred fetching eligibility given an +environment settings object settings:
    1. If settings is not a Document, return false. @@ -6949,12 +6968,16 @@ To check deferred fetching eligibility given an environment settin

    2. If the result of calling [$Is feature enabled in document for origin?$] given settings is Disabled, return false. -

    3. If settings's node navigable's parent is a - navigable whose active document's fetch group's - is eligible for deferred fetching is true, then return true. +

    4. Let parentDocument be settings's node navigable's + parent's active document. + +

    5. If parentDocument is null, then return true. + +

    6. If parentDocument's origin is same origin with + settings's origin then return true. -

    7. If the available deferred fetching quota given settings and null is less - than 64 kibibytes, return false. +

    8. If the available deferred fetching quota given parentDocument and null is + less than 64 kibibytes, return false.

    9. Return true.

    From ff93d287be971c02b1838cde315e315733c0176b Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Thu, 24 Oct 2024 13:57:12 +0100 Subject: [PATCH 042/102] Update for new quota policy --- fetch.bs | 116 ++++++++++++++----------------------------------------- 1 file changed, 28 insertions(+), 88 deletions(-) diff --git a/fetch.bs b/fetch.bs index a5f275027..713686da2 100644 --- a/fetch.bs +++ b/fetch.bs @@ -2737,14 +2737,6 @@ a list of fetch records. deferred fetch records, a list of deferred fetch records. -

    A fetch group has an associated boolean -is eligible for full-quota deferred fetching. - -To initialize a fetch group for a given environment settings object -settings, set its is eligible for full-quota deferred fetching -to the result of calling check full-quota deferred fetching eligibility given -settings. -

    A fetch record is a [=struct=]. It has the following items:

    @@ -2774,6 +2766,8 @@ not fully active. It has the following itemsimplementation-defined manner.
    +

    A navigable has an associated allotted deferred fetching quota, initially +zero.


    @@ -6844,56 +6838,34 @@ i.e., when a fetch group is terminated, or after document and an origin-or-null origin:

    This algorithm asserts that this deferred fetch doesn't exceed two quotas: one for the -top-level document (640 kibibytes), and one for the reporting origin (64 kibibytes). The larger -quota ensures that the top-level {{Document}} and its subresources don't continue using an unlimited -amount of bandwidth after being destroyed. The smaller quota ensures that a single reporting sink -doesn't reserve the whole quota to itself. - +document and it's same-origin same-tree relatives, and one for the reporting origin (64 kibibytes). +The larger quota ensures that the top-level {{Document}} and its subresources don't continue using +an unlimited amount of bandwidth after being destroyed. The smaller quota ensures that a single +reporting sink doesn't reserve the whole quota to itself.

      -
    1. -

      If request's client's fetch group - is eligible for full-quota deferred fetching is false, the user agent may - return 0. - -

      This allows implementations to prevent 3rd party environments from taking up - non-abortable bandwidth without the consent of the top-level document. Without this constraint, - those environments can still take up a small amount of bandwidth (16 kibibytes) for deferred - fetching. +

    2. Let relativeNavigables be all navigables whose + active document's relevant agent is document's relevant agent's, + top-level traversable is document's node navigable's + top-level traversable, and whose active document's + origin is same origin with document's + origin. -

    3. Let closestSameOriginInclusiveAncestor be document's - node navigable. - -

    4. While closestSameOriginInclusiveAncestor's parent is a - navigable whose active document's origin is same origin - with document's origin, set - closestSameOriginInclusiveAncestor to closestSameOriginInclusiveAncestor's - parent. - -

    5. Let directRelativeSameOriginNavigables be the - inclusive direct same origin descendants of closestSameOriginInclusiveAncestor. +

    6. Let remainingTotalQuota be zero.

    7. -

      Let remainingTotalQuota be the result of the first matching statement: - -

      -
      directRelativeSameOriginNavigables includes document's - node navigable's top-level traversable -
      640 kibibytes - -
      request's client's fetch group - is eligible for full-quota deferred fetching is true -
      64 kibibytes +

      For each navigable in relativeNavigables: +

        +
      1. Increment remainingTotalQuota by navigable's + allotted deferred fetching quota.

        +
      2. -
        Otherwise -
        16 kibibytes -
      +
    8. Let remainingQuotaForOrigin be remainingTotalQuota. +

    9. If remainingQuotaForOrigin is greater than 64 kibibytes, then set + remainingQuotaForOrigin to 64 kibibytes.

    10. -

      For each navigable in - directRelativeSameOriginNavigables: - -

        +

        For each navigable in relativeNavigables:

      1. For each deferred fetch record deferredRecord in navigable's active document's fetch group's @@ -6913,21 +6885,13 @@ doesn't reserve the whole quota to itself.

    11. -
    12. Let navigableContainers be a list of all shadow-including descendants of - navigable's active document that are navigable containers. +

    13. For each descendant of navigable's + active document's shadow-including descendants that are + navigable containers, decrement remainingTotalQuota by descendant's + content navigable's allotted deferred fetching quota. -

    14. -

      For each childNavigable of navigableContainers: - -

        -
      1. If childNavigable's active document's origin is - not same origin with navigable's active document's - origin and childNavigable's active document's - fetch group's - is eligible for full-quota deferred fetching is true, - then decrement remainingTotalQuota by 64 kibibytes. -

      -
    15. +
    16. If remainingTotalQuota is greater than remainingQuotaForOrigin + return remainingQuotaForOrigin; Otherwise return remainingTotalQuota.

  • @@ -6958,30 +6922,6 @@ doesn't reserve the whole quota to itself. -
    -To check full-quota deferred fetching eligibility given an -environment settings object settings: - -
      -
    1. If settings is not a Document, return false. - -

    2. If the result of calling [$Is feature enabled in document for origin?$] given - settings is Disabled, return false. - -

    3. Let parentDocument be settings's node navigable's - parent's active document. - -

    4. If parentDocument is null, then return true. - -

    5. If parentDocument's origin is same origin with - settings's origin then return true. - -

    6. If the available deferred fetching quota given parentDocument and null is - less than 64 kibibytes, return false. - -

    7. Return true. -

    -

    To process deferred fetches given a fetch group fetchGroup: From 8147f0d284172fd93fd3040c7313623b5dc56faf Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Thu, 24 Oct 2024 13:59:43 +0100 Subject: [PATCH 043/102] Fix

      --- fetch.bs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/fetch.bs b/fetch.bs index 713686da2..42c2633db 100644 --- a/fetch.bs +++ b/fetch.bs @@ -6853,12 +6853,9 @@ reporting sink doesn't reserve the whole quota to itself.
    1. Let remainingTotalQuota be zero.

    2. -

      For each navigable in relativeNavigables: - -

        -
      1. Increment remainingTotalQuota by navigable's - allotted deferred fetching quota.

        -
      2. +

        For each navigable in relativeNavigables, increment + remainingTotalQuota by navigable's + allotted deferred fetching quota.

      3. Let remainingQuotaForOrigin be remainingTotalQuota.

      4. If remainingQuotaForOrigin is greater than 64 kibibytes, then set @@ -6866,6 +6863,7 @@ reporting sink doesn't reserve the whole quota to itself.

      5. For each navigable in relativeNavigables: +

        1. For each deferred fetch record deferredRecord in navigable's active document's fetch group's From d49ac64702432d21ded94dd00f2fb6bbebcc47b8 Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Thu, 24 Oct 2024 14:02:58 +0100 Subject: [PATCH 044/102] Only decrement for cross-origin --- fetch.bs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/fetch.bs b/fetch.bs index 42c2633db..4b088ab2a 100644 --- a/fetch.bs +++ b/fetch.bs @@ -6885,8 +6885,9 @@ reporting sink doesn't reserve the whole quota to itself.

        2. For each descendant of navigable's active document's shadow-including descendants that are - navigable containers, decrement remainingTotalQuota by descendant's - content navigable's allotted deferred fetching quota. + navigable containers, if relativeNavigables does not contain + descendant's content navigable, then decrement remainingTotalQuota + by descendant's content navigable's allotted deferred fetching quota.

        3. If remainingTotalQuota is greater than remainingQuotaForOrigin return remainingQuotaForOrigin; Otherwise return remainingTotalQuota. From 0ea54ef5e4d860fe162cc7b702527b231ef8cb65 Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Thu, 24 Oct 2024 16:19:45 +0100 Subject: [PATCH 045/102] Apply permission policy --- fetch.bs | 100 +++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 86 insertions(+), 14 deletions(-) diff --git a/fetch.bs b/fetch.bs index 4b088ab2a..f62b13559 100644 --- a/fetch.bs +++ b/fetch.bs @@ -42,6 +42,7 @@ urlPrefix:https://httpwg.org/specs/rfc9112.html#;type:dfn;spec:http1 url:status.line;text:reason-phrase url:https://w3c.github.io/resource-timing/#dfn-mark-resource-timing;text:mark resource timing;type:dfn;spec:resource-timing +url:https://w3c.github.io/webappsec-permissions-policy/#algo-define-inherited-policy-in-container;text:define an inherited policy for feature in container;type:dfn urlPrefix:https://w3c.github.io/hr-time/#;spec:hr-time type:dfn @@ -2733,6 +2734,9 @@ functionality. fetch records, a list of fetch records. +

          Each [=/navigable=] has an associated allotted deferred fetching quota, a number +(initially zero). +

          A fetch group has an associated deferred fetch records, a list of deferred fetch records. @@ -2766,9 +2770,6 @@ not fully active. It has the following itemsimplementation-defined manner. -

          A navigable has an associated allotted deferred fetching quota, initially -zero. -


          When a fetch group fetchGroup is @@ -6902,22 +6903,93 @@ reporting sink doesn't reserve the whole quota to itself.

    -

    To get the inclusive direct same origin descendants of a navigable -root:

    +

    To allocate top-level deferred fetching quota for a +top-level traversable traversable: + +

    This is called when creating a new top-level document.

      -
    1. Let descendants be « root ». +

    2. Let document be traversable's active document. + +

    3. Let origin be document's URL's + origin. + +

    4. If the result of calling [$Is feature enabled in document for origin?$] given + deferred-fetch, document and origin is Disabled, + then set traversable's allotted deferred fetching quota to 0 and return. -

    5. Let navigableContainers be a list of all shadow-including descendants - of root's active document that are navigable containers. +

    6. +

      If the result of calling [$Is feature enabled in document for origin?$] given + deferred-fetch-minimal, document and origin is Disabled, + then return 512 kibibytes. + is Enabled, then set traversable's allotted deferred fetching quota to + 512 kibibytes and return. + +

      When this permission policy is enabled, this document delegates 8KB to each of the + first cross-origin subframes. + +

    7. +

      Set traversable's allotted deferred fetching quota to 640 kibibytes. + +

      640kb should be enough for everyone. +

    +
    + +
    +

    To allocate subframe deferred fetching quota for a [=/navigable] +navigable and an [=/origin=] initialOrigin: + +

    This is called when the container navigates the subframe. + +

      +
    1. Assert: navigable's [=navigable/parent=] is not null. + +

    2. Let parentDocument be navigable's [=navigable/parent=]'s + [=navigable/active document=]. + +

    3. If the [=define an inherited policy for feature in container|inherited policy=] + for deferred-fetch, navigable's [=navigable container=] and + initialOrigin is Enabled, and the [=available deferred fetching quota=] + given parentDocument and null is greater than 64 kibibytes, then set + navigable's [=allotted deferred fetching quota=] to 64 kibibytes and return. + +

    4. If the [=define an inherited policy for feature in container|inherited policy=] + for deferred-fetch-minimal, navigable's [=navigable container=] and + initialOrigin is Disabled, then return zero. + +

    5. Let topLevelRelatives be all navigables whose + active document's relevant agent is parentDocument's + relevant agent's, top-level traversable is navigable's + top-level traversable, and whose active document's + origin is same origin with parentDocument's [=Document/URL=]'s + origin. + +

    6. +

      If topLevelRelatives does not [=list/contain=] parentDocument's + [=node navigable=]'s top-level traversable, then set navigable's + [=allotted deferred fetching quota=] to 0 and return. + +

      Only the top level document and related documents can delegate the + deferred-fetch-minimal permission. + +

    7. Let quota be 128 kibibytes. + +

    8. +

      For each navigable that matches the following conditions: + +

      -
    9. For each childNavigable of navigableContainers - whose active document's origin is same origin with - navigable's active document's origin, extend - descendants with childNavigable's - inclusive direct same origin descendants. +

      Decrement quota by 8 kibibytes. -

    10. Return descendants. +

    11. If quota is zero, then set navigable's + [=allotted deferred fetching quota=] to 0; otherwise 8 kibibytes.

    From 7bbed7600da68664fccac3be36dd2eb802bdd066 Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Thu, 24 Oct 2024 16:25:33 +0100 Subject: [PATCH 046/102] nits --- fetch.bs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/fetch.bs b/fetch.bs index f62b13559..087d299e7 100644 --- a/fetch.bs +++ b/fetch.bs @@ -6920,13 +6920,12 @@ reporting sink doesn't reserve the whole quota to itself.
  • If the result of calling [$Is feature enabled in document for origin?$] given - deferred-fetch-minimal, document and origin is Disabled, - then return 512 kibibytes. - is Enabled, then set traversable's allotted deferred fetching quota to - 512 kibibytes and return. + deferred-fetch-minimal, document and origin is + Enabled, then set traversable's allotted deferred fetching quota + to 512 kibibytes and return.

    When this permission policy is enabled, this document delegates 8KB to each of the - first cross-origin subframes. + first 16 cross-origin subframes leaving 512kb for its own quota.

  • Set traversable's allotted deferred fetching quota to 640 kibibytes. @@ -6936,7 +6935,7 @@ reporting sink doesn't reserve the whole quota to itself.

    -

    To allocate subframe deferred fetching quota for a [=/navigable] +

    To allocate subframe deferred fetching quota for a [=/navigable=] navigable and an [=/origin=] initialOrigin:

    This is called when the container navigates the subframe. @@ -6950,7 +6949,7 @@ reporting sink doesn't reserve the whole quota to itself.

  • If the [=define an inherited policy for feature in container|inherited policy=] for deferred-fetch, navigable's [=navigable container=] and initialOrigin is Enabled, and the [=available deferred fetching quota=] - given parentDocument and null is greater than 64 kibibytes, then set + given parentDocument and null is equal greater than 64 kibibytes, then set navigable's [=allotted deferred fetching quota=] to 64 kibibytes and return.

  • If the [=define an inherited policy for feature in container|inherited policy=] From 251234649f77ac4b44fde16210d336982ec9bd33 Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Mon, 28 Oct 2024 09:13:51 +0000 Subject: [PATCH 047/102] refactor --- fetch.bs | 180 +++++++++++++++++++++++++++++++------------------------ 1 file changed, 102 insertions(+), 78 deletions(-) diff --git a/fetch.bs b/fetch.bs index 087d299e7..6857272ce 100644 --- a/fetch.bs +++ b/fetch.bs @@ -2734,8 +2734,9 @@ functionality. fetch records, a list of fetch records. -

    Each [=/navigable=] has an associated allotted deferred fetching quota, a number -(initially zero). +

    Each navigable container has an associated deferred fetching policy +(disabled, deferred-fetch and deferred-fetch-minimal). Unless +stated otherwise, it is disabled.

    A fetch group has an associated deferred fetch records, @@ -6844,21 +6845,47 @@ The larger quota ensures that the top-level {{Document}} and its subresources do an unlimited amount of bandwidth after being destroyed. The smaller quota ensures that a single reporting sink doesn't reserve the whole quota to itself.

      -
    1. Let relativeNavigables be all navigables whose - active document's relevant agent is document's relevant agent's, - top-level traversable is document's node navigable's - top-level traversable, and whose active document's - origin is same origin with document's - origin. +

    2. Let relativeNavigables be document's + direct relative navigables.

    3. Let remainingTotalQuota be zero.

    4. -

      For each navigable in relativeNavigables, increment - remainingTotalQuota by navigable's - allotted deferred fetching quota.

      +

      For each navigable in relativeNavigables: +

        +
      1. +

        If document's node navigable is a top level traversable then: +

          +
        1. Let origin be document's URL's + origin. + +

        2. If the result of calling [$Is feature enabled in document for origin?$] given + deferred-fetch, document and origin is + Disabled, then return 0. + +

        3. +

          If the result of calling [$Is feature enabled in document for origin?$] given + deferred-fetch-minimal, document and origin is + Enabled, then set remainingTotalQuota to 512 kibibytes. + +

          When this permission policy is enabled, this document delegates 8KB to each of + the first 16 cross-origin subframes leaving 512kb for its own quota. + +

        4. +

          Set remainingTotalQuota to 640 kibibytes. +

          640kb should be enough for everyone. +

        + +
      2. Otherwise, of the result of calling [$Is feature enabled in document for origin?$] given + document's node navigable's container's + deferred fetching policy, document and origin is + Enabled, then set remainingTotalQuota to the + initial subframe deferred fetch quota given document's node navigable's + container's deferred fetching policy. +

    5. Let remainingQuotaForOrigin be remainingTotalQuota. +

    6. If remainingQuotaForOrigin is greater than 64 kibibytes, then set remainingQuotaForOrigin to 64 kibibytes. @@ -6884,15 +6911,21 @@ reporting sink doesn't reserve the whole quota to itself.

  • -
  • For each descendant of navigable's - active document's shadow-including descendants that are - navigable containers, if relativeNavigables does not contain - descendant's content navigable, then decrement remainingTotalQuota - by descendant's content navigable's allotted deferred fetching quota. +

  • Let relevantContainers be « ». +

  • For each relative in navigable's + direct relative navigables, extend relevantContainers with + every shadow-including descendant of relative which is a + navigable container. -

  • If remainingTotalQuota is greater than remainingQuotaForOrigin - return remainingQuotaForOrigin; Otherwise return remainingTotalQuota. - +

  • +

    For each descendantContainer of relevantContainers, + decrement remainingTotalQuota by the initial subframe deferred fetch quota + given descendantContainer's deferred fetching policy: + +

  • If remainingTotalQuota is less than remainingQuotaForOrigin, then + return remainingTotalQuota. + +

  • Return remainingQuotaForOrigin.

  • If remainingTotalQuota is less than remainingQuotaForOrigin, then @@ -6903,95 +6936,86 @@ reporting sink doesn't reserve the whole quota to itself.

    -

    To allocate top-level deferred fetching quota for a -top-level traversable traversable: - -

    This is called when creating a new top-level document. - -

      -
    1. Let document be traversable's active document. - -

    2. Let origin be document's URL's - origin. - -

    3. If the result of calling [$Is feature enabled in document for origin?$] given - deferred-fetch, document and origin is Disabled, - then set traversable's allotted deferred fetching quota to 0 and return. - -

    4. -

      If the result of calling [$Is feature enabled in document for origin?$] given - deferred-fetch-minimal, document and origin is - Enabled, then set traversable's allotted deferred fetching quota - to 512 kibibytes and return. +To determine the initial subframe deferred fetch quota given policy, switch on +policy and return the result: +

      +
      deferred-fetch +
      64 kibibytes -

      When this permission policy is enabled, this document delegates 8KB to each of the - first 16 cross-origin subframes leaving 512kb for its own quota. +

      deferred-fetch-minimal +
      8 kibibytes -
    5. -

      Set traversable's allotted deferred fetching quota to 640 kibibytes. - -

      640kb should be enough for everyone. -

    +
    disabled +
    0 kibibytes +
    -

    To allocate subframe deferred fetching quota for a [=/navigable=] -navigable and an [=/origin=] initialOrigin: +

    To determine subframe deferred fetching policy for a navigable container +container and an origin originToNavigateTo: -

    This is called when the container navigates the subframe. +

    This is called whenever a navigable container is being navigated, e.g. an +IFrame changes its src attribute.

      -
    1. Assert: navigable's [=navigable/parent=] is not null. +

    2. Set container's deferred fetch policy to disabled. -

    3. Let parentDocument be navigable's [=navigable/parent=]'s - [=navigable/active document=]. +

    4. If the + for deferred-fetch, navigable's navigable container and + originToNavigateTo is Enabled, and the + available deferred fetching quota for container's container document is + equal greater than 64 kibibytes, then set navigable's + deferred fetch policy to deferred-fetch and return. -

    5. If the [=define an inherited policy for feature in container|inherited policy=] - for deferred-fetch, navigable's [=navigable container=] and - initialOrigin is Enabled, and the [=available deferred fetching quota=] - given parentDocument and null is equal greater than 64 kibibytes, then set - navigable's [=allotted deferred fetching quota=] to 64 kibibytes and return. +

    6. If the inherited policy + for deferred-fetch-minimal, container and originToNavigateTo is + Disabled, then set container's deferred fetch policy to + disabled and return. -

    7. If the [=define an inherited policy for feature in container|inherited policy=] - for deferred-fetch-minimal, navigable's [=navigable container=] and - initialOrigin is Disabled, then return zero. -

    8. Let topLevelRelatives be all navigables whose - active document's relevant agent is parentDocument's - relevant agent's, top-level traversable is navigable's - top-level traversable, and whose active document's - origin is same origin with parentDocument's [=Document/URL=]'s - origin. +

    9. Let topLevelRelatives be container's container document's + direct relative navigables.

    10. -

      If topLevelRelatives does not [=list/contain=] parentDocument's - [=node navigable=]'s top-level traversable, then set navigable's - [=allotted deferred fetching quota=] to 0 and return. +

      If topLevelRelatives does not contain container's + node navigable's top-level traversable, then + set container's deferred fetch policy to disabled and + return.

      Only the top level document and related documents can delegate the - deferred-fetch-minimal permission. + deferred-fetch-minimal policy. -

    11. Let quota be 128 kibibytes. +

    12. Let framesWithminimalQuota be zero. +

      For each navigable that matches the following conditions:

    13. -

      For each navigable that matches the following conditions:

      -

      Decrement quota by 8 kibibytes. +

      Increment quota by 1. -

    14. If quota is zero, then set navigable's - [=allotted deferred fetching quota=] to 0; otherwise 8 kibibytes. +

    15. If quota is less than 16, then set container's + deferred fetch policy to deferred-fetch-minimal.

    +
    +The direct relative navigables of a Document document are all +the navigables whose active document's relevant agent is +document's relevant agent's, top-level traversable is +navigable's top-level traversable, and whose +active document's origin is same origin with document's +URL's origin. +
    +

    To process deferred fetches given a fetch group fetchGroup: From 45f18de36d9061e025b9b7c61d3b9dd9a8ad93eb Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Mon, 28 Oct 2024 09:36:15 +0000 Subject: [PATCH 048/102] Refactor quota based on new explainer --- fetch.bs | 135 ++++++++++++++++++++++++++++--------------------------- 1 file changed, 68 insertions(+), 67 deletions(-) diff --git a/fetch.bs b/fetch.bs index 6857272ce..5fe7e3759 100644 --- a/fetch.bs +++ b/fetch.bs @@ -2734,7 +2734,7 @@ functionality. fetch records, a list of fetch records. -

    Each navigable container has an associated deferred fetching policy +

    Each navigable container has an associated deferred fetch policy (disabled, deferred-fetch and deferred-fetch-minimal). Unless stated otherwise, it is disabled. @@ -6836,17 +6836,50 @@ i.e., when a fetch group is terminated, or after

    -

    To get the available deferred fetching quota given a Document -document and an origin-or-null origin: +

    To process deferred fetches given a fetch group fetchGroup: + +

      +
    1. Let deferredFetchRecords be fetchGroup's + deferred fetch records. + +

    2. Let fetchGroup's + deferred fetch records be « ». -

      This algorithm asserts that this deferred fetch doesn't exceed two quotas: one for the +

    3. For each deferred fetch record + deferredRecord of deferredFetchRecords, process a deferred fetch given + deferredRecord. +

    +
    + +
    +

    To process a deferred fetch deferredRecord: +

      +
    1. If deferredRecord's invoke state is not + "deferred", then return. + +

    2. Set deferredRecord's invoke state to + "activated". + +

    3. Fetch deferredRecord's + request. +

    +
    + +

    Deferred fetching quota

    + +

    The quota asserts that this deferred fetch doesn't exceed two quotas: one for the document and it's same-origin same-tree relatives, and one for the reporting origin (64 kibibytes). The larger quota ensures that the top-level {{Document}} and its subresources don't continue using -an unlimited amount of bandwidth after being destroyed. The smaller quota ensures that a single -reporting sink doesn't reserve the whole quota to itself. +an unlimited amount of bandwidth after being destroyed. The per-origin quota ensures that a single +reporting sink (e.g. RUM library) doesn't reserve the whole quota to itself. + +

    +

    To get the available deferred fetching quota given a Document +document and an origin-or-null origin: +

    1. Let relativeNavigables be document's - direct relative navigables. + deferred fetch quota-sharing navigables.

    2. Let remainingTotalQuota be zero. @@ -6854,7 +6887,7 @@ reporting sink doesn't reserve the whole quota to itself.

      For each navigable in relativeNavigables:

      1. -

        If document's node navigable is a top level traversable then: +

        If document's node navigable is a top-level traversable then:

        1. Let origin be document's URL's origin. @@ -6878,10 +6911,10 @@ reporting sink doesn't reserve the whole quota to itself.

        2. Otherwise, of the result of calling [$Is feature enabled in document for origin?$] given document's node navigable's container's - deferred fetching policy, document and origin is + deferred fetch policy, document and origin is Enabled, then set remainingTotalQuota to the initial subframe deferred fetch quota given document's node navigable's - container's deferred fetching policy. + container's deferred fetch policy.

      2. Let remainingQuotaForOrigin be remainingTotalQuota. @@ -6913,14 +6946,14 @@ reporting sink doesn't reserve the whole quota to itself.

      3. Let relevantContainers be « ».

      4. For each relative in navigable's - direct relative navigables, extend relevantContainers with - every shadow-including descendant of relative which is a - navigable container. + deferred fetch quota-sharing navigables, extend + relevantContainers with every shadow-including descendant of + relative which is a navigable container.

      5. For each descendantContainer of relevantContainers, decrement remainingTotalQuota by the initial subframe deferred fetch quota - given descendantContainer's deferred fetching policy: + given descendantContainer's deferred fetch policy:

      6. If remainingTotalQuota is less than remainingQuotaForOrigin, then return remainingTotalQuota. @@ -6936,8 +6969,8 @@ reporting sink doesn't reserve the whole quota to itself.

    -To determine the initial subframe deferred fetch quota given policy, switch on -policy and return the result: +The initial subframe deferred fetch quota is determined by switching on the given +policy:
    deferred-fetch
    64 kibibytes @@ -6951,30 +6984,30 @@ To determine the initial subframe deferred fetch quota given pol
    -

    To determine subframe deferred fetching policy for a navigable container +

    To determine subframe deferred fetch policy for a navigable container container and an origin originToNavigateTo: -

    This is called whenever a navigable container is being navigated, e.g. an -IFrame changes its src attribute. +

    This is called whenever a navigable container is being navigated, e.g. by +setting an iframe's src.

    1. Set container's deferred fetch policy to disabled. -

    2. If the +

    3. If the inherited policy for deferred-fetch, navigable's navigable container and originToNavigateTo is Enabled, and the available deferred fetching quota for container's container document is - equal greater than 64 kibibytes, then set navigable's + equal or greater than 64 kibibytes, then set navigable's deferred fetch policy to deferred-fetch and return. -

    4. If the inherited policy +

    5. If the inherited policy for deferred-fetch-minimal, container and originToNavigateTo is Disabled, then set container's deferred fetch policy to disabled and return.

    6. Let topLevelRelatives be container's container document's - direct relative navigables. + deferred fetch quota-sharing navigables.

    7. If topLevelRelatives does not contain container's @@ -6985,68 +7018,36 @@ IFrame changes its src attribute.

      Only the top level document and related documents can delegate the deferred-fetch-minimal policy. -

    8. Let framesWithminimalQuota be zero. -

      For each navigable that matches the following conditions: - +

    9. Let framesWithMinimalQuotaPolicy be zero.

    10. - +

      For each navigable that matches the following conditions:

      -

      Increment quota by 1. +

      Increment framesWithMinimalQuotaPolicy by 1. -

    11. If quota is less than 16, then set container's +

    12. If framesWithMinimalQuotaPolicy is less than 16, then set container's deferred fetch policy to deferred-fetch-minimal.

    -The direct relative navigables of a Document document are all -the navigables whose active document's relevant agent is -document's relevant agent's, top-level traversable is -navigable's top-level traversable, and whose -active document's origin is same origin with document's +The deferred fetch quota-sharing navigables of a Document +document are al; the navigables whose active document's +relevant agent is document's relevant agent's, +top-level traversable is document's node navigable's +top-level traversable, and whose active document's +origin is same origin with document's URL's origin.
    -
    -

    To process deferred fetches given a fetch group fetchGroup: - -

      -
    1. Let deferredFetchRecords be fetchGroup's - deferred fetch records. - -

    2. Let fetchGroup's - deferred fetch records be « ». - -

    3. For each deferred fetch record - deferredRecord of deferredFetchRecords, process a deferred fetch given - deferredRecord. -

    -
    - -
    -

    To process a deferred fetch deferredRecord: -

      -
    1. If deferredRecord's invoke state is not - "deferred", then return. - -

    2. Set deferredRecord's invoke state to - "activated". - -

    3. Fetch deferredRecord's - request. -

    -
    - -

    Fetch API

    From fe47282b61d1d00596598096d715af09995d150c Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Mon, 28 Oct 2024 09:48:14 +0000 Subject: [PATCH 049/102] nits --- fetch.bs | 38 ++++++++++++++++++++++---------------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/fetch.bs b/fetch.bs index 5fe7e3759..3941df648 100644 --- a/fetch.bs +++ b/fetch.bs @@ -2735,7 +2735,7 @@ functionality. a list of fetch records.

    Each navigable container has an associated deferred fetch policy -(disabled, deferred-fetch and deferred-fetch-minimal). Unless +(disabled, deferred-fetch or deferred-fetch-minimal). Unless stated otherwise, it is disabled.

    A fetch group has an associated @@ -6968,21 +6968,6 @@ reporting sink (e.g. RUM library) doesn't reserve the whole quota to itself. -

    -The initial subframe deferred fetch quota is determined by switching on the given -policy: -
    -
    deferred-fetch -
    64 kibibytes - -
    deferred-fetch-minimal -
    8 kibibytes - -
    disabled -
    0 kibibytes - -
    -

    To determine subframe deferred fetch policy for a navigable container container and an origin originToNavigateTo: @@ -7022,6 +7007,9 @@ setting an iframe's src.

  • For each navigable that matches the following conditions:

      + +
    • navigable is not container's content navigable. +

    • topLevelRelatives contains navigable's parent @@ -7038,6 +7026,24 @@ setting an iframe's src. +

      +The initial subframe deferred fetch quota is determined by the following table: + + + + + +
      Policy + Quota +
      deferred-fetch + 64 kibibytes +
      deferred-fetch-minimal + 8 kibibytes +
      disabled + 0 kibibytes +
      +
      +
      The deferred fetch quota-sharing navigables of a Document document are al; the navigables whose active document's From 305b3a0637c678cddf9465578a986a91e1c221f5 Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Thu, 31 Oct 2024 09:38:25 +0000 Subject: [PATCH 050/102] Clarify permission policy --- fetch.bs | 42 +++++++++++++++++++++++++----------------- 1 file changed, 25 insertions(+), 17 deletions(-) diff --git a/fetch.bs b/fetch.bs index 3941df648..4f21d3e27 100644 --- a/fetch.bs +++ b/fetch.bs @@ -2735,7 +2735,8 @@ functionality. a list of fetch records.

      Each navigable container has an associated deferred fetch policy -(disabled, deferred-fetch or deferred-fetch-minimal). Unless +({{PermissionPolicy/"deferred-fetch"}}, {{PermissionPolicy/"deferred-fetch-minimal"}}, or +Disabled). Unless stated otherwise, it is disabled.

      A fetch group has an associated @@ -6873,6 +6874,14 @@ The larger quota ensures that the top-level {{Document}} and its subresources do an unlimited amount of bandwidth after being destroyed. The per-origin quota ensures that a single reporting sink (e.g. RUM library) doesn't reserve the whole quota to itself. +This specification defined a policy-controlled feature identified by the string +"deferred-fetch". Its +default allowlist is `'self'`. + +This specification defined a policy-controlled feature identified by the string +"deferred-fetch-minimal". Its +default allowlist is `*`. +

      To get the available deferred fetching quota given a Document document and an origin-or-null origin: @@ -6892,14 +6901,13 @@ reporting sink (e.g. RUM library) doesn't reserve the whole quota to itself.

    • Let origin be document's URL's origin. -

    • If the result of calling [$Is feature enabled in document for origin?$] given - deferred-fetch, document and origin is - Disabled, then return 0. +

    • If document is not allowed to use the + policy-controlled feature {{PermissionPolicy/"deferred-fetch"}}, then return 0.

    • -

      If the result of calling [$Is feature enabled in document for origin?$] given - deferred-fetch-minimal, document and origin is - Enabled, then set remainingTotalQuota to 512 kibibytes. +

    • If document is allowed to use the + policy-controlled feature {{PermissionPolicy/"deferred-fetch-minimal"}}, then set + remainingTotalQuota to 512 kibibytes.

      When this permission policy is enabled, this document delegates 8KB to each of the first 16 cross-origin subframes leaving 512kb for its own quota. @@ -6979,16 +6987,16 @@ setting an iframe's src.

    • Set container's deferred fetch policy to disabled.

    • If the inherited policy - for deferred-fetch, navigable's navigable container and + for {{PermissionPolicy/"deferred-fetch"}}, navigable's navigable container and originToNavigateTo is Enabled, and the available deferred fetching quota for container's container document is equal or greater than 64 kibibytes, then set navigable's - deferred fetch policy to deferred-fetch and return. + deferred fetch policy to {{PermissionPolicy/"deferred-fetch"}} and return.

    • If the inherited policy - for deferred-fetch-minimal, container and originToNavigateTo is - Disabled, then set container's deferred fetch policy to - disabled and return. + for {{PermissionPolicy/"deferred-fetch-minimal"}}, container and + originToNavigateTo is Disabled, then set container's + deferred fetch policy to disabled and return.

    • Let topLevelRelatives be container's container document's @@ -7001,7 +7009,7 @@ setting an iframe's src. return.

      Only the top level document and related documents can delegate the - deferred-fetch-minimal policy. + {{PermissionPolicy/"deferred-fetch-minimal"}} policy.

    • Let framesWithMinimalQuotaPolicy be zero.

    • @@ -7016,13 +7024,13 @@ setting an iframe's src.
    • topLevelRelatives does not contain navigable

    • navigable's navigable container's deferred fetch policy is - deferred-fetch-minimal + {{PermissionPolicy/"deferred-fetch-minimal"}}

    Increment framesWithMinimalQuotaPolicy by 1.

  • If framesWithMinimalQuotaPolicy is less than 16, then set container's - deferred fetch policy to deferred-fetch-minimal. + deferred fetch policy to {{PermissionPolicy/"deferred-fetch-minimal"}}. @@ -7033,10 +7041,10 @@ The initial subframe deferred fetch quota is determined by the follow Policy Quota - deferred-fetch + {{PermissionPolicy/"deferred-fetch"}} 64 kibibytes - deferred-fetch-minimal + {{PermissionPolicy/"deferred-fetch-minimal"}} 8 kibibytes disabled From 54cc33c2e792931309fcb56b869eeb712916bcfb Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Thu, 31 Oct 2024 09:52:43 +0000 Subject: [PATCH 051/102] Remove wrong use of reporting origin --- fetch.bs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/fetch.bs b/fetch.bs index 4f21d3e27..ec4fd44d6 100644 --- a/fetch.bs +++ b/fetch.bs @@ -6898,13 +6898,9 @@ This specification defined a policy-controlled feature identified by the

  • If document's node navigable is a top-level traversable then:

      -
    1. Let origin be document's URL's - origin. -

    2. If document is not allowed to use the policy-controlled feature {{PermissionPolicy/"deferred-fetch"}}, then return 0. -

    3. If document is allowed to use the policy-controlled feature {{PermissionPolicy/"deferred-fetch-minimal"}}, then set remainingTotalQuota to 512 kibibytes. @@ -6917,12 +6913,16 @@ This specification defined a policy-controlled feature identified by the

      640kb should be enough for everyone.

    -
  • Otherwise, of the result of calling [$Is feature enabled in document for origin?$] given - document's node navigable's container's - deferred fetch policy, document and origin is - Enabled, then set remainingTotalQuota to the - initial subframe deferred fetch quota given document's node navigable's - container's deferred fetch policy. +

  • +

    Otherwise:

    +
      +
    1. Let containerPolicy be document's node navigable's + container's deferred fetch policy. + +

    2. If containerPolicy is not disabled and document is + allowed to use containerPolicy, then set remainingTotalQuota to + the initial subframe deferred fetch quota given containerPolicy. +

  • Let remainingQuotaForOrigin be remainingTotalQuota. From 322a0dc3bb331ae26fb327ab4d787e99b1fa8854 Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Thu, 31 Oct 2024 10:05:14 +0000 Subject: [PATCH 052/102] typo --- fetch.bs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fetch.bs b/fetch.bs index ec4fd44d6..61e2a76c2 100644 --- a/fetch.bs +++ b/fetch.bs @@ -7054,7 +7054,7 @@ The initial subframe deferred fetch quota is determined by the follow

    The deferred fetch quota-sharing navigables of a Document -document are al; the navigables whose active document's +document are all the navigables whose active document's relevant agent is document's relevant agent's, top-level traversable is document's node navigable's top-level traversable, and whose active document's From 1e2f8589f863e45cc5aa5f0a6ba43d74984ea97e Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Wed, 20 Nov 2024 09:44:40 +0000 Subject: [PATCH 053/102] Sort out some quota logic --- fetch.bs | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/fetch.bs b/fetch.bs index 61e2a76c2..323ecfa4d 100644 --- a/fetch.bs +++ b/fetch.bs @@ -6901,7 +6901,7 @@ This specification defined a policy-controlled feature identified by the
  • If document is not allowed to use the policy-controlled feature {{PermissionPolicy/"deferred-fetch"}}, then return 0. -

  • If document is allowed to use the +

  • Otherwise, if document is allowed to use the policy-controlled feature {{PermissionPolicy/"deferred-fetch-minimal"}}, then set remainingTotalQuota to 512 kibibytes. @@ -6909,7 +6909,7 @@ This specification defined a policy-controlled feature identified by the the first 16 cross-origin subframes leaving 512kb for its own quota.

  • -

    Set remainingTotalQuota to 640 kibibytes. +

    Otherwsise, set remainingTotalQuota to 640 kibibytes.

    640kb should be enough for everyone. @@ -6958,21 +6958,16 @@ This specification defined a policy-controlled feature identified by the relevantContainers with every shadow-including descendant of relative which is a navigable container. -

  • -

    For each descendantContainer of relevantContainers, - decrement remainingTotalQuota by the initial subframe deferred fetch quota - given descendantContainer's deferred fetch policy: - -

  • If remainingTotalQuota is less than remainingQuotaForOrigin, then - return remainingTotalQuota. - -

  • Return remainingQuotaForOrigin. +

  • For each descendantContainer of relevantContainers, + decrement remainingTotalQuota by the initial subframe deferred fetch quota + given descendantContainer's deferred fetch policy. +

  • If remainingTotalQuota is less than remainingQuotaForOrigin, then return remainingTotalQuota. -

  • Return remainingQuotaForOrigin. +

  • Otherwise, return remainingQuotaForOrigin. From c740fd1264b9967ead8e0095380a7b6d9fe17b2c Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Wed, 20 Nov 2024 11:00:58 +0000 Subject: [PATCH 054/102] Sort out some quota logic --- fetch.bs | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/fetch.bs b/fetch.bs index 323ecfa4d..924d88ba3 100644 --- a/fetch.bs +++ b/fetch.bs @@ -6890,38 +6890,38 @@ This specification defined a policy-controlled feature identified by the

  • Let relativeNavigables be document's deferred fetch quota-sharing navigables. -

  • Let remainingTotalQuota be zero. +

  • +

    Let remainingTotalQuota be 640 kibibytes. +

    640kb should be enough for everyone.

  • For each navigable in relativeNavigables:

    1. -

      If document's node navigable is a top-level traversable then: +

      If navigable is a top-level traversable then:

        -
      1. If document is not allowed to use the +

      2. If navigable's active document is not allowed to use the policy-controlled feature {{PermissionPolicy/"deferred-fetch"}}, then return 0. -

      3. Otherwise, if document is allowed to use the - policy-controlled feature {{PermissionPolicy/"deferred-fetch-minimal"}}, then set +

      4. Otherwise, if navigable's active document is allowed to use + the policy-controlled feature {{PermissionPolicy/"deferred-fetch-minimal"}}, then set remainingTotalQuota to 512 kibibytes.

        When this permission policy is enabled, this document delegates 8KB to each of the first 16 cross-origin subframes leaving 512kb for its own quota. - -

      5. -

        Otherwsise, set remainingTotalQuota to 640 kibibytes. -

        640kb should be enough for everyone.

    2. Otherwise:

        -
      1. Let containerPolicy be document's node navigable's +

      2. Let containerPolicy be navigable's container's deferred fetch policy. -

      3. If containerPolicy is not disabled and document is - allowed to use containerPolicy, then set remainingTotalQuota to - the initial subframe deferred fetch quota given containerPolicy. +

      4. If containerPolicy is disabled or navigable's + active document is not allowed to use containerPolicy, then return 0. + +

      5. Otherwise, set remainingTotalQuota to the + initial subframe deferred fetch quota given containerPolicy.

    From 417dd32f2168a674a630d0ed8194b64bf270c8f4 Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Tue, 3 Dec 2024 10:41:19 +0000 Subject: [PATCH 055/102] Fix container --- fetch.bs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fetch.bs b/fetch.bs index 924d88ba3..2d1564f5c 100644 --- a/fetch.bs +++ b/fetch.bs @@ -6985,7 +6985,7 @@ setting an iframe's src. for {{PermissionPolicy/"deferred-fetch"}}, navigable's navigable container and originToNavigateTo is Enabled, and the available deferred fetching quota for container's container document is - equal or greater than 64 kibibytes, then set navigable's + equal or greater than 64 kibibytes, then set container's deferred fetch policy to {{PermissionPolicy/"deferred-fetch"}} and return.
  • If the inherited policy From 2e248662a74c60379d5d84e4e91becf1a24d8966 Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Tue, 3 Dec 2024 11:18:51 +0000 Subject: [PATCH 056/102] Extract 'total request length' to a function --- fetch.bs | 63 ++++++++++++++++++++++++++++++++------------------------ 1 file changed, 36 insertions(+), 27 deletions(-) diff --git a/fetch.bs b/fetch.bs index 2d1564f5c..24b05008b 100644 --- a/fetch.bs +++ b/fetch.bs @@ -6774,30 +6774,7 @@ i.e., when a fetch group is terminated, or after

  • If request's URL is not a potentially trustworthy url, then throw a "{{SecurityError}}" {{DOMException}}. -

  • Let totalRequestLength be the [=string/length=] of request's - URL, serialized with - [=URL serializer/exclude fragment=] set to true. - -

  • For each (name, value) in - header list, increment totalRequestLength by name's - [=byte sequence/length=] + value's [=byte sequence/length=]. - -

  • -

    If request's body is non-null then: - -

      -
    1. If request's - body's length is null, then throw a {{TypeError}}. - -

    2. -

      If request's body's source is null, then throw a - {{TypeError}}. - -

      This disallows sending deferred fetches with a live {{ReadableStream}}. - -

    3. Increment totalRequestLength by request's - body's length. -

    +
  • Let totalRequestLength be request's total request length

  • If the available deferred fetching quota given request's client and request's URL's origin @@ -6836,6 +6813,39 @@ i.e., when a fetch group is terminated, or after +

    +

    To compute the total request length of a request request: + +

      +
    1. Let totalRequestLength be the [=string/length=] of request's + URL, serialized with + [=URL serializer/exclude fragment=] set to true. + +

    2. For each (name, value) in + header list, increment totalRequestLength by name's + [=byte sequence/length=] + value's [=byte sequence/length=]. + +

    3. +

      If request's body is non-null then: + +

        +
      1. If request's + body's length is null, then throw a {{TypeError}}. + +

      2. +

        If request's body's source is null, then throw a + {{TypeError}}. + +

        This disallows sending deferred fetches with a live {{ReadableStream}}. + +

      3. Increment totalRequestLength by request's + body's length. +

      + +
      1. Return totalRequestLength. +
      +
    +

    To process deferred fetches given a fetch group fetchGroup: @@ -6939,9 +6949,8 @@ This specification defined a policy-controlled feature identified by the deferred fetch records:

      -
    1. Let length be deferredRecord's - request's body's - length. +

    2. Let length be the total request length of deferredRecord's + request.

    3. Decrement remainingTotalQuota by length. From 9c3a79923070d0c07a68ffba7cfb1ca5d6c9fdab Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Tue, 3 Dec 2024 12:54:30 +0000 Subject: [PATCH 057/102] Don't compute relatives twice --- fetch.bs | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/fetch.bs b/fetch.bs index 24b05008b..c72435faf 100644 --- a/fetch.bs +++ b/fetch.bs @@ -6958,18 +6958,19 @@ This specification defined a policy-controlled feature identified by the request's URL's origin is same origin with origin, then decrement remainingQuotaForOrigin by length. + +

    4. Let relevantContainers be all the shadow-including descendant of + navigable's active document which are a + navigable container. + +

    5. For each descendantContainer of + relevantContainers, if relativeNavigables does not + contain descendantContainer, then decrement + remainingTotalQuota by the initial subframe deferred fetch quota given + descendantContainer's deferred fetch policy.

  • -
  • Let relevantContainers be « ». -

  • For each relative in navigable's - deferred fetch quota-sharing navigables, extend - relevantContainers with every shadow-including descendant of - relative which is a navigable container. - -

  • For each descendantContainer of relevantContainers, - decrement remainingTotalQuota by the initial subframe deferred fetch quota - given descendantContainer's deferred fetch policy.

  • From 29c422fb909378fcaddfb9c60294b6663d2d2a96 Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Wed, 4 Dec 2024 11:52:12 +0000 Subject: [PATCH 058/102] Refactor loop --- fetch.bs | 88 ++++++++++++++++++++++++++++++-------------------------- 1 file changed, 48 insertions(+), 40 deletions(-) diff --git a/fetch.bs b/fetch.bs index c72435faf..e6cb4c157 100644 --- a/fetch.bs +++ b/fetch.bs @@ -6905,35 +6905,36 @@ This specification defined a policy-controlled feature identified by the

    640kb should be enough for everyone.

  • -

    For each navigable in relativeNavigables: +

    Let topRelativeNavigable in relativeNavigables[0]: +

  • +

    If topRelativeNavigable is a top-level traversable then:

      -
    1. -

      If navigable is a top-level traversable then: -

        -
      1. If navigable's active document is not allowed to use the - policy-controlled feature {{PermissionPolicy/"deferred-fetch"}}, then return 0. +

      2. If topRelativeNavigable's active document is not allowed to use + the policy-controlled feature {{PermissionPolicy/"deferred-fetch"}}, then return 0. -

      3. Otherwise, if navigable's active document is allowed to use - the policy-controlled feature {{PermissionPolicy/"deferred-fetch-minimal"}}, then set - remainingTotalQuota to 512 kibibytes. +

      4. +

        Otherwise, if topRelativeNavigable's active document is + allowed to use the policy-controlled feature + {{PermissionPolicy/"deferred-fetch-minimal"}}, then set remainingTotalQuota to 512 + kibibytes. -

        When this permission policy is enabled, this document delegates 8KB to each of - the first 16 cross-origin subframes leaving 512kb for its own quota. -

      +

      When this permission policy is enabled, this document delegates 8KB to each of + the first 16 cross-origin subframes leaving 512kb for its own quota. +

    -
  • -

    Otherwise:

    -
      -
    1. Let containerPolicy be navigable's - container's deferred fetch policy. +

    2. +

      Otherwise:

      +
        +
      1. Let containerPolicy be topRelativeNavigable's + container's deferred fetch policy. -

      2. If containerPolicy is disabled or navigable's - active document is not allowed to use containerPolicy, then return 0. +

      3. If containerPolicy is disabled or + topRelativeNavigable's active document is not allowed to use + containerPolicy, then return 0. -

      4. Otherwise, set remainingTotalQuota to the - initial subframe deferred fetch quota given containerPolicy. -

      -
    +
  • Otherwise, set remainingTotalQuota to the + initial subframe deferred fetch quota given containerPolicy. +

  • Let remainingQuotaForOrigin be remainingTotalQuota. @@ -6958,19 +6959,19 @@ This specification defined a policy-controlled feature identified by the request's URL's origin is same origin with origin, then decrement remainingQuotaForOrigin by length. - -

  • Let relevantContainers be all the shadow-including descendant of - navigable's active document which are a - navigable container. - -

  • For each descendantContainer of - relevantContainers, if relativeNavigables does not - contain descendantContainer, then decrement - remainingTotalQuota by the initial subframe deferred fetch quota given - descendantContainer's deferred fetch policy.

  • +
  • Let relevantContainers be all the shadow-including descendant of + navigable's active document which are a + navigable container. + +

  • For each descendantContainer of + relevantContainers, if relativeNavigables does not + contain descendantContainer, then decrement + remainingTotalQuota by the initial subframe deferred fetch quota given + descendantContainer's deferred fetch policy. +

  • @@ -7058,13 +7059,20 @@ The initial subframe deferred fetch quota is determined by the follow
    -The deferred fetch quota-sharing navigables of a Document -document are all the navigables whose active document's -relevant agent is document's relevant agent's, -top-level traversable is document's node navigable's -top-level traversable, and whose active document's -origin is same origin with document's -URL's origin. +

    To get the deferred fetch quota-sharing navigables of a Document +document:

    +
      +
    1. Let relatives be document's node navigable's + top-level traversable's active document's + inclusive descendant navigables. + +

    2. Remove from relatives all navigables whose + active document's origin is not same origin with + document's URL's origin, and whose + active document's relevant agent is not document's relevant agent. + +

    3. Return relatives. +

    From af751d918910083c4b4e4c5e93728def272f3b61 Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Wed, 4 Dec 2024 11:58:28 +0000 Subject: [PATCH 059/102] Fix indents --- fetch.bs | 48 +++++++++++++++++++++++------------------------- 1 file changed, 23 insertions(+), 25 deletions(-) diff --git a/fetch.bs b/fetch.bs index e6cb4c157..383d8369f 100644 --- a/fetch.bs +++ b/fetch.bs @@ -6842,7 +6842,7 @@ i.e., when a fetch group is terminated, or after body's length. -
    1. Return totalRequestLength. +
    2. Return totalRequestLength.

    @@ -6905,8 +6905,9 @@ This specification defined a policy-controlled feature identified by the

    640kb should be enough for everyone.

  • -

    Let topRelativeNavigable in relativeNavigables[0]: -

  • +

    Let topRelativeNavigable in relativeNavigables[0]. + +

  • If topRelativeNavigable is a top-level traversable then:

    1. If topRelativeNavigable's active document is not allowed to use @@ -6922,19 +6923,19 @@ This specification defined a policy-controlled feature identified by the the first 16 cross-origin subframes leaving 512kb for its own quota.

    -
  • -

    Otherwise:

    -
      -
    1. Let containerPolicy be topRelativeNavigable's - container's deferred fetch policy. +

    2. +

      Otherwise:

      +
        +
      1. Let containerPolicy be topRelativeNavigable's + container's deferred fetch policy. -

      2. If containerPolicy is disabled or - topRelativeNavigable's active document is not allowed to use - containerPolicy, then return 0. +

      3. If containerPolicy is disabled or + topRelativeNavigable's active document is not allowed to use + containerPolicy, then return 0. -

      4. Otherwise, set remainingTotalQuota to the - initial subframe deferred fetch quota given containerPolicy. -

      +
    3. Otherwise, set remainingTotalQuota to the + initial subframe deferred fetch quota given containerPolicy. +

  • Let remainingQuotaForOrigin be remainingTotalQuota. @@ -6962,18 +6963,15 @@ This specification defined a policy-controlled feature identified by the

  • -
  • Let relevantContainers be all the shadow-including descendant of - navigable's active document which are a - navigable container. - -

  • For each descendantContainer of - relevantContainers, if relativeNavigables does not - contain descendantContainer, then decrement - remainingTotalQuota by the initial subframe deferred fetch quota given - descendantContainer's deferred fetch policy. +

  • Let relevantContainers be all the shadow-including descendant of + navigable's active document which are a + navigable container. - -

  • +
  • For each descendantContainer of + relevantContainers, if relativeNavigables does not + contain descendantContainer, then decrement + remainingTotalQuota by the initial subframe deferred fetch quota given + descendantContainer's deferred fetch policy.

  • If remainingTotalQuota is less than remainingQuotaForOrigin, then return remainingTotalQuota. From 2c84b7dc35807faf68312b39623a8e839726ae97 Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Wed, 4 Dec 2024 12:01:38 +0000 Subject: [PATCH 060/102] Fix indent --- fetch.bs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/fetch.bs b/fetch.bs index 383d8369f..d3303feda 100644 --- a/fetch.bs +++ b/fetch.bs @@ -6961,7 +6961,9 @@ This specification defined a policy-controlled feature identified by the same origin with origin, then decrement remainingQuotaForOrigin by length. -

  • +
  • + +
  • Let relevantContainers be all the shadow-including descendant of navigable's active document which are a @@ -6976,7 +6978,7 @@ This specification defined a policy-controlled feature identified by the

  • If remainingTotalQuota is less than remainingQuotaForOrigin, then return remainingTotalQuota. -

  • Otherwise, return remainingQuotaForOrigin. +

  • Return remainingQuotaForOrigin. From 793a29ca0f967f9bdee09a24654fb9da83ed5236 Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Wed, 4 Dec 2024 12:04:56 +0000 Subject: [PATCH 061/102] Fix list --- fetch.bs | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/fetch.bs b/fetch.bs index d3303feda..f312b23a0 100644 --- a/fetch.bs +++ b/fetch.bs @@ -6962,18 +6962,17 @@ This specification defined a policy-controlled feature identified by the length.

  • - -
  • -
  • Let relevantContainers be all the shadow-including descendant of - navigable's active document which are a - navigable container. +

  • Let relevantContainers be all the shadow-including descendant of + navigable's active document which are + navigable containers. -

  • For each descendantContainer of - relevantContainers, if relativeNavigables does not - contain descendantContainer, then decrement - remainingTotalQuota by the initial subframe deferred fetch quota given - descendantContainer's deferred fetch policy. +

  • For each descendantContainer of + relevantContainers, if relativeNavigables does not + contain descendantContainer, then decrement + remainingTotalQuota by the initial subframe deferred fetch quota given + descendantContainer's deferred fetch policy. +

  • If remainingTotalQuota is less than remainingQuotaForOrigin, then return remainingTotalQuota. From 8998e3debba2506dba6622f4e166e1e81c808257 Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Wed, 4 Dec 2024 12:06:10 +0000 Subject: [PATCH 062/102] Remove spurious

  • --- fetch.bs | 1 - 1 file changed, 1 deletion(-) diff --git a/fetch.bs b/fetch.bs index f312b23a0..d91286d8f 100644 --- a/fetch.bs +++ b/fetch.bs @@ -6961,7 +6961,6 @@ This specification defined a policy-controlled feature identified by the same origin with origin, then decrement remainingQuotaForOrigin by length. -
  • Let relevantContainers be all the shadow-including descendant of navigable's active document which are From c7d70dc3c949a992f1e366320e92dae58c059f1a Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Wed, 4 Dec 2024 12:09:04 +0000 Subject: [PATCH 063/102] Tersify loop --- fetch.bs | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/fetch.bs b/fetch.bs index d91286d8f..34f29ad37 100644 --- a/fetch.bs +++ b/fetch.bs @@ -6962,15 +6962,12 @@ This specification defined a policy-controlled feature identified by the length. -

  • Let relevantContainers be all the shadow-including descendant of - navigable's active document which are - navigable containers. - -

  • For each descendantContainer of - relevantContainers, if relativeNavigables does not - contain descendantContainer, then decrement +

  • For each maybeContainer of shadow-including descendant + of navigable's active document, if maybeContainer is a + navigable container and relativeNavigables does not + contain maybeContainer's content navigable, then decrement remainingTotalQuota by the initial subframe deferred fetch quota given - descendantContainer's deferred fetch policy. + maybeContainer's deferred fetch policy.

  • If remainingTotalQuota is less than remainingQuotaForOrigin, then From 2548d0628889d4c80a6a0471477363b84b9a676d Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Sun, 8 Dec 2024 20:35:16 +0000 Subject: [PATCH 064/102] Refactor algorithm, simplify a lot --- fetch.bs | 252 +++++++++++++++++++++++-------------------------------- 1 file changed, 105 insertions(+), 147 deletions(-) diff --git a/fetch.bs b/fetch.bs index 34f29ad37..e3f3cf6f0 100644 --- a/fetch.bs +++ b/fetch.bs @@ -2734,10 +2734,8 @@ functionality. fetch records, a list of fetch records. -

    Each navigable container has an associated deferred fetch policy -({{PermissionPolicy/"deferred-fetch"}}, {{PermissionPolicy/"deferred-fetch-minimal"}}, or -Disabled). Unless -stated otherwise, it is disabled. +

    Each navigable container has an associated number +subframe reserved deferred-fetch quota. Unless stated otherwise, it is zero.

    A fetch group has an associated deferred fetch records, @@ -6754,7 +6752,7 @@ agent's CORS-preflight cache for which there is a cache entry match -

    Deferred fetching

    +

    Deferred fetching

    Deferred fetches allow callers to request that a fetch is invoked at the latest possible moment, i.e., when a fetch group is terminated, or after a timeout. @@ -6776,7 +6774,7 @@ i.e., when a fetch group is terminated, or after

  • Let totalRequestLength be request's total request length -

  • If the available deferred fetching quota given request's +

  • If the available deferred-fetch quota given request's client and request's URL's origin is less than totalRequestLength, then throw a "{{QuotaExceededError}}" {{DOMException}}. @@ -6876,7 +6874,7 @@ i.e., when a fetch group is terminated, or after -

    Deferred fetching quota

    +

    Deferred fetching quota

    The quota asserts that this deferred fetch doesn't exceed two quotas: one for the document and it's same-origin same-tree relatives, and one for the reporting origin (64 kibibytes). @@ -6893,181 +6891,141 @@ This specification defined a policy-controlled feature identified by the default allowlist is `*`.

    -

    To get the available deferred fetching quota given a Document +

    To get the available deferred-fetch quota given a Document document and an origin-or-null origin:

      -
    1. Let relativeNavigables be document's - deferred fetch quota-sharing navigables. +

    2. Let navigables be document's inclusive ancestor navigables, + extended with document's descendant navigables. -

    3. -

      Let remainingTotalQuota be 640 kibibytes. -

      640kb should be enough for everyone. +

    4. Let quota be zero. -

    5. -

      Let topRelativeNavigable in relativeNavigables[0]. +

    6. Let quotaForOrigin be 64 kibibytes.

    7. -

      If topRelativeNavigable is a top-level traversable then: +

      For each otherNavigable in navigables:

        -
      1. If topRelativeNavigable's active document is not allowed to use - the policy-controlled feature {{PermissionPolicy/"deferred-fetch"}}, then return 0. - +

      2. Let otherDocument be otherNavigable's active document.

      3. -

        Otherwise, if topRelativeNavigable's active document is - allowed to use the policy-controlled feature - {{PermissionPolicy/"deferred-fetch-minimal"}}, then set remainingTotalQuota to 512 - kibibytes. - -

        When this permission policy is enabled, this document delegates 8KB to each of - the first 16 cross-origin subframes leaving 512kb for its own quota. -

      - -
    8. -

      Otherwise:

      -
        -
      1. Let containerPolicy be topRelativeNavigable's - container's deferred fetch policy. - -

      2. If containerPolicy is disabled or - topRelativeNavigable's active document is not allowed to use - containerPolicy, then return 0. +

        If document and otherDocument share deferred-fetch quota, + then: +

          +
        1. +

          If otherDocument's node navigable is a + top-level traversable, then: +

            +
          1. Assert: quota is zero. -

          2. Otherwise, set remainingTotalQuota to the - initial subframe deferred fetch quota given containerPolicy. -

          +
        2. If either document or otherDocument are not + allowed to use the policy-controlled feature + {{PermissionPolicy/"deferred-fetch"}}, then return 0. -

        3. Let remainingQuotaForOrigin be remainingTotalQuota. +

        4. +

          Otherwise, if otherDocument is + allowed to use the policy-controlled feature + {{PermissionPolicy/"deferred-fetch-minimal"}}, then set quota to 512 + kibibytes. -

        5. If remainingQuotaForOrigin is greater than 64 kibibytes, then set - remainingQuotaForOrigin to 64 kibibytes. +

          When this permission policy is enabled, this document delegates 8KB to each of + the first 16 cross-origin subframes leaving 512kb for its own quota. -

        6. -

          For each navigable in relativeNavigables: -

            -
          1. -

            For each deferred fetch record deferredRecord in - navigable's active document's fetch group's - deferred fetch records:

            - -
              -
            1. Let length be the total request length of deferredRecord's - request. +

            2. +

              Otherwise, set quota be 640 kibibytes. +

              640kb should be enough for everyone. +

            +
          2. +

            For each deferred fetch record deferredRecord in + otherDocument's fetch group's + deferred fetch records:

            +
              +
            1. Let length be the total request length of + deferredRecord's request. -

            2. Decrement remainingTotalQuota by length. +

            3. Decrement quota by length. -

            4. If origin is not null, and deferredRecord's - request's URL's origin is - same origin with origin, then decrement remainingQuotaForOrigin by - length. +

            5. If origin is not null, and deferredRecord's + request's URL's origin + is same origin with origin, then decrement quotaForOrigin by + length. +

          -
        7. For each maybeContainer of shadow-including descendant - of navigable's active document, if maybeContainer is a - navigable container and relativeNavigables does not - contain maybeContainer's content navigable, then decrement - remainingTotalQuota by the initial subframe deferred fetch quota given - maybeContainer's deferred fetch policy. +

        8. +

          Otherwise, if navigable is not a top-level traversable, and its + parent's active document and document + share deferred-fetch quota, then decrement quota by the + subframe reserved deferred-fetch quota given navigable's + navigable container's subframe reserved deferred-fetch quota. + +

          This is a cross-origin/cross-agent subframe, so it spends some of the quota of + this document and its relatives.

        +
      3. If quota is less than zero, than return zero. -

      4. If remainingTotalQuota is less than remainingQuotaForOrigin, then - return remainingTotalQuota. +

      5. If quota is less than quotaForOrigin, then + return quota. -

      6. Return remainingQuotaForOrigin. +

      7. Return quotaForOrigin.

    -

    To determine subframe deferred fetch policy for a navigable container -container and an origin originToNavigateTo: - -

    This is called whenever a navigable container is being navigated, e.g. by -setting an iframe's src. - -

      -
    1. Set container's deferred fetch policy to disabled. - -

    2. If the inherited policy - for {{PermissionPolicy/"deferred-fetch"}}, navigable's navigable container and - originToNavigateTo is Enabled, and the - available deferred fetching quota for container's container document is - equal or greater than 64 kibibytes, then set container's - deferred fetch policy to {{PermissionPolicy/"deferred-fetch"}} and return. - -

    3. If the inherited policy +

      To reserve deferred-fetch quota for a navigable container +container given an origin originToNavigateTo, set +container's reserved deferred-fetch quota to the result of running the steps +corresponding to the first matching statement: + +

      +
      The inherited policy + for {{PermissionPolicy/"deferred-fetch"}}, container and originToNavigateTo + is Enabled, and the available deferred-fetch quota for + container's container document is equal or greater than 64 kibibytes +

      Return 64 kibibytes + +

      container's node document and container's node navigable's + top-level traversable's active documentshare deferred-fetch quota, and the + inherited policy for {{PermissionPolicy/"deferred-fetch-minimal"}}, container and - originToNavigateTo is Disabled, then set container's - deferred fetch policy to disabled and return. - - -
    4. Let topLevelRelatives be container's container document's - deferred fetch quota-sharing navigables. - -

    5. -

      If topLevelRelatives does not contain container's - node navigable's top-level traversable, then - set container's deferred fetch policy to disabled and - return. - -

      Only the top level document and related documents can delegate the - {{PermissionPolicy/"deferred-fetch-minimal"}} policy. - -

    6. Let framesWithMinimalQuotaPolicy be zero. -

    7. -

      For each navigable that matches the following conditions: -

      -

      Increment framesWithMinimalQuotaPolicy by 1. +

    8. If navigablesWithMinimalQuota's size is greater than 16, + then return zero. -

    9. If framesWithMinimalQuotaPolicy is less than 16, then set container's - deferred fetch policy to {{PermissionPolicy/"deferred-fetch-minimal"}}. -

    -
    +
  • Return 8 kibibytes. + +

    Otherwise +

    Return zero. + -

    -The initial subframe deferred fetch quota is determined by the following table: - - - - - -
    Policy - Quota -
    {{PermissionPolicy/"deferred-fetch"}} - 64 kibibytes -
    {{PermissionPolicy/"deferred-fetch-minimal"}} - 8 kibibytes -
    disabled - 0 kibibytes -
    -

    To get the deferred fetch quota-sharing navigables of a Document -document:

    -
      -
    1. Let relatives be document's node navigable's - top-level traversable's active document's - inclusive descendant navigables. - -

    2. Remove from relatives all navigables whose - active document's origin is not same origin with - document's URL's origin, and whose - active document's relevant agent is not document's relevant agent. - -

    3. Return relatives. -

    +

    Two Documents a and b are said to +share deferred-fetch quota if a's +relevant agent is b's relevant agent, and a's +URL's origin is same origin with var>b's +URL's origin.

    From a98471314bfae195840c473bcff2e789387d7f9d Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Mon, 9 Dec 2024 09:45:59 +0000 Subject: [PATCH 065/102] Use constants --- fetch.bs | 103 +++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 69 insertions(+), 34 deletions(-) diff --git a/fetch.bs b/fetch.bs index e3f3cf6f0..5f93b762d 100644 --- a/fetch.bs +++ b/fetch.bs @@ -6882,30 +6882,42 @@ The larger quota ensures that the top-level {{Document}} and its subresources do an unlimited amount of bandwidth after being destroyed. The per-origin quota ensures that a single reporting sink (e.g. RUM library) doesn't reserve the whole quota to itself. -This specification defined a policy-controlled feature identified by the string +

    This specification defined a policy-controlled feature identified by the string "deferred-fetch". Its default allowlist is `'self'`. -This specification defined a policy-controlled feature identified by the string +

    This specification defined a policy-controlled feature identified by the string "deferred-fetch-minimal". Its default allowlist is `*`. +

    The top-level deferred-fetch quota is 640 kibibytes. +

    The optional subframe deferred-fetch quota is 64 kibibytes. +

    The minimal subframe deferred-fetch quota is 8 kibibytes. +

    The deferred-fetch delegated quota is 128 kibibytes. +

    To get the available deferred-fetch quota given a Document document and an origin-or-null origin:

      -
    1. Let navigables be document's inclusive ancestor navigables, - extended with document's descendant navigables. -

    2. Let quota be zero. -

    3. Let quotaForOrigin be 64 kibibytes. +

    4. +

      Let quotaForOrigin be 64 kibibytes. +

    5. -

      For each otherNavigable in navigables: +

      For each otherNavigable in document's node navigable's + top-level traversable's inclusive descendant navigables: + +

      This algorithm iterates over the entire navigable tree. It accumulates quota from + the top-level traversable, and from subframes who inherit quota from cross-origin or + cross-agent navigables. Subsequently, it subtracts the quota delegated to cross-origin or + cross-agent subframes, as well as quota spent on pending deferred fetch requests. +

      1. Let otherDocument be otherNavigable's active document. +

      2. If document and otherDocument share deferred-fetch quota, then: @@ -6916,23 +6928,44 @@ This specification defined a policy-controlled feature identified by the

        1. Assert: quota is zero. -

        2. If either document or otherDocument are not +

        3. If otherDocument is not allowed to use the policy-controlled feature {{PermissionPolicy/"deferred-fetch"}}, then return 0.

        4. -

          Otherwise, if otherDocument is - allowed to use the policy-controlled feature - {{PermissionPolicy/"deferred-fetch-minimal"}}, then set quota to 512 - kibibytes. +

          Set quota be 640 kibibytes. +

          640kb should be enough for everyone. -

          When this permission policy is enabled, this document delegates 8KB to each of - the first 16 cross-origin subframes leaving 512kb for its own quota. +

        5. If otherDocument is allowed to use the + policy-controlled feature {{PermissionPolicy/"deferred-fetch-minimal"}}, then + decrement quota by deferred-fetch delegated quota. +

        + +
      3. +
          +
        1. Let otherContainer be otherDocument's node navigable's + navigable container.

        2. -

          Otherwise, set quota be 640 kibibytes. -

          640kb should be enough for everyone. +

          If otherContainer's node document and otherDocument do + not share deferred-fetching quota, then: + +

            +
          1. If otherContainer's subframe reserved deferred-fetch quota is + optional subframe deferred-fetch quota, and otherDocument is + allowed to use the policy-controlled feature + {{PermissionPolicy/"deferred-fetch"}}, then increment quota by + optional subframe deferred-fetch quota. + +

          2. Otherwise, if otherContainer's + subframe reserved deferred-fetch quota is + minimal subframe deferred-fetch quota and otherDocument + is allowed to use the policy-controlled feature + {{PermissionPolicy/"deferred-fetch-minimal"}}, then increment quota by + minimal subframe deferred-fetch quota. +

        +
      4. For each deferred fetch record deferredRecord in otherDocument's fetch group's @@ -6951,11 +6984,11 @@ This specification defined a policy-controlled feature identified by the

    6. -

      Otherwise, if navigable is not a top-level traversable, and its +

      Otherwise, if otherNavigable is not a top-level traversable, and its parent's active document and document - share deferred-fetch quota, then decrement quota by the - subframe reserved deferred-fetch quota given navigable's - navigable container's subframe reserved deferred-fetch quota. + share deferred-fetch quota, then decrement quota by + otherNavigable's navigable container's + subframe reserved deferred-fetch quota.

      This is a cross-origin/cross-agent subframe, so it spends some of the quota of this document and its relatives. @@ -6979,8 +7012,9 @@ corresponding to the first matching statement:

      The inherited policy for {{PermissionPolicy/"deferred-fetch"}}, container and originToNavigateTo is Enabled, and the available deferred-fetch quota for - container's container document is equal or greater than 64 kibibytes -

      Return 64 kibibytes + container's container document is equal or greater than + optional subframe deferred-fetch quota +

      Return optional subframe deferred-fetch quota.

      container's node document and container's node navigable's top-level traversable's active documentoriginToNavigateTo is Enabled
        -
      1. Let navigablesWithMinimalQuota be container's - node navigable's top-level traversable's - inclusive descendant navigables. +

      2. Let delegatedQuota be deferred-fetch delegated quota.

      3. -

        Remove from navigablesWithMinimalQuota any navigable - who meets any of the following conditions: - +

        For each navigable in container's + node navigable's top-level traversable's + inclusive descendant navigables who meets all of the following conditions:

        + is . + , -
      4. If navigablesWithMinimalQuota's size is greater than 16, +

        Decrement delegatedQuota by minimal subframe deferred-fetch quota. + +

      5. If delegatedQuota is less than minimal subframe deferred-fetch quota, then return zero. -

      6. Return 8 kibibytes. +

      7. Return minimal subframe deferred-fetch quota.

      +
      Otherwise

      Return zero. From 91e27364fcd12ac725d8ef15d0ec5700bcd27147 Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Mon, 9 Dec 2024 09:54:51 +0000 Subject: [PATCH 066/102] Refactor quota algorithm --- fetch.bs | 34 +++++++++------------------------- 1 file changed, 9 insertions(+), 25 deletions(-) diff --git a/fetch.bs b/fetch.bs index 5f93b762d..194cf64e4 100644 --- a/fetch.bs +++ b/fetch.bs @@ -6815,33 +6815,18 @@ i.e., when a fetch group is terminated, or after

      To compute the total request length of a request request:

        -
      1. Let totalRequestLength be the [=string/length=] of request's +

      2. Let totalRequestLength be the length of request's URL, serialized with [=URL serializer/exclude fragment=] set to true. -

      3. For each (name, value) in +

      4. For each (name, value) in request's header list, increment totalRequestLength by name's - [=byte sequence/length=] + value's [=byte sequence/length=]. + length + value's length. -

      5. -

        If request's body is non-null then: - -

          -
        1. If request's - body's length is null, then throw a {{TypeError}}. - -

        2. -

          If request's body's source is null, then throw a - {{TypeError}}. +

        3. Increment totalRequestLength by request's + body's length. -

          This disallows sending deferred fetches with a live {{ReadableStream}}. - -

        4. Increment totalRequestLength by request's - body's length. -

        - -
      6. Return totalRequestLength. -

      +
    7. Return totalRequestLength.

    @@ -6890,7 +6875,6 @@ reporting sink (e.g. RUM library) doesn't reserve the whole quota to itself. "deferred-fetch-minimal". Its default allowlist is `*`. -

    The top-level deferred-fetch quota is 640 kibibytes.

    The optional subframe deferred-fetch quota is 64 kibibytes.

    The minimal subframe deferred-fetch quota is 8 kibibytes.

    The deferred-fetch delegated quota is 128 kibibytes. @@ -6948,7 +6932,7 @@ reporting sink (e.g. RUM library) doesn't reserve the whole quota to itself.

  • If otherContainer's node document and otherDocument do - not share deferred-fetching quota, then: + not share deferred-fetch quota, then:

    1. If otherContainer's subframe reserved deferred-fetch quota is @@ -7017,7 +7001,7 @@ corresponding to the first matching statement:

      Return optional subframe deferred-fetch quota.

      container's node document and container's node navigable's - top-level traversable's active documenttop-level traversable's active document share deferred-fetch quota, and the inherited policy for {{PermissionPolicy/"deferred-fetch-minimal"}}, container and @@ -7038,7 +7022,7 @@ corresponding to the first matching statement: share deferred-fetch quota.
    2. navigable's container's reserved deferred-fetch quota - is . + is minimal subframe deferred-fetch quota. ,

      Decrement delegatedQuota by minimal subframe deferred-fetch quota. From b91401ab5f4cfc8915421e8402aadc4152ed65bc Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Mon, 9 Dec 2024 10:54:46 +0000 Subject: [PATCH 067/102] Follow up on PR feedback --- fetch.bs | 153 +++++++++++++++++++++++++++++++------------------------ 1 file changed, 86 insertions(+), 67 deletions(-) diff --git a/fetch.bs b/fetch.bs index 194cf64e4..66263da39 100644 --- a/fetch.bs +++ b/fetch.bs @@ -2753,7 +2753,7 @@ a list of deferred fetch records


      A deferred fetch record is a struct used to maintain state needed -to invoke a fetch at a later time, e.g., when a Document object is unloaded or becomes +to invoke a fetch at a later time, e.g., when a {{Document}} object is unloaded or becomes not fully active. It has the following items:

      @@ -2765,7 +2765,7 @@ not fully active. It has the following items"deferred", "aborted", or "activated".

      This value can be modified in parallel. There could be a race condition where - the Document object's event loop might read it as + the {{Document}} object's event loop might read it as "deferred" at the same time that it is changed to "activated". User agents can mitigate this race condition in an implementation-defined manner.

      @@ -6754,30 +6754,15 @@ agent's CORS-preflight cache for which there is a cache entry matchDeferred fetching -

      Deferred fetches allow callers to request that a fetch is invoked at the latest possible moment, +

      Deferred fetching allows callers to request that a fetch is invoked at the latest possible moment, i.e., when a fetch group is terminated, or after a timeout.

      -

      To request a deferred fetch given a +

      To queue a deferred fetch given a request request and a null or {{DOMHighResTimeStamp}} activateAfter (default null):

        -
      1. If request's client is not a fully active - {{Document}}, then throw an "{{InvalidStateError}}" {{DOMException}}. - -

      2. If request's URL's scheme is not an - HTTP(S) scheme, then throw a {{TypeError}}. - -

      3. If request's URL is not a - potentially trustworthy url, then throw a "{{SecurityError}}" {{DOMException}}. - -

      4. Let totalRequestLength be request's total request length - -

      5. If the available deferred-fetch quota given request's - client and request's URL's origin - is less than totalRequestLength, then throw a "{{QuotaExceededError}}" {{DOMException}}. -

      6. Set request's service-workers mode to "none".

      7. Set request's keepalive to true. @@ -6796,7 +6781,7 @@ i.e., when a fetch group is terminated, or after

      8. The user agent should wait until activateAfter milliseconds have passed, or until the user agent has a reason to believe that it is about to lose the opportunity to execute scripts, e.g., when the browser is moved to the background, or when request's - client is a Document that had a "hidden" + client is a {{Document}} that had a "hidden" visibility state for a long period of time.

      9. The user agent may wait for a further implementation-defined duration, e.g., @@ -6819,7 +6804,7 @@ i.e., when a fetch group is terminated, or after URL, serialized with [=URL serializer/exclude fragment=] set to true. -

      10. For each (name, value) in request's +

      11. For each (name, value) of request's header list, increment totalRequestLength by name's length + value's length. @@ -6869,18 +6854,27 @@ reporting sink (e.g. RUM library) doesn't reserve the whole quota to itself.

        This specification defined a policy-controlled feature identified by the string "deferred-fetch". Its -default allowlist is `'self'`. +default allowlist is "self".

        This specification defined a policy-controlled feature identified by the string "deferred-fetch-minimal". Its -default allowlist is `*`. +default allowlist is "*".

        The optional subframe deferred-fetch quota is 64 kibibytes.

        The minimal subframe deferred-fetch quota is 8 kibibytes.

        The deferred-fetch delegated quota is 128 kibibytes.

        -

        To get the available deferred-fetch quota given a Document +

        Two {{Document}}s a and b are said to +share deferred-fetch quota if a's +relevant agent is b's relevant agent, and a's +URL's origin is same origin with b's +URL's origin. +

        + + +
        +

        To get the available deferred-fetch quota given a {{Document}} document and an origin-or-null origin:

          @@ -6909,6 +6903,9 @@ reporting sink (e.g. RUM library) doesn't reserve the whole quota to itself.
        1. If otherDocument's node navigable is a top-level traversable, then: + +

          Initialize the quota from the top-level traversable. +

          1. Assert: quota is zero. @@ -6926,8 +6923,14 @@ reporting sink (e.g. RUM library) doesn't reserve the whole quota to itself.

        2. +

          Otherwise, + +

          This is a subframe, check if it inherits quota from its parent container based + on permissions policy. This would only be the case if the subframe and its parent do not share + quota, as quota is either shared or delegated. +

            -
          1. Let otherContainer be otherDocument's node navigable's +

          2. Let otherContainer be otherNavigable's navigable container.

          3. @@ -6951,19 +6954,18 @@ reporting sink (e.g. RUM library) doesn't reserve the whole quota to itself.
        3. -

          For each deferred fetch record deferredRecord in +

          For each deferred fetch record deferredRecord of otherDocument's fetch group's deferred fetch records:

            -
          1. Let length be the total request length of +

          2. Let requestLength be the total request length of deferredRecord's request. -

          3. Decrement quota by length. +

          4. Decrement quota by requestLength. -

          5. If origin is not null, and deferredRecord's - request's URL's origin - is same origin with origin, then decrement quotaForOrigin by - length. +

          6. If deferredRecord's request's + URL's origin is same origin with origin, + then decrement quotaForOrigin by requestLength.

        @@ -6988,65 +6990,64 @@ reporting sink (e.g. RUM library) doesn't reserve the whole quota to itself.

        To reserve deferred-fetch quota for a navigable container -container given an origin originToNavigateTo, set -container's reserved deferred-fetch quota to the result of running the steps -corresponding to the first matching statement: +container given an origin originToNavigateTo: + +

        This is called when the container document initiates a navigation. It potentially +reserves either 64kb or 8kb of quota for the frame, if it doesn't share deferred-fetch quota +with the container and the permissions policy allow. It is not observable to the cotnainer document +whether the reserved quota was used in practice. +This algorithm assumes that the container's document might delegate quota to the navigated frame, +and the reserved quota would only apply in that case, and would be ignored if it ends up being +shared. -

        -
        The inherited policy +
          +
        1. If the inherited policy for {{PermissionPolicy/"deferred-fetch"}}, container and originToNavigateTo is Enabled, and the available deferred-fetch quota for container's container document is equal or greater than - optional subframe deferred-fetch quota -

          Return optional subframe deferred-fetch quota. - -

          container's node document and container's node navigable's - top-level traversable's active document - share deferred-fetch quota, and the - inherited policy - for {{PermissionPolicy/"deferred-fetch-minimal"}}, container and - originToNavigateTo is Enabled -
          + optional subframe deferred-fetch quota, then set container's + reserved deferred-fetch quota to optional subframe deferred-fetch quota. + +
        2. +

          Otherwise, if the + inherited policy for + {{PermissionPolicy/"deferred-fetch-minimal"}}, container and + originToNavigateTo is Enabled, then:

          1. Let delegatedQuota be deferred-fetch delegated quota.

          2. -

            For each navigable in container's +

            For each navigable of container's node navigable's top-level traversable's inclusive descendant navigables who meets all of the following conditions:

            , + -

            Decrement delegatedQuota by minimal subframe deferred-fetch quota. +

            Decrement delegatedQuota by minimal subframe deferred-fetch quota.

          3. If delegatedQuota is less than minimal subframe deferred-fetch quota, - then return zero. + then set container's reserved deferred-fetch quota to zero. -

          4. Return minimal subframe deferred-fetch quota. +

          5. Otherwise, set container's reserved deferred-fetch quota to + minimal subframe deferred-fetch quota.

          -
          Otherwise -

          Return zero. -

        +
      12. Otherwise, set container's reserved deferred-fetch quota to zero.

      -
      -

      Two Documents a and b are said to -share deferred-fetch quota if a's -relevant agent is b's relevant agent, and a's -URL's origin is same origin with var>b's -URL's origin. -

      -

      Fetch API

      @@ -8953,8 +8954,26 @@ method steps are:
    3. If activateAfter is less than 0, then throw a {{RangeError}}. +

    4. If request's URL's scheme is not an + HTTP(S) scheme, then throw a {{TypeError}}. + +

    5. If request's client is not a fully active + {{Document}}, then throw an "{{InvalidStateError}}" {{DOMException}}. + +

    6. If request's URL is not a + potentially trustworthy url, then throw a "{{SecurityError}}" {{DOMException}}. + +

    7. If request's + body is not null, and request's + body source is null, then throw a {{TypeError}}. + +

    8. If the available deferred-fetch quota given request's + client and request's URL's origin + is less than request's total request length, then throw a + "{{QuotaExceededError}}" {{DOMException}}. +

    9. Let deferredRecord be the result of calling - request a deferred fetch given request and activateAfter. + queue a deferred fetch given request and activateAfter.

    10. Add the following abort steps to requestObject's From 043abc7182d709c724c47a9462800433391c1972 Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Mon, 9 Dec 2024 11:05:32 +0000 Subject: [PATCH 068/102] linkify activateAfter --- fetch.bs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/fetch.bs b/fetch.bs index 66263da39..34f21e833 100644 --- a/fetch.bs +++ b/fetch.bs @@ -8948,25 +8948,25 @@ method steps are:

    11. Let activateAfter be null. -

    12. If init is given and init["activateAfter"] +

    13. If init is given and init["{{DeferredRequestInit/activateAfter}}"] exists, then set activateAfter to - init["activateAfter"]. + init["{{DeferredRequestInit/activateAfter}}"].

    14. If activateAfter is less than 0, then throw a {{RangeError}}.

    15. If request's URL's scheme is not an HTTP(S) scheme, then throw a {{TypeError}}. +

    16. If request's + body is not null, and request's + body source is null, then throw a {{TypeError}}. +

    17. If request's client is not a fully active {{Document}}, then throw an "{{InvalidStateError}}" {{DOMException}}.

    18. If request's URL is not a potentially trustworthy url, then throw a "{{SecurityError}}" {{DOMException}}. -

    19. If request's - body is not null, and request's - body source is null, then throw a {{TypeError}}. -

    20. If the available deferred-fetch quota given request's client and request's URL's origin is less than request's total request length, then throw a From ed638f20b815c1bf1fc4a3a2baefc18f25cfea5c Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Mon, 9 Dec 2024 13:54:56 +0000 Subject: [PATCH 069/102] Add examples --- fetch.bs | 149 +++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 140 insertions(+), 9 deletions(-) diff --git a/fetch.bs b/fetch.bs index 34f21e833..3eb65986b 100644 --- a/fetch.bs +++ b/fetch.bs @@ -6846,11 +6846,73 @@ i.e., when a fetch group is terminated, or after

      Deferred fetching quota

      -

      The quota asserts that this deferred fetch doesn't exceed two quotas: one for the -document and it's same-origin same-tree relatives, and one for the reporting origin (64 kibibytes). -The larger quota ensures that the top-level {{Document}} and its subresources don't continue using -an unlimited amount of bandwidth after being destroyed. The per-origin quota ensures that a single -reporting sink (e.g. RUM library) doesn't reserve the whole quota to itself. + +

      The deferred-fetch quota is allocated to a top-level traversable (a "tab"), +amounting to 640 kibibytes. The top-level {{Document}} and its same-origin same-agent subframes can +use this quota to queue deferred fetches, or delegate some of it to cross-origin or cross-agent +subframes, using permissions policy. + +

      By default, 128 kibibytes out of these 640 kibibytes are allocated to delegating the quota to +cross-origin or cross-agent subframes, each reserving 8 kibibytes. + +

      The top-level {{Document}}, and subsequently its subframes, can control how much of their quota +is delegates to cross-origin/cross-agent subframes, by using {{PermissionsPolicy}}. +By default, {{PermissionPolicy/"deferred-fetch-minimal"}} is enabled for any origin, while +{{PermissionPolicy/"deferred-fetch"}} is enabled for the top-level document's origin only. +By relaxing the {{PermissionPolicy/"deferred-fetch"}} policy for particular origins and subframes, +the top-level document can allocate 64 kibibytes to those subframes. Similarly, by restricting the +{{PermissionPolicy/"deferred-fetch-minimal"}} policy for a particular origin or subframe, the +document can prevent the iframe from reserving the 8 kibibytes it would receive by default. By +disabling {{PermissionPolicy/"deferred-fetch-minimal"}} for the top-level document itself, the +entire 128 kibibytes delegated quota is collected back into the main pool of 640 kibibytes. + +

      Out of the allocated quota for a {{Document}}, only 64 kibibytes can be used concurrently for the +same reporting origin (the request's URL's origin). +This prevents a situation where particular 3rd party libraries would reserve quota +opportunistically, before they have data to send. + +

      +

      Any of the following calls to fetchLater() would throw due to +the request itself exceeding the 64 kibibytes quota allocated to a reporting origin. Note that the +size of the request includes the URL itself, the body, and the +header list. +

      
      +  fetchLater(a_64_kb_url);
      +  fetchLater("https://origin.example.com", {headers: headers_exceeding_64kb});
      +  fetchLater(a_32_kb_url, {headers: headers_exceeding_32kb});
      +  fetchLater("https://origin.example.com", {method: "POST", body: body_exceeding_64_kb});
      +
      + +

      In the following sequence, the first two requests would succeed, but the third one would throw. +That's because al overall 640 kibibytes quota was not exceeded in the first to call, however the 3rd +request exceeds the reporting-origin quota for https://a.example.com, and would throw. +

      
      +  fetchLater("https://a.example.com", {method: "POST", body: a_64kb_body});
      +  fetchLater("https://b.example.com", {method: "POST", body: a_64kb_body});
      +  fetchLater("https://a.example.com");
      +
      + +

      Same-origin same-agent subframes share the quota of their parent. However, cross-origin or +cross-agent iframes only receive 8kb of quota by default. So in the following example, the first 3 +calls would succeed and the last one would throw. +

      
      +  // In main page
      +  fetchLater("https://a.example.com", {method: "POST", body: a_64kb_body});
      +
      +  // In same-origin iframe
      +  fetchLater("https://b.example.com", {method: "POST", body: a_64kb_body});
      +
      +  // In cross-origin iframe at https://frame.example.com
      +  fetchLater("https://a.example.com", {body: a_5kb_body});
      +  fetchLater("https://a.example.com", {body: a_12kb_body});
      +
      + +

      To make the previous example not throw, the top-level {{Document}} needs to delegate some of its +quota to https://frame.example.com, for example by serving the following header: +

      Permissions-Policy: deferred-fetch=(self "https://frame.example.com")
      + +
      +

      This specification defined a policy-controlled feature identified by the string "deferred-fetch". Its @@ -7045,7 +7107,6 @@ shared.

  • Otherwise, set container's reserved deferred-fetch quota to zero. -

  • @@ -8957,9 +9018,12 @@ method steps are:
  • If request's URL's scheme is not an HTTP(S) scheme, then throw a {{TypeError}}. -

  • If request's - body is not null, and request's - body source is null, then throw a {{TypeError}}. +

  • +

    If request's + body is not null, and request's body + length is null, then throw a {{TypeError}}. + +

    Requests whose body is a {{ReadableStream}} cannot be deferred.

  • If request's client is not a fully active {{Document}}, then throw an "{{InvalidStateError}}" {{DOMException}}. @@ -8992,6 +9056,73 @@ method steps are: deferredRecord. +

    +

    The following call would queue a request to be fetched when the document is terminated: +

    fetchLater("https://report.example.com", { method: "POST",
    +  body: JSON.stringify(myReport) })
    + +

    The following call would also queue this request after 5 seconds, and the returned value would + allow callers to observe if it was indeed activated. Note that the request is guaranteed to be + invoked, even in cases where the user agent throttles timers. + +

    
    +  const result = fetchLater("https://report.example.com", {
    +      method: "POST",
    +      body: JSON.stringify(myReport),
    +      activateAfter: 5000
    +  });
    +
    +  function check_if_fetched() {
    +    return result.activated;
    +  }
    +  
    + +

    The {{FetchLaterResult}} object can be used together with an {{AbortSignal}}. For example: +

    
    +  let accumulated_events = [];
    +  let previous_result = null;
    +  const abort_signal = new AbortSignal();
    +  function accumulate_event(event) {
    +    if (previous_result) {
    +      if (previous_result.activated) {
    +        // The request is already activated, we can start from scratch.
    +        accumulated_events = [];
    +      } else {
    +        // Abort this request, and start a new one with all the events.
    +        signal.abort();
    +      }
    +    }
    +
    +    accumulated_events.push(event);
    +    fetchLater("https://report.example.com", {
    +          method: "POST",
    +          body: JSON.stringify(accumulated_events),
    +          activateAfter: 5000,
    +          abort_signal
    +      });
    +
    +    return result.activated;
    +  }
    +  
    + + +

    Any of the following calls to fetchLater() would throw: +

    
    +    // Only potentially trustworthy urls are supported.
    +    fetchLater("http://untrusted.example.com");
    +
    +    // The length of the deferred request has to be known when.
    +    fetchLater("https://origin.example.com", {body: someDynamicStream});
    +
    +    // Deferred fetching only works on active windows.
    +    const detachedWindow = iframe.contentWindow;
    +    iframe.remove();
    +    detachedWindow.fetchLater("https://origin.example.com");
    +  
    + + See deferred fetch quota examples for examples + portraying how the deferred-fetch quota works. +

    Garbage collection

    From 3fa4984f7f98a24df560d5b7c3953aa9689523ad Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Mon, 9 Dec 2024 21:10:57 +0000 Subject: [PATCH 070/102] Throw {{TypeError}} --- fetch.bs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/fetch.bs b/fetch.bs index 3eb65986b..b72eb0067 100644 --- a/fetch.bs +++ b/fetch.bs @@ -9026,10 +9026,10 @@ method steps are:

    Requests whose body is a {{ReadableStream}} cannot be deferred.

  • If request's client is not a fully active - {{Document}}, then throw an "{{InvalidStateError}}" {{DOMException}}. + {{Document}}, then throw a {{TypeError}}. -

  • If request's URL is not a - potentially trustworthy url, then throw a "{{SecurityError}}" {{DOMException}}. +

  • If request's URL is not a potentially trustworthy url, + then throw a {{TypeError}}.

  • If the available deferred-fetch quota given request's client and request's URL's origin From 3e724c49c090b25366e5c278c3b6ad1d37dc294d Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Tue, 10 Dec 2024 10:33:53 +0000 Subject: [PATCH 071/102] nits --- fetch.bs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/fetch.bs b/fetch.bs index b72eb0067..8b34d5742 100644 --- a/fetch.bs +++ b/fetch.bs @@ -6942,9 +6942,7 @@ quota to https://frame.example.com, for example by serving the foll

    1. Let quota be zero. -

    2. -

      Let quotaForOrigin be 64 kibibytes. -

      +

    3. Let quotaForOrigin be 64 kibibytes.

    4. For each otherNavigable in document's node navigable's @@ -6985,7 +6983,7 @@ quota to https://frame.example.com, for example by serving the foll

  • -

    Otherwise, +

    Otherwise:

    This is a subframe, check if it inherits quota from its parent container based on permissions policy. This would only be the case if the subframe and its parent do not share @@ -7038,8 +7036,8 @@ quota to https://frame.example.com, for example by serving the foll otherNavigable's navigable container's subframe reserved deferred-fetch quota. -

    This is a cross-origin/cross-agent subframe, so it spends some of the quota of - this document and its relatives. +

    This is a cross-origin/cross-agent subframe, so we check if it has reserved some + quota which was delegated via {{PermissionsPolicy}}.

  • If quota is less than zero, than return zero. @@ -7056,7 +7054,7 @@ quota to https://frame.example.com, for example by serving the foll

    This is called when the container document initiates a navigation. It potentially reserves either 64kb or 8kb of quota for the frame, if it doesn't share deferred-fetch quota -with the container and the permissions policy allow. It is not observable to the cotnainer document +with its parent and the permissions policy allow. It is not observable to the cotnainer document whether the reserved quota was used in practice. This algorithm assumes that the container's document might delegate quota to the navigated frame, and the reserved quota would only apply in that case, and would be ignored if it ends up being From 6500061a4afd475fad8e0413b55ca56788bced34 Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Tue, 10 Dec 2024 10:55:58 +0000 Subject: [PATCH 072/102] nits --- fetch.bs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fetch.bs b/fetch.bs index 8b34d5742..fda855567 100644 --- a/fetch.bs +++ b/fetch.bs @@ -7052,7 +7052,8 @@ quota to https://frame.example.com, for example by serving the foll

    To reserve deferred-fetch quota for a navigable container container given an origin originToNavigateTo: -

    This is called when the container document initiates a navigation. It potentially +

    This is called when the container document and the document that initiated the +navigation (the "source document") share deferred-fetch quota. It potentially reserves either 64kb or 8kb of quota for the frame, if it doesn't share deferred-fetch quota with its parent and the permissions policy allow. It is not observable to the cotnainer document whether the reserved quota was used in practice. From 5e287d9cde35ad54f8082f4dec6b3c1c68ca22ca Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Tue, 10 Dec 2024 11:33:19 +0000 Subject: [PATCH 073/102] Fix reserve algo --- fetch.bs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/fetch.bs b/fetch.bs index fda855567..2921ba18a 100644 --- a/fetch.bs +++ b/fetch.bs @@ -7073,7 +7073,10 @@ shared.

    Otherwise, if the inherited policy for {{PermissionPolicy/"deferred-fetch-minimal"}}, container and - originToNavigateTo is Enabled, then: + originToNavigateTo is Enabled, and container's + node document and container's node navigable's + top-level traversable's active document + share deferred-fetch quota, then:

    1. Let delegatedQuota be deferred-fetch delegated quota. @@ -7088,10 +7091,6 @@ shared. top-level traversable's active document do not share deferred-fetch quota. -

    2. navigable's navigable container's node document and - navigable's top-level traversable's active document - share deferred-fetch quota. -

    3. navigable's navigable container's reserved deferred-fetch quota is minimal subframe deferred-fetch quota. From b72c6647446f6040bbdc9556ea48579d35239f82 Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Tue, 10 Dec 2024 11:36:24 +0000 Subject: [PATCH 074/102] Add note about conds --- fetch.bs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/fetch.bs b/fetch.bs index 2921ba18a..4efdf05a8 100644 --- a/fetch.bs +++ b/fetch.bs @@ -7084,6 +7084,7 @@ shared.

      For each navigable of container's node navigable's top-level traversable's inclusive descendant navigables who meets all of the following conditions: +

      • navigable is not container's content navigable. @@ -7097,6 +7098,9 @@ shared.

        Decrement delegatedQuota by minimal subframe deferred-fetch quota. +

        This algorithm counts subframes, other than this one, which don't share quota with + the top-level document, and have reserved part of the deferred-fetch delegated quota. +

      • If delegatedQuota is less than minimal subframe deferred-fetch quota, then set container's reserved deferred-fetch quota to zero. From 66a69ac59c450e6b7122a5019e3e58370c83d9cf Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Tue, 10 Dec 2024 14:00:31 +0000 Subject: [PATCH 075/102] Add detailed example for iframe quota --- fetch.bs | 141 ++++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 124 insertions(+), 17 deletions(-) diff --git a/fetch.bs b/fetch.bs index 4efdf05a8..f5bd58960 100644 --- a/fetch.bs +++ b/fetch.bs @@ -6857,13 +6857,13 @@ cross-origin or cross-agent subframes, each reserving 8 kibibytes.

        The top-level {{Document}}, and subsequently its subframes, can control how much of their quota is delegates to cross-origin/cross-agent subframes, by using {{PermissionsPolicy}}. -By default, {{PermissionPolicy/"deferred-fetch-minimal"}} is enabled for any origin, while -{{PermissionPolicy/"deferred-fetch"}} is enabled for the top-level document's origin only. -By relaxing the {{PermissionPolicy/"deferred-fetch"}} policy for particular origins and subframes, +By default, {{PermissionsPolicy/"deferred-fetch-minimal"}} is enabled for any origin, while +{{PermissionsPolicy/"deferred-fetch"}} is enabled for the top-level document's origin only. +By relaxing the {{PermissionsPolicy/"deferred-fetch"}} policy for particular origins and subframes, the top-level document can allocate 64 kibibytes to those subframes. Similarly, by restricting the -{{PermissionPolicy/"deferred-fetch-minimal"}} policy for a particular origin or subframe, the +{{PermissionsPolicy/"deferred-fetch-minimal"}} policy for a particular origin or subframe, the document can prevent the iframe from reserving the 8 kibibytes it would receive by default. By -disabling {{PermissionPolicy/"deferred-fetch-minimal"}} for the top-level document itself, the +disabling {{PermissionsPolicy/"deferred-fetch-minimal"}} for the top-level document itself, the entire 128 kibibytes delegated quota is collected back into the main pool of 640 kibibytes.

        Out of the allocated quota for a {{Document}}, only 64 kibibytes can be used concurrently for the @@ -6907,19 +6907,126 @@ calls would succeed and the last one would throw. fetchLater("https://a.example.com", {body: a_12kb_body});

  • -

    To make the previous example not throw, the top-level {{Document}} needs to delegate some of its -quota to https://frame.example.com, for example by serving the following header: -

    Permissions-Policy: deferred-fetch=(self "https://frame.example.com")
    +

    To make the previous example not throw, the top-level {{Document}} can delegate some of its quota +to https://frame.example.com, for example by serving the following header: +

    Permissions-Policy: deferred-fetch=(self "https://frame.example.com")
    + +

    The following tables illustrates how quota is distributed to different iframes, each table +representing a different top-level traversable ("tab"). + +

    For a tree with its top-level Permissions-Policy header set to +deferred-fetch=(self "https://ok.example.com"): + + + + + + + + + + + + +
    Window name + Parent + Initial origin + Current origin + Quota +
    top + null + https://me.example.com + https://me.example.com + 384kb (512kb - 64kb - 64kb). d and frame g were granted + 64kb each, even though for g this quota ended up being unavilable. +
    a + top + https://me.example.com + https://me.example.com + Shared with top's quota. +
    b + a + https://x.example.com + https://x.example.com + 8kb, due to the default {{PermissionsPolicy/"deferred-fetch-minimal"}} policy. +
    c + top + https://x.example.com + https://x.example.com + 8kb, due to the default {{PermissionsPolicy/"deferred-fetch-minimal"}} policy. +
    d + top + https://ok.example.com + https://ok.example.com + 64kb, due to the {{PermissionsPolicy/"deferred-fetch"}} policy. +
    e + d + https://x.example.com + https://x.example.com + 0, as its parent doesn't share the quota with the top-level document. +
    f + d + https://me.example.com + https://me.example.com + Shared with top's quota. +
    g + top + https://ok.example.com + https://x.example.com + 0, as the reserved quota when navigating doesn't match the current {{PermissionsPolicy}}. +
    h + top + https://ok.example.com + https://me.example.com + Shared with top's quota. +
    + +

    For a tree with its top-level Permissions-Policy header set to +deferred-fetch-minimal=(); deferred-fetch=(self https://ok.example.com): + + + + + + + +
    Window name + Parent + Initial origin + Current origin + Quota +
    top + null + https://me.example.com + https://me.example.com + 576kb (640kb - 64kb). c was granted 64kb. Since {{PermissionsPolicy/"deferred-fetch-minimal"}} + was explicitly disabled, the initial top-level quota is 640kb instead of 512kb. +
    a + top + https://me.example.com + https://me.example.com + Shared with top's quota. +
    b + a + https://x.example.com + https://x.example.com + 0, due to the {{PermissionsPolicy/"deferred-fetch-minimal"}} being explicitly disabled. +
    c + top + https://ok.example.com + https://ok.example.com + 64kb, due to the {{PermissionsPolicy/"deferred-fetch"}} policy. +

    This specification defined a policy-controlled feature identified by the string -"deferred-fetch". Its +"deferred-fetch". Its default allowlist is "self".

    This specification defined a policy-controlled feature identified by the string -"deferred-fetch-minimal". Its +"deferred-fetch-minimal". Its default allowlist is "*".

    The optional subframe deferred-fetch quota is 64 kibibytes. @@ -6971,14 +7078,14 @@ quota to https://frame.example.com, for example by serving the foll

  • If otherDocument is not allowed to use the policy-controlled feature - {{PermissionPolicy/"deferred-fetch"}}, then return 0. + {{PermissionsPolicy/"deferred-fetch"}}, then return 0.

  • Set quota be 640 kibibytes.

    640kb should be enough for everyone.

  • If otherDocument is allowed to use the - policy-controlled feature {{PermissionPolicy/"deferred-fetch-minimal"}}, then + policy-controlled feature {{PermissionsPolicy/"deferred-fetch-minimal"}}, then decrement quota by deferred-fetch delegated quota. @@ -7001,14 +7108,14 @@ quota to https://frame.example.com, for example by serving the foll

  • If otherContainer's subframe reserved deferred-fetch quota is optional subframe deferred-fetch quota, and otherDocument is allowed to use the policy-controlled feature - {{PermissionPolicy/"deferred-fetch"}}, then increment quota by + {{PermissionsPolicy/"deferred-fetch"}}, then increment quota by optional subframe deferred-fetch quota.

  • Otherwise, if otherContainer's subframe reserved deferred-fetch quota is minimal subframe deferred-fetch quota and otherDocument is allowed to use the policy-controlled feature - {{PermissionPolicy/"deferred-fetch-minimal"}}, then increment quota by + {{PermissionsPolicy/"deferred-fetch-minimal"}}, then increment quota by minimal subframe deferred-fetch quota. @@ -7052,7 +7159,7 @@ quota to https://frame.example.com, for example by serving the foll

    To reserve deferred-fetch quota for a navigable container container given an origin originToNavigateTo: -

    This is called when the container document and the document that initiated the +

    This is called when container and the document that initiated the navigation (the "source document") share deferred-fetch quota. It potentially reserves either 64kb or 8kb of quota for the frame, if it doesn't share deferred-fetch quota with its parent and the permissions policy allow. It is not observable to the cotnainer document @@ -7063,7 +7170,7 @@ shared.

    1. If the inherited policy - for {{PermissionPolicy/"deferred-fetch"}}, container and originToNavigateTo + for {{PermissionsPolicy/"deferred-fetch"}}, container and originToNavigateTo is Enabled, and the available deferred-fetch quota for container's container document is equal or greater than optional subframe deferred-fetch quota, then set container's @@ -7072,7 +7179,7 @@ shared.

    2. Otherwise, if the inherited policy for - {{PermissionPolicy/"deferred-fetch-minimal"}}, container and + {{PermissionsPolicy/"deferred-fetch-minimal"}}, container and originToNavigateTo is Enabled, and container's node document and container's node navigable's top-level traversable's active document From 474571647d2b4d5ad5532e2570078fbc45a8e8ba Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Wed, 11 Dec 2024 09:34:53 +0000 Subject: [PATCH 076/102] granted --- fetch.bs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fetch.bs b/fetch.bs index f5bd58960..333738d9b 100644 --- a/fetch.bs +++ b/fetch.bs @@ -7144,7 +7144,7 @@ representing a different top-level traversable ("tab"). subframe reserved deferred-fetch quota.

      This is a cross-origin/cross-agent subframe, so we check if it has reserved some - quota which was delegated via {{PermissionsPolicy}}. + quota which was granted via {{PermissionsPolicy}}.

  • If quota is less than zero, than return zero. From 7a246610c861fc1e204bd407ca92782347bf438d Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Wed, 11 Dec 2024 11:48:06 +0000 Subject: [PATCH 077/102] Lots of fixes --- fetch.bs | 367 +++++++++++++++++++++++++++---------------------------- 1 file changed, 179 insertions(+), 188 deletions(-) diff --git a/fetch.bs b/fetch.bs index 333738d9b..6f0d336c9 100644 --- a/fetch.bs +++ b/fetch.bs @@ -2734,9 +2734,6 @@ functionality. fetch records, a list of fetch records. -

    Each navigable container has an associated number -subframe reserved deferred-fetch quota. Unless stated otherwise, it is zero. -

    A fetch group has an associated deferred fetch records, a list of deferred fetch records. @@ -2752,24 +2749,21 @@ a list of deferred fetch records


    -

    A deferred fetch record is a struct used to maintain state needed +

    A deferred fetch record is a struct used to maintain state needed to invoke a fetch at a later time, e.g., when a {{Document}} object is unloaded or becomes not fully active. It has the following items:

    -
    request +
    request
    A request. -
    invoke state (default "deferred") -
    -

    "deferred", "aborted", or "activated". - -

    This value can be modified in parallel. There could be a race condition where - the {{Document}} object's event loop might read it as - "deferred" at the same time that it is changed to "activated". User - agents can mitigate this race condition in an implementation-defined manner. +

    done (default false) +
    A boolean.
    +

    Each navigable container has an associated number +nested document reserved deferred-fetch quota. Unless stated otherwise, it is 0. +


    When a fetch group fetchGroup is @@ -2783,7 +2777,7 @@ not fully active. It has the following itemsterminate record's controller. -

  • Process deferred fetches for fetchGroup. +

  • Process or propagate deferred fetches for fetchGroup. @@ -6757,12 +6751,21 @@ agent's CORS-preflight cache for which there is a cache entry matchDeferred fetching allows callers to request that a fetch is invoked at the latest possible moment, i.e., when a fetch group is terminated, or after a timeout. +

    The deferred fetch task source is a task source used to update the result of a +deferred fetch. User agents must prioritize tasks in this task source before other task +sources, specifically task sources that can result in running scripts such as the +DOM manipulation task source, to reflect the most recent state of a +fetchLater() call before running any scripts that might depend on it. +

    To queue a deferred fetch given a -request request and a null or {{DOMHighResTimeStamp}} -activateAfter (default null): +request request, a null or {{DOMHighResTimeStamp}} +activateAfter, an onActivatedWithoutTermination, which is an algorithm that +takes no arguments:

      +
    1. Populate request from client given request. +

    2. Set request's service-workers mode to "none".

    3. Set request's keepalive to true. @@ -6778,17 +6781,21 @@ i.e., when a fetch group is terminated, or after

      If activateAfter is non-null, then run the following steps in parallel:

        -
      1. The user agent should wait until activateAfter milliseconds have passed, - or until the user agent has a reason to believe that it is about to lose the opportunity to - execute scripts, e.g., when the browser is moved to the background, or when request's - client is a {{Document}} that had a "hidden" - visibility state for a long period of time. - -

      2. The user agent may wait for a further implementation-defined duration, e.g., - in order to batch several requests together or to wait until keepalive - requests are complete. +

      3. +

        The user agent should wait until any of the following conditions is met: +

          +
        • At least activateAfter milliseconds have passed. +

        • The user agent has a reason to believe that it is about to lose the opportunity to + execute scripts, e.g., when the browser is moved to the background, or when + request's client is a {{Document}} that had a + "hidden" visibility state for a long period of time. +

        -
      4. Process a deferred fetch given deferredRecord. +

      5. If the result of calling process a deferred fetch given deferredRecord + returns true, then queue a global task on the deferred fetch task source with + request's client's + global object and + onActivatedWithoutTermination.

    4. @@ -6800,10 +6807,13 @@ i.e., when a fetch group is terminated, or after

      To compute the total request length of a request request:

        -
      1. Let totalRequestLength be the length of request's +

      2. Let totalRequestLength be the length of request's URL, serialized with [=URL serializer/exclude fragment=] set to true. +

      3. Increment totalRequestLength by the length of + request's referrer, serialized. +

      4. For each (name, value) of request's header list, increment totalRequestLength by name's length + value's length. @@ -6815,32 +6825,40 @@ i.e., when a fetch group is terminated, or after

    -

    To process deferred fetches given a fetch group fetchGroup: +

    To process or propagate deferred fetches given a fetch group +fetchGroup, for each deferred fetch record +deferredRecord of fetchGroup's +deferred fetch records:

      -
    1. Let deferredFetchRecords be fetchGroup's - deferred fetch records. +

    2. Let document be deferredRecord's + request's client. + +

    3. Let parent be document's node navigable's + container document. -

    4. Let fetchGroup's - deferred fetch records be « ». +

    5. If parent is a fully active {{Document}} whose + origin is same origin with document's + origin, then set deferredRecord's + request's client to parent and + append deferredRecord to parent's + fetch group's deferred fetch records. -

    6. For each deferred fetch record - deferredRecord of deferredFetchRecords, process a deferred fetch given - deferredRecord. +

    7. Otherwise, .

    To process a deferred fetch deferredRecord:

      -
    1. If deferredRecord's invoke state is not - "deferred", then return. +

    2. If deferredRecord's done is true, then + return false. -

    3. Set deferredRecord's invoke state to - "activated". +

    4. Set deferredRecord's done to true.

    5. Fetch deferredRecord's request. +

    6. Return true.

    @@ -6848,23 +6866,24 @@ i.e., when a fetch group is terminated, or after

    The deferred-fetch quota is allocated to a top-level traversable (a "tab"), -amounting to 640 kibibytes. The top-level {{Document}} and its same-origin same-agent subframes can -use this quota to queue deferred fetches, or delegate some of it to cross-origin or cross-agent -subframes, using permissions policy. +amounting to 640 kibibytes. The top-level {{Document}} and its same-origin nested documents can +use this quota to queue deferred fetches, or delegate some of it to cross-origin nested documents, +using permissions policy.

    By default, 128 kibibytes out of these 640 kibibytes are allocated to delegating the quota to -cross-origin or cross-agent subframes, each reserving 8 kibibytes. - -

    The top-level {{Document}}, and subsequently its subframes, can control how much of their quota -is delegates to cross-origin/cross-agent subframes, by using {{PermissionsPolicy}}. -By default, {{PermissionsPolicy/"deferred-fetch-minimal"}} is enabled for any origin, while -{{PermissionsPolicy/"deferred-fetch"}} is enabled for the top-level document's origin only. -By relaxing the {{PermissionsPolicy/"deferred-fetch"}} policy for particular origins and subframes, -the top-level document can allocate 64 kibibytes to those subframes. Similarly, by restricting the -{{PermissionsPolicy/"deferred-fetch-minimal"}} policy for a particular origin or subframe, the -document can prevent the iframe from reserving the 8 kibibytes it would receive by default. By -disabling {{PermissionsPolicy/"deferred-fetch-minimal"}} for the top-level document itself, the -entire 128 kibibytes delegated quota is collected back into the main pool of 640 kibibytes. +cross-origin nested documents, each reserving 8 kibibytes. + +

    The top-level {{Document}}, and subsequently its nested documents, can control how much of their quota +is delegates to cross-origin/cross-agent nested documents, by using {{PermissionsPolicy}}. +By default, "{{PermissionsPolicy/deferred-fetch-minimal}}" is enabled for any origin, while +"{{PermissionsPolicy/deferred-fetch}}" is enabled for the top-level document's origin only. +By relaxing the "{{PermissionsPolicy/deferred-fetch}}" policy for particular origins and nested +documents, the top-level document can allocate 64 kibibytes to those nested documents. Similarly, by +restricting the "{{PermissionsPolicy/deferred-fetch-minimal}}" policy for a particular origin or +nested document, the document can prevent the iframe from reserving the 8 kibibytes it would receive +by default. By disabling "{{PermissionsPolicy/deferred-fetch-minimal}}" for the top-level document +itself, the entire 128 kibibytes delegated quota is collected back into the main pool of 640 +kibibytes.

    Out of the allocated quota for a {{Document}}, only 64 kibibytes can be used concurrently for the same reporting origin (the request's URL's origin). @@ -6884,15 +6903,16 @@ size of the request includes the URL itself, the

  • In the following sequence, the first two requests would succeed, but the third one would throw. -That's because al overall 640 kibibytes quota was not exceeded in the first to call, however the 3rd -request exceeds the reporting-origin quota for https://a.example.com, and would throw. +That's because the overall 640 kibibytes quota was not exceeded in the first two calls, however the +3rd request exceeds the reporting-origin quota for https://a.example.com, and would +throw.

    
       fetchLater("https://a.example.com", {method: "POST", body: a_64kb_body});
       fetchLater("https://b.example.com", {method: "POST", body: a_64kb_body});
       fetchLater("https://a.example.com");
     
    -

    Same-origin same-agent subframes share the quota of their parent. However, cross-origin or +

    Same-origin nested documents share the quota of their parent. However, cross-origin or cross-agent iframes only receive 8kb of quota by default. So in the following example, the first 3 calls would succeed and the last one would throw.

    
    @@ -6929,8 +6949,8 @@ representing a different top-level traversable ("tab").
       null
       https://me.example.com
       https://me.example.com
    -  384kb (512kb - 64kb - 64kb). d and frame g were granted
    -  64kb each, even though for g this quota ended up being unavilable.
    +  384 kibibytes (512 - 64 - 64). d and frame g were granted
    +  64 kibibytes each, even though for g this quota ended up being unavailable.
      
       a
       top
    @@ -6942,19 +6962,19 @@ representing a different top-level traversable ("tab").
       a
       https://x.example.com
       https://x.example.com
    -  8kb, due to the default {{PermissionsPolicy/"deferred-fetch-minimal"}} policy.
    +  8 kibibytes, due to the default "{{PermissionsPolicy/deferred-fetch-minimal}}" policy.
      
       c
       top
       https://x.example.com
       https://x.example.com
    -  8kb, due to the default {{PermissionsPolicy/"deferred-fetch-minimal"}} policy.
    +  8 kibibytes, due to the default "{{PermissionsPolicy/deferred-fetch-minimal}}" policy.
      
       d
       top
       https://ok.example.com
       https://ok.example.com
    -  64kb, due to the {{PermissionsPolicy/"deferred-fetch"}} policy.
    +  64 kibibytes, due to the "{{PermissionsPolicy/deferred-fetch}}" policy.
      
       e
       d
    @@ -6996,7 +7016,7 @@ representing a different top-level traversable ("tab").
       null
       https://me.example.com
       https://me.example.com
    -  576kb (640kb - 64kb). c was granted 64kb. Since {{PermissionsPolicy/"deferred-fetch-minimal"}}
    +  576kb (640kb - 64kb). c was granted 64kb. Since "{{PermissionsPolicy/deferred-fetch-minimal}}"
       was explicitly disabled, the initial top-level quota is 640kb instead of 512kb.
      
       a
    @@ -7009,37 +7029,29 @@ representing a different top-level traversable ("tab").
       a
       https://x.example.com
       https://x.example.com
    -  0, due to the {{PermissionsPolicy/"deferred-fetch-minimal"}} being explicitly disabled.
    +  0, due to the "{{PermissionsPolicy/deferred-fetch-minimal}}" being explicitly disabled.
      
       c
       top
       https://ok.example.com
       https://ok.example.com
    -  64kb, due to the {{PermissionsPolicy/"deferred-fetch"}} policy.
    +  64kb, due to the "{{PermissionsPolicy/deferred-fetch}}" policy.
     
     
     
     
     
     

    This specification defined a policy-controlled feature identified by the string -"deferred-fetch". Its +"deferred-fetch". Its default allowlist is "self".

    This specification defined a policy-controlled feature identified by the string -"deferred-fetch-minimal". Its +"deferred-fetch-minimal". Its default allowlist is "*". -

    The optional subframe deferred-fetch quota is 64 kibibytes. -

    The minimal subframe deferred-fetch quota is 8 kibibytes. -

    The deferred-fetch delegated quota is 128 kibibytes. - -

    -

    Two {{Document}}s a and b are said to -share deferred-fetch quota if a's -relevant agent is b's relevant agent, and a's -URL's origin is same origin with b's -URL's origin. -

    +

    The optional nested document deferred-fetch quota is 64 kibibytes. +

    The minimal nested document deferred-fetch quota is 8 kibibytes. +

    The max containers with minimal quota is 16.

    @@ -7047,25 +7059,25 @@ representing a different top-level traversable ("tab"). document and an origin-or-null origin:
      -
    1. Let quota be zero. +

    2. Let quota be 0.

    3. Let quotaForOrigin be 64 kibibytes.

    4. -

      For each otherNavigable in document's node navigable's +

      For each otherNavigable of document's node navigable's top-level traversable's inclusive descendant navigables:

      This algorithm iterates over the entire navigable tree. It accumulates quota from - the top-level traversable, and from subframes who inherit quota from cross-origin or - cross-agent navigables. Subsequently, it subtracts the quota delegated to cross-origin or - cross-agent subframes, as well as quota spent on pending deferred fetch requests. + the top-level traversable, and from nested documents who inherit quota. Subsequently, + it subtracts the quota delegated to cross-origin nested documents, as well as quota + spent on pending deferred fetch requests.

      1. Let otherDocument be otherNavigable's active document.

      2. -

        If document and otherDocument share deferred-fetch quota, - then: +

        If document's origin is same origin with + otherDocument's origin, then:

        1. If otherDocument's node navigable is a @@ -7074,51 +7086,40 @@ representing a different top-level traversable ("tab").

          Initialize the quota from the top-level traversable.

            -
          1. Assert: quota is zero. +

          2. Assert: quota is 0.

          3. If otherDocument is not allowed to use the policy-controlled feature - {{PermissionsPolicy/"deferred-fetch"}}, then return 0. + "{{PermissionsPolicy/deferred-fetch}}", then return 0.

          4. Set quota be 640 kibibytes.

            640kb should be enough for everyone.

          5. If otherDocument is allowed to use the - policy-controlled feature {{PermissionsPolicy/"deferred-fetch-minimal"}}, then - decrement quota by deferred-fetch delegated quota. + policy-controlled feature "{{PermissionsPolicy/deferred-fetch-minimal}}", then + decrement quota by max containers with minimal quota, multiplied by + minimal nested document deferred-fetch quota.

        2. -

          Otherwise: - -

          This is a subframe, check if it inherits quota from its parent container based - on permissions policy. This would only be the case if the subframe and its parent do not share - quota, as quota is either shared or delegated. - -

            -
          1. Let otherContainer be otherNavigable's - navigable container. +

            Otherwise, if any of the following conditions is true: +

            -
              -
            1. If otherContainer's subframe reserved deferred-fetch quota is - optional subframe deferred-fetch quota, and otherDocument is - allowed to use the policy-controlled feature - {{PermissionsPolicy/"deferred-fetch"}}, then increment quota by - optional subframe deferred-fetch quota. - -

            2. Otherwise, if otherContainer's - subframe reserved deferred-fetch quota is - minimal subframe deferred-fetch quota and otherDocument - is allowed to use the policy-controlled feature - {{PermissionsPolicy/"deferred-fetch-minimal"}}, then increment quota by - minimal subframe deferred-fetch quota. -

            -
          +

          then increment quota by otherNavigable's navigable container's + nested document reserved deferred-fetch quota.

        3. For each deferred fetch record deferredRecord of @@ -7136,17 +7137,13 @@ representing a different top-level traversable ("tab").

      -
    5. -

      Otherwise, if otherNavigable is not a top-level traversable, and its - parent's active document and document - share deferred-fetch quota, then decrement quota by - otherNavigable's navigable container's - subframe reserved deferred-fetch quota. - -

      This is a cross-origin/cross-agent subframe, so we check if it has reserved some - quota which was granted via {{PermissionsPolicy}}. +

    6. Otherwise, if otherDocument's container document is a {{Document}} whose + origin is same origin with document's + origin, then decrement quota by otherNavigable's + navigable container's nested document reserved deferred-fetch quota.

    -
  • If quota is less than zero, than return zero. + +

  • If quota is less than 0, than return 0.

  • If quota is less than quotaForOrigin, then return quota. @@ -7160,8 +7157,8 @@ representing a different top-level traversable ("tab"). container given an origin originToNavigateTo:

    This is called when container and the document that initiated the -navigation (the "source document") share deferred-fetch quota. It potentially -reserves either 64kb or 8kb of quota for the frame, if it doesn't share deferred-fetch quota +navigation (the "source document") are same origin. It potentially +reserves either 64kb or 8kb of quota for the frame, if it is not same origin with its parent and the permissions policy allow. It is not observable to the cotnainer document whether the reserved quota was used in practice. This algorithm assumes that the container's document might delegate quota to the navigated frame, @@ -7169,53 +7166,48 @@ and the reserved quota would only apply in that case, and would be ignored if it shared.

      +
    1. Set container's reserved deferred-fetch quota to 0. +

    2. If the inherited policy - for {{PermissionsPolicy/"deferred-fetch"}}, container and originToNavigateTo + for "{{PermissionsPolicy/deferred-fetch}}", container and originToNavigateTo is Enabled, and the available deferred-fetch quota for container's container document is equal or greater than - optional subframe deferred-fetch quota, then set container's - reserved deferred-fetch quota to optional subframe deferred-fetch quota. + optional nested document deferred-fetch quota, then set container's + reserved deferred-fetch quota to optional nested document deferred-fetch quota and + return. -

    3. -

      Otherwise, if the - inherited policy for - {{PermissionsPolicy/"deferred-fetch-minimal"}}, container and - originToNavigateTo is Enabled, and container's - node document and container's node navigable's - top-level traversable's active document - share deferred-fetch quota, then: -

        -
      1. Let delegatedQuota be deferred-fetch delegated quota. - -

      2. -

        For each navigable of container's - node navigable's top-level traversable's - inclusive descendant navigables who meets all of the following conditions: - -

        - -

        Decrement delegatedQuota by minimal subframe deferred-fetch quota. - -

        This algorithm counts subframes, other than this one, which don't share quota with - the top-level document, and have reserved part of the deferred-fetch delegated quota. - -

      3. If delegatedQuota is less than minimal subframe deferred-fetch quota, - then set container's reserved deferred-fetch quota to zero. +

      4. If the inherited policy + for "{{PermissionsPolicy/deferred-fetch-minimal}}", container and + originToNavigateTo is Disabled, then return. + +

      5. If container's node document's origin is not + same origin with container's node navigable's + top-level traversable's active document's origin, + then return. + +

      6. Let containersWithReservedMinimalQuota be container's + node navigable's top-level traversable's + descendant navigables, removing any navigable + whose reserved deferred-fetch quota is not + minimal nested document deferred-fetch quota . + +

      7. If containersWithReservedMinimalQuota's size is less + than max containers with minimal quota, then set container's + reserved deferred-fetch quota to minimal nested document deferred-fetch quota. +

      +
  • -
  • Otherwise, set container's reserved deferred-fetch quota to - minimal subframe deferred-fetch quota. - +

    +

    To potentially free deferred-fetch quota for a {{Document}} +document, if document's node navigable's container document is +not null, and its origin is same origin with document, then +set document's node navigable's navigable container's +reserved deferred-fetch quota to 0. -

  • Otherwise, set container's reserved deferred-fetch quota to zero. +

    This is called when a {{Document}} is created. It ensures that same-origin nested +documents don't reserve quota, as they anyway share their parent quota. It can only be called upon +document creation, as the origin of the {{Document}} is only known after +redirects are handled. @@ -9093,13 +9085,12 @@ with a promise, request, responseObject, and an -

    A {{FetchLaterResult}} has an associated deferred fetch record -deferred record. +

    A {{FetchLaterResult}} has an associated activated getter steps, +which is an algorithm returning a boolean.

    -

    The activated getter steps are to return -true if this's deferred record's -invoke state is "activated"; Otherwise false. +

    The activated getter steps are to call +this's activated getter steps.

    @@ -9124,9 +9115,15 @@ method steps are:
  • If activateAfter is less than 0, then throw a {{RangeError}}. +

  • If request's client is not a fully active + {{Document}}, then throw a {{TypeError}}. +

  • If request's URL's scheme is not an HTTP(S) scheme, then throw a {{TypeError}}. +

  • If request's URL is not a potentially trustworthy URL, + then throw a {{TypeError}}. +

  • If request's body is not null, and request's body @@ -9134,35 +9131,31 @@ method steps are:

    Requests whose body is a {{ReadableStream}} cannot be deferred. -

  • If request's client is not a fully active - {{Document}}, then throw a {{TypeError}}. - -

  • If request's URL is not a potentially trustworthy url, - then throw a {{TypeError}}. -

  • If the available deferred-fetch quota given request's client and request's URL's origin is less than request's total request length, then throw a "{{QuotaExceededError}}" {{DOMException}}. +

  • Let activated be false. +

  • Let deferredRecord be the result of calling - queue a deferred fetch given request and activateAfter. + queue a deferred fetch given request, activateAfter, and the + following step: set activated to true.

  • Add the following abort steps to requestObject's signal:

      -
    1. Set deferredRecord's invoke state to - "aborted". +

    2. Set deferredRecord's done to true.

    3. Remove deferredRecord from request's client's fetch group's deferred fetch records.

    -
  • Return a new {{FetchLaterResult}} whose deferred record is - deferredRecord. +

  • Return a new {{FetchLaterResult}} whose + activated getter steps are to return activated.

    @@ -9203,14 +9196,12 @@ method steps are: } accumulated_events.push(event); - fetchLater("https://report.example.com", { + result = fetchLater("https://report.example.com", { method: "POST", body: JSON.stringify(accumulated_events), activateAfter: 5000, abort_signal }); - - return result.activated; }
  • From c82df4bb2920b0dab77295050a431748e68c28e4 Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Wed, 11 Dec 2024 14:18:46 +0000 Subject: [PATCH 078/102] Turn quota example into a tree --- fetch.bs | 207 ++++++++++++++++++++----------------------------------- 1 file changed, 76 insertions(+), 131 deletions(-) diff --git a/fetch.bs b/fetch.bs index 6f0d336c9..e557bdb2c 100644 --- a/fetch.bs +++ b/fetch.bs @@ -2777,7 +2777,7 @@ not fully active. It has the following itemsterminate record's controller. -
  • Process or propagate deferred fetches for fetchGroup. +

  • Process deferred fetches for fetchGroup. @@ -6764,6 +6764,8 @@ sources, specifically task sources that can result in running scripts such as th takes no arguments:

      +
    1. Assert: request's client is a {{Document}}. +

    2. Populate request from client given request.

    3. Set request's service-workers mode to "none". @@ -6773,9 +6775,22 @@ takes no arguments:

    4. Let deferredRecord be a new deferred fetch record whose request is request. -

    5. Append deferredRecord to request's - client's fetch group's - deferred fetch records. +

    6. Let topMostDirectSameOriginAncestor be var>request's + client. + +

    7. While topMostDirectSameOriginAncestor's node navigable's + container document is a {{Document}} whose origin is same origin + with request's client's origin, set + topMostDirectSameOriginAncestor to topMostDirectSameOriginAncestor's + node navigable's container document. + +

    8. +

      Append deferredRecord to + topMostDirectSameOriginAncestor's active document's + fetch group's deferred fetch records. + +

      This prevents a case where eagerly creating and destroying nested documents would + circumvent the keepalive quota.

    9. If activateAfter is non-null, then run the following steps in parallel:

      @@ -6825,26 +6840,11 @@ takes no arguments:
      -

      To process or propagate deferred fetches given a fetch group +

      To process deferred fetches given a fetch group fetchGroup, for each deferred fetch record deferredRecord of fetchGroup's -deferred fetch records: - -

        -
      1. Let document be deferredRecord's - request's client. - -

      2. Let parent be document's node navigable's - container document. - -

      3. If parent is a fully active {{Document}} whose - origin is same origin with document's - origin, then set deferredRecord's - request's client to parent and - append deferredRecord to parent's - fetch group's deferred fetch records. - -

      4. Otherwise, . +deferred fetch records process a deferred fetch +deferredRecord.

      @@ -6893,13 +6893,14 @@ opportunistically, before they have data to send.

      Any of the following calls to fetchLater() would throw due to the request itself exceeding the 64 kibibytes quota allocated to a reporting origin. Note that the -size of the request includes the URL itself, the body, and the -header list. +size of the request includes the URL itself, the body, the +header list, and the referrer.

      
      -  fetchLater(a_64_kb_url);
      +  fetchLater(a_72_kb_url);
         fetchLater("https://origin.example.com", {headers: headers_exceeding_64kb});
         fetchLater(a_32_kb_url, {headers: headers_exceeding_32kb});
         fetchLater("https://origin.example.com", {method: "POST", body: body_exceeding_64_kb});
      +  fetchLater(a_62_kb_url /* with a 3kb referrer */);
       

      In the following sequence, the first two requests would succeed, but the third one would throw. @@ -6919,125 +6920,69 @@ calls would succeed and the last one would throw. // In main page fetchLater("https://a.example.com", {method: "POST", body: a_64kb_body}); - // In same-origin iframe + // In same-origin nested document fetchLater("https://b.example.com", {method: "POST", body: a_64kb_body}); - // In cross-origin iframe at https://frame.example.com + // In cross-origin nested document at https://frame.example.com fetchLater("https://a.example.com", {body: a_5kb_body}); fetchLater("https://a.example.com", {body: a_12kb_body});

  • +

    To make the previous example not throw, the top-level {{Document}} can delegate some of its quota to https://frame.example.com, for example by serving the following header:

    Permissions-Policy: deferred-fetch=(self "https://frame.example.com")
    -

    The following tables illustrates how quota is distributed to different iframes, each table -representing a different top-level traversable ("tab"). - -

    For a tree with its top-level Permissions-Policy header set to -deferred-fetch=(self "https://ok.example.com"): - - - - - - - - - - - - -
    Window name - Parent - Initial origin - Current origin - Quota -
    top - null - https://me.example.com - https://me.example.com - 384 kibibytes (512 - 64 - 64). d and frame g were granted - 64 kibibytes each, even though for g this quota ended up being unavailable. -
    a - top - https://me.example.com - https://me.example.com - Shared with top's quota. -
    b - a - https://x.example.com - https://x.example.com - 8 kibibytes, due to the default "{{PermissionsPolicy/deferred-fetch-minimal}}" policy. -
    c - top - https://x.example.com - https://x.example.com - 8 kibibytes, due to the default "{{PermissionsPolicy/deferred-fetch-minimal}}" policy. -
    d - top - https://ok.example.com - https://ok.example.com - 64 kibibytes, due to the "{{PermissionsPolicy/deferred-fetch}}" policy. -
    e - d - https://x.example.com - https://x.example.com - 0, as its parent doesn't share the quota with the top-level document. -
    f - d - https://me.example.com - https://me.example.com - Shared with top's quota. -
    g - top - https://ok.example.com - https://x.example.com - 0, as the reserved quota when navigating doesn't match the current {{PermissionsPolicy}}. -
    h - top - https://ok.example.com - https://me.example.com - Shared with top's quota. -
    +

    Each nested document reserves its own quota, and all the same-origin documents in the tree share +quota with each other. So the following would work, because each frame reserve 8 kibibytes and they +share the accumulated 16 kibibytes: +

    
    +  // In cross-origin nested document at https://frame.example.com/frame-1
    +  fetchLater("https://a.example.com", {body: a_6kb_body});
     
    -

    For a tree with its top-level Permissions-Policy header set to -deferred-fetch-minimal=(); deferred-fetch=(self https://ok.example.com): + // In cross-origin nested document at https://frame.example.com/frame-2 + fetchLater("https://a.example.com", {body: a_6kb_body}); +

    - - - - - - -
    Window name - Parent - Initial origin - Current origin - Quota -
    top - null - https://me.example.com - https://me.example.com - 576kb (640kb - 64kb). c was granted 64kb. Since "{{PermissionsPolicy/deferred-fetch-minimal}}" - was explicitly disabled, the initial top-level quota is 640kb instead of 512kb. -
    a - top - https://me.example.com - https://me.example.com - Shared with top's quota. -
    b - a - https://x.example.com - https://x.example.com - 0, due to the "{{PermissionsPolicy/deferred-fetch-minimal}}" being explicitly disabled. -
    c - top - https://ok.example.com - https://ok.example.com - 64kb, due to the "{{PermissionsPolicy/deferred-fetch}}" policy. -
    +

    The following chart illustrates how quota is distributed to different nested documents in a tree: + +

    
    ++ https://me.example.com with Permissions-policy: deferred-fetch=(self "https://ok.example.com")
    +|
    ++ ---- + https://me.example.com
    +|      | Shares quota with the top-level traversable, as they're same origin.
    +|      |
    +|      + ---- + https://x.example.com
    +|               Shares 16 kibibytes together with one other cross-origin nested document of the same origin.
    +|
    +|
    ++ ---- + https://x.example.com
    +|        Shares 16 kibibytes together with one other cross-origin nested document of the same origin.
    +|
    ++ ---- + https://ok.example.com/good
    +|      | 64 kibibytes, granted via the "{{PermissionsPolicy/deferred-fetch}}" policy.
    +|      |
    +|      + ---- + https://x.example.com
    +|               0. Only documents with the same originas the top-level traversable can grant
    +|               the 8 kibibytes
    +|
    ++ ---- + https://ok.example.com/redirect, navigated to https://x.example.com
    +|        0. The reserved 64 kibibytes for https://ok.example.com are not available for https://x.example.com.
    +|
    ++ ---- + https://ok.example.com/back, navigated to https://me.example.com
    +         Shares quota with the top-level traversable, as they're same origin.
    +
    +

    In the above example, the top-level traversable and its same origin +descendants share a quota of 384 kibibytes. That value is computed as such: +

    From 664ba39fa508d43e26f9f71ea9134e49752ddc91 Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Wed, 11 Dec 2024 14:20:25 +0000 Subject: [PATCH 079/102] Some more nits --- fetch.bs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/fetch.bs b/fetch.bs index e557bdb2c..645513ee7 100644 --- a/fetch.bs +++ b/fetch.bs @@ -6874,7 +6874,7 @@ using permissions policy. cross-origin nested documents, each reserving 8 kibibytes.

    The top-level {{Document}}, and subsequently its nested documents, can control how much of their quota -is delegates to cross-origin/cross-agent nested documents, by using {{PermissionsPolicy}}. +is delegates to cross-origin/cross-agent nested documents, by using permissions policy. By default, "{{PermissionsPolicy/deferred-fetch-minimal}}" is enabled for any origin, while "{{PermissionsPolicy/deferred-fetch}}" is enabled for the top-level document's origin only. By relaxing the "{{PermissionsPolicy/deferred-fetch}}" policy for particular origins and nested @@ -6948,6 +6948,7 @@ share the accumulated 16 kibibytes:

    
     + https://me.example.com with Permissions-policy: deferred-fetch=(self "https://ok.example.com")
    +| (See below for quota)
     |
     + ---- + https://me.example.com
     |      | Shares quota with the top-level traversable, as they're same origin.
    @@ -6963,8 +6964,8 @@ share the accumulated 16 kibibytes:
     |      | 64 kibibytes, granted via the "{{PermissionsPolicy/deferred-fetch}}" policy.
     |      |
     |      + ---- + https://x.example.com
    -|               0. Only documents with the same originas the top-level traversable can grant
    -|               the 8 kibibytes
    +|               0. Only documents with the same origin as the top-level traversable can
    +|               grant the 8 kibibytes based on the "{{PermissionsPolicy/deferred-fetch-minimal}}" policy.
     |
     + ---- + https://ok.example.com/redirect, navigated to https://x.example.com
     |        0. The reserved 64 kibibytes for https://ok.example.com are not available for https://x.example.com.
    
    From cb85aadbbb692342f2006311e610c989cdaeaecd Mon Sep 17 00:00:00 2001
    From: Noam Rosenthal 
    Date: Wed, 11 Dec 2024 15:00:30 +0000
    Subject: [PATCH 080/102] Clarify containers in algo
    
    ---
     fetch.bs | 40 +++++++++++++++++++---------------------
     1 file changed, 19 insertions(+), 21 deletions(-)
    
    diff --git a/fetch.bs b/fetch.bs
    index 645513ee7..4fc300204 100644
    --- a/fetch.bs
    +++ b/fetch.bs
    @@ -2762,7 +2762,7 @@ not fully active. It has the following items
     
     

    Each navigable container has an associated number -nested document reserved deferred-fetch quota. Unless stated otherwise, it is 0. +reserved deferred-fetch quota. Unless stated otherwise, it is 0.


    @@ -7002,7 +7002,7 @@ descendants share a quota of 384 kibibytes. That value is computed as such:

    To get the available deferred-fetch quota given a {{Document}} -document and an origin-or-null origin: +requestClientDocument and an origin-or-null origin:

    1. Let quota be 0. @@ -7010,7 +7010,7 @@ descendants share a quota of 384 kibibytes. That value is computed as such:

    2. Let quotaForOrigin be 64 kibibytes.

    3. -

      For each otherNavigable of document's node navigable's +

      For each otherNavigable of requestClientDocument's node navigable's top-level traversable's inclusive descendant navigables:

      This algorithm iterates over the entire navigable tree. It accumulates quota from @@ -7020,14 +7020,14 @@ descendants share a quota of 384 kibibytes. That value is computed as such:

      1. Let otherDocument be otherNavigable's active document. +

      2. Let otherContainer be otherNavigable's navigable container.

      3. -

        If document's origin is same origin with +

        If requestClientDocument's origin is same origin with otherDocument's origin, then:

        1. -

          If otherDocument's node navigable is a - top-level traversable, then: +

          If otherContainer is null, then:

          Initialize the quota from the top-level traversable. @@ -7045,27 +7045,25 @@ descendants share a quota of 384 kibibytes. That value is computed as such:

        2. If otherDocument is allowed to use the policy-controlled feature "{{PermissionsPolicy/deferred-fetch-minimal}}", then decrement quota by max containers with minimal quota, multiplied by - minimal nested document deferred-fetch quota. + minimal.

      4. Otherwise, if any of the following conditions is true:

        -

        then increment quota by otherNavigable's navigable container's - nested document reserved deferred-fetch quota. +

        then increment quota by otherContainer's + reserved deferred-fetch quota.

      5. For each deferred fetch record deferredRecord of @@ -7084,9 +7082,9 @@ descendants share a quota of 384 kibibytes. That value is computed as such:

    4. Otherwise, if otherDocument's container document is a {{Document}} whose - origin is same origin with document's + origin is same origin with requestClientDocument's origin, then decrement quota by otherNavigable's - navigable container's nested document reserved deferred-fetch quota. + navigable container's reserved deferred-fetch quota.

  • If quota is less than 0, than return 0. @@ -7118,8 +7116,8 @@ shared. for "{{PermissionsPolicy/deferred-fetch}}", container and originToNavigateTo is Enabled, and the available deferred-fetch quota for container's container document is equal or greater than - optional nested document deferred-fetch quota, then set container's - reserved deferred-fetch quota to optional nested document deferred-fetch quota and + normal, then set container's + reserved deferred-fetch quota to normal and return.

  • If the inherited policy @@ -7135,11 +7133,11 @@ shared. node navigable's top-level traversable's descendant navigables, removing any navigable whose reserved deferred-fetch quota is not - minimal nested document deferred-fetch quota . + minimal .

  • If containersWithReservedMinimalQuota's size is less than max containers with minimal quota, then set container's - reserved deferred-fetch quota to minimal nested document deferred-fetch quota. + reserved deferred-fetch quota to minimal.

  • From e7d90fb1d9693c5b502b79ac8fb6a9562fcdc387 Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Wed, 11 Dec 2024 15:16:21 +0000 Subject: [PATCH 081/102] Make algo a bit more readable --- fetch.bs | 51 +++++++++++++++++++++++++++++---------------------- 1 file changed, 29 insertions(+), 22 deletions(-) diff --git a/fetch.bs b/fetch.bs index 4fc300204..b5516d3ea 100644 --- a/fetch.bs +++ b/fetch.bs @@ -2762,7 +2762,10 @@ not fully active. It has the following items

    Each navigable container has an associated number -reserved deferred-fetch quota. Unless stated otherwise, it is 0. +reserved deferred-fetch quota. Its possible values are +minimal quota, which is 8 kibibytes, +normal quota, which is 64 kibibytes, or 0. Unless +stated otherwise, it is 0.


    @@ -7029,14 +7032,13 @@ descendants share a quota of 384 kibibytes. That value is computed as such:
  • If otherContainer is null, then: -

    Initialize the quota from the top-level traversable. +

    Accumulate the top-level traversable's initial quota.

      -
    1. Assert: quota is 0. +

    2. If otherDocument is not allowed to use the + policy-controlled feature "{{PermissionsPolicy/deferred-fetch}}", then return 0. -

    3. If otherDocument is not - allowed to use the policy-controlled feature - "{{PermissionsPolicy/deferred-fetch}}", then return 0. +

    4. Assert: quota is 0.

    5. Set quota be 640 kibibytes. @@ -7045,30 +7047,35 @@ descendants share a quota of 384 kibibytes. That value is computed as such:

    6. If otherDocument is allowed to use the policy-controlled feature "{{PermissionsPolicy/deferred-fetch-minimal}}", then decrement quota by max containers with minimal quota, multiplied by - minimal. + minimal quota.

  • Otherwise, if any of the following conditions is true: +

    then increment quota by otherContainer's reserved deferred-fetch quota. +

    Accumulate quota granted by parent documents.

  • For each deferred fetch record deferredRecord of otherDocument's fetch group's deferred fetch records:

    + +

    Account for quota on deferred fetches performed by same origin clients. +

    1. Let requestLength be the total request length of deferredRecord's request. @@ -7081,17 +7088,17 @@ descendants share a quota of 384 kibibytes. That value is computed as such:

    -
  • Otherwise, if otherDocument's container document is a {{Document}} whose - origin is same origin with requestClientDocument's - origin, then decrement quota by otherNavigable's - navigable container's reserved deferred-fetch quota. - - -

  • If quota is less than 0, than return 0. +

  • +

    If otherDocument's container document is a {{Document}} whose + origin is same origin with requestClientDocument's + origin, then decrement quota by otherNavigable's + navigable container's reserved deferred-fetch quota. -

  • If quota is less than quotaForOrigin, then - return quota. +

    Account for quota granted to child documents. + +

  • If quota is less than 0, then return 0. +

  • If quota is less than quotaForOrigin, then return quota.

  • Return quotaForOrigin. @@ -7116,8 +7123,8 @@ shared. for "{{PermissionsPolicy/deferred-fetch}}", container and originToNavigateTo is Enabled, and the available deferred-fetch quota for container's container document is equal or greater than - normal, then set container's - reserved deferred-fetch quota to normal and + normal quota, then set container's + reserved deferred-fetch quota to normal quota and return.

  • If the inherited policy @@ -7133,11 +7140,11 @@ shared. node navigable's top-level traversable's descendant navigables, removing any navigable whose reserved deferred-fetch quota is not - minimal . + minimal quota .

  • If containersWithReservedMinimalQuota's size is less than max containers with minimal quota, then set container's - reserved deferred-fetch quota to minimal. + reserved deferred-fetch quota to minimal quota. From ef6c324ebdde7c7be6f0737eafc3785604aeadf9 Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Thu, 12 Dec 2024 11:50:27 +0000 Subject: [PATCH 082/102] nits --- fetch.bs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/fetch.bs b/fetch.bs index b5516d3ea..8739c57a1 100644 --- a/fetch.bs +++ b/fetch.bs @@ -6778,7 +6778,7 @@ takes no arguments:

  • Let deferredRecord be a new deferred fetch record whose request is request. -

  • Let topMostDirectSameOriginAncestor be var>request's +

  • Let topMostDirectSameOriginAncestor be request's client.

  • While topMostDirectSameOriginAncestor's node navigable's @@ -7091,8 +7091,8 @@ descendants share a quota of 384 kibibytes. That value is computed as such:

  • If otherDocument's container document is a {{Document}} whose origin is same origin with requestClientDocument's - origin, then decrement quota by otherNavigable's - navigable container's reserved deferred-fetch quota. + origin, then decrement quota by otherContainer's + reserved deferred-fetch quota.

    Account for quota granted to child documents. @@ -7139,7 +7139,7 @@ shared.

  • Let containersWithReservedMinimalQuota be container's node navigable's top-level traversable's descendant navigables, removing any navigable - whose reserved deferred-fetch quota is not + whose navigable container's reserved deferred-fetch quota is not minimal quota .

  • If containersWithReservedMinimalQuota's size is less From 4c07c9cf6bfb2fbe20195a1a100d3e54b134befa Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Sun, 15 Dec 2024 20:09:56 +0000 Subject: [PATCH 083/102] Update algorithm to only share quota for direct relatives --- fetch.bs | 253 +++++++++++++++++++++++++------------------------------ 1 file changed, 116 insertions(+), 137 deletions(-) diff --git a/fetch.bs b/fetch.bs index 8739c57a1..8a4bba91c 100644 --- a/fetch.bs +++ b/fetch.bs @@ -6761,14 +6761,12 @@ sources, specifically task sources that can result in running scripts such as th fetchLater() call before running any scripts that might depend on it.

    -

    To queue a deferred fetch given a -request request, a null or {{DOMHighResTimeStamp}} +

    To queue a deferred fetch given a request request, a +fetch group fetchGroup a null or {{DOMHighResTimeStamp}} activateAfter, an onActivatedWithoutTermination, which is an algorithm that takes no arguments:

      -
    1. Assert: request's client is a {{Document}}. -

    2. Populate request from client given request.

    3. Set request's service-workers mode to "none". @@ -6778,19 +6776,9 @@ takes no arguments:

    4. Let deferredRecord be a new deferred fetch record whose request is request. -

    5. Let topMostDirectSameOriginAncestor be request's - client. - -

    6. While topMostDirectSameOriginAncestor's node navigable's - container document is a {{Document}} whose origin is same origin - with request's client's origin, set - topMostDirectSameOriginAncestor to topMostDirectSameOriginAncestor's - node navigable's container document. -

    7. -

      Append deferredRecord to - topMostDirectSameOriginAncestor's active document's - fetch group's deferred fetch records. +

      Append deferredRecord to fetchGroup's + deferred fetch records.

      This prevents a case where eagerly creating and destroying nested documents would circumvent the keepalive quota. @@ -6869,15 +6857,15 @@ takes no arguments:

      The deferred-fetch quota is allocated to a top-level traversable (a "tab"), -amounting to 640 kibibytes. The top-level {{Document}} and its same-origin nested documents can -use this quota to queue deferred fetches, or delegate some of it to cross-origin nested documents, -using permissions policy. +amounting to 640 kibibytes. The top-level {{Document}} and its same-origin directly nested documents +can use this quota to queue deferred fetches, or delegate some of it to cross-origin nested +documents, using permissions policy.

      By default, 128 kibibytes out of these 640 kibibytes are allocated to delegating the quota to cross-origin nested documents, each reserving 8 kibibytes. -

      The top-level {{Document}}, and subsequently its nested documents, can control how much of their quota -is delegates to cross-origin/cross-agent nested documents, by using permissions policy. +

      The top-level {{Document}}, and subsequently its nested documents, can control how much of their +quota is delegates to cross-origin/cross-agent nested documents, by using permissions policy. By default, "{{PermissionsPolicy/deferred-fetch-minimal}}" is enabled for any origin, while "{{PermissionsPolicy/deferred-fetch}}" is enabled for the top-level document's origin only. By relaxing the "{{PermissionsPolicy/deferred-fetch}}" policy for particular origins and nested @@ -6936,9 +6924,8 @@ calls would succeed and the last one would throw. to https://frame.example.com, for example by serving the following header:

      Permissions-Policy: deferred-fetch=(self "https://frame.example.com")
      -

      Each nested document reserves its own quota, and all the same-origin documents in the tree share -quota with each other. So the following would work, because each frame reserve 8 kibibytes and they -share the accumulated 16 kibibytes: +

      Each nested document reserves its own quota. So the following would work, because each frame +reserve 8 kibibytes:

      
         // In cross-origin nested document at https://frame.example.com/frame-1
         fetchLater("https://a.example.com", {body: a_6kb_body});
      @@ -6957,11 +6944,15 @@ share the accumulated 16 kibibytes:
       |      | Shares quota with the top-level traversable, as they're same origin.
       |      |
       |      + ---- + https://x.example.com
      -|               Shares 16 kibibytes together with one other cross-origin nested document of the same origin.
      +|               8 kibibytes.
       |
       |
       + ---- + https://x.example.com
      -|        Shares 16 kibibytes together with one other cross-origin nested document of the same origin.
      +|        8 kibibytes.
      +|        |
      +|        + https://me.example.com
      +|          0. Even though it's same origin with the top-level traversable, it does not
      +|          automatically share its quota as they are separated by a cross-origin intermediary.
       |
       + ---- + https://ok.example.com/good
       |      | 64 kibibytes, granted via the "{{PermissionsPolicy/deferred-fetch}}" policy.
      @@ -6982,10 +6973,12 @@ descendants share a quota of 384 kibibytes. That value is computed as such:
       
      • 640 kibibytes are initially granted to the top-level traversable.

      • 128 kibibytes are reserved for the "{{PermissionsPolicy/deferred-fetch-minimal}}" policy. -

      • 64 kibibytes are reserved for the container navigating to https://ok.example/good. -

      • 64 kibibytes are reserved for the container navigating to https://ok.example/redirect, and lost when it navigates away. -

      • https://ok.example.com/back did not reserve 64 kibibytes, because it navigated back to top-level traversable's origin. -
      • 640 - 128 - 64 - 64 = 384 kibibytes. +

      • 64 kibibytes are reserved for the container navigating to + https://ok.example/good. +

      • 64 kibibytes are reserved for the container navigating to + https://ok.example/redirect, and lost when it navigates away. +

      • https://ok.example.com/back did not reserve 64 kibibytes, because it navigated + back to top-level traversable's origin.
      • 640 - 128 - 64 - 64 = 384 kibibytes.

    @@ -6998,14 +6991,11 @@ descendants share a quota of 384 kibibytes. That value is computed as such: "deferred-fetch-minimal". Its default allowlist is "*". -

    The optional nested document deferred-fetch quota is 64 kibibytes. -

    The minimal nested document deferred-fetch quota is 8 kibibytes.

    The max containers with minimal quota is 16. -

    To get the available deferred-fetch quota given a {{Document}} -requestClientDocument and an origin-or-null origin: +controlDocument and an origin-or-null origin:

    1. Let quota be 0. @@ -7013,90 +7003,63 @@ descendants share a quota of 384 kibibytes. That value is computed as such:

    2. Let quotaForOrigin be 64 kibibytes.

    3. -

      For each otherNavigable of requestClientDocument's node navigable's - top-level traversable's inclusive descendant navigables: - -

      This algorithm iterates over the entire navigable tree. It accumulates quota from - the top-level traversable, and from nested documents who inherit quota. Subsequently, - it subtracts the quota delegated to cross-origin nested documents, as well as quota - spent on pending deferred fetch requests. +

      If controlDocument's node navigable is a + top-level traversable, then:

        -
      1. Let otherDocument be otherNavigable's active document. -

      2. Let otherContainer be otherNavigable's navigable container. +

      3. If controlDocument is not allowed to use the + policy-controlled feature "{{PermissionsPolicy/deferred-fetch}}", then return 0.

      4. -

        If requestClientDocument's origin is same origin with - otherDocument's origin, then: -

          -
        1. -

          If otherContainer is null, then: - -

          Accumulate the top-level traversable's initial quota. - -

            -
          1. If otherDocument is not allowed to use the - policy-controlled feature "{{PermissionsPolicy/deferred-fetch}}", then return 0. - -

          2. Assert: quota is 0. - -

          3. -

            Set quota be 640 kibibytes. -

            640kb should be enough for everyone. - -

          4. If otherDocument is allowed to use the - policy-controlled feature "{{PermissionsPolicy/deferred-fetch-minimal}}", then - decrement quota by max containers with minimal quota, multiplied by - minimal quota. -

          +

          Set quota be 640 kibibytes. +

          640kb should be enough for everyone. -

        2. -

          Otherwise, if any of the following conditions is true: - -

          - -

          then increment quota by otherContainer's - reserved deferred-fetch quota. -

          Accumulate quota granted by parent documents. - -

        3. -

          For each deferred fetch record deferredRecord of - otherDocument's fetch group's - deferred fetch records:

          +
        4. If controlDocument is allowed to use the + policy-controlled feature "{{PermissionsPolicy/deferred-fetch-minimal}}", then + decrement quota by max containers with minimal quota, multiplied by + minimal quota. +

        -

        Account for quota on deferred fetches performed by same origin clients. +

        Otherwise: -

          -
        1. Let requestLength be the total request length of - deferredRecord's request. +

            +
          1. Let container be controlDocument's node navigable's + navigable container. + +

          2. If container's reserved deferred-fetch quota is + normal quota, and controlDocument is + allowed to use the policy-controlled feature + "{{PermissionsPolicy/deferred-fetch}}", then set quota to + normal quota. + +

          3. Otherwise, if container's reserved deferred-fetch quota is + minimal quota, and controlDocument is + allowed to use the policy-controlled feature + "{{PermissionsPolicy/deferred-fetch-minimal}}", then set quota to + minimal quota. +

          -
        2. Decrement quota by requestLength. +

        3. +

          For each deferred fetch record deferredRecord of + controlDocument's fetch group's + deferred fetch records:

          -
        4. If deferredRecord's request's - URL's origin is same origin with origin, - then decrement quotaForOrigin by requestLength. -

        -
      +
        +
      1. Let requestLength be the total request length of + deferredRecord's request. -

      2. -

        If otherDocument's container document is a {{Document}} whose - origin is same origin with requestClientDocument's - origin, then decrement quota by otherContainer's - reserved deferred-fetch quota. +

      3. Decrement quota by requestLength. -

        Account for quota granted to child documents. +

      4. If deferredRecord's request's + URL's origin is same origin with origin, + then decrement quotaForOrigin by requestLength.

      +
    4. For each navigable in controlDocument's + node navigable's descendant navigables whose container document's + deferred-fetch control document is controlDocument, decrement quota by + navigable's navigable container's reserved deferred-fetch quota. +

    5. If quota is less than 0, then return 0.

    6. If quota is less than quotaForOrigin, then return quota.

    7. Return quotaForOrigin. @@ -7119,32 +7082,36 @@ shared.

      1. Set container's reserved deferred-fetch quota to 0. +

      2. Let controlDocument be container's node document's + deferred-fetch control document. +

      3. If the inherited policy for "{{PermissionsPolicy/deferred-fetch}}", container and originToNavigateTo is Enabled, and the available deferred-fetch quota for - container's container document is equal or greater than + controlDocument is equal or greater than normal quota, then set container's reserved deferred-fetch quota to normal quota and return. -

      4. If the inherited policy - for "{{PermissionsPolicy/deferred-fetch-minimal}}", container and - originToNavigateTo is Disabled, then return. - -

      5. If container's node document's origin is not - same origin with container's node navigable's - top-level traversable's active document's origin, - then return. - -

      6. Let containersWithReservedMinimalQuota be container's - node navigable's top-level traversable's - descendant navigables, removing any navigable - whose navigable container's reserved deferred-fetch quota is not - minimal quota . - -

      7. If containersWithReservedMinimalQuota's size is less - than max containers with minimal quota, then set container's - reserved deferred-fetch quota to minimal quota. +

      8. +

        If all of the following conditions are true: + +

        + +

        then set container's reserved deferred-fetch quota to + minimal quota.

    @@ -7161,6 +7128,17 @@ document creation, as the origin of the {{Document}} is only redirects are handled. +
    +

    To get the deferred-fetch control document of a {{Document}} document: + +

      +
    1. If document' node navigable's container document is null or a + {{Document}} whose origin is not same origin with document, + return document; Otherwise return the deferred-fetch control document given + document' node navigable's container document. +

    +
    +

    Fetch API

    @@ -9076,22 +9054,24 @@ method steps are: then throw a {{TypeError}}.
  • -

    If request's - body is not null, and request's body - length is null, then throw a {{TypeError}}. +

    If request's body is not null, and request's + body length is null, then throw a {{TypeError}}.

    Requests whose body is a {{ReadableStream}} cannot be deferred. -

  • If the available deferred-fetch quota given request's - client and request's URL's origin - is less than request's total request length, then throw a - "{{QuotaExceededError}}" {{DOMException}}. +

  • Let controlDocument be request's client's + deferred-fetch control document. + +

  • If the available deferred-fetch quota given controlDocument and + request's URL's origin is less than + request's total request length, then throw a "{{QuotaExceededError}}" + {{DOMException}}.

  • Let activated be false. -

  • Let deferredRecord be the result of calling - queue a deferred fetch given request, activateAfter, and the - following step: set activated to true. +

  • Let deferredRecord be the result of calling queue a deferred fetch given + request, controlDocument's fetch group, activateAfter, and + the following step: set activated to true.

  • Add the following abort steps to requestObject's @@ -9100,9 +9080,8 @@ method steps are:

    1. Set deferredRecord's done to true. -

    2. Remove deferredRecord from - request's client's fetch group's - deferred fetch records. +

    3. Remove deferredRecord from controlDocument's + fetch group's deferred fetch records.

  • Return a new {{FetchLaterResult}} whose From d97b10526ac39872838aa5ab2c4487b458072ee4 Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Mon, 16 Dec 2024 12:53:26 +0000 Subject: [PATCH 084/102] Editorial pass --- fetch.bs | 47 +++++++++++++++++++++++++---------------------- 1 file changed, 25 insertions(+), 22 deletions(-) diff --git a/fetch.bs b/fetch.bs index 8a4bba91c..0aed8aa45 100644 --- a/fetch.bs +++ b/fetch.bs @@ -6762,8 +6762,8 @@ sources, specifically task sources that can result in running scripts such as th

    To queue a deferred fetch given a request request, a -fetch group fetchGroup a null or {{DOMHighResTimeStamp}} -activateAfter, an onActivatedWithoutTermination, which is an algorithm that +fetch group fetchGroup, a null or {{DOMHighResTimeStamp}} +activateAfter, and onActivatedWithoutTermination, which is takes no arguments:

      @@ -6789,8 +6789,10 @@ takes no arguments:
      1. The user agent should wait until any of the following conditions is met: +

        • At least activateAfter milliseconds have passed. +

        • The user agent has a reason to believe that it is about to lose the opportunity to execute scripts, e.g., when the browser is moved to the background, or when request's client is a {{Document}} that had a @@ -6824,19 +6826,18 @@ takes no arguments: header list, increment totalRequestLength by name's length + value's length. -

        • Increment totalRequestLength by request's - body's length. +

        • Increment totalRequestLength by request's body's + length.

        • Return totalRequestLength. +

    -

    To process deferred fetches given a fetch group -fetchGroup, for each deferred fetch record -deferredRecord of fetchGroup's -deferred fetch records process a deferred fetch -deferredRecord. - +

    To process deferred fetches given a fetch group fetchGroup, +for each deferred fetch record deferredRecord of +fetchGroup's deferred fetch records +process a deferred fetch deferredRecord.

    @@ -6847,8 +6848,8 @@ takes no arguments:
  • Set deferredRecord's done to true. -

  • Fetch deferredRecord's - request. +

  • Fetch deferredRecord's request. +

  • Return true. @@ -6865,16 +6866,16 @@ documents, using permissions policy. cross-origin nested documents, each reserving 8 kibibytes.

    The top-level {{Document}}, and subsequently its nested documents, can control how much of their -quota is delegates to cross-origin/cross-agent nested documents, by using permissions policy. -By default, "{{PermissionsPolicy/deferred-fetch-minimal}}" is enabled for any origin, while +quota is delegates to cross-origin child documents, using permissions policy. By default, +"{{PermissionsPolicy/deferred-fetch-minimal}}" is enabled for any origin, while "{{PermissionsPolicy/deferred-fetch}}" is enabled for the top-level document's origin only. By relaxing the "{{PermissionsPolicy/deferred-fetch}}" policy for particular origins and nested documents, the top-level document can allocate 64 kibibytes to those nested documents. Similarly, by restricting the "{{PermissionsPolicy/deferred-fetch-minimal}}" policy for a particular origin or -nested document, the document can prevent the iframe from reserving the 8 kibibytes it would receive -by default. By disabling "{{PermissionsPolicy/deferred-fetch-minimal}}" for the top-level document -itself, the entire 128 kibibytes delegated quota is collected back into the main pool of 640 -kibibytes. +nested document, the document can prevent the document from reserving the 8 kibibytes it would +receive by default. By disabling "{{PermissionsPolicy/deferred-fetch-minimal}}" for the top-leve +document itself, the entire 128 kibibytes delegated quota is collected back into the main pool of +640 kibibytes.

    Out of the allocated quota for a {{Document}}, only 64 kibibytes can be used concurrently for the same reporting origin (the request's URL's origin). @@ -6991,7 +6992,7 @@ descendants share a quota of 384 kibibytes. That value is computed as such: "deferred-fetch-minimal". Its default allowlist is "*". -

    The max containers with minimal quota is 16. +

    The quota reserved for deferred-fetch-minimal is 128 kibibytes.

    To get the available deferred-fetch quota given a {{Document}} @@ -7016,8 +7017,7 @@ descendants share a quota of 384 kibibytes. That value is computed as such:

  • If controlDocument is allowed to use the policy-controlled feature "{{PermissionsPolicy/deferred-fetch-minimal}}", then - decrement quota by max containers with minimal quota, multiplied by - minimal quota. + decrement quota by quota reserved for deferred-fetch-minimal.

    Otherwise: @@ -7061,7 +7061,9 @@ descendants share a quota of 384 kibibytes. That value is computed as such: navigable's navigable container's reserved deferred-fetch quota.

  • If quota is less than 0, then return 0. +

  • If quota is less than quotaForOrigin, then return quota. +

  • Return quotaForOrigin. @@ -7107,7 +7109,8 @@ shared. descendant navigables, removing any navigable whose navigable container's reserved deferred-fetch quota is not minimal quota, is less than - max containers with minimal quota + quota reserved for deferred-fetch-minimal / + minimal quota.

    then set container's reserved deferred-fetch quota to From c22873748249ca59d1bc07fc95a60279fc2c6e19 Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Mon, 16 Dec 2024 13:15:32 +0000 Subject: [PATCH 085/102] Change initial quota computation to a switch statement --- fetch.bs | 111 ++++++++++++++++++++++++++++++------------------------- 1 file changed, 60 insertions(+), 51 deletions(-) diff --git a/fetch.bs b/fetch.bs index 0aed8aa45..418577b0d 100644 --- a/fetch.bs +++ b/fetch.bs @@ -6763,7 +6763,7 @@ sources, specifically task sources that can result in running scripts such as th

    To queue a deferred fetch given a request request, a fetch group fetchGroup, a null or {{DOMHighResTimeStamp}} -activateAfter, and onActivatedWithoutTermination, which is +activateAfter, and onActivatedWithoutTermination, which is an algorithm that takes no arguments:

      @@ -6802,7 +6802,7 @@ takes no arguments:
    1. If the result of calling process a deferred fetch given deferredRecord returns true, then queue a global task on the deferred fetch task source with request's client's - global object and + global object to run onActivatedWithoutTermination.

  • @@ -6834,10 +6834,14 @@ takes no arguments:
    -

    To process deferred fetches given a fetch group fetchGroup, -for each deferred fetch record deferredRecord of -fetchGroup's deferred fetch records -process a deferred fetch deferredRecord. +

    To process deferred fetches given a fetch group fetchGroup: + +

      +
    1. For each deferred fetch record + deferredRecord of fetchGroup's + deferred fetch records, process a deferred fetch + deferredRecord. +

    @@ -6867,15 +6871,15 @@ cross-origin nested documents, each reserving 8 kibibytes.

    The top-level {{Document}}, and subsequently its nested documents, can control how much of their quota is delegates to cross-origin child documents, using permissions policy. By default, -"{{PermissionsPolicy/deferred-fetch-minimal}}" is enabled for any origin, while +the "{{PermissionsPolicy/deferred-fetch-minimal}}" policy is enabled for any origin, while "{{PermissionsPolicy/deferred-fetch}}" is enabled for the top-level document's origin only. By relaxing the "{{PermissionsPolicy/deferred-fetch}}" policy for particular origins and nested documents, the top-level document can allocate 64 kibibytes to those nested documents. Similarly, by restricting the "{{PermissionsPolicy/deferred-fetch-minimal}}" policy for a particular origin or nested document, the document can prevent the document from reserving the 8 kibibytes it would -receive by default. By disabling "{{PermissionsPolicy/deferred-fetch-minimal}}" for the top-leve -document itself, the entire 128 kibibytes delegated quota is collected back into the main pool of -640 kibibytes. +receive by default. By disabling the "{{PermissionsPolicy/deferred-fetch-minimal}}" policy for the +top-level document itself, the entire 128 kibibytes delegated quota is collected back into the main +pool of 640 kibibytes.

    Out of the allocated quota for a {{Document}}, only 64 kibibytes can be used concurrently for the same reporting origin (the request's URL's origin). @@ -6999,45 +7003,54 @@ descendants share a quota of 384 kibibytes. That value is computed as such: controlDocument and an origin-or-null origin:

      -
    1. Let quota be 0. - -

    2. Let quotaForOrigin be 64 kibibytes. -

    3. -

      If controlDocument's node navigable is a - top-level traversable, then: +

      Let quota be the result of the first matching statement: -

        -
      1. If controlDocument is not allowed to use the - policy-controlled feature "{{PermissionsPolicy/deferred-fetch}}", then return 0. +

        +
        controlDocument's node navigable is a + top-level traversable whose not allowed to use the + policy-controlled feature "{{PermissionsPolicy/deferred-fetch}}" +
        0 + +
        controlDocument's node navigable is a + top-level traversable whose allowed to use the + policy-controlled feature "{{PermissionsPolicy/deferred-fetch-minimal}}" +
        +

        512 kibibytes +

        The default of 640 kibibytes, decremented By + quota reserved for deferred-fetch-minimal) -

      2. -

        Set quota be 640 kibibytes. +

        controlDocument's node navigable is a + top-level traversable whose not allowed to use the + policy-controlled feature "{{PermissionsPolicy/deferred-fetch-minimal}}" +
        +

        640 kibibytes

        640kb should be enough for everyone. -

      3. If controlDocument is allowed to use the - policy-controlled feature "{{PermissionsPolicy/deferred-fetch-minimal}}", then - decrement quota by quota reserved for deferred-fetch-minimal. -

      +
      controlDocument's node navigable's navigable container's + reserved deferred-fetch quota is normal quota, + and controlDocument is allowed to use the policy-controlled feature + "{{PermissionsPolicy/deferred-fetch}}" +
      normal quota + +
      controlDocument's node navigable's navigable container's + reserved deferred-fetch quota is minimal quota, + and controlDocument is allowed to use the policy-controlled feature + "{{PermissionsPolicy/deferred-fetch-minimal}}" +
      minimal quota + -

      Otherwise: +

    4. +

      For each navigable in controlDocument's + node navigable's descendant navigables whose container document's + deferred-fetch control document is controlDocument, decrement quota + by navigable's navigable container's reserved deferred-fetch quota. -

        -
      1. Let container be controlDocument's node navigable's - navigable container. - -

      2. If container's reserved deferred-fetch quota is - normal quota, and controlDocument is - allowed to use the policy-controlled feature - "{{PermissionsPolicy/deferred-fetch}}", then set quota to - normal quota. - -

      3. Otherwise, if container's reserved deferred-fetch quota is - minimal quota, and controlDocument is - allowed to use the policy-controlled feature - "{{PermissionsPolicy/deferred-fetch-minimal}}", then set quota to - minimal quota. -

      +

      Delegate some of the quota to nested documents that reserved it. + +

    5. If quota is equal or less than 0, then return 0. + +

    6. Let quotaForRequestOrigin be 64 kibibytes.

    7. For each deferred fetch record deferredRecord of @@ -7052,19 +7065,15 @@ descendants share a quota of 384 kibibytes. That value is computed as such:

    8. If deferredRecord's request's URL's origin is same origin with origin, - then decrement quotaForOrigin by requestLength. + then decrement quotaForRequestOrigin by requestLength.

    -
  • For each navigable in controlDocument's - node navigable's descendant navigables whose container document's - deferred-fetch control document is controlDocument, decrement quota by - navigable's navigable container's reserved deferred-fetch quota. - -

  • If quota is less than 0, then return 0. +

  • If quota is equal or less than 0, then return 0. -

  • If quota is less than quotaForOrigin, then return quota. +

  • If quota is less than quotaForRequestOrigin, then return + quota. -

  • Return quotaForOrigin. +

  • Return quotaForRequestOrigin.

  • From 54d9aeaff57e04297853466b2b4e67b11e55f826 Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Mon, 16 Dec 2024 13:20:09 +0000 Subject: [PATCH 086/102] More editorial --- fetch.bs | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/fetch.bs b/fetch.bs index 418577b0d..4dbc7599b 100644 --- a/fetch.bs +++ b/fetch.bs @@ -7002,30 +7002,31 @@ descendants share a quota of 384 kibibytes. That value is computed as such:

    To get the available deferred-fetch quota given a {{Document}} controlDocument and an origin-or-null origin: -

      +
    1. Let navigable be controlDocument's node navigable.

    2. Let quota be the result of the first matching statement:

      controlDocument's node navigable is a - top-level traversable whose not allowed to use the - policy-controlled feature "{{PermissionsPolicy/deferred-fetch}}" + top-level traversable and controlDocument is not allowed to use + the policy-controlled feature "{{PermissionsPolicy/deferred-fetch}}"
      0
      controlDocument's node navigable is a - top-level traversable whose allowed to use the + top-level traversable and controlDocument is not allowed to use + the policy-controlled feature "{{PermissionsPolicy/deferred-fetch-minimal}}" +
      +

      640 kibibytes +

      640kb should be enough for everyone. + +

      controlDocument's node navigable is a top-level traversable + and allowed to use the policy-controlled feature "{{PermissionsPolicy/deferred-fetch-minimal}}"

      512 kibibytes

      The default of 640 kibibytes, decremented By quota reserved for deferred-fetch-minimal) -

      controlDocument's node navigable is a - top-level traversable whose not allowed to use the - policy-controlled feature "{{PermissionsPolicy/deferred-fetch-minimal}}" -
      -

      640 kibibytes -

      640kb should be enough for everyone.

      controlDocument's node navigable's navigable container's reserved deferred-fetch quota is normal quota, From 1870b694998da88ac0c5c598fc1a6b275317e98c Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Mon, 16 Dec 2024 13:55:27 +0000 Subject: [PATCH 087/102] Bring back
        --- fetch.bs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/fetch.bs b/fetch.bs index 4dbc7599b..da24644bd 100644 --- a/fetch.bs +++ b/fetch.bs @@ -6977,13 +6977,19 @@ reserve 8 kibibytes: descendants share a quota of 384 kibibytes. That value is computed as such:
        • 640 kibibytes are initially granted to the top-level traversable. +

        • 128 kibibytes are reserved for the "{{PermissionsPolicy/deferred-fetch-minimal}}" policy. +

        • 64 kibibytes are reserved for the container navigating to https://ok.example/good. +

        • 64 kibibytes are reserved for the container navigating to https://ok.example/redirect, and lost when it navigates away. +

        • https://ok.example.com/back did not reserve 64 kibibytes, because it navigated - back to top-level traversable's origin.
        • 640 - 128 - 64 - 64 = 384 kibibytes. + back to top-level traversable's origin. + +

        • 640 - 128 - 64 - 64 = 384 kibibytes.

        @@ -7002,6 +7008,7 @@ descendants share a quota of 384 kibibytes. That value is computed as such:

        To get the available deferred-fetch quota given a {{Document}} controlDocument and an origin-or-null origin: +

        1. Let navigable be controlDocument's node navigable.

        2. Let quota be the result of the first matching statement: From 88ae6696c6eeb87d25646f3dcb74f395d75513cf Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Mon, 6 Jan 2025 15:03:44 +0000 Subject: [PATCH 088/102] Nits --- fetch.bs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/fetch.bs b/fetch.bs index da24644bd..b8cae9e7a 100644 --- a/fetch.bs +++ b/fetch.bs @@ -6780,9 +6780,6 @@ takes no arguments:

          Append deferredRecord to fetchGroup's deferred fetch records. -

          This prevents a case where eagerly creating and destroying nested documents would - circumvent the keepalive quota. -

        3. If activateAfter is non-null, then run the following steps in parallel:

          @@ -6994,11 +6991,11 @@ descendants share a quota of 384 kibibytes. That value is computed as such: -

          This specification defined a policy-controlled feature identified by the string +

          This specification defines a policy-controlled feature identified by the string "deferred-fetch". Its default allowlist is "self". -

          This specification defined a policy-controlled feature identified by the string +

          This specification defines a policy-controlled feature identified by the string "deferred-fetch-minimal". Its default allowlist is "*". From 78564542388136fe3e452eca064884cdb09c8a8b Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Mon, 6 Jan 2025 15:16:12 +0000 Subject: [PATCH 089/102] Improve switch statement --- fetch.bs | 39 +++++++++++++++++++++------------------ 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/fetch.bs b/fetch.bs index b8cae9e7a..b57698387 100644 --- a/fetch.bs +++ b/fetch.bs @@ -7007,41 +7007,44 @@ descendants share a quota of 384 kibibytes. That value is computed as such:

          1. Let navigable be controlDocument's node navigable. + +

          2. Let isTopLevel be true if controlDocument's node navigable is a + top-level traversable; otherwise false. + +

          3. Let deferredFetchAllowed be true if controlDocument is + allowed to use the policy-controlled feature "{{PermissionsPolicy/deferred-fetch}}"; + otherwise false. + +

          4. Let deferredFetchMinimalAllowed be true if controlDocument is + allowed to use the policy-controlled feature + "{{PermissionsPolicy/deferred-fetch-minimal}}"; otherwise false. +

          5. Let quota be the result of the first matching statement:

            -
            controlDocument's node navigable is a - top-level traversable and controlDocument is not allowed to use - the policy-controlled feature "{{PermissionsPolicy/deferred-fetch}}" +
            isTopLevel is true and deferredFetchAllowed is false
            0 -
            controlDocument's node navigable is a - top-level traversable and controlDocument is not allowed to use - the policy-controlled feature "{{PermissionsPolicy/deferred-fetch-minimal}}" +
            isTopLevel is true and deferredFetchMinimalAllowed is false

            640 kibibytes

            640kb should be enough for everyone. -

            controlDocument's node navigable is a top-level traversable - and allowed to use the - policy-controlled feature "{{PermissionsPolicy/deferred-fetch-minimal}}" +
            isTopLevel is true

            512 kibibytes

            The default of 640 kibibytes, decremented By quota reserved for deferred-fetch-minimal) - -

            controlDocument's node navigable's navigable container's - reserved deferred-fetch quota is normal quota, - and controlDocument is allowed to use the policy-controlled feature - "{{PermissionsPolicy/deferred-fetch}}" +
            deferredFetchAllowed is true, and navigable's + navigable container's reserved deferred-fetch quota is + normal quota
            normal quota -
            controlDocument's node navigable's navigable container's - reserved deferred-fetch quota is minimal quota, - and controlDocument is allowed to use the policy-controlled feature - "{{PermissionsPolicy/deferred-fetch-minimal}}" +
            deferredFetchMinimalAllowed is true, and navigable's + navigable container's reserved deferred-fetch quota is + minimal quota
            minimal quota
            From 6432b5df055593eb80ea37c27f7f3198eed0e3a1 Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Tue, 7 Jan 2025 09:04:17 +0000 Subject: [PATCH 090/102] Update note --- fetch.bs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/fetch.bs b/fetch.bs index b57698387..4355a4eb0 100644 --- a/fetch.bs +++ b/fetch.bs @@ -7090,10 +7090,9 @@ descendants share a quota of 384 kibibytes. That value is computed as such: container given an origin originToNavigateTo:

            This is called when container and the document that initiated the -navigation (the "source document") are same origin. It potentially -reserves either 64kb or 8kb of quota for the frame, if it is not same origin -with its parent and the permissions policy allow. It is not observable to the cotnainer document -whether the reserved quota was used in practice. +navigation (the "source document") are not same origin. It potentially +reserves either 64kb or 8kb of quota for the frame, if the permissions policy allow. It is not +observable to the cotnainer document whether the reserved quota was used in practice. This algorithm assumes that the container's document might delegate quota to the navigated frame, and the reserved quota would only apply in that case, and would be ignored if it ends up being shared. From c345eb8544ff5fb01739ecb4e20f6d06c5fd36c7 Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Tue, 7 Jan 2025 09:11:19 +0000 Subject: [PATCH 091/102] Fix issue with reserve algo --- fetch.bs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/fetch.bs b/fetch.bs index 4355a4eb0..36adad1d2 100644 --- a/fetch.bs +++ b/fetch.bs @@ -7089,13 +7089,14 @@ descendants share a quota of 384 kibibytes. That value is computed as such:

            To reserve deferred-fetch quota for a navigable container container given an origin originToNavigateTo: -

            This is called when container and the document that initiated the -navigation (the "source document") are not same origin. It potentially -reserves either 64kb or 8kb of quota for the frame, if the permissions policy allow. It is not -observable to the cotnainer document whether the reserved quota was used in practice. -This algorithm assumes that the container's document might delegate quota to the navigated frame, -and the reserved quota would only apply in that case, and would be ignored if it ends up being -shared. +

            This is called on navigation, when the source document of the navigation is the +navigable's parent document. It potentially reserves either 64kb or 8kb of quota for +the frame, if the permissions policy allow. It is not observable to the cotnainer document whether +the reserved quota was used in practice. This algorithm assumes that the container's document might +delegate quota to the navigated frame, and the reserved quota would only apply in that case, and +would be ignored if it ends up being shared. If quota was reserved and the document ends up being +same origin with its parent, the quota would be +freed.

            1. Set container's reserved deferred-fetch quota to 0. From 1ca86151aca59f18fdeefae702a02a4f6f3f8a51 Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Wed, 8 Jan 2025 10:34:20 +0000 Subject: [PATCH 092/102] Clarify notes --- fetch.bs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/fetch.bs b/fetch.bs index 36adad1d2..cdb4b9a34 100644 --- a/fetch.bs +++ b/fetch.bs @@ -7091,11 +7091,11 @@ descendants share a quota of 384 kibibytes. That value is computed as such:

              This is called on navigation, when the source document of the navigation is the navigable's parent document. It potentially reserves either 64kb or 8kb of quota for -the frame, if the permissions policy allow. It is not observable to the cotnainer document whether -the reserved quota was used in practice. This algorithm assumes that the container's document might -delegate quota to the navigated frame, and the reserved quota would only apply in that case, and -would be ignored if it ends up being shared. If quota was reserved and the document ends up being -same origin with its parent, the quota would be +the container and its navigable, if allowed permissions policy. It is not observable to the +cotnainer document whether the reserved quota was used in practice. This algorithm assumes that the +container's document might delegate quota to the navigated frame, and the reserved quota would only +apply in that case, and would be ignored if it ends up being shared. If quota was reserved and the +document ends up being same origin with its parent, the quota would be freed.

                From b610d5832c2f9ca3f1044bcd45035b121f9cfbad Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Mon, 13 Jan 2025 09:52:32 +0000 Subject: [PATCH 093/102] nits --- fetch.bs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/fetch.bs b/fetch.bs index cdb4b9a34..005735d5f 100644 --- a/fetch.bs +++ b/fetch.bs @@ -7024,6 +7024,8 @@ descendants share a quota of 384 kibibytes. That value is computed as such:
                isTopLevel is true and deferredFetchAllowed is false +
                isTopLevel is false, and deferredFetchAllowed is false, and + deferredFetchMinimalAllowed is false
                0
                isTopLevel is true and deferredFetchMinimalAllowed is false @@ -7091,11 +7093,11 @@ descendants share a quota of 384 kibibytes. That value is computed as such:

                This is called on navigation, when the source document of the navigation is the navigable's parent document. It potentially reserves either 64kb or 8kb of quota for -the container and its navigable, if allowed permissions policy. It is not observable to the +the container and its navigable, if allowed by permissions policy. It is not observable to the cotnainer document whether the reserved quota was used in practice. This algorithm assumes that the -container's document might delegate quota to the navigated frame, and the reserved quota would only -apply in that case, and would be ignored if it ends up being shared. If quota was reserved and the -document ends up being same origin with its parent, the quota would be +container's document might delegate quota to the navigated container, and the reserved quota would +only apply in that case, and would be ignored if it ends up being shared. If quota was reserved and +the document ends up being same origin with its parent, the quota would be freed.

                  From efe0730aa74754f5afdd70006a0151d4feb3c562 Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Thu, 16 Jan 2025 09:23:40 +0000 Subject: [PATCH 094/102] Use 'otherwise' --- fetch.bs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/fetch.bs b/fetch.bs index 005735d5f..554e0c577 100644 --- a/fetch.bs +++ b/fetch.bs @@ -7024,8 +7024,6 @@ descendants share a quota of 384 kibibytes. That value is computed as such:
                  isTopLevel is true and deferredFetchAllowed is false -
                  isTopLevel is false, and deferredFetchAllowed is false, and - deferredFetchMinimalAllowed is false
                  0
                  isTopLevel is true and deferredFetchMinimalAllowed is false @@ -7048,6 +7046,9 @@ descendants share a quota of 384 kibibytes. That value is computed as such: navigable container's reserved deferred-fetch quota is minimal quota
                  minimal quota + +
                  Otherwise +
                  0
                1. From 303dc5a9cf95830b224ae62c7447f4e56dbc6940 Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Tue, 21 Jan 2025 13:27:50 +0000 Subject: [PATCH 095/102] Keep record on both document and control document --- fetch.bs | 87 +++++++++++++++++++++++++++++++++----------------------- 1 file changed, 52 insertions(+), 35 deletions(-) diff --git a/fetch.bs b/fetch.bs index 554e0c577..13e86277e 100644 --- a/fetch.bs +++ b/fetch.bs @@ -2757,8 +2757,12 @@ not fully active. It has the following itemsrequest
                  A request. -
                  done (default false) -
                  A boolean. +
                  notify invoked +
                  An algorithm accepting no arguments. + +
                  invoke state (default "pending") +
                  "pending", "fetching", "done", or + "aborted".

                Each navigable container has an associated number @@ -6761,10 +6765,9 @@ sources, specifically task sources that can result in running scripts such as th fetchLater() call before running any scripts that might depend on it.

                -

                To queue a deferred fetch given a request request, a -fetch group fetchGroup, a null or {{DOMHighResTimeStamp}} -activateAfter, and onActivatedWithoutTermination, which is an algorithm that -takes no arguments: +

                To queue a deferred fetch given a request request, a null or +{{DOMHighResTimeStamp}} activateAfter, and onActivatedWithoutTermination, +which is an algorithm that takes no arguments:

                1. Populate request from client given request. @@ -6774,12 +6777,24 @@ takes no arguments:

                2. Set request's keepalive to true.

                3. Let deferredRecord be a new deferred fetch record whose - request is request. + request is request, + notify invoked is + onActivatedWithoutTermination. + +

                4. Append deferredRecord to document's + fetch group's deferred fetch records. + +

                5. Let controlDocument be document's + deferred-fetch control document.

                6. -

                  Append deferredRecord to fetchGroup's +

                  If controlDocument is not document, then append + deferredRecord to controlDocument's deferred-fetch control document's deferred fetch records. +

                  This ensures that the deferred fetch is counted towards the control document's + quota if its request is still in flight after the fetch group has been terminated. +

                7. If activateAfter is non-null, then run the following steps in parallel:

                  @@ -6796,11 +6811,7 @@ takes no arguments: "hidden" visibility state for a long period of time. -
                8. If the result of calling process a deferred fetch given deferredRecord - returns true, then queue a global task on the deferred fetch task source with - request's client's - global object to run - onActivatedWithoutTermination. +

                9. Process deferredRecord.

                @@ -6844,14 +6855,21 @@ takes no arguments:

                To process a deferred fetch deferredRecord:

                  -
                1. If deferredRecord's done is true, then - return false. +

                2. If deferredRecord's invoke state is not + "pending", then return. -

                3. Set deferredRecord's done to true. +

                4. Set deferredRecord's invoke state to + "fetching". -

                5. Fetch deferredRecord's request. +

                6. Fetch deferredRecord's request, + with processRequestEndOfBody set to the following step: set + deferredRecord's invoke state to + "done". -

                7. Return true. +

                8. Queue a global task on the deferred fetch task source with + request's client's + global object to run deferredRecord's + notify invoked.

                @@ -7003,9 +7021,12 @@ descendants share a quota of 384 kibibytes. That value is computed as such:

                To get the available deferred-fetch quota given a {{Document}} -controlDocument and an origin-or-null origin: +document and an origin-or-null origin:

                  +
                1. Let controlDocument be document's + deferred-fetch control document. +

                2. Let navigable be controlDocument's node navigable.

                3. Let isTopLevel be true if controlDocument's node navigable is a @@ -7069,6 +7090,9 @@ descendants share a quota of 384 kibibytes. That value is computed as such: deferred fetch records:

                    +
                  1. If deferredRecord's invoke state is + "done" or "aborted", then continue. +

                  2. Let requestLength be the total request length of deferredRecord's request. @@ -9085,27 +9109,20 @@ method steps are:

                  3. Let controlDocument be request's client's deferred-fetch control document. -

                  4. If the available deferred-fetch quota given controlDocument and - request's URL's origin is less than - request's total request length, then throw a "{{QuotaExceededError}}" - {{DOMException}}. +

                  5. If the available deferred-fetch quota given request's + client and request's URL's + origin is less than request's total request length, then throw a + "{{QuotaExceededError}}" {{DOMException}}.

                  6. Let activated be false.

                  7. Let deferredRecord be the result of calling queue a deferred fetch given - request, controlDocument's fetch group, activateAfter, and - the following step: set activated to true. - -

                  8. -

                    Add the following abort steps to requestObject's - signal: - -

                      -
                    1. Set deferredRecord's done to true. + request, activateAfter, and the following step: set activated to + true. -

                    2. Remove deferredRecord from controlDocument's - fetch group's deferred fetch records. -

                    +
                  9. Add the following abort steps to requestObject's + signal:

                  10. Set deferredRecord's + invoke state to "aborted".

                  11. Return a new {{FetchLaterResult}} whose activated getter steps are to return activated. From 04c5a3fb381a3be580716d33e5d4173f3f6752b6 Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Tue, 21 Jan 2025 13:35:35 +0000 Subject: [PATCH 096/102] nit --- fetch.bs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fetch.bs b/fetch.bs index 13e86277e..9934aad4c 100644 --- a/fetch.bs +++ b/fetch.bs @@ -6789,7 +6789,7 @@ which is an algorithm that takes no arguments:

                  12. If controlDocument is not document, then append - deferredRecord to controlDocument's deferred-fetch control document's + deferredRecord to controlDocument's fetch group's deferred fetch records.

                    This ensures that the deferred fetch is counted towards the control document's From 5aa1df56c7b4c33096989e526262d02627860efd Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Tue, 21 Jan 2025 14:03:02 +0000 Subject: [PATCH 097/102] Try to fix build --- fetch.bs | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/fetch.bs b/fetch.bs index 9934aad4c..a3f08685b 100644 --- a/fetch.bs +++ b/fetch.bs @@ -6777,7 +6777,7 @@ which is an algorithm that takes no arguments:

                  13. Set request's keepalive to true.

                  14. Let deferredRecord be a new deferred fetch record whose - request is request, + request is request, and whose notify invoked is onActivatedWithoutTermination. @@ -6788,12 +6788,18 @@ which is an algorithm that takes no arguments: deferred-fetch control document.

                  15. -

                    If controlDocument is not document, then append - deferredRecord to controlDocument's fetch group's - deferred fetch records. +

                    If controlDocument is not document, then:

                    +
                      +
                    1. Append + deferredRecord to controlDocument's fetch group's + deferred fetch records. + +

                    2. Set request's client to controlDocument. +

                    This ensures that the deferred fetch is counted towards the control document's - quota if its request is still in flight after the fetch group has been terminated. + quota if its request is still in flight after the fetch group has been terminated.

                    +
                  16. If activateAfter is non-null, then run the following steps in parallel:

                    @@ -6867,9 +6873,9 @@ which is an algorithm that takes no arguments: "done".
                  17. Queue a global task on the deferred fetch task source with - request's client's - global object to run deferredRecord's - notify invoked. + deferredRecord's request's + client's global object to run + deferredRecord's notify invoked.

                @@ -9106,11 +9112,8 @@ method steps are:

                Requests whose body is a {{ReadableStream}} cannot be deferred. -

              1. Let controlDocument be request's client's - deferred-fetch control document. -

              2. If the available deferred-fetch quota given request's - client and request's URL's + client and request's URL's origin is less than request's total request length, then throw a "{{QuotaExceededError}}" {{DOMException}}. @@ -9121,7 +9124,7 @@ method steps are: true.

              3. Add the following abort steps to requestObject's - signal:

              4. Set deferredRecord's + signal: Set deferredRecord's invoke state to "aborted".

              5. Return a new {{FetchLaterResult}} whose From 27cec10176ae830258b3066edce4acb76cc7d051 Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Wed, 22 Jan 2025 11:13:18 +0000 Subject: [PATCH 098/102] Keep records in subframe --- fetch.bs | 57 ++++++++++++++++++++------------------------------------ 1 file changed, 20 insertions(+), 37 deletions(-) diff --git a/fetch.bs b/fetch.bs index a3f08685b..d044cfc56 100644 --- a/fetch.bs +++ b/fetch.bs @@ -2761,8 +2761,7 @@ not fully active. It has the following itemsAn algorithm accepting no arguments.

                invoke state (default "pending") -
                "pending", "fetching", "done", or - "aborted". +
                "pending", "sent", or "aborted".

      Each navigable container has an associated number @@ -6781,25 +6780,8 @@ which is an algorithm that takes no arguments: notify invoked is onActivatedWithoutTermination. -

    3. Append deferredRecord to document's - fetch group's deferred fetch records. - -

    4. Let controlDocument be document's - deferred-fetch control document. - -

    5. -

      If controlDocument is not document, then:

      -
        -
      1. Append - deferredRecord to controlDocument's fetch group's - deferred fetch records. - -

      2. Set request's client to controlDocument. -

      - -

      This ensures that the deferred fetch is counted towards the control document's - quota if its request is still in flight after the fetch group has been terminated.

      -
    6. +
    7. Append deferredRecord to request's + client's fetch group's deferred fetch records.

    8. If activateAfter is non-null, then run the following steps in parallel:

      @@ -6865,12 +6847,9 @@ which is an algorithm that takes no arguments: "pending", then return.
    9. Set deferredRecord's invoke state to - "fetching". + "sent". -

    10. Fetch deferredRecord's request, - with processRequestEndOfBody set to the following step: set - deferredRecord's invoke state to - "done". +

    11. Fetch deferredRecord's request.

    12. Queue a global task on the deferred fetch task source with deferredRecord's request's @@ -7091,22 +7070,26 @@ descendants share a quota of 384 kibibytes. That value is computed as such:

    13. Let quotaForRequestOrigin be 64 kibibytes.

    14. -

      For each deferred fetch record deferredRecord of - controlDocument's fetch group's - deferred fetch records:

      +

      For each navigable in controlDocument's + node navigable's descendant navigables whose active document's + deferred-fetch control document is controlDocument:

        -
      1. If deferredRecord's invoke state is - "done" or "aborted", then continue. +

      2. +

        For each deferred fetch record deferredRecord of + controlDocument's fetch group's + deferred fetch records:

        -
      3. Let requestLength be the total request length of - deferredRecord's request. +

          +
        1. Let requestLength be the total request length of + deferredRecord's request. -

        2. Decrement quota by requestLength. +

        3. Decrement quota by requestLength. -

        4. If deferredRecord's request's - URL's origin is same origin with origin, - then decrement quotaForRequestOrigin by requestLength. +

        5. If deferredRecord's request's + URL's origin is same origin with origin, + then decrement quotaForRequestOrigin by requestLength. +

    15. If quota is equal or less than 0, then return 0. From 5be80472f75acaa865e2728491e0315b8f1268d6 Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Wed, 22 Jan 2025 13:59:11 +0000 Subject: [PATCH 099/102] Simplify quota --- fetch.bs | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/fetch.bs b/fetch.bs index d044cfc56..07bebde9b 100644 --- a/fetch.bs +++ b/fetch.bs @@ -7057,24 +7057,20 @@ descendants share a quota of 384 kibibytes. That value is computed as such:

      0 -
    16. -

      For each navigable in controlDocument's - node navigable's descendant navigables whose container document's - deferred-fetch control document is controlDocument, decrement quota - by navigable's navigable container's reserved deferred-fetch quota. - -

      Delegate some of the quota to nested documents that reserved it. - -

    17. If quota is equal or less than 0, then return 0. -

    18. Let quotaForRequestOrigin be 64 kibibytes.

    19. For each navigable in controlDocument's - node navigable's descendant navigables whose active document's - deferred-fetch control document is controlDocument: + node navigable's inclusive descendant navigables whose + active document's deferred-fetch control document is + controlDocument:

        +
      1. For each container in navigable's + active document's shadow-including inclusive descendants which is a + navigable container, decrement quota by container's + reserved deferred-fetch quota. +

      2. For each deferred fetch record deferredRecord of controlDocument's fetch group's From 5225029a79e70a29bd8500ba5ffd7173f54b72e4 Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Thu, 23 Jan 2025 10:15:49 +0000 Subject: [PATCH 100/102] Revert "Simplify quota" This reverts commit 5be80472f75acaa865e2728491e0315b8f1268d6. --- fetch.bs | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/fetch.bs b/fetch.bs index 07bebde9b..d044cfc56 100644 --- a/fetch.bs +++ b/fetch.bs @@ -7057,20 +7057,24 @@ descendants share a quota of 384 kibibytes. That value is computed as such:

        0 +
      3. +

        For each navigable in controlDocument's + node navigable's descendant navigables whose container document's + deferred-fetch control document is controlDocument, decrement quota + by navigable's navigable container's reserved deferred-fetch quota. + +

        Delegate some of the quota to nested documents that reserved it. + +

      4. If quota is equal or less than 0, then return 0. +

      5. Let quotaForRequestOrigin be 64 kibibytes.

      6. For each navigable in controlDocument's - node navigable's inclusive descendant navigables whose - active document's deferred-fetch control document is - controlDocument: + node navigable's descendant navigables whose active document's + deferred-fetch control document is controlDocument:

          -
        1. For each container in navigable's - active document's shadow-including inclusive descendants which is a - navigable container, decrement quota by container's - reserved deferred-fetch quota. -

        2. For each deferred fetch record deferredRecord of controlDocument's fetch group's From 850bb463c8f4825c072c44a9a45e7820919057e0 Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Thu, 23 Jan 2025 10:17:57 +0000 Subject: [PATCH 101/102] Reapply "Simplify quota" This reverts commit 5225029a79e70a29bd8500ba5ffd7173f54b72e4. --- fetch.bs | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/fetch.bs b/fetch.bs index d044cfc56..07bebde9b 100644 --- a/fetch.bs +++ b/fetch.bs @@ -7057,24 +7057,20 @@ descendants share a quota of 384 kibibytes. That value is computed as such:

          0 -
        3. -

          For each navigable in controlDocument's - node navigable's descendant navigables whose container document's - deferred-fetch control document is controlDocument, decrement quota - by navigable's navigable container's reserved deferred-fetch quota. - -

          Delegate some of the quota to nested documents that reserved it. - -

        4. If quota is equal or less than 0, then return 0. -

        5. Let quotaForRequestOrigin be 64 kibibytes.

        6. For each navigable in controlDocument's - node navigable's descendant navigables whose active document's - deferred-fetch control document is controlDocument: + node navigable's inclusive descendant navigables whose + active document's deferred-fetch control document is + controlDocument:

            +
          1. For each container in navigable's + active document's shadow-including inclusive descendants which is a + navigable container, decrement quota by container's + reserved deferred-fetch quota. +

          2. For each deferred fetch record deferredRecord of controlDocument's fetch group's From 139351f9c3ea17128c34ab30ac707d344a4d611d Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Thu, 23 Jan 2025 10:19:08 +0000 Subject: [PATCH 102/102] Fix algo --- fetch.bs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fetch.bs b/fetch.bs index 07bebde9b..0aaa9266c 100644 --- a/fetch.bs +++ b/fetch.bs @@ -7073,7 +7073,7 @@ descendants share a quota of 384 kibibytes. That value is computed as such:

          3. For each deferred fetch record deferredRecord of - controlDocument's fetch group's + navigable's active document's fetch group's deferred fetch records: