WebKit Inside: CSS 样式表的匹配时机介绍了当 HTML 页面有不同 CSS 样式表引入时,CSS 样式表开始匹配的时机。后续文章继续介绍 CSS 样式表的匹配过程,但是在匹配之前,首先需要收集页面里面的 Active 样式表。
1 Active 样式表
在一个 HTML 文件里面,可能会使用标签与
标签引入许多样式表,但是这些样式表并不一定都同时在文档里面生效。有时根据业务需求,可能会只使用页面里的部分样式表。比如有一个换肤需求,页面里面可能会使用
标签引入 4 张样式表,代码如下:
上面样式表reset.css
所在的标签有
rel="stylesheet"
属性,没有title
属性,这种样式表被称为 Persisten 样式表,会一直被启用。
样式表default.css
所在的标签有
rel="stylesheet"
和title
属性,这种样式表被称为 Preferred 样式表。Preferred 样式表是默认启用。一个页面只能有一个 Preferred 样式表。
样式表fancy.css
和basic.css
所在标签有
rel="alternate stylesheet"
和title
属性,这种样式表被称为 Alternate 样式表。这些样式表默认下是不启用的,但是可以提供给用户选择。一旦用户选择了一个 Alternate 样式表,Preferred 样式表就会别禁用。
根据 标签语法,样式表
reset.css
和default.css
会在页面里面使用,而样式表fancy.css
和basic.css
会暂时不使用。
一般这种场景会给一个按钮让用户切换皮肤,当用户选择切换到Fancy
皮肤时,样式表default.css
就失效,样式表fancy.css
就会启用。但是不管用户如何切换,样式表reset.css
始终有效。更多信息可以参考 MDN Alternative Style Sheet[1]。
在用户没有换肤之前,样式表reset.css
和default.css
样式表就属于 Active 样式表,当用户选择切换之后,样式表reset.css
和fancy.css
就是 Active 样式表。
在进行 CSS 样式表匹配之前,WebKit 首先要收集页面里面所有的 Active 样式表,然后依次遍历这些 Active 样式表的 CSS Rule 进行匹配。
2 相关类图
上面类图里Style::SCope
类持有负责进行样式表匹配的Style::Resolver
类,同时它内部还有 3 个重要的数据成员:m_styleSheetCandidateNodes
是一个哈希链表,用来按顺序存储 HTML 文件里面的 与
节点,也就是
HTMLStyleElment
对象和HTMLLinkElement
对象。
m_activeStyleSheets
是一个 Vector,类似数组,用来顺序存储页面里面的 Active 样式表。
m_styleSheetsForStyleSheetList
也是一个 Vector,用来顺序存储页面里面的所有样式表。
3 获取 Candidate Node
无论内部样式表,还是外部样式表,当 WebKit 解析到 标签或者
标签时,都会调用
Style::Scope::addStyleSheetCandidateNode
方法,将自己添加到Style::Scope
的实例变量m_styleSheetCandidateNode
里面。
以内部样式表为例,下面是调用堆栈:
函数Style::Scope::addStyleSheetCandidateNode
的代码如下:
void Scope::addStyleSheetCandidateNode(Node& node, bool createdByParser)
{
if (!node.isConnected())
return;
// Until the exists, we have no choice but to compare document positions,
// since styles outside of the body and head continue to be shunted into the head
// (and thus can shift to end up before dynamically added DOM content that is also
// outside the body).
// 1. createByParser 代表当前的 node 是从 HTML 文件里解析出来的,而不是通过 JavaScript 代码动态创建的;
// m_document.bodyOrFrameset 方法判断当前页面是否解析出了 标签和