后台获取

社区组草案报告,

此版本:
https://wicg.github.io/background-fetch/
问题追踪:
GitHub
规范中的内联问题
编辑者:
(谷歌)
(谷歌)

摘要

一个处理后台大规模上传/下载的 API,提供用户可见性。

本文档的状态

该规范由 Web 平台孵化社区组 发布。 它不是 W3C 标准,也不在 W3C 标准轨道上。 请注意,根据 W3C 社区贡献者许可协议(CLA),有有限的选择退出权,其他条件适用。 了解更多关于 W3C 社区和业务组的信息。

1. 简介

Service Worker 能够获取和缓存资源,其大小仅受限于 源存储。然而,如果用户离开该站点或关闭浏览器,Service Worker可能会被 终止。即使有传递给 waitUntil() 的未完成 Promise——如果它在几分钟内没有解析,浏览器可能会认为这是对 Service Worker 的滥用,并终止该进程。

这对电池寿命和隐私非常有利,但却使得下载和缓存诸如播客和电影等大文件,以及上传视频和图片变得困难。

本规范旨在:

2. 领域

所有平台对象均在 上下文对象相关领域 中创建,除非另有规定。

3. 基础设施

当用户代理认为资源可能很快可用时,资源被认为是 暂时不可用。原因可能包括:

后台获取任务源 是一个 任务源

要在一个可选的 eventLoop(一个 事件循环,默认为调用者的 上下文对象相关设置对象负责的事件循环)上排队一个 steps(步骤),请使用 后台获取任务源eventLoop 上排队一个任务以运行 steps

3.1. Service Worker 注册的扩展

Service Worker 注册 另外包含:

3.2. 后台获取

后台获取 包含以下内容:

要获取 后台获取 的 (bgFetch) 已存储主体字节总数,请执行以下步骤:
  1. total 为 0。

  2. 对于每一个 record,在 bgFetch记录中:

    1. 通过 长度,增加 record响应数据字节total

  3. 返回 total

3.2.1. 显示

要为给定的 environment(一个 环境设置对象)显示 后台获取 (bgFetch),用户代理必须展示一个遵循以下规则的用户界面:

permission权限状态,其对应于 PermissionDescriptor 中具有 name 值为 "background-fetch"environment。如果 permission"prompt",则用户代理可能会在该算法开始时设置 bgFetch暂停标志。用户代理应允许用户接受后台获取(取消设置 bgFetch暂停标志),或拒绝后台获取(设置 bgFetch中止所有标志)。用户代理还可以提供始终允许和始终拒绝选项,可以用作此权限的 用户意图的新增信息

如果用户使用的是计量连接,或后台获取是在后台启动的,用户代理还可以考虑设置 bgFetch暂停标志

3.3. 后台获取记录

后台获取记录 包含以下内容:

3.4. 后台获取响应

后台获取响应 包含以下内容:

如果 响应暴露 状态为结果是空字符串、"success""bad-status",则可以暴露该响应。

4. 算法

4.1. 执行后台获取

注意: 这是管理后台获取的“后台”部分的算法。每个 后台获取 只运行该算法的一个实例。

要为 bgFetch(一个 后台获取)执行后台获取,请运行以下步骤:
  1. swRegistrationbgFetchService Worker 注册

  2. settledFetches 为 0。

  3. immediateFailure 为 false。

  4. failureReason 为空字符串。

  5. 对于每一个 record,在 bgFetch记录 中,并行 运行以下步骤:

    1. 完成 记录,用于 bgFetchrecord

    2. resultrecord响应数据结果

    3. 如果 failureReason 不是空字符串:

      1. 断言:result 不是 "redundant"

      2. failureReason 设置为 result

    4. settledFetches 增加 1。

    5. 如果 result"download-total-exceeded",则将 immediateFailure 设置为 true。

  6. 等待 settledFetches 等于 bgFetch记录大小,或 immediateFailure 为 true。

  7. 如果 immediateFailure 为 true,则将 bgFetch中止所有标志 设置为 true。

    注意: 完成记录 算法会监听此标志,并在设置时终止获取。

  8. 排队以下步骤swRegistration活跃后台获取编辑队列

    1. activeBgFetchesswRegistration活跃后台获取

    2. idbgFetchid

    3. 如果 activeBgFetches 包含后台获取 bgFetch,则移除 activeBgFetches[id]。

    4. 否则,将 failureReason 设置为 "aborted"

      注意: 这解决了一个竞争条件,在该条件下 abort() 已成功调用,但其中一个获取操作同时失败。如果我们已经从 abort() 返回 true,这将确保我们触发相关的中止事件。

    5. 如果 failureReason 不是空字符串,则:

      1. bgFetch结果 设置为 "failure"

      2. bgFetch失败原因 设置为 failureReason

    6. 否则,将 bgFetch结果 设置为 "success"

    7. 更新后台获取实例 用于 bgFetch

    8. eventName 为空字符串。

    9. eventConstructor 为 null。

    10. 如果 failureReason"aborted",则:

      1. eventName 设置为 "backgroundfetchabort"。

      2. eventConstructor 设置为 BackgroundFetchEvent

    11. 否则,如果 failureReason 不是空字符串,则:

      1. eventName 设置为 "backgroundfetchfail"。

      2. eventConstructor 设置为 BackgroundFetchUpdateUIEvent

    12. 否则:

      1. eventName 设置为 "backgroundfetchsuccess"。

      2. eventConstructor 设置为 BackgroundFetchUpdateUIEvent

    13. 触发一个功能事件,命名为 eventName,使用 eventConstructorswRegistration 上触发,属性如下:

      registration

      获取一个 BackgroundFetchRegistration 实例,用于 bgFetch 在事件对象的 相关领域

      然后使用 dispatchedEvent 运行以下步骤,并行

      1. 等待直到 dispatchedEvent 不再 活跃

        ServiceWorker/1348

      2. 取消设置 bgFetch记录可用标志

      3. 更新后台获取实例,用于 bgFetch

4.2. 完成记录

注意: 此算法管理 后台获取记录 的获取。每个 后台获取记录 启动此算法的一个实例,但它会递归调用以重试获取或获取部分响应的下一部分。

要为 bgFetch(一个 后台获取)和 record(一个 后台获取记录)完成记录,请运行以下步骤:
  1. responseDatarecord响应数据

  2. downloadTotalbgFetch下载总量(如果不是 0),否则为 infinity。

  3. 等待 bgFetch暂停标志 被取消设置。

  4. requestrecord请求 的副本。

    注意: 此时,请求完全存储在存储中,即使它最初是流。

  5. 设置 requestkeepalive 标志

  6. requestService Worker 模式 设置为 "none"。

  7. rangeStartresponseData字节长度

  8. 如果 rangeStart 不为 0,则 添加 range 标头request,使用 rangeStart

    注意: 如果 rangeStart 为 0,则进行正常请求。这允许初始请求利用内容编码,因为带有 range 标头的请求会添加 Accept-Encoding: identity

  9. fetchAttemptComplete 为 false。

  10. lastTransmittedSize 为 0。

  11. 获取 request

    本步骤的其余部分使用 fetch "回调",目前将任务排队。这在这里不可取也不可能,所以假设任务没有排队。(问题

    request 处理请求体,运行以下步骤:

    1. transmittedSizerequest请求体已传输字节

    2. bgFetch已上传 增加 transmittedSize 减去 lastTransmittedSize

    3. lastTransmittedSize 设置为 transmittedSize

    4. 更新后台获取实例,用于 bgFetch

    response 处理响应,运行以下步骤:

    1. 如果 response 是一个 网络错误,则:

      1. 如果资源是 暂时不可用,并且 request方法 是 `GET`,则等待资源不再 暂时不可用,然后将 fetchAttemptComplete 设置为 true 并中止这些步骤。

        注意: 如果 request方法 不是 `GET`,重新发出请求可能会产生不良副作用。如果有一种标准方法来恢复请求,它将在此采用。

      2. 如果 response 是一个 中止的网络错误,则将 responseData结果 设置为 "aborted",否则设置为 "fetch-error"

      3. fetchAttemptComplete 设置为 true,并中止这些步骤。

    2. 如果 response状态206,则:

      1. 如果 验证部分响应,对 rangeStartresponseresponseData响应 返回无效,则:

        1. responseData结果 设置为 "fetch-error"

        2. fetchAttemptComplete 设置为 true。

        3. 终止正在进行的获取,并中止这些步骤。

    3. 否则:

      1. responseData结果 设置为 "redundant"

      2. responseData 设置为一个新的 后台获取响应

      3. record响应数据 设置为 responseData

        注意: 创建记录对象 算法可能会保留对先前 后台获取响应 的引用。

      4. 更新后台获取实例,用于 bgFetch

    4. 如果 rangeStart 为 0 或 response状态 不是 206,则将 responseData响应 设置为 response 的副本,除了它的 主体

    5. streamresponse主体

    6. 每当从 stream 传输一个或多个字节时,令 bytes 为已传输字节并运行以下步骤:

      1. 如果 bgFetch已存储主体字节总量 加上 bytes 的大小大于 downloadTotal,则:

        1. 取消 stream

        2. responseData结果 设置为 "download-total-exceeded",将 fetchAttemptComplete 设置为 true,并中止这些步骤。

      2. bytes 附加到 responseData字节

      3. 如果上一步因超出配额限制而失败,将 responseData结果 设置为 "quota-exceeded",将 fetchAttemptComplete 设置为 true,并中止这些步骤。

      4. 更新后台获取实例,用于 bgFetch

    7. 如果 stream 的字节传输在任何时候正常完成,则:

      1. 如果 response状态206,则:

        1. firstBytePoslastBytePoscompleteLength提取内容范围值 的结果。

        2. 如果 completeLength 不是 null,并且等于 responseData字节长度,则将 responseData结果 设置为 "success"

          注意: 虽然我们要求整个资源或资源的剩余部分,但服务器可能没有返回剩余部分,在这种情况下,我们需要再发出一个请求。

      2. 否则,如果 response状态 不是 ok 状态,将 responseData结果 设置为 "bad-status"

      3. 否则,将 responseData结果 设置为 "success"

      4. fetchAttemptComplete 设置为 true。

    8. 如果 stream 在任何时候 出错,则:

      1. 如果资源是 暂时不可用,并且 request方法 是 `GET`,则等待资源不再 暂时不可用,然后将 fetchAttemptComplete 设置为 true。

      2. 否则,将 responseData结果 设置为 "fetch-error",并将 fetchAttemptComplete 设置为 true。

  12. result 为空字符串。

  13. 运行以下步骤,但 在中止时 bgFetch暂停标志中止所有标志 被设置:

    1. 等待 fetchAttemptComplete 为 true。

    2. result 设置为 responseData结果

  14. 如果中止,则:

    1. 如果 bgFetch暂停标志 被设置,则断言 request方法 为 `GET`。

    2. 如果 bgFetch中止所有标志 被设置,则将 responseData结果 设置为 "aborted"

    3. result 设置为 responseData结果

      注意: 结果现在被存储,因为终止获取可能会改变结果。

    4. 终止正在进行的获取。

  15. 如果 result 为空字符串,则 完成记录,用于 bgFetchrecord

4.3. 更新后台获取实例

更新后台获取实例,用于 bgFetch(一个 后台获取),请将以下步骤排队bgFetch更新处理队列
  1. downloadedbgFetch已存储主体字节总量

  2. uploadedbgFetch已上传

  3. resultbgFetch结果

  4. failureReasonbgFetch失败原因

  5. 如果 bgFetch记录可用标志 被设置,则令 recordsAvailable 为 true,否则为 false。

  6. 对于每个 环境设置对象 env,其 origin 等于 bgFetchService Worker 注册范围 URLorigin排队一个后台获取任务,用于 env负责的事件循环,运行以下步骤:

    1. bgFetchRegistrationBackgroundFetchRegistration 的实例,位于 相关领域,其 后台获取 等于 bgFetch,如果不存在则为 null。

      注意: 每个环境最多只有一个,因 获取 BackgroundFetchRegistration 实例 算法所致。

    2. 如果 bgFetchRegistration 为 null,则中止这些步骤。

    3. 如果 recordsAvailable 为 false,且 bgFetchRegistration记录可用标志 被设置,则取消设置 bgFetchRegistration记录可用标志

    4. 如果 bgFetchRegistration结果 不是空字符串,则中止这些步骤。

      注意: 这可以防止在后台获取已结算后报告进度。当操作已中止,但某些获取尚未终止时,这是可能的。

    5. 如果以下条件全为 true:

      • bgFetchRegistration已下载 等于 downloaded

      • bgFetchRegistration已上传 等于 uploaded

      • bgFetchRegistration结果 等于 result

      • bgFetchRegistration失败原因 等于 failureReason

      则中止这些步骤。

    6. bgFetchRegistration已下载 设置为 downloaded

    7. bgFetchRegistration已上传 设置为 uploaded

    8. bgFetchRegistration结果 设置为 result

    9. bgFetchRegistration失败原因 设置为 failureReason

    10. 触发一个事件,名为 "progress",在 bgFetchRegistration 上。

    我需要像鼠标移动事件那样防抖此事件。

4.4. 触发后台获取点击事件

触发后台获取点击事件,用于 bgFetch(一个 后台获取),触发一个功能性事件,命名为 "backgroundfetchclick",使用 BackgroundFetchEvent,在 bgFetchService Worker 注册上,具备以下属性:
registration

通过 获取 BackgroundFetchRegistration 实例 的结果,应用于 bgFetch,在事件对象的 相关领域中。

4.5. 获取 BackgroundFetchRegistration 实例

注意: 该算法确保为给定的 BackgroundFetchRegistration 实例返回相同的结果,用于整个 BackgroundFetchManager 生命周期中的给定 后台获取。浏览器可以优化此过程,只要无法通过相等性、扩展或弱关联数据来检测到为给定的后台获取创建了多个实例即可。

获取 BackgroundFetchRegistration 实例,用于 bgFetch(一个 后台获取),在 realm(一个 领域)中,执行以下步骤:
  1. instancesMap 为该 realm 中唯一实例的 BackgroundFetchRegistration 实例

  2. 如果 instancesMap[bgFetch] 存在,则返回 instancesMap[bgFetch]。

  3. instance 为一个新的 BackgroundFetchRegistration,在 realm 中,其 后台获取 设置为 bgFetch

  4. instancesMap[bgFetch] 设置为 instance

  5. 返回 instance

4.6. 验证部分响应

注意:该算法检查部分响应是否合理匹配所请求的内容,并可选地检查是否应与先前的响应合并。

验证部分响应,为 expectedRangeStart(一个数字),partialResponse(一个 响应),和一个可选的 previousResponse(一个 响应 或 null,除非另有说明,否则为 null),执行以下步骤:
  1. 断言:partialResponse状态206

  2. responseFirstBytePosresponseLastBytePosresponseCompleteLength 为从 partialResponse提取内容范围值 的结果。如果失败,则返回无效。

  3. 如果 responseFirstBytePos 不等于 expectedRangeStart,则返回无效。

  4. 如果 previousResponse 不为 null,则:

    1. 对于 « `ETag`、`Last-Modified` » 中的 headerName

      1. 如果 previousResponse响应头列表 包含 headerName,并且 headerNamepreviousResponse响应头列表 中的合并值与在 partialResponse响应头列表 中的合并值不相等,则返回无效。

    2. 如果 previousResponse状态206,则:

      1. previousResponseFirstBytePospreviousResponseLastBytePospreviousResponseCompleteLength 为从 previousResponse提取内容范围值 的结果。如果失败,则返回无效。

      2. 如果 previousResponseCompleteLength 不为 null,且 responseCompleteLength 不等于 previousResponseCompleteLength,则返回无效。

  5. 返回有效。

4.7. 提取内容范围值

注意:该算法将 `Content-Range` 解析为 单字节内容范围 并提取值。

提取内容范围值,从 response(一个 响应)中,执行以下步骤:
  1. 如果 response响应头列表 不包含 `Content-Range`,则返回失败。

  2. contentRangeValue 为第一个 响应头,其 名称 与 `Content-Range` 字节大小写不敏感匹配,位于 response响应头列表中。

  3. 如果按 单字节内容范围 解析 contentRangeValue 失败,则返回失败。

  4. firstBytePos 为当 contentRangeValue 被解析为 单字节内容范围 时,名为 first-byte-pos 的部分,解析为整数。

  5. lastBytePos 为当 contentRangeValue 被解析为 单字节内容范围 时,名为 last-byte-pos 的部分,解析为整数。

  6. completeLength 为当 contentRangeValue 被解析为 单字节内容范围 时,名为 complete-length 的部分。

  7. 如果 completeLength"*",则将 completeLength 设置为 null,否则将 completeLength 设置为解析为整数的 completeLength

  8. 返回 firstBytePoslastBytePoscompleteLength

解析为整数 infra/189

4.8. 创建记录对象

注意:该算法为 后台获取记录 创建平台对象。它还管理从存储的 字节流式传输 响应。此时后台获取操作可能仍在进行中。

创建记录对象,从 records(一个 列表中的 后台获取记录)到 realm(一个 领域),执行以下步骤:

所有平台对象必须在 realm 中创建。

  1. recordObjects 为一个新的 列表

  2. 对于每一个 record 中的 records

    1. responseDatarecord响应数据

    2. recordObject 为一个新的 BackgroundFetchRecord

    3. 设置 recordObjectresponseReady一个新的 Promise

    4. requestObject 为一个新的 Request 对象,包含以下内容:

      请求

      一个 record请求 的副本,包括它的 主体

      头部

      与此 Request请求头列表 相关联的新 Headers 对象。

    5. recordObjectrequest 设置为 requestObject

    6. transmittedBytes 为 0。

    7. stream一个新的可读流,带有一个 pull 操作,返回 一个新的 Promise 并执行这些步骤 并行运行

      1. 等待 字节序列长度 大于 transmittedBytes,或者 responseData结果 不是空字符串。

      2. bytes 为 null。

      3. 如果 responseData长度 大于 transmittedBytesresponseData 可以 暴露,则:

        1. bytes 设置为从 responseData字节 中,从 transmittedBytes 偏移的用户代理确定的片段。

          注意:这允许用户代理以适当的速率从存储中流式传输资源。

        2. transmittedBytes 增加 bytes长度

      4. 在任务队列中排队,于 stream相关设置对象负责的事件循环 中,使用 网络任务源 来运行这些步骤:

        1. 如果 bytes 不是 null,则:

          1. array 为一个新的 Uint8Array,封装一个新的 ArrayBuffer,由 bytes 构成。

          2. array 加入 stream

        2. 如果 responseData 可以 暴露responseData结果 不是空字符串,并且 transmittedBytesresponseData长度,则 关闭 stream

        3. 否则,如果 responseData结果"已中止",则 抛出错误,使用 AbortError DOMException

        4. 否则,如果 responseData 不能被 暴露,则 抛出错误,使用 TypeError

        5. 解决 promise

    8. 并行运行这些步骤:

      1. 等待 responseData响应 变为非空。

      2. 如果 responseData 可以 暴露,则:

        1. responseresponseData响应 的副本。

        2. 删除 `Content-Range` 从 response头列表 中。

        3. 删除 `Content-Length` 从 response头列表 中。

        4. body 为一个新的 主体,其中 设置为 stream

        5. response主体 设置为 body

        6. 在任务队列中排队,于 recordObject相关设置对象负责的事件循环 中,使用 网络任务源 来运行这些步骤:

          1. responseObject 为一个新的 Response 对象,包含以下内容:

            响应

            response

            头部

            与此 Response响应头列表 相关联的新 Headers 对象。

          2. 解决 recordObjectresponseReady,并带有 responseObject

      3. 否则,如果 responseData结果"已中止",则 拒绝 recordObjectresponseReady,并附带 AbortError DOMException

      4. 否则,拒绝 recordObjectresponseReady,并附带 TypeError

    9. recordObject 附加到 recordObjects

  3. 返回 recordObjects

4.9. 包含后台提取

要确定 map (一个 map) 是否 包含后台提取 bgFetch (一个 后台提取),请执行以下步骤:
  1. id 成为 bgFetchid

  2. 如果 map [id] 不 存在,则返回 false。

  3. 如果 map [id] 不等于 bgFetch,则返回 false。

  4. 返回 true。

要确定 map (一个 map) 是否 不包含后台提取 bgFetch (一个 后台提取),请执行以下步骤:
  1. 如果 map 包含后台提取 bgFetch,则返回 false。

  2. 返回 true。

5. 标头语法

以下是用于 HTTP ABNF单字节内容范围 语法:

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

注意: 这是 RFC 7233 允许的子集。

以上内容的铁路图:

"bytes=" first-byte-pos digit /first-byte-pos "/" last-byte-pos digit /last-byte-pos "/" complete-length "*" digit /complete-length

6. API

6.1. ServiceWorkerGlobalScope 的扩展

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

6.1.1. 事件

以下是 事件处理程序(及其对应的 事件处理程序事件类型),这些程序作为 事件处理程序 IDL 属性 必须被所有实现了 ServiceWorker 接口的对象支持:

事件处理程序事件类型 事件处理程序 接口
backgroundfetchsuccess onbackgroundfetchsuccess BackgroundFetchUpdateUIEvent
backgroundfetchfail onbackgroundfetchfail BackgroundFetchUpdateUIEvent
backgroundfetchabort onbackgroundfetchabort BackgroundFetchEvent
backgroundfetchclick onbackgroundfetchclick BackgroundFetchEvent

6.2. ServiceWorkerRegistration 的扩展

partial interface ServiceWorkerRegistration {
  readonly attribute BackgroundFetchManager backgroundFetch;
};

一个 ServiceWorkerRegistration 拥有一个 背景获取管理器 (即 BackgroundFetchManager), 初始时这是一个新的 BackgroundFetchManager,其 服务工作者注册上下文对象服务工作者注册

backgroundFetch 属性的 getter 应返回 上下文对象背景获取管理器

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;
};

一个 BackgroundFetchManager 包含:

6.3.1. fetch()

fetch(id, requests, options) 方法执行以下步骤:
  1. registration 设为 上下文对象服务工作者注册

  2. records 设为一个新的 列表

  3. uploadTotal 设为 0。

  4. 如果 requests 是一个 RequestInfo,将 requests 设为 « requests »。

  5. 如果 requests空的,则返回 一个被拒绝的 promise,并带有 TypeError

  6. 迭代 requests 的每个 request

    1. internalRequest 设为 请求,其结果为使用 request 调用 Request 构造函数。如果抛出异常,则返回 一个被拒绝的 promise 并带有异常。

    2. 如果 internalRequest模式 为 "no-cors",则返回 一个被拒绝的 promise 并带有 TypeError

    3. internalRequestclient 设为 null。

    4. record 设为一个新的 背景获取记录

    5. record请求 设为 internalRequest

    6. record 添加到 records

  7. promise 设为 一个新的 promise

  8. 将以下步骤入队registration活动背景获取编辑队列

    1. permission 设为 权限状态,使用 PermissionDescriptor,其 name"background-fetch",并使用 上下文对象相关设置对象

    2. 如果 permission"denied",则拒绝 promise 并带有 NotAllowedError DOMException 并中止这些步骤。

    3. bgFetchMap 设为 registration活动背景获取

    4. 如果 registration活动工作者 为 null,则拒绝 promise 并带有 TypeError 并中止这些步骤。

    5. 如果 bgFetchMap[id] 存在拒绝 promise 并带有 TypeError 并中止这些步骤。

    6. requestBodiesRemaining 设为 requests大小

    7. requestReadFailed 设为 false。

    8. 迭代 requests 的每个 request

      1. 如果 requestbody 为 null,则 继续

      2. stream 设为 requestbody

      3. 并行 运行这些步骤:

        1. 运行这些步骤,但 requestReadFailed 为 true 时终止:

          1. 等待 requestbody

          2. 如果 stream错误,则将 requestReadFailed 设为 true。

          注意:这确保在解决之前,我们有请求字节的副本。

        2. 如果中止stream可读,则使用 AbortErrorDOMException 使 stream 错误并中止这些步骤。

        3. uploadTotal 增加 requestbody总字节数

        4. requestBodiesRemaining 减 1。

    9. 如果在任何时候,存储 requests 因超出配额限制而失败,则 拒绝 promise 并带有 QuotaExceededError DOMException 并中止这些步骤。

    10. 等待 requestBodiesRemaining 为 0 或 requestReadFailed 为 true。

    11. 如果 requestReadFailed 为 true,则 拒绝 promise 并带有 TypeError 并中止这些步骤。

    12. bgFetch 设为一个新的 背景获取,包含以下内容:

      id

      id

      记录

      records

      下载总量

      optionsdownloadTotal 成员。

      上传总量

      uploadTotal

      图标

      如果存在,optionsicons 成员,否则为空的 列表

      标题

      如果存在,optionstitle 成员,否则为空字符串。

      服务工作者注册

      registration

    13. bgFetchMap[id] 设为 bgFetch

    14. 将背景获取任务入队以运行这些步骤:

      1. 解决 promise,并使用 获取背景获取注册实例,适用于 bgFetch 并在 上下文对象相关领域中。

    15. 并行显示 bgFetch,从 上下文对象相关设置对象中。

    16. 并行执行 背景获取,使用 bgFetch

  9. 返回 promise

6.3.2. get()

get(id) 方法在调用时必须返回一个新的 promise promise并行执行以下步骤:
  1. registration 设为 上下文对象的相关 服务工作者注册

  2. bgFetch 设为 registration活动的背景获取[id]。

  3. 如果 bgFetch 为 undefined,解决 promise 为 undefined 并中止这些步骤。

  4. 将以下步骤入队bgFetch更新处理队列中:

    1. 入队一个背景获取任务 task 以运行这些步骤:

      1. bgFetchRegistration 设为 获取背景获取注册实例 的结果,适用于 bgFetch 并在 上下文对象相关领域

      2. 解决 promise,返回 bgFetchRegistration

    2. 等待 task 完成。

      注意:这确保了新的 BackgroundFetchRegistration 实例不会错过任何 进度事件。

6.3.3. getIds()

getIds() 方法在调用时必须返回 一个新的 promise promise并行执行以下步骤:
  1. registration 设为 上下文对象的相关 服务工作者注册

  2. 解决 promise,返回 registration获取的键 结果,键是 registration活动的背景获取

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 {
  // 后台抓取尚未完成,或已成功。
  "",
  // 操作已被用户中止,或调用了abort()。
  "aborted",
  // 响应状态不是ok。
  "bad-status",
  // 抓取因其他原因失败,例如CORS、MIX、无效的部分响应,或无法重试的网络故障。
  "fetch-error",
  // 操作期间达到存储配额。
  "quota-exceeded",
  // 超过了提供的downloadTotal。
  "download-total-exceeded"
};

一个 BackgroundFetchRegistration 实例有:

注意:上述值为副本,因此它们可以同步使用。

属性 id 的getter 必须返回 上下文对象ID

属性 uploadTotal 的getter 必须返回 上下文对象上传总数

属性 downloadTotal 的getter 必须返回 上下文对象下载总数

属性 uploaded 的getter 必须返回 上下文对象已上传

属性 downloaded 的getter 必须返回 上下文对象已下载

属性 result 的getter 必须返回 上下文对象结果

属性 failureReason 的getter 必须返回 上下文对象失败原因

属性 recordsAvailable 的getter 必须在 上下文对象可用记录标志 设置时返回true, 否则返回false。

6.4.1. 事件

属性 onprogress事件处理器事件处理类型progress

事件 progress 使用 Event 接口。

6.4.2. abort()

方法 abort() 调用时,必须返回 一个新的promise promise 并按以下步骤 并行 运行:

  1. bgFetch上下文对象 关联的 background fetch

  2. swRegistrationbgFetchservice worker registration

  3. 将以下步骤入队swRegistrationactive background fetches 编辑队列

    1. activeBgFetchesswRegistrationactive background fetches

    2. idbgFetchid

    3. 如果 activeBgFetches 不包含background fetch bgFetch,则 resolve promise 为 false 并中止这些步骤。

    4. 移除 activeBgFetches[id]。

    5. resolve promise 为 true。

    6. 设置 bgFetchabort all flag

6.4.3. match()

方法 match(request, options) 调用时,必须按以下步骤运行:

  1. promise 为调用算法 matchAll() 的结果,传递 requestoptions

  2. 返回 对promise反应 的结果,带有一个实现处理程序,当被调用时以参数 matches 返回 matches[0]。

注意: 鼓励用户代理对上述操作进行优化,使其比调用 matchAll() 更快。

6.4.4. matchAll()

方法 matchAll(request, options) 调用时,必须按以下步骤运行:

  1. 如果 上下文对象记录可用标志 未设置,返回 一个被拒绝的promise 并带有 InvalidStateErrorDOMException

  2. promise一个新的promise

  3. 按以下步骤 并行 运行:

    1. matchingRecords 为一个空的 列表

    2. 对于 上下文对象background fetch 的每个 记录,执行以下步骤:

      1. 如果 请求与缓存项匹配,则对于 requestrecord请求record响应数据 中的 响应,以及 options 返回 true,则 追加 recordmatchingRecords

    3. 入列一个background fetch任务resolve promise,结果为通过 创建记录对象matchingRecords上下文对象相关领域中。

  4. 返回 promise

6.5. BackgroundFetchRecord

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

request 属性的 getter 必须返回 上下文对象请求

responseReady 属性的 getter 必须返回 上下文对象响应 promise

6.6. BackgroundFetchEvent

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

dictionary BackgroundFetchEventInit : ExtendableEventInit {
  required BackgroundFetchRegistration registration;
};
一个 BackgroundFetchEvent 拥有一个 background fetch (一个 background fetch), 最初是被 registration 初始化的值。

registration 属性必须返回初始化的值。

6.7. BackgroundFetchUpdateUIEvent

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

一个 BackgroundFetchUpdateUIEvent 拥有一个 UI 更新标志,其初始状态为未设置。

6.7.1. updateUI()

updateUI(options) 方法在调用时必须返回一个 新的 promise promise,并按照这些步骤 并行 运行:
  1. 如果以下任一情况为真:

    抛出 InvalidStateError DOMException

  2. 设置 上下文对象UI 更新标志

  3. 如果 options 为 null,则返回。

  4. bgFetch 成为 上下文对象后台提取

  5. 如果 optionsicons 成员存在,将 bgFetch图标 设置为 optionsicons

  6. 如果 optionstitle 成员存在,将 bgFetch标题 设置为 optionstitle

  7. 解决 promise

7. 自动化

为了用户代理自动化和应用程序测试的目的,本文档为扩展命令定义了以下[WebDriver]规范。

7.1. 点击

方法 URI 模板
POST /session/{session id}/backgroundfetch/{id}/click

后台提取点击 扩展命令 模拟用户激活显示后台提取的操作。远程端步骤如下:

  1. 如果当前顶级浏览上下文 不再打开,则返回 WebDriver 错误,错误代码为 WebDriver 错误代码 没有这样的窗口

  2. pageURL 为当前顶级浏览上下文的活动文档URL

  3. swRegistrationpageURL匹配的服务工作者注册

  4. 如果 swRegistration 为空,则返回 WebDriver 错误,状态码为 400,JSON 错误代码为“invalid service worker state”。

  5. bgFetch 为具有 idurl 变量 id 的最新后台提取,并且具有 swRegistration服务工作者注册,否则为 null。

  6. 如果 bgFetch 为空,则返回 WebDriver 错误,状态码为 404,JSON 错误代码为“background fetch not found”。

  7. 触发后台提取点击事件,针对 bgFetch

  8. 返回 WebDriver 成功

8. 隐私和带宽使用

提取操作可能很大且需要很长时间才能完成。在此期间,用户将从一个或多个服务器获取数据。用户的 IP 地址可能在操作过程中发生变化,并可能被用来跟踪用户的长期位置。

为了缓解这种情况,显示后台提取的步骤要求:

这些步骤还允许用户代理在用户处于计量连接时暂停后台提取

所有存储的数据都与特定的服务工作者注册相关联。清除服务工作者注册将清除所有关联的后台提取

一致性

文档约定

一致性要求通过描述性断言和 RFC 2119 术语的组合来表达。文档中使用的关键字 “MUST”、"MUST NOT"、"REQUIRED"、"SHALL"、"SHALL NOT"、"SHOULD"、"SHOULD NOT"、"RECOMMENDED"、"MAY" 和 "OPTIONAL" 应按照 RFC 2119 的描述进行解释。然而,为了提高可读性,这些词在本规范中并未全部使用大写字母。

本规范的所有文本都是规范性的,除非明确标记为非规范性部分、示例或注释。[RFC2119]

本规范中的示例以“例如”开头,或者用 class="example" 与规范性文本区分开来,如下所示:

这是一个提供信息的示例。

信息性注释以“注释”一词开头,并用 class="note" 与规范性文本区分开来,如下所示:

注意,这是一个提供信息的注释。

一致性算法

算法中以命令式表达的要求(例如“去除任何前导空格字符”或“返回 false 并中止这些步骤”)应按照引入算法时使用的关键字(如“必须”、“应”、“可以”等)的含义进行解释。

以算法或特定步骤措辞的一致性要求可以以任何方式实现,只要最终结果等效即可。特别是,本规范中定义的算法旨在易于理解,而非高性能实现。鼓励实现者进行优化。

索引

本规范定义的术语

引用定义的术语

参考文献

规范性引用

[DOM]
Anne van Kesteren. DOM 标准. 现行标准. URL: https://dom.spec.whatwg.org/
[ECMASCRIPT]
ECMAScript 语言规范. URL: https://tc39.es/ecma262/
[FETCH]
Anne van Kesteren. Fetch 标准. 现行标准. URL: https://fetch.spec.whatwg.org/
[HTML]
Anne van Kesteren; et al. HTML 标准. 现行标准. URL: https://html.spec.whatwg.org/multipage/
[IMAGE-RESOURCE]
Aaron Gustafson; Rayan Kanso; Marcos Caceres. 图像资源. 2021 年 3 月 29 日. WD. URL: https://www.w3.org/TR/image-resource/
[INFRA]
Anne van Kesteren; Domenic Denicola. Infra 标准. 现行标准. URL: https://infra.spec.whatwg.org/
[PERMISSIONS]
Mounir Lamouri; Marcos Caceres; Jeffrey Yasskin. 权限. 2020 年 7 月 20 日. WD. URL: https://www.w3.org/TR/permissions/
[RFC2119]
S. Bradner. 用于在 RFC 中指示需求级别的关键字. 1997 年 3 月. 最佳当前实践. URL: https://tools.ietf.org/html/rfc2119
[SERVICE-WORKERS-1]
Alex Russell; et al. Service Workers 1. 2019 年 11 月 19 日. CR. URL: https://www.w3.org/TR/service-workers-1/
[STREAMS]
Adam Rice; Domenic Denicola; 吉野剛史 (Takeshi Yoshino). Streams 标准. 现行标准. URL: https://streams.spec.whatwg.org/
[URL]
Anne van Kesteren. URL 标准. 现行标准. URL: https://url.spec.whatwg.org/
[WebDriver]
Simon Stewart; David Burns. WebDriver. 2018 年 6 月 5 日. REC. URL: https://www.w3.org/TR/webdriver1/
[WebIDL]
Boris Zbarsky. Web IDL. 2016 年 12 月 15 日. ED. URL: https://heycam.github.io/webidl/

IDL 索引

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 {
  // 后台获取尚未完成,或已成功。
  "",
  // 操作被用户中止,或调用了 abort()。
  "aborted",
  // 响应状态不为 "ok"。
  "bad-status",
  // 由于其他原因(如 CORS、MIX、无效的部分响应,
  // 或无法重试的网络故障)导致获取失败。
  "fetch-error",
  // 操作期间达到了存储配额。
  "quota-exceeded",
  // 提供的 downloadTotal 被超出。
  "download-total-exceeded"
};

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

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

dictionary BackgroundFetchEventInit : ExtendableEventInit {
  required BackgroundFetchRegistration registration;
};

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

问题索引

manifest/pull/710.
ServiceWorker/1348.
本步骤的其余部分使用了 fetch 的“回调”,当前这些回调会排队任务。这在此不理想或不可行,因此我们假装不排队任务。(问题
我需要类似于鼠标移动事件去抖动一样来处理此问题。
解析为整数 infra/189.
ServiceWorker/1348.