1. 动机
本节内容不具有规范性。
Web 应用程序传统上假定网络是可访问的。这种假设贯穿了整个平台。HTML 文档通过 HTTP 加载,并且传统上通过后续的 HTTP 请求获取其所有子资源。这使得 Web 内容与其他技术栈相比处于劣势。
Service worker 的首要设计目标是通过提供一个 Web Worker 上下文来弥补这一不足,该上下文可以在导航即将发生时由运行时启动。这种事件驱动的 worker 针对源和路径(或模式)进行注册,这意味着当导航到该位置时可以咨询它。与网络请求相对应的事件被分派给该 worker,由该 worker 生成的响应可以覆盖默认网络堆栈的行为。从概念上讲,这将 service worker 置于网络和文档渲染器之间,允许 service worker 为文档提供内容,即使在离线状态下也是如此。
熟悉先前解决离线问题方案的 Web 开发人员报告称,这些解决方案缺乏灵活性。因此,service worker 的设计高度程序化,以增加开发人员的复杂性为代价提供了最大的灵活性。这种复杂性部分源于需要在单线程执行模型下保持 service worker 的响应能力。因此,service worker 暴露的 API 几乎完全是异步的,这种模式在其他 JavaScript 上下文中很常见,但在这里由于需要避免阻塞文档和资源加载而更加突出。
使用 HTML5 Application Cache 的开发人员还报告称其设计的几个特性会导致无法恢复的错误。Service worker 的一个关键设计原则是错误必须始终是可恢复的。Service worker 更新过程的许多细节旨在避免这些风险。
Service worker 的启动和保持活动状态依赖于它们与事件的关系,而不是与文档的关系。这种设计大量借鉴了开发人员和供应商在使用 shared worker 和 Chrome 后台页面方面的经验。从这些系统中吸取的一个关键教训是,必须限制后台处理上下文的执行时间,既为了节省资源,也为了确保开发人员始终关注后台上下文丢失和重启的问题。因此,service worker 与 Chrome 事件页面(后台页面的后继者)有着不止一点的相似之处。Service worker 可能会在没有附加文档的情况下由用户代理启动,并且几乎随时可能被用户代理终止。从概念上讲,service worker 可以被认为是可以在不处理来自文档的任何消息的情况下启动、处理事件并终止的 Shared Worker。建议开发人员记住,service worker 可能在一秒钟内被启动和终止多次。
Service worker 是通用的、事件驱动的、有时间限制的脚本上下文,在源上运行。这些特性使其成为一系列运行时服务的天然端点,这些服务可能比特定文档的上下文生命周期更长,例如处理推送通知、后台数据同步、响应来自其他源的资源请求,或接收对计算成本高昂的数据(例如地理位置或陀螺仪)的集中更新。
2. 模型
2.1. Service Worker
service worker 是一种 web worker。service worker 在注册的 service worker client 的 origin 中执行。
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(一个 RouterRule
的 list)。初始为一个空的 list。
如果 service worker 的 event loop 在运行,则称该 service worker running。
service worker 有一个关联的 [[service worker queue]](一个 parallel queue)。
2.1.1. 生命周期
service
worker 的生命周期与事件的执行生命周期相关联,而不是与 service worker client 对 ServiceWorker
对象的引用相关联。
用户代理可以在任何时候终止 service worker,如果它:
-
没有要处理的事件。
-
检测到异常操作:例如无限循环和在处理事件时超过强加的时间限制(如果有)的任务。
2.1.2. 事件
Service Workers 规范定义了 service worker events(每个都是一个 event),包括(见列表):
2.2. Service Worker 时序
Service worker 标记某些时间点,这些时间点稍后会通过 导航时序 API 暴露。
service worker timing info 是一个 struct。它有以下 item:
- start time
-
一个
DOMHighResTimeStamp
,初始为 0。 - fetch event dispatch time
-
一个
DOMHighResTimeStamp
,初始为 0。
2.3. Service Worker 注册
service worker registration 是一个由 scope url、storage key 和一组 service worker、一个 installing worker、一个 waiting worker 和一个 active worker 组成的元组。只要 service worker registration 的 scope url 不同,用户代理可以在单个源启用多个 service worker registration。当已存在相同 scope url 的 service 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 registration 为 stale。
service worker registration 有一个关联的 update via cache mode,它是
"imports
"、"all
" 或 "none
"。初始设置为 "imports
"。
service worker registration 有一个或多个 task queue,用于备份来自其 active worker 的 event loop 的相应 task queue 中的 task。(此备份操作的目标任务源是 handle fetch task source 和 handle functional event task source。)当 active worker 被终止时,用户代理将 active worker 的 task 转储到 service worker registration 的 task queue 中,并在 active worker 启动时将这些任务重新排队到 active worker 的 event loop 的相应 task queue 中。与 event loop 拥有的 task queue 不同,service worker registration 的 task 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 registration 为 unregistered。
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:
-
设置 client 的 discarded flag。
注:实现可以丢弃其 discarded flag 被设置的客户端。
service worker client 有一个定义为 origin 的算法,该算法返回 service worker client 的 origin(如果 service worker client 是一个 environment settings object),否则返回 service worker client 的 creation URL 的 origin。
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 client 是 dedicated worker client 或 shared 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 worker 的 containing service worker registration。 service worker client 的 active service worker 按照以下小节中的解释确定。
本节的其余部分不具有规范性。
本节中的行为尚未完全规范,将在 HTML 标准中规范。该工作由 issue 和 pull request 跟踪。
2.5.1. 窗口客户端情况
window client 在 browsing context 创建和导航时创建。
当 window client 在 browsing context 创建过程中创建时:
如果 browsing context 的初始 active document 的 origin 是一个 opaque origin,则 window client 的 active service worker 设置为 null。 否则,它设置为创建者 document 的 service worker client 的 active service worker。
当 window client 在 browsing context 的 navigation 过程中创建时:
如果 fetch 通过 HTTP fetch 路由,则 window client 的 active service worker 设置为 service worker registration matching 的结果。 否则,如果创建的 document 的 origin 是一个 opaque origin 或与其创建者 document 的 origin 不相同,则 window client 的 active service worker 设置为 null。 否则,它设置为创建者 document 的 service worker client 的 active service worker。
注:对于初始替换 navigation,在 browsing context 创建时创建的初始 window client 被重用,但 active service worker 由上述相同行为确定。
注:没有沙盒指令 allow-same-origin
和
allow-scripts
的沙盒
iframe
的 active service worker 值为
null,因为它们的 origin 是一个 opaque origin。
2.5.2. 工作线程客户端情况
worker client 在用户代理运行工作线程时创建。
当 worker client 创建时:
当 fetch 通过 HTTP fetch 路由时,worker client 的 active service worker 设置为 service worker registration matching 的结果。 否则,如果 worker client 的 origin 是一个 opaque origin,或 request 的 URL 是一个 blob URL 且 worker client 的 origin 与 worker client 的 global object 的 owner set 中最后一个 item 的 origin 不相同,则 worker client 的 active service worker 设置为 null。 否则,它设置为 worker client 的 global object 的 owner set 中最后一个 item 的 environment settings object 的 active service worker。
注:具有 data: URL 的 Window client 和 worker client 的 active service worker 值为 null,因为它们的 origin 是一个 opaque origin。具有 blob URL 的 Window client 和 worker client 可以继承其创建者 document 或所有者的 active service worker,但如果 request 的 origin 与其创建者 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 的状态,遵循以下规则:
-
installing worker 不会持久化,而是被丢弃。如果 installing worker 是该 service worker registration 的唯一 service worker,则该 service worker registration 被丢弃。
为了实现这一点,用户代理必须在终止时调用 Handle User Agent Shutdown。
3. 客户端上下文
// 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 = {}); // event
options 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
worker 的 state 关联。
3.1.1.
获取 ServiceWorker
实例
environment settings object 有一个 service worker
object map,这是一个 map,其中 key 是 service worker,value 是 ServiceWorker
对象。
-
让 objectMap 为 environment 的 service worker object map。
-
如果 objectMap[serviceWorker] 不存在,则:
-
让 serviceWorkerObj 为 environment 的 Realm 中的一个新的
ServiceWorker
,并将其与 serviceWorker 关联。 -
设置 objectMap[serviceWorker] 为 serviceWorkerObj。
-
-
返回 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)
方法步骤是:
-
让 options 为 «[ "transfer" → transfer ]»。
-
以 message 和 options 作为参数调用
postMessage(message, options)
。
3.1.5. postMessage(message, options)
postMessage(message, options)
方法步骤是:
-
让 serviceWorker 为由此对象表示的 service worker。
-
让 incumbentSettings 为 incumbent settings object。
-
让 incumbentGlobal 为 incumbentSettings 的 global object。
-
让 serializeWithTransferResult 为 StructuredSerializeWithTransfer(message, options["
transfer
"])。重新抛出任何异常。 -
如果使用 "message" 和 serviceWorker 运行 Should Skip Event 算法的结果为 true,则返回。
-
并行运行以下子步骤:
-
如果使用 serviceWorker 运行 Run Service Worker 算法的结果是失败,则返回。
-
在 DOM manipulation task source 上排队一个任务来运行以下步骤:
-
让 source 通过切换 incumbentGlobal 的类型来确定:
ServiceWorkerGlobalScope
- 获取表示 incumbentGlobal 的 service worker 在 serviceWorker 的 global object 的 relevant settings object 中的 service worker 对象的结果。
Window
- 一个表示 incumbentGlobal 的 relevant settings
object 的新的
WindowClient
对象。 - 其他情况
- 一个表示 incumbentGlobal 的关联工作线程的新的
Client
对象
-
让 destination 为与 serviceWorker 关联的
ServiceWorkerGlobalScope
对象。 -
让 deserializeRecord 为 StructuredDeserializeWithTransfer(serializeWithTransferResult, destination 的 Realm)。
如果这抛出异常,让 e 为创建一个名为
messageerror
的事件的结果,使用ExtendableMessageEvent
,origin
属性初始化为 origin,source
属性初始化为 source。 -
否则:
-
让 messageClone 为 deserializeRecord.[[Deserialized]]。
-
让 newPorts 为一个由 deserializeRecord.[[TransferredValues]] 中的所有
MessagePort
对象组成的新的冻结数组(如果有的话),保持它们的相对顺序。 -
让 e 为创建一个名为
message
的事件的结果,使用ExtendableMessageEvent
,origin
属性初始化为 origin,source
属性初始化为 source,data
属性初始化为 messageClone,ports
属性初始化为 newPorts。
-
-
在 destination 上调度 e。
-
以 serviceWorker 和 e 调用 Update Service Worker Extended Events Set。
-
-
3.1.6. 事件处理程序
以下是所有实现 ServiceWorker
接口的对象必须支持的事件处理程序(及其对应的事件处理程序事件类型),作为事件处理程序 IDL 属性:
事件处理程序 | 事件处理程序事件类型 |
---|---|
onstatechange
|
statechange
|
3.2.
ServiceWorkerRegistration
[SecureContext ,Exposed =(Window ,Worker )]interface :
ServiceWorkerRegistration EventTarget {readonly attribute ServiceWorker ?installing ;readonly attribute ServiceWorker ?waiting ;readonly attribute ServiceWorker ?active ; [SameObject ]readonly attribute NavigationPreloadManager navigationPreload ;readonly attribute USVString scope ;readonly attribute ServiceWorkerUpdateViaCache updateViaCache ; [NewObject ]Promise <undefined >update (); [NewObject ]Promise <boolean >unregister (); // eventattribute 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,其中 key 是 service worker
registration,value 是 ServiceWorkerRegistration
对象。
-
让 objectMap 为 environment 的 service worker registration object map。
-
如果 objectMap[registration] 不存在,则:
-
让 registrationObject 为 environment 的 Realm 中的一个新的
ServiceWorkerRegistration
。 -
设置 registrationObject 的 service worker registration 为 registration。
-
设置 registrationObject 的
installing
属性为 null。 -
设置 registrationObject 的
waiting
属性为 null。 -
设置 registrationObject 的
active
属性为 null。 -
如果 registration 的 installing worker 不为 null,则设置 registrationObject 的
installing
属性为获取表示 registration 的 installing worker 在 environment 中的 service worker 对象的结果。 -
如果 registration 的 waiting worker 不为 null,则设置 registrationObject 的
waiting
属性为获取表示 registration 的 waiting worker 在 environment 中的 service worker 对象的结果。 -
如果 registration 的 active worker 不为 null,则设置 registrationObject 的
active
属性为获取表示 registration 的 active worker 在 environment 中的 service worker 对象的结果。 -
设置 objectMap[registration] 为 registrationObject。
-
-
返回 objectMap[registration]。
3.2.2. installing
installing
属性必须返回最后设置的值。
注:在一个 Realm 中,每个关联的 service
worker 只有一个 ServiceWorker
对象。
3.2.3. waiting
waiting
属性必须返回最后设置的值。
注:在一个 Realm 中,每个关联的 service
worker 只有一个 ServiceWorker
对象。
3.2.4.
active
active
属性必须返回最后设置的值。
注:在一个 Realm 中,每个关联的 service
worker 只有一个 ServiceWorker
对象。
3.2.5. navigationPreload
navigationPreload
获取器步骤是返回 service worker
registration 的 NavigationPreloadManager
对象。
3.2.6. scope
scope
获取器步骤是返回 service worker
registration 的序列化的 scope url。
registration.scope
的值,例如从
navigator.serviceWorker.ready.then(registration => console.log(registration.scope))
获得,将是 "https://example.com/
"。
3.2.7. updateViaCache
updateViaCache
获取器步骤是返回
service worker
registration 的 update via cache mode。
3.2.8. update()
update()
方法步骤是:
-
让 registration 为 service worker registration。
-
让 newestWorker 为以 registration 作为参数运行 Get Newest Worker 算法的结果。
-
如果 newestWorker 为 null,返回一个被拒绝的 promise,拒绝理由为 "
InvalidStateError
"DOMException
,并中止这些步骤。 -
如果此对象的 relevant global object globalObject 是一个
ServiceWorkerGlobalScope
对象,且 globalObject 的关联 service worker 的 state 是 "installing
",返回一个被拒绝的 promise,拒绝理由为 "InvalidStateError
"DOMException
,并中止这些步骤。 -
让 promise 为一个 promise。
-
让 job 为使用 update、registration 的 storage key、registration 的 scope url、newestWorker 的 script url、promise 和此对象的 relevant settings object 运行 Create Job 的结果。
-
设置 job 的 worker type 为 newestWorker 的 type。
-
使用 job 调用 Schedule Job。
-
返回 promise。
3.2.9. unregister()
注:unregister()
方法注销 service worker registration。重要的是要注意,当前受控的 service worker client 的 active service worker 的 containing service worker
registration 在所有使用此 service worker registration 的 service worker
client(包括自身)卸载之前一直有效。也就是说,unregister()
方法只影响后续的导航。
unregister()
方法步骤是:
-
让 registration 为 service worker registration。
-
让 promise 为一个新的 promise。
-
让 job 为使用 unregister、registration 的 storage key、registration 的 scope url、null、promise 和此对象的 relevant settings object 运行 Create Job 的结果。
-
使用 job 调用 Schedule Job。
-
返回 promise。
3.2.10. 事件处理程序
以下是所有实现 ServiceWorkerRegistration
接口的对象必须支持的事件处理程序(及其对应的事件处理程序事件类型),作为事件处理程序 IDL 属性:
事件处理程序 | 事件处理程序事件类型 |
---|---|
onupdatefound
|
updatefound
|
3.3.
navigator.serviceWorker
partial interface Navigator { [SecureContext ,SameObject ]readonly attribute ServiceWorkerContainer serviceWorker ; };partial interface WorkerNavigator { [SecureContext ,SameObject ]readonly attribute ServiceWorkerContainer serviceWorker ; };
serviceWorker
getter
的步骤是返回与此对象关联的 ServiceWorkerContainer
对象。
3.4.
ServiceWorkerContainer
[SecureContext ,Exposed =(Window ,Worker )]interface :
ServiceWorkerContainer EventTarget {readonly attribute ServiceWorker ?controller ;readonly attribute Promise <ServiceWorkerRegistration >ready ; [NewObject ]Promise <ServiceWorkerRegistration >register ((TrustedScriptURL or USVString ),
scriptURL optional RegistrationOptions = {}); [
options NewObject ]Promise <(ServiceWorkerRegistration or undefined )>getRegistration (optional USVString = ""); [
clientURL NewObject ]Promise <FrozenArray <ServiceWorkerRegistration >>getRegistrations ();undefined startMessages (); // 事件attribute EventHandler oncontrollerchange ;attribute EventHandler onmessage ; // message 事件的 event.source 是 ServiceWorker 对象attribute EventHandler onmessageerror ; };
dictionary {
RegistrationOptions USVString ;
scope WorkerType = "classic";
type ServiceWorkerUpdateViaCache = "imports"; };
updateViaCache
用户代理在创建 Navigator
对象或 WorkerNavigator
对象时必须创建一个 ServiceWorkerContainer
对象,并将其与该对象关联。
ServiceWorkerContainer
提供了注册、注销和更新服务工作线程注册的功能,并提供对服务工作线程注册及其关联的服务工作线程状态的访问。
ServiceWorkerContainer
具有一个关联的服务工作线程客户端,它是一个服务工作线程客户端,其全局对象与从中检索 ServiceWorkerContainer
的 Navigator
对象或 WorkerNavigator
对象相关联。
ServiceWorkerContainer
对象具有一个关联的就绪 promise(一个promise 或
null)。它最初为 null。
ServiceWorkerContainer
对象具有一个名为客户端消息队列的任务源,最初为空。客户端消息队列可以启用或禁用,并且最初是禁用的。当 ServiceWorkerContainer
对象的客户端消息队列启用时,事件循环必须将其用作其任务源之一。当 ServiceWorkerContainer
对象的相关全局对象是 Window
对象时,在其客户端消息队列上排队的所有任务必须与其相关设置对象的关联文档相关联。
3.4.1. controller
controller
属性必须运行以下步骤:
-
如果 client 的活动服务工作线程为 null,则返回 null。
-
返回获取服务工作线程对象的结果,该对象表示此对象的相关设置对象中 client 的活动服务工作线程。
注意:如果请求是强制刷新(shift+refresh),则 navigator.serviceWorker.controller
返回 null
。
3.4.2.
ready
ready
属性必须运行以下步骤:
-
如果此对象的就绪 promise 为 null,则将此对象的就绪 promise 设置为一个新的 promise。
-
令 readyPromise 为此对象的就绪 promise。
-
如果 readyPromise 处于 pending 状态,则并行运行以下子步骤:
-
令 storage key 为运行获取存储密钥(给定 client)的结果。
-
令 registration 为运行匹配服务工作线程注册(给定 storage key 和 client 的创建 URL)的结果。
-
如果 registration 不为 null,并且 registration 的活动工作线程不为 null,则在 readyPromise 的相关设置对象的负责事件循环上,使用DOM 操作任务源将任务排队,以使用获取服务工作线程注册对象(表示 readyPromise 的相关设置对象中的 registration)的结果来解析 readyPromise。
-
返回 readyPromise。
注意:返回的就绪 promise 永远不会拒绝。如果它在此算法中没有解析,它最终会在匹配的服务工作线程注册被注册并且其活动工作线程被设置时解析。(请参阅相关的激活算法步骤。)
3.4.3. register(scriptURL, options)
注意:register(scriptURL, options)
方法为给定的作用域 URL
创建或更新服务工作线程注册。如果成功,服务工作线程注册会将提供的
scriptURL
绑定到一个作用域 URL,该 URL
随后用于导航匹配。
register(scriptURL, options)
方法的步骤是:
-
令 p 为一个promise。
-
将 scriptURL 设置为调用获取受信任类型兼容字符串的结果,参数为
TrustedScriptURL
、此对象的相关全局对象、scriptURL、"ServiceWorkerContainer register" 和 "script"。 -
令 scriptURL 为解析 scriptURL(使用此对象的相关设置对象的API 基础 URL)的结果。
-
令 scopeURL 为 null。
-
如果 options["
scope
"] 存在,则将 scopeURL 设置为解析 options["scope
"](使用此对象的相关设置对象的API 基础 URL)的结果。 -
调用开始注册,参数为 scopeURL、scriptURL、p、client、client 的创建 URL、options["
type
"] 和 options["updateViaCache
"]。 -
返回 p。
3.4.4. getRegistration(clientURL)
getRegistration(clientURL)
方法的步骤是:
-
令 storage key 为运行获取存储密钥(给定 client)的结果。
-
令 clientURL 为解析 clientURL(使用此对象的相关设置对象的API 基础 URL)的结果。
-
如果 clientURL 解析失败,则返回一个被
TypeError
拒绝的promise。 -
将 clientURL 的片段设置为 null。
-
如果 clientURL 的源不是 client 的源,则返回一个被 "
SecurityError
"DOMException
拒绝的 promise。 -
令 promise 为一个新的promise。
-
并行运行以下子步骤:
-
令 registration 为运行匹配服务工作线程注册(给定 storage key 和 clientURL)的结果。
-
如果 registration 为 null,则使用 undefined 解析 promise 并中止这些步骤。
-
使用获取服务工作线程注册对象(表示 promise 的相关设置对象中的 registration)的结果来解析 promise。
-
-
返回 promise。
3.4.5. getRegistrations()
getRegistrations()
方法的步骤是:
-
令 client storage key 为运行获取存储密钥(给定 client)的结果。
-
令 promise 为一个新的 promise。
-
并行运行以下步骤:
-
令 registrations 为一个新的列表。
-
-
返回 promise。
3.4.6. startMessages()
3.4.7. 事件处理程序
以下是所有实现 ServiceWorkerContainer
接口的对象必须支持的事件处理程序(及其对应的事件处理程序事件类型),作为事件处理程序 IDL 属性:
事件处理程序 | 事件处理程序事件类型 |
---|---|
oncontrollerchange
|
controllerchange
|
onmessage
|
message
|
onmessageerror
|
messageerror
|
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) 。
|
3.6.
NavigationPreloadManager
[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
3.6.1.
enable()
enable()
方法的步骤是:
-
令 promise 为一个新的 promise。
-
并行运行以下步骤:
-
如果 registration 的活动工作线程为 null,则用 "
InvalidStateError
"DOMException
拒绝 promise,并中止这些步骤。 -
设置 registration 的导航预加载启用标志。
-
用 undefined 解析 promise。
-
返回 promise。
3.6.2. disable()
disable()
方法的步骤是:
-
令 promise 为一个新的 promise。
-
并行运行以下步骤:
-
如果 registration 的活动工作线程为 null,则用 "
InvalidStateError
"DOMException
拒绝 promise,并中止这些步骤。 -
取消设置 registration 的导航预加载启用标志。
-
用 undefined 解析 promise。
-
返回 promise。
3.6.3. setHeaderValue(value)
setHeaderValue(value)
方法的步骤是:
-
令 promise 为一个新的 promise。
-
并行运行以下步骤:
-
如果 registration 的活动工作线程为 null,则用 "
InvalidStateError
"DOMException
拒绝 promise,并中止这些步骤。 -
将 registration 的导航预加载标头值设置为 value。
-
用 undefined 解析 promise。
-
返回 promise。
3.6.4. getState()
getState()
方法的步骤是:
-
令 promise 为一个新的 promise。
-
并行运行以下步骤:
-
令 state 为一个新的
NavigationPreloadState
字典。 -
将 state["
headerValue
"] 设置为 registration 的导航预加载标头值。 -
用 state 解析 promise。
-
返回 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
4.1.2. registration
registration
getter
的步骤是返回获取服务工作线程注册对象的结果,该对象表示此对象的服务工作线程的包含服务工作线程注册在此对象的相关设置对象中。
4.1.3. serviceWorker
serviceWorker
getter
的步骤是返回获取服务工作线程对象的结果,该对象表示此对象的服务工作线程在此对象的相关设置对象中。
4.1.4. skipWaiting()
注意:skipWaiting()
方法允许此服务工作线程从注册的等待位置前进到活动位置,即使服务工作线程客户端正在使用该注册。
skipWaiting()
方法的步骤是:
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
4.2.2.
frameType
4.2.3.
id
4.2.4.
type
type
getter 的步骤是:
4.2.5.
postMessage(message, transfer)
postMessage(message, transfer)
方法的步骤是:
-
令 options 为 «[ "transfer" → transfer ]»。
-
使用 message 和 options 作为参数调用
postMessage(message, options)
。
4.2.6.
postMessage(message, options)
postMessage(message, options)
方法的步骤是:
-
令 contextObject 为此对象。
-
令 sourceSettings 为 contextObject 的相关设置对象。
-
令 serializeWithTransferResult 为 StructuredSerializeWithTransfer(message, options["
transfer
"])。 重新抛出任何异常。 -
并行运行以下步骤:
-
令 targetClient 为 null。
-
对于每个服务工作线程客户端 client:
-
如果 targetClient 为 null,则返回。
-
令 destination 为其关联的服务工作线程客户端为 targetClient 的
ServiceWorkerContainer
对象。 -
向 destination 的客户端消息队列添加一个运行以下步骤的任务:
-
令 source 为获取服务工作线程对象的结果,该对象表示 contextObject 的相关全局对象的服务工作线程在 targetClient 中。
-
令 deserializeRecord 为 StructuredDeserializeWithTransfer(serializeWithTransferResult, destination 的相关 Realm)。
如果此操作引发异常,则捕获该异常,使用
MessageEvent
在 destination 处触发名为messageerror
的事件,并将origin
属性初始化为 origin,将source
属性初始化为 source,然后中止这些步骤。 -
令 messageClone 为 deserializeRecord.[[Deserialized]]。
-
令 newPorts 为一个新的冻结数组,其中包含 deserializeRecord.[[TransferredValues]] 中的所有
MessagePort
对象(如果存在)。 -
使用
MessageEvent
在 destination 处分派名为message
的事件,并将origin
属性初始化为 origin,将source
属性初始化为 source,将data
属性初始化为 messageClone,并将ports
属性初始化为 newPorts。
-
4.2.7.
visibilityState
4.2.8.
focused
4.2.9.
ancestorOrigins
4.2.10.
focus()
focus()
方法的步骤是:
4.2.11.
navigate(url)
navigate(url)
方法的步骤是:
-
令 url 为使用此对象的相关设置对象的API 基本 URL 解析 url 的结果。
-
如果 url 解析失败,则返回一个用
TypeError
拒绝的 promise。 -
如果 url 是
about:blank
,则返回一个用TypeError
拒绝的 promise。 -
如果此对象关联的服务工作线程客户端的活动服务工作线程不是此对象的相关全局对象的服务工作线程,则返回一个用
TypeError
拒绝的 promise。 -
令 promise 为一个新的promise。
-
使用用户交互任务源,在此对象关联的服务工作线程客户端的负责事件循环上排队一个任务以运行以下步骤:
-
如果 browsingContext 的关联文档未完全激活,则使用DOM 操作任务源在 serviceWorkerEventLoop 上排队一个任务以用
TypeError
拒绝 promise,并中止这些步骤。 -
HandleNavigate:使用 browsingContext 的关联文档,将 browsingContext 导航到 url,并将exceptionsEnabled 设置为 true。
-
如果在标记为 HandleNavigate 的步骤中调用的算法步骤抛出异常,则使用DOM 操作任务源在 serviceWorkerEventLoop 上排队一个任务以用该异常拒绝 promise,并中止这些步骤。
-
令 frameType 为使用 browsingContext 运行获取框架类型的结果。
-
令 visibilityState 为 browsingContext 的活动文档的
visibilityState
属性值。 -
令 ancestorOriginsList 为 browsingContext 的活动文档的相关全局对象的
Location
对象的祖先源列表的关联列表。
-
返回 promise。
4.3.
Clients
接口
[Exposed =ServiceWorker ]interface { // 返回的对象每次都将是新的实例 [
Clients NewObject ]Promise <(Client or undefined )>get (DOMString ); [
id NewObject ]Promise <FrozenArray <Client >>matchAll (optional ClientQueryOptions = {}); [
options NewObject ]Promise <WindowClient ?>openWindow (USVString ); [
url NewObject ]Promise <undefined >claim (); };
dictionary {
ClientQueryOptions boolean =
includeUncontrolled false ;ClientType = "window"; };
type
enum {
ClientType ,
"window" ,
"worker" ,
"sharedworker" };
"all"
用户代理在创建 ServiceWorkerGlobalScope
对象时必须创建一个 Clients
对象,并将其与该对象关联。
4.3.1.
get(id)
get(id)
方法的步骤是:
4.3.2.
matchAll(options)
matchAll(options)
方法的步骤是:
-
令 promise 为一个新的 promise。
-
并行运行以下步骤:
-
令 targetClients 为一个新的列表。
-
对于每个服务工作线程客户端 client,其中运行获取存储密钥(给定 client)的结果等于关联的服务工作线程的包含的服务工作线程注册的存储密钥:
-
令 matchedWindowData 为一个新的列表。
-
令 matchedClients 为一个新的列表。
-
对于 targetClients 中的每个服务工作线程客户端 client:
-
如果 options["
type
"] 是"window"
或"all"
,并且 client 不是环境设置对象或者是窗口客户端,则:-
令 windowData 为 «[ "client" → client, "ancestorOriginsList" → 一个新的列表 ]»。
-
令 browsingContext 为 null。
-
令 isClientEnumerable 为 true。
-
如果 client 是一个环境设置对象,则将 browsingContext 设置为 client 的全局对象的浏览上下文。
-
否则,将 browsingContext 设置为 client 的目标浏览上下文。
-
使用用户交互任务源,在 browsingContext 的事件循环上排队一个任务 task 以运行以下子步骤:
-
如果 browsingContext 已被丢弃,则将 isClientEnumerable 设置为 false 并中止这些步骤。
-
如果 client 是一个窗口客户端并且 client 的关联文档不是 browsingContext 的活动文档,则将 isClientEnumerable 设置为 false 并中止这些步骤。
-
将 windowData["
frameType
"] 设置为使用 browsingContext 运行获取框架类型的结果。 -
将 windowData["
visibilityState
"] 设置为 browsingContext 的活动文档的visibilityState
属性值。 -
将 windowData["
focusState
"] 设置为使用 browsingContext 的活动文档作为参数运行具有焦点步骤的结果。 -
如果 client 是一个窗口客户端,则将 windowData["
ancestorOriginsList
"] 设置为 browsingContext 的活动文档的相关全局对象的Location
对象的祖先源列表的关联列表。
-
-
等待 task 执行完毕。
注意:等待是阻塞等待,但实现者可以并行运行迭代,只要状态不被破坏。
-
如果 isClientEnumerable 为 true,则:
-
将 windowData 添加到 matchedWindowData。
-
-
-
否则,如果 options["
type
"] 是"worker"
或"all"
并且 client 是一个专用工作线程客户端,或者 options["type
"] 是"sharedworker"
或"all"
并且 client 是一个共享工作线程客户端,则:-
将 client 添加到 matchedClients。
-
-
-
-
返回 promise。
4.3.3.
openWindow(url)
openWindow(url)
方法的步骤如下:
-
令 url 为使用此对象的相关设置对象的API 基本 URL 解析 url 的结果。
-
如果 url 解析失败,则返回一个用
TypeError
拒绝的 promise。 -
如果 url 是
about:blank
,则返回一个用TypeError
拒绝的 promise。 -
如果此源中没有
Window
具有瞬时激活,则返回一个用 "InvalidAccessError
"DOMException
拒绝的 promise。 -
令 promise 为一个新的promise。
-
并行运行这些子步骤:
-
令 newContext 为一个新的顶级浏览上下文。
-
使用用户交互任务源,在 newContext 的
Window
对象的环境设置对象的负责事件循环上排队一个任务以运行以下步骤:-
HandleNavigate:将 newContext 导航到 url,并将exceptionsEnabled 设置为 true,historyHandling 设置为 "
replace
"。 -
如果在标记为 HandleNavigate 的步骤中调用的算法步骤抛出异常,则使用DOM 操作任务源在 serviceWorkerEventLoop 上排队一个任务以用该异常拒绝 promise,并中止这些步骤。
-
令 frameType 为使用 newContext 运行获取框架类型的结果。
-
令 visibilityState 为 newContext 的活动文档的
visibilityState
属性值。 -
令 ancestorOriginsList 为 newContext 的活动文档的相关全局对象的
Location
对象的祖先源列表的关联列表。
-
-
-
返回 promise。
4.3.4.
claim()
claim()
方法的步骤如下:
-
如果服务工作线程不是活动工作线程,则返回一个用 "
InvalidStateError
"DOMException
拒绝的 promise。 -
令 promise 为一个新的promise。
-
并行运行以下子步骤:
-
对于每个服务工作线程客户端 client,其中运行获取存储密钥(给定 client)的结果等于服务工作线程的包含的服务工作线程注册的存储密钥:
-
令 storage key 为运行获取存储密钥(给定 client)的结果。
-
令 registration 为运行匹配服务工作线程注册(给定 storage key 和 client 的创建 URL)的结果。
-
如果 registration 不是服务工作线程的包含的服务工作线程注册,则继续。
注意:如果服务工作线程的包含的服务工作线程注册已注销,则 registration 将为 null。
-
如果 client 的活动服务工作线程不是服务工作线程,则:
-
使用 client 作为参数调用处理服务工作线程客户端卸载。
-
使用 client 作为参数调用通知控制器更改算法。
-
-
用 undefined 解析 promise。
-
-
返回 promise。
4.4.
ExtendableEvent
接口
[Exposed =ServiceWorker ]interface :
ExtendableEvent Event {(
constructor DOMString ,
type optional ExtendableEventInit = {});
eventInitDict undefined waitUntil (Promise <any >); };
f
dictionary :
ExtendableEventInit EventInit { // 为派生事件的向前兼容性定义 };
一个 ExtendableEvent
对象有一个关联的延长生命周期 promise(一个promise数组)。它最初是一个空数组。
一个 ExtendableEvent
对象有一个关联的待处理 promise 计数(延长生命周期 promise中待处理 promise
的数量)。它最初设置为零。
一个 ExtendableEvent
对象有一个关联的超时标志。它最初未设置,如果在待处理
promise 计数大于零的情况下,经过可选的用户代理施加的延迟后设置。
当一个 ExtendableEvent
对象的超时标志未设置,并且其待处理
promise 计数大于零或其分派标志已设置时,称该对象是活动的。
服务工作线程有两个生命周期事件,install
和 activate
。服务工作线程对
activate
事件和 install
事件使用 ExtendableEvent
接口。
服务工作线程扩展定义事件处理程序时可以使用或扩展 ExtendableEvent
接口。
4.4.1.
event.waitUntil(f)
注意:waitUntil()
方法延长事件的生命周期。
ExtendableEvent
)中的
promise(一个promise),请运行以下步骤:
-
如果 event 的
isTrusted
属性为 false,则抛出一个 "InvalidStateError
"DOMException
。 -
如果 event 不是活动的,则抛出一个 "
InvalidStateError
"DOMException
。注意:如果在调用事件处理程序的任务中没有添加生命周期延长 promise,则在后续异步任务中调用
waitUntil()
将会抛出异常。 -
将 promise 添加到 event 的延长生命周期 promise中。
-
将 event 的待处理 promise 计数增加一。
注意:即使给定的 promise 已经解决,待处理 promise 计数也会增加。相应的计数减少是在对 promise 的反应所排队的微任务中完成的。
-
在 promise 兑现或拒绝时,排队一个微任务以运行这些子步骤:
-
将 event 的待处理 promise 计数减少一。
-
如果 event 的待处理 promise 计数为 0,则:
-
如果服务工作线程没有待处理事件对该服务工作线程返回 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)
方法的步骤是:
-
如果 rules 是一个
RouterRule
字典,则将 rules 设置为 « rules »。 -
对于 rules 中的每个 rule:
-
如果使用 rule["
condition
"] 和 serviceWorker 运行验证路由器条件算法返回 false,则返回一个被拒绝的 promise,其值为TypeError
。 -
如果 rule["
source
"] 是 "fetch-event
" 或 "race-network-and-fetch-handler
",并且 serviceWorker 的要处理的事件类型集合不包含fetch
,则返回一个被拒绝的 promise,其值为TypeError
。
-
-
令 lifetimePromise 为一个新的promise。
-
将生命周期 promise 添加 lifetimePromise 到此对象。
注意:
event.addRoutes(rules)
默认延长事件的生命周期,就像调用了event.waitUntil(promise)
一样。 -
令 promise 为一个新的promise。
-
在 promise 兑现或拒绝时,用 undefined 解析 lifetimePromise。
注意:此步骤是为了使 lifetimePromise 始终被兑现,以避免安装事件失败。
-
将以下步骤加入队列到[[service worker queue]]:
-
返回 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
4.6.7.
event.respondWith(r)
注意:开发者可以使用一个解析为 Response
对象的 promise 或一个 Response
对象(它会自动转换为 promise)来设置参数 r。否则,将向 Fetch
返回一个网络错误。关于跨域内容污染的渲染器端安全检查与 Fetch
中定义的过滤响应类型相关联。
respondWith(r)
方法的步骤是:
-
令 event 为此对象。
-
如果 event 的分派标志未设置,则抛出一个 "
InvalidStateError
"DOMException
。 -
如果 event 的respond-with 进入标志已设置,则抛出一个 "
InvalidStateError
"DOMException
。 -
将生命周期 promise 添加 r 到 event。
注意:
event.respondWith(r)
默认延长事件的生命周期,就像调用了event.waitUntil(r)
一样。 -
设置 event 的respond-with 进入标志。
-
设置 event 的等待响应标志。
-
令 targetRealm 为 event 的相关 Realm。
-
在 r 被拒绝时:
-
设置 event 的respond-with 错误标志。
-
取消设置 event 的等待响应标志。
-
-
在 r 以 response 兑现时:
-
如果 response 不是一个
Response
对象,则设置respond-with 错误标志。注意:如果respond-with 错误标志已设置,则通过处理 Fetch 算法将网络错误返回给 Fetch。(参见步骤 21.1。)否则,通过处理 Fetch 算法将值 response 返回给 Fetch。(参见步骤 22.1。)
-
否则:
-
令 bytes 为一个空字节序列。
-
令 end-of-body 为 false。
-
令 done 为 false。
-
如果 response 的主体非空,则运行这些子步骤:
-
令 pullAlgorithm 为运行这些步骤的操作:
-
给定 readRequest,从 reader 读取一个块。
-
令 cancelAlgorithm 为取消 reader 的操作。
-
令 highWaterMark 为用户代理选择的非负、非 NaN 数字。
-
令 sizeAlgorithm 为一个接受块对象并返回用户代理选择的非负、非 NaN、非无穷大数字的算法。
-
令 newStream 为一个新的
ReadableStream
,在 targetRealm 中使用pullAlgorithm pullAlgorithm、cancelAlgorithm cancelAlgorithm、highWaterMark highWaterMark 和sizeAlgorithm sizeAlgorithm 设置。 -
当 done 为 false 时,并行重复运行这些子步骤:
-
如果 newStream 出错,则将 done 设置为 true。
-
否则,如果 bytes 为空且 end-of-body 为 true,则关闭 newStream 并将 done 设置为 true。
-
否则,如果 bytes 不为空,则运行这些子子步骤:
-
令 chunk 为从 bytes 开头开始的 bytes 的子序列。
-
从 bytes 中移除 chunk。
-
令 buffer 为在 targetRealm 中创建并包含 chunk 的
ArrayBuffer
对象。 -
将一个在 targetRealm 中创建并包装 buffer 的
Uint8Array
对象加入队列到 newStream。
-
-
注意:这些子步骤旨在产生与将 response 的主体的流“管道化”到 potentialResponse 相同的可观察结果。
注意:service worker 以块形式写入的数据不保证接收数据的客户端以相同的块形式读取。也就是说,客户端将读取写入的相同数据,但浏览器可能会以不同的方式对其进行分块。
-
将 event 的潜在响应设置为 potentialResponse。
-
-
取消设置 event 的等待响应标志。
-
4.7.
ExtendableMessageEvent
接口
[Exposed =ServiceWorker ]interface :
ExtendableMessageEvent ExtendableEvent {(
constructor DOMString ,
type optional ExtendableMessageEventInit = {});
eventInitDict readonly attribute any data ;readonly attribute USVString origin ;readonly attribute DOMString lastEventId ; [SameObject ]readonly attribute (Client or ServiceWorker or MessagePort )?source ;readonly attribute FrozenArray <MessagePort >ports ; };
dictionary :
ExtendableMessageEventInit ExtendableEventInit {any =
data null ;USVString = "";
origin DOMString = ""; (
lastEventId Client or ServiceWorker or MessagePort )?=
source null ;sequence <MessagePort >= []; };
ports
服务工作线程定义了可扩展的 message
事件,以允许延长事件的生命周期。对于 message
事件,服务工作线程使用
ExtendableMessageEvent
接口,该接口扩展了 ExtendableEvent
接口。
4.7.1.
event.data
data
属性必须返回其初始化时的值。创建对象时,此属性必须初始化为 null。它表示正在发送的消息。
4.7.2.
event.origin
origin
属性必须返回其初始化时的值。创建对象时,此属性必须初始化为空字符串。它表示发送消息的服务工作线程客户端的源。
4.7.3. event.lastEventId
lastEventId
属性必须返回其初始化时的值。创建对象时,此属性必须初始化为空字符串。
4.7.4.
event.source
source
属性必须返回其初始化时的值。创建对象时,此属性必须初始化为 null。它表示从中发送消息的 Client
对象。
4.7.5.
event.ports
ports
属性必须返回其初始化时的值。创建对象时,此属性必须初始化为空数组。它表示正在发送的 MessagePort
数组。
4.8. 事件
以下事件(称为服务工作线程事件)在 ServiceWorkerGlobalScope
对象上分派:
事件名称 | 接口 | 类别 | 分派时机… |
---|---|---|---|
install
|
InstallEvent
|
生命周期 | 服务工作线程的所属服务工作线程注册的安装中工作线程发生更改时。(参见安装算法的步骤 11.2。) |
activate
|
ExtendableEvent
|
生命周期 | 服务工作线程的所属服务工作线程注册的活动工作线程发生更改时。(参见激活算法的步骤 12.2。) |
fetch
|
FetchEvent
|
功能性 | http fetch 调用处理 Fetch 并传入 request
时。执行处理 Fetch
的结果是,服务工作线程向 http
fetch 返回一个响应。该响应(由 Response
对象表示)可以从 Cache
对象中检索,也可以使用 self.fetch(input, init)
方法直接从网络获取。(自定义 Response
对象是另一种选择。) |
push | PushEvent
|
功能性 | (参见触发推送事件。) |
notificationclick | NotificationEvent
|
功能性 | (参见激活通知。) |
notificationclose | NotificationEvent
|
功能性 | (参见关闭通知。) |
sync | SyncEvent
|
功能性 | (参见触发同步事件。) |
canmakepayment | CanMakePaymentEvent | 功能性 | (参见处理 CanMakePaymentEvent。) |
paymentrequest | PaymentRequestEvent | 功能性 | (参见处理 PaymentRequestEvent。) |
message
|
ExtendableMessageEvent
|
旧版 | 当它收到一条消息时。 |
messageerror
|
MessageEvent
|
旧版 | 当它收到一条无法反序列化的消息时。 |
5. 缓存
为了让开发者能够完全管理其内容缓存以供离线使用,Window
和 WorkerGlobalScope
提供了异步缓存方法,用于打开和操作 Cache
对象。一个源可以拥有多个命名的 Cache
对象,其内容完全由脚本控制。缓存不在源之间共享,并且它们与浏览器的 HTTP 缓存完全隔离。
5.1. 构造
请求响应列表是一个由请求(一个请求)和响应(一个响应)组成的列表的元组。
相关请求响应列表是此对象表示的实例。
5.2. 理解缓存生命周期
Cache
实例不是浏览器
HTTP 缓存的一部分。Cache
对象正是开发者必须自己管理的内容。Cache
对象除非开发者明确请求,否则不会更新。Cache
对象除非开发者删除条目,否则不会过期。Cache
对象不会仅仅因为服务工作线程脚本更新而消失。也就是说,缓存不会自动更新。更新必须手动管理。这意味着开发者应该按名称对缓存进行版本控制,并确保仅从能够安全操作该缓存的服务工作线程版本中使用缓存。
5.3. self.caches
partial interface mixin WindowOrWorkerGlobalScope { [SecureContext ,SameObject ]readonly attribute CacheStorage caches ; };
5.3.1.
caches
caches
获取器步骤是返回此对象的关联 CacheStorage
对象。
5.4. Cache
接口
[SecureContext ,Exposed =(Window ,Worker )]interface { [
Cache NewObject ]Promise <(Response or undefined )>match (RequestInfo ,
request optional CacheQueryOptions = {}); [
options NewObject ]Promise <FrozenArray <Response >>matchAll (optional RequestInfo ,
request optional CacheQueryOptions = {}); [
options NewObject ]Promise <undefined >add (RequestInfo ); [
request NewObject ]Promise <undefined >addAll (sequence <RequestInfo >); [
requests NewObject ]Promise <undefined >put (RequestInfo ,
request Response ); [
response NewObject ]Promise <boolean >delete (RequestInfo ,
request optional CacheQueryOptions = {}); [
options NewObject ]Promise <FrozenArray <Request >>keys (optional RequestInfo ,
request optional CacheQueryOptions = {}); };
options
dictionary {
CacheQueryOptions boolean =
ignoreSearch false ;boolean =
ignoreMethod false ;boolean =
ignoreVary false ; };
Cache
对象表示一个请求响应列表。跨文档和工作线程的多个实现 Cache
接口的独立对象可以同时与同一个请求响应列表相关联。
缓存批量操作是一个结构体,包含:
-
类型(“
delete
”或“put
”)。 -
请求(一个请求)。
-
响应(一个响应)。
-
选项(一个
CacheQueryOptions
)。
5.4.1.
match(request, options)
match(request, options)
方法的步骤是:
-
令 promise 为一个新的 promise。
-
并行运行这些子步骤:
-
令 p 为使用 request 和 options 运行
matchAll(request, options)
方法中指定的算法的结果。 -
等待 p 稳定。
-
如果 p 因异常而被拒绝,则:
-
用该异常拒绝 promise。
-
-
否则,如果 p 以数组 responses 解析,则:
-
如果 responses 是一个空数组,则:
-
用 undefined 解析 promise。
-
-
否则:
-
用 responses 的第一个元素解析 promise。
-
-
-
-
返回 promise。
5.4.2.
matchAll(request, options)
matchAll(request, options)
方法的步骤是:
-
令 r 为 null。
-
如果可选参数 request 未省略,则:
-
如果 request 是一个
Request
对象,则:-
将 r 设置为 request 的请求。
-
如果 r 的方法不是 `
GET
` 且 options.ignoreMethod 为 false,则返回一个已解析的 promise,其值为空数组。
-
-
否则,如果 request 是一个字符串,则:
-
将 r 设置为以 request 作为参数调用
Request
初始值作为构造函数的结果的关联请求。如果此操作抛出异常,则返回一个被拒绝的 promise,其值为该异常。
-
-
-
令 promise 为一个新的 promise。
-
并行运行这些子步骤:
-
返回 promise。
5.4.3.
add(request)
add(request)
方法的步骤是:
-
令 requests 为一个仅包含 request 的数组。
-
令 responseArrayPromise 为以 requests 作为参数运行
addAll(requests)
中指定的算法的结果。 -
返回对 responseArrayPromise 作出反应的结果,该反应带有一个返回 undefined 的履行处理程序。
5.4.4.
addAll(requests)
addAll(requests)
方法的步骤是:
-
令 responsePromises 为一个空列表。
-
令 requestList 为一个空列表。
-
对于 requests 中类型为
Request
的每个 request:-
令 r 为 request 的请求。
-
如果 r 的URL 的方案不是“
http
”和“https
”之一,或者 r 的方法不是 `GET
`,则返回一个被拒绝的 promise,其值为TypeError
。
-
-
对于 requests 中的每个 request:
-
令 r 为以 request 作为参数调用
Request
初始值作为构造函数的结果的关联请求。如果此操作抛出异常,则返回一个被拒绝的 promise,其值为该异常。 -
如果 r 的URL 的方案不是“
http
”和“https
”之一,则:-
对于 fetchControllers 中的每个 fetchController,中止 fetchController。
-
返回一个被拒绝的 promise,其值为
TypeError
。
-
-
如果 r 的客户端的全局对象是一个
ServiceWorkerGlobalScope
对象,则将 request 的服务工作线程模式设置为“none
”。 -
将 r 添加到 requestList。
-
令 responsePromise 为一个新的 promise。
-
并行运行以下子步骤:
-
为 response 处理响应,运行这些子步骤:
-
为 response 处理响应体结束,运行这些子步骤:
-
如果 response 的中止标志已设置,则用“
AbortError
”DOMException
拒绝 responsePromise 并中止这些步骤。 -
用 response 解析 responsePromise。
注意:当响应的主体完全接收后,才允许缓存提交。
-
-
将 responsePromise 添加到 responsePromises。
-
-
令 p 为获取一个等待所有 responsePromises 的 promise 的结果。
-
返回对 p 作出反应的结果,该反应带有一个履行处理程序,当使用参数 responses 调用该处理程序时,执行以下子步骤:
-
令 operations 为一个空列表。
-
令 index 为零。
-
对于 responses 中的每个 response:
-
令 cacheJobPromise 为一个新的 promise。
-
并行运行以下子步骤:
-
返回 cacheJobPromise。
-
5.4.5.
put(request, response)
put(request, response)
方法的步骤是:
-
令 innerRequest 为 null。
-
否则:
-
令 requestObj 为以 request 作为参数调用
Request
的构造函数的结果。如果此操作抛出 exception,则返回一个被拒绝的 promise,其值为 exception。 -
将 innerRequest 设置为 requestObj 的请求。
-
-
如果 innerRequest 的URL 的方案不是“
http
”和“https
”之一,或者 innerRequest 的方法不是 `GET
`,则返回一个被拒绝的 promise,其值为TypeError
。 -
令 innerResponse 为 response 的响应。
-
如果 innerResponse 的状态是
206
,则返回一个被拒绝的 promise,其值为TypeError
。 -
如果 innerResponse 的主体被扰动或被锁定,则返回一个被拒绝的 promise,其值为
TypeError
。 -
令 clonedResponse 为 innerResponse 的克隆。
-
令 bodyReadPromise 为一个已解析的 promise,其值为 undefined。
-
如果 innerResponse 的主体非空,则运行这些子步骤:
注意:这确保了 innerResponse 的主体被锁定,并且我们在 clonedResponse 中拥有主体的完整缓冲副本。实现可以通过直接流式传输到磁盘而不是内存来进行优化。
-
令 operations 为一个空列表。
-
令 operation 为一个缓存批量操作。
-
将 operation 的类型设置为“
put
”。 -
将 operation 的请求设置为 innerRequest。
-
将 operation 的响应设置为 clonedResponse。
-
追加 operation 到 operations。
-
返回 bodyReadPromise 的履行结果:
-
令 cacheJobPromise 为一个新的 promise。
-
返回 cacheJobPromise 并并行运行这些步骤:
-
5.4.6.
delete(request, options)
delete(request, options)
方法的步骤是:
-
令 r 为 null。
-
如果 request 是一个
Request
对象,则:-
将 r 设置为 request 的请求。
-
如果 r 的方法不是 `
GET
` 且 options.ignoreMethod 为 false,则返回一个已解析的 promise,其值为 false。
-
-
否则,如果 request 是一个字符串,则:
-
将 r 设置为以 request 作为参数调用
Request
初始值作为构造函数的结果的关联请求。如果此操作抛出异常,则返回一个被拒绝的 promise,其值为该异常。
-
-
令 operations 为一个空列表。
-
令 operation 为一个缓存批量操作。
-
将 operation 的类型设置为“
delete
”。 -
将 operation 的请求设置为 r。
-
将 operation 的选项设置为 options。
-
追加 operation 到 operations。
-
令 cacheJobPromise 为一个新的 promise。
-
并行运行以下子步骤:
-
返回 cacheJobPromise。
5.4.7.
keys(request, options)
keys(request, options)
方法的步骤是:
-
令 r 为 null。
-
如果可选参数 request 未省略,则:
-
如果 request 是一个
Request
对象,则:-
将 r 设置为 request 的请求。
-
如果 r 的方法不是 `
GET
` 且 options.ignoreMethod 为 false,则返回一个已解析的 promise,其值为空数组。
-
-
否则,如果 request 是一个字符串,则:
-
将 r 设置为以 request 作为参数调用
Request
初始值作为构造函数的结果的关联请求。如果此操作抛出异常,则返回一个被拒绝的 promise,其值为该异常。
-
-
-
令 promise 为一个新的 promise。
-
并行运行这些子步骤:
-
令 requests 为一个空列表。
-
如果可选参数 request 被省略,则:
-
否则:
-
-
返回 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 对象,但完全是异步的,并带有额外的便捷方法。这些方法,
clear
、forEach
、entries
和 values
,
由于 TC39 正在进行的关于异步迭代的讨论,被有意地排除在第一个版本的范围之外。
当创建 Window
对象或 WorkerGlobalScope
对象时,用户代理 必须 创建一个 CacheStorage
对象,并将其与该全局对象关联。
一个 CacheStorage
对象表示其关联的全局对象的环境设置对象的源的一个名称到缓存的映射。跨文档和工作者的多个实现
CacheStorage
接口的独立对象可以同时关联到同一个名称到缓存的映射。
5.5.1.
match(request, options)
方法
match(request, options)
方法的步骤是:
-
如果 options 中的 "
cacheName
" 存在,则:-
返回一个新的 promise promise 并并行运行以下子步骤:
-
对于相关的名称到缓存映射中的每一个 cacheName → cache 对:
-
如果 options 中的 "
cacheName
" 匹配 cacheName,则:-
使用在
Cache
接口的match(request, options)
方法中指定的算法运行的结果来解决 promise,使用 request 和 options 作为参数(并将 cache 作为match(request, options)
的\[[Call]]
内部方法的 thisArgument)。 -
中止这些步骤。
-
-
-
用 undefined 解决 promise。
-
-
-
否则:
-
令 promise 为一个已用 undefined 解决的 promise。
-
对于相关的名称到缓存映射中的每一个 cacheName → cache 对:
-
将 promise 设置为对自身作出反应的结果,该反应带有一个完成处理程序,当以参数 response 调用该处理程序时,执行以下子步骤:
-
如果 response 不是 undefined,则返回 response。
-
返回在
Cache
接口的match(request, options)
方法中指定的算法运行的结果,使用 request 和 options 作为参数(并将 cache 作为match(request, options)
的\[[Call]]
内部方法的 thisArgument)。
-
-
-
返回 promise。
-
5.5.2.
has(cacheName)
方法
has(cacheName)
方法的步骤是:
-
令 promise 为一个新的 promise。
-
并行运行以下子步骤:
-
对于相关的名称到缓存映射中的每一个 key → value 对:
-
如果 cacheName 匹配 key,则用 true 解决 promise 并中止这些步骤。
-
-
用 false 解决 promise。
-
-
返回 promise。
5.5.3.
open(cacheName)
方法
open(cacheName)
方法的步骤是:
-
令 promise 为一个新的 promise。
-
并行运行以下子步骤:
-
对于相关的名称到缓存映射中的每一个 key → value 对:
-
如果 cacheName 匹配 key,则:
-
用一个表示 value 的新的
Cache
对象来解决 promise。 -
中止这些步骤。
-
-
-
令 cache 为一个新的请求响应列表。
-
设置相关的名称到缓存映射中 cacheName 对应的值为 cache。如果此缓存写入操作因超出授予的配额限制而失败,则用一个 "
QuotaExceededError
"DOMException
来拒绝 promise 并中止这些步骤。 -
用一个表示 cache 的新的
Cache
对象来解决 promise。
-
-
返回 promise。
5.5.4.
delete(cacheName)
方法
delete(cacheName)
方法的步骤是:
-
令 promise 为使用 cacheName 运行
has(cacheName)
方法中指定的算法的结果。 -
返回对 promise 作出反应的结果,该反应带有一个完成处理程序,当以参数 cacheExists 调用该处理程序时,执行以下子步骤:
-
如果 cacheExists 为 false,则:
-
返回 false。
-
-
令 cacheJobPromise 为一个新的 promise。
-
并行运行以下子步骤:
-
移除相关的名称到缓存映射中 cacheName 对应的条目。
-
用 true 解决 cacheJobPromise。
注:此步骤之后,现有的 DOM 对象(即当前引用的 Cache、Request 和 Response 对象)应保持功能正常。
-
-
返回 cacheJobPromise。
-
5.5.5.
keys()
方法
keys()
方法的步骤是:
-
令 promise 为一个新的 promise。
-
并行运行以下子步骤:
-
令 cacheKeys 为获取相关的名称到缓存映射的键的结果。
-
用 cacheKeys 解决 promise。
-
-
返回 promise。
6. 安全注意事项
6.1. 安全上下文
服务工作线程必须在安全上下文中执行。服务工作线程客户端也必须是安全上下文才能注册服务工作线程注册,才能访问服务工作线程注册和服务工作线程,才能与服务工作线程进行消息传递,以及被服务工作线程操作。
注:这实际上意味着服务工作线程及其服务工作线程客户端需要通过
HTTPS 托管。用户代理可以允许将 localhost
(参见要求)、
127.0.0.0/8
和 ::1/128
用于开发目的。此限制的主要原因是为了保护用户免受与不安全上下文相关的风险。
6.2. 内容安全策略
每当用户代理使用服务工作线程 serviceWorker 调用运行服务工作线程算法时:
-
如果 serviceWorker 的脚本资源是通过包含值 policy 的
Content-Security-Policy
HTTP 标头传递的,则用户代理必须为 serviceWorker 强制执行 policy。 -
如果 serviceWorker 的脚本资源是通过包含值 policy 的
Content-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:
-
令 map 为 serviceWorker 的脚本资源映射。
-
令 url 为 request 的URL。
-
如果 serviceWorker 的状态不是 "
parsed
" 或 "installing
": -
如果 map[url] 存在:
-
将 url Append 到 serviceWorker 的 set of used scripts。
-
返回 map[url]。
-
-
令 registration 为 serviceWorker 的包含的服务工作线程注册。
-
将 request 的服务工作线程模式设置为 "
none
"。 -
如果以下任一情况为真,则将 request 的缓存模式设置为 "
no-cache
":-
registration 的通过缓存更新模式是 "
none
"。 -
当前全局对象的强制绕过导入脚本缓存标志已设置。
-
registration 已过时。
-
-
令 response 为获取 request 的结果。
-
如果 response 的缓存状态不是 "
local
",则将 registration 的上次更新检查时间设置为当前时间。 -
将 map[url] Set 为 response。
-
将 url Append 到 serviceWorker 的 set of used scripts。
-
设置 serviceWorker 的经典脚本导入标志。
-
返回 response。
6.4. 跨源资源和 CORS
本节是非规范性的。
应用程序倾向于缓存来自 CDN 或其他源的项目。可以使用
<script>
、<img>
、<video>
和
<link>
元素直接请求其中的许多项目。如果这种运行时协作在离线时中断,那将是极大的限制。类似地,当设置了适当的 CORS 标头时,可以获取多种类型的非源资源。服务工作线程通过允许 Caches
获取和缓存非源项目来实现这一点。但是,存在一些限制。首先,与在 Cache
中作为
Response
对象(其对应的响应是基本过滤响应)管理的同源资源不同,存储的对象是
Response
对象,其对应的响应是CORS 过滤响应或不透明过滤响应。它们可以像 Response
对象(其对应的响应是基本过滤响应)一样传递给 event.respondWith(r)
方法,但不能以有意义的方式以编程方式创建。这些限制对于维护平台的安全不变性是必要的。允许 Caches
存储它们可以使应用程序在大多数情况下避免重新架构。
6.5. 路径限制
本节是非规范性的。
除了源限制之外,服务工作线程还受到服务工作线程脚本的路径的限制。例如,位于
https://www.example.com/~bob/sw.js
的服务工作线程脚本可以注册到作用域 URL
https://www.example.com/~bob/
,但不能注册到作用域
https://www.example.com/
或
https://www.example.com/~alice/
。这为在同一源上的不同目录中托管多用户内容的站点提供了一些保护。但是,路径限制不被视为硬安全边界,只有源才是。鼓励站点在适当时使用不同的源来安全地隔离站点的段。
服务器可以通过在服务工作线程脚本上设置 Service-Worker-Allowed 标头来移除路径限制。
6.6. 服务工作线程脚本请求
本节是非规范性的。
为了进一步防范在站点上恶意注册服务工作线程,本规范要求:
-
Service-Worker 标头存在于服务工作线程脚本请求中,并且
-
服务工作线程脚本使用JavaScript MIME 类型提供服务。
6.7. 实现者注意事项
本节是非规范性的。
鼓励实现者注意:
6.8. 隐私
服务工作线程引入了新的持久存储功能,包括注册映射(用于服务工作线程注册及其服务工作线程)、请求响应列表和名称到缓存映射(用于缓存)以及脚本资源映射(用于脚本资源)。为了保护用户免受任何潜在的未经批准的跟踪威胁,当用户打算清除这些持久存储时,应该清除它们,并且应该维护并与现有用户控件(例如清除所有现有持久存储)进行互操作。
7. 可扩展性
服务工作线程规范可由其他规范扩展。
7.1. 定义绑定到服务工作线程注册的 API
规范可以通过对 ServiceWorkerRegistration
接口使用部分接口定义来定义与服务工作线程注册相关联的
API,其中它可以定义规范特定的属性和方法:
partial interface ServiceWorkerRegistration { // 例如,定义一个 API 命名空间readonly attribute APISpaceType APISpace ; // 例如,定义一个方法Promise <T >methodName (/* 参数列表 */); };
7.2. 定义功能性事件
规范可以通过扩展 ExtendableEvent
接口来定义功能性事件:
// 例如,定义 FunctionalEvent 接口interface FunctionalEvent :ExtendableEvent { // 添加功能性事件自身的属性和方法 };
7.3. 定义事件处理程序
规范可以使用部分接口定义为 ServiceWorkerGlobalScope
接口定义相应功能性事件的事件处理程序属性:
partial interface ServiceWorkerGlobalScope {attribute EventHandler onfunctionalevent ; };
7.4. 触发功能性事件
附录 A:算法
以下定义是用户代理在整个规范中使用的内部数据结构。
一个 注册映射 是一个 有序映射,其中键是(存储密钥,序列化的 作用域 URL),值是 服务工作者注册。
一个 任务 是对 服务工作者注册 的注册、更新和取消注册请求之一的抽象。
一个 任务 有一个 工作者类型(“classic
”或“module
”)。
一个 任务 有一个 通过缓存更新模式,它是“imports
”、
“all
”或“none
”。
一个 任务 有一个 客户端(一个 服务工作者客户端)。它最初为 null。
一个 任务 有一个 任务承诺(一个 承诺)。它最初为 null。
一个 任务 有一个 包含的任务队列(一个 任务队列 或 null)。它最初为 null。
一个 任务 有一个 等效任务列表(一个 任务列表)。它最初是空列表。
一个 任务 有一个 强制绕过缓存标志。它最初未设置。
当它们的 任务类型 相同并且满足以下条件时,两个 任务 是 等效的:
一个 任务队列 是一个线程安全的 队列,用于同步并发 任务集合。任务队列 包含 任务 作为其 项。一个 任务队列 最初为空。
一个 作用域到任务队列映射 是一个 有序映射,其中键是 作用域 URL(序列化的),值是 任务队列。
一个 错误的导入脚本响应 是一个 响应,满足以下任一条件:
-
响应的 类型是“
error
” -
从响应的 标头列表中提取 MIME 类型的结果不是 JavaScript MIME 类型
注意:保持此定义与获取经典工作者导入脚本同步。
创建任务
调度任务
- 输入
-
job,一个 任务
- 输出
-
无
-
令 jobQueue 为 null。
-
如果 作用域到任务队列映射[jobScope] 不存在,则设置 作用域到任务队列映射[jobScope] 为一个新的 任务队列。
-
将 jobQueue 设置为 作用域到任务队列映射[jobScope]。
-
如果 jobQueue 为空,则:
-
否则:
运行任务
- 输入
-
jobQueue,一个 任务队列
- 输出
-
无
完成任务
- 输入
-
job,一个 任务
- 输出
-
无
解决任务 Promise
- 输入
-
job,一个 任务
value,任何值
- 输出
-
无
拒绝任务 Promise
开始注册
- 输入
-
scopeURL,一个 URL 或失败或 null
scriptURL,一个 URL 或失败
promise,一个 承诺
client,一个 服务工作者客户端
referrer,一个 URL
workerType,一个 工作者类型
updateViaCache,一个 通过缓存更新模式
- 输出
-
无
-
如果 scriptURL 失败,则用
TypeError
拒绝 promise 并中止这些步骤。 -
将 scriptURL 的 片段设置为 null。
-
如果 scriptURL 的 方案不是“
http
”和“https
”之一,则用TypeError
拒绝 promise 并中止这些步骤。 -
如果 scriptURL 的 路径中的任何字符串包含ASCII 不区分大小写的“
%2f
”或ASCII 不区分大小写的“%5c
”,则用TypeError
拒绝 promise 并中止这些步骤。 -
如果 scopeURL 为 null,则将 scopeURL 设置为使用 scriptURL 解析字符串“
./
”的结果。注意:默认情况下,注册的作用域 URL 设置为服务工作者脚本的位置。
-
如果 scopeURL 失败,则用
TypeError
拒绝 promise 并中止这些步骤。 -
将 scopeURL 的 片段设置为 null。
-
如果 scopeURL 的 方案不是“
http
”和“https
”之一,则用TypeError
拒绝 promise 并中止这些步骤。 -
如果 scopeURL 的 路径中的任何字符串包含ASCII 不区分大小写的“
%2f
”或ASCII 不区分大小写的“%5c
”,则用TypeError
拒绝 promise 并中止这些步骤。 -
令 storage key 为给定 client 运行 获取存储密钥的结果。
-
令 job 为使用注册、storage key、scopeURL、scriptURL、promise 和 client 运行 创建任务的结果。
-
将 job 的 工作者类型设置为 workerType。
-
将 job 的 通过缓存更新模式设置为 updateViaCache。
-
将 job 的 引用者设置为 referrer。
-
使用 job 调用 调度任务。
注册
- 输入
-
job,一个 任务
- 输出
-
无
-
如果以 job 的 脚本 URL 的源作为参数运行潜在可信源的结果是
不可信
,则:-
使用 job 和“
SecurityError
”DOMException
调用拒绝任务承诺。 -
使用 job 调用完成任务并中止这些步骤。
-
-
如果 job 的 脚本 URL 的 源和 job 的 引用者的 源不是同源,则:
-
使用 job 和“
SecurityError
”DOMException
调用拒绝任务承诺。 -
使用 job 调用完成任务并中止这些步骤。
-
-
如果 job 的 作用域 URL 的 源和 job 的 引用者的 源不是同源,则:
-
使用 job 和“
SecurityError
”DOMException
调用拒绝任务承诺。 -
使用 job 调用完成任务并中止这些步骤。
-
-
如果 registration 不为 null,则:
-
否则:
-
以 job 作为参数调用更新算法。
更新
- 输入
-
job,一个 任务
- 输出
-
无
-
如果 registration 为 null,则:
-
令 newestWorker 为以 registration 作为参数运行获取最新工作者算法的结果。
-
如果 job 的 任务类型是更新,并且 newestWorker 不为 null 且其 脚本 URL 不等于 job 的 脚本 URL,则:
-
令 hasUpdatedResources 为 false。
-
根据 job 的 工作者类型,使用以下选项运行这些子步骤:
- "
classic
" -
给定 job 的 序列化的 脚本 URL、 job 的 客户端、“
serviceworker
”以及为此服务工作者创建的环境设置对象,获取经典工作者脚本。 - "
module
" -
给定 job 的 序列化的 脚本 URL、 job 的 客户端、“
serviceworker
”、 “omit
”以及为此服务工作者创建的环境设置对象,获取模块工作者脚本图。
使用待创建的环境设置对象而不是具体的环境设置对象。这是由于服务工作者与其他Web 工作者相比具有独特的处理模型。HTML 标准的脚本获取算法最初是为其他Web 工作者设计的,需要执行环境的环境设置对象,但服务工作者在更新算法中单独获取脚本,之后脚本通过运行服务工作者算法多次运行。
HTML 中的获取经典工作者脚本算法和获取模块工作者脚本图算法将 job 的 客户端作为参数。job 的 客户端在从软更新算法传递时为 null。
要给定 request 执行获取钩子,请运行以下步骤:
-
将 `
Service-Worker
`/`script
` 追加到 request 的 标头列表。注意:请参阅附录 B:扩展 HTTP 标头中 Service-Worker 标头的定义。
-
如果以下任一情况为真,则将 request 的 缓存模式设置为“
no-cache
”:注意:即使缓存模式未设置为“
no-cache
”,用户代理也会遵守网络层中 Cache-Control 标头的 max-age 值,以确定是否应绕过浏览器缓存。 -
将 request 的 服务工作者模式设置为“
none
”。 -
如果isTopLevel 标志未设置,则返回获取 request 的结果。
-
将 request 的 重定向模式设置为“
error
”。 -
从 response 的 标头列表中提取 MIME 类型。如果此 MIME 类型(忽略参数)不是JavaScript MIME 类型,则:
-
使用 job 和“
SecurityError
”DOMException
调用拒绝任务承诺。 -
使用网络错误异步完成这些步骤。
-
-
令 serviceWorkerAllowed 为给定 `
Service-Worker-Allowed
` 和 response 的 标头列表提取标头列表值的结果。注意:请参阅附录 B:扩展 HTTP 标头中Service-Worker-Allowed 标头的定义。
-
令 policyContainer 为给定 response 从获取响应创建策略容器的结果。
-
如果 serviceWorkerAllowed 失败,则:
-
使用网络错误异步完成这些步骤。
-
-
令 scopeURL 为 registration 的 作用域 URL。
-
令 maxScopeString 为 null。
-
如果 serviceWorkerAllowed 为 null,则:
-
否则:
-
令 scopeString 为“
/
”,后跟 scopeURL 的 路径中的字符串(包括空字符串),彼此用“/
”分隔。 -
如果 maxScopeString 为 null 或 scopeString 不以 maxScopeString 开头,则:
-
使用 job 和“
SecurityError
”DOMException
调用拒绝任务承诺。 -
使用网络错误异步完成这些步骤。
-
-
令 url 为 request 的 URL。
-
将 updatedResourceMap[url] 设置为 response。
-
如果 response 的 缓存状态不是“
local
”,则将 registration 的 上次更新检查时间设置为当前时间。 -
如果以下任一情况为真,则将 hasUpdatedResources 设置为 true:
-
如果 hasUpdatedResources 为 false 且 newestWorker 的 经典脚本导入标志已设置,则:
注意:以下检查以查看导入的脚本是否已更新,因为主脚本未更改。
-
对于 newestWorker 的 脚本资源映射中的每个 importUrl → storedResponse:
-
如果 importUrl 是 url,则继续。
-
令 importRequest 为一个新的请求,其URL 为 importUrl,客户端为 job 的 客户端,目标为 “
script
”,解析器元数据为“not parser-inserted
”,并且其使用 URL 凭据标志已设置。 -
如果以下任一情况为真,则将 importRequest 的 缓存模式设置为“
no-cache
”: -
令 fetchedResponse 为获取 importRequest 的结果。
-
将 updatedResourceMap[importRequest 的 URL] 设置为 fetchedResponse。
-
将 fetchedResponse 设置为 fetchedResponse 的 不安全响应。
-
如果 fetchedResponse 的 缓存状态不是“
local
”,则将 registration 的 上次更新检查时间设置为当前时间。 -
如果 fetchedResponse 是一个错误的导入脚本响应,则继续。
注意:对于逐字节检查,importScripts() 的错误响应将被忽略。仅考虑现有工作者的良好响应和潜在更新工作者的良好响应。有关一些基本原理,请参阅问题 #1374。
-
如果 fetchedResponse 的 主体与 storedResponse 的 不安全响应的 主体不是逐字节相同的,则将 hasUpdatedResources 设置为 true。
注意:此步骤中的控件不会中断循环,以继续处理所有导入的脚本以填充缓存。
-
-
-
使用 response 异步完成这些步骤。
当算法异步完成时,继续执行这些步骤的其余部分,其中 script 是异步完成值。
- "
-
如果 script 为 null 或使用 script 的 记录、script 的 基本 URL和 « » 是异步模块为 true,则:
-
使用 job 和
TypeError
调用拒绝任务承诺。注意:如果先前使用“
SecurityError
”DOMException
调用了拒绝任务承诺,则此操作将不执行任何操作。 -
如果 newestWorker 为 null,则移除 注册映射[(registration 的 存储密钥, 序列化的 scopeURL)]。
-
使用 job 调用完成任务并中止这些步骤。
-
-
如果 hasUpdatedResources 为 false,则:
-
令 worker 为一个新的服务工作者。
-
将 worker 的 脚本 URL 设置为 job 的 脚本 URL, worker 的 脚本资源设置为 script, worker 的 类型设置为 job 的 工作者类型,并将 worker 的 脚本资源映射设置为 updatedResourceMap。
-
将 url 追加到 worker 的 已用脚本集。
-
如果 job 的 强制绕过缓存标志已设置,则令 forceBypassCache 为 true,否则为 false。
-
令 runResult 为使用 worker 和 forceBypassCache 运行运行服务工作者算法的结果。
-
如果 runResult 是失败或突然完成,则:
-
否则,以 job、worker 和 registration 作为参数调用安装算法。
软更新
用户代理可以根据需要随时调用此方法来检查更新。
- 输入
-
registration,一个 服务工作者注册
forceBypassCache,一个可选布尔值,默认为 false
注意:实现者可以使用 forceBypassCache 来辅助调试(例如从开发人员工具调用),其他扩展服务工作者的规范也可以根据自身需要使用该标志。
- 输出
-
无
安装
-
令 installFailed 为 false。
-
令 newestWorker 为以 registration 作为参数运行获取最新工作者算法的结果。
-
以 registration、“
正在安装
”和 worker 作为参数运行更新注册状态算法。 -
断言:job 的 任务承诺不为 null。
-
使用 job 和 registration 调用解决任务承诺。
-
对于 settingsObjects 中的每个 settingsObject,在 settingsObject 的 负责的事件循环上的DOM 操作任务源中排队一个任务以运行以下步骤:
-
令 registrationObjects 为 settingsObject 的 领域中的每个
ServiceWorkerRegistration
对象,其服务工作者注册是 registration。 -
对于 registrationObjects 中的每个 registrationObject,在 registrationObject 上触发一个名为
updatefound
的事件。
-
-
令 installingWorker 为 registration 的 正在安装的工作者。
-
如果使用 installingWorker 和 “install” 运行应该跳过事件算法的结果为 false,则:
-
如果 job 的 强制绕过缓存标志已设置,则令 forceBypassCache 为 true,否则为 false。
-
如果使用 installingWorker 和 forceBypassCache 运行运行服务工作者算法的结果是失败, 则:
-
将 installFailed 设置为 true。
-
-
否则:
-
-
如果 installFailed 为 true,则:
-
对于 map 中的每个 url:
-
如果 registration 的 等待中的工作者不为 null,则:
-
以 registration、“
等待中
”和 registration 的 正在安装的工作者作为参数运行更新注册状态算法。 -
以 registration、“
正在安装
”和 null 作为参数运行更新注册状态算法。 -
使用 job 调用完成任务。
-
使用 registration 调用尝试激活。
注意:如果尝试激活此处未触发激活,则当由现有活动工作者控制的最后一个客户端卸载时,异步调用
skipWaiting()
时,或现有活动工作者的延长生命周期承诺解决时,将再次尝试激活。
激活
- 输入
-
registration,一个 服务工作者注册
- 输出
-
无
-
如果 registration 的 等待中工作者为 null,则中止这些步骤。
-
如果 registration 的 活动工作者不为 null,则:
-
以 registration、“
等待中
”和 null 作为参数运行更新注册状态算法。 -
以 registration 的 活动工作者和“
正在激活
”作为参数运行更新工作者状态算法。注意:一旦活动工作者正在激活,运行时脚本错误或强制终止活动工作者都不会阻止活动工作者被激活。
注意:确保将激活处理程序设计为执行非必要工作(例如清理)。这是因为激活处理程序可能无法全部运行完成,尤其是在激活期间浏览器终止的情况下。服务工作者应设计为即使激活处理程序未全部成功完成也能正常运行。
-
令 matchedClients 为一个列表,其中包含服务工作者客户端,其创建 URL 匹配 registration 的 存储密钥和 registration 的 作用域 URL。
-
对于 matchedClients 中的每个 client, 在 client 的 负责的事件循环上,使用DOM 操作任务源排队一个任务,以运行以下子步骤:
-
令 readyPromise 为 client 的 全局对象的
ServiceWorkerContainer
对象的 就绪承诺。 -
如果 readyPromise 为 null,则继续。
-
如果 readyPromise 处于待定状态,则使用在 readyPromise 的 相关设置对象中表示 registration 的获取服务工作者注册对象的结果来解决 readyPromise。
-
-
令 activeWorker 为 registration 的 活动工作者。
-
如果使用 activeWorker 和 “activate” 运行应该跳过事件算法的结果为 false,则:
尝试激活
- 输入
-
registration,一个 服务工作者注册
- 输出
-
无
设置 ServiceWorkerGlobalScope
- 输入
-
serviceWorker,一个 服务工作者
- 输出
-
一个
ServiceWorkerGlobalScope
对象或 null
注意:此算法返回一个可用于 CSP 检查的 ServiceWorkerGlobalScope
,或者返回
null。如果 serviceWorker 有一个活动的 ServiceWorkerGlobalScope
,
则返回它。否则,将新创建一个对象。
在规范中,此类安全检查需要创建 ServiceWorkerGlobalScope
、
一个相关设置对象、一个领域和一个代理。在
实现中,所需的工作量可能要少得多。因此,实现可以在其等效算法中执行较少的工作,而在运行服务工作者中执行更多的工作,只要结果在可观察的范围内是等效的即可。(特别是,只要所有安全检查都具有相同的结果。)
-
令 unsafeCreationTime 为不安全的共享当前时间。
-
如果 serviceWorker 的 状态是“
冗余
”,则返回 null。 -
断言:serviceWorker 的 启动状态为 null。
-
令 setupFailed 为 false。
-
令 globalObject 为 null。
-
令 agent 为获取服务工作者代理的结果,并在此上下文中运行以下步骤:
-
令 realmExecutionContext 为给定 agent 和以下自定义项创建新领域的结果:
-
对于全局对象,创建一个新的
ServiceWorkerGlobalScope
对象。令 workerGlobalScope 为创建的对象。
-
-
令 settingsObject 为一个新的环境设置对象,其算法定义如下:
-
将 settingsObject 的 id 设置为一个新的唯一不透明字符串,将 创建 URL 设置为 serviceWorker 的 脚本 URL,将 顶级创建 URL 设置为 null,将 顶级源 设置为一个实现定义的值,将 目标浏览上下文 设置为 null,并将 活动服务工作者 设置为 null。
-
创建一个新的
WorkerLocation
对象并将其与 workerGlobalScope 关联。 -
如果为全局对象运行 CSP 初始化算法在 workerGlobalScope 上执行时返回“
Blocked
”,则将 setupFailed 设置为 true 并中止这些步骤。 -
将 globalObject 设置为 workerGlobalScope。
-
-
等待 globalObject 不为 null,或者 setupFailed 为 true。
-
如果 setupFailed 为 true,则返回 null。
-
返回 globalObject。
运行服务工作者
注意:此算法将阻塞,直到服务工作者正在运行或启动失败。
-
如果 serviceWorker 的 状态为“
冗余
”,则返回失败。 -
断言:serviceWorker 的 启动状态为 null。
-
令 script 为 serviceWorker 的 脚本资源。
-
断言:script 不为 null。
-
令 startFailed 为 false。
-
令 workerGlobalScope 为 serviceWorker 的 全局对象。
-
如果 workerGlobalScope 为 null:
-
将 workerGlobalScope 设置为使用 serviceWorker 运行 设置 ServiceWorkerGlobalScope 算法的结果。
-
如果 workerGlobalScope 为 null,则返回失败。
-
将 serviceWorker 的 全局对象设置为 workerGlobalScope。
-
-
获取 workerGlobalScope 的 领域执行上下文的代理,并在此上下文中运行以下步骤:
-
如果 forceBypassCache 为 true,则设置 workerGlobalScope 的 导入脚本强制绕过缓存标志。
-
如果 serviceWorker 是一个 活动工作者,并且在 serviceWorker 的 包含的服务工作者注册的 任务队列中有任何任务排队,则使用其原始任务源将它们以相同顺序排队到 serviceWorker 的 事件循环的 任务队列。
-
令 evaluationStatus 为 null。
-
如果 script 是一个经典脚本,则:
-
将 evaluationStatus 设置为运行经典脚本 script 的结果。
-
如果 evaluationStatus.[[Value]] 为空,这意味着脚本未被评估。将 startFailed 设置为 true 并中止这些步骤。
-
-
否则,如果 script 是一个模块脚本,则:
-
令 evaluationPromise 为运行模块脚本 script 的结果,并将报告错误设置为 false。
-
断言:evaluationPromise.[[PromiseState]] 不为“pending”。
-
如果 evaluationPromise.[[PromiseState]] 为“rejected”:
-
将 evaluationStatus 设置为 ThrowCompletion(evaluationPromise.[[PromiseResult]])。
-
-
否则:
-
将 evaluationStatus 设置为 NormalCompletion(undefined)。
-
-
-
如果脚本被终止服务工作者算法中止,则将 startFailed 设置为 true 并中止这些步骤。
-
将 serviceWorker 的 启动状态设置为 evaluationStatus。
-
如果 script 的 曾经被评估标志未设置,则:
-
对于 settingsObject 的 全局对象的关联事件侦听器列表的每个 eventType 事件类型:
注意:如果全局对象的关联事件侦听器列表此时没有任何添加的事件侦听器,则服务工作者的要处理的事件类型集仍然是一个空集。
-
设置 script 的 曾经被评估标志。
-
取消设置 serviceWorker 的 所有 fetch 侦听器均为空标志。
-
如果使用 workerGlobalScope 的所有 Fetch 侦听器均为空算法返回 true,则用户代理可以设置 serviceWorker 的 所有 fetch 侦听器均为空标志。
-
-
运行由 settingsObject 指定的负责的事件循环,直到它被销毁。
-
-
等待 serviceWorker 正在运行,或者 startFailed 为 true。
-
如果 startFailed 为 true,则返回失败。
-
返回 serviceWorker 的 启动状态。
所有 Fetch 侦听器均为空
- 输入
-
workerGlobalScope,一个 全局对象。
- 输出
-
一个布尔值
-
令 eventHandler 为 workerGlobalScope 的 事件处理程序映射["onfetch"] 的值。
-
令 eventListenerCallbacks 为给定 workerGlobalScope 调用旧版获取服务工作者 fetch 事件侦听器回调的结果。
-
对于 eventListenerCallbacks 中的每个 eventListenerCallback:
-
令 callback 为 null。
-
如果 eventHandler 不为 null 且 eventListenerCallback 等于 eventHandler 的 侦听器的 回调,则将 callback 设置为转换为 ECMAScript 值 eventHandler 的 值的结果。
-
否则,将 callback 设置为转换为 ECMAScript 值 eventListenerCallback 的结果。
-
如果 IsCallable(callback) 为 false,则返回 false。
注意:使用
handleEvent(event)
的回调对象假定为非空。这避免了调用handleEvent(event)
的 getter,这可能会在此检查期间修改事件侦听器。
注意:这将检测像
() => {}
这样的“fetch
”侦听器。一些站点具有带有空主体的 fetch 事件侦听器,以使其被 Chromium 识别为渐进式 Web 应用程序 (PWA)。 -
-
返回 true。
注意:鼓励用户代理显示警告,指出空的“fetch
”侦听器是不必要的,并且可能会对性能产生负面影响。
终止服务工作者
- 输入
-
serviceWorker,一个 服务工作者
- 输出
-
无
-
与 serviceWorker 的主循环并行运行以下步骤:
-
令 serviceWorkerGlobalScope 为 serviceWorker 的 全局对象。
-
将 serviceWorkerGlobalScope 的 关闭标志设置为 true。
-
如果在 serviceWorkerGlobalScope 的 事件循环的 任务队列中排队了任何任务,其任务源是处理 fetch 任务源或处理功能事件任务源,则使用其原始任务源将它们以相同顺序排队到 serviceWorker 的 包含的服务工作者注册的相应任务队列,并从 serviceWorkerGlobalScope 的 事件循环的 任务队列中丢弃所有任务(包括任务,其任务源既不是处理 fetch 任务源也不是处理功能事件任务源)而不处理它们。
注意:这实际上意味着 fetch 事件和其他功能事件(如推送事件)由注册的任务队列备份,而其他任务(包括消息事件)则被丢弃。
-
中止当前在 serviceWorker 中运行的脚本。
-
将 serviceWorker 的 启动状态设置为 null。
-
处理 Fetch
处理 Fetch 算法是获取处理的入口点,该处理移交给服务工作者上下文。
-
令 registration 为 null。
-
令 client 为 request 的 客户端。
-
令 reservedClient 为 request 的 保留客户端。
-
令 preloadResponse 为一个新的 promise。
-
令 workerRealm 为 null。
-
令 timingInfo 为一个新的 服务工作者计时信息。
-
断言:request 的 目标不是 "
serviceworker
"。 -
如果 request 的 目标是 "
embed
" 或 "object
",则:-
返回 null。
-
-
否则,如果 request 是一个非子资源请求,则:
-
如果 reservedClient 不为 null 并且是一个环境设置对象, 则:
-
如果 reservedClient 不是一个安全上下文,则返回 null。
-
-
否则:
-
如果 request 是一个导航请求,并且触发它的导航是以 shift+reload 或等效方式启动的,则返回 null。
-
断言 reservedClient 不为 null。
-
令 storage key 为给定 reservedClient 运行获取存储密钥的结果。
-
将 registration 设置为给定 storage key 和 request 的 URL 运行匹配服务工作者注册的结果。
-
如果 registration 为 null 或 registration 的 活动工作者为 null,则返回 null。
-
如果 request 的 目标不是
"report"
, 则将 reservedClient 的 活动服务工作者设置为 registration 的 活动工作者。
注意:从这一点开始,服务工作者客户端开始使用其活动服务工作者的包含的服务工作者注册。
-
-
否则,如果 request 是一个子资源请求,则:
-
如果 client 的 活动服务工作者不为 null,则将 registration 设置为 client 的 活动服务工作者的 包含的服务工作者注册。
-
否则,返回 null。
-
-
令 activeWorker 为 registration 的 活动工作者。
-
如果以下任一情况为真,则令 shouldSoftUpdate 为 true,否则为 false:
-
-
如果 source 是
"network"
: -
否则,如果 source 是
"cache"
, 或者 source["cacheName
"] 存在,则:-
对于 registration 的 存储密钥的 名称到缓存映射中的每个 cacheName → cache。
-
如果 source["
cacheName
"] 存在且 source["cacheName
"] 不是 cacheName,则继续。 -
令 requestResponses 为使用 request、一个新的
CacheQueryOptions
和 cache 运行查询缓存的结果。 -
如果 requestResponses 是一个空的列表,则返回 null。
-
否则:
-
令 requestResponse 为 requestResponses 的第一个元素。
-
令 response 为 requestResponse 的响应。
-
令 globalObject 为 activeWorker 的 全局对象。
-
如果 globalObject 为 null:
-
将 globalObject 设置为使用 activeWorker 运行设置 ServiceWorkerGlobalScope的结果。
-
-
如果 globalObject 为 null,则返回 null。
注意:这仅创建一个 ServiceWorkerGlobalScope,因为 CORS 检查需要它。预计实现不会在此处实际创建 ServiceWorkerGlobalScope。
-
-
-
返回 null。
-
否则,如果 source 是
"race-network-and-fetch-handler"
, 并且 request 的 方法是 `GET
`,则:-
令 raceFetchController 为 null。
-
并行运行以下子步骤,但当 fetchController 的 状态为 "
terminated
" 或 "aborted
" 时中止: -
如果中止且 raceFetchController 不为 null,则:
-
用 undefined 解析 preloadResponse。
-
并行运行以下子步骤:
-
令 fetchHandlerResponse 为使用 request、 registration、useHighResPerformanceTimers、 timingInfo、workerRealm、 reservedClient、preloadResponse 和 raceResponse 创建 Fetch 事件并分派的结果。
-
如果 fetchHandlerResponse 不为 null 且不是一个网络错误,并且 raceFetchController 不为 null,则中止 raceFetchController。
-
将 fetchHandlerResponse 入队到 queue。
-
-
等待直到 queue 不为空。
-
返回出队 queue 的结果。
-
断言:source 是 "
fetch-event
"
-
如果 request 是一个导航请求,registration 的 导航预加载启用标志已设置,request 的 方法是 `
GET
`, registration 的 活动工作者的 要处理的事件类型集包含fetch
,并且 registration 的 活动工作者的 所有 fetch 侦听器均为空标志未设置,则:注意:如果上述情况为真,只是 registration 的 活动工作者的 要处理的事件类型集不包含
fetch
,则用户代理可能希望显示控制台警告,因为开发人员的意图尚不清楚。-
令 preloadRequest 为克隆请求 request 的结果。
-
令 preloadRequestHeaders 为 preloadRequest 的 标头列表。
-
令 preloadResponseObject 为一个新的
Response
对象,与一个新的Headers
对象关联,其守卫为 "immutable
"。 -
追加到 preloadRequestHeaders 一个新的标头,其名称为 `
Service-Worker-Navigation-Preload
`,值为 registration 的 导航预加载标头值。 -
将 preloadRequest 的 服务工作者模式设置为 "
none
"。 -
令 preloadFetchController 为 null。
-
并行运行以下子步骤,但当 fetchController 的 状态为 "
terminated
" 或 "aborted
" 时中止: -
如果中止,则:
-
令 deserializedError 为给定 null 和 workerRealm 反序列化序列化中止原因的结果。
-
用 deserializedError 中止 preloadFetchController。
-
-
-
否则,用 undefined 解析 preloadResponse。
-
返回使用 request、registration、useHighResPerformanceTimers、 timingInfo、workerRealm、reservedClient、 preloadResponse 和 null 创建 Fetch 事件并分派的结果。
创建 Fetch 事件并分派
- 输入
-
request,一个 请求
registration,一个 服务工作者注册
useHighResPerformanceTimers,一个布尔值
timingInfo,一个 服务工作者计时信息
reservedClient,一个 保留客户端
preloadResponse,一个 promise
raceResponse,一个 竞争响应或 null
- 输出
-
一个 响应
-
令 response 为 null。
-
令 eventCanceled 为 false。
-
令 client 为 request 的 客户端。
-
令 activeWorker 为 registration 的 活动工作者。
-
令 eventHandled 为 null。
-
令 handleFetchFailed 为 false。
-
令 respondWithEntered 为 false。
-
如果以下任一情况为真,则令 shouldSoftUpdate 为 true,否则为 false:
-
如果使用 "fetch" 和 activeWorker 运行应该跳过事件算法的结果为 true,则:
-
如果 activeWorker 的 所有 fetch 侦听器均为空标志已设置:
-
如果 useHighResPerformanceTimers 为 true,则将 useHighResPerformanceTimers 设置为 activeWorker 的 全局对象的 跨源隔离能力。
-
令 timingInfo 的 开始时间为给定 useHighResPerformanceTimers 的粗略共享当前时间。
-
如果 activeWorker 的 状态为 "
activating
",则等待 activeWorker 的 状态变为 "activated
"。 -
如果使用 activeWorker 运行运行服务工作者算法的结果为失败,则将 handleFetchFailed 设置为 true。
-
否则:
-
将 eventHandled 设置为 workerRealm 中的一个新的 promise。
-
如果 raceResponse 不为 null,则设置 activeWorker 的 全局对象的 竞争响应映射[request] 为 raceResponse。
-
将任务 task 排队以运行以下子步骤:
-
令 e 为使用
FetchEvent
创建事件的结果。 -
令 abortController 为 workerRealm 中的一个新的
AbortController
对象。 -
令 requestObject 为给定 request、一个新的
Headers
对象的守卫(为 "immutable
")、abortController 的 信号和 workerRealm 创建一个Request
对象的结果。 -
将 e 的
cancelable
属性初始化为 true。 -
将 e 的
request
属性初始化为 requestObject。 -
将 e 的
preloadResponse
初始化为 preloadResponse。 -
如果 request 是一个非子资源请求,并且 request 的 目标不是
"report"
, 则将 e 的resultingClientId
属性初始化为 reservedClient 的 id,否则初始化为空字符串。 -
如果 request 是一个导航请求,则将 e 的
replacesClientId
属性初始化为 request 的 替换客户端 ID, 否则初始化为空字符串。 -
将 e 的
handled
初始化为 eventHandled。 -
令 timingInfo 的 fetch 事件分派时间为给定 useHighResPerformanceTimers 的粗略共享当前时间。
-
使用 activeWorker 和 e 调用更新服务工作者扩展事件集。
-
如果 e 的 respond-with entered 标志已设置,则将 respondWithEntered 设置为 true。
-
如果 e 的 wait to respond 标志已设置,则:
-
等待直到 e 的 wait to respond 标志未设置。
-
如果 e 的 respond-with error 标志已设置,则将 handleFetchFailed 设置为 true。
-
否则,将 response 设置为 e 的 潜在响应。
-
-
如果 response 为 null,request 的 主体不为 null,并且 request 的 主体的 源为 null,则:
-
如果 response 不为 null,则将 response 的 服务工作者计时信息设置为 timingInfo。
-
如果 e 的 canceled 标志已设置,则将 eventCanceled 设置为 true。
-
如果 fetchController 状态为 "
terminated
" 或 "aborted
",则:-
令 deserializedError 为给定 fetchController 的 序列化中止原因和 workerRealm 反序列化序列化中止原因的结果。
-
如果 task 被丢弃,则将 handleFetchFailed 设置为 true。
task 必须使用 activeWorker 的 事件循环和处理 fetch 任务源。
-
-
等待 task 执行完毕或 handleFetchFailed 为 true。
-
如果 activeWorker 的 全局对象的 竞争响应映射[request] 存在,则移除 activeWorker 的 全局对象的 竞争响应映射[request]。
-
如果 respondWithEntered 为 false,则:
-
如果 eventCanceled 为 true,则:
-
如果 eventHandled 不为 null,则在 workerRealm 中使用 "
NetworkError
"DOMException
拒绝 eventHandled。 -
返回一个网络错误。
-
-
如果 eventHandled 不为 null,则解析 eventHandled。
-
如果 raceResponse 的 值不为 null,则:
-
返回 null。
-
-
如果 handleFetchFailed 为 true,则:
-
如果 eventHandled 不为 null,则在 workerRealm 中使用 "
NetworkError
"DOMException
拒绝 eventHandled。 -
返回一个网络错误。
-
-
如果 eventHandled 不为 null,则解析 eventHandled。
-
返回 response。
解析 URL 模式
- 输入
-
rawPattern,一个
URLPatternCompatible
serviceWorker,一个 服务工作者
- 输出
-
一个 URL 模式
-
令 baseURL 为 serviceWorker 的 脚本 URL。
-
返回给定 baseURL,使用 rawPattern 从 Web IDL 值构建 URL 模式的结果。
验证路由器条件
- 输入
-
condition,一个
RouterCondition
serviceWorker,一个 服务工作者
- 输出
-
一个布尔值
-
令 hasCondition 为 false。
-
如果 condition["
urlPattern
"] 存在,则:-
令 rawPattern 为 condition["
urlPattern
"]。 -
令 pattern 为传递 rawPattern 和 serviceWorker 运行解析 URL 模式算法的结果。如果此操作抛出异常,则捕获该异常并返回 false。
-
如果 pattern 具有正则表达式组,则返回 false。
注意:由于运行用户定义的正则表达式存在安全问题,因此禁止这样做。
-
将 hasCondition 设置为 true。
-
-
如果 condition["
requestMethod
"] 存在:-
令 method 为 condition["
requestMethod
"]。 -
如果 method 不是一个方法,则返回 false。
-
如果 method 是一个禁止的方法,则返回 false。
-
将 hasCondition 设置为 true。
-
-
如果 condition["
requestMode
"] 存在,则将 hasCondition 设置为 true。 -
如果 condition["
requestDestination
"] 存在,则将 hasCondition 设置为 true。 -
如果 condition["
runningStatus
"] 存在,则将 hasCondition 设置为 true。 -
返回 hasCondition。
匹配路由器条件
- 输入
-
condition,一个
RouterCondition
serviceWorker,一个 服务工作者
request,一个 请求
- 输出
-
一个布尔值
注意:如果存在多个条件(例如,设置了
urlPattern
、runningStatus
和 requestMethod
),则所有条件都需要匹配才能返回
true。
-
否则:
注意:验证路由器条件算法保证
or
、not
和其他条件是互斥的。-
如果 condition["
urlPattern
"] 存在,则:-
令 rawPattern 为 condition["
urlPattern
"]。 -
令 pattern 为传递 rawPattern 和 serviceWorker 运行解析 URL 模式算法的结果。
-
-
如果 condition["
requestMethod
"] 存在,则:-
令 method 为 condition["
requestMethod
"]。 -
规范化 method。
-
如果 request 的 方法不是 method,则返回 false。
-
-
如果 condition["
requestMode
"] 存在,则:-
令 mode 为 condition["
requestMode
"]。 -
如果 request 的 模式不是 mode, 则返回 false。
-
-
如果 condition["
requestDestination
"] 存在,则:-
令 destination 为 condition["
requestDestination
"]。 -
如果 request 的 目标不是 destination,则返回 false。
-
-
如果 condition["
runningStatus
"] 存在,则:-
令 runningStatus 为 condition["
runningStatus
"]。 -
如果 runningStatus 是
"running"
, 并且 serviceWorker 未运行,则返回 false。 -
如果 runningStatus 是
"not-running"
, 并且 serviceWorker 正在运行,则返回 false。
-
-
返回 true。
-
检查路由器注册限制
- 输入
-
routerRules,一个路由器规则列表
- 输出
-
一个布尔值
注意:路由器条件可能很复杂,并且可以使用 _or
和 not
进行嵌套。
为了防止过度处理,此算法引入了两个限制。首先,条件总数(计算所有嵌套条件)不能超过 1024。其次,嵌套深度限制为 10 级,以避免指数级计算。
计算路由器内部条件
- 输入
-
condition,一个
RouterCondition
result,一个计数路由器条件结果
depth,一个数字
- 输出
-
result,一个计数路由器条件结果
获取路由器源
- 输入
-
serviceWorker,一个服务工作者
request,一个请求
- 输出
-
RouterSource
或 null
是否应跳过事件
- 输入
-
eventName,一个字符串
serviceWorker,一个服务工作者
- 输出
-
一个布尔值
注意:为避免不必要的延迟,本规范允许在服务工作者的全局范围内首次执行脚本期间未确定性地添加该事件的事件侦听器时跳过事件分派。
触发功能性事件
- 输入
-
eventName,一个字符串
eventConstructor,一个扩展了
ExtendableEvent
的事件构造函数registration,一个服务工作者注册
initialization,可选的 event 属性初始化,由 eventConstructor 构造
postDispatchSteps,可选步骤,在活动工作者的事件循环中运行,并将 dispatchedEvent 设置为已分派的 eventConstructor 实例。
- 输出
-
无
-
断言:registration 的活动工作者不为 null。
-
令 activeWorker 为 registration 的活动工作者。
-
如果使用 eventName 和 activeWorker 运行是否应跳过事件的结果为 true,则:
-
如果 activeWorker 的状态为 "
activating
",则等待 activeWorker 的状态变为 "activated
"。 -
如果使用 activeWorker 运行运行服务工作者算法的结果为失败,则:
-
将任务 task 排队以运行这些子步骤:
-
令 event 为使用 eventConstructor 和 activeWorker 的全局对象的相关领域创建事件的结果。
-
如果 initialization 不为 null,则使用 initialization 初始化 event。
-
使用 activeWorker 和 event 调用更新服务工作者扩展事件集。
-
如果 postDispatchSteps 不为 null,则将 event 作为 dispatchedEvent 传递并运行 postDispatchSteps。
task 必须使用 activeWorker 的事件循环和处理功能性事件任务源。
-
-
等待 task 执行完毕或被丢弃。
amazingthing
" 事件(类型为 AmazingThingEvent
),并初始化事件对象的属性,其描述如下:
-
在 serviceWorkerRegistration 上使用
AmazingThingEvent
触发功能性事件 "amazingthing
",并具有以下属性:- 属性名称
-
值
- 另一个属性名称
-
另一个值
然后使用 dispatchedEvent 运行以下步骤:
-
在服务工作者的事件循环中对 dispatchedEvent 执行任何所需操作。
请注意,初始化步骤和分派后步骤是可选的。如果不需要它们,则描述如下:
-
在 serviceWorkerRegistration 上使用
ExtendableEvent
触发功能性事件 "whatever
"。
处理服务工作者客户端卸载
用户代理必须在服务工作者客户端通过卸载文档清理步骤或终止卸载时运行这些步骤。
- 输入
-
client,一个服务工作者客户端
- 输出
-
无
处理用户代理关闭
- 输入
-
无
- 输出
-
无
更新服务工作者扩展事件集
取消注册
- 输入
-
job,一个作业
- 输出
-
无
设置注册
清除注册
- 输入
-
registration,一个服务工作者注册
- 输出
-
无
尝试清除注册
- 输入
-
registration,一个服务工作者注册
- 输出
-
无
-
如果没有服务工作者客户端正在使用 registration 并且以下所有条件都为真,则使用 registration 调用清除注册:
-
registration 的安装中工作者为 null,或者使用 registration 的安装中工作者运行服务工作者没有待处理事件的结果为 true。
-
registration 的等待中工作者为 null,或者使用 registration 的等待中工作者运行服务工作者没有待处理事件的结果为 true。
-
registration 的活动工作者为 null,或者使用 registration 的活动工作者运行服务工作者没有待处理事件的结果为 true。
-
更新注册状态
- 输入
-
registration,一个服务工作者注册
target,一个字符串("
installing
"、"waiting
" 或 "active
" 之一)source,一个服务工作者或 null
- 输出
-
无
-
令 registrationObjects 为一个包含与 registration 关联的所有
ServiceWorkerRegistration
对象的数组。 -
如果 target 是 "
installing
",则: -
否则,如果 target 是 "
waiting
",则: -
否则,如果 target 是 "
active
",则:-
将 registration 的活动工作者设置为 source。
-
对于 registrationObjects 中的每个 registrationObject:
-
更新工作者状态
-
断言:state 不是 "
parsed
"。注意:"
parsed
" 是初始状态。一个服务工作者永远不会更新到此状态。 -
将 worker 的状态设置为 state。
-
对于 settingsObjects 中的每个 settingsObject,在 settingsObject 的负责的事件循环中的DOM 操作任务源上将任务排队以运行以下步骤:
-
令 objectMap 为 settingsObject 的服务工作者对象映射。
-
如果 objectMap[worker] 不存在,则中止这些步骤。
-
令 workerObj 为 objectMap[worker]。
-
将 workerObj 的
state
设置为 state。 -
在 workerObj 上触发名为
statechange
的事件。
-
通知控制器更改
- 输入
-
client,一个服务工作者客户端
- 输出
-
无
-
断言:client 不为 null。
-
如果 client 是一个环境设置对象,将任务排队以在与 client 关联的
ServiceWorkerContainer
对象上触发名为controllerchange
的事件。
匹配服务工作者注册
-
原子地运行以下步骤。
-
令 clientURLString 为序列化的 clientURL。
-
令 matchingScopeString 为空字符串。
-
令 scopeStringSet 为一个空列表。
-
如果存在,则将 matchingScopeString 设置为 scopeStringSet 中 clientURLString 值开头的最长值。
注意:此步骤中的 URL 字符串匹配是基于前缀的,而不是基于路径结构的。例如,具有 "https://example.com/prefix-of/resource.html" 的客户端 URL 字符串将匹配作用域为 "https://example.com/prefix" 的注册。URL 字符串比较对于同源安全性是安全的,因为 HTTP(S) URL 在 URL 的源部分的末尾始终使用尾部斜杠进行序列化。
-
令 matchingScope 为 null。
-
如果 matchingScopeString 不是空字符串,则:
-
返回给定 storage key 和 matchingScope 运行获取注册的结果。
获取注册
获取最新工作者
服务工作者没有待处理事件
- 输入
-
worker,一个服务工作者
- 输出
-
True 或 false,一个布尔值
创建客户端
创建窗口客户端
- 输入
-
client,一个服务工作者客户端
frameType,一个字符串
visibilityState,一个字符串
focusState,一个布尔值
ancestorOriginsList,一个列表
- 输出
-
windowClient,一个
WindowClient
对象
获取框架类型
- 输入
-
navigable,一个可导航对象
- 输出
-
frameType,一个字符串
解决 Get Client Promise
-
如果 client 是一个环境设置对象,则:
-
如果 client 不是一个安全上下文,则在 promise 的相关设置对象的负责的事件循环上使用DOM 操作任务源将任务排队以使用 "
SecurityError
"DOMException
拒绝 promise,并中止这些步骤。
-
-
否则:
-
如果 client 的创建 URL 不是一个潜在可信 URL,则在 promise 的相关设置对象的负责的事件循环上使用DOM 操作任务源将任务排队以使用 "
SecurityError
"DOMException
拒绝 promise,并中止这些步骤。
-
-
否则:
-
令 browsingContext 为 null。
-
如果 client 是一个环境设置对象,则将 browsingContext 设置为 client 的全局对象的浏览上下文。
-
否则,将 browsingContext 设置为 client 的目标浏览上下文。
-
查询缓存
- 输入
-
requestQuery,一个请求
options,一个可选的
CacheQueryOptions
对象targetStorage,一个可选的请求响应列表
- 输出
-
resultList,一个请求响应列表
-
令 resultList 为一个空的列表。
-
令 storage 为 null。
-
如果省略了可选参数 targetStorage,则将 storage 设置为相关的请求响应列表。
-
否则,将 storage 设置为 targetStorage。
-
对于 storage 中的每个 requestResponse:
-
令 cachedRequest 为 requestResponse 的请求。
-
令 cachedResponse 为 requestResponse 的响应。
-
如果使用 requestQuery、cachedRequest、cachedResponse 和 options 的请求匹配缓存项返回 true,则:
-
令 requestCopy 为 cachedRequest 的副本。
-
令 responseCopy 为 cachedResponse 的副本。
-
将 requestCopy/responseCopy 添加到 resultList。
-
-
-
返回 resultList。
请求匹配缓存项
- 输入
-
requestQuery,一个请求
request,一个请求
response,一个响应或 null,可选,默认为 null
options,一个可选的
CacheQueryOptions
对象 - 输出
-
一个布尔值
-
如果 options["
ignoreMethod
"] 为 false 且 request 的方法不是 `GET
`,则返回 false。 -
令 queryURL 为 requestQuery 的URL。
-
令 cachedURL 为 request 的URL。
-
如果 options["
ignoreSearch
"] 为 true,则: -
如果 queryURL 在设置了排除片段标志的情况下不等于 cachedURL,则返回 false。
-
如果 response 为 null,options["
ignoreVary
"] 为 true,或者 response 的标头列表不包含 `Vary
`,则返回 true。 -
令 fieldValues 为包含与Vary 标头的字段值对应的元素的列表,该标头的名称为 `
Vary
`,值为该标头的值。 -
对于 fieldValues 中的每个 fieldValue:
-
返回 true。
批量缓存操作
-
令 cache 为相关的请求响应列表。
-
令 backupCache 为一个作为 cache 副本的新的请求响应列表。
-
令 addedItems 为一个空的列表。
-
尝试原子地运行以下子步骤:
-
令 resultList 为一个空的列表。
-
对于 operations 中的每个 operation:
-
如果 operation 的类型匹配 "
delete
" 且 operation 的响应不为 null,则抛出一个TypeError
。 -
如果使用 operation 的请求、operation 的选项和 addedItems 运行查询缓存的结果不为空,则抛出一个 "
InvalidStateError
"DOMException
。 -
令 requestResponses 为一个空的列表。
-
如果 operation 的类型匹配 "
delete
",则: -
否则,如果 operation 的类型匹配 "
put
",则:-
对于 requestResponses 中的每个 requestResponse:
-
如果前两个步骤中的缓存写入操作由于超出授予的配额限制而失败,则抛出一个 "
QuotaExceededError
"DOMException
。
-
返回 resultList。
-
-
然后,如果抛出了异常,则:
注意:当抛出异常时,实现会撤消(回滚)在批量操作作业期间对缓存存储所做的任何更改。
是异步模块
查找竞争响应
附录 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 标头 navigator. serviceWorker. register( "/js/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 给予的大力专业支持。