本文为HTML标准解读系列文章,其他文章详见这里。
在上一篇文章中,我们探讨了document对象是如何选择浏览上下文的。选择浏览上下文,其实是document对象的创建与初始化过程的第一步。本篇文章我将继续基于HTML标准,主要是7.11小节,揭示整个初始化的过程,并且会对一些重点的部分进行展开讨论。
document对象的创建和初始化过程
在HTML标准7.11.10中,对document对象的创建与初始化过程列出了详细的算法,整个过程有20个步骤,一些步骤下面还有子算法... 但是整个过程可以归纳总结为如下几步:
- 根据COOP策略,获取对应的浏览上下文;
- 创建document关联的window对象;
- 创建document对象,并同时设置其基本信息与安全特性;
- 基本信息:
Type、Content Type、origin、URL、timing info;- 安全特性:
- policy container 策略容器
- permission policy 权限策略
- active sandboxing flag set 激活的沙箱标志组
- cross-origin opener policy 跨域程序打开策略(COOP)
- 设置document的
readyState为loading;- 初始化CSP策略
- 根据请求设置document的
referrer属性- 处理相关header头
Refreshheader- early hint
Linkheader- 返回document对象
这里的第一步在上一篇文章已经做了详细的讲解,这里就不再啰嗦,接下来我会从不同的方面展开这里的步骤。
创建window对象
大多数时候,window对象与document对象是一对一的关系,并且他们可以通过window.document与document.defaultView相互访问。
但是,如果一个document对象没有对应的浏览上下文,那么他也不会有对应window对象。比如document.implementation.createDocument(),并不会走上面提到的算法,而是有自己的初始化过程。所以访问它的document.defaultView的值为null。
除此以外,在满足特定条件的时候,一个window与document还能是一对二的关系。
基本信息:Type与Content Type
在初始化过程中,document的type和content type的值会直接决定后续浏览器对document的处理过程。
type的值只有两种:"xml"或"html"。xml表示这是一个XML文档,html表示这是一个HTML文档。这个值会决定后续解析内容是使用XML解析器还是HTML解析器。
Content type,表示内容类型。不同的类型,也会有不同的处理步骤,以一个type=html的document为例:
- 如果内容是HTML文件,即
Content type为"text/html":浏览器就会启动HTML解析器来解析内容,这是我们最常使用的过程。 - 如果内容是文本资源,比如
text/plain、text/javascript:浏览器会把文本内容包裹在一个pre元素中进行再进行解析。 - 如果内容是多媒体资源,比如
image/png、video/mp4:浏览器会自动创建一个对应的多媒体元素,如image元素、video元素,来包裹资源内容,并不再启用HTML解析器,直接跳过解析的过程, - 如果内容是需要加载其他插件的资源,比如
application/pdf:浏览器会自动创建一个embed元素包裹对应的资源,并不再启用HTML解析器,直接跳过解析的过程, - ...
以上列举的例子,你都可以自己的本地进行测试,比如拖不同类型的文件进浏览器,看看生成的document的HTML是长什么样子的。
document关联的安全策略/特性
根据上面的算法,可以看到,与document相关的安全策略/特性包括这些:
- 一个policy container(策略容器),容器里面包含的策略有:
- CSP列表
- embedder policy (跨源程序嵌入器策略)
- referrer policy
- permission policy (权限策略)
- active sandboxing flag set (激活的沙箱标志组)
- cross-origin opener policy (跨源程序打开策略/COOP)
你可能会感觉这些策略很零散,WHATWG也是这么想的,所以它也在策略容器下面的解释文本作了个标记,说正在把其他的策略迁移到容器里面,未来我们可能会看到更多散落在外面的策略都放在策略容器里面。
深入讲解这些策略超出了本文所涉及的内容,我用一张表给大家总结他们的内容,并贴上了对应的参考链接。
| 安全特性 | 描述 | 参考 |
|---|---|---|
| CSP策略 | 控制页面可以加载哪些资源、运行哪些脚本等等。 | MDN CSP标准 |
| embedder policy | 控制加载资源是否必须经过服务端的显式授权(使用CORP)。 | 这篇文章把来龙去脉讲清楚了 标准解释 |
| referrer policy | 控制发送请求的时候,referer头要带多少信息。 | MDN 标准 |
| permission policy | 启用或禁用浏览器特定的功能,如相机功能。 | 标准 |
| active sandboxing flag set | 使用iframe的sandbox属性可以显式声明这个组,但是每一个顶层浏览上下文的document自己也有一个这样的组,起始的时候为空。 | 标准解释 |
| cross-origin opener policy | 决定两个浏览上下文是否共享同一个浏览上下文组,功能与noopener类似。 | 这篇文章把来龙去脉讲清楚了 标准解释 |
相关header头处理
处理header头是整个初始化步骤的最后几步了:
Refreshheader:如果声明了这个header头,指示需要定时刷新页面,跟<meta http-equiv="refresh" content="30">做的事情是一样的;- early hint: 使用103的状态码,表示提前加载一些资源;
Linkheader:跟link标签的作用一样。
document的状态更新
细心的读者可能会发现,在上面的初始化步骤中,document的readyState是没有被更新的。没错,在初始化完毕之后,document还要等HTML解析器把文档的内容解析完,才会把readyState更新为interactive,然后等到页面中所有的资源加载完毕,readyState才会变成complete。
标准列出的具体的过程,几个关键节点如下所示:
- 完成document对象的初始化;
- 完成上面提到的内容的解析处理,这一步之后,不管实际步骤需不需要使用到HTML解析器,都会标记HTML解析已经完成了;
- 更新document的状态
readyState为interactive; - 执行所有
defer script与没有标记async的module script; - 触发document对象上的
DOMContentLoaded事件; - 执行剩余的
script; - 更新document的状态
readyState为complete。 - 触发window的
load事件。
总结
本文,我为你展示了document对象从创建、初始化、到完成加载的整个过程。因为实际的过程是一个非常复杂的系统,我并不能在一篇文章里事无巨细地给你讲全,我只是把这个过程中最重要的一些节点罗列了出来,为你提供整个过程的「宏观认知」。这种宏观认知就像树干一样,不管你是要自己读标准,还是要深入研究具体的分支细节,都能给你提供坚实的基础。