css样式表解析过程

如果转载本文,请注明出处!

CSSGrammar.y文件中定义了编译css样式表的词法规则和语法规则。我先将CSSGrammar.y文件中的非终结符与WebCore模块的c++类的对应关系总结出来。

  • term对应CSSParserValue对象。
  • expr对应CSSParserValueList对象。
  • operator刚被解析出来时,以字符形式存储在解释器堆栈上。等到需要与其他term组合时,转换为CSSParserValue对象存入CSSParserValueList对象中。
  • error对应0。如果解析某条css规则时发现了错误,那么将0存储到解释器堆栈上。
  • property对应一个整数值。整数值实际是属性名称的hash值。
  • prio对应一个true或者false。如果有符号'!',那么将true压入解释器堆栈。否则,将false压入解释器堆栈。
  • declaration对应CSSProperty对象。只是declaration对应的CSSProperty对象并未压入解释器堆栈,而是直接存入CSSParser的成员变量m_parsedProperties中。如果declaration是合法的,那么向解释器堆栈压入一个true。否则,压入一个false。
  • decl_list对应Vector<CSSProperty, 256>。如果无错,那么将true压入解释器堆栈。如果有错,那么将false压入解释器堆栈。
  • declaration_list与decl_list相同。
  • pseudo_page对应CSSParserSelector对象。
  • match对应CSSSelector::Match对象。
  • attrib对应CSSParserSelector对象。如果是[attribute-name]形式,那么CSSParserSelector的attribute记录attribute-name。如果是[attribute-name=xxx]形式,那么CSSParserSelector的match记录=, value记录xxx, attribute记录attribute-name。
  • attr_name对应CSSParserString对象。
  • class对应CSSParserSelector对象。
  • specifier对应CSSParserSelector对象。
  • specifier_list对应CSSParserSelector对象。m_relation用于记录关系,m_tagHistory用于记录另一个Selector。如果是父子选择符,那么父亲是this,m_relation是sub, 子记录在this->m_tagHistory。
  • element_name对应CSSParserString对象。
  • simple_selector对应CSSParserSelector对象。
  • simple_selector_list对应Vector<OwnPtr<CSSParserSelector> >对象。simple_selector_list是指selector, selector,xxx样式。
  • namespace_selector对应CSSParserString对象。
  • selector对应CSSParserSelector对象。形如selector combinator simple_selector,CSSParserSelector.m_relation是combinator, CSSParserSelector.m_tagHistory是selector, simple_selector->....->m_tagHistory是this。
  • combinator对应CSSSelector::Relation对象。
  • ruleset对应CSSStyleRule对象。在生成CSSRule对象时,会将CSSParser::m_parsedProperties转换为StylePropertySet对象。然后将StylePropertySet对象记录到m_parsedRules成员变量中。
  • unary_operator对应1或者-1。
  • region对应WebKitCSSRegionRule对象。
  • region_selector对应Vector<OwnPtr<CSSParserSelector> >对象。
  • font_face对应CSSFontFaceRule对象。
  • margin_sym对应CSSSelector::MarginBoxType对象。
  • page_selector对应CSSParserSelector对象。
  • page对应CSSPageRule对象。
  • key对应CSSParserValue对象。
  • key_list对应CSSParserValueList对象。
  • keyframe_rule对应StyleKeyframe对象。
  • keyframes_rule对应WebKitCSSKeyframesRule对象。
  • keyframes对应WebKitCSSKeyframesRule对象。

主要语法单位的关系以及语法单位与webcore对象的对应关系如下图所示:

选择符有多种类型:属性选择符、类选择符、id选择符、元素选择符、伪类选择符、伪元素选择符还有更为复杂的组合选择符。这些选择符对应的CSSSelector对象有什么不同?这里对选择符多做一些分析。

首先来看属性选择符。CSSSelector中定义了两个嵌套结构体CSSSelector::Match和CSSSelecotr::RaraData。CSSSelector就是使用这两种类型的成员变量m_match、m_data来记录属性选择符。碰到属性选择符时,属性名称记录在m_data.m_attribute中,属性值记录在m_data.m_value中。类型记录在m_match中。属性选择符类型与CSSSelector::Match的对应关系如下所示。
  • 形如"[ xxx ]" 类型的选择符,使用CSSSelector::Set来描绘素。当DOM元素设置了xxx属性时,匹配成功。
  • 形如“[ xxx = vvv ]”类型的选择符,使用CSSSelector::Exact来描述。当DOM元素设置了xxx属性且设置的值等于vvv时,匹配成功。
  • 形如"[ xxx ~= vvv ]"类型的选择符,使用CSSSelector::List来描述。当DOM元素设置了xxx属性、设置的值是一系列 以空格分开的字符串、其中的一个字符串等于vvv,那么匹配成功。
  • 形如"[ xxx |= vvv ]"类型的选择符,使用CSSSelector::Hyphen来描述。当DOM元素设置了xxx属性、设置的值是一系列用连字符分开的字符串、第一个字符串等于vvv时,匹配成功。
  • 形如"[ xxx ^= vvv ]"类型的选择符,使用CSSSelector::Begin来描述。当DOM元素设置了xxx属性,设置的值是一个字符串且vvv是值的前缀时,匹配成功。
  • 形如"[ xxx $= vvv ]"类型的选择符,使用CSSSelector::End来描述。当DOM元素设置了xxx属性,设置的值是一个字符串且vvv是值的后缀时,匹配成功。
  • 形如"[ xxx *= vvv ]"类型的选择符,使用CSSSelector::Contain来描述。当DOM元素设置了xxx属性,设置的值是一个字符串且vvv是值的子串时,匹配成功。

类选择符也用到了成员变量m_match和m_data。类选择符的样式是".xxx"。CSSSelector对象将字符串"xxx"记录在m_data.m_value中,将CSSSelector::Class记录在m_match中。

id选择符也用到了成员变量m_match和m_data。id选择符的样式是"#xxx"。CSSSelector对象将字符串"#xxx"记录在m_data.m_value中,将CSSSelecotr::Id记录在m_match中。

元素选择符用到了成员变量m_tag。元素选择符的样式是"tag-name" or "*"。CSSSelector直接将字符串"tag-name"或者"*"记录在m_tag中。

伪类选择符用到了成员变量m_match、m_pseudoType、m_data。伪类选择符的样式是":xxx"。伪类选择符的种类非场多。碰到伪类选择符,CSSSelector对象将m_match设置为CSSSelector::PseudoClass,将字符串"xxx"记录到m_data.m_value中,再执行CSSSelector::pseudoType方法来解析出伪类的类型。另外,如果伪类选择符还需要设置参数,那么将参数记录到m_data.m_argument。例如:nth-child(xxx),xxx就记录在m_data.m_argument。伪类的类型可以阅读CSSSelector::PseudoType类型以及位于CSSSelector.cpp文件中的nameToPseudoTypeMap方法。

伪元素选择符用到了成员变量m_match、m_pseudoType、m_data。伪元素选择符的样式是"::xxx"。CSSSelector将m_match设在为CSSSelector::PseudoElement,将字符串"xxx"记录到m_data.m_value中,再执行CSSSelector::pseudoType解析伪元素类型。

组合选择符是由两个选择符根据一定关系组合起来的。目前webcore实现了五种组合关系: "子孙"、“孩子”、"直接相邻兄弟”、“间接相邻兄弟”、“属性组合”。组合选择符除了使用前述的成员变量之外,还需要用到m_relation。另外,对于CSSParserSelector对象来说,关系是通过指针m_tagHistory来实现。对于CSSSelector来说,关系通过地址紧临来实现,也就是说(this, this+1)具有关系。为叙述方便,我假设关系是通过指针ptr来实现的。

子孙选择符是形如"selector1 space selector2"样式的。如果DOM元素匹配selector2且DOM某祖先元素匹配selector1,那么才匹配成功。selector2的m_relation设置为CSSSelector::Descendant,并设置selector2->ptr为selector1的地址。

孩子选择符是形如"selector1 > selector2"样式的。如果DOM元素匹配selector2且DOM元素的父亲匹配selector1,那么匹配成功。selector2的m_relation设置为CSSSelector::Child,并设置selector2->ptr为selector1的地址。

直接相邻兄弟选择符是形如"selector1 + selector2"样式的。如果DOM元素A与DOM元素B具有相同的父节点、A紧跟在B之后且A匹配selector2、B匹配selector1,那么匹配成功。A可以使用选择符的样式。selector2的m_relation设置为CSSSelector::DirectAdjacent,并设置selector2->ptr为selector1的地址。

间接相邻兄弟选择符形如"selector1 ~ selector2"样式的。如果DOM元素A与DOM元素B具有相同父节点、A跟在B之后无需紧跟且A匹配selector2、B匹配selector1,那么匹配成功。A可以使用选择符的样式。selector2的m_relation设置为CSSSelector::IndirectAdjacent,并设置selector2->ptr为selector1的地址。

属性组合是形如"spec-selector spec-selector"样式。spec-selector表示id选择符、类选择符、属性选择符、伪类选择符。这种情况与子孙选择符样式很相似,区别在于子孙选择符是在此次处理之后添加的。有两种情况,分别会有SubSelector和ShadowDescendent两种关系。

假设选择符类型是"div .abc ['color']"。

  1. CSSParser先创建一个元素选择符对象div。
  2. 创建类选择符对象abc。
  3. 创建属性选择符对象color。
  4. 对abc、color做属性组合,结果是abc->ptr ==> color,关系为CSSSelector::SubSelector。
  5. 将abc与"*"做属性组合,结果是abc->ptr ==> abc,关系为CSSSelector::SubSelector,color的m_tag被设置为"*"。
  6. 将abc与div做子孙组合,结果是abc->ptr ==> color, 关系为CSSSelector::SubSelector。color->ptr ==> div,关系为Descendant。

假设选择符类型是"div ::abc ['color']"。

  1. CSSParser先创建一个元素选择符对象div。
  2. 创建伪元素选择符对象abc。
  3. 创建属性选择符对象color。
  4. 对abc、color做属性组合,结果是abc->ptr ==> color,关系为CSSSelector::SubSelector。color具有关系,为ShadowDescendant。
  5. 将abc与"*"做属性组合,结果是abc->ptr ==> color,关系为CSSSelector::SubSelector。color->ptr ==>“*”,关系为ShadowDescendant。
  6. 将abc与div做子孙组合,结果是abc->ptr ==> color, 关系为CSSSelector::SubSelector。color->ptr ==>“*”,关系为ShadowDescendant。"*"->ptr ==> div,关系为Descendant。

css语法解释器的过程是:
  1. 先创建CSSStyleSheet对象。将CSSStyleSheet对象的指针存储到CSSParser对象中。
  2. CSSParser识别出一个simple-selector,形如"div"或者".class"。创建一个CSSParserSelector对象。
  3. CSSParser识别出一个关系符和另一个simple-selecotr,那么修改之前创建的simple-selecotr, 创建组合关系符。
  4. 循环第3步直至碰到逗号或者左大括号。
  5. 如果碰到逗号,那么取出CSSParser的reuse vector,然后将堆栈尾部的CSSParserSelector对象弹出存入Vecotr中,最后跳转至第2步。如果碰到左大括号,那么跳转至第6步。
  6. 识别属性名称,将属性名称的hash值压入解释器堆栈。
  7. 识别属性值,创建CSSParserValue对象,并将CSSParserValue对象存入解释器堆栈。
  8. 将属性名称和属性值弹出栈,创建CSSProperty对象。并将CSSProperty对象存入CSSParser成员变量m_parsedProperties中。
  9. 如果识别处属性名称,那么转至第6步。如果识别右大括号,那么转至第10步。
  10. 将reuse vector从堆栈中弹出,并创建CSSStyleRule对象。CSSStyleRule对象的选择符就是reuse vector, 样式值就是CSSParser的成员变量m_parsedProperties。
  11. 把CSSStyleRule添加到CSSStyleSheet中。
  12. 清空CSSParser内部缓存结果。
  13. 如果没有内容了,那么结束。否则跳转值第2步。

 

补:nth-child和nth-last-child的实现细节。

:nth-child()和nth-last-child()的实现与CSSSelector::RareData有关系。CSSSelector::RareData的三个成员变量m_a, m_b, m_argument记录了nth-child和nth-last-child需要的数据。m_a记录n的系数,m_b记录常量,m_argument记录css源码。假设css源码是:nth-child(2n+1),那么m_a为2, m_b为1, m_argument为2n+1。CSSSelector::RareData::parseNth方法是利用m_argument设置m_a, m_b。CSSSelector::RareData::mathNth是判断count是否符合条件约束。基本规则是:如果m_a大于0,那么(count-m_b)%m_a==0则符合;如果m_a小于0,那么(m_b-count)%(-m_a)==0,则符合。

没有更多推荐了,返回首页