前言
CSS
在
Webkit
中的实现属于相对独立的一个模块,注意这里说的是相对。
CSS
在
Webkit
中的作用自然是不言而喻的,在
Web
早期,文档的结构和样式还未分离的那个时代,
HTML
担负了文档的结构和样式这两个双重任 务,即
HTML
既负责文档的结构,同时文档的样式也通过
HTML
中通过标签的属性来指定。可想而知,在那个时候
HMTL
页面的开发和使用比起现在而言是多 么的不便。
不过仔细想想,这恐怕与当时的技术发展程度有着很大大关,首先,那个时候互联网远不像现在这样普及,另外,网页也远不如现在这样复杂,不像现在, 可以说,世界上信息的主要传播方式是以网页形式出现的,没数据说明,但我觉得至少趋势是这样的。 就这样,互联网在不断的前进之中,直到后来
CSS
的出现,大大的改进了
Web
的开发模式,从此,文档的结构和样式被清晰的一分为二。
HTML
主要负责文档 的结构,而
CSS
则担负着文档的样式指定。
关于
CSS
的介绍网上已经有很多了,在这里将主要从
Webkit
实现的角度对其进行介绍。
1、CSS是什么
CSS
是
Cascading Style Sheets
的缩写,按照官方定义,它可以被认为是一个样式表语言
,它允许用户通过它来为结构化文档(HTML文档)指定样式。通过使用CSS用户可以将文档的内容和样式分离,从而简化Web页面的开发和维护。
既然说它是一个样式表语言,那么它就有相应的语法规则,规定了如何如何来书写一个样式表,让其作用与文档内容达到书写者想要的外观。
CSS
的语法规 则是比较简单的, 自顶向下的来看,一个级联样式表(
CSS
)是由一系列的规则(
ruler
)组成的, 每一条规则又是由一个选择器(
selector
)和若干条声明(
Declearation
)组成的。每条声明(
Declearation
)又是一个键值 对,由属性(
property
)和值(
value
)来组成,如下图所示。
从这里可以看到,语法是很简单的,使用起来也确实很简单。注意,我在这里只是说使用简单,就跟铅笔一样,谁都会用,铅笔的使用当然是简单的不能再简 单了,但是就是这样普通的工具,在专业人士和普通人的手里所能创造出的东西是截然不同的。所以我想说的是,你能很快的学会如何使用
CSS
并不代表你编织出 漂亮的网页,它只是一个工具,能发挥到什么程度还得看人。
转回来,从其简单的语法来看,似乎是只要简单的将其转化为对应的程序设计模型即可,但实际中,
CSS
的实现还是比较复杂的,其复杂性就在于,
CSS
本身的复杂程度,它定义了一系列的规则来决定为哪些元素来指定样式,以及样式的继承关系,哪些是继承的,哪些是非继承的,以及作用于同一个元素的多个样式 的叠加,还有就是它对所有能指定的样式都有完整的对应的属性集。所以从实现的角度来看,一个完整的,兼容于标准的
CSS
实现,需要顾及到的东西还是很多 的。
2、CSS实现模型
webkit css
部分的实现代码为于目录
webcore/css
中,算是
webkit
中一个相对独立的模块,下面类图是我为了更好的了解
css
实现所作,大致勾勒出了
CSS
的内部实现。
用户所书写的
css
文档,最终会转化为
webkit
内部的模型表示,这里有几个比较重要的类。
3、CSS默认样式表
从
Webkit CSS
的实现可以看到,即使你不指定任何样式表,实际上当
CSS
模块运作起来的时候,它都会载入几张默认的样式表,要知道,在
CSSStyleSelector
的构造函数中,总是会调用
loadDefaultStyle()
这个函数,其作用就是载入默认的样式表。
这些默认的样式表包含了一些
HTML
元素的最基本的样式信息。相信在使用
css
的用户中,大多数人都不会在对
display:block
吧,是啊,几乎所有使用
css html
的人都知道
div
是一个块级元素,所以没人会多此一举,但是通过了解其
CSS
模块的具体实现,我们可以知道,这些个默认的样式表其实就已经为我们 指定了一系列我们认为的想当然的规则。
这四个默认样式表是:
- html4UserAgentStyleSheet。
- quirksUserAgentStyleSheet。
- svgUserAgentStyleSheet。
- sourceUserAgentStyleSheet。
额,从名字上大致也能够了解1, 2了吧,它们不是以文件形式存储,而是在CSS中以字符数组的形式出现,也就是说作为数据编到代码里面去了,应该是考虑到每次都要使用默认样式表而为了减少I/O造成的性能损失。
为了说明我前面所说的,这些默认样式表描述的都是些个关于HTML元素的最基本的信息,还是来看个例子吧,
比如说
html4UserAgentStyleSheet
,从名字上可以看到,这张应该就是传说中的浏览器默认样式表了。看看都有些啥吧,这里只截取个片段。
html {
2 display: block
3 }
4
5 head {
6 display: none
7 }
8
9 meta {
10 display: none
11 }
12
13 title {
14 display: none
15 }
16
17 link {
18 display: none
19 }
20
21 style {
22 display: none
23 }
24
25 script {
26 display: none
27 }
28
29 body {
30 display: block;
31 margin: 8px
32 }
33
34 p {
35 display: block;
36 margin: 1.0__qem 0px
37 }
38
39 div {
40 display: block
41 }
42
43 layer {
44 display: block
45 }
从上面可以看出,真就是些最基本的属性的指定,如果没有这些默认值指定的话,用户还得自行添加这些规则,那会很麻烦。
其他几张表在此不作分析。
4、CSS解析
CSS
使用的时候,只需要将按照其语法规范,书写一个规则集合,然后保存为一个**.css
文件,在
html
中引用即可,当然这里使用的是外部样式表的方式,只是使用
CSS
的一种方式,在这里我不打算讨论
CSS**的几种使用方式,所以都按外部的来。
那么这种按照语法规则书写的
CSS
样式表式如何转换为
Webkit
内部的
CSS
模型的呢,这自然需要通过词法语法分析。在这里,
Webkit
使用了 自动代码生成工具生成了相应的代码,也就是说词法分析和语法分析这部分代码是自动生成的,但它们不够完整,然后我们需要自己写一些配合性的代码才能让真个
CSS
模块工作起来,说的再白一些,就是需要我们自己是写一些函数让那些个自动生成的代码来
Call Back
,用过其他各类解析器的朋友们应该很熟悉这个吧。如果谁对这部分代码有兴趣,可以研究一下。我倒是曾经为找一个跨平台的
bug
调过这部分代码,结 构还是蛮简单的,代码看起来稍多了些。入口是
yylex
和
yyparse
,有兴趣可以自己看看。
那么
Webkit
中实现的这些个
Call Back
们在哪里呢?就在
CSSParser
中了,显然,刨去生成的代码不说,需要手工完成的
CSS
解析代码部分就是这个了。
CSS
的一些解析功能的入口 也在此处,它们会调用
lex
,
parse
等生成代码。相对的,生成代码中需要的
Call Back
也需要在这里实现。
举例来说,现在可以来看一个较大单位的回调函数的实现,
createStyleRule()
,该函数将在一般性的规则需要被建立的时候调用。
CSSRule* CSSParser::createStyleRule(CSSSelector* selector)
{
CSSStyleRule* rule = 0;
if (selector) {
rule = new CSSStyleRule(styleElement);
m_parsedStyleObjects.append(rule);
rule->setSelector(sinkFloatingSelector(selector));
rule->setDeclaration(new CSSMutableStyleDeclaration(rule, parsedProperties, numParsedProperties));
}
clearProperties();
return rule;
}
从该函数的实现可以很清楚的看到,解析器达到某条件需要创建一个
CSSStyleRule
的时候将调用该函数,该函数的功能是创建一个
CSSStyleRule
,并将其添加已解析的样式对象列表
m_parsedStyleObjects
中去,这里的对象就是指的
Rule
。那么如此一来, 经过这样一番解析后,作为输入的样式表中的所有
Style Rule
将被转化为
Webkit
的内部模型对象
CSSStyleRule
对象,存储在
m_parsedStyleObjects
中,它是一个Vector。
像这样的函数还有
createCharsetRule
,
createImportRule
,
createMediaRule
等等,它们的作用大体上和
createStyleRule
类似,都是为创建
Rule
而准备的,只不过是不同类型的
Rule
。
了解了上面这些,大体上能够就能够了解
CSS
解析式怎么运作的。但是我们解析所要的结果是什么?通过调用
CSSStyleSheet
的
parseString
函数,上
CSS
解析过程将启动,解析完一遍后,所有的
Rule
都将存储在对应的
CSSStyleSheet
对象中。但是这个时候的 规则依然是不易于处理的,需要将之转换为
CSSRuleSet
,
CSSRuleSet
提供了一个
addRulesFromSheet
方法,能将
CSSStyleSheet
中的
rule
转换为
CSSRuleSet
中的
rule
,这样所有的纯样式规则都会放存储在对应的集合当中,这种集合的抽象就是
CSSRuleSet
。以后就可以基于这些个
CSSRuleSet
来决定每个页面中的元素的样式了,后面会有介绍。
5、CSS如何作用于Render Tree
所谓的作用于
Render Tree
其实是指基于上面的解析成果来为相应的
Render Object
来指定特定的样式,这个样式的抽象就是
RenderStyle
。