服务工作线程

W3C 候选推荐标准草案

关于本文档的更多详细信息
此版本:
https://www.w3.org/TR/2025/CRD-service-workers-20250306/
最新发布版本:
https://www.w3.org/TR/service-workers/
编辑草案:
https://w3c.github.io/ServiceWorker/
历史:
https://www.w3.org/standards/history/service-workers/
实现报告:
https://wpt.fyi/service-workers
反馈:
GitHub
规范内联
编辑:
(微软)
(谷歌)
前任编辑:
(谷歌)
(谷歌)
(微软,2018 年 4 月前代表三星)
(谷歌)
测试:
web-platform-tests service-workers/ (进行中的工作)

摘要

本规范的核心是一种 worker,可被唤醒以接收事件。它提供了一个事件接收点,用于在其他处理方式不合适,或根本不存在其他接收点的情况下使用。

例如,为了允许开发者决定如何获取页面,事件需要在可能尚未存在该来源的任何执行上下文之前被调度。要响应推送消息或持久化下载的完成,原本注册该需求的上下文可能已不复存在。在这些情况下,service worker 是理想的事件接收点。

本规范还提供了一个fetch event,以及一个与 HTTP 缓存设计相似的请求与响应存储,从而使开发离线可用的 Web 应用更加容易。

本文件的状态

此部分描述了本文件在发布时的状态。可以在 W3C 技术报告索引(https://www.w3.org/TR/)中找到 W3C 当前的所有出版物列表以及本技术报告的最新修订版。

本文件由Web Applications Working Group基于 Recommendation track 作为候选推荐等级草案(Candidate Recommendation Draft)发布。

如果针对本规范有任何反馈和意见,欢迎提交问题或发送邮件至 public-webapps@w3.org订阅存档)并在邮件主题开头添加 [service-workers]

候选推荐(Candidate Recommendation)的发布并不代表 W3C 及其成员的背书。候选推荐等级草案(Candidate Recommendation Draft)整合了工作组打算纳入后续候选推荐快照的对之前候选推荐所做的更改。

这是一个草案文件,可能随时被更新、替换或由其他文件取代。将此文件视为进行中的工作,而非正式结论进行引用是不合适的。

本文件由在 W3C 专利政策 之下运作的小组编写。 W3C 维护了一个 公开的专利披露列表, 其中包含与本小组的可交付成果相关的专利披露;该页面还提供了披露专利的说明。任何个人若实际知晓并认为某项专利包含 必要声明 ,则必须根据 W3C 专利政策》 第6节 进行披露。

本文件受 2023年11月03日 W3C 流程文档 管辖。

1. 动机

本节内容不具有规范性。

Web 应用程序传统上假定网络是可访问的。这种假设贯穿了整个平台。HTML 文档通过 HTTP 加载,并且传统上通过后续的 HTTP 请求获取其所有子资源。这使得 Web 内容与其他技术栈相比处于劣势。

Service worker 的首要设计目标是通过提供一个 Web Worker 上下文来弥补这一不足,该上下文可以在导航即将发生时由运行时启动。这种事件驱动的 worker 针对源和路径(或模式)进行注册,这意味着当导航到该位置时可以咨询它。与网络请求相对应的事件被分派给该 worker,由该 worker 生成的响应可以覆盖默认网络堆栈的行为。从概念上讲,这将 service worker 置于网络和文档渲染器之间,允许 service worker 为文档提供内容,即使在离线状态下也是如此。

熟悉先前解决离线问题方案的 Web 开发人员报告称,这些解决方案缺乏灵活性。因此,service worker 的设计高度程序化,以增加开发人员的复杂性为代价提供了最大的灵活性。这种复杂性部分源于需要在单线程执行模型下保持 service worker 的响应能力。因此,service worker 暴露的 API 几乎完全是异步的,这种模式在其他 JavaScript 上下文中很常见,但在这里由于需要避免阻塞文档和资源加载而更加突出。

使用 HTML5 Application Cache 的开发人员还报告称其设计的几个特性会导致无法恢复的错误Service worker 的一个关键设计原则是错误必须始终是可恢复的。Service worker 更新过程的许多细节旨在避免这些风险。

Service worker 的启动和保持活动状态依赖于它们与事件的关系,而不是与文档的关系。这种设计大量借鉴了开发人员和供应商在使用 shared workerChrome 后台页面方面的经验。从这些系统中吸取的一个关键教训是,必须限制后台处理上下文的执行时间,既为了节省资源,也为了确保开发人员始终关注后台上下文丢失和重启的问题。因此,service workerChrome 事件页面(后台页面的后继者)有着不止一点的相似之处。Service worker 可能会在没有附加文档的情况下由用户代理启动,并且几乎随时可能被用户代理终止。从概念上讲,service worker 可以被认为是可以在不处理来自文档的任何消息的情况下启动、处理事件并终止的 Shared Worker。建议开发人员记住,service worker 可能在一秒钟内被启动和终止多次。

Service worker 是通用的、事件驱动的、有时间限制的脚本上下文,在源上运行。这些特性使其成为一系列运行时服务的天然端点,这些服务可能比特定文档的上下文生命周期更长,例如处理推送通知、后台数据同步、响应来自其他源的资源请求,或接收对计算成本高昂的数据(例如地理位置或陀螺仪)的集中更新。

2. 模型

2.1. Service Worker

service worker 是一种 web workerservice worker 在注册的 service worker clientorigin 中执行。

service worker 有一个关联的 state,它是 "parsed"、"installing"、"installed"、"activating"、"activated" 和 "redundant" 中的一个。初始值为 "parsed"。

service worker 有一个关联的 script url(一个 URL)。

service worker 有一个关联的 type,它是 "classic" 或 "module"。除非另有说明,否则它是 "classic"。

service worker 有一个关联的 containing service worker registration(一个 service worker registration),其中包含自身。

service worker 有一个关联的 global object(一个 ServiceWorkerGlobalScope 对象或 null)。

service worker 有一个关联的 script resource(一个 script),它表示自己的脚本资源。初始设置为 null。

script resource 有一个关联的 has ever been evaluated flag。初始为未设置。

script resource 有一个关联的 policy container(一个 policy container)。初始为一个新的 policy container。

service worker 有一个关联的 script resource map,它是一个 ordered map,其中键是 URL,值是 response

service worker 有一个关联的 set of used scripts(一个 set),其 item 是一个 URL。初始为一个新的 set

注:set of used scripts 仅用于在安装后从新 worker 的映射中删除未使用的资源,这些资源在更新检查期间基于旧 worker 的映射填充。

service worker 有一个关联的 skip waiting flag。除非另有说明,否则它是未设置的。

service worker 有一个关联的 classic scripts imported flag。初始为未设置。

service worker 有一个关联的 set of event types to handle(一个 set),其 item 是一个 event listener 的事件类型。初始为一个新的 set

service worker 有一个关联的 set of extended events(一个 set),其 item 是一个 ExtendableEvent。初始为一个新的 set

service worker 有一个关联的 start status,它可以是 null 或一个 Completion。初始为 null。

service worker 有一个关联的 all fetch listeners are empty flag。初始为未设置。

service worker 有一个关联的 list of router rules(一个 RouterRulelist)。初始为一个空的 list

如果 service workerevent loop 在运行,则称该 service worker running

service worker 有一个关联的 [[service worker queue]](一个 parallel queue)。

2.1.1. 生命周期

service worker 的生命周期与事件的执行生命周期相关联,而不是与 service worker clientServiceWorker 对象的引用相关联。

用户代理可以在任何时候终止 service worker,如果它:

  • 没有要处理的事件。

  • 检测到异常操作:例如无限循环和在处理事件时超过强加的时间限制(如果有)的任务。

2.1.2. 事件

Service Workers 规范定义了 service worker events(每个都是一个 event),包括(见列表):

2.2. Service Worker 时序

Service worker 标记某些时间点,这些时间点稍后会通过 导航时序 API 暴露。

service worker timing info 是一个 struct。它有以下 item

start time

一个 DOMHighResTimeStamp,初始为 0。

fetch event dispatch time

一个 DOMHighResTimeStamp,初始为 0。

2.3. Service Worker 注册

service worker registration 是一个由 scope urlstorage key 和一组 service worker、一个 installing worker、一个 waiting worker 和一个 active worker 组成的元组。只要 service worker registrationscope url 不同,用户代理可以在单个源启用多个 service worker registration。当已存在相同 scope urlservice worker registration 时,新的注册会导致现有的 service worker registration 被替换。

service worker registration 有一个关联的 storage key(一个 storage key)。

service worker registration 有一个关联的 scope url(一个 URL)。

service worker registration 有一个关联的 installing worker(一个 service worker 或 null),其 state 是 "installing"。初始设置为 null。

service worker registration 有一个关联的 waiting worker(一个 service worker 或 null),其 state 是 "installed"。初始设置为 null。

service worker registration 有一个关联的 active worker(一个 service worker 或 null),其 state 是 "activating" 或 "activated"。初始设置为 null。

service worker registration 有一个关联的 last update check time。初始设置为 null。

如果注册的 last update check time 非空,且当前时间减去注册的 last update check time 计算出的秒数时间差大于 86400,则称 service worker registrationstale

service worker registration 有一个关联的 update via cache mode,它是 "imports"、"all" 或 "none"。初始设置为 "imports"。

service worker registration 有一个或多个 task queue,用于备份来自其 active workerevent loop 的相应 task queue 中的 task。(此备份操作的目标任务源是 handle fetch task sourcehandle functional event task source。)当 active worker终止时,用户代理将 active workertask 转储到 service worker registrationtask queue 中,并在 active worker 启动时将这些任务重新排队active workerevent loop 的相应 task queue 中。与 event loop 拥有的 task queue 不同,service worker registrationtask queue 本身不会被任何 event loop 处理。

service worker registration 有一个关联的 NavigationPreloadManager 对象。

service worker registration 有一个关联的 navigation preload enabled flag。初始为未设置。

service worker registration 有一个关联的 navigation preload header value,它是一个 byte sequence。初始设置为 `true`。

如果 registration map[该 service worker registration 的(storage key序列化的 scope url)] 不是该 service worker registration,则称该 service worker registrationunregistered

2.3.1. 生命周期

除非明确注销,否则用户代理必须持久保留注册的 service worker registration 列表。用户代理有一个 registration map,存储 service worker registration 的(storage key序列化的 scope url)元组与相应 service worker registration 的条目。service worker registration 的生命周期超出了在其相应 service worker client 生命周期内代表它们的 ServiceWorkerRegistration 对象的生命周期。

2.4. Service Worker 客户端

service worker client 是一个 environment

service worker client 有一个关联的 discarded flag。初始为未设置。

每个 service worker client 都有以下 environment discarding steps

  1. 设置 clientdiscarded flag

注:实现可以丢弃其 discarded flag 被设置的客户端。

service worker client 有一个定义为 origin 的算法,该算法返回 service worker clientorigin(如果 service worker client 是一个 environment settings object),否则返回 service worker clientcreation URLorigin

window client 是一个 service worker client,其 global object 是一个 Window 对象。

dedicated worker client 是一个 service worker client,其 global object 是一个 DedicatedWorkerGlobalScope 对象。

shared worker client 是一个 service worker client,其 global object 是一个 SharedWorkerGlobalScope 对象。

worker clientdedicated worker clientshared worker client

2.5. 控制和使用

service worker client 有一个 active service worker,它为自身的加载和子资源提供服务。当 service worker client 有一个非空的 active service worker 时,称其被该 active service worker 控制。当 service worker client 被一个 service worker 控制时,称该 service worker client 正在使用 service workercontaining service worker registrationservice worker clientactive service worker 按照以下小节中的解释确定。

本节的其余部分不具有规范性。

本节中的行为尚未完全规范,将在 HTML 标准中规范。该工作由 issuepull request 跟踪。

2.5.1. 窗口客户端情况

window clientbrowsing context 创建导航创建

window clientbrowsing context 创建过程中创建时:

如果 browsing context 的初始 active documentorigin 是一个 opaque origin,则 window clientactive service worker 设置为 null。 否则,它设置为创建者 documentservice worker clientactive service worker

window clientbrowsing contextnavigation 过程中创建时:

如果 fetch 通过 HTTP fetch 路由,则 window clientactive service worker 设置为 service worker registration matching 的结果。 否则,如果创建的 documentorigin 是一个 opaque origin 或与其创建者 documentorigin相同,则 window clientactive service worker 设置为 null。 否则,它设置为创建者 documentservice worker clientactive service worker

注:对于初始替换 navigation,在 browsing context 创建创建的初始 window client 被重用,但 active service worker 由上述相同行为确定。

注:没有沙盒指令 allow-same-originallow-scripts沙盒 iframeactive service worker 值为 null,因为它们的 origin 是一个 opaque origin

2.5.2. 工作线程客户端情况

worker client 在用户代理运行工作线程创建

worker client 创建时:

fetch 通过 HTTP fetch 路由时,worker clientactive service worker 设置为 service worker registration matching 的结果。 否则,如果 worker clientorigin 是一个 opaque origin,或 requestURL 是一个 blob URLworker clientoriginworker clientglobal objectowner set 中最后一个 itemorigin相同,则 worker clientactive service worker 设置为 null。 否则,它设置为 worker clientglobal objectowner set 中最后一个 itemenvironment settings objectactive service worker

注:具有 data: URLWindow clientworker clientactive service worker 值为 null,因为它们的 origin 是一个 opaque origin。具有 blob URLWindow clientworker client 可以继承其创建者 document 或所有者的 active service worker,但如果 requestorigin 与其创建者 document 或所有者的 origin相同,则 active service worker 设置为 null。

2.6. 任务源

service worker 使用以下附加的 task source

handle fetch task source

task source 用于向 service worker 调度 fetch 事件。

handle functional event task source

task source 用于向 service worker 调度其他 功能事件的功能,例如 push 事件。

注:用户代理可以为每种功能事件类型使用单独的任务源,以避免某些功能事件的队首阻塞现象。

2.7. 用户代理关闭

用户代理必须在重启时维护其存储的 service worker registration 的状态,遵循以下规则:

为了实现这一点,用户代理必须在终止时调用 Handle User Agent Shutdown

3. 客户端上下文

使用 service worker 进行引导:
// scope 默认为脚本所在的路径
// 在这个例子中是 "/"
navigator.serviceWorker.register("/serviceworker.js").then(registration => {
  console.log("成功!");
  if (registration.installing) {
    registration.installing.postMessage("来自正在安装页面的问候。");
  }
}, err => {
  console.error("安装 worker 失败!", err);
});

3.1. ServiceWorker

[SecureContext, Exposed=(Window,Worker)]
interface ServiceWorker : EventTarget {
  readonly attribute USVString scriptURL;
  readonly attribute ServiceWorkerState state;
  undefined postMessage(any message, sequence<object> transfer);
  undefined postMessage(any message, optional StructuredSerializeOptions options = {});

  // event
  attribute EventHandler onstatechange;
};
ServiceWorker includes AbstractWorker;

enum ServiceWorkerState {
  "parsed",
  "installing",
  "installed",
  "activating",
  "activated",
  "redundant"
};

ServiceWorker 对象表示一个 service worker。每个 ServiceWorker 对象都与一个 service worker 关联。跨文档和工作线程的多个实现了 ServiceWorker 接口的独立对象可以同时都与同一个 service worker 关联。

ServiceWorker 对象有一个关联的 ServiceWorkerState 对象,该对象本身与 service workerstate 关联。

3.1.1. 获取 ServiceWorker 实例

environment settings object 有一个 service worker object map,这是一个 map,其中 keyservice workervalueServiceWorker 对象。

获取表示 serviceWorker(一个 service worker)在 environment(一个 environment settings object)中的 service worker 对象,运行以下步骤:
  1. objectMapenvironmentservice worker object map

  2. 如果 objectMap[serviceWorker] 不存在,则:

    1. serviceWorkerObjenvironmentRealm 中的一个新的 ServiceWorker,并将其与 serviceWorker 关联。

    2. 设置 serviceWorkerObjstateserviceWorkerstate

    3. 设置 objectMap[serviceWorker] 为 serviceWorkerObj

  3. 返回 objectMap[serviceWorker]。

3.1.2. scriptURL

scriptURL 获取器步骤是返回 service worker序列化script url

例如,考虑一个通过导航到 https://example.com/app.html 创建的文档,该文档通过以下之前执行的注册调用进行匹配
// 页面 https://example.com/app.html 上的脚本
navigator.serviceWorker.register("/service_worker.js");

navigator.serviceWorker.controller.scriptURL 的值将是 "https://example.com/service_worker.js"。

3.1.3. state

state 属性必须返回最后设置的值(在 ServiceWorkerState 枚举中)。

3.1.4. postMessage(message, transfer)

postMessage(message, transfer) 方法步骤是:

  1. options 为 «[ "transfer" → transfer ]»。

  2. messageoptions 作为参数调用 postMessage(message, options)

3.1.5. postMessage(message, options)

postMessage(message, options) 方法步骤是:

  1. serviceWorker 为由此对象表示的 service worker

  2. incumbentSettingsincumbent settings object

  3. incumbentGlobalincumbentSettingsglobal object

  4. serializeWithTransferResultStructuredSerializeWithTransfer(message, options["transfer"])。重新抛出任何异常。

  5. 如果使用 "message" 和 serviceWorker 运行 Should Skip Event 算法的结果为 true,则返回。

  6. 并行运行以下子步骤:

    1. 如果使用 serviceWorker 运行 Run Service Worker 算法的结果是失败,则返回。

    2. DOM manipulation task source排队一个任务来运行以下步骤:

      1. source 通过切换 incumbentGlobal 的类型来确定:

        ServiceWorkerGlobalScope
        获取表示 incumbentGlobalservice workerserviceWorkerglobal objectrelevant settings object 中的 service worker 对象的结果。
        Window
        一个表示 incumbentGlobalrelevant settings object 的新的 WindowClient 对象。
        其他情况
        一个表示 incumbentGlobal 的关联工作线程的新的 Client 对象
      2. originincumbentSettingsorigin序列化

      3. destination 为与 serviceWorker 关联的 ServiceWorkerGlobalScope 对象。

      4. deserializeRecordStructuredDeserializeWithTransfer(serializeWithTransferResult, destinationRealm)。

        如果这抛出异常,让 e创建一个名为 messageerror 的事件的结果,使用 ExtendableMessageEventorigin 属性初始化为 originsource 属性初始化为 source

      5. 否则:

        1. messageClonedeserializeRecord.[[Deserialized]]。

        2. newPorts 为一个由 deserializeRecord.[[TransferredValues]] 中的所有 MessagePort 对象组成的新的冻结数组(如果有的话),保持它们的相对顺序。

        3. e创建一个名为 message 的事件的结果,使用 ExtendableMessageEventorigin 属性初始化为 originsource 属性初始化为 sourcedata 属性初始化为 messageCloneports 属性初始化为 newPorts

      6. destination调度 e

      7. serviceWorkere 调用 Update Service Worker Extended Events Set

3.1.6. 事件处理程序

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

事件处理程序 事件处理程序事件类型
onstatechange statechange

3.2. ServiceWorkerRegistration

[SecureContext, Exposed=(Window,Worker)]
interface ServiceWorkerRegistration : EventTarget {
  readonly attribute ServiceWorker? installing;
  readonly attribute ServiceWorker? waiting;
  readonly attribute ServiceWorker? active;
  [SameObject] readonly attribute NavigationPreloadManager navigationPreload;

  readonly attribute USVString scope;
  readonly attribute ServiceWorkerUpdateViaCache updateViaCache;

  [NewObject] Promise<undefined> update();
  [NewObject] Promise<boolean> unregister();

  // event
  attribute EventHandler onupdatefound;
};

enum ServiceWorkerUpdateViaCache {
  "imports",
  "all",
  "none"
};

ServiceWorkerRegistration 有一个 service worker registration(一个 service worker registration)。

3.2.1. 获取 ServiceWorkerRegistration 实例

environment settings object 有一个 service worker registration object map,这是一个 map,其中 keyservice worker registrationvalueServiceWorkerRegistration 对象。

获取表示 registration(一个 service worker registration)在 environment(一个 environment settings object)中的 service worker registration 对象,运行以下步骤:
  1. objectMapenvironmentservice worker registration object map

  2. 如果 objectMap[registration] 不存在,则:

    1. registrationObjectenvironmentRealm 中的一个新的 ServiceWorkerRegistration

    2. 设置 registrationObjectservice worker registrationregistration

    3. 设置 registrationObjectinstalling 属性为 null。

    4. 设置 registrationObjectwaiting 属性为 null。

    5. 设置 registrationObjectactive 属性为 null。

    6. 如果 registrationinstalling worker 不为 null,则设置 registrationObjectinstalling 属性为获取表示 registrationinstalling workerenvironment 中的 service worker 对象的结果。

    7. 如果 registrationwaiting worker 不为 null,则设置 registrationObjectwaiting 属性为获取表示 registrationwaiting workerenvironment 中的 service worker 对象的结果。

    8. 如果 registrationactive worker 不为 null,则设置 registrationObjectactive 属性为获取表示 registrationactive workerenvironment 中的 service worker 对象的结果。

    9. 设置 objectMap[registration] 为 registrationObject

  3. 返回 objectMap[registration]。

installing 属性必须返回最后设置的值。

注:在一个 Realm 中,每个关联的 service worker 只有一个 ServiceWorker 对象。

waiting 属性必须返回最后设置的值。

注:在一个 Realm 中,每个关联的 service worker 只有一个 ServiceWorker 对象。

active 属性必须返回最后设置的值。

注:在一个 Realm 中,每个关联的 service worker 只有一个 ServiceWorker 对象。

3.2.5. navigationPreload

navigationPreload 获取器步骤是返回 service worker registrationNavigationPreloadManager 对象。

3.2.6. scope

scope 获取器步骤是返回 service worker registration序列化scope url

§ 3.1.2 scriptURL 的示例中,registration.scope 的值,例如从 navigator.serviceWorker.ready.then(registration => console.log(registration.scope)) 获得,将是 "https://example.com/"。

3.2.7. updateViaCache

updateViaCache 获取器步骤是返回 service worker registrationupdate via cache mode

3.2.8. update()

update() 方法步骤是:

  1. registrationservice worker registration

  2. newestWorker 为以 registration 作为参数运行 Get Newest Worker 算法的结果。

  3. 如果 newestWorker 为 null,返回一个被拒绝的 promise,拒绝理由为 "InvalidStateError" DOMException,并中止这些步骤。

  4. 如果此对象relevant global object globalObject 是一个 ServiceWorkerGlobalScope 对象,且 globalObject 的关联 service workerstate 是 "installing",返回一个被拒绝的 promise,拒绝理由为 "InvalidStateError" DOMException,并中止这些步骤。

  5. promise 为一个 promise

  6. job 为使用 updateregistrationstorage keyregistrationscope urlnewestWorkerscript urlpromise此对象relevant settings object 运行 Create Job 的结果。

  7. 设置 jobworker typenewestWorkertype

  8. 使用 job 调用 Schedule Job

  9. 返回 promise

注:unregister() 方法注销 service worker registration。重要的是要注意,当前受控service worker clientactive service workercontaining service worker registration 在所有使用此 service worker registrationservice worker client(包括自身)卸载之前一直有效。也就是说,unregister() 方法只影响后续的导航

unregister() 方法步骤是:

  1. registrationservice worker registration

  2. promise一个新的 promise

  3. job 为使用 unregisterregistrationstorage keyregistrationscope url、null、promise此对象relevant settings object 运行 Create Job 的结果。

  4. 使用 job 调用 Schedule Job

  5. 返回 promise

3.2.10. 事件处理程序

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

事件处理程序 事件处理程序事件类型
onupdatefound updatefound
partial interface Navigator {
              [SecureContext, SameObject] readonly attribute ServiceWorkerContainer serviceWorker;
            };
            
            partial interface WorkerNavigator {
              [SecureContext, SameObject] readonly attribute ServiceWorkerContainer serviceWorker;
            };
            

serviceWorker getter 的步骤是返回与此对象关联的 ServiceWorkerContainer 对象。

3.4. ServiceWorkerContainer

[SecureContext, Exposed=(Window,Worker)]
            interface ServiceWorkerContainer : EventTarget {
              readonly attribute ServiceWorker? controller;
              readonly attribute Promise<ServiceWorkerRegistration> ready;
            
              [NewObject] Promise<ServiceWorkerRegistration> register((TrustedScriptURL or USVString) scriptURL, optional RegistrationOptions options = {});
            
              [NewObject] Promise<(ServiceWorkerRegistration or undefined)> getRegistration(optional USVString clientURL = "");
              [NewObject] Promise<FrozenArray<ServiceWorkerRegistration>> getRegistrations();
            
              undefined startMessages();
            
            
              // 事件
              attribute EventHandler oncontrollerchange;
              attribute EventHandler onmessage; // message 事件的 event.source 是 ServiceWorker 对象
              attribute EventHandler onmessageerror;
            };
            
dictionary RegistrationOptions {
              USVString scope;
              WorkerType type = "classic";
              ServiceWorkerUpdateViaCache updateViaCache = "imports";
            };
            

用户代理在创建 Navigator 对象或 WorkerNavigator 对象时必须创建一个 ServiceWorkerContainer 对象,并将其与该对象关联。

ServiceWorkerContainer 提供了注册、注销和更新服务工作线程注册的功能,并提供对服务工作线程注册及其关联的服务工作线程状态的访问。

ServiceWorkerContainer 具有一个关联的服务工作线程客户端,它是一个服务工作线程客户端,其全局对象与从中检索 ServiceWorkerContainerNavigator 对象或 WorkerNavigator 对象相关联。

ServiceWorkerContainer 对象具有一个关联的就绪 promise(一个promise 或 null)。它最初为 null。

ServiceWorkerContainer 对象具有一个名为客户端消息队列任务源,最初为空。客户端消息队列可以启用或禁用,并且最初是禁用的。当 ServiceWorkerContainer 对象的客户端消息队列启用时,事件循环必须将其用作其任务源之一。当 ServiceWorkerContainer 对象的相关全局对象Window 对象时,在其客户端消息队列排队的所有任务必须与其相关设置对象关联文档相关联。

controller 属性必须运行以下步骤:

  1. client此对象服务工作线程客户端

  2. 如果 client活动服务工作线程为 null,则返回 null。

  3. 返回获取服务工作线程对象的结果,该对象表示此对象相关设置对象client活动服务工作线程

注意:如果请求是强制刷新(shift+refresh),则 navigator.serviceWorker.controller 返回 null

ready 属性必须运行以下步骤:

  1. 如果此对象就绪 promise 为 null,则将此对象就绪 promise 设置为一个新的 promise

  2. readyPromise此对象就绪 promise

  3. 如果 readyPromise 处于 pending 状态,则并行运行以下子步骤:

    1. client此对象服务工作线程客户端

    2. storage key 为运行获取存储密钥(给定 client)的结果。

    3. registration 为运行匹配服务工作线程注册(给定 storage keyclient创建 URL)的结果。

    4. 如果 registration 不为 null,并且 registration活动工作线程不为 null,则在 readyPromise相关设置对象负责事件循环上,使用DOM 操作任务源将任务排队,以使用获取服务工作线程注册对象(表示 readyPromise相关设置对象中的 registration)的结果来解析 readyPromise

  4. 返回 readyPromise

注意:返回的就绪 promise 永远不会拒绝。如果它在此算法中没有解析,它最终会在匹配的服务工作线程注册被注册并且其活动工作线程被设置时解析。(请参阅相关的激活算法步骤。)

注意:register(scriptURL, options) 方法为给定的作用域 URL 创建或更新服务工作线程注册。如果成功,服务工作线程注册会将提供的 scriptURL 绑定到一个作用域 URL,该 URL 随后用于导航匹配

register(scriptURL, options) 方法的步骤是:

  1. p 为一个promise

  2. scriptURL 设置为调用获取受信任类型兼容字符串的结果,参数为 TrustedScriptURL此对象相关全局对象scriptURL、"ServiceWorkerContainer register" 和 "script"。

  3. client此对象服务工作线程客户端

  4. scriptURL解析 scriptURL(使用此对象相关设置对象API 基础 URL)的结果。

  5. scopeURL 为 null。

  6. 如果 options["scope"] 存在,则将 scopeURL 设置为解析 options["scope"](使用此对象相关设置对象API 基础 URL)的结果。

  7. 调用开始注册,参数为 scopeURLscriptURLpclientclient创建 URLoptions["type"] 和 options["updateViaCache"]。

  8. 返回 p

getRegistration(clientURL) 方法的步骤是:

  1. client此对象服务工作线程客户端

  2. storage key 为运行获取存储密钥(给定 client)的结果。

  3. clientURL解析 clientURL(使用此对象相关设置对象API 基础 URL)的结果。

  4. 如果 clientURL 解析失败,则返回一个被 TypeError 拒绝的promise

  5. clientURL片段设置为 null。

  6. 如果 clientURL不是 client,则返回一个被 "SecurityError" DOMException 拒绝的 promise

  7. promise 为一个新的promise

  8. 并行运行以下子步骤:

    1. registration 为运行匹配服务工作线程注册(给定 storage keyclientURL)的结果。

    2. 如果 registration 为 null,则使用 undefined 解析 promise 并中止这些步骤。

    3. 使用获取服务工作线程注册对象(表示 promise相关设置对象中的 registration)的结果来解析 promise

  9. 返回 promise

getRegistrations() 方法的步骤是:

  1. client此对象服务工作线程客户端

  2. client storage key 为运行获取存储密钥(给定 client)的结果。

  3. promise一个新的 promise

  4. 并行运行以下步骤:

    1. registrations 为一个新的列表

    2. 对于注册映射中的每个 (storage key, scope) → registration

      1. 如果 storage key 等于 client storage key,则将 registration 追加registrations

    3. promise相关设置对象负责事件循环上,使用DOM 操作任务源将任务排队以运行以下步骤:

      1. registrationObjects 为一个新的列表

      2. 对于 registrations 中的每个 registration

        1. registrationObj获取服务工作线程注册对象(表示 promise相关设置对象中的 registration)的结果。

        2. registrationObj 追加registrationObjects

      3. 使用 promise相关 Realm 中的 registrationObjects新的冻结数组来解析 promise

  5. 返回 promise

startMessages() 方法的步骤是:如果此对象客户端消息队列未启用,则启用它。

3.4.7. 事件处理程序

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

事件处理程序 事件处理程序事件类型
oncontrollerchange controllerchange
onmessage message
onmessageerror messageerror

首次执行 onmessage setter 步骤时,启用此对象客户端消息队列

3.5. 事件

以下事件在 ServiceWorker 对象上分派:

事件名称 接口 分派时机…
statechange Event ServiceWorker 对象的 state 属性已更改。

以下事件在 ServiceWorkerRegistration 对象上分派:

事件名称 接口 分派时机…
updatefound Event 服务工作线程注册安装中工作线程发生更改。(请参阅安装算法的步骤 8。)

以下事件在 ServiceWorkerContainer 对象上分派:

事件名称 接口 分派时机…
controllerchange Event 服务工作线程客户端活动服务工作线程发生更改。(请参阅激活算法的步骤 9.2。服务工作线程跳过等待标志会导致服务工作线程注册激活服务工作线程客户端使用服务工作线程注册时发生,navigator.serviceWorker.controller 会立即将活动工作线程反映为控制服务工作线程客户端服务工作线程。)
message Event 服务工作线程客户端服务工作线程接收到一条消息。请参阅 postMessage(message, options)
messageerror Event 服务工作线程客户端服务工作线程收到一条无法反序列化的消息。请参阅 postMessage(message, options)
[SecureContext, Exposed=(Window,Worker)]
        interface NavigationPreloadManager {
          Promise<undefined> enable();
          Promise<undefined> disable();
          Promise<undefined> setHeaderValue(ByteString value);
          Promise<NavigationPreloadState> getState();
        };
        
        dictionary NavigationPreloadState {
          boolean enabled = false;
          ByteString headerValue;
        };
        

enable() 方法的步骤是:

  1. promise一个新的 promise

  2. 并行运行以下步骤:

    1. registration此对象关联的服务工作线程注册

    2. 如果 registration活动工作线程为 null,则用 "InvalidStateError" DOMException 拒绝 promise,并中止这些步骤。

    3. 设置 registration导航预加载启用标志

    4. 用 undefined 解析 promise

  3. 返回 promise

disable() 方法的步骤是:

  1. promise一个新的 promise

  2. 并行运行以下步骤:

    1. registration此对象关联的服务工作线程注册

    2. 如果 registration活动工作线程为 null,则用 "InvalidStateError" DOMException 拒绝 promise,并中止这些步骤。

    3. 取消设置 registration导航预加载启用标志

    4. 用 undefined 解析 promise

  3. 返回 promise

setHeaderValue(value) 方法的步骤是:

  1. promise一个新的 promise

  2. 并行运行以下步骤:

    1. registration此对象关联的服务工作线程注册

    2. 如果 registration活动工作线程为 null,则用 "InvalidStateError" DOMException 拒绝 promise,并中止这些步骤。

    3. registration导航预加载标头值设置为 value

    4. 用 undefined 解析 promise

  3. 返回 promise

getState() 方法的步骤是:

  1. promise一个新的 promise

  2. 并行运行以下步骤:

    1. registration此对象关联的服务工作线程注册

    2. state 为一个新的 NavigationPreloadState 字典。

    3. 如果 registration导航预加载启用标志已设置,则将 state["enabled"] 设置为 true。

    4. state["headerValue"] 设置为 registration导航预加载标头值

    5. state 解析 promise

  3. 返回 promise

4. 执行上下文

提供缓存资源:
// caching.js
        self.addEventListener("install", event => {
          event.waitUntil(
            // 打开资源缓存。
            caches.open("shell-v1").then(cache => {
              // 开始获取它们的过程。仅当所有资源都已存储时才成功。
              // 即使只有一个资源失败,也会导致整个操作失败。
              return cache.addAll([
                "/app.html",
                "/assets/v1/base.css",
                "/assets/v1/app.js",
                "/assets/v1/logo.png",
                "/assets/v1/intro_video.webm"
              ]);
            })
          );
        });
        
        self.addEventListener("fetch", event => {
          // 在服务工作线程成功安装并激活之前,不会向其分派任何 "fetch" 事件。
        
          // 缓存上的所有操作(包括匹配 URL)都是异步的,因此我们大量使用 promise。
          // e.respondWith()甚至接受 promise 来启用此功能:
          event.respondWith(
            caches.match(e.request).then(response => {
              return response || fetch(e.request);
            }).catch(() => {
              return caches.match("/fallback.html");
            })
          );
        });
        

4.1. ServiceWorkerGlobalScope 接口

[Global=(Worker,ServiceWorker), Exposed=ServiceWorker, SecureContext]
        interface ServiceWorkerGlobalScope : WorkerGlobalScope {
          [SameObject] readonly attribute Clients clients;
          [SameObject] readonly attribute ServiceWorkerRegistration registration;
          [SameObject] readonly attribute ServiceWorker serviceWorker;
        
          [NewObject] Promise<undefined> skipWaiting();
        
          attribute EventHandler oninstall;
          attribute EventHandler onactivate;
          attribute EventHandler onfetch;
        
          attribute EventHandler onmessage;
          attribute EventHandler onmessageerror;
        };
        

ServiceWorkerGlobalScope 对象表示服务工作线程的全局执行上下文。

ServiceWorkerGlobalScope 对象具有关联的服务工作线程(一个服务工作线程)。

ServiceWorkerGlobalScope 对象具有关联的导入脚本强制绕过缓存标志。它最初未设置。

ServiceWorkerGlobalScope 对象具有关联的竞争响应映射,它是一个有序映射,其中请求竞争响应

竞争响应是一个结构体,用于在 "race-network-and-fetch-handler" 执行时包含网络响应。它有一个,可以是一个响应、“pending”或 null。

注意:ServiceWorkerGlobalScope 对象提供在源上运行的通用的、事件驱动的、有时间限制的脚本执行上下文。一旦成功注册服务工作线程将根据其与事件的关系(而非服务工作线程客户端)启动、保持活动状态和终止。不得在服务工作线程内部发起任何类型的同步请求。

4.1.1. clients

clients getter 的步骤是返回与此对象关联的 Clients 对象。

4.1.2. registration

registration getter 的步骤是返回获取服务工作线程注册对象的结果,该对象表示此对象服务工作线程包含服务工作线程注册此对象相关设置对象中。

4.1.3. serviceWorker

serviceWorker getter 的步骤是返回获取服务工作线程对象的结果,该对象表示此对象服务工作线程此对象相关设置对象中。

4.1.4. skipWaiting()

注意:skipWaiting() 方法允许此服务工作线程注册等待位置前进到活动位置,即使服务工作线程客户端正在使用注册

skipWaiting() 方法的步骤是:

  1. promise 为一个新的promise

  2. 并行运行以下子步骤:

    1. 设置服务工作线程跳过等待标志

    2. 使用服务工作线程包含服务工作线程注册调用尝试激活

    3. 用 undefined 解析 promise

  3. 返回 promise

4.1.5. 事件处理程序

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

事件处理程序 事件处理程序事件类型
oninstall install
onactivate activate
onfetch fetch
onmessage message
onmessageerror messageerror

4.2. Client 接口

[Exposed=ServiceWorker]
            interface Client {
              readonly attribute USVString url;
              readonly attribute FrameType frameType;
              readonly attribute DOMString id;
              readonly attribute ClientType type;
              undefined postMessage(any message, sequence<object> transfer);
              undefined postMessage(any message, optional StructuredSerializeOptions options = {});
            };
            
            [Exposed=ServiceWorker]
            interface WindowClient : Client {
              readonly attribute VisibilityState visibilityState;
              readonly attribute boolean focused;
              [SameObject] readonly attribute FrozenArray<USVString> ancestorOrigins;
              [NewObject] Promise<WindowClient> focus();
              [NewObject] Promise<WindowClient?> navigate(USVString url);
            };
            
            enum FrameType {
              "auxiliary",
              "top-level",
              "nested",
              "none"
            };
            

Client 对象具有关联的服务工作线程客户端(一个服务工作线程客户端)。

Client 对象具有关联的框架类型,它是 "auxiliary"、"top-level"、"nested" 和 "none" 之一。除非另有说明,否则为 "none"。

WindowClient 对象具有关联的浏览上下文,即其服务工作线程客户端全局对象浏览上下文

WindowClient 对象具有关联的可见性状态,它是 visibilityState 属性值之一。

WindowClient 对象具有关联的焦点状态,其值为 true 或 false(初始为 false)。

WindowClient 对象具有关联的祖先源数组

4.2.1. url

url getter 的步骤是返回此对象关联的服务工作线程客户端序列化创建 URL

4.2.2. frameType

frameType getter 的步骤是返回此对象框架类型

4.2.3. id

id getter 的步骤是返回此对象关联的服务工作线程客户端id

4.2.4. type

type getter 的步骤是:

  1. client此对象服务工作线程客户端

  2. 如果 client 是一个环境设置对象,则:

    1. 如果 client 是一个窗口客户端,返回 "window"

    2. 否则,如果 client 是一个专用工作线程客户端,返回 "worker"

    3. 否则,如果 client 是一个共享工作线程客户端,返回 "sharedworker"

  3. 否则:

    1. 返回 "window"

4.2.5. postMessage(message, transfer)

postMessage(message, transfer) 方法的步骤是:

  1. options 为 «[ "transfer" → transfer ]»。

  2. 使用 messageoptions 作为参数调用 postMessage(message, options)

4.2.6. postMessage(message, options)

postMessage(message, options) 方法的步骤是:

  1. contextObject此对象

  2. sourceSettingscontextObject相关设置对象

  3. serializeWithTransferResultStructuredSerializeWithTransfer(message, options["transfer"])。 重新抛出任何异常。

  4. 并行运行以下步骤:

    1. targetClient 为 null。

    2. 对于每个服务工作线程客户端 client

      1. 如果 clientcontextObject服务工作线程客户端,则将 targetClient 设置为 client,并中断

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

    4. destination 为其关联的服务工作线程客户端targetClientServiceWorkerContainer 对象。

    5. destination客户端消息队列添加一个运行以下步骤的任务

      1. originsourceSettings序列化

      2. source获取服务工作线程对象的结果,该对象表示 contextObject相关全局对象服务工作线程targetClient 中。

      3. deserializeRecordStructuredDeserializeWithTransfer(serializeWithTransferResult, destination相关 Realm)。

        如果此操作引发异常,则捕获该异常,使用 MessageEventdestination触发名为 messageerror 的事件,并将 origin 属性初始化为 origin,将 source 属性初始化为 source,然后中止这些步骤。

      4. messageClonedeserializeRecord.[[Deserialized]]。

      5. newPorts 为一个新的冻结数组,其中包含 deserializeRecord.[[TransferredValues]] 中的所有 MessagePort 对象(如果存在)。

      6. 使用 MessageEventdestination分派名为 message 的事件,并将 origin 属性初始化为 origin,将 source 属性初始化为 source,将 data 属性初始化为 messageClone,并将 ports 属性初始化为 newPorts

4.2.7. visibilityState

visibilityState getter 的步骤是返回此对象可见性状态

4.2.8. focused

focused getter 的步骤是返回此对象焦点状态

4.2.9. ancestorOrigins

ancestorOrigins getter 的步骤是返回此对象关联的祖先源数组

4.2.10. focus()

focus() 方法的步骤是:

  1. 如果此中没有 Window 具有瞬时激活,则返回一个用 "InvalidAccessError" DOMException 拒绝的 promise

  2. serviceWorkerEventLoop周围代理事件循环

  3. promise 为一个新的promise

  4. 使用用户交互任务源,在此对象关联的服务工作线程客户端负责事件循环排队一个任务以运行以下步骤:

    1. 使用此对象浏览上下文运行聚焦步骤

    2. frameType 为使用此对象浏览上下文运行获取框架类型的结果。

    3. visibilityState此对象浏览上下文活动文档visibilityState 属性值。

    4. focusState 为使用此对象浏览上下文活动文档运行具有焦点步骤的结果。

    5. ancestorOriginsList此对象浏览上下文活动文档相关全局对象Location 对象的祖先源列表的关联列表。

    6. 使用DOM 操作任务源,在 serviceWorkerEventLoop排队一个任务以运行以下步骤:

      1. windowClient 为使用此对象关联的服务工作线程客户端frameTypevisibilityStatefocusStateancestorOriginsList 运行创建窗口客户端的结果。

      2. 如果 windowClient焦点状态为 true,则用 windowClient 解析 promise

      3. 否则,用 TypeError 拒绝 promise

  5. 返回 promise

4.2.11. navigate(url)

navigate(url) 方法的步骤是:

  1. url 为使用此对象相关设置对象API 基本 URL 解析 url 的结果。

  2. 如果 url 解析失败,则返回一个用 TypeError 拒绝的 promise

  3. 如果 urlabout:blank,则返回一个用 TypeError 拒绝的 promise

  4. 如果此对象关联的服务工作线程客户端活动服务工作线程不是此对象相关全局对象服务工作线程,则返回一个用 TypeError 拒绝的 promise

  5. serviceWorkerEventLoop当前全局对象事件循环

  6. promise 为一个新的promise

  7. 使用用户交互任务源,在此对象关联的服务工作线程客户端负责事件循环排队一个任务以运行以下步骤:

    1. browsingContext此对象浏览上下文

    2. 如果 browsingContext关联文档完全激活,则使用DOM 操作任务源serviceWorkerEventLoop排队一个任务以用 TypeError 拒绝 promise,并中止这些步骤。

    3. HandleNavigate:使用 browsingContext关联文档,将 browsingContext 导航url,并将exceptionsEnabled 设置为 true。

    4. 如果在标记为 HandleNavigate 的步骤中调用的算法步骤抛出异常,则使用DOM 操作任务源serviceWorkerEventLoop排队一个任务以用该异常拒绝 promise,并中止这些步骤。

    5. frameType 为使用 browsingContext 运行获取框架类型的结果。

    6. visibilityStatebrowsingContext活动文档visibilityState 属性值。

    7. focusState 为使用 browsingContext活动文档运行具有焦点步骤的结果。

    8. ancestorOriginsListbrowsingContext活动文档相关全局对象Location 对象的祖先源列表的关联列表。

    9. 使用DOM 操作任务源,在 serviceWorkerEventLoop排队一个任务以运行以下步骤:

      1. 如果 browsingContextWindow 对象的环境设置对象创建 URL服务工作线程相同,则用 null 解析 promise 并中止这些步骤。

      2. windowClient 为使用此对象服务工作线程客户端frameTypevisibilityStatefocusStateancestorOriginsList 运行创建窗口客户端的结果。

      3. windowClient 解析 promise

  8. 返回 promise

4.3. Clients 接口

[Exposed=ServiceWorker]
            interface Clients {
              // 返回的对象每次都将是新的实例
              [NewObject] Promise<(Client or undefined)> get(DOMString id);
              [NewObject] Promise<FrozenArray<Client>> matchAll(optional ClientQueryOptions options = {});
              [NewObject] Promise<WindowClient?> openWindow(USVString url);
              [NewObject] Promise<undefined> claim();
            };
            
dictionary ClientQueryOptions {
              boolean includeUncontrolled = false;
              ClientType type = "window";
            };
            
enum ClientType {
              "window",
              "worker",
              "sharedworker",
              "all"
            };
            

用户代理在创建 ServiceWorkerGlobalScope 对象时必须创建一个 Clients 对象,并将其与该对象关联。

4.3.1. get(id)

get(id) 方法的步骤是:

  1. promise 为一个新的 promise

  2. 并行运行这些子步骤:

    1. 对于每个服务工作线程客户端 client,其中运行获取存储密钥(给定 client)的结果等于关联的服务工作线程包含的服务工作线程注册存储密钥

      1. 如果 clientid 不是 id,则继续

      2. 等待 client执行就绪标志被设置,或者 client丢弃标志被设置。

      3. 如果 client执行就绪标志已设置,则使用 clientpromise 调用解析 Get Client Promise,并中止这些步骤。

    2. 用 undefined 解析 promise

  3. 返回 promise

4.3.2. matchAll(options)

matchAll(options) 方法的步骤是:

  1. promise一个新的 promise

  2. 并行运行以下步骤:

    1. targetClients 为一个新的列表

    2. 对于每个服务工作线程客户端 client,其中运行获取存储密钥(给定 client)的结果等于关联的服务工作线程包含的服务工作线程注册存储密钥

      1. 如果 client执行就绪标志未设置,或者 client丢弃标志已设置,则继续

      2. 如果 client 不是安全上下文,则继续

      3. 如果 options["includeUncontrolled"] 为 false,并且如果 client活动服务工作线程不是关联的服务工作线程,则继续

      4. client 添加到 targetClients

    3. matchedWindowData 为一个新的列表

    4. matchedClients 为一个新的列表

    5. 对于 targetClients 中的每个服务工作线程客户端 client

      1. 如果 options["type"] 是 "window""all",并且 client 不是环境设置对象或者是窗口客户端,则:

        1. windowData 为 «[ "client" → client, "ancestorOriginsList" → 一个新的列表 ]»。

        2. browsingContext 为 null。

        3. isClientEnumerable 为 true。

        4. 如果 client 是一个环境设置对象,则将 browsingContext 设置为 client全局对象浏览上下文

        5. 否则,将 browsingContext 设置为 client目标浏览上下文

        6. 使用用户交互任务源,在 browsingContext事件循环排队一个任务 task 以运行以下子步骤:

          1. 如果 browsingContext 已被丢弃,则将 isClientEnumerable 设置为 false 并中止这些步骤。

          2. 如果 client 是一个窗口客户端并且 client关联文档不是 browsingContext活动文档,则将 isClientEnumerable 设置为 false 并中止这些步骤。

          3. windowData["frameType"] 设置为使用 browsingContext 运行获取框架类型的结果。

          4. windowData["visibilityState"] 设置为 browsingContext活动文档visibilityState 属性值。

          5. windowData["focusState"] 设置为使用 browsingContext活动文档作为参数运行具有焦点步骤的结果。

          6. 如果 client 是一个窗口客户端,则将 windowData["ancestorOriginsList"] 设置为 browsingContext活动文档相关全局对象Location 对象的祖先源列表的关联列表。

        7. 等待 task 执行完毕。

          注意:等待是阻塞等待,但实现者可以并行运行迭代,只要状态不被破坏。

        8. 如果 isClientEnumerable 为 true,则:

          1. windowData 添加到 matchedWindowData

      2. 否则,如果 options["type"] 是 "worker""all" 并且 client 是一个专用工作线程客户端,或者 options["type"] 是 "sharedworker""all" 并且 client 是一个共享工作线程客户端,则:

        1. client 添加到 matchedClients

    6. 使用DOM 操作任务源,在 promise相关设置对象负责事件循环排队一个任务以运行以下步骤:

      1. clientObjects 为一个新的列表

      2. 对于 matchedWindowData 中的每个 windowData

        1. windowClient 为使用 windowData["client"]、windowData["frameType"]、windowData["visibilityState"]、windowData["focusState"] 和 windowData["ancestorOriginsList"] 作为参数运行创建窗口客户端算法的结果。

        2. 追加 windowClientclientObjects

      3. 对于 matchedClients 中的每个 client

        1. clientObject 为使用 client 作为参数运行创建客户端算法的结果。

        2. 追加 clientObjectclientObjects

      4. clientObjects 进行排序,使得:

        注意:窗口客户端始终排在工作线程客户端之前。

      5. promise相关 Realm 中的 clientObjects新的冻结数组解析 promise

  3. 返回 promise

4.3.3. openWindow(url)

openWindow(url) 方法的步骤如下:

  1. url 为使用此对象相关设置对象API 基本 URL 解析 url 的结果。

  2. 如果 url 解析失败,则返回一个用 TypeError 拒绝的 promise

  3. 如果 urlabout:blank,则返回一个用 TypeError 拒绝的 promise

  4. 如果此中没有 Window 具有瞬时激活,则返回一个用 "InvalidAccessError" DOMException 拒绝的 promise

  5. serviceWorkerEventLoop当前全局对象事件循环

  6. promise 为一个新的promise

  7. 并行运行这些子步骤:

    1. newContext 为一个新的顶级浏览上下文

    2. 使用用户交互任务源,在 newContextWindow 对象的环境设置对象负责事件循环排队一个任务以运行以下步骤:

      1. HandleNavigate:将 newContext 导航url,并将exceptionsEnabled 设置为 true,historyHandling 设置为 "replace"。

      2. 如果在标记为 HandleNavigate 的步骤中调用的算法步骤抛出异常,则使用DOM 操作任务源serviceWorkerEventLoop排队一个任务以用该异常拒绝 promise,并中止这些步骤。

      3. frameType 为使用 newContext 运行获取框架类型的结果。

      4. visibilityStatenewContext活动文档visibilityState 属性值。

      5. focusState 为使用 newContext活动文档作为参数运行具有焦点步骤的结果。

      6. ancestorOriginsListnewContext活动文档相关全局对象Location 对象的祖先源列表的关联列表。

      7. 使用DOM 操作任务源serviceWorkerEventLoop排队一个任务以运行以下步骤:

        1. 如果运行获取存储密钥(给定 newContextWindow 对象的环境设置对象)的结果与服务工作线程包含的服务工作线程注册存储密钥相等,则用 null 解析 promise 并中止这些步骤。

        2. client 为使用 newContextWindow 对象的环境设置对象frameTypevisibilityStatefocusStateancestorOriginsList 作为参数运行创建窗口客户端的结果。

        3. client 解析 promise

  8. 返回 promise

4.3.4. claim()

claim() 方法的步骤如下:

  1. 如果服务工作线程不是活动工作线程,则返回一个用 "InvalidStateError" DOMException 拒绝的 promise

  2. promise 为一个新的promise

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

    1. 对于每个服务工作线程客户端 client,其中运行获取存储密钥(给定 client)的结果等于服务工作线程包含的服务工作线程注册存储密钥

      1. 如果 client执行就绪标志未设置,或者 client丢弃标志已设置,则继续

      2. 如果 client 不是安全上下文,则继续

      3. storage key 为运行获取存储密钥(给定 client)的结果。

      4. registration 为运行匹配服务工作线程注册(给定 storage keyclient创建 URL)的结果。

      5. 如果 registration 不是服务工作线程包含的服务工作线程注册,则继续

        注意:如果服务工作线程包含的服务工作线程注册注销,则 registration 将为 null。

      6. 如果 client活动服务工作线程不是服务工作线程,则:

        1. 使用 client 作为参数调用处理服务工作线程客户端卸载

        2. client活动服务工作线程设置为服务工作线程

        3. 使用 client 作为参数调用通知控制器更改算法。

    2. 用 undefined 解析 promise

  4. 返回 promise

4.4. ExtendableEvent 接口

[Exposed=ServiceWorker]
            interface ExtendableEvent : Event {
              constructor(DOMString type, optional ExtendableEventInit eventInitDict = {});
              undefined waitUntil(Promise<any> f);
            };
            
dictionary ExtendableEventInit : EventInit {
              // 为派生事件的向前兼容性定义
            };
            

一个 ExtendableEvent 对象有一个关联的延长生命周期 promise(一个promise数组)。它最初是一个空数组。

一个 ExtendableEvent 对象有一个关联的待处理 promise 计数延长生命周期 promise中待处理 promise 的数量)。它最初设置为零。

一个 ExtendableEvent 对象有一个关联的超时标志。它最初未设置,如果在待处理 promise 计数大于零的情况下,经过可选的用户代理施加的延迟后设置。

当一个 ExtendableEvent 对象的超时标志未设置,并且其待处理 promise 计数大于零或其分派标志已设置时,称该对象是活动的

服务工作线程有两个生命周期事件installactivate服务工作线程activate 事件和 install 事件使用 ExtendableEvent 接口。

服务工作线程扩展定义事件处理程序可以使用或扩展 ExtendableEvent 接口。

4.4.1. event.waitUntil(f)

注意:waitUntil() 方法延长事件的生命周期。

waitUntil(f) 方法的步骤是将生命周期 promise f 添加到此对象

要将生命周期 promise 添加event(一个 ExtendableEvent)中的 promise(一个promise),请运行以下步骤:
  1. 如果 eventisTrusted 属性为 false,则抛出一个 "InvalidStateError" DOMException

  2. 如果 event 不是活动的,则抛出一个 "InvalidStateError" DOMException

    注意:如果在调用事件处理程序的任务中没有添加生命周期延长 promise,则在后续异步任务中调用 waitUntil() 将会抛出异常。

  3. promise 添加到 event延长生命周期 promise中。

  4. event待处理 promise 计数增加一。

    注意:即使给定的 promise 已经解决,待处理 promise 计数也会增加。相应的计数减少是在对 promise 的反应所排队的微任务中完成的。

  5. promise 兑现拒绝时,排队一个微任务以运行这些子步骤:

    1. event待处理 promise 计数减少一。

    2. 如果 event待处理 promise 计数为 0,则:

      1. registration当前全局对象的关联服务工作线程包含的服务工作线程注册

      2. 如果 registration注销,则使用 registration 调用尝试清除注册

      3. 如果 registration 不为 null,则使用 registration 调用尝试激活

如果服务工作线程没有待处理事件对该服务工作线程返回 false,则用户代理不应终止服务工作线程

服务工作线程扩展定义事件处理程序可以定义它们自己的行为,允许延长生命周期 promise建议操作长度,并且延长生命周期 promise中的任何promise的拒绝状态建议操作失败。

注意:服务工作线程会延迟将安装中的工作线程视为“installed”,直到 install 事件的延长生命周期 promise中的所有promise都成功解决。(请参阅相关的安装算法步骤。)如果任何 promise 被拒绝,则安装失败。这主要用于确保在服务工作线程所依赖的所有核心缓存都填充完毕之前,不将其视为“installed”。同样,服务工作线程会延迟将活动工作线程视为“activated”,直到 activate 事件的延长生命周期 promise中的所有promise都解决。(请参阅相关的激活算法步骤。)这主要用于确保在服务工作线程升级数据库模式并删除过时的缓存条目之前,不会向其分派任何功能性事件

4.5. InstallEvent 接口

[Exposed=ServiceWorker]
            interface InstallEvent : ExtendableEvent {
              constructor(DOMString type, optional ExtendableEventInit eventInitDict = {});
              Promise<undefined> addRoutes((RouterRule or sequence<RouterRule>) rules);
            };
            
            dictionary RouterRule {
              required RouterCondition condition;
              required RouterSource source;
            };
            
            dictionary RouterCondition {
              URLPatternCompatible urlPattern;
              ByteString requestMethod;
              RequestMode requestMode;
              RequestDestination requestDestination;
              RunningStatus runningStatus;
            
              sequence<RouterCondition> _or;
              RouterCondition not;
            };
            
            typedef (RouterSourceDict or RouterSourceEnum) RouterSource;
            
            dictionary RouterSourceDict {
              DOMString cacheName;
            };
            
            enum RunningStatus { "running", "not-running" };
            enum RouterSourceEnum {
              "cache",
              "fetch-event",
              "network",
              "race-network-and-fetch-handler"
            };
            

计数路由器条件结果是一个结构体,包含:

  • 条件计数(一个数字)。

  • 超出配额(一个布尔值)。

4.5.1. event.addRoutes(rules)

注意:addRoutes(rules) 为此服务工作线程注册规则,以卸载 fetch 事件处理程序通常执行的简单任务。

addRoutes(rules) 方法的步骤是:

  1. 如果 rules 是一个 RouterRule 字典,则将 rules 设置为 « rules »。

  2. serviceWorker当前全局对象的关联服务工作线程

  3. 对于 rules 中的每个 rule

    1. 如果使用 rule["condition"] 和 serviceWorker 运行验证路由器条件算法返回 false,则返回一个被拒绝的 promise,其值为 TypeError

    2. 如果 rule["source"] 是 "fetch-event" 或 "race-network-and-fetch-handler",并且 serviceWorker要处理的事件类型集合包含 fetch,则返回一个被拒绝的 promise,其值为 TypeError

  4. lifetimePromise 为一个新的promise

  5. 生命周期 promise 添加 lifetimePromise此对象

    注意:event.addRoutes(rules) 默认延长事件的生命周期,就像调用了 event.waitUntil(promise) 一样。

  6. promise 为一个新的promise

  7. promise 兑现拒绝时,用 undefined 解析 lifetimePromise

    注意:此步骤是为了使 lifetimePromise 始终被兑现,以避免安装事件失败。

  8. 将以下步骤加入队列[[service worker queue]]

    1. allRulesserviceWorker路由器规则列表的副本。

    2. 对于 rules 中的每个 rule

      1. rule 追加到 allRules

    3. 如果使用 allRules 运行检查路由器注册限制返回 false,则用 TypeError 拒绝 promise

    4. serviceWorker路由器规则列表设置为 allRules

    5. serviceWorkerEventLoop当前全局对象事件循环

    6. 使用DOM 操作任务源,在 serviceWorkerEventLoop排队一个任务以运行以下步骤:

      1. 用 undefined 解析 promise

  9. 返回 promise

4.6. FetchEvent 接口

[Exposed=ServiceWorker]
            interface FetchEvent : ExtendableEvent {
              constructor(DOMString type, FetchEventInit eventInitDict);
              [SameObject] readonly attribute Request request;
              readonly attribute Promise<any> preloadResponse;
              readonly attribute DOMString clientId;
              readonly attribute DOMString resultingClientId;
              readonly attribute DOMString replacesClientId;
              readonly attribute Promise<undefined> handled;
            
              undefined respondWith(Promise<Response> r);
            };
            
dictionary FetchEventInit : ExtendableEventInit {
              required Request request;
              Promise<any> preloadResponse;
              DOMString clientId = "";
              DOMString resultingClientId = "";
              DOMString replacesClientId = "";
              Promise<undefined> handled;
            };
            

Service workers 有一个重要的功能性事件 fetch。对于 fetch 事件,service workers 使用 FetchEvent 接口,该接口扩展了 ExtendableEvent 接口。

每个使用 FetchEvent 接口的事件都有一个关联的潜在响应(一个 response),初始设置为 null,以及以下初始未设置的关联标志:

  • 等待响应标志

  • respond-with 进入标志

  • respond-with 错误标志

4.6.1. event.request

request 属性必须返回其初始化时的值。

4.6.2. event.preloadResponse

preloadResponse 属性必须返回其初始化时的值。创建事件时,该属性必须初始化为一个已解决的 promise,其值为 undefined。

4.6.3. event.clientId

clientId 属性必须返回其初始化时的值。创建事件时,该属性必须初始化为空字符串。

4.6.4. event.resultingClientId

resultingClientId 属性必须返回其初始化时的值。创建事件时,该属性必须初始化为空字符串。

4.6.5. event.replacesClientId

replacesClientId 属性必须返回其初始化时的值。创建事件时,该属性必须初始化为空字符串。

4.6.6. event.handled

handled 属性必须返回其初始化时的值。创建事件时,该属性必须初始化为一个待定的 promise

4.6.7. event.respondWith(r)

注意:开发者可以使用一个解析为 Response 对象的 promise 或一个 Response 对象(它会自动转换为 promise)来设置参数 r。否则,将向 Fetch 返回一个网络错误。关于跨域内容污染的渲染器端安全检查与 Fetch 中定义的过滤响应类型相关联。

respondWith(r) 方法的步骤是:

  1. event此对象

  2. 如果 event分派标志未设置,则抛出一个 "InvalidStateError" DOMException

  3. 如果 eventrespond-with 进入标志已设置,则抛出一个 "InvalidStateError" DOMException

  4. 生命周期 promise 添加 revent

    注意:event.respondWith(r) 默认延长事件的生命周期,就像调用了 event.waitUntil(r) 一样。

  5. 设置 event停止传播标志立即停止传播标志

  6. 设置 eventrespond-with 进入标志

  7. 设置 event等待响应标志

  8. targetRealmevent相关 Realm

  9. r 被拒绝时:

    1. 设置 eventrespond-with 错误标志

    2. 取消设置 event等待响应标志

  10. rresponse 兑现时:

    1. 如果 response 不是一个 Response 对象,则设置respond-with 错误标志

      注意:如果respond-with 错误标志已设置,则通过处理 Fetch 算法将网络错误返回给 Fetch。(参见步骤 21.1。)否则,通过处理 Fetch 算法将值 response 返回给 Fetch。(参见步骤 22.1。)

    2. 否则:

      1. bytes 为一个空字节序列。

      2. end-of-body 为 false。

      3. done 为 false。

      4. potentialResponseresponse 的关联响应的副本,但其主体除外。

      5. 如果 response主体非空,则运行这些子步骤:

        1. reader 为从 response主体获取读取器的结果。

        2. pullAlgorithm 为运行这些步骤的操作:

          1. readRequest 为一个新的读取请求,包含以下项目

            给定 chunk块步骤
            1. 断言:chunk 是一个 Uint8Array

            2. chunk 表示的字节追加到 bytes

            3. potentialResponse主体信息编码大小增加 bytes字节长度

            4. potentialResponse主体信息解码大小增加 bytes字节长度

            5. 执行 ! DetachArrayBuffer(chunk.[[ViewedArrayBuffer]])。

            关闭步骤
            1. end-of-body 设置为 true。

            错误步骤
            1. TypeError 错误 newStream

          2. 给定 readRequest,从 reader 读取一个块

        3. cancelAlgorithm取消 reader 的操作。

        4. highWaterMark 为用户代理选择的非负、非 NaN 数字。

        5. sizeAlgorithm 为一个接受对象并返回用户代理选择的非负、非 NaN、非无穷大数字的算法。

        6. newStream 为一个新的 ReadableStream,在 targetRealm 中使用pullAlgorithm pullAlgorithmcancelAlgorithm cancelAlgorithmhighWaterMark highWaterMarksizeAlgorithm sizeAlgorithm 设置

        7. potentialResponse主体设置为一个新的主体,其newStream

        8. done 为 false 时,并行重复运行这些子步骤:

          1. 如果 newStream 出错,则将 done 设置为 true。

          2. 否则,如果 bytes 为空且 end-of-body 为 true,则关闭 newStream 并将 done 设置为 true。

          3. 否则,如果 bytes 不为空,则运行这些子子步骤:

            1. chunk 为从 bytes 开头开始的 bytes 的子序列。

            2. bytes 中移除 chunk

            3. buffer 为在 targetRealm 中创建并包含 chunkArrayBuffer 对象。

            4. 将一个在 targetRealm 中创建并包装 bufferUint8Array 对象加入队列newStream

        注意:这些子步骤旨在产生与将 response主体“管道化”到 potentialResponse 相同的可观察结果。

        注意:service worker 以块形式写入的数据不保证接收数据的客户端以相同的块形式读取。也就是说,客户端将读取写入的相同数据,但浏览器可能会以不同的方式对其进行分块。

      6. event潜在响应设置为 potentialResponse

    3. 取消设置 event等待响应标志

4.7. ExtendableMessageEvent 接口

[Exposed=ServiceWorker]
interface ExtendableMessageEvent : ExtendableEvent {
    constructor(DOMString type, optional ExtendableMessageEventInit eventInitDict = {});
    readonly attribute any data;
    readonly attribute USVString origin;
    readonly attribute DOMString lastEventId;
    [SameObject] readonly attribute (Client or ServiceWorker or MessagePort)? source;
    readonly attribute FrozenArray<MessagePort> ports;
};
dictionary ExtendableMessageEventInit : ExtendableEventInit {
    any data = null;
    USVString origin = "";
    DOMString lastEventId = "";
    (Client or ServiceWorker or MessagePort)? source = null;
    sequence<MessagePort> ports = [];
};

服务工作线程定义了可扩展的 message 事件,以允许延长事件的生命周期。对于 message 事件,服务工作线程使用 ExtendableMessageEvent 接口,该接口扩展了 ExtendableEvent 接口。

4.7.1. event.data

data 属性必须返回其初始化时的值。创建对象时,此属性必须初始化为 null。它表示正在发送的消息。

4.7.2. event.origin

origin 属性必须返回其初始化时的值。创建对象时,此属性必须初始化为空字符串。它表示发送消息的服务工作线程客户端

4.7.3. event.lastEventId

lastEventId 属性必须返回其初始化时的值。创建对象时,此属性必须初始化为空字符串。

4.7.4. event.source

source 属性必须返回其初始化时的值。创建对象时,此属性必须初始化为 null。它表示从中发送消息的 Client 对象。

4.7.5. event.ports

ports 属性必须返回其初始化时的值。创建对象时,此属性必须初始化为空数组。它表示正在发送的 MessagePort 数组。

4.8. 事件

以下事件(称为服务工作线程事件)在 ServiceWorkerGlobalScope 对象上分派:

事件名称 接口 类别 分派时机…
install InstallEvent 生命周期 服务工作线程所属服务工作线程注册安装中工作线程发生更改时。(参见安装算法的步骤 11.2。)
activate ExtendableEvent 生命周期 服务工作线程所属服务工作线程注册活动工作线程发生更改时。(参见激活算法的步骤 12.2。)
fetch FetchEvent 功能性 http fetch 调用处理 Fetch 并传入 request 时。执行处理 Fetch 的结果是,服务工作线程http fetch 返回一个响应。该响应(由 Response 对象表示)可以从 Cache 对象中检索,也可以使用 self.fetch(input, init) 方法直接从网络获取。(自定义 Response 对象是另一种选择。)
push PushEvent 功能性 (参见触发推送事件。)
notificationclick NotificationEvent 功能性 (参见激活通知。)
notificationclose NotificationEvent 功能性 (参见关闭通知。)
sync SyncEvent 功能性 (参见触发同步事件。)
canmakepayment CanMakePaymentEvent 功能性 (参见处理 CanMakePaymentEvent。)
paymentrequest PaymentRequestEvent 功能性 (参见处理 PaymentRequestEvent。)
message ExtendableMessageEvent 旧版 当它收到一条消息时。
messageerror MessageEvent 旧版 当它收到一条无法反序列化的消息时。

5. 缓存

为了让开发者能够完全管理其内容缓存以供离线使用,WindowWorkerGlobalScope 提供了异步缓存方法,用于打开和操作 Cache 对象。一个可以拥有多个命名的 Cache 对象,其内容完全由脚本控制。缓存不在之间共享,并且它们与浏览器的 HTTP 缓存完全隔离。

5.1. 构造

请求响应列表是一个由请求(一个请求)和响应(一个响应)组成的列表元组

相关请求响应列表此对象表示的实例。

名称到缓存映射是一个有序映射,其条目由一个(一个表示请求响应列表名称的字符串)和一个(一个请求响应列表)组成。

每个存储键都有一个关联的名称到缓存映射

相关名称到缓存映射是与从此对象的关联全局对象环境设置对象获取存储键相关联的名称到缓存映射

5.2. 理解缓存生命周期

Cache 实例不是浏览器 HTTP 缓存的一部分。Cache 对象正是开发者必须自己管理的内容。Cache 对象除非开发者明确请求,否则不会更新。Cache 对象除非开发者删除条目,否则不会过期。Cache 对象不会仅仅因为服务工作线程脚本更新而消失。也就是说,缓存不会自动更新。更新必须手动管理。这意味着开发者应该按名称对缓存进行版本控制,并确保仅从能够安全操作该缓存的服务工作线程版本中使用缓存。

5.3. self.caches

partial interface mixin WindowOrWorkerGlobalScope {
    [SecureContext, SameObject] readonly attribute CacheStorage caches;
};

5.3.1. caches

caches 获取器步骤是返回此对象的关联 CacheStorage 对象。

5.4. Cache 接口

[SecureContext, Exposed=(Window,Worker)]
interface Cache {
    [NewObject] Promise<(Response or undefined)> match(RequestInfo request, optional CacheQueryOptions options = {});
    [NewObject] Promise<FrozenArray<Response>> matchAll(optional RequestInfo request, optional CacheQueryOptions options = {});
    [NewObject] Promise<undefined> add(RequestInfo request);
    [NewObject] Promise<undefined> addAll(sequence<RequestInfo> requests);
    [NewObject] Promise<undefined> put(RequestInfo request, Response response);
    [NewObject] Promise<boolean> delete(RequestInfo request, optional CacheQueryOptions options = {});
    [NewObject] Promise<FrozenArray<Request>> keys(optional RequestInfo request, optional CacheQueryOptions options = {});
};
dictionary CacheQueryOptions {
    boolean ignoreSearch = false;
    boolean ignoreMethod = false;
    boolean ignoreVary = false;
};

Cache 对象表示一个请求响应列表。跨文档和工作线程的多个实现 Cache 接口的独立对象可以同时与同一个请求响应列表相关联。

缓存批量操作是一个结构体,包含:

5.4.1. match(request, options)

match(request, options) 方法的步骤是:

  1. promise一个新的 promise

  2. 并行运行这些子步骤:

    1. p 为使用 requestoptions 运行 matchAll(request, options) 方法中指定的算法的结果。

    2. 等待 p 稳定。

    3. 如果 p 因异常而被拒绝,则:

      1. 用该异常拒绝 promise

    4. 否则,如果 p 以数组 responses 解析,则:

      1. 如果 responses 是一个空数组,则:

        1. 用 undefined 解析 promise

      2. 否则:

        1. responses 的第一个元素解析 promise

  3. 返回 promise

5.4.2. matchAll(request, options)

matchAll(request, options) 方法的步骤是:

  1. r 为 null。

  2. 如果可选参数 request 未省略,则:

    1. 如果 request 是一个 Request 对象,则:

      1. r 设置为 request请求

      2. 如果 r方法不是 `GET` 且 options.ignoreMethod 为 false,则返回一个已解析的 promise,其值为空数组。

    2. 否则,如果 request 是一个字符串,则:

      1. r 设置为以 request 作为参数调用 Request 初始值作为构造函数的结果的关联请求。如果此操作抛出异常,则返回一个被拒绝的 promise,其值为该异常。

  3. realm此对象相关领域

  4. promise一个新的 promise

  5. 并行运行这些子步骤:

    1. responses 为一个空列表

    2. 如果可选参数 request 被省略,则:

      1. 对于相关请求响应列表中的每个 requestResponse

        1. requestResponse 的响应副本添加到 responses

    3. 否则:

      1. requestResponses 为使用 roptions 运行查询缓存的结果。

      2. 对于 requestResponses 中的每个 requestResponse

        1. requestResponse 的响应副本添加到 responses

    4. 对于 responses 中的每个 response

      1. 如果 response类型是“opaque”且使用 promise相关设置对象promise相关设置对象、“”和 response内部响应进行跨源资源策略检查返回blocked,则用 TypeError 拒绝 promise 并中止这些步骤。

    5. 使用DOM 操作任务源,在 promise相关设置对象负责事件循环排队一个任务,以执行以下步骤:

      1. responseList 为一个列表

      2. 对于 responses 中的每个 response

        1. 将一个新的 Response 对象(与 response 相关联)和一个新的 Headers 对象(其守卫为“immutable”)添加到 responseList

      3. realm 中,用从 responseList 创建冻结数组解析 promise

  6. 返回 promise

5.4.3. add(request)

add(request) 方法的步骤是:

  1. requests 为一个仅包含 request 的数组。

  2. responseArrayPromise 为以 requests 作为参数运行 addAll(requests) 中指定的算法的结果。

  3. 返回responseArrayPromise 作出反应的结果,该反应带有一个返回 undefined 的履行处理程序。

5.4.4. addAll(requests)

addAll(requests) 方法的步骤是:

  1. responsePromises 为一个空列表

  2. requestList 为一个空列表

  3. 对于 requests 中类型为 Request 的每个 request

    1. rrequest请求

    2. 如果 rURL方案不是“http”和“https”之一,或者 r方法不是 `GET`,则返回一个被拒绝的 promise,其值为 TypeError

  4. fetchControllers 为一个获取控制器列表

  5. 对于 requests 中的每个 request

    1. r 为以 request 作为参数调用 Request 初始值作为构造函数的结果的关联请求。如果此操作抛出异常,则返回一个被拒绝的 promise,其值为该异常。

    2. 如果 rURL方案不是“http”和“https”之一,则:

      1. 对于 fetchControllers 中的每个 fetchController中止 fetchController

      2. 返回一个被拒绝的 promise,其值为 TypeError

    3. 如果 r客户端全局对象是一个 ServiceWorkerGlobalScope 对象,则将 request服务工作线程模式设置为“none”。

    4. r发起者设置为“fetch”,并将目标设置为“subresource”。

    5. r 添加到 requestList

    6. responsePromise一个新的 promise

    7. 并行运行以下子步骤:

      • 追加获取 r 的结果。

      • response 处理响应,运行这些子步骤:

        1. 如果 response类型是“error”,或者 response状态不是正常状态或者是 206,则用 TypeError 拒绝 responsePromise

        2. 否则,如果 response标头列表包含一个名为 `Vary` 的标头,则:

          1. fieldValues 为包含与 Vary 标头的字段值对应的元素的列表

          2. 对于 fieldValues 中的每个 fieldValue

            1. 如果 fieldValue 匹配“*”,则:

              1. TypeError 拒绝 responsePromise

              2. 对于 fetchControllers 中的每个 fetchController中止 fetchController

              3. 中止这些步骤。

      • response 处理响应体结束,运行这些子步骤:

        1. 如果 response中止标志已设置,则用“AbortErrorDOMException 拒绝 responsePromise 并中止这些步骤。

        2. response 解析 responsePromise

        注意:当响应的主体完全接收后,才允许缓存提交。

    8. responsePromise 添加到 responsePromises

  6. p获取一个等待所有 responsePromises 的 promise 的结果。

  7. 返回p 作出反应的结果,该反应带有一个履行处理程序,当使用参数 responses 调用该处理程序时,执行以下子步骤:

    1. operations 为一个空列表

    2. index 为零。

    3. 对于 responses 中的每个 response

      1. operation 为一个缓存批量操作

      2. operation类型设置为“put”。

      3. operation请求设置为 requestList[index]。

      4. operation响应设置为 response

      5. 追加 operationoperations

      6. index 增加一。

    4. realm此对象相关领域

    5. cacheJobPromise一个新的 promise

    6. 并行运行以下子步骤:

      1. errorData 为 null。

      2. 调用批量缓存操作并传入 operations。如果此操作抛出异常,则将 errorData 设置为该异常。

      3. 使用DOM 操作任务源,在 cacheJobPromise相关设置对象负责事件循环排队一个任务,以执行以下子步骤:

        1. 如果 errorData 为 null,则用 undefined 解析 cacheJobPromise

        2. 否则,在 realm 中,用一个新的异常(其值为 errorData)拒绝 cacheJobPromise

    7. 返回 cacheJobPromise

5.4.5. put(request, response)

put(request, response) 方法的步骤是:

  1. innerRequest 为 null。

  2. 如果 request 是一个 Request 对象,则将 innerRequest 设置为 request请求

  3. 否则:

    1. requestObj 为以 request 作为参数调用 Request 的构造函数的结果。如果此操作抛出 exception,则返回一个被拒绝的 promise,其值为 exception

    2. innerRequest 设置为 requestObj请求

  4. 如果 innerRequestURL方案不是“http”和“https”之一,或者 innerRequest方法不是 `GET`,则返回一个被拒绝的 promise,其值为 TypeError

  5. innerResponseresponse响应

  6. 如果 innerResponse状态206,则返回一个被拒绝的 promise,其值为 TypeError

  7. 如果 innerResponse标头列表包含一个名为 `Vary` 的标头,则:

    1. fieldValues 为包含与 Vary 标头的字段值对应的列表

    2. 对于 fieldValues 中的每个 fieldValue

      1. 如果 fieldValue 匹配“*”,则返回一个被拒绝的 promise,其值为 TypeError

  8. 如果 innerResponse主体被扰动被锁定,则返回一个被拒绝的 promise,其值为 TypeError

  9. clonedResponseinnerResponse克隆

  10. bodyReadPromise一个已解析的 promise,其值为 undefined。

  11. 如果 innerResponse主体非空,则运行这些子步骤:

    1. streaminnerResponse主体

    2. reader 为为 stream 获取读取器的结果。

    3. bodyReadPromise 设置为从 reader 读取所有字节的结果。

    注意:这确保了 innerResponse主体锁定,并且我们在 clonedResponse 中拥有主体的完整缓冲副本。实现可以通过直接流式传输到磁盘而不是内存来进行优化。

  12. operations 为一个空列表

  13. operation 为一个缓存批量操作

  14. operation类型设置为“put”。

  15. operation请求设置为 innerRequest

  16. operation响应设置为 clonedResponse

  17. 追加 operationoperations

  18. realm此对象相关领域

  19. 返回 bodyReadPromise履行结果:

    1. cacheJobPromise一个新的 promise

    2. 返回 cacheJobPromise并行运行这些步骤:

      1. errorData 为 null。

      2. 调用批量缓存操作并传入 operations。如果此操作抛出异常,则将 errorData 设置为该异常。

      3. 使用DOM 操作任务源,在 cacheJobPromise相关设置对象负责事件循环排队一个任务,以执行以下子步骤:

        1. 如果 errorData 为 null,则用 undefined 解析 cacheJobPromise

        2. 否则,在 realm 中,用一个新的异常(其值为 errorData)拒绝 cacheJobPromise

5.4.6. delete(request, options)

delete(request, options) 方法的步骤是:

  1. r 为 null。

  2. 如果 request 是一个 Request 对象,则:

    1. r 设置为 request请求

    2. 如果 r方法不是 `GET` 且 options.ignoreMethod 为 false,则返回一个已解析的 promise,其值为 false。

  3. 否则,如果 request 是一个字符串,则:

    1. r 设置为以 request 作为参数调用 Request 初始值作为构造函数的结果的关联请求。如果此操作抛出异常,则返回一个被拒绝的 promise,其值为该异常。

  4. operations 为一个空列表

  5. operation 为一个缓存批量操作

  6. operation类型设置为“delete”。

  7. operation请求设置为 r

  8. operation选项设置为 options

  9. 追加 operationoperations

  10. realm此对象相关领域

  11. cacheJobPromise一个新的 promise

  12. 并行运行以下子步骤:

    1. errorData 为 null。

    2. requestResponses 为使用 operations 运行批量缓存操作的结果。如果此操作抛出异常,则将 errorData 设置为该异常。

    3. 使用DOM 操作任务源,在 cacheJobPromise相关设置对象负责事件循环排队一个任务,以执行以下子步骤:

      1. 如果 errorData 为 null,则:

        1. 如果 requestResponses 不为空,则用 true 解析 cacheJobPromise

        2. 否则,用 false 解析 cacheJobPromise

      2. 否则,在 realm 中,用一个新的异常(其值为 errorData)拒绝 cacheJobPromise

  13. 返回 cacheJobPromise

5.4.7. keys(request, options)

keys(request, options) 方法的步骤是:

  1. r 为 null。

  2. 如果可选参数 request 未省略,则:

    1. 如果 request 是一个 Request 对象,则:

      1. r 设置为 request请求

      2. 如果 r方法不是 `GET` 且 options.ignoreMethod 为 false,则返回一个已解析的 promise,其值为空数组。

    2. 否则,如果 request 是一个字符串,则:

      1. r 设置为以 request 作为参数调用 Request 初始值作为构造函数的结果的关联请求。如果此操作抛出异常,则返回一个被拒绝的 promise,其值为该异常。

  3. realm此对象相关领域

  4. promise一个新的 promise

  5. 并行运行这些子步骤:

    1. requests 为一个空列表

    2. 如果可选参数 request 被省略,则:

      1. 对于相关请求响应列表中的每个 requestResponse

        1. requestResponse 的请求添加到 requests

    3. 否则:

      1. requestResponses 为使用 roptions 运行查询缓存的结果。

      2. 对于 requestResponses 中的每个 requestResponse

        1. requestResponse 的请求添加到 requests

    4. 使用DOM 操作任务源,在 promise相关设置对象负责事件循环排队一个任务,以执行以下步骤:

      1. requestList 为一个列表

      2. 对于 requests 中的每个 request

        1. 将一个新的 Request 对象(与 request 相关联)和一个新的关联 Headers 对象(其守卫为“immutable”)添加到 requestList

      3. realm 中,用从 requestList 创建冻结数组解析 promise

  6. 返回 promise

5.5. CacheStorage 接口

[SecureContext, Exposed=(Window,Worker)]
interface CacheStorage {
    [NewObject] Promise<(Response or undefined)> match(RequestInfo request, optional MultiCacheQueryOptions options = {});
    [NewObject] Promise<boolean> has(DOMString cacheName);
    [NewObject] Promise<Cache> open(DOMString cacheName);
    [NewObject] Promise<boolean> delete(DOMString cacheName);
    [NewObject] Promise<sequence<DOMString>> keys();
};

dictionary MultiCacheQueryOptions : CacheQueryOptions {
    DOMString cacheName;
};

注:CacheStorage 接口主要设计为在很大程度上符合 ECMAScript 6 Map 对象,但完全是异步的,并带有额外的便捷方法。这些方法, clearforEachentriesvalues, 由于 TC39 正在进行的关于异步迭代的讨论,被有意地排除在第一个版本的范围之外。

当创建 Window 对象或 WorkerGlobalScope 对象时,用户代理 必须 创建一个 CacheStorage 对象,并将其与该全局对象关联。

一个 CacheStorage 对象表示其关联的全局对象环境设置对象的一个名称到缓存的映射。跨文档和工作者的多个实现 CacheStorage 接口的独立对象可以同时关联到同一个名称到缓存的映射

5.5.1. match(request, options) 方法

match(request, options) 方法的步骤是:

  1. 如果 options 中的 "cacheName" 存在,则:

    1. 返回一个新的 promise promise并行运行以下子步骤:

      1. 对于相关的名称到缓存映射中的每一个 cacheNamecache 对:

        1. 如果 options 中的 "cacheName" 匹配 cacheName,则:

          1. 使用在 Cache 接口的 match(request, options) 方法中指定的算法运行的结果来解决 promise,使用 requestoptions 作为参数(并将 cache 作为 match(request, options)\[[Call]] 内部方法的 thisArgument)。

          2. 中止这些步骤。

      2. 用 undefined 解决 promise

  2. 否则:

    1. promise 为一个已用 undefined 解决的 promise

    2. 对于相关的名称到缓存映射中的每一个 cacheNamecache 对:

      1. promise 设置为对自身作出反应的结果,该反应带有一个完成处理程序,当以参数 response 调用该处理程序时,执行以下子步骤:

        1. 如果 response 不是 undefined,则返回 response

        2. 返回在 Cache 接口的 match(request, options) 方法中指定的算法运行的结果,使用 requestoptions 作为参数(并将 cache 作为 match(request, options)\[[Call]] 内部方法的 thisArgument)。

    3. 返回 promise

5.5.2. has(cacheName) 方法

has(cacheName) 方法的步骤是:

  1. promise 为一个新的 promise

  2. 并行运行以下子步骤:

    1. 对于相关的名称到缓存映射中的每一个 keyvalue 对:

      1. 如果 cacheName 匹配 key,则用 true 解决 promise 并中止这些步骤。

    2. 用 false 解决 promise

  3. 返回 promise

5.5.3. open(cacheName) 方法

open(cacheName) 方法的步骤是:

  1. promise 为一个新的 promise

  2. 并行运行以下子步骤:

    1. 对于相关的名称到缓存映射中的每一个 keyvalue 对:

      1. 如果 cacheName 匹配 key,则:

        1. 用一个表示 value 的新的 Cache 对象来解决 promise

        2. 中止这些步骤。

    2. cache 为一个新的请求响应列表

    3. 设置相关的名称到缓存映射cacheName 对应的值为 cache。如果此缓存写入操作因超出授予的配额限制而失败,则用一个 "QuotaExceededError" DOMException 来拒绝 promise 并中止这些步骤。

    4. 用一个表示 cache 的新的 Cache 对象来解决 promise

  3. 返回 promise

5.5.4. delete(cacheName) 方法

delete(cacheName) 方法的步骤是:

  1. promise 为使用 cacheName 运行 has(cacheName) 方法中指定的算法的结果。

  2. 返回promise 作出反应的结果,该反应带有一个完成处理程序,当以参数 cacheExists 调用该处理程序时,执行以下子步骤:

    1. 如果 cacheExists 为 false,则:

      1. 返回 false。

    2. cacheJobPromise 为一个新的 promise

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

      1. 移除相关的名称到缓存映射cacheName 对应的条目。

      2. 用 true 解决 cacheJobPromise

      注:此步骤之后,现有的 DOM 对象(即当前引用的 Cache、Request 和 Response 对象)应保持功能正常。

    4. 返回 cacheJobPromise

5.5.5. keys() 方法

keys() 方法的步骤是:

  1. promise 为一个新的 promise

  2. 并行运行以下子步骤:

    1. cacheKeys获取相关的名称到缓存映射的键的结果。

      注:结果有序集合中的的顺序与其对应条目添加到名称到缓存的映射的顺序相同。

    2. cacheKeys 解决 promise

  3. 返回 promise

6. 安全注意事项

6.1. 安全上下文

服务工作线程必须安全上下文中执行。服务工作线程客户端必须安全上下文才能注册服务工作线程注册,才能访问服务工作线程注册服务工作线程,才能与服务工作线程进行消息传递,以及被服务工作线程操作。

注:这实际上意味着服务工作线程及其服务工作线程客户端需要通过 HTTPS 托管。用户代理可以允许将 localhost(参见要求)、 127.0.0.0/8::1/128 用于开发目的。此限制的主要原因是为了保护用户免受与不安全上下文相关的风险

6.2. 内容安全策略

每当用户代理使用服务工作线程 serviceWorker 调用运行服务工作线程算法时:

  • 如果 serviceWorker脚本资源是通过包含值 policyContent-Security-Policy HTTP 标头传递的,则用户代理必须serviceWorker 强制执行 policy

  • 如果 serviceWorker脚本资源是通过包含值 policyContent-Security-Policy-Report-Only HTTP 标头传递的,则用户代理必须serviceWorker 监视 policy

此限制的主要原因是为了缓解一大类内容注入漏洞,例如跨站脚本 (XSS)。

6.3. 源相对性

6.3.1. 源限制

本节是非规范性的。

服务工作线程在注册服务工作线程客户端中执行。主要应用程序会遇到的高级问题之一是它们是否可以从 CDN 托管。根据定义,这些是其他地方的服务器,通常在其他上。因此,服务工作线程不能托管在 CDN 上。但是它们可以通过 importScripts() 包含资源。此限制的原因是服务工作线程为恶意行为者提供了将糟糕的一天变成糟糕的永恒的机会。

6.3.2. importScripts(urls)

当在 ServiceWorkerGlobalScope 对象上调用 importScripts(urls) 方法时,用户代理必须将脚本导入工作线程全局作用域,给定此 ServiceWorkerGlobalScope 对象和 urls,并使用以下执行获取挂钩步骤,给定请求 request

  1. serviceWorkerrequest客户端全局对象服务工作线程

  2. mapserviceWorker脚本资源映射

  3. urlrequestURL

  4. 如果 serviceWorker状态不是 "parsed" 或 "installing":

    1. 如果 map[url] 存在,则返回它,否则返回网络错误

  5. 如果 map[url] 存在

    1. url AppendserviceWorkerset of used scripts

    2. 返回 map[url]。

  6. registrationserviceWorker包含的服务工作线程注册

  7. request服务工作线程模式设置为 "none"。

  8. 如果以下任一情况为真,则将 request缓存模式设置为 "no-cache":

  9. response获取 request 的结果。

  10. 如果 response缓存状态不是 "local",则将 registration上次更新检查时间设置为当前时间。

  11. 如果 response不安全响应错误的导入脚本响应,则 返回网络错误

  12. map[url] Setresponse

  13. url AppendserviceWorkerset of used scripts

  14. 设置 serviceWorker经典脚本导入标志

  15. 返回 response

6.4. 跨源资源和 CORS

本节是非规范性的。

应用程序倾向于缓存来自 CDN 或其他的项目。可以使用 <script><img><video><link> 元素直接请求其中的许多项目。如果这种运行时协作在离线时中断,那将是极大的限制。类似地,当设置了适当的 CORS 标头时,可以获取多种类型的非资源。服务工作线程通过允许 Caches 获取和缓存非源项目来实现这一点。但是,存在一些限制。首先,与在 Cache 中作为 Response 对象(其对应的响应基本过滤响应)管理的同源资源不同,存储的对象是 Response 对象,其对应的响应CORS 过滤响应不透明过滤响应。它们可以像 Response 对象(其对应的响应基本过滤响应)一样传递给 event.respondWith(r) 方法,但不能以有意义的方式以编程方式创建。这些限制对于维护平台的安全不变性是必要的。允许 Caches 存储它们可以使应用程序在大多数情况下避免重新架构。

6.5. 路径限制

本节是非规范性的。

除了源限制之外,服务工作线程还受到服务工作线程脚本的路径的限制。例如,位于 https://www.example.com/~bob/sw.js 的服务工作线程脚本可以注册到作用域 URL https://www.example.com/~bob/,但不能注册到作用域 https://www.example.com/https://www.example.com/~alice/。这为在同一源上的不同目录中托管多用户内容的站点提供了一些保护。但是,路径限制不被视为硬安全边界,只有源才是。鼓励站点在适当时使用不同的源来安全地隔离站点的段。

服务器可以通过在服务工作线程脚本上设置 Service-Worker-Allowed 标头来移除路径限制。

6.6. 服务工作线程脚本请求

本节是非规范性的。

为了进一步防范在站点上恶意注册服务工作线程,本规范要求:

6.7. 实现者注意事项

本节是非规范性的。

鼓励实现者注意:

  • 插件不应通过服务工作线程加载。由于插件可能会从其自己的 URL 获取其安全源,因此嵌入服务工作线程无法处理它。因此,处理获取算法会使 <embed><object> 请求立即回退到网络,而不分派 fetch 事件。

  • 某些旧版网络堆栈代码可能需要仔细审核,以了解与服务工作线程交互的后果。

6.8. 隐私

服务工作线程引入了新的持久存储功能,包括注册映射(用于服务工作线程注册及其服务工作线程)、请求响应列表名称到缓存映射(用于缓存)以及脚本资源映射(用于脚本资源)。为了保护用户免受任何潜在的未经批准的跟踪威胁,当用户打算清除这些持久存储时,应该清除它们,并且应该维护并与现有用户控件(例如清除所有现有持久存储)进行互操作。

7. 可扩展性

服务工作线程规范可由其他规范扩展。

7.1. 定义绑定到服务工作线程注册的 API

规范可以通过对 ServiceWorkerRegistration 接口使用部分接口定义来定义与服务工作线程注册相关联的 API,其中它可以定义规范特定的属性和方法:

partial interface ServiceWorkerRegistration {
    // 例如,定义一个 API 命名空间
    readonly attribute APISpaceType APISpace;
    // 例如,定义一个方法
    Promise<T> methodName(/* 参数列表 */);
};

7.2. 定义功能性事件

规范可以通过扩展 ExtendableEvent 接口来定义功能性事件

// 例如,定义 FunctionalEvent 接口
interface FunctionalEvent : ExtendableEvent {
    // 添加功能性事件自身的属性和方法
};

7.3. 定义事件处理程序

规范可以使用部分接口定义为 ServiceWorkerGlobalScope 接口定义相应功能性事件的事件处理程序属性:

partial interface ServiceWorkerGlobalScope {
    attribute EventHandler onfunctionalevent;
};

7.4. 触发功能性事件

要请求将功能性事件分派到服务工作线程注册活动工作线程,规范调用触发功能性事件

附录 A:算法

以下定义是用户代理在整个规范中使用的内部数据结构。

一个 注册映射 是一个 有序映射,其中键是(存储密钥序列化的 作用域 URL),值是 服务工作者注册

一个 任务 是对 服务工作者注册 的注册、更新和取消注册请求之一的抽象。

一个 任务 有一个 任务类型,它是注册更新取消注册之一。

一个 任务 有一个 存储密钥(一个 存储密钥)。

一个 任务 有一个 作用域 URL(一个 URL)。

一个 任务 有一个 脚本 URL(一个 URL)。

一个 任务 有一个 工作者类型(“classic”或“module”)。

一个 任务 有一个 通过缓存更新模式,它是“imports”、 “all”或“none”。

一个 任务 有一个 客户端(一个 服务工作者客户端)。它最初为 null。

一个 任务 有一个 引用者(一个 URL 或 null)。

一个 任务 有一个 任务承诺(一个 承诺)。它最初为 null。

一个 任务 有一个 包含的任务队列(一个 任务队列 或 null)。它最初为 null。

一个 任务 有一个 等效任务列表(一个 任务列表)。它最初是空列表。

一个 任务 有一个 强制绕过缓存标志。它最初未设置。

当它们的 任务类型 相同并且满足以下条件时,两个 任务等效的

一个 任务队列 是一个线程安全的 队列,用于同步并发 任务集合。任务队列 包含 任务 作为其 。一个 任务队列 最初为空。

一个 作用域到任务队列映射 是一个 有序映射,其中键是 作用域 URL序列化的),值是 任务队列

一个 错误的导入脚本响应 是一个 响应,满足以下任一条件:

创建任务

输入

jobType,一个 任务类型

storage key,一个 存储密钥

scopeURL,一个 URL

scriptURL,一个 URL

promise,一个 承诺

client,一个 服务工作者客户端

输出

job,一个 任务

  1. job 为一个新的 任务

  2. job任务类型设置为 jobType

  3. job存储密钥设置为 storage key

  4. job作用域 URL 设置为 scopeURL

  5. job脚本 URL 设置为 scriptURL

  6. job任务承诺设置为 promise

  7. job客户端设置为 client

  8. 如果 client 不为 null,则将 job引用者设置为 client创建 URL

  9. 返回 job

调度任务

输入

job,一个 任务

输出

  1. jobQueue 为 null。

  2. jobScopejob作用域 URL序列化的)。

  3. 如果 作用域到任务队列映射[jobScope] 不存在,则设置 作用域到任务队列映射[jobScope] 为一个新的 任务队列

  4. jobQueue 设置为 作用域到任务队列映射[jobScope]。

  5. 如果 jobQueue 为空,则:

    1. job包含的任务队列设置为 jobQueue,并将 job 加入 jobQueue

    2. 使用 jobQueue 调用 运行任务

  6. 否则:

    1. lastJobjobQueue 末尾的元素。

    2. 如果 joblastJob 等效,并且 lastJob任务承诺尚未解决,则将 job 追加到 lastJob等效任务列表中。

    3. 否则,将 job包含的任务队列设置为 jobQueue,并将 job 加入 jobQueue

运行任务

输入

jobQueue,一个 任务队列

输出

  1. 断言:jobQueue 不为空

  2. 排队一个任务以运行以下步骤:

    1. jobjobQueue 中的第一个 项目

    2. 如果 job任务类型注册,则并行运行注册,参数为 job

    3. 否则,如果 job任务类型更新,则并行运行更新,参数为 job

      注意:对于注册任务和更新任务,用户代理会延迟将运行任务的任务排入队列,直到 DOMContentLoaded 事件已分派到启动该任务的文档。

    4. 否则,如果 job任务类型取消注册,则并行运行取消注册,参数为 job

完成任务

输入

job,一个 任务

输出

  1. jobQueuejob包含的任务队列

  2. 断言:jobQueue 中的第一个 项目job

  3. jobQueue 出队

  4. 如果 jobQueue 不为空,则使用 jobQueue 调用 运行任务

解决任务 Promise

输入

job,一个 任务

value,任何值

输出

  1. 如果 job客户端不为 null,则在 job客户端负责的事件循环上使用 DOM 操作任务源排队一个任务,以运行以下子步骤:

    1. convertedValue 为 null。

    2. 如果 job任务类型注册更新,则将 convertedValue 设置为在 job客户端中表示 value获取服务工作者注册对象的结果。

    3. 否则,在 job客户端领域中,将 convertedValue 设置为 value

    4. 使用 convertedValue 解决 job任务承诺

  2. 对于 job等效任务列表中的每个 equivalentJob

    1. 如果 equivalentJob客户端为 null,则继续循环的下一次迭代。

    2. equivalentJob客户端负责的事件循环上使用 DOM 操作任务源排队一个任务,以运行以下子步骤:

      1. convertedValue 为 null。

      2. 如果 equivalentJob任务类型注册更新,则将 convertedValue 设置为在 equivalentJob客户端中表示 value获取服务工作者注册对象的结果。

      3. 否则,在 equivalentJob客户端领域中,将 convertedValue 设置为 value

      4. 使用 convertedValue 解决 equivalentJob任务承诺

拒绝任务 Promise

输入

job,一个 任务

errorData创建异常所需的信息

输出

  1. 如果 job客户端不为 null,则在 job客户端负责的事件循环上使用 DOM 操作任务源排队一个任务,以在 job客户端领域中使用 errorData 以一个新的异常拒绝 job任务承诺

  2. 对于 job等效任务列表中的每个 equivalentJob

    1. 如果 equivalentJob客户端为 null,则继续

    2. equivalentJob客户端负责的事件循环上使用 DOM 操作任务源排队一个任务,以在 equivalentJob客户端领域中使用 errorData 以一个新的异常拒绝 equivalentJob任务承诺

开始注册

输入

scopeURL,一个 URL 或失败或 null

scriptURL,一个 URL 或失败

promise,一个 承诺

client,一个 服务工作者客户端

referrer,一个 URL

workerType,一个 工作者类型

updateViaCache,一个 通过缓存更新模式

输出

  1. 如果 scriptURL 失败,则用 TypeError 拒绝 promise 并中止这些步骤。

  2. scriptURL片段设置为 null。

    注意:用户代理不存储脚本 URL 的片段。这意味着片段对识别服务工作者没有影响。

  3. 如果 scriptURL方案不是“http”和“https”之一,则用 TypeError 拒绝 promise 并中止这些步骤。

  4. 如果 scriptURL路径中的任何字符串包含ASCII 不区分大小写的“%2f”或ASCII 不区分大小写的“%5c”,则用 TypeError 拒绝 promise 并中止这些步骤。

  5. 如果 scopeURL 为 null,则将 scopeURL 设置为使用 scriptURL 解析字符串“./”的结果。

    注意:默认情况下,注册的作用域 URL 设置为服务工作者脚本的位置。

  6. 如果 scopeURL 失败,则用 TypeError 拒绝 promise 并中止这些步骤。

  7. scopeURL片段设置为 null。

    注意:用户代理不存储作用域 URL 的片段。这意味着片段对识别服务工作者注册没有影响。

  8. 如果 scopeURL方案不是“http”和“https”之一,则用 TypeError 拒绝 promise 并中止这些步骤。

  9. 如果 scopeURL路径中的任何字符串包含ASCII 不区分大小写的“%2f”或ASCII 不区分大小写的“%5c”,则用 TypeError 拒绝 promise 并中止这些步骤。

  10. storage key 为给定 client 运行 获取存储密钥的结果。

  11. job 为使用注册storage keyscopeURLscriptURLpromiseclient 运行 创建任务的结果。

  12. job工作者类型设置为 workerType

  13. job通过缓存更新模式设置为 updateViaCache

  14. job引用者设置为 referrer

  15. 使用 job 调用 调度任务

注册

输入

job,一个 任务

输出

  1. 如果以 job脚本 URL作为参数运行潜在可信源的结果是不可信,则:

    1. 使用 job 和“SecurityErrorDOMException 调用拒绝任务承诺

    2. 使用 job 调用完成任务并中止这些步骤。

  2. 如果 job脚本 URLjob引用者不是同源,则:

    1. 使用 job 和“SecurityErrorDOMException 调用拒绝任务承诺

    2. 使用 job 调用完成任务并中止这些步骤。

  3. 如果 job作用域 URLjob引用者不是同源,则:

    1. 使用 job 和“SecurityErrorDOMException 调用拒绝任务承诺

    2. 使用 job 调用完成任务并中止这些步骤。

  4. registration 为给定 job存储密钥job作用域 URL 运行获取注册的结果。

  5. 如果 registration 不为 null,则:

    1. newestWorker 为以 registration 作为参数运行获取最新工作者算法的结果。

    2. 如果 newestWorker 不为 null,job脚本 URL 等于 newestWorker脚本 URLjob工作者类型等于 newestWorker类型,并且 job通过缓存更新模式的值等于 registration通过缓存更新模式,则:

      1. 使用 jobregistration 调用解决任务承诺

      2. 使用 job 调用完成任务并中止这些步骤。

  6. 否则:

    1. 使用 job存储密钥job作用域 URLjob通过缓存更新模式调用设置注册算法。

  7. job 作为参数调用更新算法。

更新

输入

job,一个 任务

输出

  1. registration 为给定 job存储密钥job作用域 URL 运行获取注册的结果。

  2. 如果 registration 为 null,则:

    1. 使用 jobTypeError 调用拒绝任务承诺

    2. 使用 job 调用完成任务并中止这些步骤。

  3. newestWorker 为以 registration 作为参数运行获取最新工作者算法的结果。

  4. 如果 job任务类型更新,并且 newestWorker 不为 null 且其 脚本 URL等于 job脚本 URL,则:

    1. 使用 jobTypeError 调用拒绝任务承诺

    2. 使用 job 调用完成任务并中止这些步骤。

  5. hasUpdatedResources 为 false。

  6. updatedResourceMap 为一个有序映射,其中URL响应

  7. 根据 job工作者类型,使用以下选项运行这些子步骤:

    "classic"

    给定 job序列化的 脚本 URLjob客户端、“serviceworker”以及为此服务工作者创建的环境设置对象获取经典工作者脚本

    "module"

    给定 job序列化的 脚本 URLjob客户端、“serviceworker”、 “omit”以及为此服务工作者创建的环境设置对象获取模块工作者脚本图

    使用待创建的环境设置对象而不是具体的环境设置对象。这是由于服务工作者与其他Web 工作者相比具有独特的处理模型。HTML 标准的脚本获取算法最初是为其他Web 工作者设计的,需要执行环境的环境设置对象,但服务工作者在更新算法中单独获取脚本,之后脚本通过运行服务工作者算法多次运行。

    HTML 中的获取经典工作者脚本算法和获取模块工作者脚本图算法将 job客户端作为参数。job客户端在从软更新算法传递时为 null。

    要给定 request 执行获取钩子,请运行以下步骤:

    1. 将 `Service-Worker`/`script` 追加到 request标头列表

      注意:请参阅附录 B:扩展 HTTP 标头中 Service-Worker 标头的定义。

    2. 如果以下任一情况为真,则将 request缓存模式设置为“no-cache”:

      注意:即使缓存模式未设置为“no-cache”,用户代理也会遵守网络层中 Cache-Control 标头的 max-age 值,以确定是否应绕过浏览器缓存。

    3. request服务工作者模式设置为“none”。

    4. 如果isTopLevel 标志未设置,则返回获取 request 的结果。

    5. request重定向模式设置为“error”。

    6. 获取 request,并异步等待作为获取的处理响应的一部分为响应 response 运行其余步骤。

    7. response标头列表提取 MIME 类型。如果此 MIME 类型(忽略参数)不是JavaScript MIME 类型,则:

      1. 使用 job 和“SecurityErrorDOMException 调用拒绝任务承诺

      2. 使用网络错误异步完成这些步骤。

    8. serviceWorkerAllowed 为给定 `Service-Worker-Allowed` 和 response标头列表提取标头列表值的结果。

      注意:请参阅附录 B:扩展 HTTP 标头中Service-Worker-Allowed 标头的定义。

    9. policyContainer 为给定 response 从获取响应创建策略容器的结果。

    10. 如果 serviceWorkerAllowed 失败,则:

      1. 使用网络错误异步完成这些步骤。

    11. scopeURLregistration作用域 URL

    12. maxScopeString 为 null。

    13. 如果 serviceWorkerAllowed 为 null,则:

      1. resolvedScope 为使用 job脚本 URL 作为基本 URL 解析./”的结果。

      2. maxScopeString 设置为“/”,后跟 resolvedScope路径中的字符串(包括空字符串),彼此用“/”分隔。

        注意:resolvedScope路径中的最后一项将始终为空字符串,因此 maxScopeString 将带有一个尾随的“/”。

    14. 否则:

      1. maxScope 为使用 job脚本 URL 作为基本 URL 解析 serviceWorkerAllowed 的结果。

      2. 如果 maxScopejob脚本 URL,则:

        1. maxScopeString 设置为“/”,后跟 maxScope路径中的字符串(包括空字符串),彼此用“/”分隔。

    15. scopeString 为“/”,后跟 scopeURL路径中的字符串(包括空字符串),彼此用“/”分隔。

    16. 如果 maxScopeString 为 null 或 scopeString 不以 maxScopeString 开头,则:

      1. 使用 job 和“SecurityErrorDOMException 调用拒绝任务承诺

      2. 使用网络错误异步完成这些步骤。

    17. urlrequestURL

    18. updatedResourceMap[url] 设置为 response

    19. 如果 response缓存状态不是“local”,则将 registration上次更新检查时间设置为当前时间。

    20. 如果以下任一情况为真,则将 hasUpdatedResources 设置为 true:

    21. 如果 hasUpdatedResources 为 false 且 newestWorker经典脚本导入标志已设置,则:

      注意:以下检查以查看导入的脚本是否已更新,因为主脚本未更改。

      1. 对于 newestWorker脚本资源映射中的每个 importUrlstoredResponse

        1. 如果 importUrlurl,则继续。

        2. importRequest 为一个新的请求,其URLimportUrl客户端job客户端目标为 “script”,解析器元数据为“not parser-inserted”,并且其使用 URL 凭据标志已设置。

        3. 如果以下任一情况为真,则将 importRequest缓存模式设置为“no-cache”:

        4. fetchedResponse获取 importRequest 的结果。

        5. updatedResourceMap[importRequestURL] 设置为 fetchedResponse

        6. fetchedResponse 设置为 fetchedResponse不安全响应

        7. 如果 fetchedResponse缓存状态不是“local”,则将 registration上次更新检查时间设置为当前时间。

        8. 如果 fetchedResponse 是一个错误的导入脚本响应,则继续。

          注意:对于逐字节检查,importScripts() 的错误响应将被忽略。仅考虑现有工作者的良好响应和潜在更新工作者的良好响应。有关一些基本原理,请参阅问题 #1374

        9. 如果 fetchedResponse主体storedResponse不安全响应主体不是逐字节相同的,则将 hasUpdatedResources 设置为 true。

          注意:此步骤中的控件不会中断循环,以继续处理所有导入的脚本以填充缓存。

    22. 使用 response 异步完成这些步骤。

    当算法异步完成时,继续执行这些步骤的其余部分,其中 script 是异步完成值。

  8. 如果 script 为 null 或使用 script记录script基本 URL和 « » 是异步模块为 true,则:

    1. 使用 jobTypeError 调用拒绝任务承诺

      注意:如果先前使用“SecurityErrorDOMException 调用了拒绝任务承诺,则此操作将不执行任何操作。

    2. 如果 newestWorker 为 null,则移除 注册映射[(registration存储密钥, 序列化的 scopeURL)]。

    3. 使用 job 调用完成任务并中止这些步骤。

  9. 如果 hasUpdatedResources 为 false,则:

    1. registration通过缓存更新模式设置为 job通过缓存更新模式

    2. 使用 jobregistration 调用解决任务承诺

    3. 使用 job 调用完成任务并中止这些步骤。

  10. worker 为一个新的服务工作者

  11. worker脚本 URL 设置为 job脚本 URLworker脚本资源设置为 scriptworker类型设置为 job工作者类型,并将 worker脚本资源映射设置为 updatedResourceMap

  12. url 追加到 worker已用脚本集

  13. worker脚本资源策略容器设置为 policyContainer

  14. 如果 job强制绕过缓存标志已设置,则令 forceBypassCache 为 true,否则为 false。

  15. runResult 为使用 workerforceBypassCache 运行运行服务工作者算法的结果。

  16. 如果 runResult失败突然完成,则:

    1. 使用 jobTypeError 调用拒绝任务承诺

    2. 如果 newestWorker 为 null,则移除 注册映射[(registration存储密钥, 序列化的 scopeURL)]。

    3. 使用 job 调用完成任务

  17. 否则,以 jobworkerregistration 作为参数调用安装算法。

软更新

用户代理可以根据需要随时调用此方法来检查更新。

输入

registration,一个 服务工作者注册

forceBypassCache,一个可选布尔值,默认为 false

注意:实现者可以使用 forceBypassCache 来辅助调试(例如从开发人员工具调用),其他扩展服务工作者的规范也可以根据自身需要使用该标志。

输出

  1. newestWorker 为以 registration 作为参数运行获取最新工作者算法的结果。

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

  3. job 为使用更新registration存储密钥registration作用域 URLnewestWorker脚本 URL、 null 和 null 运行创建任务的结果。

  4. job工作者类型设置为 newestWorker类型

  5. 如果 forceBypassCache 为 true,则设置 job强制绕过缓存标志

  6. 使用 job 调用调度任务

安装

输入

job,一个 任务

worker,一个 服务工作者

registration,一个 服务工作者注册

输出

  1. installFailed 为 false。

  2. newestWorker 为以 registration 作为参数运行获取最新工作者算法的结果。

  3. registration通过缓存更新模式设置为 job通过缓存更新模式

  4. registration、“正在安装”和 worker 作为参数运行更新注册状态算法。

  5. registration正在安装的工作者和“正在安装”作为参数运行更新工作者状态算法。

  6. 断言:job任务承诺不为 null。

  7. 使用 jobregistration 调用解决任务承诺

  8. settingsObjects 为所有环境设置对象,其registration作用域 URL

  9. 对于 settingsObjects 中的每个 settingsObject,在 settingsObject负责的事件循环上的DOM 操作任务源排队一个任务以运行以下步骤:

    1. registrationObjectssettingsObject领域中的每个 ServiceWorkerRegistration 对象,其服务工作者注册registration

    2. 对于 registrationObjects 中的每个 registrationObject,在 registrationObject触发一个名为 updatefound 的事件。

  10. installingWorkerregistration正在安装的工作者

  11. 如果使用 installingWorker 和 “install” 运行应该跳过事件算法的结果为 false,则:

    1. 如果 job强制绕过缓存标志已设置,则令 forceBypassCache 为 true,否则为 false。

    2. 如果使用 installingWorkerforceBypassCache 运行运行服务工作者算法的结果是失败, 则:

      1. installFailed 设置为 true。

    3. 否则:

      1. installingWorker事件循环上使用DOM 操作任务源排队一个任务 task 以运行以下步骤:

        1. e 为使用 InstallEvent 创建事件的结果。

        2. etype 属性初始化为 install

        3. installingWorker全局对象分派 e

        4. 等待异步扩展并行运行以下子步骤:

          1. 等待直到 e 不再 活动

          2. 如果 e超时标志已设置,则将 installFailed 设置为 true。

          3. p获取一个等待所有 e延长生命周期承诺的承诺的结果。

          4. p 被拒绝后,将 installFailed 设置为 true。

        如果 task 被丢弃,则将 installFailed 设置为 true。

      2. 等待 task 执行完毕或被丢弃。

      3. 等待标记为等待异步扩展的步骤完成。

  12. 如果 installFailed 为 true,则:

    1. registration正在安装的工作者和 “冗余”作为参数运行更新工作者状态算法。

    2. registration、“正在安装”和 null 作为参数运行更新注册状态算法。

    3. 如果 newestWorker 为 null,则移除 注册映射[(registration存储密钥, 序列化的 registration作用域 URL)]。

    4. 使用 job 调用完成任务并中止这些步骤。

  13. mapregistration正在安装的工作者脚本资源映射

  14. usedSetregistration正在安装的工作者已用脚本集

  15. 对于 map 中的每个 url

    1. 如果 usedSet包含 url,则移除 map[url]。

  16. 如果 registration等待中的工作者不为 null,则:

    1. 终止 registration等待中的工作者

    2. registration等待中的工作者和“冗余”作为参数运行更新工作者状态算法。

  17. registration、“等待中”和 registration正在安装的工作者作为参数运行更新注册状态算法。

  18. registration、“正在安装”和 null 作为参数运行更新注册状态算法。

  19. registration等待中的工作者和“已安装”作为参数运行更新工作者状态算法。

  20. 使用 job 调用完成任务

  21. 等待此算法中调用的更新工作者状态排队的所有任务执行完毕。

  22. 使用 registration 调用尝试激活

    注意:如果尝试激活此处未触发激活,则当由现有活动工作者控制的最后一个客户端卸载时,异步调用 skipWaiting() 时,或现有活动工作者延长生命周期承诺解决时,将再次尝试激活

激活

输入

registration,一个 服务工作者注册

输出

  1. 如果 registration等待中工作者为 null,则中止这些步骤。

  2. 如果 registration活动工作者不为 null,则:

    1. 终止 registration活动工作者

    2. registration活动工作者和“冗余”作为参数运行更新工作者状态算法。

  3. registration、“活动”和 registration等待中工作者作为参数运行更新注册状态算法。

  4. registration、“等待中”和 null 作为参数运行更新注册状态算法。

  5. registration活动工作者和“正在激活”作为参数运行更新工作者状态算法。

    注意:一旦活动工作者正在激活,运行时脚本错误或强制终止活动工作者都不会阻止活动工作者被激活。

    注意:确保将激活处理程序设计为执行非必要工作(例如清理)。这是因为激活处理程序可能无法全部运行完成,尤其是在激活期间浏览器终止的情况下。服务工作者应设计为即使激活处理程序未全部成功完成也能正常运行。

  6. matchedClients 为一个列表,其中包含服务工作者客户端,其创建 URL 匹配 registration存储密钥registration作用域 URL

  7. 对于 matchedClients 中的每个 client, 在 client负责的事件循环上,使用DOM 操作任务源排队一个任务,以运行以下子步骤:

    1. readyPromiseclient全局对象ServiceWorkerContainer 对象的 就绪承诺

    2. 如果 readyPromise 为 null,则继续

    3. 如果 readyPromise 处于待定状态,则使用在 readyPromise相关设置对象中表示 registration获取服务工作者注册对象的结果来解决 readyPromise

  8. 对于每个服务工作者客户端 client,它正在使用 registration

    1. client活动工作者设置为 registration活动工作者

    2. client 作为参数调用通知控制器更改算法。

  9. activeWorkerregistration活动工作者

  10. 如果使用 activeWorker 和 “activate” 运行应该跳过事件算法的结果为 false,则:

    1. 如果使用 activeWorker 运行运行服务工作者算法的结果不是失败,则:

      1. activeWorker事件循环上使用DOM 操作任务源排队一个任务 task 以运行以下步骤:

        1. e 为使用 ExtendableEvent 创建事件的结果。

        2. etype 属性初始化为 activate

        3. activeWorker全局对象分派 e

        4. 等待异步扩展并行等待,直到 e 不再 活动

      2. 等待 task 执行完毕或被丢弃。

      3. 等待标记为等待异步扩展的步骤完成。

  11. registration活动工作者和“已激活”作为参数运行更新工作者状态算法。

尝试激活

输入

registration,一个 服务工作者注册

输出

  1. 如果 registration等待中工作者为 null,则返回。

  2. 如果 registration活动工作者不为 null 且 registration活动工作者状态是“正在激活”, 则返回。

    注意:如果现有的活动工作者仍处于正在激活状态,则等待中工作者的激活将被延迟。

  3. 如果以下任一情况为真,则使用 registration 调用激活

设置 ServiceWorkerGlobalScope

输入

serviceWorker,一个 服务工作者

输出

一个 ServiceWorkerGlobalScope 对象或 null

注意:此算法返回一个可用于 CSP 检查的 ServiceWorkerGlobalScope,或者返回 null。如果 serviceWorker 有一个活动的 ServiceWorkerGlobalScope, 则返回它。否则,将新创建一个对象。

此算法为服务工作者执行最少的设置,以便创建可用于 CSP 和 COEP 等安全检查的内容。本规范确保在执行任何此类检查之前调用此算法。

在规范中,此类安全检查需要创建 ServiceWorkerGlobalScope、 一个相关设置对象、一个领域和一个代理。在 实现中,所需的工作量可能要少得多。因此,实现可以在其等效算法中执行较少的工作,而在运行服务工作者中执行更多的工作,只要结果在可观察的范围内是等效的即可。(特别是,只要所有安全检查都具有相同的结果。)

  1. unsafeCreationTime不安全的共享当前时间

  2. 如果 serviceWorker 正在运行,则返回 serviceWorker全局对象

  3. 如果 serviceWorker状态是“冗余”,则返回 null。

  4. 如果 serviceWorker全局对象不为 null,则 返回 serviceWorker全局对象

  5. 断言:serviceWorker启动状态为 null。

  6. setupFailed 为 false。

  7. globalObject 为 null。

  8. agent获取服务工作者代理的结果,并在此上下文中运行以下步骤:

    1. realmExecutionContext 为给定 agent 和以下自定义项创建新领域的结果:

    2. settingsObject 为一个新的环境设置对象,其算法定义如下:

      领域执行上下文

      返回 realmExecutionContext

      模块映射

      返回 workerGlobalScope模块映射

      API 基本 URL

      返回 serviceWorker脚本 URL

      返回其注册的服务工作者客户端

      策略容器

      返回 workerGlobalScope策略容器

      时间源

      返回给定 workerGlobalScope跨源隔离能力粗化 unsafeCreationTime 的结果。

    3. settingsObjectid 设置为一个新的唯一不透明字符串,将 创建 URL 设置为 serviceWorker脚本 URL,将 顶级创建 URL 设置为 null,将 顶级源 设置为一个实现定义的值,将 目标浏览上下文 设置为 null,并将 活动服务工作者 设置为 null。

    4. workerGlobalScopeURL 设置为 serviceWorker脚本 URL

    5. workerGlobalScope策略容器 设置为 serviceWorker脚本资源策略容器

    6. workerGlobalScope类型 设置为 serviceWorker类型

    7. 创建一个新的 WorkerLocation 对象并将其与 workerGlobalScope 关联。

    8. 如果为全局对象运行 CSP 初始化算法在 workerGlobalScope 上执行时返回“Blocked”,则将 setupFailed 设置为 true 并中止这些步骤。

    9. globalObject 设置为 workerGlobalScope

  9. 等待 globalObject 不为 null,或者 setupFailed 为 true。

  10. 如果 setupFailed 为 true,则返回 null。

  11. 返回 globalObject

运行服务工作者

输入

serviceWorker,一个 服务工作者

forceBypassCache,一个可选布尔值,默认为 false

输出

一个 完成记录失败

注意:此算法将阻塞,直到服务工作者正在运行或启动失败。

  1. 如果 serviceWorker 正在运行,则返回 serviceWorker启动状态

  2. 如果 serviceWorker状态为“冗余”,则返回失败

  3. 断言:serviceWorker启动状态为 null。

  4. scriptserviceWorker脚本资源

  5. 断言:script 不为 null。

  6. startFailed 为 false。

  7. workerGlobalScopeserviceWorker全局对象

  8. 如果 workerGlobalScope 为 null:

    1. workerGlobalScope 设置为使用 serviceWorker 运行 设置 ServiceWorkerGlobalScope 算法的结果。

    2. 如果 workerGlobalScope 为 null,则返回失败

    3. serviceWorker全局对象设置为 workerGlobalScope

  9. 获取 workerGlobalScope领域执行上下文的代理,并在此上下文中运行以下步骤:

    1. 如果 forceBypassCache 为 true,则设置 workerGlobalScope导入脚本强制绕过缓存标志

    2. 如果 serviceWorker 是一个 活动工作者,并且在 serviceWorker包含的服务工作者注册任务队列中有任何任务排队,则使用其原始任务源将它们以相同顺序排队serviceWorker事件循环任务队列

    3. evaluationStatus 为 null。

    4. 如果 script 是一个经典脚本,则:

      1. evaluationStatus 设置为运行经典脚本 script 的结果。

      2. 如果 evaluationStatus.[[Value]] 为空,这意味着脚本未被评估。将 startFailed 设置为 true 并中止这些步骤。

    5. 否则,如果 script 是一个模块脚本,则:

      1. evaluationPromise运行模块脚本 script 的结果,并将报告错误设置为 false。

      2. 断言:evaluationPromise.[[PromiseState]] 不为“pending”。

      3. 如果 evaluationPromise.[[PromiseState]] 为“rejected”:

        1. evaluationStatus 设置为 ThrowCompletion(evaluationPromise.[[PromiseResult]])。

      4. 否则:

        1. evaluationStatus 设置为 NormalCompletion(undefined)。

    6. 如果脚本被终止服务工作者算法中止,则将 startFailed 设置为 true 并中止这些步骤。

    7. serviceWorker启动状态设置为 evaluationStatus

    8. 如果 script曾经被评估标志未设置,则:

      1. 对于 settingsObject全局对象的关联事件侦听器列表的每个 eventType 事件类型:

        1. 追加 eventTypeworkerGlobalScope 的关联 服务工作者要处理的事件类型集

        注意:如果全局对象的关联事件侦听器列表此时没有任何添加的事件侦听器,则服务工作者的要处理的事件类型集仍然是一个空集。

      2. 设置 script曾经被评估标志

      3. 取消设置 serviceWorker所有 fetch 侦听器均为空标志

      4. 如果使用 workerGlobalScope所有 Fetch 侦听器均为空算法返回 true,则用户代理可以设置 serviceWorker所有 fetch 侦听器均为空标志

    9. 运行由 settingsObject 指定的负责的事件循环,直到它被销毁。

    10. 清除 workerGlobalScope活动计时器映射

  10. 等待 serviceWorker 正在运行,或者 startFailed 为 true。

  11. 如果 startFailed 为 true,则返回失败

  12. 返回 serviceWorker启动状态

所有 Fetch 侦听器均为空

输入

workerGlobalScope,一个 全局对象

输出

一个布尔值

  1. 如果 workerGlobalScope要处理的事件类型集包含 fetch,则返回 true。

  2. eventHandlerworkerGlobalScope事件处理程序映射["onfetch"] 的值。

  3. eventListenerCallbacks 为给定 workerGlobalScope 调用旧版获取服务工作者 fetch 事件侦听器回调的结果。

  4. 对于 eventListenerCallbacks 中的每个 eventListenerCallback

    1. callback 为 null。

    2. 如果 eventHandler 不为 null 且 eventListenerCallback 等于 eventHandler侦听器回调,则将 callback 设置为转换为 ECMAScript 值 eventHandler的结果。

    3. 否则,将 callback 设置为转换为 ECMAScript 值 eventListenerCallback 的结果。

    4. 如果 IsCallable(callback) 为 false,则返回 false。

      注意:使用 handleEvent(event)回调对象假定为非空。这避免了调用 handleEvent(event) 的 getter,这可能会在此检查期间修改事件侦听器。

    5. 如果 callback函数体不为空(即存在语句声明),则返回 false。

    注意:这将检测像 () => {} 这样的“fetch”侦听器。一些站点具有带有空主体的 fetch 事件侦听器,以使其被 Chromium 识别为渐进式 Web 应用程序 (PWA)。

  5. 返回 true。

注意:鼓励用户代理显示警告,指出空的“fetch”侦听器是不必要的,并且可能会对性能产生负面影响。

终止服务工作者

输入

serviceWorker,一个 服务工作者

输出

  1. serviceWorker 的主循环并行运行以下步骤:

    1. serviceWorkerGlobalScopeserviceWorker全局对象

    2. serviceWorkerGlobalScope关闭标志设置为 true。

    3. 移除 serviceWorker扩展事件集中的所有项目

    4. 如果在 serviceWorkerGlobalScope事件循环任务队列中排队了任何任务,其任务源处理 fetch 任务源处理功能事件任务源,则使用其原始任务源将它们以相同顺序排队serviceWorker包含的服务工作者注册的相应任务队列,并从 serviceWorkerGlobalScope事件循环任务队列中丢弃所有任务(包括任务,其任务源既不是处理 fetch 任务源也不是处理功能事件任务源)而不处理它们。

      注意:这实际上意味着 fetch 事件和其他功能事件(如推送事件)由注册的任务队列备份,而其他任务(包括消息事件)则被丢弃。

    5. 中止当前在 serviceWorker 中运行的脚本。

    6. serviceWorker启动状态设置为 null。

处理 Fetch

处理 Fetch 算法是获取处理的入口点,该处理移交给服务工作者上下文。

输入

request,一个 请求

fetchController,一个 获取控制器

useHighResPerformanceTimers,一个布尔值

输出

一个 响应

  1. registration 为 null。

  2. clientrequest客户端

  3. reservedClientrequest保留客户端

  4. preloadResponse 为一个新的 promise

  5. workerRealm 为 null。

  6. timingInfo 为一个新的 服务工作者计时信息

  7. 断言:request目标不是 "serviceworker"。

  8. 如果 request目标是 "embed" 或 "object",则:

    1. 返回 null。

  9. 否则,如果 request 是一个非子资源请求,则:

    1. 如果 reservedClient 不为 null 并且是一个环境设置对象, 则:

      1. 如果 reservedClient 不是一个安全上下文,则返回 null。

    2. 否则:

      1. 如果 requestURL 不是一个潜在可信 URL,则返回 null。

    3. 如果 request 是一个导航请求,并且触发它的导航是以 shift+reload 或等效方式启动的,则返回 null。

    4. 断言 reservedClient 不为 null。

    5. storage key 为给定 reservedClient 运行获取存储密钥的结果。

    6. registration 设置为给定 storage keyrequestURL 运行匹配服务工作者注册的结果。

    7. 如果 registration 为 null 或 registration活动工作者为 null,则返回 null。

    8. 如果 request目标不是 "report", 则将 reservedClient活动服务工作者设置为 registration活动工作者

    注意:从这一点开始,服务工作者客户端开始使用活动服务工作者包含的服务工作者注册

  10. 否则,如果 request 是一个子资源请求,则:

    1. 如果 client活动服务工作者不为 null,则将 registration 设置为 client活动服务工作者包含的服务工作者注册

    2. 否则,返回 null。

  11. activeWorkerregistration活动工作者

  12. 如果以下任一情况为真,则令 shouldSoftUpdate 为 true,否则为 false:

  13. 如果 activeWorker路由器规则列表不为空

    1. source 为使用 registration活动工作者request 运行获取路由器源算法的结果。

    2. 如果 source"network"

      1. 如果 shouldSoftUpdate 为 true,则并行运行软更新算法,参数为 registration

      2. 返回 null。

    3. 否则,如果 source"cache", 或者 source["cacheName"] 存在,则:

      1. 如果 shouldSoftUpdate 为 true,则并行运行软更新算法,参数为 registration

      2. 对于 registration存储密钥名称到缓存映射中的每个 cacheNamecache

        1. 如果 source["cacheName"] 存在source["cacheName"] 不是 cacheName,则继续

        2. requestResponses 为使用 request、一个新的 CacheQueryOptionscache 运行查询缓存的结果。

        3. 如果 requestResponses 是一个空的列表,则返回 null。

        4. 否则:

          1. requestResponserequestResponses 的第一个元素。

          2. responserequestResponse 的响应。

          3. globalObjectactiveWorker全局对象

          4. 如果 globalObject 为 null:

            1. globalObject 设置为使用 activeWorker 运行设置 ServiceWorkerGlobalScope的结果。

          5. 如果 globalObject 为 null,则返回 null。

          注意:这仅创建一个 ServiceWorkerGlobalScope,因为 CORS 检查需要它。预计实现不会在此处实际创建 ServiceWorkerGlobalScope。

          1. 如果 response类型是 "opaque",并且使用 globalObjectglobalObject、"" 和 response内部响应进行跨源资源策略检查返回已阻止,则返回 null。

          2. 返回 response

      3. 返回 null。

    4. 否则,如果 source"race-network-and-fetch-handler", 并且 request方法是 `GET`,则:

      1. 如果 shouldSoftUpdate 为 true,则并行运行软更新算法,参数为 registration

      2. queue 为一个空的队列,其中包含响应

      3. raceFetchController 为 null。

      4. raceResponse 为一个竞争响应,其为 "pending"。

      5. 并行运行以下子步骤,但当 fetchController状态为 "terminated" 或 "aborted" 时中止

        1. raceFetchController 设置为给定 request 调用获取的结果, 并将processResponse 设置为以下步骤,给定一个响应 raceNetworkRequestResponse

          1. 如果 raceNetworkRequestResponse状态正常状态,则:

            1. raceResponse设置为 raceNetworkRequestResponse

            2. raceNetworkRequestResponse 入队到 queue

          2. 否则,将 raceResponse设置为一个网络错误

      6. 如果中止raceFetchController 不为 null,则:

        1. 中止 raceFetchController

        2. raceResponse 设置为一个竞争响应,其为 null。

      7. 用 undefined 解析 preloadResponse

      8. 并行运行以下子步骤:

        1. fetchHandlerResponse 为使用 requestregistrationuseHighResPerformanceTimerstimingInfoworkerRealmreservedClientpreloadResponseraceResponse 创建 Fetch 事件并分派的结果。

        2. 如果 fetchHandlerResponse 不为 null 且不是一个网络错误,并且 raceFetchController 不为 null,则中止 raceFetchController

        3. fetchHandlerResponse 入队到 queue

      9. 等待直到 queue 不为空。

      10. 返回出队 queue 的结果。

    5. 断言:source 是 "fetch-event"

  14. 如果 request 是一个导航请求registration导航预加载启用标志已设置,request方法是 `GET`, registration活动工作者要处理的事件类型集包含 fetch,并且 registration活动工作者所有 fetch 侦听器均为空标志未设置,则:

    注意:如果上述情况为真,只是 registration活动工作者要处理的事件类型集包含 fetch,则用户代理可能希望显示控制台警告,因为开发人员的意图尚不清楚。

    1. preloadRequest克隆请求 request 的结果。

    2. preloadRequestHeaderspreloadRequest标头列表

    3. preloadResponseObject 为一个新的 Response 对象,与一个新的 Headers 对象关联,其守卫为 "immutable"。

    4. 追加preloadRequestHeaders 一个新的标头,其名称为 `Service-Worker-Navigation-Preload`,registration导航预加载标头值

    5. preloadRequest服务工作者模式设置为 "none"。

    6. preloadFetchController 为 null。

    7. 并行运行以下子步骤,但当 fetchController状态为 "terminated" 或 "aborted" 时中止

      1. preloadFetchController 设置为获取 preloadRequest 的结果。

        对于 navigationPreloadResponse,要处理响应,请运行以下子步骤:

        1. 如果 navigationPreloadResponse类型是 "error",则用 TypeError 拒绝 preloadResponse 并终止这些子步骤。

        2. preloadResponseObjectnavigationPreloadResponse 关联。

        3. preloadResponseObject 解析 preloadResponse

    8. 如果中止,则:

      1. deserializedError 为给定 null 和 workerRealm 反序列化序列化中止原因的结果。

      2. deserializedError 中止 preloadFetchController

  15. 否则,用 undefined 解析 preloadResponse

  16. 返回使用 requestregistrationuseHighResPerformanceTimerstimingInfoworkerRealmreservedClientpreloadResponse 和 null 创建 Fetch 事件并分派的结果。

创建 Fetch 事件并分派

输入

request,一个 请求

registration,一个 服务工作者注册

useHighResPerformanceTimers,一个布尔值

timingInfo,一个 服务工作者计时信息

workerRealm全局对象相关领域

reservedClient,一个 保留客户端

preloadResponse,一个 promise

raceResponse,一个 竞争响应或 null

输出

一个 响应

  1. response 为 null。

  2. eventCanceled 为 false。

  3. clientrequest客户端

  4. activeWorkerregistration活动工作者

  5. eventHandled 为 null。

  6. handleFetchFailed 为 false。

  7. respondWithEntered 为 false。

  8. 如果以下任一情况为真,则令 shouldSoftUpdate 为 true,否则为 false:

  9. 如果使用 "fetch" 和 activeWorker 运行应该跳过事件算法的结果为 true,则:

    1. 如果 shouldSoftUpdate 为 true,则并行运行软更新算法,参数为 registration

    2. 返回 null。

  10. 如果 activeWorker所有 fetch 侦听器均为空标志已设置:

    1. 并行

      1. 如果 activeWorker状态为 "activating",则等待 activeWorker状态变为 "activated"。

      2. 使用 activeWorker 运行运行服务工作者算法。

      3. 如果 shouldSoftUpdate 为 true,则使用 registration 运行软更新算法。

    2. 返回 null。

  11. 如果 useHighResPerformanceTimers 为 true,则将 useHighResPerformanceTimers 设置为 activeWorker全局对象跨源隔离能力

  12. timingInfo开始时间为给定 useHighResPerformanceTimers粗略共享当前时间

  13. 如果 activeWorker状态为 "activating",则等待 activeWorker状态变为 "activated"。

  14. 如果使用 activeWorker 运行运行服务工作者算法的结果为失败,则将 handleFetchFailed 设置为 true。

  15. 否则:

    1. workerRealm 设置为 activeWorker全局对象相关领域

    2. eventHandled 设置为 workerRealm 中的一个新的 promise

    3. 如果 raceResponse 不为 null,则设置 activeWorker全局对象竞争响应映射[request] 为 raceResponse

    4. 将任务 task 排队以运行以下子步骤:

      1. e 为使用 FetchEvent 创建事件的结果。

      2. abortControllerworkerRealm 中的一个新的 AbortController 对象。

      3. requestObject 为给定 request、一个新的 Headers 对象的守卫(为 "immutable")、abortController信号workerRealm 创建一个 Request 对象的结果。

      4. etype 属性初始化为 fetch

      5. ecancelable 属性初始化为 true。

      6. erequest 属性初始化为 requestObject

      7. epreloadResponse 初始化为 preloadResponse

      8. eclientId 属性初始化为 clientid

      9. 如果 request 是一个非子资源请求,并且 request目标不是 "report", 则将 eresultingClientId 属性初始化为 reservedClientid,否则初始化为空字符串。

      10. 如果 request 是一个导航请求,则将 ereplacesClientId 属性初始化为 request替换客户端 ID, 否则初始化为空字符串。

      11. ehandled 初始化为 eventHandled

      12. timingInfofetch 事件分派时间为给定 useHighResPerformanceTimers粗略共享当前时间

      13. activeWorker全局对象分派 e

      14. 使用 activeWorkere 调用更新服务工作者扩展事件集

      15. 如果 erespond-with entered 标志已设置,则将 respondWithEntered 设置为 true。

      16. 如果 ewait to respond 标志已设置,则:

        1. 等待直到 ewait to respond 标志未设置。

        2. 如果 erespond-with error 标志已设置,则将 handleFetchFailed 设置为 true。

        3. 否则,将 response 设置为 e潜在响应

      17. 如果 response 为 null,request主体不为 null,并且 request主体为 null,则:

        1. 如果 request主体不可用,则将 handleFetchFailed 设置为 true。

        2. 否则,使用 undefined 取消 request主体

      18. 如果 response 不为 null,则将 response服务工作者计时信息设置为 timingInfo

      19. 如果 ecanceled 标志已设置,则将 eventCanceled 设置为 true。

      20. 如果 fetchController 状态为 "terminated" 或 "aborted",则:

        1. deserializedError 为给定 fetchController序列化中止原因workerRealm 反序列化序列化中止原因的结果。

        2. 将任务排队以使用 deserializedErrorabortController发出中止信号

      如果 task 被丢弃,则将 handleFetchFailed 设置为 true。

      task 必须使用 activeWorker事件循环处理 fetch 任务源

  16. 等待 task 执行完毕或 handleFetchFailed 为 true。

  17. 如果 shouldSoftUpdate 为 true,则并行运行软更新算法,参数为 registration

  18. 如果 activeWorker全局对象竞争响应映射[request] 存在,则移除 activeWorker全局对象竞争响应映射[request]。

  19. 如果 respondWithEntered 为 false,则:

    1. 如果 eventCanceled 为 true,则:

      1. 如果 eventHandled 不为 null,则在 workerRealm 中使用 "NetworkError" DOMException 拒绝 eventHandled

      2. 返回一个网络错误

    2. 如果 eventHandled 不为 null,则解析 eventHandled

    3. 如果 raceResponse不为 null,则:

      1. 等待直到 raceResponse不是 "pending"。

      2. 如果 raceResponse是一个响应,则返回 raceResponse

    4. 返回 null。

  20. 如果 handleFetchFailed 为 true,则:

    1. 如果 eventHandled 不为 null,则在 workerRealm 中使用 "NetworkError" DOMException 拒绝 eventHandled

    2. 返回一个网络错误

  21. 如果 eventHandled 不为 null,则解析 eventHandled

  22. 返回 response

解析 URL 模式

输入

rawPattern,一个 URLPatternCompatible

serviceWorker,一个 服务工作者

输出

一个 URL 模式

  1. baseURLserviceWorker脚本 URL

  2. 返回给定 baseURL,使用 rawPattern 从 Web IDL 值构建 URL 模式的结果。

验证路由器条件

输入

condition,一个 RouterCondition

serviceWorker,一个 服务工作者

输出

一个布尔值

  1. hasCondition 为 false。

  2. 如果 condition["urlPattern"] 存在,则:

    1. rawPatterncondition["urlPattern"]。

    2. pattern 为传递 rawPatternserviceWorker 运行解析 URL 模式算法的结果。如果此操作抛出异常,则捕获该异常并返回 false。

    3. 如果 pattern 具有正则表达式组,则返回 false。

      注意:由于运行用户定义的正则表达式存在安全问题,因此禁止这样做。

    4. hasCondition 设置为 true。

  3. 如果 condition["requestMethod"] 存在

    1. methodcondition["requestMethod"]。

    2. 如果 method 不是一个方法,则返回 false。

    3. 如果 method 是一个禁止的方法,则返回 false。

    4. hasCondition 设置为 true。

  4. 如果 condition["requestMode"] 存在,则将 hasCondition 设置为 true。

  5. 如果 condition["requestDestination"] 存在,则将 hasCondition 设置为 true。

  6. 如果 condition["runningStatus"] 存在,则将 hasCondition 设置为 true。

  7. 如果 condition["_or"] 存在,则:

    1. 如果 hasCondition 为 true,则返回 false。

      注意:为了便于理解路由器规则,"or" 条件与其他条件互斥。

    2. orConditionscondition["_or"]。

    3. 对于 orConditions 中的每个 orCondition

      1. 如果使用 orConditionserviceWorker 运行验证路由器条件算法返回 false,则返回 false。

    4. hasCondition 设置为 true。

  8. 如果 condition["not"] 存在,则:

    1. 如果 hasCondition 为 true,则返回 false。

      注意:为了便于理解路由器规则,"not" 条件与其他条件互斥。

    2. 如果使用 condition["not"] 和 serviceWorker 运行验证路由器条件算法返回 false,则返回 false。

    3. hasCondition 设置为 true。

  9. 返回 hasCondition

匹配路由器条件

输入

condition,一个 RouterCondition

serviceWorker,一个 服务工作者

request,一个 请求

输出

一个布尔值

注意:如果存在多个条件(例如,设置了 urlPatternrunningStatusrequestMethod),则所有条件都需要匹配才能返回 true。

  1. 如果 condition["or"] 存在,则:

    1. orConditionscondition["or"]。

    2. 对于 orConditions 中的每个 orCondition

      1. 如果使用 orConditionserviceWorkerrequest 运行匹配路由器条件算法返回 true,则返回 true。

    3. 返回 false。

  2. 如果 condition["not"] 存在,则:

    1. 如果使用 condition["not"]、 serviceWorkerrequest 运行匹配路由器条件算法返回 true,则返回 false。

    2. 返回 true。

  3. 否则:

    注意:验证路由器条件算法保证 ornot 和其他条件是互斥的。

    1. 如果 condition["urlPattern"] 存在,则:

      1. rawPatterncondition["urlPattern"]。

      2. pattern 为传递 rawPatternserviceWorker 运行解析 URL 模式算法的结果。

      3. 如果使用 patternrequestURL 运行匹配返回 null,则返回 false。

    2. 如果 condition["requestMethod"] 存在,则:

      1. methodcondition["requestMethod"]。

      2. 规范化 method

      3. 如果 request方法不是 method,则返回 false。

    3. 如果 condition["requestMode"] 存在,则:

      1. modecondition["requestMode"]。

      2. 如果 request模式不是 mode, 则返回 false。

    4. 如果 condition["requestDestination"] 存在,则:

      1. destinationcondition["requestDestination"]。

      2. 如果 request目标不是 destination,则返回 false。

    5. 如果 condition["runningStatus"] 存在,则:

      1. runningStatuscondition["runningStatus"]。

      2. 如果 runningStatus"running", 并且 serviceWorker运行,则返回 false。

      3. 如果 runningStatus"not-running", 并且 serviceWorker 正在运行,则返回 false。

    6. 返回 true。

检查路由器注册限制

输入

routerRules,一个路由器规则列表

输出

一个布尔值

注意:路由器条件可能很复杂,并且可以使用 _ornot 进行嵌套。 为了防止过度处理,此算法引入了两个限制。首先,条件总数(计算所有嵌套条件)不能超过 1024。其次,嵌套深度限制为 10 级,以避免指数级计算。

  1. result 为一个计数路由器条件结果

  2. result条件计数设置为 1024。

  3. result超出配额设置为 false。

  4. 对于 routerRules 中的每个 rule

    1. result 设置为使用 rule["condition"]、 result 和 10 运行计算路由器内部条件的结果。

    2. 如果 result超出配额为 true,则返回 false。

  5. 返回 true。

计算路由器内部条件

输入

condition,一个 RouterCondition

result,一个计数路由器条件结果

depth,一个数字

输出

result,一个计数路由器条件结果

  1. result条件计数减一。

  2. 如果 result条件计数为零,或者 depth 为零,则:

    1. result超出配额设置为 true。

    2. 返回 result

  3. 如果 condition["_or"] 存在,则:

    1. depth 减一。

    2. 对于 condition["_or"] 中的每个 orCondition

      1. result 设置为使用 orConditionresultdepth 运行计算路由器内部条件的结果。

      2. 如果 result超出配额为 true,则返回 result

  4. 否则,如果 condition["not"] 存在,则:

    1. depth 减一。

    2. result 设置为使用 condition["not"]、 resultdepth 运行计算路由器内部条件的结果。

    3. 如果 result超出配额为 true,则返回 result

  5. 返回 result

获取路由器源

输入

serviceWorker,一个服务工作者

request,一个请求

输出

RouterSource 或 null

  1. 对于 serviceWorker路由器规则列表中的每个 rule

    1. 如果使用 rule["condition"]、 serviceWorkerrequest 运行匹配路由器条件算法返回 true,则返回 rule["source"]。

  2. 返回 null。

是否应跳过事件

输入

eventName,一个字符串

serviceWorker,一个服务工作者

输出

一个布尔值

注意:为避免不必要的延迟,本规范允许在服务工作者的全局范围内首次执行脚本期间未确定性地添加该事件的事件侦听器时跳过事件分派。

  1. 如果 serviceWorker要处理的事件类型集包含 eventName,则用户代理可以返回 true。

  2. 返回 false。

触发功能性事件

输入

eventName,一个字符串

eventConstructor,一个扩展了 ExtendableEvent 的事件构造函数

registration,一个服务工作者注册

initialization,可选的 event 属性初始化,由 eventConstructor 构造

postDispatchSteps,可选步骤,在活动工作者的事件循环中运行,并将 dispatchedEvent 设置为已分派eventConstructor 实例。

输出

  1. 断言:registration活动工作者不为 null。

  2. activeWorkerregistration活动工作者

  3. 如果使用 eventNameactiveWorker 运行是否应跳过事件的结果为 true,则:

    1. 如果 registration 过时,则并行运行软更新算法,参数为 registration

    2. 返回。

  4. 如果 activeWorker状态为 "activating",则等待 activeWorker状态变为 "activated"。

  5. 如果使用 activeWorker 运行运行服务工作者算法的结果为失败,则:

    1. 如果 registration 过时,则并行运行软更新算法,参数为 registration

    2. 返回。

  6. 将任务 task 排队以运行这些子步骤:

    1. event 为使用 eventConstructoractiveWorker全局对象相关领域创建事件的结果。

    2. 如果 initialization 不为 null,则使用 initialization 初始化 event

    3. activeWorker全局对象分派 event

    4. 使用 activeWorkerevent 调用更新服务工作者扩展事件集

    5. 如果 postDispatchSteps 不为 null,则将 event 作为 dispatchedEvent 传递并运行 postDispatchSteps

    task 必须使用 activeWorker事件循环处理功能性事件任务源

  7. 等待 task 执行完毕或被丢弃。

  8. 如果 registration 过时,则并行运行软更新算法,参数为 registration

要在特定的 serviceWorkerRegistration 上触发 "amazingthing" 事件(类型为 AmazingThingEvent),并初始化事件对象的属性,其描述如下:
  1. serviceWorkerRegistration 上使用 AmazingThingEvent 触发功能性事件 "amazingthing",并具有以下属性:

    属性名称

    另一个属性名称

    另一个值

    然后使用 dispatchedEvent 运行以下步骤:

    1. 在服务工作者的事件循环中对 dispatchedEvent 执行任何所需操作。

请注意,初始化步骤和分派后步骤是可选的。如果不需要它们,则描述如下:

  1. serviceWorkerRegistration 上使用 ExtendableEvent 触发功能性事件 "whatever"。

处理服务工作者客户端卸载

用户代理必须服务工作者客户端通过卸载文档清理步骤终止卸载时运行这些步骤。

输入

client,一个服务工作者客户端

输出

  1. 原子地运行以下步骤。

  2. registrationclient 使用服务工作者注册

  3. 如果 registration 为 null,则中止这些步骤。

  4. 如果任何其他服务工作者客户端正在使用 registration,则中止这些步骤。

  5. 如果 registration取消注册,则使用 registration 调用尝试清除注册

  6. 使用 registration 调用尝试激活

处理用户代理关闭

输入

输出

  1. 对于注册映射中的每个 registration

    1. 如果 registration安装中工作者不为 null,则:

      1. 如果 registration等待中工作者为 null 且 registration活动工作者为 null,则使用 registration 调用清除注册并继续循环的下一次迭代。

      2. 否则,将 registration安装中工作者设置为 null。

    2. 如果 registration等待中工作者不为 null,则并行

      1. 使用 registration 调用激活

更新服务工作者扩展事件集

输入

worker,一个服务工作者

event,一个事件

输出

  1. 断言:event分派标志未设置。

  2. 对于 worker扩展事件集中的每个 item

    1. 如果 item 不是活动的,则从 worker扩展事件集移除 item

  3. 如果 event活动的,则将 event 追加worker扩展事件集

取消注册

输入

job,一个作业

输出

  1. 如果 job作用域 URL不是 job客户端,则:

    1. 使用 job 和 "SecurityError" DOMException 调用拒绝作业承诺

    2. 使用 job 调用完成作业并中止这些步骤。

  2. registration 为给定 job存储键job作用域 URL 运行获取注册的结果。

  3. 如果 registration 为 null,则:

    1. 使用 job 和 false 调用解决作业承诺

    2. 使用 job 调用完成作业并中止这些步骤。

  4. 移除 注册映射[(registration存储键, job作用域 URL)]。

  5. 使用 job 和 true 调用解决作业承诺

  6. 使用 registration 调用尝试清除注册

    注意:如果尝试清除注册此处未触发清除注册,则当最后一个使用该注册的客户端卸载或该注册的服务工作者的延长生命周期承诺解决时,将再次尝试清除注册

  7. 使用 job 调用完成作业

设置注册

输入

storage key,一个存储键

scope,一个URL

updateViaCache,一个通过缓存更新模式

输出

registration,一个服务工作者注册

  1. 原子地运行以下步骤。

  2. scopeString 为设置了排除片段标志序列化 scope

  3. registration 为一个新的服务工作者注册,其存储键设置为 storage key作用域 URL 设置为 scope通过缓存更新模式设置为 updateViaCache

  4. 设置 注册映射[(storage key, scopeString)] 为 registration

  5. 返回 registration

清除注册

输入

registration,一个服务工作者注册

输出

  1. 原子地运行以下步骤。

  2. 如果 registration安装中工作者不为 null,则:

    1. 终止 registration安装中工作者

    2. 运行更新工作者状态算法,将 registration安装中工作者和 "redundant" 作为参数传递。

    3. 运行更新注册状态算法,将 registration、"installing" 和 null 作为参数传递。

  3. 如果 registration等待中工作者不为 null,则:

    1. 终止 registration等待中工作者

    2. 运行更新工作者状态算法,将 registration等待中工作者和 "redundant" 作为参数传递。

    3. 运行更新注册状态算法,将 registration、"waiting" 和 null 作为参数传递。

  4. 如果 registration活动工作者不为 null,则:

    1. 终止 registration活动工作者

    2. 运行更新工作者状态算法,将 registration活动工作者和 "redundant" 作为参数传递。

    3. 运行更新注册状态算法,将 registration、"active" 和 null 作为参数传递。

尝试清除注册

输入

registration,一个服务工作者注册

输出

  1. 如果没有服务工作者客户端正在使用 registration 并且以下所有条件都为真,则使用 registration 调用清除注册

更新注册状态

输入

registration,一个服务工作者注册

target,一个字符串("installing"、"waiting" 或 "active" 之一)

source,一个服务工作者或 null

输出

  1. registrationObjects 为一个包含与 registration 关联的所有 ServiceWorkerRegistration 对象的数组。

  2. 如果 target 是 "installing",则:

    1. registration安装中工作者设置为 source

    2. 对于 registrationObjects 中的每个 registrationObject

      1. 将任务排队以将 registrationObjectinstalling 属性设置为 null(如果 registration安装中工作者为 null),或者设置为在 registrationObject相关设置对象获取表示 registration安装中工作者的服务工作者对象的结果。

  3. 否则,如果 target 是 "waiting",则:

    1. registration等待中工作者设置为 source

    2. 对于 registrationObjects 中的每个 registrationObject

      1. 将任务排队以将 registrationObjectwaiting 属性设置为 null(如果 registration等待中工作者为 null),或者设置为在 registrationObject相关设置对象获取表示 registration等待中工作者的服务工作者对象的结果。

  4. 否则,如果 target 是 "active",则:

    1. registration活动工作者设置为 source

    2. 对于 registrationObjects 中的每个 registrationObject

      1. 将任务排队以将 registrationObjectactive 属性设置为 null(如果 registration活动工作者为 null),或者设置为在 registrationObject相关设置对象获取表示 registration活动工作者的服务工作者对象的结果。

    任务必须使用 registrationObject相关设置对象负责的事件循环DOM 操作任务源

更新工作者状态

输入

worker,一个服务工作者

state,一个服务工作者 状态

输出

  1. 断言:state 不是 "parsed"。

    注意:"parsed" 是初始状态。一个服务工作者永远不会更新到此状态。

  2. worker状态设置为 state

  3. settingsObjects 为所有环境设置对象,其worker脚本 URL

  4. 对于 settingsObjects 中的每个 settingsObject,在 settingsObject负责的事件循环中的DOM 操作任务源将任务排队以运行以下步骤:

    1. objectMapsettingsObject服务工作者对象映射

    2. 如果 objectMap[worker] 不存在,则中止这些步骤。

    3. workerObjobjectMap[worker]。

    4. workerObjstate 设置为 state

    5. workerObj触发名为 statechange 的事件。

通知控制器更改

输入

client,一个服务工作者客户端

输出

  1. 断言:client 不为 null。

  2. 如果 client 是一个环境设置对象将任务排队以在与 client 关联ServiceWorkerContainer 对象上触发名为 controllerchange 的事件。

任务必须使用 client负责的事件循环DOM 操作任务源

匹配服务工作者注册

输入

storage key,一个存储键

clientURL,一个URL

输出

一个服务工作者注册

  1. 原子地运行以下步骤。

  2. clientURLString序列化clientURL

  3. matchingScopeString 为空字符串。

  4. scopeStringSet 为一个空列表。

  5. 对于注册映射中的每个 (entry storage key, entry scope):

    1. 如果 storage key 等于 entry storage key,则将 entry scope 追加scopeStringSet 的末尾。

  6. 如果存在,则将 matchingScopeString 设置为 scopeStringSetclientURLString 值开头的最长值。

    注意:此步骤中的 URL 字符串匹配是基于前缀的,而不是基于路径结构的。例如,具有 "https://example.com/prefix-of/resource.html" 的客户端 URL 字符串将匹配作用域为 "https://example.com/prefix" 的注册。URL 字符串比较对于同源安全性是安全的,因为 HTTP(S) URL 在 URL 的源部分的末尾始终使用尾部斜杠进行序列化

  7. matchingScope 为 null。

  8. 如果 matchingScopeString 不是空字符串,则:

    1. matchingScope 设置为解析 matchingScopeString 的结果。

    2. 断言:matchingScopeclientURL同源

  9. 返回给定 storage keymatchingScope 运行获取注册的结果。

获取注册

输入

storage key,一个存储键

scope,一个URL

输出

一个服务工作者注册

  1. 原子地运行以下步骤。

  2. scopeString 为空字符串。

  3. 如果 scope 不为 null,则将 scopeString 设置为设置了排除片段标志序列化 scope

  4. 对于注册映射中的每个 (entry storage key, entry scope) → registration

    1. 如果 storage key 等于 entry storage key 并且 scopeString 匹配 entry scope,则返回 registration

  5. 返回 null。

获取最新工作者

输入

registration,一个服务工作者注册

输出

newestWorker,一个服务工作者

  1. 原子地运行以下步骤。

  2. newestWorker 为 null。

  3. 如果 registration安装中工作者不为 null,则将 newestWorker 设置为 registration安装中工作者

  4. 否则,如果 registration等待中工作者不为 null,则将 newestWorker 设置为 registration等待中工作者

  5. 否则,如果 registration活动工作者不为 null,则将 newestWorker 设置为 registration活动工作者

  6. 返回 newestWorker

服务工作者没有待处理事件

输入

worker,一个服务工作者

输出

True 或 false,一个布尔值

  1. 对于 worker扩展事件集中的每个 event

    1. 如果 event活动的,则返回 false。

  2. 返回 true。

创建客户端

输入

client,一个服务工作者客户端

输出

clientObject,一个 Client 对象

  1. clientObject 为一个新的 Client 对象。

  2. clientObject服务工作者客户端设置为 client

  3. 返回 clientObject

创建窗口客户端

输入

client,一个服务工作者客户端

frameType,一个字符串

visibilityState,一个字符串

focusState,一个布尔值

ancestorOriginsList,一个列表

输出

windowClient,一个 WindowClient 对象

  1. windowClient 为一个新的 WindowClient 对象。

  2. windowClient服务工作者客户端设置为 client

  3. windowClient框架类型设置为 frameType

  4. windowClient可见性状态设置为 visibilityState

  5. windowClient焦点状态设置为 focusState

  6. windowClient祖先源数组设置为从 ancestorOriginsList 创建的冻结数组

  7. 返回 windowClient

获取框架类型

输入

navigable,一个可导航对象

输出

frameType,一个字符串

  1. 如果 navigable父级不为 null,则返回 "nested"。

  2. 如果 navigable活动浏览上下文是一个辅助浏览上下文,则返回 "auxiliary"。

  3. 返回 "top-level"。

解决 Get Client Promise

输入

client,一个服务工作者客户端

promise,一个promise

输出

  1. 如果 client 是一个环境设置对象,则:

    1. 如果 client 不是一个安全上下文,则在 promise相关设置对象负责的事件循环上使用DOM 操作任务源将任务排队以使用 "SecurityError" DOMException 拒绝 promise,并中止这些步骤。

  2. 否则:

    1. 如果 client创建 URL 不是一个潜在可信 URL,则在 promise相关设置对象负责的事件循环上使用DOM 操作任务源将任务排队以使用 "SecurityError" DOMException 拒绝 promise,并中止这些步骤。

  3. 如果 client 是一个环境设置对象并且不是一个窗口客户端,则:

    1. clientObject 为以 client 作为参数运行创建客户端算法的结果。

    2. promise相关设置对象负责的事件循环上使用DOM 操作任务源将任务排队以使用 clientObject 解决 promise,并中止这些步骤。

  4. 否则:

    1. browsingContext 为 null。

    2. 如果 client 是一个环境设置对象,则将 browsingContext 设置为 client全局对象浏览上下文

    3. 否则,将 browsingContext 设置为 client目标浏览上下文

    4. navigable 为具有 browsingContext活动浏览上下文可导航对象

    5. browsingContext事件循环上使用用户交互任务源将任务排队以运行以下步骤:

      1. frameType 为以 navigable 运行获取框架类型的结果。

      2. visibilityStatebrowsingContext活动文档visibilityState 属性值。

      3. focusState 为以 browsingContext活动文档作为参数运行具有焦点步骤的结果。

      4. ancestorOriginsList 为空列表。

      5. 如果 client 是一个窗口客户端,则将 ancestorOriginsList 设置为 browsingContext活动文档相关全局对象Location 对象的祖先源列表的关联列表。

      6. promise相关设置对象负责的事件循环上使用DOM 操作任务源将任务排队以运行以下步骤:

        1. 如果 client已丢弃标志已设置,则使用 undefined 解决 promise 并中止这些步骤。

        2. windowClient 为使用 clientframeTypevisibilityStatefocusStateancestorOriginsList 运行创建窗口客户端的结果。

        3. 使用 windowClient 解决 promise

查询缓存

输入

requestQuery,一个请求

options,一个可选的 CacheQueryOptions 对象

targetStorage,一个可选的请求响应列表

输出

resultList,一个请求响应列表

  1. resultList 为一个空的列表

  2. storage 为 null。

  3. 如果省略了可选参数 targetStorage,则将 storage 设置为相关的请求响应列表

  4. 否则,将 storage 设置为 targetStorage

  5. 对于 storage 中的每个 requestResponse

    1. cachedRequestrequestResponse 的请求。

    2. cachedResponserequestResponse 的响应。

    3. 如果使用 requestQuerycachedRequestcachedResponseoptions请求匹配缓存项返回 true,则:

      1. requestCopycachedRequest 的副本。

      2. responseCopycachedResponse 的副本。

      3. requestCopy/responseCopy 添加到 resultList

  6. 返回 resultList

请求匹配缓存项

输入

requestQuery,一个请求

request,一个请求

response,一个响应或 null,可选,默认为 null

options,一个可选的 CacheQueryOptions 对象

输出

一个布尔值

  1. 如果 options["ignoreMethod"] 为 false 且 request方法不是 `GET`,则返回 false。

  2. queryURLrequestQueryURL

  3. cachedURLrequestURL

  4. 如果 options["ignoreSearch"] 为 true,则:

    1. cachedURL查询设置为空字符串。

    2. queryURL查询设置为空字符串。

  5. 如果 queryURL 在设置了排除片段标志的情况下不等于 cachedURL,则返回 false。

  6. 如果 response 为 null,options["ignoreVary"] 为 true,或者 response标头列表包含 `Vary`,则返回 true。

  7. fieldValues 为包含与Vary 标头的字段值对应的元素的列表,该标头名称为 `Vary`,为该标头的值。

  8. 对于 fieldValues 中的每个 fieldValue

    1. 如果 fieldValue 匹配 "*",或者给定 fieldValuerequest标头列表组合值与给定 fieldValuerequestQuery标头列表组合值不匹配,则返回 false。

  9. 返回 true。

批量缓存操作

输入

operations,一个缓存批量操作对象的列表

输出

resultList,一个请求响应列表

  1. cache相关的请求响应列表

  2. backupCache 为一个作为 cache 副本的新的请求响应列表

  3. addedItems 为一个空的列表

  4. 尝试原子地运行以下子步骤:

    1. resultList 为一个空的列表

    2. 对于 operations 中的每个 operation

      1. 如果 operation类型既不匹配 "delete" 也不匹配 "put",则抛出一个 TypeError

      2. 如果 operation类型匹配 "delete" 且 operation响应不为 null,则抛出一个 TypeError

      3. 如果使用 operation请求operation选项addedItems 运行查询缓存的结果不为空,则抛出一个 "InvalidStateError" DOMException

      4. requestResponses 为一个空的列表

      5. 如果 operation类型匹配 "delete",则:

        1. requestResponses 设置为使用 operation请求operation选项运行查询缓存的结果。

        2. 对于 requestResponses 中的每个 requestResponse

          1. cache移除值与 requestResponse 匹配的

      6. 否则,如果 operation类型匹配 "put",则:

        1. 如果 operation响应为 null,则抛出一个 TypeError

        2. roperation请求的关联请求

        3. 如果 rURL方案不是 "http" 或 "https" 之一,则抛出一个 TypeError

        4. 如果 r方法不是 `GET`,则抛出一个 TypeError

        5. 如果 operation选项不为 null,则抛出一个 TypeError

        6. requestResponses 设置为使用 operation请求运行查询缓存的结果。

        7. 对于 requestResponses 中的每个 requestResponse

          1. cache移除值与 requestResponse 匹配的

        8. operation请求/operation响应追加cache

        9. 如果前两个步骤中的缓存写入操作由于超出授予的配额限制而失败,则抛出一个 "QuotaExceededError" DOMException

        10. operation请求/operation响应追加addedItems

      7. operation请求/operation响应追加resultList

    3. 返回 resultList

  5. 然后,如果抛出了异常,则:

    1. 相关的请求响应列表移除所有

    2. 对于 backupCache 中的每个 requestResponse

      1. requestResponse 追加相关的请求响应列表

    3. 抛出异常。

    注意:抛出异常时,实现会撤消(回滚)在批量操作作业期间对缓存存储所做的任何更改。

是异步模块

输入

record,一个模块记录

moduleMap,一个模块映射

base,一个URL

seen,一个URL集合

输出

一个布尔值

  1. 如果 record 不是一个循环模块记录,则:

    1. 返回 false。

  2. 如果 record.[[Async]] 为 true,则:

    1. 返回 true。

  3. 对于 record.[[RequestedModules]] 中的每个字符串 requested

    1. url 为给定 baserequested 解析模块说明符的结果。

    2. 断言:url 永远不会失败,因为解析模块说明符先前必须已使用这两个相同的参数成功。

    3. 如果 seen包含 url,则:

      1. 追加 urlseen

      2. 如果 moduleMap[url] 没有记录,则:

        1. 返回 false。

      3. 如果对于 moduleMap[url] 的记录moduleMapbaseseen是异步模块为 true,则:

        1. 返回 true。

  4. 返回 false。

查找竞争响应

输入

request,一个请求

输出

一个响应或 null

  1. registration 为 null。

  2. 如果 request 是一个非子资源请求,则:

    1. 如果 request保留客户端为 null,则返回 null。

    2. storage key 为给定 request保留客户端运行获取存储密钥的结果。

    3. registration 设置为给定 storage keyrequestURL 运行匹配服务工作者注册的结果。

  3. 否则,如果 request 是一个子资源请求,则:

    1. clientrequest客户端

    2. 如果 client活动服务工作者为 null,则返回 null。

    3. registration 设置为 client活动服务工作者包含服务工作者注册

  4. 否则,返回 null。

  5. activeWorkerregistration活动工作者

  6. mapactiveWorker全局对象竞争响应映射

  7. 如果 map[request] 存在,则:

    1. entrymap[request]。

    2. 移除 map[request]。

    3. 等待直到 entry不是 "pending"

    4. 如果 entry响应,则返回 entry

  8. 返回 null。

附录 B:扩展 HTTP 标头

Service Worker 脚本请求

一个用于获取 service worker脚本资源的 HTTP 请求将包含以下标头

`Service-Worker`

指示此请求是一个 service worker脚本资源请求。

注意:此标头有助于管理员记录请求并检测威胁。

Service Worker 脚本响应

service worker脚本资源请求的 HTTP 响应可以包含以下标头

`Service-Worker-Allowed`

指示用户代理将覆盖路径限制,该限制限制了脚本可以控制的最大允许作用域 URL,并将其设置为给定值。

注意:该值为一个 URL。如果给定的是相对 URL,则会根据脚本的 URL 进行解析。

默认作用域:
// 最大允许作用域默认为脚本所在的路径
// 在此示例中为 "/js/"
navigator.serviceWorker.register("/js/sw.js").then(() => {
    console.log("安装成功,默认作用域为 '/js/'。");
});
没有 Service-Worker-Allowed 标头的上级路径:
// 将作用域设置为脚本位置的上级路径
// 响应中没有 Service-Worker-Allowed 标头
navigator.serviceWorker.register("/js/sw.js", { scope: "/" }).catch(() => {
    console.error("由于违反路径限制,安装失败。");
});
带有 Service-Worker-Allowed 标头的上级路径:
// 将作用域设置为脚本位置的上级路径
// 响应中包含 "Service-Worker-Allowed : /"
navigator.serviceWorker.register("/js/sw.js", { scope: "/" }).then(() => {
    console.log("安装成功,因为最大允许作用域已覆盖为 '/'。");
});
即使带有 Service-Worker-Allowed 标头也存在路径限制冲突:
// 将作用域设置为脚本位置的上级路径
// 响应中包含 "Service-Worker-Allowed : /foo"
navigator.serviceWorker.register("/foo/bar/sw.js", { scope: "/" }).catch(() => {
    console.error("安装失败,因为作用域仍超出覆盖的最大允许作用域。");
});

语法

service worker脚本资源请求和响应使用的标头值的 ABNF

Service-Worker = %x73.63.72.69.70.74 ; "script",区分大小写

注意:Service-Worker-Allowed 标头值的验证是通过 URL 解析算法(在更新算法中)完成的,而不是使用 ABNF。

8. 致谢

深深感谢 Andrew Betts 组织并主持了一个由志同道合者组成的小型研讨会,其中包括:Jake Archibald、Jackson Gabbard、Tobie Langel、Robin Berjon、Patrick Lauke、Christian Heilmann。 благодаря清晰的讨论和概述的用例,许多事情成为可能。还要感谢 Andrew 提高了人们对离线问题的认识。他组织的 EdgeConf 并将离线作为一个持续性议题,为这项工作的进展创造了许多机会和联系。

在 service worker 的整个开发过程中,Anne van Kesteren 慷慨地贡献了他对 Web 平台奥秘和标准制定经验的百科全书般的知识。没有他之前在描述 URL、HTTP Fetch、Promises 和 DOM 的真实行为方面的工作,本规范将是不完整的。同样,没有 Ian Hickson 严谨的 Web Worker 规范,本规范也不可能实现。非常感谢他。

排名不分先后,深深感谢以下人士的设计指导和讨论:Jungkee Song、Alec Flett、David Barrett-Kahn、Aaron Boodman、Michael Nordman、Tom Ashworth、Kinuko Yasuda、Darin Fisher、Jonas Sicking、Jesús Leganés Combarro、Mark Christian、Dave Hermann、Yehuda Katz、François Remy、Ilya Grigorik、Will Chan、Domenic Denicola、Nikhil Marathe、Yves Lafon、Adam Barth、Greg Simon、Devdatta Akhawe、Dominic Cooney、Jeffrey Yasskin、Joshua Bell、Boris Zbarsky、Matt Falkenhagen、Tobie Langel、Gavin Peters、Ben Kelly、Hiroki Nakagawa、Jake Archibald、Josh Soref、Jinho Bang、Yutaka Hirano、Michael(tm) Smith、isonmad、Ali Alabbas、Philip Jägenstedt、Mike Pennisi 和 Eric Willigers。

Jason Weber、Chris Wilson、Paul Kinlan、Ehsan Akhgari 和 Daniel Austin 就需求和标准化过程提供了宝贵且及时的反馈。

作者们还要感谢 Dimitri Glazkov,他的脚本和格式化工具对本规范的制作至关重要。作者们也非常感谢他的悉心指导。

还要感谢 Vivian Cromwell、Greg Simon、Alex Komoroske、Wonsuk Lee 和 Seojin Kim 给予的大力专业支持。

一致性

文档约定

一致性要求通过描述性断言和 RFC 2119 术语的组合来表达。 本文档规范部分的关键词“MUST”、“MUST NOT”、“REQUIRED”、“SHALL”、“SHALL NOT”、“SHOULD”、“SHOULD NOT”、“RECOMMENDED”、“MAY”和“OPTIONAL” 应按照 RFC 2119 中的描述进行解释。 但是,为了便于阅读, 这些词在本规范中不大写显示。

本规范的所有文本都是规范性的, 明确标记为非规范性的章节、示例和注释除外。 [RFC2119]

本规范中的示例以“例如”一词引入, 或通过 class="example" 与规范性文本分开, 如下所示:

这是一个信息性示例的例子。

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

注意,这是一个信息性注释。

一致性算法

作为算法一部分以祈使语气表述的要求 (例如“去除任何前导空格字符” 或“返回 false 并中止这些步骤”) 应根据引入算法时使用的关键词 (“必须”、“应该”、“可以”等)的含义进行解释。

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

索引

本规范定义的术语

引用定义的术语

参考文献

规范性参考文献

[CSP-NEXT]
Scripting Policy. 编辑草案. URL: https://wicg.github.io/csp-next/scripting-policy.html
[CSP3]
Mike West; Antonio Sartori. Content Security Policy Level 3. 2025年2月6日. 工作草案. URL: https://www.w3.org/TR/CSP3/
[DOM]
Anne van Kesteren. DOM Standard. 现行标准. URL: https://dom.spec.whatwg.org/
[ECMASCRIPT]
ECMAScript Language Specification. URL: https://tc39.es/ecma262/multipage/
[FETCH]
Anne van Kesteren. Fetch Standard. 现行标准. URL: https://fetch.spec.whatwg.org/
[FileAPI]
Marijn Kruisselbrink. File API. 2024年12月4日. 工作草案. URL: https://www.w3.org/TR/FileAPI/
[HR-TIME-3]
Yoav Weiss. High Resolution Time. 2024年11月7日. 工作草案. URL: https://www.w3.org/TR/hr-time-3/
[HTML]
Anne van Kesteren; et al. HTML Standard. 现行标准. URL: https://html.spec.whatwg.org/multipage/
[INFRA]
Anne van Kesteren; Domenic Denicola. Infra Standard. 现行标准. URL: https://infra.spec.whatwg.org/
[MIMESNIFF]
Gordon P. Hemsley. MIME Sniffing Standard. 现行标准. URL: https://mimesniff.spec.whatwg.org/
[NAVIGATION-TIMING-2]
Yoav Weiss; Noam Rosenthal. Navigation Timing Level 2. 2025年2月13日. 工作草案. URL: https://www.w3.org/TR/navigation-timing-2/
[PAGE-LIFECYCLE]
Page Lifecycle. 社区组报告草案. URL: https://wicg.github.io/page-lifecycle/
[PAGE-VISIBILITY]
Jatinder Mann; Arvind Jain. Page Visibility (Second Edition). 2013年10月29日. 推荐标准. URL: https://www.w3.org/TR/page-visibility/
[RFC2119]
S. Bradner. Key words for use in RFCs to Indicate Requirement Levels. 1997年3月. 最佳实践. URL: https://datatracker.ietf.org/doc/html/rfc2119
[RFC5234]
D. Crocker, Ed.; P. Overell. Augmented BNF for Syntax Specifications: ABNF. 2008年1月. 互联网标准. URL: https://www.rfc-editor.org/rfc/rfc5234
[RFC7230]
R. Fielding, Ed.; J. Reschke, Ed.. Hypertext Transfer Protocol (HTTP/1.1): Message Syntax and Routing. 2014年6月. 建议标准. URL: https://httpwg.org/specs/rfc7230.html
[RFC7231]
R. Fielding, Ed.; J. Reschke, Ed.. Hypertext Transfer Protocol (HTTP/1.1): Semantics and Content. 2014年6月. 建议标准. URL: https://httpwg.org/specs/rfc7231.html
[SCREEN-CAPTURE]
Jan-Ivar Bruaroey; Elad Alon. Screen Capture. 2025年2月13日. 工作草案. URL: https://www.w3.org/TR/screen-capture/
[SECURE-CONTEXTS]
Mike West. Secure Contexts. 2023年11月10日. 候选推荐草案. URL: https://www.w3.org/TR/secure-contexts/
[STORAGE]
Anne van Kesteren. Storage Standard. 现行标准. URL: https://storage.spec.whatwg.org/
[STREAMS]
Adam Rice; et al. Streams Standard. 现行标准. URL: https://streams.spec.whatwg.org/
[TRUSTED-TYPES]
Krzysztof Kotowicz. Trusted Types. 2025年1月10日. 工作草案. URL: https://www.w3.org/TR/trusted-types/
[URL]
Anne van Kesteren. URL Standard. 现行标准. URL: https://url.spec.whatwg.org/
[URLPATTERN]
Ben Kelly; Jeremy Roman; 宍戸俊哉 (Shunya Shishido). URL Pattern Standard. 现行标准. URL: https://urlpattern.spec.whatwg.org/
[WEBIDL]
Edgar Chen; Timothy Gu. Web IDL Standard. 现行标准. URL: https://webidl.spec.whatwg.org/

信息性参考文献

[UNSANCTIONED-TRACKING]
Unsanctioned Web Tracking. 2015年7月17日. W3C TAG 调查结果. URL: https://www.w3.org/2001/tag/doc/unsanctioned-tracking/

IDL 索引

[SecureContext, Exposed=(Window,Worker)]
interface ServiceWorker : EventTarget {
  readonly attribute USVString scriptURL;
  readonly attribute ServiceWorkerState state;
  undefined postMessage(any message, sequence<object> transfer);
  undefined postMessage(any message, optional StructuredSerializeOptions options = {});

  // event
  attribute EventHandler onstatechange;
};
ServiceWorker includes AbstractWorker;

enum ServiceWorkerState {
  "parsed",
  "installing",
  "installed",
  "activating",
  "activated",
  "redundant"
};

[SecureContext, Exposed=(Window,Worker)]
interface ServiceWorkerRegistration : EventTarget {
  readonly attribute ServiceWorker? installing;
  readonly attribute ServiceWorker? waiting;
  readonly attribute ServiceWorker? active;
  [SameObject] readonly attribute NavigationPreloadManager navigationPreload;

  readonly attribute USVString scope;
  readonly attribute ServiceWorkerUpdateViaCache updateViaCache;

  [NewObject] Promise<undefined> update();
  [NewObject] Promise<boolean> unregister();

  // event
  attribute EventHandler onupdatefound;
};

enum ServiceWorkerUpdateViaCache {
  "imports",
  "all",
  "none"
};

partial interface Navigator {
  [SecureContext, SameObject] readonly attribute ServiceWorkerContainer serviceWorker;
};

partial interface WorkerNavigator {
  [SecureContext, SameObject] readonly attribute ServiceWorkerContainer serviceWorker;
};

[SecureContext, Exposed=(Window,Worker)]
interface ServiceWorkerContainer : EventTarget {
  readonly attribute ServiceWorker? controller;
  readonly attribute Promise<ServiceWorkerRegistration> ready;

  [NewObject] Promise<ServiceWorkerRegistration> register((TrustedScriptURL or USVString) scriptURL, optional RegistrationOptions options = {});

  [NewObject] Promise<(ServiceWorkerRegistration or undefined)> getRegistration(optional USVString clientURL = "");
  [NewObject] Promise<FrozenArray<ServiceWorkerRegistration>> getRegistrations();

  undefined startMessages();


  // events
  attribute EventHandler oncontrollerchange;
  attribute EventHandler onmessage; // event.source of message events is ServiceWorker object
  attribute EventHandler onmessageerror;
};

dictionary RegistrationOptions {
  USVString scope;
  WorkerType type = "classic";
  ServiceWorkerUpdateViaCache updateViaCache = "imports";
};

[SecureContext, Exposed=(Window,Worker)]
interface NavigationPreloadManager {
  Promise<undefined> enable();
  Promise<undefined> disable();
  Promise<undefined> setHeaderValue(ByteString value);
  Promise<NavigationPreloadState> getState();
};

dictionary NavigationPreloadState {
  boolean enabled = false;
  ByteString headerValue;
};

[Global=(Worker,ServiceWorker), Exposed=ServiceWorker, SecureContext]
interface ServiceWorkerGlobalScope : WorkerGlobalScope {
  [SameObject] readonly attribute Clients clients;
  [SameObject] readonly attribute ServiceWorkerRegistration registration;
  [SameObject] readonly attribute ServiceWorker serviceWorker;

  [NewObject] Promise<undefined> skipWaiting();

  attribute EventHandler oninstall;
  attribute EventHandler onactivate;
  attribute EventHandler onfetch;

  attribute EventHandler onmessage;
  attribute EventHandler onmessageerror;
};

[Exposed=ServiceWorker]
interface Client {
  readonly attribute USVString url;
  readonly attribute FrameType frameType;
  readonly attribute DOMString id;
  readonly attribute ClientType type;
  undefined postMessage(any message, sequence<object> transfer);
  undefined postMessage(any message, optional StructuredSerializeOptions options = {});
};

[Exposed=ServiceWorker]
interface WindowClient : Client {
  readonly attribute VisibilityState visibilityState;
  readonly attribute boolean focused;
  [SameObject] readonly attribute FrozenArray<USVString> ancestorOrigins;
  [NewObject] Promise<WindowClient> focus();
  [NewObject] Promise<WindowClient?> navigate(USVString url);
};

enum FrameType {
  "auxiliary",
  "top-level",
  "nested",
  "none"
};

[Exposed=ServiceWorker]
interface Clients {
  // The objects returned will be new instances every time
  [NewObject] Promise<(Client or undefined)> get(DOMString id);
  [NewObject] Promise<FrozenArray<Client>> matchAll(optional ClientQueryOptions options = {});
  [NewObject] Promise<WindowClient?> openWindow(USVString url);
  [NewObject] Promise<undefined> claim();
};

dictionary ClientQueryOptions {
  boolean includeUncontrolled = false;
  ClientType type = "window";
};

enum ClientType {
  "window",
  "worker",
  "sharedworker",
  "all"
};

[Exposed=ServiceWorker]
interface ExtendableEvent : Event {
  constructor(DOMString type, optional ExtendableEventInit eventInitDict = {});
  undefined waitUntil(Promise<any> f);
};

dictionary ExtendableEventInit : EventInit {
  // Defined for the forward compatibility across the derived events
};

[Exposed=ServiceWorker]
interface InstallEvent : ExtendableEvent {
  constructor(DOMString type, optional ExtendableEventInit eventInitDict = {});
  Promise<undefined> addRoutes((RouterRule or sequence<RouterRule>) rules);
};

dictionary RouterRule {
  required RouterCondition condition;
  required RouterSource source;
};

dictionary RouterCondition {
  URLPatternCompatible urlPattern;
  ByteString requestMethod;
  RequestMode requestMode;
  RequestDestination requestDestination;
  RunningStatus runningStatus;

  sequence<RouterCondition> _or;
  RouterCondition not;
};

typedef (RouterSourceDict or RouterSourceEnum) RouterSource;

dictionary RouterSourceDict {
  DOMString cacheName;
};

enum RunningStatus { "running", "not-running" };
enum RouterSourceEnum {
  "cache",
  "fetch-event",
  "network",
  "race-network-and-fetch-handler"
};

[Exposed=ServiceWorker]
interface FetchEvent : ExtendableEvent {
  constructor(DOMString type, FetchEventInit eventInitDict);
  [SameObject] readonly attribute Request request;
  readonly attribute Promise<any> preloadResponse;
  readonly attribute DOMString clientId;
  readonly attribute DOMString resultingClientId;
  readonly attribute DOMString replacesClientId;
  readonly attribute Promise<undefined> handled;

  undefined respondWith(Promise<Response> r);
};

dictionary FetchEventInit : ExtendableEventInit {
  required Request request;
  Promise<any> preloadResponse;
  DOMString clientId = "";
  DOMString resultingClientId = "";
  DOMString replacesClientId = "";
  Promise<undefined> handled;
};

[Exposed=ServiceWorker]
interface ExtendableMessageEvent : ExtendableEvent {
  constructor(DOMString type, optional ExtendableMessageEventInit eventInitDict = {});
  readonly attribute any data;
  readonly attribute USVString origin;
  readonly attribute DOMString lastEventId;
  [SameObject] readonly attribute (Client or ServiceWorker or MessagePort)? source;
  readonly attribute FrozenArray<MessagePort> ports;
};

dictionary ExtendableMessageEventInit : ExtendableEventInit {
  any data = null;
  USVString origin = "";
  DOMString lastEventId = "";
  (Client or ServiceWorker or MessagePort)? source = null;
  sequence<MessagePort> ports = [];
};

partial interface mixin WindowOrWorkerGlobalScope {
  [SecureContext, SameObject] readonly attribute CacheStorage caches;
};

[SecureContext, Exposed=(Window,Worker)]
interface Cache {
  [NewObject] Promise<(Response or undefined)> match(RequestInfo request, optional CacheQueryOptions options = {});
  [NewObject] Promise<FrozenArray<Response>> matchAll(optional RequestInfo request, optional CacheQueryOptions options = {});
  [NewObject] Promise<undefined> add(RequestInfo request);
  [NewObject] Promise<undefined> addAll(sequence<RequestInfo> requests);
  [NewObject] Promise<undefined> put(RequestInfo request, Response response);
  [NewObject] Promise<boolean> delete(RequestInfo request, optional CacheQueryOptions options = {});
  [NewObject] Promise<FrozenArray<Request>> keys(optional RequestInfo request, optional CacheQueryOptions options = {});
};

dictionary CacheQueryOptions {
  boolean ignoreSearch = false;
  boolean ignoreMethod = false;
  boolean ignoreVary = false;
};

[SecureContext, Exposed=(Window,Worker)]
interface CacheStorage {
  [NewObject] Promise<(Response or undefined)> match(RequestInfo request, optional MultiCacheQueryOptions options = {});
  [NewObject] Promise<boolean> has(DOMString cacheName);
  [NewObject] Promise<Cache> open(DOMString cacheName);
  [NewObject] Promise<boolean> delete(DOMString cacheName);
  [NewObject] Promise<sequence<DOMString>> keys();
};

dictionary MultiCacheQueryOptions : CacheQueryOptions {
  DOMString cacheName;
};

问题索引

本节中的行为尚未完全指定,将在 HTML Standard 中指定。该工作由 issuepull request 跟踪。
使用待创建的 environment settings object 而不是具体的 environment settings object。这是由于服务工作线程与其它 web workers 的处理模型相比,其处理模型具有独特性。HTML 标准中最初为其它 web workers 设计的脚本获取算法需要执行环境的 environment settings object,但是服务工作线程在脚本稍后通过 Run Service Worker 算法多次运行之前,在 Update 算法中单独获取脚本。
HTML 中的 fetch a classic worker script 算法和 fetch a module worker script graph 算法将 jobclient 作为参数。当从 Soft Update 算法传递时,jobclient 为 null。
MDN

Cache/add

In all current engines.

Firefox41+Safari11.1+Chrome44+
Opera?Edge79+
Edge (Legacy)16+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet4.0+Opera Mobile?
MDN

Cache/addAll

In all current engines.

Firefox41+Safari11.1+Chrome46+
Opera?Edge79+
Edge (Legacy)16+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

Cache/delete

In all current engines.

Firefox41+Safari11.1+Chrome43+
Opera?Edge79+
Edge (Legacy)16+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

Cache/keys

In all current engines.

Firefox41+Safari11.1+Chrome43+
Opera?Edge79+
Edge (Legacy)16+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

Cache/match

In all current engines.

Firefox41+Safari11.1+Chrome43+
Opera?Edge79+
Edge (Legacy)16+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

Cache/matchAll

In all current engines.

Firefox41+Safari11.1+Chrome47+
Opera34+Edge79+
Edge (Legacy)16+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

Cache/put

In all current engines.

Firefox41+Safari11.1+Chrome43+
Opera?Edge79+
Edge (Legacy)16+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet4.0+Opera Mobile?
MDN

Cache

In all current engines.

Firefox41+Safari11.1+Chrome40+
Opera?Edge79+
Edge (Legacy)16+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet4.0+Opera Mobile?
MDN

CacheStorage/delete

In all current engines.

Firefox41+Safari11.1+Chrome40+
Opera?Edge79+
Edge (Legacy)16+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

CacheStorage/has

In all current engines.

Firefox41+Safari11.1+Chrome40+
Opera?Edge79+
Edge (Legacy)16+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

CacheStorage/keys

In all current engines.

Firefox41+Safari11.1+Chrome40+
Opera?Edge79+
Edge (Legacy)16+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

CacheStorage/match

In all current engines.

Firefox41+Safari11.1+Chrome54+
Opera?Edge79+
Edge (Legacy)16+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

CacheStorage/open

In all current engines.

Firefox41+Safari11.1+Chrome40+
Opera?Edge79+
Edge (Legacy)16+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

CacheStorage

In all current engines.

Firefox41+Safari11.1+Chrome43+
Opera?Edge79+
Edge (Legacy)16+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

Client/frameType

In all current engines.

Firefox44+Safari11.1+Chrome43+
Opera?Edge79+
Edge (Legacy)17+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

Client/id

In all current engines.

Firefox44+Safari11.1+Chrome40+
Opera?Edge79+
Edge (Legacy)17+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

Client/postMessage

In all current engines.

Firefox44+Safari11.1+Chrome40+
Opera?Edge79+
Edge (Legacy)17+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

Client/type

In all current engines.

Firefox54+Safari11.1+Chrome60+
Opera?Edge79+
Edge (Legacy)17+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

Client/url

In all current engines.

Firefox44+Safari11.1+Chrome40+
Opera?Edge79+
Edge (Legacy)17+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

Client

In all current engines.

Firefox44+Safari11.1+Chrome40+
Opera?Edge79+
Edge (Legacy)17+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

Clients/claim

In all current engines.

Firefox44+Safari11.1+Chrome42+
Opera?Edge79+
Edge (Legacy)17+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

Clients/get

In all current engines.

Firefox45+Safari11.1+Chrome51+
Opera?Edge79+
Edge (Legacy)17+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

Clients/matchAll

In all current engines.

Firefox54+Safari11.1+Chrome42+
Opera29+Edge79+
Edge (Legacy)17+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile29+
MDN

Clients/openWindow

In all current engines.

Firefox44+Safari11.1+Chrome40+
Opera38+Edge79+
Edge (Legacy)17+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile41+
MDN

Clients

In all current engines.

Firefox44+Safari11.1+Chrome40+
Opera?Edge79+
Edge (Legacy)17+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

ExtendableEvent/ExtendableEvent

In all current engines.

Firefox44+Safari11.1+Chrome41+
Opera24+Edge79+
Edge (Legacy)17+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile24+
MDN

ExtendableEvent/waitUntil

In all current engines.

Firefox44+Safari11.1+Chrome40+
Opera24+Edge79+
Edge (Legacy)17+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile24+
MDN

ExtendableEvent

In all current engines.

Firefox44+Safari11.1+Chrome40+
Opera24+Edge79+
Edge (Legacy)17+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile24+
MDN

ExtendableMessageEvent/ExtendableMessageEvent

In all current engines.

Firefox44+Safari11.1+Chrome51+
Opera?Edge79+
Edge (Legacy)17+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

ExtendableMessageEvent/data

In all current engines.

Firefox44+Safari11.1+Chrome51+
Opera?Edge79+
Edge (Legacy)17+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

ExtendableMessageEvent/lastEventId

In all current engines.

Firefox44+Safari11.1+Chrome51+
Opera?Edge79+
Edge (Legacy)17+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

ExtendableMessageEvent/origin

In all current engines.

Firefox44+Safari11.1+Chrome51+
Opera?Edge79+
Edge (Legacy)17+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

ExtendableMessageEvent/ports

In all current engines.

Firefox44+Safari11.1+Chrome51+
Opera?Edge79+
Edge (Legacy)17+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

ExtendableMessageEvent/source

In all current engines.

Firefox44+Safari11.1+Chrome51+
Opera?Edge79+
Edge (Legacy)17+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

ExtendableMessageEvent

In all current engines.

Firefox44+Safari11.1+Chrome51+
Opera?Edge79+
Edge (Legacy)17+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

FetchEvent/FetchEvent

In all current engines.

Firefox44+Safari11.1+Chrome44+
Opera?Edge79+
Edge (Legacy)17+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

FetchEvent/clientId

In all current engines.

Firefox45+Safari11.1+Chrome49+
Opera?Edge79+
Edge (Legacy)17+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

FetchEvent/handled

In all current engines.

Firefox84+Safari16+Chrome86+
Opera?Edge86+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

FetchEvent/preloadResponse

In all current engines.

Firefox99+Safari15.4+Chrome59+
Opera?Edge79+
Edge (Legacy)18IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

FetchEvent/replacesClientId

In no current engines.

FirefoxNoneSafariNoneChromeNone
Opera?EdgeNone
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

FetchEvent/request

In all current engines.

Firefox44+Safari11.1+Chrome40+
Opera?Edge79+
Edge (Legacy)17+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

FetchEvent/respondWith

In all current engines.

Firefox44+Safari11.1+Chrome42+
Opera?Edge79+
Edge (Legacy)17+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

FetchEvent/resultingClientId

In all current engines.

Firefox65+Safari16+Chrome72+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile50+
MDN

FetchEvent

In all current engines.

Firefox44+Safari11.1+Chrome40+
Opera?Edge79+
Edge (Legacy)17+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

NavigationPreloadManager/disable

In all current engines.

Firefox99+Safari15.4+Chrome59+
Opera?Edge79+
Edge (Legacy)18IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

NavigationPreloadManager/enable

In all current engines.

Firefox99+Safari15.4+Chrome59+
Opera?Edge79+
Edge (Legacy)18IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

NavigationPreloadManager/getState

In all current engines.

Firefox99+Safari15.4+Chrome59+
Opera?Edge79+
Edge (Legacy)18IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

NavigationPreloadManager/setHeaderValue

In all current engines.

Firefox99+Safari15.4+Chrome59+
Opera?Edge79+
Edge (Legacy)18IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

NavigationPreloadManager

In all current engines.

Firefox99+Safari15.4+Chrome59+
Opera?Edge79+
Edge (Legacy)18IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

Navigator/serviceWorker

In all current engines.

Firefox44+Safari11.1+Chrome40+
Opera?Edge79+
Edge (Legacy)17+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

ServiceWorker/postMessage

In all current engines.

Firefox44+Safari11.1+Chrome40+
Opera?Edge79+
Edge (Legacy)17+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

ServiceWorker/postMessage

In all current engines.

Firefox44+Safari11.1+Chrome40+
Opera?Edge79+
Edge (Legacy)17+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

ServiceWorker/scriptURL

In all current engines.

Firefox44+Safari11.1+Chrome40+
Opera?Edge79+
Edge (Legacy)17+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

ServiceWorker/state

In all current engines.

Firefox44+Safari11.1+Chrome40+
Opera?Edge79+
Edge (Legacy)17+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

ServiceWorker/statechange_event

In all current engines.

Firefox44+Safari11.1+Chrome40+
Opera?Edge79+
Edge (Legacy)17+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

ServiceWorker

In all current engines.

Firefox44+Safari11.1+Chrome40+
Opera?Edge79+
Edge (Legacy)17+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

ServiceWorkerContainer/controller

In all current engines.

Firefox44+Safari11.1+Chrome40+
Opera?Edge79+
Edge (Legacy)17+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

ServiceWorkerContainer/controllerchange_event

In all current engines.

Firefox44+Safari11.1+Chrome40+
Opera?Edge79+
Edge (Legacy)17+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

ServiceWorkerContainer/getRegistration

In all current engines.

Firefox44+Safari11.1+Chrome40+
Opera?Edge79+
Edge (Legacy)17+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

ServiceWorkerContainer/getRegistrations

In all current engines.

Firefox44+Safari11.1+Chrome45+
Opera27+Edge79+
Edge (Legacy)17+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView40+Samsung Internet4.0+Opera Mobile27+
MDN

ServiceWorkerContainer/message_event

In all current engines.

Firefox44+Safari11.1+Chrome40+
Opera?Edge79+
Edge (Legacy)17+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

ServiceWorkerContainer/ready

In all current engines.

Firefox44+Safari11.1+Chrome40+
Opera?Edge79+
Edge (Legacy)17+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

ServiceWorkerContainer/register

In all current engines.

Firefox44+Safari11.1+Chrome40+
Opera?Edge79+
Edge (Legacy)17+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

ServiceWorkerContainer/startMessages

In all current engines.

Firefox64+Safari11.1+Chrome74+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile50+
MDN

ServiceWorkerContainer

In all current engines.

Firefox44+Safari11.1+Chrome40+
Opera?Edge79+
Edge (Legacy)17+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

ServiceWorkerGlobalScope/activate_event

In all current engines.

Firefox44+Safari11.1+Chrome40+
Opera24+Edge79+
Edge (Legacy)17+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile24+
MDN

ServiceWorkerGlobalScope/activate_event

In all current engines.

Firefox44+Safari11.1+Chrome40+
Opera24+Edge79+
Edge (Legacy)17+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile24+
MDN

ServiceWorkerGlobalScope/clients

In all current engines.

Firefox44+Safari11.1+Chrome40+
Opera24+Edge79+
Edge (Legacy)17+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile24+
MDN

ServiceWorkerGlobalScope/fetch_event

In all current engines.

Firefox44+Safari11.1+Chrome40+
Opera24+Edge79+
Edge (Legacy)17+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile24+
MDN

ServiceWorkerGlobalScope/install_event

In all current engines.

Firefox44+Safari11.1+Chrome40+
Opera24+Edge79+
Edge (Legacy)17+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile24+
MDN

ServiceWorkerGlobalScope/message_event

In all current engines.

Firefox44+Safari11.1+Chrome40+
Opera24+Edge79+
Edge (Legacy)17+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile24+
MDN

ServiceWorkerGlobalScope/message_event

In all current engines.

Firefox44+Safari11.1+Chrome40+
Opera24+Edge79+
Edge (Legacy)17+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile24+
MDN

ServiceWorkerGlobalScope/registration

In all current engines.

Firefox44+Safari11.1+Chrome42+
Opera26+Edge79+
Edge (Legacy)17+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile26+
MDN

ServiceWorkerGlobalScope/skipWaiting

In all current engines.

Firefox44+Safari11.1+Chrome41+
Opera25+Edge79+
Edge (Legacy)17+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile25+
MDN

ServiceWorkerGlobalScope

In all current engines.

Firefox44+Safari11.1+Chrome40+
Opera24+Edge79+
Edge (Legacy)17+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile24+
MDN

ServiceWorkerRegistration/active

In all current engines.

Firefox44+Safari11.1+Chrome40+
Opera?Edge79+
Edge (Legacy)17+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

ServiceWorkerRegistration/installing

In all current engines.

Firefox44+Safari11.1+Chrome40+
Opera?Edge79+
Edge (Legacy)17+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

ServiceWorkerRegistration/navigationPreload

In all current engines.

Firefox99+Safari15.4+Chrome59+
Opera?Edge79+
Edge (Legacy)17+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet4.0+Opera Mobile?
MDN

ServiceWorkerRegistration/scope

In all current engines.

Firefox44+Safari11.1+Chrome40+
Opera?Edge79+
Edge (Legacy)17+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

ServiceWorkerRegistration/unregister

In all current engines.

Firefox44+Safari11.1+Chrome40+
Opera?Edge79+
Edge (Legacy)17+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

ServiceWorkerRegistration/update

In all current engines.

Firefox44+Safari11.1+Chrome45+
Opera?Edge79+
Edge (Legacy)17+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet4.0+Opera Mobile?
MDN

ServiceWorkerRegistration/updatefound_event

In all current engines.

Firefox44+Safari11.1+Chrome40+
Opera?Edge79+
Edge (Legacy)17+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

ServiceWorkerRegistration/updateViaCache

In all current engines.

Firefox57+Safari11.1+Chrome68+
Opera?Edge79+
Edge (Legacy)18IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

ServiceWorkerRegistration/waiting

In all current engines.

Firefox44+Safari11.1+Chrome40+
Opera?Edge79+
Edge (Legacy)17+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

ServiceWorkerRegistration

In all current engines.

Firefox44+Safari11.1+Chrome40+
Opera?Edge79+
Edge (Legacy)17+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

WindowClient/focus

In all current engines.

Firefox44+Safari11.1+Chrome42+
Opera?Edge79+
Edge (Legacy)17+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

WindowClient/focused

In all current engines.

Firefox44+Safari11.1+Chrome42+
Opera?Edge79+
Edge (Legacy)17+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

WindowClient/navigate

In all current engines.

Firefox50+Safari16+Chrome49+
Opera?Edge79+
Edge (Legacy)17+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView42+Samsung Internet4.0+Opera Mobile?
MDN

WindowClient/visibilityState

In all current engines.

Firefox44+Safari11.1+Chrome42+
Opera?Edge79+
Edge (Legacy)17+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

WindowClient

In all current engines.

Firefox44+Safari11.1+Chrome42+
Opera?Edge79+
Edge (Legacy)17+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

caches

In all current engines.

Firefox41+Safari11.1+Chrome40+
Opera?Edge79+
Edge (Legacy)16+IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

Headers/Service-Worker-Navigation-Preload

In all current engines.

Firefoxpreview+Safari15.4+Chrome59+
Opera?Edge79+
Edge (Legacy)18IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?