diff --git a/fetch.bs b/fetch.bs index 4d01e315b..075e8262f 100755 --- a/fetch.bs +++ b/fetch.bs @@ -114,6 +114,7 @@ urlPrefix:https://tc39.es/ecma262/#;type:dfn;spec:ecma-262 @@ -7558,6 +7559,14 @@ dictionary RequestInit { RequestDuplex duplex; RequestPriority priority; any window; // can only be set to null + FetchObserverCallback observer; +}; + +callback FetchObserverCallback = undefined (FetchObserver requestObserver, FetchObserver responseObserver); + +[Exposed=(Window,Worker)] +interface FetchObserver : EventTarget { + attribute EventHandler onprogress; }; enum RequestDestination { "", "audio", "audioworklet", "document", "embed", "font", "frame", "iframe", "image", "json", "manifest", "object", "paintworklet", "report", "script", "sharedworker", "style", "track", "video", "worker", "xslt" }; @@ -8584,10 +8593,67 @@ method steps are: https://github.com/whatwg/dom/issues/1031#issuecomment-1233206400 --> +
  • Let hasUploadListeners be false. + +

  • Let requestObserver be null. + +

  • Let responseObserver be null. + +

  • +

    If init["{{RequestInit/observer}}"] exists, then: + +

      +
    1. Let observerCallback be init["{{RequestInit/observer}}"]. + +

    2. Set requestObserver to a {{FetchObserver}}. + +

    3. Set responseObserver to a {{FetchObserver}}. + +

    4. Let args be « requestObserver, responseObserver ». + +

    5. [=invoke|Invoke=] observerCallback with args + and "rethrow". If this throws an exception, reject p with it + and return p. + +

    6. If one or more progress event listeners were added to + requestObserver, then set hasUploadListeners to true. +

    + +
  • Let requestBodyTransmitted be 0. + +

  • Let requestBodyLength be request's body's + length, if request's body is non-null; + otherwise 0. + +

  • Assert: requestBodyLength is an integer. + +

  • +

    Let processRequestBodyChunkLength, given a bytesLength, be these steps: + +

      +
    1. Increase requestBodyTransmitted by bytesLength. + +

    2. If not roughly 50ms has passed since these steps were last invoked, then return. + +

    3. If hasUploadListeners is true, then fire a progress event named + progress at requestObserver with requestBodyTransmitted + and requestBodyLength. +

    +
  • -

    Set controller to the result of calling fetch given - request and processResponse given response being - these steps: +

    Let processRequestEndOfBody be these steps: + +

      +
    1. If hasUploadListeners is false, then return. + +

    2. Increase requestBodyTransmitted by bytesLength. + +

    3. Fire a progress event named progress at requestObserver + with requestBodyTransmitted and requestBodyLength. +

    + +
  • +

    Let processResponse given a response be these steps:

    1. If locallyAborted is true, then abort these steps. @@ -8615,10 +8681,17 @@ method steps are:

    2. Resolve p with responseObject.

    +
  • Set controller to the result of calling fetch given + request with processResponse set to processResponse, + processRequestBodyChunkLength set to processRequestBodyChunkLength, + and processRequestEndOfBody set to processRequestEndOfBody. +

  • Return p. +TEMPORARY progress +

    To abort a fetch() call with a promise, request, responseObject, and an error: @@ -9132,6 +9205,119 @@ done only by navigations). The fetch controller is also used to process the next manual redirect for requests with redirect mode set to "manual". +

    Interface {{ProgressEvent}}

    + +
    +[Exposed=(Window,Worker)]
    +interface ProgressEvent : Event {
    +  constructor(DOMString type, optional ProgressEventInit eventInitDict = {});
    +
    +  readonly attribute boolean lengthComputable;
    +  readonly attribute double loaded;
    +  readonly attribute double total;
    +};
    +
    +dictionary ProgressEventInit : EventInit {
    +  boolean lengthComputable = false;
    +  double loaded = 0;
    +  double total = 0;
    +};
    +
    + +

    Events using the {{ProgressEvent}} interface indicate some kind of progression. + +

    The +lengthComputable, +loaded, and +total +getter steps are to return the value they were initialized to. + + +

    Firing events using the {{ProgressEvent}} interface

    + +

    To fire a progress event named e at +target, given transmitted and length, means to fire an event +named e at target, using {{ProgressEvent}}, with the {{ProgressEvent/loaded}} +attribute initialized to transmitted, and if length is not 0, with the +{{ProgressEvent/lengthComputable}} attribute initialized to true and the {{ProgressEvent/total}} +attribute initialized to length. + + +

    Suggested names for events using the {{ProgressEvent}} interface

    + +

    This section is non-normative. + +

    The suggested {{Event/type}} +attribute values for use with +events using the +{{ProgressEvent}} interface are summarized in the table below. +Specification editors are free to tune the details to their specific +scenarios, though are strongly encouraged to discuss their usage with the +WHATWG community to ensure input from people familiar with the subject. + + + + + + + + + + + +
    {{Event/type}} attribute value + Description + Times + When +
    loadstart + Progress has begun. + Once. + First. +
    progress + In progress. + Once or more. + After loadstart has been + dispatched. +
    error + Progression failed. + Zero or once (mutually exclusive). + After the last progress has + been + dispatched. +
    abort + Progression is terminated. +
    timeout + Progression is terminated due to preset time expiring. +
    load + Progression is successful. +
    loadend + Progress has stopped. + Once. + After one of error, abort, + timeout or load has been + dispatched. +
    + +

    The error, abort, timeout, and +load event types are mutually exclusive. + +

    Throughout the web platform the error, abort, +timeout and load event types have +their {{Event/bubbles}} and {{Event/cancelable}} +attributes initialized to false, so it is suggested that for consistency all +events using the +{{ProgressEvent}} interface do the same. + + +

    Security considerations

    + +

    For cross-origin requests some kind of opt-in, e.g., the +CORS protocol, has to be used before events using the +{{ProgressEvent}} interface are +dispatched +as information (e.g., size) would be revealed that cannot be obtained +otherwise. +

    Acknowledgments