Service Workers 每日版

W3C 候选推荐标准草案

关于本文档的更多详细信息
本版本:
https://www.w3.org/TR/2025/CRD-service-workers-20251212/
最新发布版本:
https://www.w3.org/TR/service-workers/
编辑草案:
https://w3c.github.io/ServiceWorker/
以往版本:
历史记录:
https://www.w3.org/standards/history/service-workers/
实现报告:
https://wpt.fyi/results/service-workers
测试套件:
https://github.com/web-platform-tests/wpt/tree/master/service-workers
反馈:
GitHub
规范内反馈
编辑者:
(微软)
(谷歌)
前任编辑者:
(谷歌)
(谷歌)
(微软,代表三星至2018年4月)
(谷歌)

摘要

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

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

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

本文档状态

本节描述了本文件在发布时的状态。当前W3C出版物列表以及本技术报告的最新修订版,可在W3C标准与草案索引中找到。

本文由Web应用工作组推荐路径候选推荐草案方式发布。 以候选推荐草案形式发布并不意味着得到了W3C及其成员的认可。 候选推荐草案整合了工作组打算纳入下一个候选推荐快照的前一候选推荐版本中的更改。

本文件为草案,可能随时被更新、替换或废弃。 因此不应将本文件作为除“工作进行中”之外的引用。

本文件为动态文档。需要注意的是,规范中可能包含尚未实现的功能,以及未来可能更改的细节。Service Workers 1是正在推进成为W3C推荐标准的版本。

本文件由受W3C专利政策约束的小组制作。W3C维护着与该小组交付成果相关的任何专利披露的公共列表;该页面也包括披露专利的说明。知晓专利且确信其包含必要权利要求的个人,必须依照W3C专利政策第6节的规定进行披露。

本文件受2025年8月18日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应用缓存的开发者也反馈称该设计的若干属性会导致无法恢复的错误Service worker的一个关键设计原则是,错误应当始终可被恢复。Service workers的更新流程许多细节,都是为了避免这些风险而设计的。

Service workers的启动与存活依赖于其与事件的关系,而非文档。这种设计大量借鉴了开发者和厂商在Shared WorkerChrome后台页面方面的经验。来自这些系统的一个重要教训是,必须对后台处理上下文的执行时间进行限制,既为了节省资源,也为了确保开发者始终牢记后台上下文的丢失与重启。因此,service workersChrome事件页面(后台页面的继任者)有诸多相似之处。Service workers可以被用户代理在没有附加文档的情况下启动,并且几乎可在任何时候被用户代理终止。从概念上看,service workers可被视为无需处理来自文档消息即可启动、处理事件并终止的Shared Worker。建议开发者时刻留意,service workers可能在一秒内被多次启动与终止。

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

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 会标记某些时刻,这些时刻随后会通过 navigation timing API 和 resource timing API 对外暴露。

service worker 时序信息是一个结构体。它包含以下条目

开始时间

一个 DOMHighResTimeStamp,初始值为 0。

fetch 事件派发时间

一个 DOMHighResTimeStamp,初始值为 0。

worker 路由器评估开始

一个 DOMHighResTimeStamp,初始值为 0。

worker 缓存查找开始

一个 DOMHighResTimeStamp,初始值为 0。

worker 匹配的路由器源码

一个 DOMString,初始值为空字符串。

worker 最终路由器源码

一个 DOMString,初始值为空字符串。

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<ServiceWorkerRegistration> 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();


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

用户代理在创建 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 为调用 获取受信任类型兼容字符串 时的结果,参数为 TrustedScriptURLthis相关全局对象scriptURL、"ServiceWorkerContainer register" 以及 "script"。

  3. clientthisservice worker client

  4. scriptURL 为用 this相关设置对象API 基础 URL解析 scriptURL 的结果。

  5. scopeURL 为 null。

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

  7. scopeURLscriptURLpclientclient创建 URLoptions["type"] 及 options["updateViaCache"] 调用 Start Register

  8. 返回 p

getRegistration(clientURL) 方法的步骤是:

  1. clientthisservice worker 客户端

  2. storage key 为对 client 执行 获取存储键 的结果。

  3. clientURL 为用 this相关设置对象API 基础 URL 解析 clientURL 的结果。

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

  5. clientURLfragment(片段) 设为 null。

  6. 如果 clientURLorigin(源) 不等于 clientorigin(源),则返回一个以 "SecurityError" DOMException 拒绝的 promise

  7. promise 为一个新的 promise

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

    1. registration 为以 storage keyclientURL 作为输入,运行 匹配 Service Worker 注册 的结果。

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

    3. 用在 promise相关设置对象获取代表 registration 的 service worker 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. visibilityStatethis浏览上下文活动文档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活动文档 执行 has focus 步骤 的结果。

    8. ancestorOriginsListbrowsingContext活动文档相关全局对象Location 对象的 ancestor origins 列表的关联列表。

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

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

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

      3. windowClient 解析 promise

  8. 返回 promise

4.3. Clients 接口

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

用户代理在创建 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活动文档 作为参数,运行 has focus 步骤 的结果。

      6. ancestorOriginsListnewContext活动文档相关全局对象Location 对象的 ancestor origins 列表的关联列表。

      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 {
  // Defined for the forward compatibility across the derived events
};

一个 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 生命周期 service worker所属 service worker 注册installing worker 发生变化时。(见 Install 算法第 11.2 步)
activate ExtendableEvent 生命周期 service worker所属 service worker 注册active worker 发生变化时。(见 Activate 算法第 12.2 步)
fetch FetchEvent 功能性 http fetch 调用 Handle Fetch 并传入 request 时触发。经过 Handle Fetch 后,service worker 会向 http fetch 返回一个 response。该 response 可表示为 Response 对象,可以从 Cache 对象中获取,也可以通过 self.fetch(input, init) 方法直接从网络获取。(也可以返回自定义 Response 对象。)
push PushEvent 功能性 (参见 触发 push event
notificationclick NotificationEvent 功能性 (参见 激活通知
notificationclose NotificationEvent 功能性 (参见 关闭通知
sync SyncEvent 功能性 (参见 触发 sync event
canmakepayment CanMakePaymentEvent 功能性 (参见 处理 CanMakePaymentEvent
paymentrequest PaymentRequestEvent 功能性 (参见 处理 PaymentRequestEvent
message ExtendableMessageEvent 旧版 接收到消息时。
messageerror MessageEvent 旧版 当收到无法反序列化的消息时。

5. 缓存

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

5.1. 构造

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

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

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

相关名称到缓存映射 对于一个 CacheStorage 对象,是与对该对象的 获取本地存储瓶映射, 参数为该对象的 相关设置对象 和 "caches",所返回结果相关联的 名称到缓存映射

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. 如果 requestRequest 对象,则将 innerRequest 设为 requestrequest

  3. 否则:

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

    2. innerRequest 设为 requestObjrequest

  4. 如果 innerRequesturlscheme 不是 "http" 或 "https" 之一,或 innerRequestmethod 不是 `GET`,则返回被拒绝的 promise,原因为 TypeError

  5. innerResponseresponseresponse

  6. 如果 innerResponsestatus206,则返回被拒绝的 promise,原因为 TypeError

  7. 如果 innerResponseheader list 包含一个 header 名为 `Vary`,则:

    1. fieldValues列表,其包含 ,这些项对应 Vary 头的 字段值

    2. 对于 fieldValue 属于 fieldValues 的每一项:

      1. 如果 fieldValue 匹配 "*",则返回被拒绝的 promise,原因为 TypeError

  8. 如果 innerResponsebody被扰动锁定,则返回被拒绝的 promise,原因为 TypeError

  9. clonedResponseinnerResponse 的克隆

  10. bodyReadPromise以 undefined 解决的 promise

  11. 如果 innerResponsebody 非空,执行以下子步骤:

    1. streaminnerResponsebody

    2. reader获取 stream 的 reader 的结果。

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

    注意:此操作确保 innerResponsebody锁定,且我们在 clonedResponse 中已有全部缓冲的副本。实现上可优化为直接流式写入磁盘而非内存。

  12. operations 为一个空的列表

  13. operation 为一个缓存批处理操作

  14. operationtype 设为 "put"。

  15. operationrequest 设为 innerRequest

  16. operationresponse 设为 clonedResponse

  17. operation添加到operations中。

  18. realmthis相关 realm

  19. 返回 bodyReadPromise 实现后的结果:

    1. cacheJobPromise一个新的 promise

    2. 返回 cacheJobPromise,并并行地执行以下步骤:

      1. errorData 为 null。

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

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

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

        2. 否则,使用 errorDatarealm新建 异常,并以此拒绝 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 社区仍在讨论异步迭代的相关方案。

当创建 CacheStorage 对象所依托的 WindowWorkerGlobalScope 时,用户代理必须创建一个 CacheStorage 对象。

一个 CacheStorage 对象代表与执行 名称到缓存映射相关联、并用其 获取本地存储瓶映射相关设置对象 及 "caches" 运行结果相关的对象。 多个在不同文档和 workers 中实现 CacheStorage 接口的独立对象,可以同时关联到同一个 名称到缓存映射

5.5.1. match(request, options) 方法

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

  1. 如果 options["cacheName"] 存在,则:

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

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

        1. 如果 options["cacheName"] 与 cacheName 匹配,则:

          1. requestoptions 作为参数,运行 match(request, options) 方法对应的算法(以 cache 作为该 match(request, options) 内部 \[[Call]] 方法的 thisArgument), 并用其结果解决 promise

          2. 终止这些步骤。

      2. 用 undefined 解决 promise

  2. 否则:

    1. promise以 undefined 解决的 promise

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

      1. promise 设为反应到自身,使用 fulfillment handler,该 handler 被调用且参数为 response 时,执行以下子步骤:

        1. 如果 response 非 undefined,返回 response

        2. requestoptions 作为参数,运行 match(request, options) 方法对应的算法(以 cache 作为该 match(request, options) 内部 \[[Call]] 方法的 thisArgument), 并返回其结果。

    3. 返回 promise

5.5.2. has(cacheName) 方法

has(cacheName) 方法的步骤是:

  1. promise一个新的 promise

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

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

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

    2. 用 false 解决 promise

  3. 返回 promise

5.5.3. open(cacheName)

open(cacheName) 方法的步骤如下:

  1. promise一个新的 promise

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

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

      1. 如果 cacheNamekey 匹配,则:

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

        2. 中止这些步骤。

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

    3. 设置 相关名称到缓存映射[cacheName] 为 cache。如果此缓存写入操作因超出授权配额限制而失败,拒绝 promise,原因为 QuotaExceededError,并中止这些步骤。

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

  3. 返回 promise

5.5.4. delete(cacheName)

delete(cacheName) 方法的步骤如下:

  1. promise 为以 cacheName 调用 has(cacheName) 方法指定算法的结果。

  2. 返回对 promise 调用反应的结果,使用 fulfillment handler,当被以参数 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获取 相关名称到缓存映射的所有 key 的结果。

      注意: 结果 有序集合中的 按它们被加入 名称到缓存映射的顺序排列。

    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 头,也可以fetch许多跨的资源。Service workers 通过允许 Caches 对跨源项目进行抓取和缓存来实现这一点。不过,存在一些限制。首先,与同源资源不同,同源资源被作为 Cache 中的 Response 对象存储,这些对象的 responsesbasic filtered response,而跨源对象被存储为 Response,其 responses 要么是 CORS 过滤响应,要么是 opaque 过滤响应。这些对象可以和 event.respondWith(r) 方法一起按与 Response 对象(其 responsesbasic filtered responses)相同的方式传递,但这些跨源对象不能通过代码有意义地创建。这些限制是为了维护平台的安全不变式。允许 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. 隐私

Service workers 引入了新的持久化存储特性,包括 registration map(注册表映射)(用于 service worker registrations 及其 service workers)、请求-响应列表名称到缓存映射(用于缓存)、以及脚本资源映射(用于脚本资源)。为保护用户免受可能的未授权跟踪威胁,这些持久化存储应当在用户有意清理时被清除,并应当与现有用户控制(例如清空所有持久化存储)保持兼容与协作。

7. 可扩展性

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

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

规范可以通过对 ServiceWorkerRegistration 接口使用 partial interface 定义,将某个 API 绑定到service worker registration,在该接口上可以定义规范特定的属性和方法:

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),值是 Service Worker注册

一个 任务 是对 Service Worker注册 的注册、更新和取消注册请求之一的抽象。

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

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

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

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

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

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

一个 任务 有一个 客户端(一个 Service Worker客户端)。它最初为 null。

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

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

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

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

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

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

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

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

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

竞态结果是一个二元组,包含一个路由响应和一个使用的路由

竞态结果关联一个 路由响应(一个 响应)。

竞态结果关联一个 使用的路由(一个 RouterSourceEnum)。

创建任务

输入

jobType,一个 任务类型

storage key,一个 存储密钥

scopeURL,一个 URL

scriptURL,一个 URL

promise,一个 承诺

client,一个 Service Worker客户端

输出

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获取Service Worker注册对象的结果。

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

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

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

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

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

      1. convertedValue 为 null。

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

      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,一个 Service Worker客户端

referrer,一个 URL

workerType,一个 工作者类型

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

输出

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

  2. scriptURL片段设置为 null。

    注意:用户代理不存储脚本 URL 的片段。这意味着片段对识别Service Worker没有影响。

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

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

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

    注意:默认情况下,注册的作用域 URL 设置为Service Worker脚本的位置。

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

  7. scopeURL片段设置为 null。

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

  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”以及为此Service Worker创建的环境设置对象获取经典工作者脚本

    "module"

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

    使用即将创建的 环境设置对象,而不是具体的 环境设置对象。这是因为 service worker 拥有不同于其它 web workers 的独特处理模型。HTML 标准最初为其他 web workers 设计的脚本抓取算法需要执行环境的 环境设置对象,但 service worker 会在 更新 算法中单独抓取脚本,之后该脚本会通过 运行 Service Worker 算法多次运行。

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

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

    1. 将 `Service-Worker`/`script` 添加到 requestheader list 中。

      注意: Service-Worker 头的定义见附录 B:扩展 HTTP 头。

    2. 如果下列任一条件为真,则将 requestcache mode 设为 "no-cache":

      注意: 即使 cache mode 不是 "no-cache",用户代理仍会在网络层遵循 Cache-Control 头中的 max-age 值,判断是否绕过浏览器缓存。

    3. requestservice-workers mode 设为 "none"。

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

    5. requestredirect mode 设为 "error"。

    6. 抓取 request,并在异步等待 fetch 的 processResponse 中以 response 继续执行后续步骤。

    7. responseheader list 提取 MIME 类型。如果此 MIME 类型(忽略参数)不是JavaScript MIME 类型,则:

      1. 调用 Reject Job Promise,传入 job 及 "SecurityError" DOMException

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

    8. serviceWorkerAllowedextracting header list values 的结果,参数为 `Service-Worker-Allowed` 和 responseheader list

      注意: Service-Worker-Allowed 头的定义参见附录 B:扩展 HTTP 头。

    9. policyContainer 设为 从 fetch 响应创建策略容器 的结果,参数为 response

    10. 如果 serviceWorkerAllowed 是失败,则:

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

    11. scopeURLregistrationscope url

    12. maxScopeString 为 null。

    13. 如果 serviceWorkerAllowed 为 null,则:

      1. resolvedScope 为以 jobscript url 作为基础 URL解析 "./" 的结果。

      2. maxScopeString 设为 "/",随后添加 resolvedScopepath(包括空字符串)中的各字符串,每个之间以 "/" 分隔。

        注意: resolvedScopepath 最后一项总为一个空字符串,因此 maxScopeString 会以 "/" 结尾。

    14. 否则:

      1. maxScope 为以 jobscript url 作为基础 URL解析 serviceWorkerAllowed 的结果。

      2. 如果 maxScopeorigin 等于 jobscript urlorigin,则:

        1. maxScopeString 设为 "/",随后添加 maxScopepath(包括空字符串)中的各字符串,每个之间以 "/" 分隔。

    15. scopeString 为 "/",随后添加 scopeURLpath(包括空字符串)中的各字符串,每个之间以 "/" 分隔。

    16. 如果 maxScopeString 为 null 或 scopeString 不是以 maxScopeString 前缀开始,则:

      1. 调用 Reject Job Promise,传入 job 及 "SecurityError" DOMException

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

    17. urlrequesturl

    18. updatedResourceMap[url] 设为 response

    19. 如果 responsecache state 不是 "local",将 registrationlast update check time 设为当前时间。

    20. 如果下列任一条件为真,则将 hasUpdatedResources 设为 true:

    21. 如果 hasUpdatedResources 为 false 且 newestWorkerclassic scripts imported flag 已设置,则:

      注意: 下列操作检查是否有被 import 的脚本被更新,因为主脚本没有变更。

      1. 对于 newestWorkerscript resource map 中每一个 importUrlstoredResponse

        1. 如果 importUrlurl,则继续。

        2. importRequest 为一个新的 request,其 urlimportUrlclientjobclientdestination 为 "script", parser metadata 为 "not parser-inserted", 并且 use-URL-credentials flag 已设置。

        3. 如果下列任一条件为真,则将 importRequestcache mode 设为 "no-cache":

        4. fetchedResponse抓取 importRequest 的结果。

        5. updatedResourceMap[importRequesturl] 设为 fetchedResponse

        6. fetchedResponse 设为其 unsafe response

        7. 如果 fetchedResponsecache state 不是 "local",则将 registrationlast update check time 设为当前时间。

        8. 如果 fetchedResponsebad import script response,则继续。

          注意: 对于 importScripts() 的错误响应会在字节比对时被忽略,仅对现有 worker 和潜在新 worker 的好的响应进行比对。详细原因可参考 issue #1374

        9. 如果 fetchedResponsebodystoredResponseunsafe responsebody 不完全一致,设 hasUpdatedResources 为 true。

          注意: 无论上一步结果如何,都不终止循环,以完成所有被 import 脚本的缓存。

    22. 异步以 response 完成这些步骤。

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

  8. 如果 script 为 null,或以 scriptrecordscriptbase URL 和 « » 调用 Is Async Module 为 true,则:

    1. 调用 Reject Job Promise,传入 jobTypeError

      注意: 如果之前已调用 Reject Job Promise 并传入 "SecurityError" DOMException,则该操作不会产生效果。

    2. 如果 newestWorker 为 null,则 移除 registration map[(registrationstorage key序列化scopeURL)]。

    3. 调用 Finish Job,传入 job,并中止这些步骤。

  9. 如果 hasUpdatedResources 为 false,则:

    1. registrationupdate via cache mode 设为 jobupdate via cache mode

    2. 调用 Resolve Job Promise,传入 jobregistration

    3. 调用 Finish Job,传入 job,并中止这些步骤。

  10. worker 为一个新的 service worker

  11. workerscript url 设为 jobscript urlworkerscript resource 设为 scriptworkertype 设为 jobworker typeworkerscript resource map 设为 updatedResourceMap

  12. url 添加到 workerset of used scripts

  13. workerscript resourcepolicy container 设为 policyContainer

  14. forceBypassCache 为 true 如果 jobforce bypass cache flag 被设置,否则为 false。

  15. runResult 为以 workerforceBypassCache 作为参数运行 Run Service Worker 算法的结果。

  16. 如果 runResultfailureabrupt completion,则:

    1. 调用 Reject Job Promise,传入 jobTypeError

    2. 如果 newestWorker 为 null,则 移除 registration map[(registrationstorage key序列化scopeURL)]。

    3. 调用 Finish Job,传入 job

  17. 否则,调用 Install 算法,参数为 jobworkerregistration

软更新

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

输入

registration,一个 Service Worker注册

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

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

输出

  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,一个 Service Worker

registration,一个 Service Worker注册

输出

  1. installFailed 为 false。

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

  3. registrationupdate via cache mode 设为 jobupdate via cache mode

  4. registration、"installing" 及 worker 作为参数运行 更新注册状态算法。

  5. registrationinstalling worker 和 "installing" 作为参数运行 更新工作者状态算法。

  6. 断言:jobjob promise 非 null。

  7. 调用 Resolve Job Promise,传入 jobregistration

  8. settingsObjects 为所有 环境设置对象,其 origin 等于 registrationscope urlorigin

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

    1. registrationObjectssettingsObjectrealm 中所有 ServiceWorkerRegistration 对象,其 service worker registration 等于 registration

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

  10. installingWorkerregistrationinstalling worker

  11. 如果以 installingWorker 和 "install" 作为参数运行 Should Skip Event 算法的结果为 false,则:

    1. forceBypassCache 为 true,若 jobforce bypass cache flag 已设置,否则为 false。

    2. 如果以 installingWorkerforceBypassCache 作为参数运行 Run Service Worker 算法的结果为 failure,则:

      1. installFailed 设为 true。

    3. 否则:

      1. installingWorkerevent loop 上,使用 DOM 操作任务源,队列一个 task,用于运行以下步骤:

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

        2. 初始化 etype 属性为 install

        3. 派发 einstallingWorker全局对象

        4. WaitForAsynchronousExtensions并行运行以下子步骤:

          1. 等待 e 不再active

          2. 如果 etimed out flag 被设置,将 installFailed 设为 true。

          3. p获取所有 eextend lifetime promises 的 promise 结果。

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

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

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

      3. 等待 WaitForAsynchronousExtensions 标记的步骤完成。

  12. 如果 installFailed 为 true,则:

    1. registrationinstalling worker 和 "redundant" 作为参数运行 更新工作者状态算法。

    2. registration、"installing" 和 null 作为参数运行 更新注册状态算法。

    3. 如果 newestWorker 为 null,则 移除 registration map[(registrationstorage key序列化registrationscope url)]。

    4. 调用 Finish Job,传入 job 并中止这些步骤。

  13. mapregistrationinstalling workerscript resource map

  14. usedSetregistrationinstalling workerset of used scripts

  15. 对于 map 中的每个 url

    1. 如果 usedSet包含 url,则移除 map[url]。

  16. 如果 registrationwaiting worker 不为 null,则:

    1. 终止 registrationwaiting worker

    2. registrationwaiting worker 和 "redundant" 作为参数运行 更新工作者状态算法。

  17. registration、"waiting" 及 registrationinstalling worker 作为参数运行 更新注册状态算法。

  18. registration、"installing" 和 null 作为参数运行 更新注册状态算法。

  19. registrationwaiting worker 和 "installed" 作为参数运行 更新工作者状态算法。

  20. 调用 Finish Job,传入 job

  21. 等待所有由本算法内 Update Worker State 调用而队列任务均已执行。

  22. 调用 Try Activate,参数为 registration

    注意:如果这里 Try Activate 未触发 Activate,则当最后一个被现有active worker控制的 client 被卸载时,或异步调用 skipWaiting() 时,或者现有 extend lifetime promises 达到 settled 状态时,会再尝试 Activate

激活

输入

registration,一个 Service Worker注册

输出

  1. 如果 registration等待中工作者为 null,则中止这些步骤。

  2. 如果 registration活动工作者不为 null,则:

    1. 终止 registration活动工作者

    2. registration活动工作者和“冗余”作为参数运行更新工作者状态算法。

  3. registration、“活动”和 registration等待中工作者作为参数运行更新注册状态算法。

  4. registration、“等待中”和 null 作为参数运行更新注册状态算法。

  5. registration活动工作者和“正在激活”作为参数运行更新工作者状态算法。

    注意:一旦活动工作者正在激活,运行时脚本错误或强制终止活动工作者都不会阻止活动工作者被激活。

    注意:确保将激活处理程序设计为执行非必要工作(例如清理)。这是因为激活处理程序可能无法全部运行完成,尤其是在激活期间浏览器终止的情况下。Service Worker应设计为即使激活处理程序未全部成功完成也能正常运行。

  6. matchedClients 为一个列表,其中包含Service Worker客户端,其创建 URL 匹配 registration存储密钥registration作用域 URL

  7. 对于 matchedClients 中的每个 client, 在 client负责的事件循环上,使用DOM 操作任务源排队一个任务,以运行以下子步骤:

    1. readyPromiseclient全局对象ServiceWorkerContainer 对象的 就绪承诺

    2. 如果 readyPromise 为 null,则继续

    3. 如果 readyPromise 处于待定状态,则使用在 readyPromise相关设置对象中表示 registration获取Service Worker注册对象的结果来解决 readyPromise

  8. 对于每个Service Worker客户端 client,它正在使用 registration

    1. client活动工作者设置为 registration活动工作者

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

  9. activeWorkerregistration活动工作者

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

    1. 如果使用 activeWorker 运行运行Service Worker算法的结果不是失败,则:

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

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

        2. etype 属性初始化为 activate

        3. activeWorker全局对象分派 e

        4. 等待异步扩展并行等待,直到 e 不再 活动

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

      3. 等待标记为等待异步扩展的步骤完成。

  11. registration活动工作者和“已激活”作为参数运行更新工作者状态算法。

尝试激活

输入

registration,一个 Service Worker注册

输出

  1. 如果 registration等待中工作者为 null,则返回。

  2. 如果 registration活动工作者不为 null 且 registration活动工作者状态是“正在激活”, 则返回。

    注意:如果现有的活动工作者仍处于正在激活状态,则等待中工作者的激活将被延迟。

  3. 如果以下任一情况为真,则使用 registration 调用激活

设置 ServiceWorkerGlobalScope

输入

serviceWorker,一个 Service Worker

输出

一个 ServiceWorkerGlobalScope 对象或 null

注意:此算法返回一个可用于 CSP 检查的 ServiceWorkerGlobalScope,或者返回 null。如果 serviceWorker 有一个活动的 ServiceWorkerGlobalScope, 则返回它。否则,将新创建一个对象。

此算法为 service worker 做了最小化的初始化工作,以满足如 CSP 和 COEP 等安全检查对可用对象的需求。本规范确保在进行此类安全检查前会调用该算法。

在规范中,这类安全检查需要创建一个 ServiceWorkerGlobalScope、一个 相关设置对象、一个 realm,以及一个 agent。在实际实现中,所需的工作量可能远少于规范。 因此,实际实现中可以在此算法的等价过程中做更少的事情,将更多逻辑放在 运行 Service Worker 算法中,只要观察到的结果等价即可。(特别是,只要所有安全检查的结果一致即可。)

  1. unsafeCreationTime不安全共享当前时间

  2. 如果 serviceWorker 正在运行,则返回 serviceWorker全局对象

  3. 如果 serviceWorkerstate 为 "redundant",则返回 null。

  4. 如果 serviceWorker全局对象 不为 null,则返回 serviceWorker全局对象

  5. 断言:serviceWorker启动状态为 null。

  6. setupFailed 为 false。

  7. globalObject 为 null。

  8. agent获取 service worker agent 的结果,并在该上下文中执行以下步骤:

    1. realmExecutionContext 为以 agent 和以下定制项 创建新 realm 的结果:

    2. settingsObject 为一个新的 环境设置对象,其算法定义如下:

      realm execution context

      返回 realmExecutionContext

      module map

      返回 workerGlobalScopemodule map

      API base URL

      返回 serviceWorkerscript url

      origin

      返回其注册的 service worker clientorigin

      has cross-site ancestor

      返回其注册的 service worker clienthas cross-site ancestor

      policy container

      返回 workerGlobalScopepolicy container

      time origin

      返回以 workerGlobalScopecross-origin isolated capability 为参数,对 unsafeCreationTime 进行 模糊化 的结果。

    3. settingsObjectid 设为新的唯一不透明字符串,creation URL 设为 serviceWorkerscript urltop-level creation URL 设为 null,top-level origin 设为实现自定义值,target browsing context 设为 null,active service worker 设为 null。

    4. workerGlobalScopeurl 设为 serviceWorkerscript url

    5. workerGlobalScopepolicy container 设为 serviceWorkerscript resourcepolicy container

    6. workerGlobalScopetype 设为 serviceWorkertype

    7. 创建一个新的 WorkerLocation 对象,并将其关联到 workerGlobalScope

    8. 如果对 workerGlobalScope 执行 运行全局对象 CSP 初始化 算法返回 "Blocked",则将 setupFailed 设为 true 并中止这些步骤。

    9. globalObject 设为 workerGlobalScope

  9. 等待 globalObject 不为 null,或 setupFailed 为 true。

  10. 如果 setupFailed 为 true,则返回 null。

  11. 返回 globalObject

运行Service Worker

输入

serviceWorker,一个 Service Worker

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

输出

一个 完成记录失败

注意:此算法将阻塞,直到Service Worker正在运行或启动失败。

  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包含的Service Worker注册任务队列中有任何任务排队,则使用其原始任务源将它们以相同顺序排队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. 如果脚本被终止Service Worker算法中止,则将 startFailed 设置为 true 并中止这些步骤。

    7. serviceWorker启动状态设置为 evaluationStatus

    8. 如果 script曾经被评估标志未设置,则:

      1. 对于 settingsObject全局对象的关联事件侦听器列表的每个 eventType 事件类型:

        1. 追加 eventTypeworkerGlobalScope 的关联 Service Worker要处理的事件类型集

        注意:如果全局对象的关联事件侦听器列表此时没有任何添加的事件侦听器,则Service Worker的要处理的事件类型集仍然是一个空集。

      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 调用旧版获取Service Worker fetch 事件侦听器回调的结果。

  4. 对于 eventListenerCallbacks 中的每个 eventListenerCallback

    1. callback 为 null。

    2. 如果 eventHandler 不为 null 且 eventListenerCallback 等于 eventHandlerlistenercallback,则将 callback 设为 转换为 ECMAScript 值后的 eventHandlervalue

    3. 否则,将 callback 设为 转换为 ECMAScript 值后的 eventListenerCallback

    4. 如果 IsCallable(callback) 为 false,则返回 false。

      注意:使用 handleEvent(event)Callback 对象被视为非空。这样可以避免在此检查过程中调用 handleEvent(event) 的 getter,从而可能导致事件监听器在检查期间被修改。

    5. 如果 callback函数体非空(即存在 语句声明),则返回 false。

    注意:这将检测像 () => {} 这样的“fetch”侦听器。一些站点具有带有空主体的 fetch 事件侦听器,以使其被 Chromium 识别为渐进式 Web 应用程序 (PWA)。

  5. 返回 true。

注意:鼓励用户代理显示警告,指出空的“fetch”侦听器是不必要的,并且可能会对性能产生负面影响。

终止Service Worker

输入

serviceWorker,一个 Service Worker

输出

  1. serviceWorker 的主循环并行运行以下步骤:

    1. serviceWorkerGlobalScopeserviceWorker全局对象

    2. serviceWorkerGlobalScope关闭标志设置为 true。

    3. 移除 serviceWorker扩展事件集中的所有项目

    4. 如果在 serviceWorkerGlobalScope事件循环任务队列中排队了任何任务,其任务源处理 fetch 任务源处理功能事件任务源,则使用其原始任务源将它们以相同顺序排队serviceWorker包含的Service Worker注册的相应任务队列,并从 serviceWorkerGlobalScope事件循环任务队列中丢弃所有任务(包括任务,其任务源既不是处理 fetch 任务源也不是处理功能事件任务源)而不处理它们。

      注意:这实际上意味着 fetch 事件和其他功能事件(如推送事件)由注册的任务队列备份,而其他任务(包括消息事件)则被丢弃。

    5. 中止当前在 serviceWorker 中运行的脚本。

    6. serviceWorker启动状态设置为 null。

处理 Fetch

处理 Fetch 算法是获取处理的入口点,该处理移交给Service Worker上下文。

输入

request,一个request

fetchController,一个fetch controller

useHighResPerformanceTimers,一个布尔值

输出

一个 responseservice worker timing info

  1. registration 为 null。

  2. clientrequestclient

  3. reservedClientrequestreserved client

  4. preloadResponse 为一个新的 promise

  5. workerRealm 为 null。

  6. timingInfo 为一个新的 service worker timing info

  7. 断言:requestdestination 不为 "serviceworker"。

  8. 如果 requestdestination 是 "embed" 或 "object",则:

    1. 返回 null。

  9. 否则如果 request非子资源请求,则:

    1. 如果 reservedClient 不为 null 且是environment settings object,则:

      1. 如果 reservedClient 不是安全上下文,返回 null。

    2. 否则:

      1. 如果 requesturl 不是potentially trustworthy URL,返回 null。

    3. 如果 request导航请求,且发起该导航的为 shift+reload 或等效操作,返回 null。

    4. 断言 reservedClient 不为 null。

    5. storage key 为以 reservedClient 运行 obtain a storage key 的结果。

    6. registration 设为以 storage keyrequesturl 运行 Match Service Worker Registration 的结果。

    7. 如果 registration 为 null 或 registrationactive worker 为 null,返回 null。

    8. 如果 requestdestination 不是 "report",则将 reservedClientactive service worker 设为 registrationactive worker

    注意:从此处起,service worker client 开始使用active service workercontaining service worker registration

  10. 否则如果 request子资源请求,则:

    1. 如果 clientactive service worker 不为 null,则将 registration 设为 clientactive service workercontaining service worker registration

    2. 否则,返回 null。

  11. activeWorkerregistrationactive worker

  12. shouldSoftUpdate 为 true,若下述任一条件为真,否则为 false:

  13. 如果 activeWorker路由规则列表 非空

    1. timingInfoworker router evaluation start 设为以 useHighResPerformanceTimers 为参数的粗化共享当前时间

    2. source 为以 registrationactive workerrequest 运行 Get Router Source 算法的结果。

    3. 如果 source 非 null,则:

      1. timingInfoworker matched router source 设为 sourceworker final router source 设为 "network"

      2. 如果 source"network"

        1. 如果 shouldSoftUpdate 为 true,则并行运行 Soft Update 算法,参数为registration

        2. 返回 timingInfo

      3. 否则如果 source"cache", 或 source["cacheName"] 存在,则:

        1. 如果 shouldSoftUpdate 为 true,则并行运行 Soft Update 算法,参数为registration

        2. timingInfoworker cache lookup start 设为以 useHighResPerformanceTimers 为参数的粗化共享当前时间

        3. environment 为 null。

        4. 如果 request非子资源请求,则:

          1. environment 设为 reservedClient

        5. 否则:

          1. environment 设为 client

        6. caches 为以 environment 和 "caches" 调用 obtain a local storage bottle map 的结果。

        7. 对于 caches 中的每个 cacheNamecache

          1. 如果 source["cacheName"] 存在source["cacheName"] 不是 cacheName,则 跳过本次循环

          2. requestResponses 为以 request、新的 CacheQueryOptionscache 调用 Query Cache 的结果。

          3. 如果 requestResponses 是空的列表,则返回 timingInfo

          4. 否则:

            1. requestResponserequestResponses 的第一个元素。

            2. responserequestResponse 的 response。

            3. globalObjectactiveWorkerglobal object

            4. 如果 globalObject 为 null:

              1. globalObject 设为以 activeWorker 调用 Setup ServiceWorkerGlobalScope 的结果。

            5. 如果 globalObject 为 null,返回 timingInfo

            注意:这里只创建了 ServiceWorkerGlobalScope,是因为 CORS 检查需要。实际实现通常不会真的在此处创建 ServiceWorkerGlobalScope。

            1. 如果 responsetype 为 "opaque",且以 globalObjectoriginglobalObject、空字符串和 responseinternal response 运行 cross-origin resource policy check 返回 blocked,则返回 timingInfo

            2. timingInfoworker final router source 设为 "cache"

            3. resultservice worker timing info 设为 timingInfo

            4. 返回 response

        8. 返回 timingInfo

      4. 否则如果 source"race-network-and-fetch-handler",且 requestmethod 为 `GET`,则:

        1. 如果 shouldSoftUpdate 为 true,则并行运行 Soft Update,参数为 registration

        2. queue 为一个空的queue,类型为race result

        3. raceFetchController 为 null。

        4. raceResponse 为一个race response,其中 value 为 "pending"。

        5. 并行执行以下子步骤:

          1. 如果 fetchControllerstate 是 "terminated" 或 "aborted",则将 raceResponse 设为 race responsevalue 为 null,并终止这些步骤。

          2. raceFetchController 设为以下调用的结果:以 request 作为参数调用 fetch,其中 processResponse 的步骤如下,入参为 response raceNetworkRequestResponse

            1. 执行下列步骤,但 fetchControllerstate 为 "terminated" 或 "aborted" 时中止。

              1. raceResponsevalue 设为 raceNetworkRequestResponse

              2. 如果 raceNetworkRequestResponsestatusok 状态,则:

                1. raceNetworkResultrace result,其中 routed responseraceNetworkRequestResponseused route"network"

                2. 入队 raceNetworkResultqueue

            2. 如果被中止raceFetchController 不为 null,则:

              1. Abort raceFetchController

              2. raceResponse 设为一个 race responsevalue 为 null。

        6. 以 undefined 解决 preloadResponse

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

          1. fetchHandlerResponse 为以 requestregistrationuseHighResPerformanceTimerstimingInfoworkerRealmreservedClientpreloadResponseraceResponse 调用 Create Fetch Event and Dispatch 的结果。

          2. 如果 fetchHandlerResponse 不为 null 且不是network error,且 raceFetchController 不为 null,则abort raceFetchController

          3. raceFetchHandlerResultrace result,其 routed responsefetchHandlerResponseused route"fetch-event"

          4. 入队 raceFetchHandlerResultqueue

        8. 等待 queue 非空。

        9. resultdequeue queue 的结果。

        10. routedResponseresultrouted response

        11. 如果 routedResponse 为 null:

          1. 返回 timingInfo

        12. 如果 resultused route"network",则:

          1. routedResponseservice worker timing info 设为 timingInfo

        13. routedResponseservice worker timing infoworker final router source 设为 resultused route

        14. 返回 routedResponse

      5. 断言:source 为 "fetch-event"

  14. responseForAutoPreload 为 null。

  15. 如果 request导航请求requestmethod 为 `GET`,且 registrationactive workerset of event types to handle 包含 fetch,且 registrationactive workerall fetch listeners are empty flag 未设置,则:

    1. 如果 registrationnavigation preload enabled flag 已设置,则:

      注意:如果上述条件均为真,但 registrationactive workerset of event types to handle 不包含 fetch,则用户代理应考虑在控制台输出警告,因为开发者意图不清楚。

      1. preloadRequestrequest clone 的结果。

      2. preloadRequestHeaderspreloadRequestheader list

      3. preloadResponseObject 为一个与 Headers 新对象(其 guard 为 "immutable")关联的新 Response 对象。

      4. preloadRequestHeaders 追加一个新的 header,其 name 为 `Service-Worker-Navigation-Preload`,valueregistrationnavigation preload header value

      5. preloadRequestservice-workers mode 设为 "none"。

      6. preloadFetchController 为 null。

      7. 并行运行以下子步骤,但 fetchControllerstate 为 "terminated" 或 "aborted" 时终止:

        1. preloadFetchController 设为抓取 preloadRequest 的结果。

          对于 navigationPreloadResponseprocessResponse,执行以下子步骤:

          1. 如果 navigationPreloadResponsetype 为 "error",则以 TypeError 拒绝 preloadResponse 并终止这些步骤。

          2. preloadResponseObject 关联到 navigationPreloadResponse

          3. preloadResponseObject 解决 preloadResponse

      8. 如果被中止,则:

        1. deserializedError 为以 null 和 workerRealm 运行 deserialize a serialized abort reason 的结果。

        2. Abort preloadFetchController,参数为 deserializedError

    2. 否则如果 timingInfoworker matched router source 不为 "fetch-event",用户代理可以运行以下子步骤:

      注意:用户代理可在创建 fetch 事件时并行发起网络请求,用于减少启动开销。

      1. 断言:timingInfoworker matched router source 为 null。

      2. autoPreloadFetchController 为 null。

      3. responseForAutoPreload 设为一个 race response,其 value 为 "pending"。

      4. 并行运行以下子步骤,但 fetchControllerstate 为 "terminated" 或 "aborted" 时终止:

        1. autoPreloadFetchController 设为以 request 调用 fetch 的结果,其中 processResponse 的步骤是接受 response autoPreloadRequestResponse

          1. responseForAutoPreloadvalue 设为 autoPreloadRequestResponse

      5. 如果被中止autoPreloadFetchController 不为 null,则:

        1. Abort autoPreloadFetchController

        2. responseForAutoPreload 设为一个 race responsevalue 为 null。

      6. 以 undefined 解决 preloadResponse

  16. 否则,以 undefined 解决 preloadResponse

  17. fetchResult 为以 requestregistrationuseHighResPerformanceTimerstimingInfoworkerRealmreservedClientpreloadResponseresponseForAutoPreload 作为参数调用 Create Fetch Event and Dispatch 的结果。

  18. 如果 timingInfoworker final router source 非空字符串:

    1. 断言 timingInfoworker final router source"network"

    2. 如果 fetchResult 为 null,则返回 timingInfo

    3. 否则:

      1. 断言 fetchResultservice worker timing infoworker final router source"network"

      2. fetchResultservice worker timing infoworker final router source 设为 "fetch-event"

  19. 返回 fetchResult

创建 Fetch 事件并分派

输入

request,一个请求

registration,一个service worker 注册

useHighResPerformanceTimers,布尔值

timingInfo,一个service worker 定时信息

workerRealm相关 realm,即全局对象的相关 realm

reservedClient,一个reserved client

preloadResponse,一个promise

raceResponse,一个竞态响应或 null

输出

一个响应或 null

  1. response 为 null。

  2. eventCanceled 为 false。

  3. clientrequestclient

  4. activeWorkerregistrationactive worker

  5. eventHandled 为 null。

  6. handleFetchFailed 为 false。

  7. respondWithEntered 为 false。

  8. networkError网络错误

  9. 如果 raceResponse 不为 null:

    1. networkErrorservice worker 定时信息 设为 timingInfo

  10. shouldSoftUpdate 为 true,若下列任一条件为真,否则为 false:

  11. 如果以 "fetch" 和 activeWorker 运行 Should Skip Event 算法结果为 true,则:

    1. 如果 shouldSoftUpdate 为 true,则并行运行 Soft Update 算法,参数为 registration

    2. 返回 null。

  12. 如果 activeWorkerall fetch listeners are empty flag 被设置:

    1. 并行

      1. 如果 activeWorkerstate 为 "activating",则等待 activeWorkerstate 变为 "activated"。

      2. activeWorker 运行 Run Service Worker 算法。

      3. 如果 shouldSoftUpdate 为 true,则运行 Soft Update 算法,参数为 registration

    2. 返回 null。

  13. 如果 useHighResPerformanceTimers 为 true,则将 useHighResPerformanceTimers 设为 activeWorkerglobal objectcross-origin isolated capability

  14. timingInfostart time 设为以 useHighResPerformanceTimers 为参数的 粗化共享当前时间

  15. 如果 activeWorkerstate 为 "activating",则等待 activeWorkerstate 变为 "activated"。

  16. 如果以 activeWorker 运行 Run Service Worker 算法结果为 failure,则将 handleFetchFailed 设为 true。

  17. 否则:

    1. workerRealm 设为 activeWorker相关 realm

    2. eventHandled 设为 workerRealm 中的新 promise

    3. 如果 raceResponse 不为 null,则设置 activeWorkerglobal objectrace response map[request] 为 raceResponse

    4. 队列化一个任务 task,执行以下子步骤:

      1. e 为以 FetchEvent 创建的事件

      2. abortController新的 AbortController,位于 workerRealm

      3. requestObject构建 Request 对象的结果,参数为 request、新的 Headersguard 为 "immutable"、abortControllersignal、以及 workerRealm

      4. 初始化 etype 属性为 fetch

      5. 初始化 ecancelable 属性为 true。

      6. 初始化 erequest 属性为 requestObject

      7. 初始化 epreloadResponsepreloadResponse

      8. 初始化 eclientId 属性为 clientid

      9. 如果 request非子资源请求且其 destination 不为 "report",则初始化 eresultingClientId 属性为 reservedClientid,否则设为空字符串。

      10. 如果 request导航请求,则初始化 ereplacesClientId 属性为 requestreplaces client id,否则设为空字符串。

      11. 初始化 ehandledeventHandled

      12. timingInfofetch event dispatch time 设为以 useHighResPerformanceTimers 为参数的 粗化共享当前时间

      13. 派发 eactiveWorkerglobal object

      14. 调用 Update Service Worker Extended Events Set,参数为 activeWorkere

      15. 如果 erespond-with entered flag 被设置,则将 respondWithEntered 设为 true。

      16. 如果 ewait to respond flag 被设置,则:

        1. 等待 ewait to respond flag 被取消。

        2. 如果 erespond-with error flag 设置,则将 handleFetchFailed 设为 true。

        3. 否则,将 response 设为 epotential response

      17. 如果 response 为 null,且 requestbody 不为 null,且 requestbodysource 为 null,则:

        1. 如果 requestbody不可用,则将 handleFetchFailed 设为 true。

        2. 否则,取消 requestbody,参数为 undefined。

      18. 如果 response 不为 null,则将 responseservice worker 定时信息 设为 timingInfo

      19. 如果 ecanceled flag 被设置,则将 eventCanceled 设为 true。

      20. 如果 fetchControllerstate 是 "terminated" 或 "aborted",则:

        1. deserializedError 为以 fetchControllerserialized abort reasonworkerRealm 调用 deserialize a serialized abort reason

        2. 队列化一个任务以在 abortControllersignal abort,参数为 deserializedError

      如果 task 被丢弃,将 handleFetchFailed 设为 true。

      task 必须使用 activeWorkerevent loophandle fetch task source

  18. 等待 task 执行完毕,或 handleFetchFailed 为 true。

  19. 如果 shouldSoftUpdate 为 true,则并行运行 Soft Update 算法,参数为 registration

  20. 如果 activeWorkerglobal objectrace response map[request] 存在,则移除 activeWorkerglobal objectrace response map[request]。

  21. 如果 respondWithEntered 为 false,则:

    1. 如果 eventCanceled 为 true,则:

      1. 如果 eventHandled 不为 null,则拒绝 eventHandled,原因是 "NetworkError" DOMException,位于 workerRealm

      2. 返回 networkError

    2. 如果 eventHandled 不为 null,则resolve eventHandled

    3. 如果 raceResponse 不为 null,且 raceResponsevalue 不为 null,则:

      1. 等待 raceResponsevalue 不为 "pending"。

      2. 如果 raceResponsevalue 是一个响应,则返回 raceResponsevalue

    4. 返回 null。

  22. 如果 handleFetchFailed 为 true,则:

    1. 如果 eventHandled 不为 null,则拒绝 eventHandled,原因是 "NetworkError" DOMException,位于 workerRealm

    2. 返回 networkError

  23. 如果 eventHandled 不为 null,则resolve eventHandled

  24. 返回 response

解析 URL 模式

输入

rawPattern,一个 URLPatternCompatible

serviceWorker,一个 Service Worker

输出

一个 URL 模式

  1. baseURLserviceWorker脚本 URL

  2. 返回给定 baseURL,使用 rawPattern 从 Web IDL 值构建 URL 模式的结果。

验证路由器条件

输入

condition,一个 RouterCondition

serviceWorker,一个 Service Worker

输出

一个布尔值

  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,一个 Service Worker

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,一个Service Worker

request,一个请求

输出

RouterSource 或 null

  1. 对于 serviceWorker路由器规则列表中的每个 rule

    1. 如果使用 rule["condition"]、 serviceWorkerrequest 运行匹配路由器条件算法返回 true,则返回 rule["source"]。

  2. 返回 null。

是否应跳过事件

输入

eventName,一个字符串

serviceWorker,一个Service Worker

输出

一个布尔值

注意:为避免不必要的延迟,本规范允许在Service Worker的全局范围内首次执行脚本期间未确定性地添加该事件的事件侦听器时跳过事件分派。

  1. 如果 serviceWorker要处理的事件类型集包含 eventName,则用户代理可以返回 true。

  2. 返回 false。

触发功能性事件

输入

eventName, 一个字符串

eventConstructor, 一个继承自 ExtendableEvent 的事件构造器

registration, 一个 service worker registration

initialization, 可选的 event 属性初始化,用于由 eventConstructor 构造的事件实例

postDispatchSteps, 可选的步骤,在线程上于 active worker 的事件循环中运行,且在运行时 dispatchedEvent 被设为已被 分发eventConstructor 的实例

输出

  1. 断言:registrationactive worker 不为 null。

  2. activeWorkerregistrationactive worker

  3. 如果以 eventNameactiveWorker 运行 Should Skip Event 的结果为 true,则:

    1. 如果 registrationstale,则并行运行 Soft Update 算法,参数为 registration

    2. 返回。

  4. 如果 activeWorkerstate 为 "activating",则等待 activeWorkerstate 变为 "activated"。

  5. 如果以 activeWorker 运行 Run Service Worker 算法的结果为 failure,则:

    1. 如果 registrationstale,则并行运行 Soft Update 算法,参数为 registration

    2. 返回。

  6. 队列化一个任务 task 来运行下列子步骤:

    1. event 为以 eventConstructorrelevant realm(来自 activeWorkerglobal object)创建事件的结果。

    2. 如果 initialization 不为 null,则用 initialization 初始化 event

    3. activeWorker全局对象上分发event

    4. 调用 Update Service Worker Extended Events Set,参数为 activeWorkerevent

    5. 如果 postDispatchSteps 不为 null,则运行 postDispatchSteps 并将 event 作为 dispatchedEvent 传入。

    task 必须 使用 activeWorker事件循环处理功能性事件任务源

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

  8. 如果 registrationstale,则并行运行 Soft Update 算法,参数为 registration

要在特定的 serviceWorkerRegistration 上触发一个 "amazingthing" 事件(类型为 AmazingThingEvent),并初始化事件对象的属性,说明性 prose 如下:
  1. 触发功能性事件 "amazingthing",使用 AmazingThingEventserviceWorkerRegistration 上,并设置如下属性:

    propertyName

    value

    anotherPropertyName

    anotherValue

    然后用 dispatchedEvent 运行下列步骤:

    1. 在 service worker 的事件循环中对 dispatchedEvent 做你需要的操作。

注意:初始化步骤和分发后步骤是可选的。如果不需要,prose 可写为:

  1. 触发功能性事件 "whatever",使用 ExtendableEventserviceWorkerRegistration 上。

处理 Service Worker 客户端 卸载

service worker client 通过 卸载文档清理步骤终止 卸载时,用户代理必须运行这些步骤。

输入

client,一个 service worker client

输出

  1. 以原子方式运行下列步骤。

  2. registrationclient使用service worker registration

  3. 如果 registration 为 null,则中止这些步骤。

  4. 如果有其他 service worker client 正在使用 registration,则中止这些步骤。

  5. 如果 registrationunregistered,则以 registration 为参数调用 Try Clear Registration

  6. registration 为参数调用 Try Activate

处理用户代理 关闭

输入

输出

  1. 对于每个 registration(来自 registration mapvalues):

    1. 如果 registrationinstalling worker 不为 null,则:

      1. 如果 registrationwaiting worker 为 null 且 registrationactive worker 为 null,则以 registration 为参数调用 Clear Registration 并继续下一个循环迭代。

      2. 否则,将 registrationinstalling worker 设为 null。

    2. 如果 registrationwaiting worker 不为 null,则并行执行:

      1. registration 为参数调用 Activate

更新 Service Worker 扩展事件 集合

输入

worker, 一个 service worker

event, 一个 event

输出

  1. 断言:eventdispatch flag 未设置。

  2. 对于 workerset of extended events 中的每个 item

    1. 如果 item 不是 active,则从 workerset of extended events移除 item

  3. 如果 eventactive,则将 event 追加workerset of extended events

注销

输入

job, 一个 job

输出

  1. 如果 joborigin(来自 jobscope url)与 jobclientorigin 不相同,则:

    1. job 和 "SecurityError" DOMException 为参数调用 Reject Job Promise

    2. job 为参数调用 Finish Job 并中止这些步骤。

  2. registration 为以 jobstorage keyjobscope url 为参数运行 Get Registration 的结果。

  3. 如果 registration 为 null,则:

    1. job 和 false 为参数调用 Resolve Job Promise

    2. job 为参数调用 Finish Job 并中止这些步骤。

  4. registration map移除 键 (registrationstorage key, jobscope url)。

  5. job 和 true 为参数调用 Resolve Job Promise

  6. registration 为参数调用 Try Clear Registration

    注意:如果 Try Clear Registration 在此未触发 Clear Registration,当最后一个正在使用该注册的客户端被卸载或该注册的 service workers 的 extend lifetime promises 解决时,会再次尝试 Clear Registration

  7. job 为参数调用 Finish Job

设置 注册

输入

storage key, 一个 storage key

scope, 一个 URL

updateViaCache, 一个 update via cache mode

输出

registration, 一个 service worker registration

  1. 以原子方式运行下列步骤。

  2. scopeString 为将 scope 序列化 后的字符串,且设置 exclude fragment flag

  3. registration 为一个新的 service worker registration,其 storage key 设为 storage keyscope url 设为 scope,以及 update via cache mode 设为 updateViaCache

  4. registration map[(storage key, scopeString)] 设为 registration

  5. 返回 registration

清除 注册

输入

registration, 一个 service worker registration

输出

  1. 以原子方式运行下列步骤。

  2. 如果 registrationinstalling worker 不为 null,则:

    1. 终止 (Terminate) registrationinstalling worker

    2. registrationinstalling worker 和 "redundant" 为参数运行 Update Worker State 算法。

    3. registration、"installing" 和 null 为参数运行 Update Registration State 算法。

  3. 如果 registrationwaiting worker 不为 null,则:

    1. 终止 (Terminate) registrationwaiting worker

    2. registrationwaiting worker 和 "redundant" 为参数运行 Update Worker State 算法。

    3. registration、"waiting" 和 null 为参数运行 Update Registration State 算法。

  4. 如果 registrationactive worker 不为 null,则:

    1. 终止 (Terminate) registrationactive worker

    2. registrationactive worker 和 "redundant" 为参数运行 Update Worker State 算法。

    3. registration、"active" 和 null 为参数运行 Update Registration State 算法。

尝试 清除 注册

输入

registration, 一个 service worker registration

输出

  1. 当且仅当没有任何 service worker client 正在使用 registration,且下列所有条件都为真时,调用 Clear Registration

更新 注册 状态

输入

registration, 一个 service worker registration

target, 一个字符串(取值之一为 "installing"、"waiting" 和 "active")

source, 一个 service worker 或 null

输出

  1. registrationObjects 为包含所有与 registration 关联的 ServiceWorkerRegistration 对象的数组。

  2. 如果 target 为 "installing",则:

    1. registrationinstalling worker 设为 source

    2. 对于 registrationObjects 中的每个 registrationObject

      1. 队列化一个任务:如果 registrationinstalling worker 为 null,则将 registrationObjectinstalling 属性设为 null,否则将该属性设为在 registrationObjectrelevant settings object 中表示 registrationservice worker object 的结果。

  3. 否则如果 target 为 "waiting",则:

    1. registrationwaiting worker 设为 source

    2. 对于 registrationObjects 中的每个 registrationObject

      1. 队列化一个任务:如果 registrationwaiting worker 为 null,则将 registrationObjectwaiting 属性设为 null,否则将其设为在 registrationObjectrelevant settings object 中表示 registrationservice worker object 的结果。

  4. 否则如果 target 为 "active",则:

    1. registrationactive worker 设为 source

    2. 对于 registrationObjects 中的每个 registrationObject

      1. 队列化一个任务:如果 registrationactive worker 为 null,则将 registrationObjectactive 属性设为 null,否则将其设为在 registrationObjectrelevant settings object 中表示 registrationservice worker object 的结果。

    任务 必须 使用 registrationObjectrelevant settings objectresponsible event loopDOM manipulation task source

更新 Worker 状态

输入

worker, 一个 service worker

state, 一个 service workerstate

输出

  1. 断言:state 不是 "parsed"。

    注意:"parsed" 是初始状态。service worker 不会被更新到此状态。

  2. workerstate 设为 state

  3. settingsObjects 为所有其 environment settings objects,其 originworkerscript urlorigin 相同。

  4. 对于 settingsObjects 中的每个 settingsObject,在其 responsible event loop 上,使用 DOM manipulation task source 队列化一个任务,运行下列步骤:

    1. objectMapsettingsObjectservice worker object map

    2. 如果 objectMap[worker] 不存在,则中止这些步骤。

    3. workerObjobjectMap[worker]。

    4. workerObjstate 设为 state

    5. workerObj触发 名为 statechange 的事件。

通知 控制器 变更

输入

client, 一个 service worker client

输出

  1. 断言:client 不为 null。

  2. 如果 client 是一个 environment settings object,则队列化一个任务,以在与 client 关联的 ServiceWorkerContainer 对象上 触发 名为 controllerchange 的事件。

任务 必须 使用 clientresponsible event loopDOM manipulation task source

匹配 Service Worker 注册

输入

storage key, 一个 storage key

clientURL, 一个 URL

输出

一个 service worker registration

  1. 以原子方式运行下列步骤。

  2. clientURLString 为将 clientURL 序列化 后的字符串。

  3. matchingScopeString 为空字符串。

  4. scopeStringSet 为一个空列表。

  5. 对于每个 (entry storage key, entry scope)(来自 registration mapkeys):

    1. 如果 storage keyentry storage key 相等,则将 entry scope 追加scopeStringSet 的末尾。

  6. matchingScopeString 设为 scopeStringSet 中的最长值(若存在)且该值是 clientURLString 的前缀。

    注意:此处的 URL 字符串匹配基于前缀而非路径结构。例如,客户端 URL 字符串 "https://example.com/prefix-of/resource.html" 将匹配作用域为 "https://example.com/prefix" 的注册。对于同源安全性,URL 字符串比较是安全的,因为 HTTP(S) URL 在序列化时会在 origin 部分末尾包含斜线。

  7. matchingScope 为 null。

  8. 如果 matchingScopeString 不是空字符串,则:

    1. matchingScope 设为对 matchingScopeString 运行 解析 的结果。

    2. 断言:matchingScopeoriginclientURLorigin同源

  9. 返回以 storage keymatchingScope 为参数运行 Get Registration 的结果。

获取 注册

输入

storage key, 一个 storage key

scope, 一个 URL

输出

一个 service worker registration

  1. 以原子方式运行下列步骤。

  2. scopeString 为空字符串。

  3. 如果 scope 不为 null,则将 scopeString 设为将 scope 序列化 后的结果,且设置 exclude fragment flag

  4. 对于每个 (entry storage key, entry scope) → registration(来自 registration map):

    1. 如果 storage keyentry storage key 相等scopeString 匹配 entry scope,则返回 registration

  5. 返回 null。

获取 最新 Worker

输入

registration, 一个 service worker registration

输出

newestWorker, 一个 service worker

  1. 以原子方式运行以下步骤。

  2. newestWorker为 null。

  3. 如果registrationinstalling worker不为 null,则将newestWorker设为registrationinstalling worker

  4. 否则如果registrationwaiting worker不为 null,则将newestWorker设为registrationwaiting worker

  5. 否则如果registrationactive worker不为 null,则将newestWorker设为registrationactive worker

  6. 返回newestWorker

Service Worker 无 待处理 事件

输入

worker, 一个 service worker

输出

布尔值(True 或 False)

  1. 对于 workerset of extended events 中的每个 event

    1. 如果 eventactive,则返回 false。

  2. 返回 true。

创建 Client

输入

client, 一个 service worker client

输出

clientObject, 一个 Client 对象

  1. clientObject 为一个新的 Client 对象。

  2. clientObjectservice worker client 设为 client

  3. 返回 clientObject

创建 Window Client

输入

client, 一个 service worker client

frameType, 一个字符串

visibilityState, 一个字符串

focusState, 一个布尔值

ancestorOriginsList, 一个列表

输出

windowClient, 一个 WindowClient 对象

  1. windowClient 为一个新的 WindowClient 对象。

  2. windowClientservice worker client 设为 client

  3. windowClientframe type 设为 frameType

  4. windowClientvisibility state 设为 visibilityState

  5. windowClientfocus state 设为 focusState

  6. windowClientancestor origins array 设为由 ancestorOriginsList 创建的一个 frozen array

  7. 返回 windowClient

获取 Frame 类型

输入

navigable, 一个 navigable

输出

frameType, 一个字符串

  1. 如果 navigableparent 不为 null,则返回 "nested"。

  2. 如果 navigableactive browsing context 是一个 auxiliary browsing context,则返回 "auxiliary"。

  3. 返回 "top-level"。

解析 获取 Client 的 Promise

输入

client, 一个 service worker client

promise, 一个 promise

输出

  1. 如果 client 是一个 environment settings object,则:

    1. 如果 client 不是 secure context,则在 promiserelevant settings objectresponsible event loop 上,使用 DOM manipulation task source 队列化一个任务,以拒绝 promise,原因是 "SecurityError" DOMException,并中止这些步骤。

  2. 否则:

    1. 如果 clientcreation URL 不是一个 potentially trustworthy URL,则在 promiserelevant settings objectresponsible event loop 上,使用 DOM manipulation task source 队列化一个任务,以拒绝 promise,原因是 "SecurityError" DOMException,并中止这些步骤。

  3. 如果 client 是一个 environment settings object 且不是一个 window client,则:

    1. clientObject 为以 client 为参数运行 Create Client 算法的结果。

    2. promiserelevant settings objectresponsible event loop 上,使用 DOM manipulation task source 队列化一个任务,以用 clientObject 解析 promise,并中止这些步骤。

  4. 否则:

    1. browsingContext 为 null。

    2. 如果 client 是一个 environment settings object,则将 browsingContext 设为 clientglobal objectbrowsing context

    3. 否则,将 browsingContext 设为 clienttarget browsing context

    4. navigablebrowsingContext 的具有 active browsing contextnavigable

    5. 队列化一个任务,在 browsingContextevent loop 上运行,使用 user interaction task source

      1. frameType 为以 navigable 运行 Get Frame Type 的结果。

      2. visibilityStatebrowsingContextactive documentvisibilityState 属性值。

      3. focusState 为以 browsingContextactive document 为参数运行 has focus steps 的结果。

      4. ancestorOriginsList 为空列表。

      5. 如果 client 是一个 window client,则将 ancestorOriginsList 设为 browsingContextactive documentrelevant global objectLocation 对象的 ancestor origins list 的关联列表。

      6. promiserelevant settings objectresponsible event loop 上,使用 DOM manipulation task source 队列化一个任务,运行下列步骤:

        1. 如果 clientdiscarded flag 已设置,则用 undefined 解析 promise 并中止这些步骤。

        2. windowClient 为以 client, frameType, visibilityState, focusStateancestorOriginsList 为参数运行 Create Window Client 的结果。

        3. windowClient 解析 promise

查询 缓存

输入

requestQuery, 一个 request

options, 一个可选的 CacheQueryOptions 对象

targetStorage, 一个可选的 request response list

输出

resultList, 一个 request response list

  1. resultList 为一个空的 列表

  2. storage 为 null。

  3. 如果 可选 参数 targetStorage 被省略,则将 storage 设为 relevant request response list

  4. 否则,将 storage 设为 targetStorage

  5. 对于 storage 中的每个 requestResponse

    1. cachedRequestrequestResponse 的 request。

    2. cachedResponserequestResponse 的 response。

    3. 如果 用 requestQuery, cachedRequest, cachedResponseoptions 调用 Request Matches Cached Item 返回 true,则:

      1. requestCopycachedRequest 的副本。

      2. responseCopycachedResponse 的副本。

      3. requestCopy/responseCopy 添加到 resultList

  6. 返回 resultList

请求 匹配 缓存 项

输入

requestQuery, 一个 request

request, 一个 request

response, 一个可选的 response 或 null(默认 null)

options, 一个可选的 CacheQueryOptions 对象

输出

布尔值

  1. 如果 options["ignoreMethod"] 为 false 且 requestmethod 不是 `GET`,则返回 false。

  2. queryURLrequestQueryurl

  3. cachedURLrequesturl

  4. 如果 options["ignoreSearch"] 为 true,则:

    1. cachedURLquery 设为空字符串。

    2. queryURLquery 设为空字符串。

  5. 如果 在设置 exclude fragment flagqueryURLcachedURL 不相等,则返回 false。

  6. 如果 response 为 null,或 options["ignoreVary"] 为 true,或 responseheader list 不包含 `Vary`,则返回 true。

  7. fieldValues 为一个列表,包含 response 的 `Vary` 头的 field-values 对应的元素。

  8. 对于 fieldValues 中的每个 fieldValue

    1. 如果 fieldValue 匹配 "*",或以 fieldValuerequestheader list 计算的 combined value 与以 fieldValuerequestQueryheader list 计算的 combined value 不匹配,则返回 false。

  9. 返回 true。

批量 缓存 操作

输入

operations, 一个 列表,包含若干 cache batch operation 对象

输出

resultList, 一个 request response list

  1. cacherelevant request response list

  2. backupCache 为一个新的 request response list,其为 cache 的副本。

  3. addedItems 为一个空的 列表

  4. 尝试以原子方式运行下列子步骤:

    1. resultList 为一个空的 列表

    2. 对于 operations 中的每个 operation

      1. 如果 operationtype 既不匹配 "delete" 也不匹配 "put",则抛出 一个 TypeError

      2. 如果 operationtype 匹配 "delete" 且 operationresponse 不为 null,则抛出 一个 TypeError

      3. 如果 以 operationrequestoperationoptionsaddedItems 调用 Query Cache 的结果非空,则抛出 一个 "InvalidStateError" DOMException

      4. requestResponses 为一个空的 列表

      5. 如果 operationtype 匹配 "delete",则:

        1. requestResponses 设为以 operationrequestoperationoptions 调用 Query Cache 的结果。

        2. 对于 requestResponses 中的每个 requestResponse

          1. 将与 requestResponse 匹配的 cache移除

      6. 否则如果 operationtype 匹配 "put",则:

        1. 如果 operationresponse 为 null,则抛出 一个 TypeError

        2. roperationrequest 的关联 request

        3. 如果 rurlscheme 不是 "http" 或 "https",则抛出 一个 TypeError

        4. 如果 rmethod 不是 `GET`,则抛出 一个 TypeError

        5. 如果 operationoptions 不为 null,则抛出 一个 TypeError

        6. requestResponses 设为以 operationrequest 调用 Query Cache 的结果。

        7. 对于 requestResponses 中的每个 requestResponse

          1. 将与 requestResponse 匹配的 cache移除

        8. operationrequest/operationresponse 追加cache

        9. 如果前两步的缓存写入操作因超出授权配额限制而失败,则抛出 一个 QuotaExceededError

        10. operationrequest/operationresponse 追加addedItems

      7. operationrequest/operationresponse 追加resultList

    3. 返回 resultList

  5. 然后,如果抛出异常,则:

    1. relevant request response list移除 所有项。

    2. 对于 backupCache 中的每个 requestResponse

      1. requestResponse 追加relevant request response list

    3. 抛出 该异常。

    注意:当抛出异常时,实施须回滚在批量操作作业期间对缓存存储所做的任何更改。

是否 为 异步 模块

输入

record, 一个 Module Record

moduleMap, 一个 module map

base, 一个 URL

seen, 一个 集合,包含 URLs

输出

布尔值

  1. 如果 record 不是一个 Cyclic Module Record,则:

    1. 返回 false。

  2. 如果 record.[[Async]] 为 true,则:

    1. 返回 true。

  3. 对于 record.[[RequestedModules]] 中的每个 字符串 requested

    1. url 为以 baserequested 调用 解析模块说明符 的结果。

    2. 断言:url 永不为失败,因为以相同两个参数先前对 解析模块说明符 必定已成功。

    3. 如果 seen 不包含 url,则:

      1. url 追加seen

      2. 如果 moduleMap[url] 没有 record,则:

        1. 返回 false。

      3. 如果 对 moduleMap[url] 的 Is Async Module(参数为 该记录、moduleMapbase、与 seen) 返回 true,则:

        1. 返回 true。

  4. 返回 false。

查找 竞态 响应

输入

request, 一个 request

输出

一个 response 或 null

  1. registration 为 null。

  2. 如果 request 是一个 非子资源请求,则:

    1. 如果 requestreserved client 为 null,则返回 null。

    2. storage key 为以 requestreserved client 调用 obtain a storage key 的结果。

    3. registration 设为以 storage keyrequesturl 调用 Match Service Worker Registration 的结果。

  3. 否则如果 request 是一个 子资源请求,则:

    1. clientrequestclient

    2. 如果 clientactive service worker 为 null,则返回 null。

    3. registration 设为 clientactive service workercontaining service worker registration

  4. 否则,返回 null。

  5. activeWorkerregistrationactive worker

  6. mapactiveWorkerglobal objectrace response map

  7. 如果 map[request] 存在,则:

    1. entrymap[request].

    2. 等待直到 entryvalue 不为 "pending"。

    3. 如果 entry 是一个 响应(response),则返回对 克隆 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]
脚本策略。编辑草案。网址:https://wicg.github.io/csp-next/scripting-policy.html
[CSP3]
Mike West; Antonio Sartori. 内容安全策略第3级。2025年11月6日。工作草案。网址:https://www.w3.org/TR/CSP3/
[DOM]
Anne van Kesteren. DOM 标准。常青标准。网址:https://dom.spec.whatwg.org/
[ECMASCRIPT]
ECMAScript 语言规范。网址:https://tc39.es/ecma262/multipage/
[FETCH]
Anne van Kesteren. Fetch 标准。常青标准。网址:https://fetch.spec.whatwg.org/
[FileAPI]
Marijn Kruisselbrink. File API。2024年12月4日。工作草案。网址:https://www.w3.org/TR/FileAPI/
[HR-TIME-3]
Yoav Weiss. 高分辨率时间。2024年11月7日。工作草案。网址:https://www.w3.org/TR/hr-time-3/
[HTML]
Anne van Kesteren; 等. HTML 标准。常青标准。网址:https://html.spec.whatwg.org/multipage/
[INFRA]
Anne van Kesteren; Domenic Denicola. 基础设施标准。常青标准。网址:https://infra.spec.whatwg.org/
[MIMESNIFF]
Gordon P. Hemsley. MIME 嗅探标准。常青标准。网址:https://mimesniff.spec.whatwg.org/
[NAVIGATION-TIMING-2]
Yoav Weiss; Noam Rosenthal. 导航时序 第2级。2025年11月11日。工作草案。网址:https://www.w3.org/TR/navigation-timing-2/
[PAGE-LIFECYCLE]
页面生命周期。社区组草案报告。网址:https://wicg.github.io/page-lifecycle/
[RESOURCE-TIMING]
Yoav Weiss; Noam Rosenthal. 资源时序。2025年8月20日。候选推荐。网址:https://www.w3.org/TR/resource-timing/
[RFC2119]
S. Bradner. RFC 用于指示需求级别的关键词。1997年3月。最佳现行实践。网址:https://datatracker.ietf.org/doc/html/rfc2119
[RFC5234]
D. Crocker, 编辑; P. Overell. 语法规范的增强 BNF:ABNF。2008年1月。互联网标准。网址:https://www.rfc-editor.org/rfc/rfc5234
[RFC7230]
R. Fielding, 编辑; J. Reschke, 编辑. 超文本传输协议(HTTP/1.1):消息语法与路由。2014年6月。建议标准。网址:https://httpwg.org/specs/rfc7230.html
[RFC7231]
R. Fielding, 编辑; J. Reschke, 编辑. 超文本传输协议(HTTP/1.1):语义与内容。2014年6月。建议标准。网址:https://httpwg.org/specs/rfc7231.html
[SCREEN-CAPTURE]
Jan-Ivar Bruaroey; Elad Alon. 屏幕捕获。2025年7月17日。工作草案。网址:https://www.w3.org/TR/screen-capture/
[SECURE-CONTEXTS]
Mike West. 安全上下文。2023年11月10日。候选推荐。网址:https://www.w3.org/TR/secure-contexts/
[STORAGE]
Anne van Kesteren. 存储标准。常青标准。网址:https://storage.spec.whatwg.org/
[STREAMS]
Adam Rice; 等. 流标准。常青标准。网址:https://streams.spec.whatwg.org/
[TRUSTED-TYPES]
Krzysztof Kotowicz. 受信类型。2025年11月3日。工作草案。网址:https://www.w3.org/TR/trusted-types/
[URL]
Anne van Kesteren. URL 标准。常青标准。网址:https://url.spec.whatwg.org/
[URLPATTERN]
Ben Kelly; Jeremy Roman; 宍戸俊哉 (Shunya Shishido). URL 模式标准。常青标准。网址:https://urlpattern.spec.whatwg.org/
[WEBIDL]
Edgar Chen; Timothy Gu. Web IDL 标准。常青标准。网址: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<ServiceWorkerRegistration> 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?