Background Fetch

Editor’s Draft,

This version:
https://wicg.github.io/background-fetch/
Issue Tracking:
GitHub
Inline In Spec
Editors:
(Google)
(Google)

Abstract

An API to handle large uploads/downloads in the background with user visibility.

Status of this document

This specification was published by the Web Platform Incubator Community Group. It is not a W3C Standard nor is it on the W3C Standards Track. Please note that under the W3C Community Contributor License Agreement (CLA) there is a limited opt-out and other conditions apply. Learn more about W3C Community and Business Groups.

1. Introduction

A service worker is capable of fetching and caching assets, the size of which is restricted only by origin storage. However, if the user navigates away from the site or closes the browser, the service worker is likely to be killed. This can happen even if there’s a pending promise passed to waitUntil() - if it hasn’t resolved within a few minutes the browser may consider it an abuse of service worker and kill the process.

This is excellent for battery and privacy, but it makes it difficult to download and cache large assets such as podcasts and movies, and upload video and images.

This specification aims to:

2. Realms

All platform objects are created in the context object's relevant Realm unless otherwise specified.

3. Infrastructure

A resource is considered temporarily unavailable if the user agent believes the resource may soon become available. Reasons may include:

The background fetch task source is a task source.

To queue a bgfetch task on an optional eventLoop (an event loop, defaulting to the caller’s context object's relevant settings object's responsible event loop) with steps (steps), queue a task on eventLoop using the background fetch task source to run steps.

3.1. Extensions to service worker registration

A service worker registration additionally has:

3.2. Background fetch

A background fetch consists of:

To get a background fetch's (bgFetch) stored body bytes total, run these steps:
  1. Let total be 0.

  2. For each record of bgFetch’s records:

    1. Increment total by the length of record’s response data's bytes.

  3. Return total.

3.2.1. Display

To display a background fetch (bgFetch) for a given environment (an environment settings object), the user agent must present a user interface that follows these rules:

Let permission be the permission state for a PermissionDescriptor with name "background-fetch" and environment. If permission is "prompt", then the user agent may set bgFetch’s paused flag at the beginning of this algorithm. The user agent should allow the user to either accept the background fetch (unsetting bgFetch’s paused flag), or refuse the background fetch (setting bgFetch’s abort all flag). The user agent may also provide always-allow and alway-deny options, which can be used as new information about the user’s intent for this permission.

The user agent may also consider setting bgFetch’s paused flag if the user is on a metered connection, or the background fetch was started in the background.

3.3. Background fetch record

A background fetch record consists of:

3.4. Background fetch response

A Background fetch response consists of:

The response can be exposed if the result is the empty string, "success", or "bad-status".

4. Algorithms

4.1. Perform a background fetch

Note: This is the algorithm that manages the 'background' parts of a background fetch. Only one instance of this algorithm runs per background fetch.

To perform a background fetch for bgFetch (a background fetch), run these steps:
  1. Let swRegistration be bgFetch’s service worker registration.

  2. Let settledFetches be 0.

  3. Let immediateFailure be false.

  4. Let failureReason be the empty string.

  5. For each record in bgFetch’s records, run these steps in parallel:

    1. Complete a record for bgFetch and record.

    2. Let result be record’s response data's result.

    3. If failureReason is not the empty string:

      1. Assert: result is not "redundant".

      2. Set failureReason to result.

    4. Increment settledFetches by 1.

    5. If result is "download-total-exceeded", then set immediateFailure to true.

  6. Wait for settledFetches to be bgFetch’s records's size, or immediateFailure to be true.

  7. If immediateFailure is true, set bgFetch’s abort all flag.

    Note: The complete a record algorithm listens to this flag and terminates the fetch when set.

  8. Enqueue the following steps to swRegistration’s active background fetches edit queue:

    1. Let activeBgFetches be swRegistration’s active background fetches.

    2. Let id be bgFetch’s id.

    3. If activeBgFetches contains background fetch bgFetch, then remove activeBgFetches[id].

    4. Otherwise, set failureReason to "aborted".

      Note: This handles a race condition where abort() was successfully called but one of the fetches failed at the same time. If we’ve returned true from abort(), this ensures we fire the related abort event.

    5. If failureReason is not the empty string, then:

      1. Set bgFetch’s result to "failure".

      2. Set bgFetch’s failure reason to failureReason.

    6. Otherwise, set bgFetch’s result to "success".

    7. Update background fetch instances for bgFetch.

    8. Let eventName be the empty string.

    9. Let eventConstructor be null.

    10. If failureReason is "aborted", then:

      1. Set eventName to "backgroundfetchabort".

      2. Set eventConstructor to BackgroundFetchEvent.

    11. Otherwise, if failureReason is not the empty string, then:

      1. Set eventName to "backgroundfetchfail".

      2. Set eventConstructor to BackgroundFetchUpdateUIEvent.

    12. Otherwise:

      1. Set eventName to "backgroundfetchsuccess".

      2. Set eventConstructor to BackgroundFetchUpdateUIEvent.

    13. Fire a functional event named eventName using eventConstructor on swRegistration with the following properties:

      registration

      The result of getting a BackgroundFetchRegistration instance for bgFetch in the event object’s relevant Realm.

      Then run these steps with dispatchedEvent in parallel:

      1. Wait until dispatchedEvent is not active.

        ServiceWorker/1348.

      2. Unset bgFetch’s records available flag.

      3. Update background fetch instances for bgFetch.

4.2. Complete a record

Note: This algorithm manages the fetching of a background fetch record. One instance of this algorithm is started per background fetch record, but it is called recursively to retry fetches or to fetch the next part of a partial response.

To complete a record for bgFetch (a background fetch) and record (a background fetch record), run these steps:
  1. Let responseData be record’s response data.

  2. Let downloadTotal be bgFetch’s download total if it is not 0, otherwise infinity.

  3. Wait for bgFetch’s paused flag to be unset.

  4. Let request be a copy of record’s request.

    Note: At this point the request is entirely held in storage, even if it started as a stream.

  5. Set request’s keepalive flag.

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

  7. Let rangeStart be the length of responseData’s bytes.

  8. If rangeStart is not 0, then add a range header to request with rangeStart.

    Note: If the rangeStart is 0, a normal request is made. This allows the initial request to make use of content encoding, since Accept-Encoding: identity is added to requests with a range header.

  9. Let fetchAttemptComplete be false.

  10. Let lastTransmittedSize be 0.

  11. Fetch request.

    The remainder of this step uses fetch "callbacks" which currently queue tasks. This isn’t desirable or possible here, so let’s pretend that tasks aren’t queued. (issue)

    To process request body for request, run these steps:

    1. Let transmittedSize be request’s body's transmitted bytes.

    2. Increment bgFetch’s uploaded by transmittedSize minus lastTransmittedSize.

    3. Set lastTransmittedSize to transmittedSize.

    4. Update background fetch instances for bgFetch.

    To process response for response, run these steps:

    1. If response is a network error, then:

      1. If the resource is temporarily unavailable and request’s method is `GET`, then wait until the resource is not temporarily unavailable, then set fetchAttemptComplete to true and abort these steps.

        Note: If request’s method is not `GET`, reissuing the request may have unwanted side effects. If a standard method to resume requests becomes available, it’ll be adopted here.

      2. If response is an aborted network error, then set responseData’s result to "aborted", otherwise "fetch-error".

      3. Set fetchAttemptComplete to true, and abort these steps.

    2. If response’s status is 206, then:

      1. If validate a partial response for rangeStart, response, and responseData’s response returns invalid, then:

        1. Set responseData’s result to "fetch-error".

        2. Set fetchAttemptComplete to true.

        3. Terminate the ongoing fetch, and abort these steps.

    3. Otherwise:

      1. Set responseData’s result to "redundant".

      2. Set responseData to a new background fetch response.

      3. Set record’s response data to responseData.

        Note: The create record objects algorithm may hold a reference to the previous background fetch response.

      4. Update background fetch instances for bgFetch.

    4. If rangeStart is 0 or response’s status is not 206, then set responseData’s response to a copy of response except for its body.

    5. Let stream be the response body's stream.

    6. Whenever one or more bytes are transmitted from stream, let bytes be the transmitted bytes and run these steps:

      1. If bgFetch’s stored body bytes total plus the size of bytes is greater than downloadTotal, then:

        1. Cancel stream.

        2. Set responseData’s result to "download-total-exceeded", fetchAttemptComplete to true, and abort these steps.

      2. Append bytes to responseData’s bytes.

      3. If the previous step fails due to exceeding a quota limit, set responseData’s result to "quota-exceeded", fetchAttemptComplete to true, and abort these steps.

      4. Update background fetch instances for bgFetch.

    7. If at any point the bytes transmission for stream is done normally, then:

      1. If response’s status is 206, then:

        1. Let firstBytePos, lastBytePos, and completeLength be the result of extracting content-range values from response.

        2. If completeLength is not null, and equal to the length of responseData’s bytes, set responseData’s result to "success".

          Note: Although we ask for the whole resource, or the remainder of the resource, the server may not have returned the remainder, in which case we need to make an additional request.

      2. Otherwise, if response’s status is not an ok status, set responseData’s result to "bad-status".

      3. Otherwise, set responseData’s result to "success".

      4. Set fetchAttemptComplete to true.

    8. If at any point stream becomes errored, then:

      1. If the resource is temporarily unavailable and request’s method is `GET`, then wait until the resource is not temporarily unavailable, then set fetchAttemptComplete to true.

      2. Otherwise, set responseData’s result to "fetch-error" and fetchAttemptComplete to true.

  12. Let result be the empty string.

  13. Run these steps, but abort when bgFetch’s paused flag or abort all flag is set:

    1. Wait for fetchAttemptComplete to be true.

    2. Set result to responseData’s result.

  14. If aborted, then:

    1. If bgFetch’s paused flag is set, then assert request’s method is `GET`.

    2. If bgFetch’s abort all flag is set, then set responseData’s result to "aborted".

    3. Set result to responseData’s result.

      Note: The result is stored now, as terminating the fetch may change the result.

    4. Terminate the ongoing fetch.

  15. If result is the empty string, then complete a record for bgFetch and record.

4.3. Update background fetch instances

To update background fetch instances for bgFetch (a background fetch), enqueue the following steps to bgFetch’s update handling queue:
  1. Let downloaded be bgFetch’s stored body bytes total.

  2. Let uploaded be bgFetch’s uploaded.

  3. Let result be bgFetch’s result.

  4. Let failureReason be bgFetch’s failure reason.

  5. Let recordsAvailable be true if bgFetch’s records available flag is set, otherwise false.

  6. For each environment settings object env whose origin is equal to bgFetch’s service worker registration's scope URL's origin, queue a bgfetch task on env’s responsible event loop to run these steps:

    1. Let bgFetchRegistration be the instance of BackgroundFetchRegistration within the relevant realm whose background fetch is equal to bgFetch, or null if none exists.

      Note: There will be at most one per environment, due to the get a BackgroundFetchRegistration instance algorithm.

    2. If bgFetchRegistration is null, then abort these steps.

    3. If recordsAvailable is false and bgFetchRegistration’s records available flag is set, then unset bgFetchRegistration’s records available flag.

    4. If bgFetchRegistration’s result is not the empty string, then abort these steps.

      Note: This prevents progress being reported after the background fetch has settled. This is possible when the operation has aborted, but some fetches haven’t yet terminated.

    5. If all of the following are true:

      • bgFetchRegistration’s downloaded is equal to downloaded.

      • bgFetchRegistration’s uploaded is equal to uploaded.

      • bgFetchRegistration’s result is equal to result.

      • bgFetchRegistration’s failure reason is equal to failureReason.

      Then abort these steps.

    6. Set bgFetchRegistration’s downloaded to downloaded.

    7. Set bgFetchRegistration’s uploaded to uploaded.

    8. Set bgFetchRegistration’s result to result.

    9. Set bgFetchRegistration’s failure reason to failureReason.

    10. Fire an event named "progress" at bgFetchRegistration.

    I need to debounce this similar to how mouse move events debounce.

4.4. Fire a background fetch click event

To fire a background fetch click event for a bgFetch (a background fetch), fire a functional event named "backgroundfetchclick" using BackgroundFetchEvent on bgFetch’s service worker registration with the following properties:
registration

The result of getting a BackgroundFetchRegistration instance for bgFetch in the event object’s relevant Realm.

4.5. Get a BackgroundFetchRegistration instance

Note: This algorithm ensures the same BackgroundFetchRegistration instance is returned for a given background fetch throughout the life of a BackgroundFetchManager. It’s okay for browsers to optimise this, as long as there’s no way to tell that more than one instance has been created for a given background fetch (e.g. through equality, expandos, or weakly-associated data).

To get a BackgroundFetchRegistration instance for a bgFetch (a background fetch) in realm (a Realm), run these steps:
  1. Let instancesMap be the BackgroundFetchRegistration instances of the only instance of BackgroundFetchManager within this realm.

  2. If instancesMap[bgFetch] exists, then return instancesMap[bgFetch].

  3. Let instance be a new BackgroundFetchRegistration in realm, with its background fetch set to bgFetch.

  4. Set instancesMap[bgFetch] to instance.

  5. Return instance.

4.6. Validate a partial response

Note: This algorithm checks if a partial response reasonably matches what was requested, and optionally checks if it should be combined with a previous response.

To validate a partial response for an expectedRangeStart (a number), a partialResponse (a response), and an optional previousResponse (a response or null, null unless otherwise specified), run these steps:
  1. Assert: partialResponse’s status is 206.

  2. Let responseFirstBytePos, responseLastBytePos, and responseCompleteLength be the result of extracting content-range values from partialResponse. If this fails, then return invalid.

  3. If responseFirstBytePos does not equal expectedRangeStart, then return invalid.

  4. If previousResponse is not null, then:

    1. If previousResponse’s header list contains `ETag`, and header equality for `ETag`, previousResponse’s header list, and partialResponse’s header list is false, then return invalid.

    2. If previousResponse’s header list contains `Last-Modified`, and header equality for `Last-Modified`, previousResponse’s header list, and partialResponse’s header list is false, then return invalid.

    3. If previousResponse’s status is 206, then:

      1. Let previousResponseFirstBytePos, previousResponseLastBytePos, and previousResponseCompleteLength be the result of extracting content-range values from previousResponse. If this fails, then return invalid.

      2. If previousResponseCompleteLength is not null, and responseCompleteLength does not equal previousResponseCompleteLength, then return invalid.

  5. Return valid.

4.7. Extract content-range values

Note: This algorithm parses `Content-Range` as a single byte content-range and extracts the values.

To extract content-range values from a response (a response), run these steps:
  1. If response’s header list does not contain `Content-Range`, then return failure.

  2. Let contentRangeValue be the value of the first header whose name is a byte-case-insensitive match for `Content-Range` in response’s header list.

  3. If parsing contentRangeValue per single byte content-range fails, then return failure.

  4. Let firstBytePos be the portion of contentRangeValue named first-byte-pos when parsed as single byte content-range, parsed as an integer.

  5. Let lastBytePos be the portion of contentRangeValue named last-byte-pos when parsed as single byte content-range, parsed as an integer.

  6. Let completeLength be the portion of contentRangeValue named complete-length when parsed as single byte content-range.

  7. If completeLength is "*", then set completeLength to null, otherwise set completeLength to completeLength parsed as an integer.

  8. Return firstBytePos, lastBytePos, and completeLength.

Parsing as an integer infra/189.

4.8. Header equality

Note: This algorithm returns true if two header lists contain the same value for a given header name, or neither has a header of that name.

To determine header equality for a headerName (a byte sequence), in headerList1 (a header list), and headerList2 (a header list), run these steps:
  1. If headerList1 does not contain headerName, and headerList2 does not contain headerName, then return true.

  2. If headerList1 contains headerName, and headerList2 contains headerName, then:

    1. Let header1Value be the value of the first header whose name is a byte-case-insensitive match for headerName in headerList1.

    2. Let header2Value be the value of the first header whose name is a byte-case-insensitive match for headerName in headerList2.

    3. If header1Value is identical to header2Value, then return true, otherwise return false.

  3. Return false.

4.9. Create record objects

Note: This algorithm creates platform objects for background fetch records. It also manages the streaming of the response from the stored bytes. The background fetch operation may still be in progress at this point.

To create record objects from records (a list of background fetch records) in realm (a Realm), run these steps:

All platform objects must be created in realm.

  1. Let recordObjects be a new list.

  2. For each record of records:

    1. Let responseData be record’s response data.

    2. Let recordObject be a new BackgroundFetchRecord.

    3. Set recordObject’s responseReady to a new promise.

    4. Let requestObject be a new Request object with the following set:

      Request

      A copy of record’s request.

      Headers

      A new Headers object associated with this Request's request's header list.

    5. Set recordObject’s request to requestObject.

    6. Let transmittedBytes be 0.

    7. Let stream be a new readable stream with a pull action that returns a new promise promise and runs these steps in parallel:

      1. Wait for the length of responseData’s bytes to be greater than transmittedBytes, or responseData’s result not to be the empty string.

      2. Let bytes be null.

      3. If the length of responseData’s bytes is greater than transmittedBytes and responseData may be exposed, then:

        1. Set bytes to a user agent determined slice of responseData’s bytes, starting from an offset of transmittedBytes.

          Note: This allows the user agent to stream the resource from storage at an appropriate rate.

        2. Increment transmittedBytes by byteslength.

      4. Queue a task in stream’s relevant settings object's responsible event loop using the networking task source to run these steps:

        1. If bytes is not null, then:

          1. Let array be a new Uint8Array wrapping a new ArrayBuffer of bytes.

          2. Enqueue array into stream.

        2. If responseData may be exposed, responseData’s result is not the empty string, and transmittedBytes is the length of responseData’s bytes, then close stream.

        3. Otherwise, if responseData’s result is "aborted", then error stream with an AbortError DOMException.

        4. Otherwise, if responseData may not be exposed, then error stream with a TypeError.

        5. Resolve promise.

    8. Run these steps in parallel:

      1. Wait for responseData’s response to not be not-null.

      2. If responseData may be exposed, then:

        1. Let response be a copy of responseData’s response.

        2. Delete `Content-Range` from response’s header list.

        3. Delete `Content-Length` from response’s header list.

        4. Let body be a new body with stream set to stream.

        5. Set response’s body to body.

        6. Queue a task in recordObject’s relevant settings object's responsible event loop using the networking task source to run these steps:

          1. Let responseObject be a new Response object with the following set:

            Response

            response.

            Headers

            A new Headers object associated with this Response's response's header list.

          2. Resolve recordObject’s responseReady with responseObject.

      3. Otherwise, if responseData’s result is "aborted", then reject recordObject’s responseReady with an AbortError DOMException.

      4. Otherwise, reject recordObject’s responseReady with a TypeError.

    9. Append recordObject to recordObjects.

  3. Return recordObjects.

4.10. Contains background fetch

To determine whether a map (a map) contains background fetch bgFetch (a background fetch), run these steps:
  1. Let id be bgFetch’s id.

  2. If map[id] does not exist, then return false.

  3. If map[id] does not equal bgFetch, then return false.

  4. Return true.

To determine whether a map (a map) does not contain background fetch bgFetch (a background fetch), run these steps:
  1. If map contains background fetch bgFetch, then return false.

  2. Return true.

5. Header syntax

The following is HTTP ABNF for a single byte content-range:

"bytes=" first-byte-pos "-" last-byte-pos "/" complete-length
first-byte-pos = 1*DIGIT
last-byte-pos  = 1*DIGIT
complete-length = ( 1*DIGIT / "*" )

Note: This is a subset of what RFC 7233 allows.

The above as a railroad diagram:

6. API

6.1. Extensions to ServiceWorkerGlobalScope

partial interface ServiceWorkerGlobalScope {
  attribute EventHandler onbackgroundfetchsuccess;
  attribute EventHandler onbackgroundfetchfail;
  attribute EventHandler onbackgroundfetchabort;
  attribute EventHandler onbackgroundfetchclick;
};

6.1.1. Events

The following is the event handler (and its corresponding event handler event type) that must be supported, as event handler IDL attributes, by all objects implementing ServiceWorker interface:

event handler event type event handler Interface
backgroundfetchsuccess onbackgroundfetchsuccess BackgroundFetchUpdateUIEvent
backgroundfetchfail onbackgroundfetchfail BackgroundFetchUpdateUIEvent
backgroundfetchabort onbackgroundfetchabort BackgroundFetchEvent
backgroundfetchclick onbackgroundfetchclick BackgroundFetchEvent

6.2. Extensions to ServiceWorkerRegistration

partial interface ServiceWorkerRegistration {
  readonly attribute BackgroundFetchManager backgroundFetch;
};

A ServiceWorkerRegistration has a background fetch manager (a BackgroundFetchManager), initially a new BackgroundFetchManager whose service worker registration is the context object's service worker registration.

The backgroundFetch attribute’s getter must return the context object's background fetch manager.

6.3. BackgroundFetchManager

[Exposed=(Window,Worker)]
interface BackgroundFetchManager {
  Promise<BackgroundFetchRegistration> fetch(DOMString id, (RequestInfo or sequence<RequestInfo>) requests, optional BackgroundFetchOptions options);
  Promise<BackgroundFetchRegistration?> get(DOMString id);
  Promise<sequence<DOMString>> getIds();
};

dictionary BackgroundFetchUIOptions {
  sequence<ImageResource> icons;
  DOMString title;
};

dictionary BackgroundFetchOptions : BackgroundFetchUIOptions {
  unsigned long long downloadTotal = 0;
};

A BackgroundFetchManager has:

6.3.1. fetch()

The fetch(id, requests, options) method, when invoked, run these steps:
  1. Let registration be the context object's service worker registration.

  2. Let records be a new list.

  3. Let uploadTotal be 0.

  4. If requests is a RequestInfo, set requests to « requests ».

  5. If requests is empty, then return a promise rejected with a TypeError.

  6. For each request of requests:

    1. Let internalRequest be the request of the result of invoking the Request constructor with request. If this throws an exception, return a promise rejected with the exception.

    2. If internalRequest’s mode is "no-cors", then return a promise rejected with a TypeError.

    3. Set internalRequest’s client to null.

    4. Let record be a new background fetch record.

    5. Set record’s request to internalRequest.

    6. Append record to records.

  7. Let promise be a new promise.

  8. Enqueue the following steps to registration’s active background fetches edit queue:

    1. Let permission be the permission state for a PermissionDescriptor with name "background-fetch", and the context object's relevant settings object.

    2. If permission is "denied", then reject promise with a NotAllowedError DOMException and abort these steps.

    3. Let bgFetchMap be registration’s active background fetches.

    4. If registration’s active worker is null, then reject promise with a TypeError and abort these steps.

    5. If bgFetchMap[id] exists, reject promise with a TypeError and abort these steps.

    6. Let requestBodiesRemaining be the size of requests.

    7. Let requestReadFailed be false.

    8. For each request of requests:

      1. If request’s body is null, then continue.

      2. Let stream be request’s body's stream.

      3. Run these steps in parallel:

        1. Run these steps but abort when requestReadFailed is true:

          1. Wait for request’s body.

          2. If stream has errored, then set requestReadFailed to true.

          Note: This ensures we have a copy of the request bytes before resolving.

        2. If aborted and stream is readable, then error stream with an AbortError DOMException and abort these steps.

        3. Increment uploadTotal by request’s body's total bytes.

        4. Decrement requestBodiesRemaining by 1.

    9. If at any point storing requests fails due to exceeding a quota limit, reject promise with a QuotaExceededError DOMException and abort these steps.

    10. Wait for requestBodiesRemaining to be 0, or requestReadFailed to be true.

    11. If requestReadFailed is true, then reject promise with a TypeError and abort these steps.

    12. Let bgFetch be a new background fetch with:

      id

      id.

      records

      records.

      download total

      optionsdownloadTotal member.

      upload total

      uploadTotal.

      icons

      optionsicons member if present, otherwise an empty list.

      title

      optionstitle member if present, otherwise the empty string.

      service worker registration

      registration.

    13. Set bgFetchMap[id] to bgFetch.

    14. Queue a bgfetch task to run these steps:

      1. Resolve promise with the result of getting a BackgroundFetchRegistration instance for bgFetch in the context object's relevant Realm.

    15. In parallel, display bgFetch from the context object's relevant settings object.

    16. In parallel, perform a background fetch with bgFetch.

  9. Return promise.

6.3.2. get()

The get(id) method, when invoked, must return a new promise promise and run these steps in parallel:
  1. Let registration be the context object's associated service worker registration.

  2. Let bgFetch be registration’s active background fetches[id].

  3. If bgFetch is undefined, then resolve promise with undefined and abort these steps.

  4. Enqueue the following steps to bgFetch’s update handling queue:

    1. Queue a bgfetch task task to run these steps:

      1. Let bgFetchRegistration be the result of getting a BackgroundFetchRegistration instance for bgFetch in the context object's relevant Realm.

      2. Resolve promise with bgFetchRegistration.

    2. Wait for task to complete.

      Note: This ensures the potential new instance of BackgroundFetchRegistration doesn’t miss any progress events.

6.3.3. getIds()

The getIds() method, when invoked, must return a new promise promise and run these steps in parallel:
  1. Let registration be the context object's associated service worker registration.

  2. Resolve promise with the result of getting the keys of registration’s active background fetches.

6.4. BackgroundFetchRegistration

[Exposed=(Window,Worker)]
interface BackgroundFetchRegistration : EventTarget {
  readonly attribute DOMString id;
  readonly attribute unsigned long long uploadTotal;
  readonly attribute unsigned long long uploaded;
  readonly attribute unsigned long long downloadTotal;
  readonly attribute unsigned long long downloaded;
  readonly attribute BackgroundFetchResult result;
  readonly attribute BackgroundFetchFailureReason failureReason;
  readonly attribute boolean recordsAvailable;

  attribute EventHandler onprogress;

  Promise<boolean> abort();
  Promise<BackgroundFetchRecord> match(RequestInfo request, optional CacheQueryOptions options);
  Promise<sequence<BackgroundFetchRecord>> matchAll(optional RequestInfo request, optional CacheQueryOptions options);
};

enum BackgroundFetchResult { "", "success", "failure" };

enum BackgroundFetchFailureReason {
  // The background fetch has not completed yet, or was successful.
  "",
  // The operation was aborted by the user, or abort() was called.
  "aborted",
  // A response had a not-ok-status.
  "bad-status",
  // A fetch failed for other reasons, e.g. CORS, MIX, an invalid partial response,
  // or a general network failure for a fetch that cannot be retried.
  "fetch-error",
  // Storage quota was reached during the operation.
  "quota-exceeded",
  // The provided downloadTotal was exceeded.
  "download-total-exceeded"
};

A BackgroundFetchRegistration instance has:

Note: The above values are copied so they’re available synchronously.

The id attribute’s getter must return the context object's id.

The uploadTotal attribute’s getter must return the context object's upload total.

The downloadTotal attribute’s getter must return the context object's download total.

The uploaded attribute’s getter must return the context object's uploaded.

The downloaded attribute’s getter must return the context object's downloaded.

The result attribute’s getter must return the context object's result.

The failureReason attribute’s getter must return the context object's failure reason.

The recordsAvailable attribute’s getter must return true if the context object's records available flag is set, otherwise false.

6.4.1. Events

The onprogress event handler has the event handler event type of progress.

The progress event uses the Event interface.

6.4.2. abort()

The abort() method, when invoked, must return a new promise promise and run these steps in parallel:
  1. Let bgFetch be the context object's associated background fetch.

  2. Let swRegistration be bgFetch’s service worker registration.

  3. Enqueue the following steps to swRegistration’s active background fetches edit queue:

    1. Let activeBgFetches be swRegistration’s active background fetches.

    2. Let id be bgFetch’s id.

    3. If activeBgFetches does not contain background fetch bgFetch, then resolve promise with false and abort these steps.

    4. Remove activeBgFetches[id].

    5. Resolve promise with true.

    6. Set bgFetch’s abort all flag.

6.4.3. match()

The match(request, options) method, when invoked, must run these steps:
  1. Let promise be the result of calling the algorithm matchAll() passing request and options.

  2. Return the result of transforming promise with a fulfilment handler that, when called with argument matches, returns matches[0].

Note: User agents are encouraged to optimise the above so it’s faster than calling matchAll().

6.4.4. matchAll()

The matchAll(request, options) method, when invoked, must run these steps:
  1. If the context object's records available flag is unset, return a promise rejected with an InvalidStateError DOMException.

  2. Let promise be a new promise.

  3. Run these steps in parallel:

    1. Let matchingRecords be an empty list.

    2. For each record of context object's background fetch's records:

      1. If request matches cached item for request, record’s request, record’s response data's response, and options returns true, then append record to matchingRecords.

    3. Queue a bgfetch task to resolve promise with the result of creating record objects from matchingRecords in the context object's relevant Realm.

  4. Return promise.

6.5. BackgroundFetchRecord

[Exposed=(Window,Worker)]
interface BackgroundFetchRecord {
  readonly attribute Request request;
  readonly attribute Promise<Response> responseReady;
};
A BackgroundFetchRecord has:

The request attribute’s getter must return the context object's request.

The responseReady attribute’s getter must return the context object's response promise.

6.6. BackgroundFetchEvent

[Constructor(DOMString type, BackgroundFetchEventInit init), Exposed=ServiceWorker]
interface BackgroundFetchEvent : ExtendableEvent {
  readonly attribute BackgroundFetchRegistration registration;
};

dictionary BackgroundFetchEventInit : ExtendableEventInit {
  required BackgroundFetchRegistration registration;
};
A BackgroundFetchEvent has a background fetch (a background fetch), initially the background fetch of the value registration was initialized to.

The registration attribute must return the value it was initialized to.

6.7. BackgroundFetchUpdateUIEvent

[Constructor(DOMString type, BackgroundFetchEventInit init), Exposed=ServiceWorker]
interface BackgroundFetchUpdateUIEvent : BackgroundFetchEvent {
  Promise<void> updateUI(optional BackgroundFetchUIOptions options);
};

A BackgroundFetchUpdateUIEvent has an UI updated flag which is initially unset.

6.7.1. updateUI()

The updateUI(options) method, when invoked, must return a new promise promise and run these steps in parallel:
  1. If any of the following is true:

    Throw an InvalidStateError DOMException.

  2. Set the context object's UI updated flag.

  3. If options is null, return.

  4. Let bgFetch be the context object's background fetch.

  5. If options’s icons member is present, set bgFetch’s icons to options’s icons.

  6. If options’s title member is present, set bgFetch’s title to options’s title.

  7. Resolve promise.

7. Automation

For the purposes of user-agent automation and application testing, this document defines the following extension command for the [WebDriver] specification.

7.1. Click

Method URI Template
POST /session/{session id}/backgroundfetch/{id}/click

The background fetch click extension command simulates the user activating the display of a background fetch. The remote end steps are:

  1. If the current top-level browsing context is no longer open, then return a WebDriver error with WebDriver error code no such window.

  2. Let pageURL be the current top-level browsing context's active document's URL.

  3. Let swRegistration be the matching service worker registration for pageURL.

  4. If swRegistration is null, then return a WebDriver error with status 400 and JSON error code "invalid service worker state".

  5. Let bgFetch be the newest background fetch with an id of url variable id and a service worker registration of swRegistration, or null if none exists.

  6. If bgFetch is null, then return a WebDriver error with status 404 and JSON error code "background fetch not found".

  7. Fire a background fetch click event for bgFetch.

  8. Return WebDriver success.

8. Privacy and bandwidth usage

Fetches can be large and take a long time to complete. During this time the user will be fetching data from one or more servers. The user’s IP address, which may change during the operation, could be used to track the user’s location over time.

To mitigate this, the steps for displaying a background fetch require:

The steps also allow a user agent to pause the background fetch if the user is on a metered connection.

All data stored is associated with a particular service worker registration. Clearing a service worker registration will clear all associated background fetches.

Conformance

Conformance requirements are expressed with a combination of descriptive assertions and RFC 2119 terminology. The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in the normative parts of this document are to be interpreted as described in RFC 2119. However, for readability, these words do not appear in all uppercase letters in this specification.

All of the text of this specification is normative except sections explicitly marked as non-normative, examples, and notes. [RFC2119]

Examples in this specification are introduced with the words “for example” or are set apart from the normative text with class="example", like this:

This is an example of an informative example.

Informative notes begin with the word “Note” and are set apart from the normative text with class="note", like this:

Note, this is an informative note.

Index

Terms defined by this specification

Terms defined by reference

References

Normative References

[DOM]
Anne van Kesteren. DOM Standard. Living Standard. URL: https://dom.spec.whatwg.org/
[ECMASCRIPT]
ECMAScript Language Specification. URL: https://tc39.github.io/ecma262/
[FETCH]
Anne van Kesteren. Fetch Standard. Living Standard. URL: https://fetch.spec.whatwg.org/
[HTML]
Anne van Kesteren; et al. HTML Standard. Living Standard. URL: https://html.spec.whatwg.org/multipage/
[INFRA]
Anne van Kesteren; Domenic Denicola. Infra Standard. Living Standard. URL: https://infra.spec.whatwg.org/
[PERMISSIONS]
Mounir Lamouri; Marcos Caceres; Jeffrey Yasskin. Permissions. 25 September 2017. WD. URL: https://www.w3.org/TR/permissions/
[PROMISES-GUIDE]
Domenic Denicola. Writing Promise-Using Specifications. 16 February 2016. Finding of the W3C TAG. URL: https://www.w3.org/2001/tag/doc/promises-guide
[RFC2119]
S. Bradner. Key words for use in RFCs to Indicate Requirement Levels. March 1997. Best Current Practice. URL: https://tools.ietf.org/html/rfc2119
[SERVICE-WORKERS-1]
Alex Russell; et al. Service Workers 1. 2 November 2017. WD. URL: https://www.w3.org/TR/service-workers-1/
[URL]
Anne van Kesteren. URL Standard. Living Standard. URL: https://url.spec.whatwg.org/
[WebDriver]
Simon Stewart; David Burns. WebDriver. 5 June 2018. REC. URL: https://www.w3.org/TR/webdriver1/
[WebIDL]
Cameron McCormack; Boris Zbarsky; Tobie Langel. Web IDL. 15 December 2016. ED. URL: https://heycam.github.io/webidl/

IDL Index

partial interface ServiceWorkerGlobalScope {
  attribute EventHandler onbackgroundfetchsuccess;
  attribute EventHandler onbackgroundfetchfail;
  attribute EventHandler onbackgroundfetchabort;
  attribute EventHandler onbackgroundfetchclick;
};

partial interface ServiceWorkerRegistration {
  readonly attribute BackgroundFetchManager backgroundFetch;
};

[Exposed=(Window,Worker)]
interface BackgroundFetchManager {
  Promise<BackgroundFetchRegistration> fetch(DOMString id, (RequestInfo or sequence<RequestInfo>) requests, optional BackgroundFetchOptions options);
  Promise<BackgroundFetchRegistration?> get(DOMString id);
  Promise<sequence<DOMString>> getIds();
};

dictionary BackgroundFetchUIOptions {
  sequence<ImageResource> icons;
  DOMString title;
};

dictionary BackgroundFetchOptions : BackgroundFetchUIOptions {
  unsigned long long downloadTotal = 0;
};

[Exposed=(Window,Worker)]
interface BackgroundFetchRegistration : EventTarget {
  readonly attribute DOMString id;
  readonly attribute unsigned long long uploadTotal;
  readonly attribute unsigned long long uploaded;
  readonly attribute unsigned long long downloadTotal;
  readonly attribute unsigned long long downloaded;
  readonly attribute BackgroundFetchResult result;
  readonly attribute BackgroundFetchFailureReason failureReason;
  readonly attribute boolean recordsAvailable;

  attribute EventHandler onprogress;

  Promise<boolean> abort();
  Promise<BackgroundFetchRecord> match(RequestInfo request, optional CacheQueryOptions options);
  Promise<sequence<BackgroundFetchRecord>> matchAll(optional RequestInfo request, optional CacheQueryOptions options);
};

enum BackgroundFetchResult { "", "success", "failure" };

enum BackgroundFetchFailureReason {
  // The background fetch has not completed yet, or was successful.
  "",
  // The operation was aborted by the user, or abort() was called.
  "aborted",
  // A response had a not-ok-status.
  "bad-status",
  // A fetch failed for other reasons, e.g. CORS, MIX, an invalid partial response,
  // or a general network failure for a fetch that cannot be retried.
  "fetch-error",
  // Storage quota was reached during the operation.
  "quota-exceeded",
  // The provided downloadTotal was exceeded.
  "download-total-exceeded"
};

[Exposed=(Window,Worker)]
interface BackgroundFetchRecord {
  readonly attribute Request request;
  readonly attribute Promise<Response> responseReady;
};

[Constructor(DOMString type, BackgroundFetchEventInit init), Exposed=ServiceWorker]
interface BackgroundFetchEvent : ExtendableEvent {
  readonly attribute BackgroundFetchRegistration registration;
};

dictionary BackgroundFetchEventInit : ExtendableEventInit {
  required BackgroundFetchRegistration registration;
};

[Constructor(DOMString type, BackgroundFetchEventInit init), Exposed=ServiceWorker]
interface BackgroundFetchUpdateUIEvent : BackgroundFetchEvent {
  Promise<void> updateUI(optional BackgroundFetchUIOptions options);
};

Issues Index

manifest/pull/710.
ServiceWorker/1348.
The remainder of this step uses fetch "callbacks" which currently queue tasks. This isn’t desirable or possible here, so let’s pretend that tasks aren’t queued. (issue)
I need to debounce this similar to how mouse move events debounce.
Parsing as an integer infra/189.
ServiceWorker/1348.