媒介
史上最长的分享,编辑的时间表现感情稳固。本日早读文章必要你很长的时间阅读,由@ikeike443和@kiyoto01翻译分享。
正文从这开始~
这是一篇全面先容WebKit和Gecko内部操纵的入门文章,是以色列开辟职员塔利·加希尔大量研究的结果。在已往的几年中,她查阅了全部公开辟布的关于欣赏器内部机制的数据(请拜见资源),并花了很多时间来研读网络欣赏器的源代码。她写道:
在IE占据90%市场份额的年代,我们除了把欣赏器当成一个“黑箱”,什么也做不了。但是如今,开放源代码的欣赏器拥有了过半的市场份额,因此,是时间来揭开秘密的面纱,一探网络欣赏器的黑幕了。呃,内里只有数以百万行计的C++代码...
塔利在她的网站上公布了本身的研究结果,但是我们以为它值得让更多的人来相识,以是我们在此重新整理并公布。
作为一名网络开辟职员,学习欣赏器的内部工作原理将有助于您作出更明智的决定,并明白那些最佳开辟实践的个中缘由。只管这是一篇相称长的文档,但是我们发起您花些时间来细致阅读;读完之后,您肯定会以为所费不虚。
保罗·爱丽诗(PaulIrish),Chrome欣赏器开辟职员事件部
简介
网络欣赏器很大概是利用最广的软件。在这篇入门文章中,我将会先容它们的幕后工作原理。我们会相识到,从您在地点栏输入google.com直到您在欣赏器屏幕上看到Google首页的整个过程中都发生了些什么。
我们要讨论的欣赏器
如今利用的主流欣赏器有五个:InternetExplorer、Firefox、Safari、Chrome欣赏器和Opera。本文中以开放源代码欣赏器为例,即Firefox、Chrome欣赏器和Safari(部分开源)。根据StatCounter欣赏器统计数据,如今(2011年8月)Firefox、Safari和Chrome欣赏器的总市场占据率将近60%。由此可见,如今开放源代码欣赏器在欣赏器市场中占据了非常坚固的部分。
欣赏器的重要功能
欣赏器的重要功能就是向服务器发出哀求,在欣赏器窗口中展示您选择的网络资源。这里所说的资源一样平常是指HTML文档,也可以是PDF、图片或其他的范例。资源的位置由用户利用URI(同一资源标示符)指定。
欣赏器表明并表现HTML文件的方式是在HTML和CSS规范中指定的。这些规范由网络标准化构造W3C(万维网同盟)举行维护。
多年以来,各欣赏器都没有完全服从这些规范,同时还在开辟本身独有的扩展程序,这给网络开辟职员带来了严峻的兼容性题目。如今,大多数的欣赏器都是或多或少地服从规范。
欣赏器的用户界面有很多相互雷同的元素,此中包罗:
用来输入URI的地点栏
进步和退却按钮
书签设置选项
用于革新和克制加载当前文档的革新和克制按钮
用于返回主页的主页按钮
奇怪的是,欣赏器的用户界面并没有任何正式的规范,这是多年来的最佳实践天然发展以及相互之间相互模仿的结果。HTML5也没有界说欣赏器必须具有的用户界面元素,但列出了一些通用的元素,比方地点栏、状态栏和工具栏等。固然,各欣赏器也可以有本身独特的功能,比如Firefox的下载管理器。
欣赏器的高层布局
欣赏器的重要组件为(1.1):
用户界面-包罗地点栏、进步/退却按钮、书签菜单等。除了欣赏器主窗口表现的您哀求的页面外,其他表现的各个部分都属于用户界面。
欣赏器引擎-在用户界面和出现引擎之间传送指令。
出现引擎-负责表现哀求的内容。假如哀求的内容是HTML,它就负责分析HTML和CSS内容,并将分析后的内容表现在屏幕上。
网络-用于网络调用,比如HTTP哀求。其接口与平台无关,并为全部平台提供底层实现。
用户界面后端-用于绘制根本的窗口小部件,比如组合框和窗口。其公开了与平台无关的通用接口,而在底层利用操纵体系的用户界面方法。
Java表明器。用于分析和实行Java代码。
数据存储。这是长期层。欣赏器必要在硬盘上生存各种数据,比方Cookie。新的HTML规范(HTML5)界说了“网络数据库”,这是一个完备(但是轻便)的欣赏器内数据库。
欣赏器的重要组件
值得留意的是,和大多数欣赏器差别,Chrome欣赏器的每个标签页都分别对应一个出现引擎实例。每个标签页都是一个独立的进程。
出现引擎
出现引擎的作用嘛...固然就是“出现”了,也就是在欣赏器的屏幕上表现哀求的内容。
默认环境下,出现引擎可表现HTML和XML文档与图片。通过插件(或欣赏器扩展程序),还可以表现其他范例的内容;比方,利用PDF查察器插件就能表现PDF文档。但是在本章中,我们将会合先容其重要用途:表现利用CSS格式化的HTML内容和图片。
出现引擎
本文所讨论的欣赏器(Firefox、Chrome欣赏器和Safari)是基于两种出现引擎构建的。Firefox利用的是Gecko,这是Mozilla公司“自制”的出现引擎。而Safari和Chrome欣赏器利用的都是WebKit。
WebKit是一种开放源代码出现引擎,早先用于Linux平台,随后由Apple公司举行修改,从而支持苹果机和Windows。有关详情,请参阅webkit.org。
主流程
出现引擎一开始会从网络层获取哀求文档的内容,内容的巨细一样平常限定在8000个块以内。
然后举行如下所示的根本流程:
出现引擎的根本流程
出现引擎将开始分析HTML文档,并将各标记逐个转化成“内容树”上的DOM节点。同时也会分析外部CSS文件以及样式元素中的样式数据。HTML中这些带有视觉指令的样式信息将用于创建另一个树布局:出现树。
出现树包罗多个带有视觉属性(如颜色和尺寸)的矩形。这些矩形的分列次序就是它们将在屏幕上表现的次序。
出现树构建完毕之后,进入“布局”处理惩罚阶段,也就是为每个节点分配一个应出如今屏幕上简直切坐标。下一个阶段是绘制-出现引擎会遍历出现树,由用户界面后端层将每个节点绘制出来。
必要偏重指出的是,这是一个渐进的过程。为到达更好的用户体验,出现引擎会力图尽快将内容表现在屏幕上。它不必比及整个HTML文档分析完毕之后,就会开始构建出现树和设置布局。在不绝吸取和处理惩罚来自网络的别的内容的同时,出现引擎会将部分内容分析并表现出来。
主流程示例
WebKit主流程
Mozilla的Gecko出现引擎主流程
从图3和图4可以看出,固然WebKit和Gecko利用的术语略有差别,但团体流程是根本雷同的。
Gecko将视觉格式化元素构成的树称为“框架树”。每个元素都是一个框架。WebKit利用的术语是“出现树”,它由“出现对象”构成。对于元素的放置,WebKit利用的术语是“布局”,而Gecko称之为“重排”。对于毗连DOM节点和可视化信息从而创建出现树的过程,WebKit利用的术语是“附加”。有一个渺小的非语义差别,就是Gecko在HTML与DOM树之间尚有一个称为“内容槽”的层,用于天生DOM元素。我们会逐一叙述流程中的每一部分:
分析-综述
分析是出现引擎中非常紧张的一个环节,因此我们要更深入地讲授。起首,来先容一下分析。
分析文档是指将文档转化成为故意义的布局,也就是可让代码明白和利用的布局。分析得到的结果通常是代表了文档布局的节点树,它称作分析树大概语法树。
示例-分析2+3-1这个表达式,会返回下面的树:
数学表达式树节点
语法
分析是以文档所依照的语法规则(编写文档所用的语言或格式)为底子的。全部可以分析的格式都必须对应确定的语法(由词汇和语法规则构成)。这称为与上下文无关的语法。人类语言并不属于如许的语言,因此无法用通例的分析技能举行分析。
分析器和词法分析器的组合
分析的过程可以分成两个子过程:词法分析和语法分析。
词法分析是将输入内容分割成大量标记的过程。标记是语言中的词汇,即构成内容的单位。在人类语言中,它相称于语言字典中的单词。
语法分析是应用语言的语法规则的过程。
分析器通常将分析工作分给以下两个组件来处理惩罚:词法分析器(偶然也称为标记天生器),负责将输入内容分解成一个个有效标记;而分析器负责根据语言的语法规则分析文档的布局,从而构建分析树。词法分析器知道怎样将无关的字符(比如空格和换行符)分离出来。
从源文档到分析树
分析是一个迭代的过程。通常,分析器会向词法分析器哀求一个新标记,并实行将其与某条语法规则举行匹配。假如发现了匹配规则,分析器会将一个对应于该标记的节点添加到分析树中,然后继承哀求下一个标记。
假如没有规则可以匹配,分析器就会将标记存储到内部,并继承哀求标记,直至找到可与全部内部存储的标记匹配的规则。假如找不到任何匹配规则,分析器就会引发一个非常。这意味着文档无效,包罗语法错误。
翻译
很多时间,分析树还不是终极产物。分析通常是在翻译过程中利用的,而翻译是指将输入文档转换成另一种格式。编译就是如许一个例子。编译器可将源代码编译成呆板代码,具体过程是起首将源代码分析成分析树,然后将分析树翻译成呆板代码文档。
编译流程
分析示例
在图5中,我们通过一个数学表达式创建了分析树。如今,让我们试着界说一个简单的数学语言,用来演示分析的过程。
词汇:我们用的语言可包罗整数、加号和减号。
语法:
构成语言的语法单位是表达式、项和运算符。
我们用的语言可以包罗恣意数量的表达式。
表达式的界说是:一个“项”接一个“运算符”,然后再接一个“项”。
运算符是加号或减号。
项是一个整数或一个表达式。
让我们分析一下2+3-1。
匹配语法规则的第一个子串是2,而根据第5条语法规则,这是一个项。匹配语法规则的第二个子串是2+3,而根据第3条规则(一个项接一个运算符,然后再接一个项),这是一个表达式。下一个匹配项已经到了输入的竣事。2+3-1是一个表达式,由于我们已经知道2+3是一个项,如许就符合“一个项接一个运算符,然后再接一个项”的规则。2++不与任何规则匹配,因此是无效的输入。
词汇和语法的正式界说
词汇通常用正则表达式表现。
比方,我们的示例语言可以界说如下:
INTEGER:0|[1-9][0-9]*
PLUS:+
MINUS:-
正如您所看到的,这里用正则表达式给出了整数的界说。
语法通常利用一种称为BNF的格式来界说。我们的示例语言可以界说如下:
expression:=termoperationterm
operation:=PLUS|MINUS
term:=INTEGER|expression
之前我们说过,假如语言的语法是与上下文无关的语法,就可以由通例分析器举行分析。与上下文无关的语法的直观界说就是可以完全用BNF格式表达的语法。有关正式界说,请参阅关于与上下文无关的语法的维基百科文章。
分析器范例
有两种根本范例的分析器:自上而下分析器和自下而上分析器。直观地来说,自上而下的分析器从语法的高层布局出发,实行从中找到匹配的布局。而自下而上的分析器从低层规则出发,将输入内容渐渐转化为语法规则,直至满意高层规则。
让我们来看看这两种分析器会怎样分析我们的示例:
自上而下的分析器会从高层的规则开始:起首将2+3标识为一个表达式,然后将2+3-1标识为一个表达式(标识表达式的过程涉及到匹配其他规则,但是出发点是最高级别的规则)。
自下而上的分析器将扫描输入内容,找到匹配的规则后,将匹配的输入内容更换成规则。云云继承更换,直到输入内容的末了。部分匹配的表达式生存在分析器的堆栈中。
这种自下而上的分析器称为移位归约分析器,由于输入在向右移位(假想有一个指针从输入内容的开头移动到末了),而且渐渐归约到语法规则上。
主动天生分析器
有一些工具可以资助您天生分析器,它们称为分析器天生器。您只要向其提供您所用语言的语法(词汇和语法规则),它就会天生相应的分析器。创建分析器必要对分析有深刻明白,而人工创建并优化分析器并不是一件轻易的事变,以是分析器天生器黑白常实用的。
WebKit利用了两种非常闻名的分析器天生器:用于创建词法分析器的Flex以及用于创建分析器的Bison(您也大概碰到Lex和Yacc如许的别名)。Flex的输入是包罗标记的正则表达式界说的文件。Bison的输入是采取BNF格式的语言语法规则。
HTML分析器
HTML分析器的任务是将HTML标记分析成分析树。
HTML语法界说
HTML的词汇和语法在W3C构造创建的规范中举行了界说。当前的版本是HTML4,HTML5正在处理惩罚过程中。
非与上下文无关的语法
正如我们在分析过程的简介中已经相识到的,语法可以用BNF等格式举行正式界说。
很遗憾,全部的通例分析器都不实用于HTML(我并不是开顽笑,它们可以用于分析CSS和Java)。HTML并不能很轻易地用分析器所需的与上下文无关的语法来界说。
有一种可以界说HTML的正规格式:DTD(DocumentTypeDefinition,文档范例界说),但它不是与上下文无关的语法。
这初看起来很奇怪:HTML和XML非常相似。有很多XML分析器可以利用。HTML存在一个XML变体(XHTML),那么有什么大的区别呢?
区别在于HTML的处理惩罚更为“宽容”,它答应您省略某些隐式添加的标记,偶然还能省略一些起始大概竣事标记等等。和XML严格的语法差别,HTML团体来看是一种“软性”的语法。
显然,这种看上去渺小的差别实际上却带来了巨大的影响。一方面,这是HTML云云盛行的缘故起因:它能包涵您的错误,简化网络开辟。另一方面,这使得它很难编写正式的语法。概括地说,HTML无法很轻易地通过通例分析器分析(由于它的语法不是与上下文无关的语法),也无法通过XML分析器来分析。
HTMLDTD
HTML的界说采取了DTD格式。此格式可用于界说SGML族的语言。它包罗全部答应利用的元素及其属性和条理布局的界说。如上文所述,HTMLDTD无法构成与上下文无关的语法。
DTD存在一些变体。严格模式完全服从HTML规范,而其他模式可支持从前的欣赏器所利用的标记。如许做的目标是确保向下兼容一些早期版本的内容。最新的严格模式DTD可以在这里找到:www.w3.org/TR/html4/strict.dtd
DOM
分析器的输出“分析树”是由DOM元素和属性节点构成的树布局。DOM是文档对象模子(DocumentObjectModel)的缩写。它是HTML文档的对象表现,同时也是外部内容(比方Java)与HTML元素之间的接口。
分析树的根节点是“Document”对象。
DOM与标记之间险些是逐一对应的关系。比如下面这段标记:
html
body
p
HelloWorld
/p
divimgsrc="example.png"//div
/body
/html
可翻译成如下的DOM树:
示例标记的DOM树
和HTML一样,DOM也是由W3C构造指定的。请拜见www.w3.org/DOM/DOMTR。这是关于文档操纵的通用规范。此中一个特定模块形貌针对HTML的元素。HTML的界说可以在这里找到:www.w3.org/TR/2003/REC-DOM-Level-2-HTML-20030109/idl-definitions.html。
我所说的树包罗DOM节点,指的是树是由实现了某个DOM接口的元素构成的。欣赏器在具体的实现中会有一些供内部利用的其他属性。
分析算法
我们在之前章节已经说过,HTML无法用通例的自上而下或自下而上的分析器举行分析。
缘故起因在于:
语言的宽容本质。
欣赏器向来对一些常见的无效HTML用法采取包涵态度。
分析过程必要不绝地反复。源内容在分析过程中通常不会改变,但是在HTML中,脚本标记假如包罗document.write,就会添加额外的标记,如许分析过程实际上就更改了输入内容。
由于不能利用通例的分析技能,欣赏器就创建了自界说的分析器来分析HTML。
HTML5规范具体地形貌了分析算法。此算法由两个阶段构成:标记化和树构建。
标记化是词法分析过程,将输入内容分析成多个标记。HTML标记包罗起始标记、竣事标记、属性名称和属性值。
标记天生器辨认标记,转达给树构造器,然后担当下一个字符以辨认下一个标记;云云反复直到输入的竣事。
HTML分析流程(摘自HTML5规范)
标记化算法
该算法的输出结果是HTML标记。该算法利用状态机来表现。每一个状态吸取来自输入信息流的一个或多个字符,并根据这些字符更新下一个状态。当前的标记化状态和树布局状态会影响进入下一状态的决定。这意味着,纵然吸取的字符雷同,对于下一个精确的状态也会产生差别的结果,具体取决于当前的状态。该算法相称复杂,无法在此详述,以是我们通过一个简单的示例来资助各人明白其原理。
根本示例-将下面的HTML代码标记化:
html
body
Helloworld
/body
/html
初始状态是数据状态。碰到字符时,状态更改为“标记打开状态”。吸取一个a-z字符会创建“起始标记”,状态更改为“标记名称状态”。这个状态会不停保持到吸取字符。在此期间吸取的每个字符都会附加到新的标记名称上。在本例中,我们创建的标记是html标记。
碰到标记时,会发送当前的标记,状态改回“数据状态”。body标记也会举行同样的处理惩罚。如今html和body标记均已发出。如今我们回到“数据状态”。吸取到Helloworld中的H字符时,将创建并发送字符标记,直到吸取/body中的。我们将为Helloworld中的每个字符都发送一个字符标记。
如今我们回到“标记打开状态”。吸取下一个输入字符/时,会创建endtagtoken并改为“标记名称状态”。我们会再次保持这个状态,直到吸取。然后将发送新的标记,并回到“数据状态”。/html输入也会举行同样的处理惩罚。
对示例输入举行标记化
树构建算法
在创建分析器的同时,也会创建Document对象。在树构建阶段,以Document为根节点的DOM树也会不绝举行修改,向此中添加各种元素。标记天生器发送的每个节点都会由树构建器举行处理惩罚。规范中界说了每个标记所对应的DOM元素,这些元素会在吸取到相应的标记时创建。这些元素不但会添加到DOM树中,还会添加到开放元素的堆栈中。此堆栈用于改正嵌套错误和处理惩罚未关闭的标记。其算法也可以用状态机来形貌。这些状态称为“插入模式”。
让我们来看看示例输入的树构建过程:
html
body
Helloworld
/body
/html
树构建阶段的输入是一个来自标记化阶段的标记序列。第一个模式是“initialmode”。吸取HTML标记后转为“beforehtml”模式,并在这个模式下重新处理惩罚此标记。如许会创建一个HTMLHtmlElement元素,并将其附加到Document根对象上。
然后状态将改为“beforehead”。此时我们吸取“body”标记。纵然我们的示例中没有“head”标记,体系也会隐式创建一个HTMLHeadElement,并将其添加到树中。
如今我们进入了“inhead”模式,然后转入“afterhead”模式。体系对body标记举行重新处理惩罚,创建并插入HTMLBodyElement,同时模式变化为“inbody”。
如今,吸取由“Helloworld”字符串天生的一系列字符标记。吸取第一个字符时会创建并插入“Text”节点,而其他字符也将附加到该节点。
吸取body竣事标记会触发“afterbody”模式。如今我们将吸取HTML竣事标记,然后进入“afterafterbody”模式。吸取到文件竣事标记后,分析过程就此竣事。
示例HTML的树构建
分析竣事后的操纵
在此阶段,欣赏器会将文档标注为交互状态,并开始分析那些处于“deferred”模式的脚本,也就是那些应在文档分析完成后才实行的脚本。然后,文档状态将设置为“完成”,一个“加载”变乱将随之触发。
您可以在HTML5规范中查察标记化和树构建的完备算法
欣赏器的容错机制
您在欣赏HTML网页时从来不会看到“语法无效”的错误。这是由于欣赏器会改正任何无效内容,然后继承工作。
以下面的HTML代码为例:
html
mytag
/mytag
div
p
/div
ReallylousyHTML
/p
/html
在这里,我已经违背了很多语法规则(“mytag”不是标准的标记,“p”和“div”元素之间的嵌套有误等等),但是欣赏器仍旧会精确地表现这些内容,而且毫无怨言。由于有大量的分析器代码会改正HTML网页作者的错误。
差别欣赏器的错误处理惩罚机制相称同等,但令人称奇的是,这种机制并不是HTML当前规范的一部分。和书签管理以及进步/退却按钮一样,它也是欣赏器在多年发展中的产物。很多网站都广泛存在着一些已知的无效HTML布局,每一种欣赏器都会实行通过和其他欣赏器一样的方式来修复这些无效布局。
HTML5规范界说了一部分如许的要求。WebKit在HTML分析器类的开头解释中对此做了很好的概括。
分析器对标记化输入内容举行分析,以构建文档树。假如文档的格式精确,就直接举行分析。
遗憾的是,我们不得不处理惩罚很多格式错误的HTML文档,以是分析器必须具备肯定的容错性。
我们至少要可以或许处理惩罚以下错误环境:
显着不能在某些外部标记中添加的元素。在此环境下,我们应该关闭全部标记,直到出现克制添加的元素,然后再参加该元素。
我们不能直接添加的元素。这很大概是网页作者忘记添加了此中的一些标记(大概此中的标记是可选的)。这些标签大概包罗:HTMLHEADBODYTBODYTRTDLI(尚有遗漏的吗?)。
向inline元素内添加block元素。关闭全部inline元素,直到出现下一个较高级的block元素。
假如如许仍旧无效,可关闭全部元素,直到可以添加元素为止,大概忽略该标记。
让我们看一些WebKit容错的示例:
利用了/br而不是br
有些网站利用了/br而不是br。为了与IE和Firefox兼容,WebKit将其与br做同样的处理惩罚。
代码如下:
if(t-isCloseTag(brTag)m_document-inCompatMode()){
reportError(MalformedBRError);
t-beginTag=true;
}
请留意,错误处理惩罚是在内部举行的,用户并不会看到这个过程。
离散表格
离散表格是指位于其他表格内容中,但又不在任何一个单位格内的表格。
比如以下的示例:
table
table
trtdinnertable/td/tr
/table
trtdoutertable/td/tr
/table
WebKit会将其条理布局更改为两个同级表格:
table
trtdoutertable/td/tr
/table
table
trtdinnertable/td/tr
/table
代码如下:
if(m_inStrayTableContentlocalName==tableTag)
popBlock(tableTag);
WebKit利用一个堆栈来生存当前的元素内容,它会从外部表格的堆栈中弹出内部表格。如今,这两个表格就变成了同级关系。
嵌套的表单位素
假如用户在一个表单位素中又放入了另一个表单,那么第二个表单将被忽略。
代码如下:
if(!m_currentFormElement){
m_currentFormElement=newHTMLFormElement(formTag,m_document);
}
过于复杂的标记条理布局
代码的解释已经说得很清楚了。
示例网站www.liceo.edu.mx嵌套了约1500个标记,全都来自一堆b标记。我们只答应最多20层同范例标记的嵌套,假如再嵌套更多,就会全部忽略。
boolHTMLParser::allowNestedRedundantTag(constAtomicStringtagName)
{
unsignedi=0;
for(HTMLStackElem*curr=m_blockStack;
icMaxRedundantTagDepthcurrcurr-tagName==tagName;
curr=curr-next,i++){}
returni!=cMaxRedundantTagDepth;
}
放错位置的html大概body竣事标记
同样,代码的解释已经说得很清楚了。
支持格式非常糟糕的HTML代码。我们从不关闭body标记,由于一些愚笨的网页会在实际文档竣事之前就关闭。我们通过调用end()来实行关闭操纵。
if(t-tagName==htmlTag||t-tagName==bodyTag)
return;
以是网页作者必要留意,除非您想作为反面讲义出如今WebKit容错代码段的示例中,否则还请编写格式精确的HTML代码。
CSS分析
还记得简介中分析的概念吗?和HTML差别,CSS是上下文无关的语法,可以利用简介中形貌的各种分析器举行分析。究竟上,CSS规范界说了CSS的词法和语法。
让我们来看一些示例:
词法语法(词汇)是针对各个标记用正则表达式界说的:
comment/*[^*]**+([^/*][^*]**+)*/
num[0-9]+|[0-9]*"."[0-9]+
nonascii[200-377]
nmstart[_a-z]|{nonascii}|{escape}
nmchar[_a-z0-9-]|{nonascii}|{escape}
name{nmchar}+
ident{nmstart}{nmchar}*
“ident”是标识符(identifier)的缩写,比如类名。“name”是元素的ID(通过“#”来引用)。
语法是采取BNF格式形貌的。
ruleset
:selector[','S*selector]*
'{'S*declaration[';'S*declaration]*'}'S*
;
selector
:simple_selector[combinatorselector|S+[combinator?selector]?]?
;
simple_selector
:element_name[HASH|class|attrib|pseudo]*
|[HASH|class|attrib|pseudo]+
;
class
:'.'IDENT
;
element_name
:IDENT|'*'
;
attrib
:'['S*IDENTS*[['='|INCLUDES|DASHMATCH]S*
[IDENT|STRING]S*]']'
;
pseudo
:':'[IDENT|FUNCTIONS*[IDENTS*]')']
;
表明:这是一个规则集的布局:
div.error,a.error{
color:red;
font-weight:bold;
}
div.error和a.error是选择器。大括号内的部分包罗了由此规则集应用的规则。此布局的正式界说是如许的:
ruleset
:selector[','S*selector]*
'{'S*declaration[';'S*declaration]*'}'S*
;
这表现一个规则集就是一个选择器,大概由逗号和空格(S表现空格)分隔的多个(数量可选)选择器。规则集包罗了大括号,以及此中的一个或多个(数量可选)由分号分隔的声明。“声明”和“选择器”将由下面的BNF格式界说。
关于本文
译者:@ikeike443和@kiyoto01
原文:https://www.html5rocks.com/zh/tutorials/internals/howbrowserswork/
我要评论