From 06b5d1d8fb6de007eedd9429ba70a4fa5b23fc51 Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Tue, 2 May 2023 15:22:42 +0300 Subject: [PATCH 01/87] 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 d398cf3b..a760c374 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

@@ -8641,6 +8731,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 3f3ef6087c0494dbf87e4f4dd0f693cd6b0da9b7 Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Thu, 4 May 2023 12:50:47 +0300 Subject: [PATCH 02/87] nits --- fetch.bs | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/fetch.bs b/fetch.bs index a760c374..16c37ff7 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 @@ -8742,7 +8742,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 @@ -8778,8 +8778,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 @@ -8794,7 +8794,7 @@ when a fetch group is terminated, or after a tim

         
         dictionary DeferredRequestInit : RequestInit {
        -  DOMHighResTimeStamp? backgroundTimeout;
        +  DOMHighResTimeStamp? inactiveTimeout;
         };
         
         partial interface mixin WindowOrWorkerGlobalScope {
        @@ -8820,16 +8820,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 bc10119c978992f69033bc22eabb20d479d7a650 Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Mon, 8 May 2023 13:50:34 +0300 Subject: [PATCH 03/87] 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 16c37ff7..52f1d1db 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 3cbb03c4d2e6c29b074b8e561ebb1bf0284c95d1 Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Mon, 8 May 2023 14:00:32 +0300 Subject: [PATCH 04/87] Move sections around --- fetch.bs | 223 +++++++++++++++++++++++++++---------------------------- 1 file changed, 111 insertions(+), 112 deletions(-) diff --git a/fetch.bs b/fetch.bs index 52f1d1db..3a801130 100644 --- a/fetch.bs +++ b/fetch.bs @@ -6808,6 +6808,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

      @@ -8535,7 +8591,7 @@ otherwise false.
  • -

    Fetch method

    +

    Fetch methods

     partial interface mixin WindowOrWorkerGlobalScope {
    @@ -8675,6 +8731,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 @@ -8731,117 +8841,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 5418780befe48bfb778f5c4f2666005c43b826dc Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Mon, 8 May 2023 14:06:13 +0300 Subject: [PATCH 05/87] Rename to fetchLater and backgroundTimeout --- fetch.bs | 37 +++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/fetch.bs b/fetch.bs index 3a801130..f704b44a 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 @@ -6817,7 +6818,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 @@ -6853,8 +6854,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 @@ -8731,22 +8732,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:

        @@ -8762,16 +8763,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 649ed8e81fe8f55e5540b1d5546fd1e603d6f523 Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Mon, 8 May 2023 18:48:39 +0300 Subject: [PATCH 06/87] Fix hierarchy --- fetch.bs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fetch.bs b/fetch.bs index f704b44a..8a9c9f6e 100644 --- a/fetch.bs +++ b/fetch.bs @@ -8592,8 +8592,9 @@ otherwise false.

      -

      Fetch methods

      +

      Fetch methods

      +

      Fetch Method

       partial interface mixin WindowOrWorkerGlobalScope {
         [NewObject] Promise<Response> fetch(RequestInfo input, optional RequestInit init = {});
      
      From 732751a054dbb612340d45f9e858c00821c75e0a Mon Sep 17 00:00:00 2001
      From: Noam Rosenthal 
      Date: Sun, 11 Jun 2023 16:21:12 +0300
      Subject: [PATCH 07/87] 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 8a9c9f6e..ae2a54fa 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

      @@ -8741,19 +8725,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. @@ -8773,17 +8767,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 7882b2275a031bf3f3cf306efb498139a7e53733 Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Sun, 11 Jun 2023 16:25:03 +0300 Subject: [PATCH 08/87] nit --- fetch.bs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fetch.bs b/fetch.bs index ae2a54fa..d607a84d 100644 --- a/fetch.bs +++ b/fetch.bs @@ -6796,8 +6796,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 24320ab18fa68557b7b2adf2708c52455268c13c Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Sun, 18 Jun 2023 13:20:34 +0300 Subject: [PATCH 09/87] or/and --- fetch.bs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fetch.bs b/fetch.bs index d607a84d..00b073ec 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 f12534e99c04e2e272524f0f9584541b1854da24 Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Thu, 29 Jun 2023 08:55:37 +0300 Subject: [PATCH 10/87] Remove spurious promise --- fetch.bs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/fetch.bs b/fetch.bs index 00b073ec..1fd414f9 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). @@ -8749,12 +8751,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 13b37f5aa269589ff07d990c67849132da547407 Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Thu, 29 Jun 2023 11:42:26 +0300 Subject: [PATCH 11/87] Rename backgrounded --- fetch.bs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fetch.bs b/fetch.bs index 1fd414f9..e1f4a4cd 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 5df11efa7df7236969bb8270c630fb4cbdabfc54 Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Tue, 4 Jul 2023 14:17:53 +0300 Subject: [PATCH 12/87] Throw a few more errors --- fetch.bs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/fetch.bs b/fetch.bs index e1f4a4cd..65ebacf1 100644 --- a/fetch.bs +++ b/fetch.bs @@ -8750,6 +8750,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. @@ -8758,6 +8766,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 6fc6d7e19d4a363f49e9abb546bb47cd213257c9 Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Wed, 5 Jul 2023 10:57:59 +0300 Subject: [PATCH 13/87] Always set keepalive --- fetch.bs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fetch.bs b/fetch.bs index 65ebacf1..2af4e2e4 100644 --- a/fetch.bs +++ b/fetch.bs @@ -6837,6 +6837,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 03ed395d018f1238be852284c1d4c0648b70bf2f Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Sun, 20 Aug 2023 12:45:38 +0300 Subject: [PATCH 14/87] Nits --- fetch.bs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/fetch.bs b/fetch.bs index 2af4e2e4..4ad98b60 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 fd2d6f1b7bd66ef05a45dc17953f418c8afd91ff Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Sun, 20 Aug 2023 13:17:34 +0300 Subject: [PATCH 15/87] 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 4ad98b60..dd7220dc 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

          @@ -6842,8 +6839,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 @@ -8730,7 +8727,7 @@ dictionary DeferredRequestInit : RequestInit { }; interface FetchLaterResult { - readonly attribute boolean sent; + readonly attribute boolean activated; }; partial interface mixin WindowOrWorkerGlobalScope { @@ -8742,8 +8739,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 5945a0f4d626530112ac187d86cf28256cc31205 Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Wed, 23 Aug 2023 11:04:34 +0300 Subject: [PATCH 16/87] Throw RangeError when backgroundTimeout is negative --- fetch.bs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fetch.bs b/fetch.bs index dd7220dc..b9bde58a 100644 --- a/fetch.bs +++ b/fetch.bs @@ -8780,6 +8780,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 86fc164b64f10e8452f77140e5f167a0d3c66da2 Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Tue, 19 Sep 2023 13:00:22 +0300 Subject: [PATCH 17/87] Refactor out backgroundTimeout, use activationTimeout --- fetch.bs | 124 +++++++++++++++++++++++++++---------------------------- 1 file changed, 60 insertions(+), 64 deletions(-) diff --git a/fetch.bs b/fetch.bs index b9bde58a..8e7200a7 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.

        @@ -6795,13 +6750,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 @@ -6839,17 +6793,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

        @@ -8723,7 +8719,7 @@ with a promise, request, responseObject, and an
         
         dictionary DeferredRequestInit : RequestInit {
        -  DOMHighResTimeStamp backgroundTimeout;
        +  DOMHighResTimeStamp activationTimeout;
         };
         
         interface FetchLaterResult {
        @@ -8772,18 +8768,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 b60230fb51cea343cecfe2568219ca9945b4090c Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Tue, 19 Sep 2023 13:11:23 +0300 Subject: [PATCH 18/87] Throw on ReadableStream --- fetch.bs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/fetch.bs b/fetch.bs index 8e7200a7..7cfb8569 100644 --- a/fetch.bs +++ b/fetch.bs @@ -8768,6 +8768,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 8038c5d4285c16fa2fdc386b62801c47cec3c980 Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Tue, 19 Sep 2023 15:01:56 +0300 Subject: [PATCH 19/87] Fix grammar --- fetch.bs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fetch.bs b/fetch.bs index 7cfb8569..6c799c50 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 6da45354dd0b66646f515fca158c64bd5f318480 Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Mon, 2 Oct 2023 21:18:43 +0300 Subject: [PATCH 20/87] 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 6c799c50..325028d3 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.

      @@ -6755,7 +6758,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 @@ -6798,12 +6801,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.

        @@ -6843,10 +6850,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

      @@ -8575,7 +8578,7 @@ otherwise false.

      Fetch methods

      -

      Fetch Method

      +

      Fetch Methods

       partial interface mixin WindowOrWorkerGlobalScope {
         [NewObject] Promise<Response> fetch(RequestInfo input, optional RequestInit init = {});
      @@ -8713,15 +8716,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;
     };
    @@ -8747,15 +8748,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. @@ -8768,24 +8769,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 d3d3c64a8236f8faad8eeb9c7be7e4823c62a7fd Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Tue, 3 Oct 2023 14:14:54 +0300 Subject: [PATCH 21/87] Handle CR nits --- fetch.bs | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/fetch.bs b/fetch.bs index 325028d3..928e811a 100644 --- a/fetch.bs +++ b/fetch.bs @@ -6764,6 +6764,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. @@ -6789,7 +6803,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. @@ -8763,20 +8777,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"] @@ -8788,8 +8788,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 a169b33977054a2a4b21ae41076d8a79c2be931b Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Tue, 3 Oct 2023 15:44:38 +0300 Subject: [PATCH 22/87] Remove h2 --- fetch.bs | 2 -- 1 file changed, 2 deletions(-) diff --git a/fetch.bs b/fetch.bs index 928e811a..e52decec 100644 --- a/fetch.bs +++ b/fetch.bs @@ -8590,8 +8590,6 @@ otherwise false.

    -

    Fetch methods

    -

    Fetch Methods

     partial interface mixin WindowOrWorkerGlobalScope {
    
    From 91fd7eef135b84ab322ba549e10af98e33f4cdde Mon Sep 17 00:00:00 2001
    From: Noam Rosenthal 
    Date: Tue, 3 Oct 2023 15:45:10 +0300
    Subject: [PATCH 23/87] Bring back line break
    
    ---
     fetch.bs | 1 +
     1 file changed, 1 insertion(+)
    
    diff --git a/fetch.bs b/fetch.bs
    index e52decec..9821082d 100644
    --- a/fetch.bs
    +++ b/fetch.bs
    @@ -8591,6 +8591,7 @@ otherwise false.
     
     
     

    Fetch Methods

    +
     partial interface mixin WindowOrWorkerGlobalScope {
       [NewObject] Promise<Response> fetch(RequestInfo input, optional RequestInit init = {});
    
    From 1024c7bc53652c1be23202fa143548da7ec99008 Mon Sep 17 00:00:00 2001
    From: Noam Rosenthal 
    Date: Tue, 3 Oct 2023 15:45:34 +0300
    Subject: [PATCH 24/87] nit
    
    ---
     fetch.bs | 2 +-
     1 file changed, 1 insertion(+), 1 deletion(-)
    
    diff --git a/fetch.bs b/fetch.bs
    index 9821082d..0a17837e 100644
    --- a/fetch.bs
    +++ b/fetch.bs
    @@ -8590,7 +8590,7 @@ otherwise false.
     
     
     
    -

    Fetch Methods

    +

    Fetch methods

     partial interface mixin WindowOrWorkerGlobalScope {
    
    From 89f4df6df50b922e4f54303ca239cd8d71bbdbb0 Mon Sep 17 00:00:00 2001
    From: Noam Rosenthal 
    Date: Tue, 3 Oct 2023 15:45:46 +0300
    Subject: [PATCH 25/87] nit
    
    ---
     fetch.bs | 2 +-
     1 file changed, 1 insertion(+), 1 deletion(-)
    
    diff --git a/fetch.bs b/fetch.bs
    index 0a17837e..96cf0e96 100644
    --- a/fetch.bs
    +++ b/fetch.bs
    @@ -8590,7 +8590,7 @@ otherwise false.
     
     
     
    -

    Fetch methods

    +

    Fetch methods

     partial interface mixin WindowOrWorkerGlobalScope {
    
    From ee03b2ba977f2f1af4cbbdde0e63c2233ec9f6d1 Mon Sep 17 00:00:00 2001
    From: Noam Rosenthal 
    Date: Tue, 3 Oct 2023 15:47:27 +0300
    Subject: [PATCH 26/87] nit
    
    ---
     fetch.bs | 2 +-
     1 file changed, 1 insertion(+), 1 deletion(-)
    
    diff --git a/fetch.bs b/fetch.bs
    index 96cf0e96..e35dd805 100644
    --- a/fetch.bs
    +++ b/fetch.bs
    @@ -8590,7 +8590,7 @@ otherwise false.
     
     
     
    -

    Fetch methods

    +

    Fetch methods

     partial interface mixin WindowOrWorkerGlobalScope {
    
    From c061b8cfdf7fa2d84c5e23526fe1ed3687462e14 Mon Sep 17 00:00:00 2001
    From: Noam Rosenthal 
    Date: Mon, 9 Oct 2023 19:07:27 +0300
    Subject: [PATCH 27/87] - 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 e35dd805..a2064baa 100644
    --- a/fetch.bs
    +++ b/fetch.bs
    @@ -6758,7 +6758,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 @@ -6815,12 +6815,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 @@ -8732,7 +8734,7 @@ with a promise, request, responseObject, and an

         
         dictionary DeferredRequestInit : RequestInit {
        -  DOMHighResTimeStamp activationDeadline;
        +  DOMHighResTimeStamp activateAfter;
         };
         
         [Exposed=Window,Worker]
        @@ -8776,18 +8778,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 95ef13911df4e0da74f1926b9793ba574bc9daa5 Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Wed, 25 Oct 2023 10:19:14 +0100 Subject: [PATCH 28/87] 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 a2064baa..c1eb80a8 100644 --- a/fetch.bs +++ b/fetch.bs @@ -8762,13 +8762,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 570bd49061f365e86f7301e0d2367b541c49dba7 Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Tue, 12 Mar 2024 18:00:58 +0000 Subject: [PATCH 29/87] 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 c1eb80a8..0fea2827 100644 --- a/fetch.bs +++ b/fetch.bs @@ -6770,40 +6770,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. @@ -6836,6 +6886,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 ef05c02a4fd480a9f770aae8dc5ffce62effde85 Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Tue, 12 Mar 2024 20:29:16 +0000 Subject: [PATCH 30/87] Check permissions policy --- fetch.bs | 47 ++++++++++++++++++++++++++++++++--------------- 1 file changed, 32 insertions(+), 15 deletions(-) diff --git a/fetch.bs b/fetch.bs index 0fea2827..3ca3485c 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 @@ -6784,16 +6788,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 @@ -6810,10 +6828,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: @@ -6886,9 +6903,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. @@ -6896,18 +6913,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 d91bce59013d53bb7a378366cb3ed78710da4706 Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Wed, 13 Mar 2024 08:27:12 +0000 Subject: [PATCH 31/87] Narrow scope to Window --- fetch.bs | 121 ++++++++++++++----------------------------------------- 1 file changed, 31 insertions(+), 90 deletions(-) diff --git a/fetch.bs b/fetch.bs index 3ca3485c..20d8a338 100644 --- a/fetch.bs +++ b/fetch.bs @@ -6765,8 +6765,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}}. @@ -6787,31 +6789,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 @@ -6820,54 +6811,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}}.

      @@ -6903,30 +6868,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: @@ -8828,12 +8769,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 b52f1c28349f74382acd949b697432dac92ec317 Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Wed, 13 Mar 2024 12:01:31 +0000 Subject: [PATCH 32/87] Remove existing NotAllowedError --- fetch.bs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/fetch.bs b/fetch.bs index 20d8a338..7a075123 100644 --- a/fetch.bs +++ b/fetch.bs @@ -6771,7 +6771,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}}. @@ -8794,11 +8794,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 6475ce2bf408aede8113328749509dd74883ee50 Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Wed, 13 Mar 2024 12:30:20 +0000 Subject: [PATCH 33/87] Fix null-body issues --- fetch.bs | 88 +++++++++++++++++++++++++++++--------------------------- 1 file changed, 46 insertions(+), 42 deletions(-) diff --git a/fetch.bs b/fetch.bs index 7a075123..8d43b538 100644 --- a/fetch.bs +++ b/fetch.bs @@ -6776,67 +6776,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 c3e863bc106a36748b0397f8165af7617caebebd Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Mon, 25 Mar 2024 16:49:32 +0000 Subject: [PATCH 34/87] Revise based on nits --- fetch.bs | 91 ++++++++++++++++++++++++++++---------------------------- 1 file changed, 45 insertions(+), 46 deletions(-) diff --git a/fetch.bs b/fetch.bs index 8d43b538..ee17ff20 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.
        @@ -6757,9 +6759,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): @@ -6774,14 +6776,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 @@ -6805,10 +6807,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. @@ -6824,14 +6826,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. @@ -6851,16 +6853,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. @@ -6872,7 +6874,7 @@ i.e. when a fetch group is terminated, or after

          -
          +

          To process deferred fetches given a fetch group fetchGroup:

            @@ -6888,7 +6890,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 @@ -6903,6 +6905,7 @@ i.e. when a fetch group is terminated, or after

          +

          Fetch API

          The fetch() method is relatively low-level API for @@ -8634,6 +8637,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 = {}); +};

  • @@ -8767,22 +8784,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. @@ -8812,8 +8813,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 5a95bf4bcfa0a6d74a83b54b5d3baea52d5fce00 Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Mon, 25 Mar 2024 16:54:20 +0000 Subject: [PATCH 35/87] Use correct dfn --- fetch.bs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/fetch.bs b/fetch.bs index ee17ff20..208b95c2 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 @@ -8795,7 +8796,7 @@ true if this's deferred record's

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

      From 4d35d2067717bcbb9fd5d06c17734670db8a4894 Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Mon, 25 Mar 2024 16:58:40 +0000 Subject: [PATCH 36/87] Remove spurious whitespace --- fetch.bs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fetch.bs b/fetch.bs index 208b95c2..9e0cb51f 100644 --- a/fetch.bs +++ b/fetch.bs @@ -6779,7 +6779,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 f08e7210e7d077e8d0324b3f4c61fc58f0ff06f3 Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Mon, 25 Mar 2024 17:34:03 +0000 Subject: [PATCH 37/87] Use $ --- fetch.bs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/fetch.bs b/fetch.bs index 9e0cb51f..37735945 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 @@ -6779,7 +6775,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 2a2cb6716c433615e22cb62eb9efb1c2e411e266 Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Thu, 11 Apr 2024 14:34:59 +0100 Subject: [PATCH 38/87] 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 37735945..94c9864c 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:

      @@ -6775,9 +6782,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: @@ -6792,50 +6799,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". @@ -6871,6 +6839,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 6b1a11f9c92cc1986332ea2a4969345392e18a97 Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Thu, 11 Apr 2024 14:46:46 +0100 Subject: [PATCH 39/87] nits --- fetch.bs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/fetch.bs b/fetch.bs index 94c9864c..6ef4e41d 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 @@ -6771,8 +6771,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. @@ -6946,6 +6946,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 8dcc0919b7ae30e58faeb6f028622f5da26742ba Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Fri, 26 Apr 2024 15:11:02 +0100 Subject: [PATCH 40/87] 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 6ef4e41d..c0dcde1b 100644 --- a/fetch.bs +++ b/fetch.bs @@ -6774,8 +6774,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}}. @@ -6786,6 +6784,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: @@ -6799,13 +6805,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 315f23314ca50f8eb06408d352f5578d838618a8 Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Thu, 5 Sep 2024 10:34:39 +0100 Subject: [PATCH 41/87] 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 c0dcde1b..a394aa48 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: @@ -6780,10 +6781,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. @@ -6857,6 +6854,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. @@ -6869,11 +6876,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 @@ -6909,7 +6927,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.

  • @@ -6944,8 +6963,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. @@ -6953,12 +6972,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 4ba3cc33b32501ae63ba63874e1e5d1c4e9549fd Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Thu, 24 Oct 2024 13:57:12 +0100 Subject: [PATCH 42/87] Update for new quota policy --- fetch.bs | 116 ++++++++++++++----------------------------------------- 1 file changed, 28 insertions(+), 88 deletions(-) diff --git a/fetch.bs b/fetch.bs index a394aa48..1eb14de4 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.


    @@ -6848,56 +6842,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 @@ -6917,21 +6889,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.

  • @@ -6962,30 +6926,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 f38dd65af0c867978c32ad64ef599b7b637c0fd6 Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Thu, 24 Oct 2024 13:59:43 +0100 Subject: [PATCH 43/87] Fix

      --- fetch.bs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/fetch.bs b/fetch.bs index 1eb14de4..ce4014af 100644 --- a/fetch.bs +++ b/fetch.bs @@ -6857,12 +6857,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 @@ -6870,6 +6867,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 7159d2b913df0417c4499fc381e48e1133ce6a43 Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Thu, 24 Oct 2024 14:02:58 +0100 Subject: [PATCH 44/87] Only decrement for cross-origin --- fetch.bs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/fetch.bs b/fetch.bs index ce4014af..cf5bfb53 100644 --- a/fetch.bs +++ b/fetch.bs @@ -6889,8 +6889,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 defc074c88fbe337f67d6b082e479be225c0cd30 Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Thu, 24 Oct 2024 16:19:45 +0100 Subject: [PATCH 45/87] Apply permission policy --- fetch.bs | 100 +++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 86 insertions(+), 14 deletions(-) diff --git a/fetch.bs b/fetch.bs index cf5bfb53..054f285d 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 @@ -6906,22 +6907,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 25ddd90e654f74c33c7c77d4931a2e0059a84445 Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Thu, 24 Oct 2024 16:25:33 +0100 Subject: [PATCH 46/87] nits --- fetch.bs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/fetch.bs b/fetch.bs index 054f285d..96081a4d 100644 --- a/fetch.bs +++ b/fetch.bs @@ -6924,13 +6924,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. @@ -6940,7 +6939,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. @@ -6954,7 +6953,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 aac70bded245c649019b0e38679dc5071ef32990 Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Mon, 28 Oct 2024 09:13:51 +0000 Subject: [PATCH 47/87] refactor --- fetch.bs | 180 +++++++++++++++++++++++++++++++------------------------ 1 file changed, 102 insertions(+), 78 deletions(-) diff --git a/fetch.bs b/fetch.bs index 96081a4d..13fe80b3 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, @@ -6848,21 +6849,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. @@ -6888,15 +6915,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 @@ -6907,95 +6940,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 e7450ac6ebe62a35a38bbe1cb6253be1eef4f279 Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Mon, 28 Oct 2024 09:36:15 +0000 Subject: [PATCH 48/87] 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 13fe80b3..ef4b5f16 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. @@ -6840,17 +6840,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. @@ -6858,7 +6891,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. @@ -6882,10 +6915,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. @@ -6917,14 +6950,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. @@ -6940,8 +6973,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 @@ -6955,30 +6988,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 @@ -6989,68 +7022,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 1f7b7a04e8e671790b551f5ff28ce9b92ed7b85b Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Mon, 28 Oct 2024 09:48:14 +0000 Subject: [PATCH 49/87] nits --- fetch.bs | 38 ++++++++++++++++++++++---------------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/fetch.bs b/fetch.bs index ef4b5f16..6814d247 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 @@ -6972,21 +6972,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: @@ -7026,6 +7011,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 @@ -7042,6 +7030,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 fe9332e840ce595ca2e008ce20d28d786d5df704 Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Thu, 31 Oct 2024 09:38:25 +0000 Subject: [PATCH 50/87] Clarify permission policy --- fetch.bs | 42 +++++++++++++++++++++++++----------------- 1 file changed, 25 insertions(+), 17 deletions(-) diff --git a/fetch.bs b/fetch.bs index 6814d247..8e0c1042 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 @@ -6877,6 +6878,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: @@ -6896,14 +6905,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. @@ -6983,16 +6991,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 @@ -7005,7 +7013,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.

    • @@ -7020,13 +7028,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"}}. @@ -7037,10 +7045,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 69d61fe3e7d63843b6303507f2f4139dcb3f63dc Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Thu, 31 Oct 2024 09:52:43 +0000 Subject: [PATCH 51/87] 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 8e0c1042..c56eb48f 100644 --- a/fetch.bs +++ b/fetch.bs @@ -6902,13 +6902,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. @@ -6921,12 +6917,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 afd4607099fc9751fb25fe3c93f093e11f300386 Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Thu, 31 Oct 2024 10:05:14 +0000 Subject: [PATCH 52/87] typo --- fetch.bs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fetch.bs b/fetch.bs index c56eb48f..75e52495 100644 --- a/fetch.bs +++ b/fetch.bs @@ -7058,7 +7058,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 f3ee764e5a64288044a7c1f5f3fc222c4d3dd47c Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Wed, 20 Nov 2024 09:44:40 +0000 Subject: [PATCH 53/87] Sort out some quota logic --- fetch.bs | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/fetch.bs b/fetch.bs index 75e52495..15684957 100644 --- a/fetch.bs +++ b/fetch.bs @@ -6905,7 +6905,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. @@ -6913,7 +6913,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. @@ -6962,21 +6962,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 d7ae778e40ef3def5a5c8010a5eb557afc78e827 Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Wed, 20 Nov 2024 11:00:58 +0000 Subject: [PATCH 54/87] Sort out some quota logic --- fetch.bs | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/fetch.bs b/fetch.bs index 15684957..cad7e930 100644 --- a/fetch.bs +++ b/fetch.bs @@ -6894,38 +6894,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 595ed70bbf20e3dee812a5e66ef810bf59ce3fc4 Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Tue, 3 Dec 2024 10:41:19 +0000 Subject: [PATCH 55/87] Fix container --- fetch.bs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fetch.bs b/fetch.bs index cad7e930..9dddcd5b 100644 --- a/fetch.bs +++ b/fetch.bs @@ -6989,7 +6989,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 38462e7ba5a04545528e8f4eee5a0e20cd55bc8a Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Tue, 3 Dec 2024 11:18:51 +0000 Subject: [PATCH 56/87] 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 9dddcd5b..22713950 100644 --- a/fetch.bs +++ b/fetch.bs @@ -6778,30 +6778,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 @@ -6840,6 +6817,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: @@ -6943,9 +6953,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 0f15a9b5556c6d3951b9705d758c58c94dd36b09 Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Tue, 3 Dec 2024 12:54:30 +0000 Subject: [PATCH 57/87] Don't compute relatives twice --- fetch.bs | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/fetch.bs b/fetch.bs index 22713950..8a68a674 100644 --- a/fetch.bs +++ b/fetch.bs @@ -6962,18 +6962,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 9c90b7c4e8fcd838ecd17e832545c21134bc5c6a Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Wed, 4 Dec 2024 11:52:12 +0000 Subject: [PATCH 58/87] Refactor loop --- fetch.bs | 88 ++++++++++++++++++++++++++++++-------------------------- 1 file changed, 48 insertions(+), 40 deletions(-) diff --git a/fetch.bs b/fetch.bs index 8a68a674..b5a84e6a 100644 --- a/fetch.bs +++ b/fetch.bs @@ -6909,35 +6909,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 topRelativeNavigable's active document is not allowed to use + the policy-controlled feature {{PermissionPolicy/"deferred-fetch"}}, then return 0. +

    2. -

      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. +

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

      2. 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. +

      -

      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 topRelativeNavigable's + container's deferred fetch policy. -

    2. -

      Otherwise:

      -
        -
      1. Let containerPolicy be navigable'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 navigable'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. @@ -6962,19 +6963,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. +

  • @@ -7062,13 +7063,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 b28d802d461e005f30211495546598e3d12e51b7 Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Wed, 4 Dec 2024 11:58:28 +0000 Subject: [PATCH 59/87] Fix indents --- fetch.bs | 48 +++++++++++++++++++++++------------------------- 1 file changed, 23 insertions(+), 25 deletions(-) diff --git a/fetch.bs b/fetch.bs index b5a84e6a..d63441d4 100644 --- a/fetch.bs +++ b/fetch.bs @@ -6846,7 +6846,7 @@ i.e., when a fetch group is terminated, or after body's length. -
    1. Return totalRequestLength. +
    2. Return totalRequestLength.

    @@ -6909,8 +6909,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 @@ -6926,19 +6927,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. @@ -6966,18 +6967,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 6f7ddee0b8f8b4e656cf7fc48bddb8f12e1ffc3c Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Wed, 4 Dec 2024 12:01:38 +0000 Subject: [PATCH 60/87] Fix indent --- fetch.bs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/fetch.bs b/fetch.bs index d63441d4..fdfa5283 100644 --- a/fetch.bs +++ b/fetch.bs @@ -6965,7 +6965,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 @@ -6980,7 +6982,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 d6c26241081943ff76f3b031f24916e23962fa87 Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Wed, 4 Dec 2024 12:04:56 +0000 Subject: [PATCH 61/87] Fix list --- fetch.bs | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/fetch.bs b/fetch.bs index fdfa5283..52663e37 100644 --- a/fetch.bs +++ b/fetch.bs @@ -6966,18 +6966,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 6407fa0c0f49ace90b638ec566252dc2a3281411 Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Wed, 4 Dec 2024 12:06:10 +0000 Subject: [PATCH 62/87] Remove spurious

  • --- fetch.bs | 1 - 1 file changed, 1 deletion(-) diff --git a/fetch.bs b/fetch.bs index 52663e37..ec58cdae 100644 --- a/fetch.bs +++ b/fetch.bs @@ -6965,7 +6965,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 598ef2f0558ebe22fe68eb6a5e2144b8e08a2dd6 Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Wed, 4 Dec 2024 12:09:04 +0000 Subject: [PATCH 63/87] Tersify loop --- fetch.bs | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/fetch.bs b/fetch.bs index ec58cdae..b4ba215e 100644 --- a/fetch.bs +++ b/fetch.bs @@ -6966,15 +6966,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 e1ec33459c160884a6ab1de8aa828d5dea1984ee Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Sun, 8 Dec 2024 20:35:16 +0000 Subject: [PATCH 64/87] Refactor algorithm, simplify a lot --- fetch.bs | 252 +++++++++++++++++++++++-------------------------------- 1 file changed, 105 insertions(+), 147 deletions(-) diff --git a/fetch.bs b/fetch.bs index b4ba215e..281b588f 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, @@ -6758,7 +6756,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. @@ -6780,7 +6778,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}}. @@ -6880,7 +6878,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). @@ -6897,181 +6895,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 04615c50d4a62d735d5f6da3724e149ba15ba30e Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Mon, 9 Dec 2024 09:45:59 +0000 Subject: [PATCH 65/87] Use constants --- fetch.bs | 103 +++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 69 insertions(+), 34 deletions(-) diff --git a/fetch.bs b/fetch.bs index 281b588f..9b3be657 100644 --- a/fetch.bs +++ b/fetch.bs @@ -6886,30 +6886,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: @@ -6920,23 +6932,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 @@ -6955,11 +6988,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. @@ -6983,8 +7016,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 d8423e0d2727460523efc55b5d6d784fcd0907d0 Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Mon, 9 Dec 2024 09:54:51 +0000 Subject: [PATCH 66/87] Refactor quota algorithm --- fetch.bs | 34 +++++++++------------------------- 1 file changed, 9 insertions(+), 25 deletions(-) diff --git a/fetch.bs b/fetch.bs index 9b3be657..403a2a9c 100644 --- a/fetch.bs +++ b/fetch.bs @@ -6819,33 +6819,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.

    @@ -6894,7 +6879,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. @@ -6952,7 +6936,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 @@ -7021,7 +7005,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 @@ -7042,7 +7026,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 a9ee76ee91fe340bb71f911fc7e5dc3aff6d6ac1 Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Mon, 9 Dec 2024 10:54:46 +0000 Subject: [PATCH 67/87] Follow up on PR feedback --- fetch.bs | 153 +++++++++++++++++++++++++++++++------------------------ 1 file changed, 86 insertions(+), 67 deletions(-) diff --git a/fetch.bs b/fetch.bs index 403a2a9c..000fa201 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.

      @@ -6758,30 +6758,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. @@ -6800,7 +6785,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., @@ -6823,7 +6808,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. @@ -6873,18 +6858,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:

          @@ -6913,6 +6907,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. @@ -6930,8 +6927,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. @@ -6955,19 +6958,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.

        @@ -6992,65 +6994,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

      @@ -8961,8 +8962,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 616f6ba18d54d53149fd513d979e66ea34404024 Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Mon, 9 Dec 2024 11:05:32 +0000 Subject: [PATCH 68/87] linkify activateAfter --- fetch.bs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/fetch.bs b/fetch.bs index 000fa201..0929017a 100644 --- a/fetch.bs +++ b/fetch.bs @@ -8956,25 +8956,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 78a667fd3f5bc9f81dcd54ff6521846aff4d3a09 Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Mon, 9 Dec 2024 13:54:56 +0000 Subject: [PATCH 69/87] Add examples --- fetch.bs | 149 +++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 140 insertions(+), 9 deletions(-) diff --git a/fetch.bs b/fetch.bs index 0929017a..b1d4d3e8 100644 --- a/fetch.bs +++ b/fetch.bs @@ -6850,11 +6850,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 @@ -7049,7 +7111,6 @@ shared.

  • Otherwise, set container's reserved deferred-fetch quota to zero. -

  • @@ -8965,9 +9026,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}}. @@ -9000,6 +9064,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 e816ae9a5d0310e9717c704334edbcc147de3b04 Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Mon, 9 Dec 2024 21:10:57 +0000 Subject: [PATCH 70/87] Throw {{TypeError}} --- fetch.bs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/fetch.bs b/fetch.bs index b1d4d3e8..7f1f24df 100644 --- a/fetch.bs +++ b/fetch.bs @@ -9034,10 +9034,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 0f0a60df611ca5152d4242f01252ce1a9d1bd547 Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Tue, 10 Dec 2024 10:33:53 +0000 Subject: [PATCH 71/87] nits --- fetch.bs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/fetch.bs b/fetch.bs index 7f1f24df..907af7ee 100644 --- a/fetch.bs +++ b/fetch.bs @@ -6946,9 +6946,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 @@ -6989,7 +6987,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 @@ -7042,8 +7040,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. @@ -7060,7 +7058,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 a4aa615b358246839ea70d0c4348e06611188627 Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Tue, 10 Dec 2024 10:55:58 +0000 Subject: [PATCH 72/87] nits --- fetch.bs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fetch.bs b/fetch.bs index 907af7ee..7eb61c01 100644 --- a/fetch.bs +++ b/fetch.bs @@ -7056,7 +7056,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 8882b1878c0420a61d82a25fbe9eb981af016e70 Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Tue, 10 Dec 2024 11:33:19 +0000 Subject: [PATCH 73/87] Fix reserve algo --- fetch.bs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/fetch.bs b/fetch.bs index 7eb61c01..3cdfb432 100644 --- a/fetch.bs +++ b/fetch.bs @@ -7077,7 +7077,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. @@ -7092,10 +7095,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 1a8b32e6c4ecd63173f6ef589279ce9aea7fd849 Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Tue, 10 Dec 2024 11:36:24 +0000 Subject: [PATCH 74/87] Add note about conds --- fetch.bs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/fetch.bs b/fetch.bs index 3cdfb432..b1be41be 100644 --- a/fetch.bs +++ b/fetch.bs @@ -7088,6 +7088,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. @@ -7101,6 +7102,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 898c2bfa1ec81aa5929469d4e86e8f2df3d4a3d3 Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Tue, 10 Dec 2024 14:00:31 +0000 Subject: [PATCH 75/87] 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 b1be41be..c9fd36a4 100644 --- a/fetch.bs +++ b/fetch.bs @@ -6861,13 +6861,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 @@ -6911,19 +6911,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. @@ -6975,14 +7082,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. @@ -7005,14 +7112,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. @@ -7056,7 +7163,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 @@ -7067,7 +7174,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 @@ -7076,7 +7183,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 5676db3835d86ce08481a0158b42e92b9c28bfa5 Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Wed, 11 Dec 2024 09:34:53 +0000 Subject: [PATCH 76/87] granted --- fetch.bs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fetch.bs b/fetch.bs index c9fd36a4..c924c6e7 100644 --- a/fetch.bs +++ b/fetch.bs @@ -7148,7 +7148,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 4f780833e63ea48d701d6a30296735e7fdf3638f Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Wed, 11 Dec 2024 11:48:06 +0000 Subject: [PATCH 77/87] Lots of fixes --- fetch.bs | 367 +++++++++++++++++++++++++++---------------------------- 1 file changed, 179 insertions(+), 188 deletions(-) diff --git a/fetch.bs b/fetch.bs index c924c6e7..b856d0af 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. @@ -6761,12 +6755,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. @@ -6782,17 +6785,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. @@ -6804,10 +6811,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. @@ -6819,32 +6829,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.

    @@ -6852,23 +6870,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). @@ -6888,15 +6907,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.

    
    @@ -6933,8 +6953,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
    @@ -6946,19 +6966,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
    @@ -7000,7 +7020,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
    @@ -7013,37 +7033,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.

    @@ -7051,25 +7063,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 @@ -7078,51 +7090,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 @@ -7140,17 +7141,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. @@ -7164,8 +7161,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, @@ -7173,53 +7170,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. @@ -9101,13 +9093,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.

    @@ -9132,9 +9123,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 @@ -9142,35 +9139,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.

    @@ -9211,14 +9204,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 1882126772532d20066fcffddaaba4a7f0c9e448 Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Wed, 11 Dec 2024 14:18:46 +0000 Subject: [PATCH 78/87] 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 b856d0af..afa50c40 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. @@ -6768,6 +6768,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". @@ -6777,9 +6779,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:

      @@ -6829,26 +6844,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.

      @@ -6897,13 +6897,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. @@ -6923,125 +6924,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 aa3a7894f20cc1c898903b6ff52ff3b1726a8d41 Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Wed, 11 Dec 2024 14:20:25 +0000 Subject: [PATCH 79/87] Some more nits --- fetch.bs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/fetch.bs b/fetch.bs index afa50c40..0f2ab8c2 100644 --- a/fetch.bs +++ b/fetch.bs @@ -6878,7 +6878,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 @@ -6952,6 +6952,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.
    @@ -6967,8 +6968,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 6b4976b8ae5785ab77a4b72e55505c352cd58c02 Mon Sep 17 00:00:00 2001
    From: Noam Rosenthal 
    Date: Wed, 11 Dec 2024 15:00:30 +0000
    Subject: [PATCH 80/87] Clarify containers in algo
    
    ---
     fetch.bs | 40 +++++++++++++++++++---------------------
     1 file changed, 19 insertions(+), 21 deletions(-)
    
    diff --git a/fetch.bs b/fetch.bs
    index 0f2ab8c2..bbbcdbc1 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.


    @@ -7006,7 +7006,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. @@ -7014,7 +7014,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 @@ -7024,14 +7024,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. @@ -7049,27 +7049,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 @@ -7088,9 +7086,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. @@ -7122,8 +7120,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 @@ -7139,11 +7137,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 fd528236a07d250a8508db1598a339a0d74cd09d Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Wed, 11 Dec 2024 15:16:21 +0000 Subject: [PATCH 81/87] 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 bbbcdbc1..31fdd42a 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.


    @@ -7033,14 +7036,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. @@ -7049,30 +7051,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. @@ -7085,17 +7092,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. @@ -7120,8 +7127,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 @@ -7137,11 +7144,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 409aa54fe6b8cf7adcaa78672dbf97ad92a29b7c Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Thu, 12 Dec 2024 11:50:27 +0000 Subject: [PATCH 82/87] nits --- fetch.bs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/fetch.bs b/fetch.bs index 31fdd42a..82b41483 100644 --- a/fetch.bs +++ b/fetch.bs @@ -6782,7 +6782,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 @@ -7095,8 +7095,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. @@ -7143,7 +7143,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 04d734b0b5ac4c1876278118aaca0326039f489d Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Sun, 15 Dec 2024 20:09:56 +0000 Subject: [PATCH 83/87] 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 82b41483..122c7372 100644 --- a/fetch.bs +++ b/fetch.bs @@ -6765,14 +6765,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". @@ -6782,19 +6780,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. @@ -6873,15 +6861,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 @@ -6940,9 +6928,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});
      @@ -6961,11 +6948,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.
      @@ -6986,10 +6977,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.

    @@ -7002,14 +6995,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. @@ -7017,90 +7007,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. @@ -7123,32 +7086,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.

    @@ -7165,6 +7132,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

    @@ -9084,22 +9062,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 @@ -9108,9 +9088,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 b74644ea84632d47828e19669ddf7f8d29863016 Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Mon, 16 Dec 2024 12:53:26 +0000 Subject: [PATCH 84/87] Editorial pass --- fetch.bs | 47 +++++++++++++++++++++++++---------------------- 1 file changed, 25 insertions(+), 22 deletions(-) diff --git a/fetch.bs b/fetch.bs index 122c7372..ad2bf1f0 100644 --- a/fetch.bs +++ b/fetch.bs @@ -6766,8 +6766,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:

      @@ -6793,8 +6793,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 @@ -6828,19 +6830,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.

    @@ -6851,8 +6852,8 @@ takes no arguments:
  • Set deferredRecord's done to true. -

  • Fetch deferredRecord's - request. +

  • Fetch deferredRecord's request. +

  • Return true. @@ -6869,16 +6870,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). @@ -6995,7 +6996,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}} @@ -7020,8 +7021,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: @@ -7065,7 +7065,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. @@ -7111,7 +7113,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 2e8599f015f987d766ab582c7470f08ee13b84a1 Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Mon, 16 Dec 2024 13:15:32 +0000 Subject: [PATCH 85/87] 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 ad2bf1f0..c7a318fd 100644 --- a/fetch.bs +++ b/fetch.bs @@ -6767,7 +6767,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:

      @@ -6806,7 +6806,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.

  • @@ -6838,10 +6838,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. +

    @@ -6871,15 +6875,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). @@ -7003,45 +7007,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 @@ -7056,19 +7069,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 9d336f1f31e43e7dc5e209b2707ed09d94678f91 Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Mon, 16 Dec 2024 13:20:09 +0000 Subject: [PATCH 86/87] More editorial --- fetch.bs | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/fetch.bs b/fetch.bs index c7a318fd..6a71fa13 100644 --- a/fetch.bs +++ b/fetch.bs @@ -7006,30 +7006,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 197806d165e65a1bfc751b58af0980862de36cdb Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Mon, 16 Dec 2024 13:55:27 +0000 Subject: [PATCH 87/87] Bring back
        --- fetch.bs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/fetch.bs b/fetch.bs index 6a71fa13..c5096ced 100644 --- a/fetch.bs +++ b/fetch.bs @@ -6981,13 +6981,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.

        @@ -7006,6 +7012,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: