当前位置: 首页 > Web前端 > CSS

浏览器内核的CSS解释器和样式布局

时间:2023-03-30 22:58:20 CSS

微信公众号:爱写bug的阿拉斯加,有什么问题或者建议请在后台留言,我会尽力解决你的问题.前言这篇文章是对最近看的《WebKit技术内幕》这本书的一些理解和笔记。而【WebKit技术内幕】是对基于WebKit的Chromium项目的讲解。本书继续从上面浏览器核心的HTML解释器和DOM模型分析WebKit的CSS解释器和样式布局。从整个网页的加载和渲染过程来看,CSS解释器和规则匹配是在DOM树建立之后,RenderObject树建立之前,会保存CSS解释器的解释结果,然后RenderObject树会进行规格匹配和Layout计算。当网页有用户交互或动画等动作时,JavaScript代码也可以通过CSSDOM等技术非常方便地修改CSS代码。这时候WebKit就需要重新解释样式,重复上面的过程。一、CSS的基本功能1.1.1简介CSS全称CascadingStyleSheet,中文名称为层叠样式表,主要用于控制网页的显示样式。1.1.2样式规则图6-1描述了一个典型的CSS规则结构。规则由两部分组成——规则门和规则体。规则头由一个或多个选择器组成;规则体由一个或多个样式声明组成,每个样式声明由一个样式名和一个样式值组成,表示该规则指定和设置了哪些样式。当HTML中的元素通过后续匹配算法使用此规则时,则这些样式将设置为该元素的样式,除非有更高优先级的规则匹配该元素。1.1.3选择器CSS选择器是用于匹配相应HTML元素的一级模式。Whentheselectormatchesthecorrespondingelement,thevariousstylevalues??containedintheselectorwillactonthematchedelement.通过选择器,CSS可以精确控制HTML页面中任意一个或多个元素的样式属性。具体的我们这里就不介绍了,请参考CSS规范。1.1.4盒模型盒模型(Boxmodel,或盒模型)就是我们常说的盒模型,它是在CSS标准中引入的,用来表示HTML标签元素的布局结构。一个盒子模型大致包括四部分:边距(Margin)、边框(Border)、内边距(Padding)和内容(Content)。1.4.5包含块模型WebKit在计算元素框的位置和大小时,WebKit需要计算元素与另一个矩形区域的相对位置,称为元素的包含块。上面介绍的盒模型计算并确定包含块中的每个元素。包含块的具体定义如下:根元素的包含块称为初始包含块,其大小通常为视口的大小。对于position属性设置为“static”或“relative”的其他元素,其包含块是最近祖先的盒模型中的内容区域(Content)。如果元素的位置属性是“固定的”,那么元素的包含在HTML文档之外,因为它固定在可见区域的特定位置。如果元素的position属性为“absolute”,则该元素的包含块由具有“absolute”、“relative”或“fixed”属性的最近祖先决定,具体规则如下:如果一个元素具有“inline”属性,那么元素的包含块就是这个祖先的第一个和最近的内联框的填充包围的区域;否则,包含块是该祖先的填充所包围的区域。1.1.6CSS样式属性CSS标准定义了各种样式属性来描述元素的显示效果。这些属性大致分为以下几类:Background:比如背景颜色,背景图片。文本:设置文本缩进、对齐方式。字间距。字母间距。字符转换、装饰和空白字符等。字体:设置字体属性,可以嵌入或自定义。此外,还可以设置加粗、变形等属性。列表:设置列表类型,列表可以用字母、希腊字母、数字等进行编号。表格:通过设置边框来达到显示表格视觉效果的目的。设置是否将表格边框合并为单个边框,设置单元格边框分隔距离,设置表头位置,设置是否显示表格中的空单元格,设置单元格、行、列的显示算法等。定位:CSS提供元素的相对、绝对和浮动定位。1.1.7CSSOM(CSS对象模型)CSSOM全称为CSS对象模型。它的思想是在DOM中的一些节点接口中加入JavaScript获取和操作CSS属性或接口的接口,使JavaScript可以动态地操作CSS样式。DOM为JavaScript提供了修改HTML文档的接口。同样,CSSOM也为JavaScript提供了一个接口,用于获取和修改CSS代码设置的样式信息。对于内部和外部样式表,CSSOM定义了样式表的接口,称为“CSSStyleSheet”,这是一个可以在JavaScript代码中访问的接口。通过该接口,开发者可以获取JavaScript中样式表的各种信息,如CSS“href”、样式表类型“type”、规则信息“cssRules”等,甚至可以获取样式中的CSS规则列表床单。该接口不同于DOM中的“Script”节点或“Link”节点,它是CSSOM定义的新接口。开发者可以通过document.styleSheets查看当前网页包含的所有CSS样式表。这是因为CSSOM扩展了DOM中的Document接口。以下是新增加的属性:W3C还定义了另外一个规范是CSSDOMView,它的基本含义是为Window、Document、Element、HTMLElement、MouseEvent等界面增加一些新的属性。这些CSS属性允许JavaScript获取视图信息,这些信息用于表示与视图相关的特征,例如窗口大小和网页滚动位移,元素的框位置,鼠标事件的坐标等信息。下面是CSSDOMView对Window的扩展:1.2CSS解释器和规则匹配1.2.1StyleWebKit表示类对于CSS样式表,是否嵌入或外部文档,WebKit使用CSSStyleSheet类来表示。图6-5描述了WebKit在内部如何表示CSS文档。一切都从DOM的文档类开始。DoucmentStyleSheetCollection类,包含CSSStyleSheet,CSS样式表WebKit的内部表示类,包含CSS的href、type、content等信息。CSS的内容是样式信息StyleSheetContent,它包含了样式规则列表(StyleRuleBase)。样式规则用于CSS解释器的工作过程。WebKit下面的部分主要是整理解释规则,为DOM中的元素匹配相应的规则,从而应用规则中的属性值序列。这个过程的主要负责人是StyleSheetResolver类,它属于Document类,包含一个DocumentRuleSets类来表示多个规则集(RuleSet)。每个规则集就是把前面解释后的结果结合起来,进行分类,比如id类规则,label类规则等。至于为什么会有多个规则集,是因为这些规则集可能来源于default规则集,或网页的自定义规则集。1.2.2解释过程CSS解释过程是指CSS字符串经过CSS解释器处理后,对渲染引擎内部规则的表示过程。在WebKit中,流程如图6-8所示。基本思想是这个过程由CSSParser类负责。CSSParser类实际上是一个桥接类,实际的解释工作是由CSSGrammer.y.in完成的。CSSGrammer.y.in是Bison的输入文件,Bison是一种生成解释器的工具。Bison基于CSSGrammer.y.in——CSSGrammer类生成一个CSS解释器。当然,CSSGrammer类需要调用CSSParser类来处理解释结果,比如需要使用CSSParser类来创建选择器对象、属性、规则等。在解释网页中的自定义CSS样式之前,WebKit渲染引擎实际上为每一个网页都设置了一个默认样式,它决定了网页上没有设置的元素属性,它们的默认值,以及要显示的效果。一般来说,不同的WebKit端口可以设置不同的默认样式。以下是Chrome浏览器使用的默认样式,决定了默认的网页显示。1.2.4样式规则匹配样式规则建立后,WebKit将规则的结果保存在DocumentRuleSets对象类中。当DOM节点被创建时,WebKit会为其中的一些节点(仅可见节点)选择合适的样式信息。这些任务由StyleResolver处理。当然,实际的匹配工作还是在DocumentRuleSets类中完成的。图6-9描述了样式规则匹配涉及的主要WebKit相关类。基本思想是使用StyleResolver类来匹配DOM元素节点的样式。StyleResolver类根据元素的标签名称、类别等信息,从样式规则中找到最匹配的规则,然后将样式信息保存到新建的RenderStyle对象中。最后,这些RenderStyle对象最终交由RenderObject类来管理和使用。规则的匹配是通过ElementRuleCollector类计算得到的。它根据元素的属性等从DocumentRuleSets类中获取规则集,依次匹配ID、category、label等选择器信息,获取元素的样式。首先,当WebKit需要为HTML元素创建RenderObject类时,StyleResolver类负责获取样式信息并返回包含匹配样式结果的RenderStyle对象。其次,根据实际需要,每个元素可能需要匹配不同来源的规则,依次是用户代理(浏览器)规则集、用户规则集、HTML网页中包含的自定义规则集。这三个规则以类似的方式匹配。这里以自定义规则匹配为例进行说明。同样,对于自定义规则集,它首先查找ID规则,检查匹配规则,然后依次检查类型规则、标签规则等。如果某个规则与该元素匹配,WebKit会将这些规则保存在匹配结果中。最后,WebKit对规则进行排序。对于此元素所需的样式属性,WebKit从优先级较高的规则中选择并返回样式属性值。1.2.5JavaScript样式CSSDOM定义了JavaScript访问样式的能力和方法。使用CSSDOM接口改变属性值的过程,在WebKit中,这需要JavaScript引擎和渲染引擎的配合。粗略的想法是,JavaScript引擎调用设置属性值的公共处理函数,然后调用属性值解析函数,在本例中是CSS的JavaScript绑定函数。WebKit再将解释后的信息设置到元素的“style”属性的style“webkitTransform”中,然后设置flag表示元素需要重新计算样式,触发重新计算布局。最后是WebKit的重绘,图6-12描述了主要流程。1.3WebKit布局1.3.1基础WebKit创建RenderObject对象后,每个对象并不知道自己的位置、大小等信息。WebKit根据框架模型计算它们的位置、大小等信息的过程称为布局计算。第5章介绍了Frame类,用来表示网页的框架结构,每个框架都有一个FrameView类,用来表示框架的视图结构。FrameView类主要负责视图任务,如网页视图大小、滚动、布局计算、绘制等,是一个通用的入口类。“layout”和“needsLayout”,用于布局计算,判断是否需要布局计算,实际的布局计算在RenderObject类中。布局计算根据其计算的范围大致可以分为两类:第一类是对整个RenderObject树的计算;第二类是RenderObject树中某个子树的计算,常见于文本元素或者overflow:auto块中,这种情况下,其子树布局的变化不会影响到其周围元素的布局,所以没有需要在更大范围内重新计算布局。1.3.2布局计算布局计算是一个递归的过程,因为一个节点的大小通常需要先计算出它的子节点的位置和大小。图6-14描述了计算RenderObject节点布局的主要过程。省略了很多判断和步骤,主要逻辑由RenderObject类的“布局”功能完成。首先这个函数会判断RenderObject节点是否需要重新计算,通常是通过检查位数组中对应的标志位,child是否需要计算布局等。其次这个函数会判断网页的宽度还有垂直方向的边距,因为网页通常是垂直方向滚动的,水平方向尽量不用滚动。同样,该函数遍历它的每个子节点并依次计算它们的布局。每个元素都实现自己的“布局”功能,该功能根据特定算法计算该类型元素的布局。如果页面元素定义了自己的宽度和高度,那么WebKit会根据定义的宽度和高度来确定元素的大小。对于文本节点等行内元素,需要结合其字体大小和文本大小来确定其对应的宽度。高的。如果页面元素决定的宽高超过了布局容器的包含块提供的宽高,并且其overflow属性为visible或者auto,WebKit会提供滚动条来保证其所有内容都能显示出来。除非网页定义了页面元素的宽高,一般来说,页面元素的宽高都是在布局时通过关联计算出来的。如果元素有子元素,WebKit需要递归这个过程。最后节点根据自己孩子的大小计算出自己的高度,整个过程结束。重新布局的情况:首先,在第一次打开网页时,浏览器设置网页的可见区域(viewport),调用计算布局的方法。这其实描述了一个常见的场景,就是当可见区域发生变化时,WebKit需要重新计算布局,因为网页的包含块的大小发生了变化。其次,网页动画触发布局计算。当网页显示时,动画可能会改变style属性,所以WebKit需要重新计算。然后JavaScript代码直接通过CSSDOM等方式修改样式信息,同样会触发WebKit重新计算布局。最后,布局计算也由用户交互触发,例如滚动网页,这会触发新的区域布局计算。CSS的布局计算是基于包含块和盒模型的,也就是说这些元素的布局计算都是依赖于块的,比如“div”通常就是一个块,前面说到它们通常会在垂直方向展开.但是,CSS标准还规定了行布局形式,它们是内联元素。行内元素呈现行布局,这意味着元素显示在行中。以“div”元素为例,如果属性“style”设置为“displa:inline”,则该元素是一个内联元素,那么它可能与前一个元素在同一行。如果该元素没有设置此属性,则它是块元素并显示在新行中。这显然会增加处理的复杂度。为此,WebKit的处理方式是——对于一个块元素对应的RenderObject对象,它的子对象要么是块元素对应的RenderObject对象,要么是非行内元素对应的RenderObject对象,这可以通过创建匿名块来实现(匿名块)对象。布局计算比较耗时,而且一旦布局发生变化,WebKit需要稍后重绘。另一方面,减少样式变化,依托HTML5的新特性,可以有效提高网页的渲染效率。总结匹配算法结合CSS规则设置样式选择器是选择一个元素的盒子模型,通常称为盒子模型,包括margin,border,padding,contentCSSOM称为CSS对象模型,JavaScript可以获取和操作CSS特性。CSS解释过程是指CSS字符串经过CSS解释器处理后,对渲染引擎内部规则的表示过程。WebKit创建RenderObject对象后,每个对象都不知道自己的位置、大小等信息。WebKit根据框架模型(Frame类的FrameView)计算它们的位置和大小的过程称为布局计算。布局计算是一个递归的过程,而且还会重新布局。最后,希望这篇文章对你有所帮助。敬请期待下次分享第七章渲染基础。对全栈开发感兴趣的朋友可以扫描下方二维码关注我公众号——爱写bug的阿拉斯加,分享web开发相关技术文章、热点资源、全栈程序员的成长之路.