1. 引言
本节不是规范性的。
本模块定义 CSS 样式表的抽象语法和解析
以及其他使用 CSS 语法的内容
(例如 HTML style 属性)。
它定义了用于将 Unicode 代码点流 (换言之,即文本) 转换为 CSS 标记流的算法, 然后进一步转换为 CSS 对象, 例如样式表、规则和声明。
1.1. 模块交互
本模块定义 CSS 样式表的语法和解析。 它取代 CSS 2.1 中定义的词法扫描器和语法。
2. CSS 语法说明
本节不是规范性的。
CSS 文档是一系列样式 规则——它们是将样式应用于文档中元素的限定规则——以及 at-rule——它们为 CSS 文档定义特殊处理 规则或值。
限定规则以 前奏开始 然后具有一个由 {} 包裹、包含一系列声明的块。 前奏的含义会根据规则出现的上下文而变化——对于样式规则,它是一个选择器,用于指定 声明将应用于哪些元素。 每个声明都有一个名称, 后跟冒号和声明值。 声明之间以分号分隔。
典型的规则可能如下所示:
p > a {
color: blue;
text-decoration: underline;
}
在上述规则中,"p > a" 是选择器,
如果源文档是 HTML,
它会选择所有
a
元素,这些元素是
p
元素的子元素。
"color: blue" 是一个声明,它指定
对于匹配该选择器的元素,
它们的 color
属性应具有值 blue。
类似地,它们的 text-decoration 属性应具有值 underline。
At-rule 各不相同,但它们具有 共同的基本结构。 它们以 "@" 代码点开始,后跟作为 CSS 关键字的名称。 某些 at-rule 是简单语句, 名称后跟更多 CSS 值以指定其行为, 最后以分号结束。 另一些则是块; 它们可以在名称后跟 CSS 值, 但以一个由 {} 包裹的块结束, 类似于限定规则。 甚至这些块的内容也由给定的 at-rule 决定: 有时它们包含一系列声明,就像限定 规则; 其他时候,它们可能包含其他块、at-rule,或完全不同的其他结构。
下面是几个 at-rule 示例, 用于展示它们可能包含的各种语法。
@import "my-styles.css";
@import at-rule 是一个简单语句。 在其名称之后,它接受单个字符串或 url() 函数 以指示它应导入的样式表。
@page :left {
margin-left: 4cm;
margin-right: 3cm;
}
@page at-rule 由可选的页面选择器(:left 伪类)组成, 后跟一个属性块,这些属性会在打印时应用于页面。 因此,它与普通样式规则非常相似, 不同之处在于其属性不应用于任何“元素”, 而是应用于页面本身。
@media print {
body { font-size: 10pt }
}
@media at-rule 以媒体类型开始 并后跟一个可选媒体查询列表。 其块包含完整的规则, 这些规则仅在 @media 的 条件满足时应用。
属性名称和 at-rule 名称始终是 ident 序列, 它们必须以 ident-start 代码点、两个连字符, 或一个连字符后跟 ident-start 代码点开始, 然后可以包含零个或多个 ident 代码点。 你可以包含任意 代码点, 即使是 CSS 在其语法中使用的代码点, 只要对其进行转义即可。
选择器的语法在选择器规范中定义。 类似地,各种 CSS 值的语法在值与单位规范中定义。 各个 at-rule 的特殊语法可在定义它们的规范中找到。
2.1. 转义
本节不是规范性的。
任何 Unicode 代码点都可以包含在 ident 序列或带引号的字符串中 方法是对其进行转义。 CSS 转义序列以反斜杠 (\) 开始,并继续为:
- 任何不是代码点中的 十六进制数字或换行符的 Unicode 代码点。 转义序列会被该 代码点替换。
-
或一个到六个十六进制数字,后跟
一个可选的空白。
转义序列会被 Unicode 代码点替换,
其值由十六进制数字给出。
此可选空白允许十六进制转义序列
后跟“真正的”十六进制数字。
值为 "&B" 的 ident 序列 可以写成 \26 B 或 \000026B。
转义序列后的“真正”空格必须写两次。
2.2. 错误处理
本节不是规范性的。
当 CSS 中发生错误时, 解析器会尝试平稳恢复, 仅丢弃最少量的内容, 然后恢复正常解析。 这是因为错误并不总是错误操作——新语法对于旧解析器而言看起来像错误, 而能够向语言添加新语法 而不必担心包含它的样式表在旧 UA 中完全失效是很有用的。
精确的错误恢复行为在解析器本身中有详细说明, 但它足够简单,因此简短描述也相当准确。
- 在样式表的“顶层”, <at-keyword-token> 会开始一个 at-rule。 其他任何内容都会开始一个限定规则, 并被包含在规则的前奏中。 这可能会产生无效选择器, 但这不是 CSS 解析器所关心的——最坏情况下,这意味着该选择器不会匹配任何内容。
- 一旦 at-rule 开始, 从解析器的角度看就不存在无效内容; 所有内容都是 at-rule 前奏的一部分。 遇到 <semicolon-token> 会立即结束 at-rule, 而遇到左花括号 <{-token> 则开始 at-rule 的主体。 at-rule 会向前查找,同时匹配块(由 ()、{} 或 [] 包围的内容), 直到找到一个未与其他任何内容匹配 且不位于另一个块内的右花括号 <}-token>。 然后根据 at-rule 自身的语法解释其内容。
- 限定规则的工作方式类似, 但分号不会结束它们; 相反,分号只会作为规则前奏的一部分被接收。 找到第一个 {} 块时, 其内容始终会被解释为声明列表。
- 解释声明列表时, 任何位置出现未知语法都会导致解析器丢弃当前正在 构建的声明, 并向前查找直到找到分号(或块的结尾)。 然后它会重新开始,再次尝试解析声明。
-
如果样式表结束时仍有任何规则、声明、函数、字符串等处于打开状态,
所有内容都会自动闭合。
这不会使它们无效,
尽管它们可能不完整,
因而在根据其语法进行验证时被丢弃。
例如,translateX() 函数的语法是:
translateX( <translation-value> )
但是,样式表可能在函数尚未闭合时结束,例如:
.foo { transform: translate(50pxCSS 解析器会将其解析为包含一个声明的样式规则, 其值是一个名为 "translate" 的函数。 这与上述语法匹配, 即使结束标记没有出现在标记流中, 因为当解析器完成时, 已无法再确定结束标记是否存在; 你所拥有的只是存在一个块和一个函数这一事实。
解析每个构造(声明、样式规则、at-rule)之后, 用户代理会根据其预期语法对其进行检查。 如果它不匹配该语法, 则它是无效的, 并会被 UA 忽略, UA 会将其视为完全不存在。
3. CSS 的标记化和解析
用户代理必须使用本规范中描述的解析规则 从 text/css 资源生成 [CSSOM] 树。 这些规则共同定义了所谓的 CSS 解析器。
本规范定义 CSS 文档的解析规则, 无论它们在语法上是否正确。 解析算法中的某些位置被称为解析错误。 解析错误的错误处理有明确的定义: 用户代理在遇到此类问题时必须按照下文描述采取行动, 或者必须在遇到其不希望应用下述 规则的第一个错误时中止处理。
如果文档中存在一个或多个解析错误条件, 一致性检查器必须向用户报告至少一个解析错误条件, 并且如果文档中不存在解析错误条件, 则不得报告解析错误条件。 如果存在多个解析错误条件,一致性检查器可以报告多个解析错误条件。 一致性检查器不要求从解析错误中恢复, 但如果进行恢复, 则必须以与用户代理相同的方式恢复。
3.1. 解析模型概述
CSS 解析过程的输入由 Unicode 代码点流组成, 它先经过标记化阶段,然后经过树构建阶段。 输出是一个 CSSStyleSheet 对象。
注:不支持脚本的实现 不必实际创建 CSSOM CSSStyleSheet 对象, 但在这种情况下,CSSOM 树仍会被用作规范其余部分的模型。
3.2. 输入字节流
解析样式表时, 构成标记化阶段输入的 Unicode 代码点流 最初可能被用户代理视为字节流 (通常来自网络或本地文件系统)。 如果是这样,用户代理必须根据特定的字符编码 将这些字节解码为 代码点。
- 如果 HTTP 或等效协议为 stylesheet 提供了编码标签(例如通过 Content-Type 标头的 charset 参数), 则从 encoding label 获取编码。 如果该操作未返回失败, 则返回它。
-
否则,检查 stylesheet 的字节流。
如果该流的前 1024 个字节以如下十六进制序列开头:
40 63 68 61 72 73 65 74 20 22 XX* 22 3B
其中每个
XX字节都是介于 016 和 2116(含)之间的值 或介于 2316 和 7F16(含)之间的值, 则获取编码 从由XX字节序列组成的字符串, 并将其解释为ASCII。该字节序列是什么意思?
上述字节序列 按 ASCII 解码时, 是字符串 "
@charset "…";", 其中 "…" 是与编码标签相对应的字节序列。如果返回值为
utf-16be或utf-16le, 则返回utf-8; 如果它不是失败的其他任何值, 则返回它。为什么声明为 utf-16 时使用 utf-8?
编码声明的字节在 ASCII 中拼写为“
@charset "…";”, 但 UTF-16 与 ASCII 不兼容。 要么你输入了完全乱码的内容(例如䁣桡牳整•utf-16be∻)以获得文档中 正确的字节, 而我们不希望鼓励这种做法, 要么你的文档实际上采用与 ASCII 兼容的编码, 而你的编码声明是在撒谎。无论哪种情况,默认使用 UTF-8 都是一个合理的答案。
此外,这也模仿了 HTML 的
<meta charset>属性的行为。注:请注意,编码 声明的语法看起来像一个名为 at-rule 的 @charset, 但实际上并不存在这样的规则, 而且其书写规则比通常用于 识别这种规则的规则严格得多。 你可以在 CSS 中执行的许多操作,如果确实存在这样的规则,会产生有效的 @charset 规则, 例如使用多个空格、注释或单引号, 都会导致编码声明无法被识别。 此行为使编码声明尽可能简单, 从而最大程度提高其被正确实现的可能性。
- 否则,如果引用 文档提供了环境编码, 则返回它。
-
否则,返回
utf-8。
尽管 UTF-8 是 Web 的默认编码, 并且许多较新的基于 Web 的文件格式假定或要求使用 UTF-8 编码, 但 CSS 创建于尚不清楚哪种编码最终会占据主导地位的时期, 因此不能自动假定样式表采用 UTF-8。
样式表作者应该使用 UTF-8 编写其样式表,
并确保 HTTP 标头(或等效方法)声明样式表的编码
为 UTF-8,
或者引用文档声明其编码为 UTF-8。
(在 HTML 中,这通过向 head 添加 <meta charset=utf-8> 元素
来完成。)
如果这些选项均不可用, 作者应以 UTF-8 BOM 开始样式表 或以下确切字符
@charset "utf-8";
引用从字节解码的 CSS 样式表的文档语言 可以为每个此类样式表定义环境 编码, 当其他编码提示不可用或无法使用时,将其用作回退编码。
环境编码这一概念仅为与旧版 内容兼容而存在。 新格式和新的链接机制不应该提供环境编码, 这样在缺少更明确的信息时,样式表就会改为默认使用 UTF-8。
注:[HTML] 定义了<link rel=stylesheet>
的环境编码。
注:[CSSOM] 定义了<xml-stylesheet?>
的
环境编码。
注:[CSS-CASCADE-3] 定义
@import 的环境编码。
3.3. 预处理输入流
输入流由 随输入字节流被解码而推入其中的经过筛选的 代码点 组成。
4. 标记化
要将代码点流 input 标记化为 CSS 标记流, 重复从 input 中消费一个标记 直到遇到 <EOF-token>, 将每个返回的标记推入一个流中。
注:每次调用消费一个标记算法 都会返回单个标记, 因此它也可以“按需”用于标记化代码点流, 即在解析期间, 如果需要的话。
标记化步骤的输出是由以下零个或多个标记组成的流: <ident-token>、 <function-token>、 <at-keyword-token>、 <hash-token>、 <string-token>、 <bad-string-token>、 <url-token>、 <bad-url-token>、 <delim-token>、 <number-token>、 <percentage-token>、 <dimension-token>、 <unicode-range-token>、 <whitespace-token>、 <CDO-token>、 <CDC-token>、 <colon-token>、 <semicolon-token>、 <comma-token>、 <[-token>、 <]-token>、 <(-token>、 <)-token>、 <{-token>, 以及 <}-token>。
- <ident-token>、<function-token>、<at-keyword-token>、<hash-token>、<string-token>,以及 <url-token> 都具有由零个或多个代码 点组成的值。 此外,hash 标记还有一个类型标志,其值设置为 "id" 或 "unrestricted"。若未另行设置,则类型标志默认为 "unrestricted"。
- <delim-token> 具有由单个代码 点组成的值。
-
<number-token>、<percentage-token> 和 <dimension-token> 具有数值,
以及一个可选的符号字符,
设置为 "+"、"-" 或不存在。
<number-token> 和 <dimension-token> 还具有一个类型标志,设置 为 "integer" 或 "number"。如果未另行设置,则类型标志默认为 "integer"。 <dimension-token> 还具有由一个或多个代码点组成的单位。
- <unicode-range-token> 具有起始和结束代码 点。 它表示一个包含端点的代码点范围 (包括起始点和结束点)。 如果结束代码点位于起始代码点之前, 则它表示空范围。
注:hash 标记的类型标志用于 选择器语法 [SELECT]。 只有类型为 "id" 的 hash 标记才是有效的 ID 选择器。
4.1. 标记铁路图
本节是非规范性的。
本节以铁路图的形式 提供标记器的信息性视图。 铁路图比显式解析器更紧凑, 但通常比正则表达式更易阅读。
这些图是信息性的且不完整的; 它们描述“正确”标记的语法, 但完全不描述错误处理。 提供这些图只是为了更容易直观掌握每种标记的语法。
名称类似 <foo-token> 的图表示标记。 其余图表示其他图所引用的产生式。
- 注释
-
- 换行符
-
- 空白
-
- 十六进制数字
-
- 转义
-
- <whitespace-token>
-
- ws*
-
- <ident-token>
-
- <function-token>
-
- <at-keyword-token>
-
- <hash-token>
-
- <string-token>
-
- <url-token>
-
- <number-token>
-
- <dimension-token>
-
- <percentage-token>
-
- <CDO-token>
-
- <CDC-token>
-
- <unicode-range-token>
-
4.2. 定义
本节定义了标记化阶段使用的若干术语。
- 下一个输入代码 点
- 输入流中尚未被消费的第一个代码点。
- 当前输入 代码点
- 最后一个已被消费的代码点。
- 重新消费当前输入代码点
- 将当前输入代码点推回输入流的前端, 这样,下次指示你消费下一个输入代码点时, 将改为重新消费当前输入代码 点。
- EOF 代码点
- 一个概念上的代码点,表示输入流的末尾。 每当输入流为空时, 下一个输入 代码点始终是 EOF 代码点。
- 数字
- 介于 U+0030 DIGIT ZERO (0) 和 U+0039 DIGIT NINE (9) 之间(含两端)的代码点。
- 十六进制数字
- 一个数字, 或介于 U+0041 LATIN CAPITAL LETTER A (A) 和 U+0046 LATIN CAPITAL LETTER F (F) 之间(含两端)的代码点, 或介于 U+0061 LATIN SMALL LETTER A (a) 和 U+0066 LATIN SMALL LETTER F (f) 之间(含两端)的代码点。
- 大写字母
- 介于 U+0041 LATIN CAPITAL LETTER A (A) 和 U+005A LATIN CAPITAL LETTER Z (Z) 之间(含两端)的代码点。
- 小写字母
- 介于 U+0061 LATIN SMALL LETTER A (a) 和 U+007A LATIN SMALL LETTER Z (z) 之间(含两端)的代码点。
- 字母
- 一个大写字母 或一个小写 字母。
- 非 ASCII ident 代码点
-
一个值为以下任意值的代码点:
-
U+00B7
-
介于 U+00C0 和 U+00D6 之间
-
介于 U+00D8 和 U+00F6 之间
-
介于 U+00F8 和 U+037D 之间
-
介于 U+037F 和 U+1FFF 之间
-
U+200C
-
U+200D
-
U+203F
-
U+2040
-
介于 U+2070 和 U+218F 之间
-
介于 U+2C00 和 U+2FEF 之间
-
介于 U+3001 和 U+D7FF 之间
-
介于 U+F900 和 U+FDCF 之间
-
介于 U+FDF0 和 U+FFFD 之间
-
大于或等于 U+10000
所有这些范围都包含端点。
为什么专门是这些字符?
这与允许在 HTML 有效自定义元素名称中使用的非 ASCII 代码点列表 相匹配。 它排除了一些表现为空白的字符, 或可能在某些工具中导致渲染或解析问题的字符, 例如方向覆盖代码点。
请注意,这组限制 比 UAX 31 为标识符推荐的限制更宽松 (JavaScript 等语言使用它来限制其标识符语法), 因而允许诸如 以组合字符开始标识符之类的情况。 与 HTML 自定义元素名称保持一致 (从而可以为所有自定义元素编写选择器, 而无需使用转义) 被认为很有价值, 而 HTML 所限制的字符集合 很好地涵盖了“高价值”限制。
这些限制并不能避免所有可能令人困惑的渲染; 混合使用 LTR 和 RTL 文字的字符 在大多数文本编辑器中 仍可能导致意外的视觉换位, 例如。 源文本也可以在非 ident 上下文中包含受限字符: 例如,其中大多数在字符串中完全有效。 即使以会产生无效 CSS 的方式使用, 它们造成的解析错误也可能仅限于不重要的部分, 而它们对代码审查工具中源文本渲染的影响 却可能很大和/或具有恶意。 有关这类“源文本攻击”的更多详细信息, 请参阅这篇 Rust-lang 博客 文章 (存档)。
-
- ident-start 代码点
- 一个字母, 一个非 ASCII ident 代码点, 或 U+005F LOW LINE (_)。
- ident 代码 点
- 一个ident-start 代码点, 一个数字, 或 U+002D HYPHEN-MINUS (-)。
- 不可打印 代码点
- 介于 U+0000 NULL 和 U+0008 BACKSPACE 之间(含两端)的代码点, 或 U+000B LINE TABULATION, 或介于 U+000E SHIFT OUT 和 U+001F INFORMATION SEPARATOR ONE 之间(含两端)的代码点, 或 U+007F DELETE。
- 换行符
- U+000A LINE FEED。 请注意,U+000D CARRIAGE RETURN 和 U+000C FORM FEED 不包含在此定义中, 因为它们会在预处理期间转换为 U+000A LINE FEED。
- 空白
- 一个换行符、U+0009 CHARACTER TABULATION 或 U+0020 SPACE。
- 允许的最大 代码点
- Unicode 定义的最大代码点:U+10FFFF。
- ident 序列
-
与 <ident-token> 具有相同语法的一系列代码点。
注:<at-keyword-token> 中 "@" 之后的部分, <hash-token>(类型标志为 "id")中 "#" 之后的部分, <function-token> 中 "(" 之前的部分, 以及 <dimension-token> 的单位 都是ident 序列。
4.3. 标记器算法
本节定义的算法将代码点流转换为标记 流。
4.3.1. 消费一个标记
本节描述如何从代码 点流中消费一个标记。 它还接受一个可选的布尔值 unicode ranges allowed, 默认为 false。 它将返回任意类型的单个标记。
消费注释。
消费下一个 输入代码点。
- 空白
- 尽可能多地消费空白。 返回一个 <whitespace-token>。
- U+0022 QUOTATION MARK (")
- 消费一个 字符串标记 并返回它。
- U+0023 NUMBER SIGN (#)
-
如果下一个
输入代码点是一个ident
代码点
或接下来的两个输入代码点
构成有效转义,
则:
- 创建一个 <hash-token>。
- 如果接下来的 3 个输入代码点会开始一个 ident 序列, 则将 <hash-token> 的类型标志设置为 "id"。
- 消费一个 ident 序列, 并将 <hash-token> 的值设置为返回的字符串。
- 返回 <hash-token>。
否则, 返回一个 <delim-token>, 其值设置为当前输入代码点。
- U+0027 APOSTROPHE (')
- 消费一个 字符串标记 并返回它。
- U+0028 LEFT PARENTHESIS (()
- 返回一个 <(-token>。
- U+0029 RIGHT PARENTHESIS ())
- 返回一个 <)-token>。
- U+002B PLUS SIGN (+)
-
如果输入流以数字开始,
则重新消费当前输入代码点,
消费一个
数值标记,
并返回它。
否则, 返回一个 <delim-token>, 其值设置为当前输入代码点。
- U+002C COMMA (,)
- 返回一个 <comma-token>。
- U+002D HYPHEN-MINUS (-)
-
如果输入流以数字开始,
则重新消费当前输入代码点,
消费一个
数值标记,
并返回它。
否则, 如果接下来的 2 个输入代码点是 U+002D HYPHEN-MINUS U+003E GREATER-THAN SIGN (->), 则消费它们 并返回一个 <CDC-token>。
否则, 如果输入流以 ident 序列开始, 则重新消费当前输入代码点, 消费一个类 ident 标记, 并返回它。
否则, 返回一个 <delim-token>, 其值设置为当前输入代码点。
- U+002E FULL STOP (.)
-
如果输入流以数字开始,
则重新消费当前输入代码点,
消费一个
数值标记,
并返回它。
否则, 返回一个 <delim-token>, 其值设置为当前输入代码点。
- U+003A COLON (:)
- 返回一个 <colon-token>。
- U+003B SEMICOLON (;)
- 返回一个 <semicolon-token>。
- U+003C LESS-THAN SIGN (<)
-
如果接下来的 3 个
输入代码点是
U+0021 EXCLAMATION MARK
U+002D HYPHEN-MINUS
U+002D HYPHEN-MINUS
(!--),
则消费它们
并返回一个 <CDO-token>。
否则, 返回一个 <delim-token>, 其值设置为当前输入代码点。
- U+0040 COMMERCIAL AT (@)
-
如果接下来的 3 个
输入代码点
会开始一个 ident
序列,
则消费一个
ident 序列,
创建一个 <at-keyword-token>,其值设置为返回的
值,
并返回它。
否则, 返回一个 <delim-token>, 其值设置为当前输入代码点。
- U+005B LEFT SQUARE BRACKET ([)
- 返回一个 <[-token>。
- U+005C REVERSE SOLIDUS (\)
-
如果输入流以有效转义开始,
则重新消费当前输入代码点,
消费一个类 ident 标记,
并返回它。
否则, 这是一个解析错误。 返回一个 <delim-token>, 其值设置为当前输入代码点。
- U+005D RIGHT SQUARE BRACKET (])
- 返回一个 <]-token>。
- U+007B LEFT CURLY BRACKET ({)
- 返回一个 <{-token>。
- U+007D RIGHT CURLY BRACKET (})
- 返回一个 <}-token>。
- 数字
- 重新消费当前输入代码点, 消费一个 数值标记, 并返回它。
- U+0055 LATIN CAPITAL LETTER U (U)
- u+0075 LATIN LOWERCASE LETTER U (u)
-
如果 unicode ranges allowed 为 true
且输入流会开始一个 unicode-range,
则重新消费当前输入代码点,
消费一个 unicode-range 标记,
并返回它。
否则, 重新消费当前输入代码点, 消费一个类 ident 标记, 并返回它。
- ident-start 代码点
- 重新消费当前输入代码点, 消费一个类 ident 标记, 并返回它。
- EOF
- 返回一个 <EOF-token>。
- 其他任何内容
- 返回一个 <delim-token>, 其值设置为当前输入代码点。
4.3.2. 消费注释
本节描述如何从代码点流中消费注释。 它不返回任何内容。
如果接下来的两个 输入代码点是 U+002F SOLIDUS (/) 后跟 U+002A ASTERISK (*), 则消费它们 以及之后直至并包括第一个后跟 U+002F SOLIDUS (/) 的 U+002A ASTERISK (*) 的所有代码点, 或直至 EOF 代码点。 返回此步骤的开头。
如果前一段通过消费 EOF 代码点而结束, 则这是一个解析错误。
不返回任何内容。
4.3.3. 消费一个数值标记
本节描述如何从代码点流中消费一个数值标记。 它返回 <number-token>、<percentage-token> 或 <dimension-token> 中的一个。
消费一个数字,并令 number 为结果。
如果接下来的 3 个 输入代码点会开始一个 ident 序列, 则:
- 创建一个与 number 具有相同值、类型标志和 符号字符的 <dimension-token>, 并将其单位初始设置为空字符串。
- 消费一个 ident 序列。 将 <dimension-token> 的单位设置为返回值。
- 返回 <dimension-token>。
否则, 如果下一个输入 代码点是 U+0025 PERCENTAGE SIGN (%), 则消费它。 创建一个与 number 具有相同值和符号字符的 <percentage-token>, 并返回它。
否则, 创建一个与 number 具有相同值、类型标志和符号 字符的 <number-token>, 并返回它。
4.3.4. 消费一个类 ident 标记
本节描述如何从代码 点流中消费一个类 ident 标记。 它返回 <ident-token>、<function-token>、<url-token> 或 <bad-url-token>。
消费一个 ident 序列,并令 string 为结果。
如果 string 的值与 "url" ASCII 大小写不敏感匹配, 且下一个 输入代码点是 U+0028 LEFT PARENTHESIS ((), 则消费它。 当接下来的两个输入代码点都是空白时, 消费下一个输入代码点。 如果接下来的一个或两个输入代码点是 U+0022 QUOTATION MARK (")、 U+0027 APOSTROPHE ('), 或空白后跟 U+0022 QUOTATION MARK (") 或 U+0027 APOSTROPHE ('), 则创建一个 <function-token>, 其值设置为 string 并返回它。 否则, 消费一个 url 标记, 并返回它。
否则, 如果下一个输入 代码点是 U+0028 LEFT PARENTHESIS ((), 则消费它。 创建一个 <function-token>, 其值设置为 string 并返回它。
否则, 创建一个 <ident-token>, 其值设置为 string 并返回它。
4.3.5. 消费一个字符串标记
本节描述如何从代码点流中消费一个字符串标记。 它返回 <string-token> 或 <bad-string-token>。
调用此算法时可以传入一个 ending code point, 它表示结束字符串的代码点。 如果未指定 ending code point, 则使用当前 输入代码点。
初始创建一个值为空字符串的 <string-token>。
从流中重复消费下一个输入代码点:
- ending code point
- 返回 <string-token>。
- EOF
- 这是一个解析错误。 返回 <string-token>。
- 换行符
- 这是一个解析错误。 重新消费当前输入代码点, 创建一个 <bad-string-token>,并返回它。
- U+005C REVERSE SOLIDUS (\)
-
如果下一个
输入代码点是 EOF,
则不执行任何操作。
否则, 如果下一个输入代码点是换行符, 则消费它。
否则, (流以有效 转义开始) 消费一个转义代码点 并将返回的代码点附加到 <string-token> 的值。
- 其他任何内容
- 将当前输入代码点附加到 <string-token> 的值。
4.3.6. 消费一个 url 标记
本节描述如何从代码点流中消费一个 url 标记。 它返回 <url-token> 或 <bad-url-token>。
注:此算法假定初始的 "url(" 已 被消费。 此算法还假定调用它是为了消费一个“不带引号”的值, 例如 url(foo)。 带引号的值,例如 url("foo"), 会被解析为 <function-token>。 消费一个类 ident 标记会自动处理这种 区别; 否则不应该直接调用此算法。
- 初始创建一个值为空字符串的 <url-token>。
- 尽可能多地消费空白。
-
从流中重复消费下一个输入代码点:
- U+0029 RIGHT PARENTHESIS ())
- 返回 <url-token>。
- EOF
- 这是一个解析错误。 返回 <url-token>。
- 空白
- 尽可能多地消费空白。 如果下一个输入代码点是 U+0029 RIGHT PARENTHESIS ()) 或 EOF, 则消费它并返回 <url-token> (如果遇到 EOF,则这是一个解析错误); 否则, 消费错误 url 的剩余部分, 创建一个 <bad-url-token>, 并返回它。
- U+0022 QUOTATION MARK (")
- U+0027 APOSTROPHE (')
- U+0028 LEFT PARENTHESIS (()
- 不可打印代码点
- U+0027 APOSTROPHE (')
- 这是一个解析错误。 消费错误 url 的剩余部分, 创建一个 <bad-url-token>, 并返回它。
- U+005C REVERSE SOLIDUS (\)
-
如果流以有效转义开始,
则消费一个转义代码点
并将返回的代码点附加到 <url-token> 的值。
否则, 这是一个解析 错误。 消费错误 url 的剩余部分, 创建一个 <bad-url-token>, 并返回它。
- 其他任何内容
- 将当前输入代码点 附加到 <url-token> 的值。
4.3.7. 消费一个转义代码点
本节描述如何消费一个转义代码点。 它假定 U+005C REVERSE SOLIDUS (\) 已经被消费 并且下一个输入代码点已经验证 为有效转义的一部分。 它将返回一个代码点。
消费下一个 输入代码点。
- 十六进制数字
- 尽可能多地消费十六进制数字, 但不超过 5 个。 请注意,这意味着总共消费了 1-6 个十六进制数字。 如果下一个 输入代码点是 空白, 则也消费它。 将十六进制数字解释为一个十六进制数。 如果此数字为零, 或对应于代理项, 或大于允许的最大代码点, 则返回 U+FFFD REPLACEMENT CHARACTER (�)。 否则,返回具有该值的代码点。
- EOF
- 这是一个解析错误。 返回 U+FFFD REPLACEMENT CHARACTER (�)。
- 其他任何内容
- 返回当前输入代码点。
4.3.8. 检查两个代码点是否构成有效转义
本节描述如何检查两个代码点是否构成有效 转义。 此处描述的算法可以显式传入两个代码点来调用, 也可以直接传入输入流来调用。 在后一种情况下,所指的两个代码点是 当前 输入代码点 和下一个 输入代码点, 顺序如此。
注:此算法不会消费任何额外的代码 点。
如果第一个代码点不是 U+005C REVERSE SOLIDUS (\), 则返回 false。
否则,返回 true。
4.3.9. 检查三个代码点是否会开始一个 ident 序列
本节描述如何 检查三个代码点是否会 开始一个ident 序列。 此处描述的算法可以显式传入三个代码点来调用, 也可以直接传入输入流来调用。 在后一种情况下,所指的三个代码点是 当前 输入代码点 和接下来的两个 输入代码点, 顺序如此。
注:此算法不会消费任何额外的代码 点。
查看第一个代码点:
- U+002D HYPHEN-MINUS
- 如果第二个代码点是一个ident-start 代码点 或 U+002D HYPHEN-MINUS, 或第二个和第三个代码点构成有效转义, 则返回 true。 否则,返回 false。
- ident-start 代码点
- 返回 true。
- U+005C REVERSE SOLIDUS (\)
- 如果第一个和第二个代码点构成有效转义, 则返回 true。 否则,返回 false。
- 其他任何内容
- 返回 false。
4.3.10. 检查三个代码点是否会开始一个数字
本节描述如何检查三个代码点是否会 开始一个数字。 此处描述的算法可以显式传入三个代码点来调用, 也可以直接传入输入流来调用。 在后一种情况下,所指的三个代码点是 当前 输入代码点 和接下来的两个 输入代码点, 顺序如此。
注:此算法不会消费任何额外的代码 点。
查看第一个代码点:
- U+002B PLUS SIGN (+)
- U+002D HYPHEN-MINUS (-)
-
如果第二个代码点
是一个数字,
则返回 true。
否则, 如果第二个代码点 是 U+002E FULL STOP (.) 且第三个代码点 是一个数字, 则返回 true。
否则,返回 false。
- U+002E FULL STOP (.)
- 如果第二个代码点 是一个数字, 则返回 true。 否则,返回 false。
- 数字
- 返回 true。
- 其他任何内容
- 返回 false。
4.3.11. 检查三个代码点是否会开始一个 unicode-range
本节描述如何 检查三个代码点 是否会开始一个 unicode-range。 此处描述的算法可以显式传入三个代码点来调用, 也可以直接传入输入流来调用。 在后一种情况下,所指的三个代码点是 当前 输入代码点 和接下来的两个 输入代码点, 顺序如此。
注:此算法不会消费任何额外的代码 点。
如果以下所有条件均为 true:
-
第一个代码点是 U+0055 LATIN CAPITAL LETTER U (U) 或 U+0075 LATIN SMALL LETTER U (u)
-
第二个代码点是 U+002B PLUS SIGN (+)。
-
第三个代码点是 U+003F QUESTION MARK (?) 或一个十六进制数字
则返回 true。
否则返回 false。
4.3.12. 消费一个 ident 序列
本节描述如何从代码点流中消费一个 ident 序列。 它返回一个字符串,其中包含 从第一个代码点开始,可由流中相邻代码点形成的 最长名称。
注:此算法不会验证 为确保返回的代码点会 构成 <ident-token> 所必需的前几个代码点。 如果这正是预期用途, 请确保流在调用此算法之前以 ident 序列开始 。
令 result 初始为空字符串。
从流中重复消费下一个输入代码点:
- ident 代码点
- 将代码点附加到 result。
- 流以有效转义开始
- 消费一个转义代码点。 将返回的代码点附加到 result。
- 其他任何内容
- 重新消费当前输入代码点。 返回 result。
4.3.13. 消费一个数字
本节描述如何从代码点流中消费一个数字。 它返回一个数值 value, 一个值为 "integer" 或 "number" 的字符串 type, 以及一个值为 "+"、"-" 或缺失的可选 sign character。
注:此算法不会验证 为确保能够从流中获得数字所必需的前几个代码点 。 请确保流在调用此算法之前以数字开始 。
按顺序执行以下步骤:
- 令 type 为字符串 "integer"。 令 number part 和 exponent part 为空字符串。
- 如果下一个 输入代码点是 U+002B PLUS SIGN (+) 或 U+002D HYPHEN-MINUS (-), 则消费它。 将其附加到 number part 并将 sign character 设置为它。
- 当下一个输入代码点是一个数字时, 消费它并将其附加到 number part。
- 如果接下来的 2 个 输入代码点是 U+002E FULL STOP (.) 后跟一个数字, 则:
- 如果接下来的 2 个 或 3 个输入代码点是 U+0045 LATIN CAPITAL LETTER E (E) 或 U+0065 LATIN SMALL LETTER E (e), 可选地后跟 U+002D HYPHEN-MINUS (-) 或 U+002B PLUS SIGN (+), 再后跟一个数字, 则:
-
令 value 为将 number part
解释为十进制数的结果。
如果 exponent part 非空, 则将其解释为十进制整数, 然后计算 10 的该结果次幂, 将其乘以 value, 并将 value 设置为该结果。
- 返回 value、type 和 sign character。
4.3.14. 消费一个 unicode-range 标记
本节描述如何消费一个 unicode-range 标记 从代码点流中。 它返回一个 <unicode-range-token>。
注:此算法不会验证 为确保返回的代码点 会构成 <unicode-range-token> 所必需的前几个代码点。 请确保流在调用此算法之前会开始一个 unicode-range 。
注:在正常情况下,标记器不会产生此标记 。 此算法仅在 消费 unicode-range 描述符的值期间调用, 而该算法本身也仅作为一种特殊情况被调用, 用于解析 unicode-range 描述符; 整个语言中仅有这一次调用, 是由于早期 CSS 中糟糕的语法设计。
-
消费接下来的两个输入代码点并丢弃它们。
-
尽可能多地消费十六进制数字, 但不超过 6 个。 如果消费的十六进制数字少于 6 个, 则尽可能多地消费 U+003F QUESTION MARK (?) 代码点, 但数量不得超过使十六进制数字 与 U+003F QUESTION MARK (?) 代码点的总数 等于 6 所需的数量。
令 first segment 为已消费的代码点。
-
如果 first segment 包含任何问号代码点,则:
-
将 first segment 中的问号替换为 U+0030 DIGIT ZERO (0) 代码点, 并将结果解释为十六进制数。 令其为 start of range。
-
将 first segment 中的问号替换为 U+0046 LATIN CAPITAL LETTER F (F) 代码点, 并将结果解释为十六进制数。 令其为 end of range。
-
返回一个新的 <unicode-range-token>,从 start of range 开始并在 end of range 结束。
-
-
否则,将 first segment 解释为十六进制数, 并令结果为 start of range。
-
如果接下来的 2 个输入代码点是 U+002D HYPHEN-MINUS (-) 后跟一个十六进制数字, 则:
-
消费下一个输入代码点。
-
尽可能多地消费十六进制 数字,但不超过 6 个。 将已消费的代码点解释为十六进制数。 令其为 end of range。
-
返回一个新的 <unicode-range-token>,从 start of range 开始并在 end of range 结束。
-
-
否则,返回一个新的 <unicode-range-token>,其起点和终点均 为 start of range。
4.3.15. 消费错误 url 的剩余部分
本节描述如何从代码 点流中消费错误 url 的剩余部分, 在标记器意识到自己正处于 <bad-url-token> 而不是 <url-token> 中间后进行“清理”。 它不返回任何内容; 其唯一用途是消费足够多的输入流以到达恢复点, 在那里可以恢复正常标记化。
从流中重复消费下一个输入代码点:
- U+0029 RIGHT PARENTHESIS ())
- EOF
- 返回。
- 输入流以有效转义开始
- 消费一个转义代码点。 这允许遇到转义的右圆括号("\)")而不会结束 <bad-url-token>。 除此之外,这与“其他任何内容”分支完全相同。
- 其他任何内容
- 不执行任何操作。
5. 解析
CSS 解析器将标记流 (由标记化过程产生, 如本规范前文所定义) 转换为若干 CSS 构造中的一个或多个 (具体取决于调用了哪个解析算法)。
5.1. 解析器铁路图
本节是非规范性的。
本节以铁路图的形式 提供解析器的信息性视图。
这些图是信息性的且不完整的; 它们描述“正确”样式表的语法, 但完全不描述错误处理。 提供这些图只是为了更容易直观掌握语法。
- 样式表
-
- At-rule
-
- 限定规则
-
- {} 块
-
- 声明
-
- !important
-
- 组件值
-
- 简单块
-
- 函数块
-
5.2. CSS 解析结果
解析结果可以是以下任意一种 (或这些结果的列表):
- 样式表
-
样式表具有一个规则列表。
- 规则
- at-rule
- at-rule 具有一个名称,该名称是字符串, 以及一个由组件值列表组成的前奏。 块 at-rule (以 {}-block 结束) 还将具有 一个声明列表 和一个子规则列表。 (语句 at-rule 以分号结束, 则不会具有这些内容。)
- 限定规则
-
限定规则具有
一个由组件值列表组成的前奏、
一个声明列表,
以及一个子规则列表。
注:大多数限定规则都是样式规则, 其中前奏是选择器 [selectors-4] 而其声明是属性。
- 声明
-
声明具有一个名称,该名称是字符串,
以及一个由组件值列表组成的值,
还有一个初始未设置的 important 标志。
它还具有一个可选的 original text,该文本是一个字符串
(仅为少数声明捕获)。
声明还进一步分类 为属性声明或描述符 声明, 前者设置 CSS 属性 并且最常出现在限定规则中, 后者设置 CSS 描述符, 它仅出现在at-rule中。 (这种分类不会发生在语法层面; 相反,它取决于声明出现的位置, 并由定义给定规则的相应规范定义。)
- 组件值
- 组件值是保留标记、 函数、 或简单块之一。
- 保留标记
-
标记器产生的任何标记,
除 <function-token>、
<{-token>、
<(-token>、
和 <[-token> 之外。
注:上面列出的非保留标记 总是被消费到更高级别的对象中, 即函数或简单块, 因此它们本身永远不会出现在任何解析器输出中。
注:标记 <}-token>、<)-token>、<]-token>、<bad-string-token> 和 <bad-url-token> 始终是解析错误, 但本规范会将它们保留在标记流中,以允许其他规范, 例如媒体查询, 定义比直接丢弃整个声明或块 更细粒度的错误处理。
- 函数
- 函数具有一个名称 和一个由组件值列表组成的值。
- 简单
块
- {}-block
- []-block
- ()-block
- {}-block
- 简单块具有关联标记(<[-token>、<(-token> 或 <{-token> 之一) 以及一个由组件值列表组成的值。
5.3. 标记流
标记 流是一个结构, 表示标记和/或组件值的流。 它具有以下项:
- 标记
-
注:为 简单起见,本规范假定 输入流已经完全标记化 然后才开始解析。 但是,解析算法只使用一个“前瞻”标记, 因此在实践中,标记化和解析 可以同步进行。
- 索引
-
指向标记的索引, 表示解析的进度。 它最初从 0 开始。
- 已标记索引
-
一个由 索引值组成的栈, 表示解析器可能返回的位置。 它最初为空。
CSS 中有少数位置 需要引用 为声明值解析的精确文本 (而不只是从该文本产生了哪些标记)。 这里的算法结构没有显式描述这一点, 但标记流 因此必须 能够按需重现声明的原始文本。 有关何时需要这样做的详细信息,请参阅消费一个 声明 。
可以对标记流执行若干操作:
- 下一个标记
-
如果该索引超出列表末尾的范围, 则改为一个 <eof-token>。
- 空
-
如果下一个标记是 <eof-token>,则标记流为空 。
- 消费一个标记
- 丢弃一个标记
- 标记位置
- 恢复标记位置
- 丢弃标记位置
- 丢弃空白
-
当下一个标记是 <whitespace-token> 时, 丢弃一个标记。
- 处理
-
要进行处理, 给定以下标记类型及关联操作列表, 执行与下一个标记关联的操作。 重复执行,直到其中一个操作返回某个内容, 然后返回该内容。
<eof-token> 是一个概念性标记, 实际上并非由标记器产生, 用于指示标记流已耗尽。
5.4. 解析器入口点
本节定义的算法从 CSS 标记列表 生成高级 CSS 对象。
要将给定的 input 规范化为标记流:
注:其他规范可以为 自身用途定义额外的入口点。
- “解析 样式表”旨在作为普通解析器入口点, 用于解析样式表。
-
“解析样式表内容”旨在供
CSSStyleSheet replace()方法 及类似方法使用, 这些方法将文本解析为 现有样式表的内容。 -
“解析规则”旨在
供
CSSStyleSheet insertRule()方法 及类似方法使用, 这些方法将文本解析为单个规则。CSSStyleSheet#insertRule方法 以及可能存在的类似函数, 都将文本解析为单个规则。 - “解析 声明”用于 @supports 条件。[CSS3-CONDITIONAL]
-
“解析
块的内容”旨在用于解析 CSS 中任何块的内容
(包括 style 属性之类的内容),
以及诸如
CSSStyleDeclaration cssText 属性之类的 API。 - “解析 组件值”用于需要消费单个值的内容, 例如 attr() 的解析规则。
-
“解析组件值列表”用于
表现属性的内容,
这些内容将文本解析为单个声明的值,
或用于解析独立选择器 [SELECT] 或媒体查询列表 [MEDIAQ],
例如在选择器 API
或
mediaHTML 属性中。
5.4.1. 根据 CSS 语法解析某内容
通常需要解析字符串或标记列表 以查看它是否匹配某个 CSS 语法, 如果匹配, 则根据该语法将其解构。 本节为此类操作提供一个通用钩子。 它应以类似如下方式调用: “将 foo 解析为 CSS <color>”,或类似方式。
如果输入不匹配提供的语法, 此算法返回失败, 否则返回根据该语法解析输入的结果, 这是与所提供语法规范相对应的未指定结构。 返回值只能由规范文本进行交互, 在这种情况下,表示形式的歧义不会造成问题。 如果其目的是在规范语言之外公开, 使用结果的规范必须明确将其转换为明确定义的表示形式, 例如通过调用 CSS 序列化算法 (例如“序列化为 CSS <string> 值”)。
注:此算法 以及根据 CSS 语法解析逗号分隔列表 通常是其他规范唯一想要调用的解析算法。 其余解析算法主要用于 [CSSOM] 以及相关的“显式构造 CSS 结构”情况。 如果你认为需要使用其他算法之一, 请先向 CSSWG 咨询指导。
5.4.2. 根据 CSS 语法解析逗号分隔列表
当然可以根据包含逗号的语法 解析一个值, 但如果值的任何部分解析失败, 整个内容都会解析失败, 并返回失败。
有时这正是所需行为
(例如在列表值 CSS 属性中);
但在其他时候,
最好让值中每个逗号分隔的子部分单独解析,
对成功解析的部分采用一种处理方式,
对解析失败的部分采用另一种处理方式
(通常忽略它们,
例如在
<img sizes>
中)。
此算法提供了一个简单的钩子来实现这一点。 它返回一个由“顶层”逗号分割的值列表, 其中每个值要么是失败 (如果解析失败) 要么是解析结果 (一种未指定结构, 如解析算法中所述)。
- 规范化 input, 并将 input 设置为结果。
- 如果 input 仅包含 <whitespace-token>, 则返回一个空列表。
- 从 input 解析逗号分隔的组件值 列表, 并令 list 为返回值。
- 对 list 中的每个 item, 将 item 替换为 使用 grammar 解析 item 的结果。
- 返回 list。
5.4.3. 解析样式表
5.4.4. 解析样式表内容
5.4.5. 解析块的内容
5.4.6. 解析规则
- 规范化 input, 并将 input 设置为结果。
- 从 input 丢弃空白。
-
如果来自 input 的下一个标记是 <EOF-token>,
则返回语法错误。
否则, 如果来自 input 的下一个标记是 <at-keyword-token>, 则从 input 消费一个 at-rule, 并令 rule 为返回值。
否则, 从 input 消费一个限定规则 并令 rule 为返回值。 如果返回的是什么都没有或无效规则错误, 则返回语法错误。
- 从 input 丢弃空白。
- 如果来自 input 的下一个标记是 <EOF-token>, 则返回 rule。 否则,返回语法错误。
5.4.7. 解析声明
注:与“解析声明列表”不同, 此操作只解析一个声明,而不解析 at-rule。
5.4.8. 解析组件值
5.4.9. 解析组件值列表
5.4.10. 解析逗号分隔的组件值列表
5.5. 解析器算法
以下算法构成解析器。 它们由上述解析器入口点调用, 通常不应由其他规范直接调用。
请注意,CSS 解析区分大小写, 并且在至少某些情况下,检查给定上下文中构造的有效性 必须在解析期间进行。 本规范有意不指定 应如何传递足够的上下文 以启用有效性检查。
5.5.1. 消费样式表的内容
要从标记流 input 中消费 样式表的内容:
令 rules 为一个初始为空的规则列表。
处理 input:
- <whitespace-token>
- 从 input 丢弃一个标记。
- <EOF-token>
- 返回 rules。
- <CDO-token>
- <CDC-token>
- 从 input 丢弃一个标记。
- <at-keyword-token>
- 从 input 消费一个 at-rule。 如果返回了任何内容, 将其附加到 rules。
- 其他任何内容
- 从 input 消费 一个限定规则。 如果返回了一个规则, 将其附加到 rules。
5.5.2. 消费一个 at-rule
给定可选的 bool nested(默认为 false),要从标记流 input 中消费一个 at-rule :
断言:下一个标记是 <at-keyword-token>。
从 input 消费一个标记, 并令 rule 为一个新的at-rule, 其名称设置为返回标记的值, 其前奏初始设置为空列表, 且没有声明或子规则。
处理 input:
- <semicolon-token>
- <EOF-token>
- 从 input 丢弃一个标记。 如果 rule 在当前上下文中有效, 返回它; 否则不返回任何内容。
- <}-token>
-
如果 nested 为 true:
-
如果 rule 在当前上下文中有效, 返回它。
-
否则,不返回任何内容。
否则,消费一个标记 并将结果附加到 rule 的前奏。
-
- <{-token>
-
从
input 消费一个块,
并将结果赋给 rule 的子规则。
注:如果结果包含列表形式的 声明, 它们在 CSSOM 中如何具体化 取决于规则。 有些规则会将它们全部转换为嵌套声明规则, 另一些会将它们全部视为声明, 还有一些会以不同于其余项的方式处理第一项。
如果 rule 在当前上下文中有效, 返回它。 否则不返回任何内容。
- 其他任何内容
- 从 input 消费一个组件值 并将返回值附加到 rule 的前奏。
5.5.3. 消费一个限定规则
给定可选的标记 stop token 和可选的 bool nested(默认为 false),要从标记流 input 中消费一个 限定规则:
令 rule 为一个新的限定规则, 其前奏、声明和子规则 初始均设置为空列表。
处理 input:
- <EOF-token>
- stop token(如果已传入)
- 这是一个解析错误。 不返回任何内容。
- <}-token>
- 这是一个解析错误。 如果 nested 为 true, 不返回任何内容。 否则, 消费一个标记 并将结果附加到 rule 的前奏。
- <{-token>
-
如果 rule 的前奏中前两个非 <whitespace-token> 值
是
一个值以 "--" 开头的 <ident-token>
后跟一个 <colon-token>,则:
-
如果 nested 为 true, 从 input 消费错误 声明的剩余部分, 并将 nested 设置为 true, 然后不返回任何内容。
-
如果 nested 为 false, 从 input 消费一个 块, 然后不返回任何内容。
为什么要进行此检查?
声明和限定规则 在允许的语法中通常不会重叠。 当前定义的 CSS 属性都不允许其值中包含 {}-block, 因此
foo:bar {};肯定是规则, 而foo: bar;肯定是属性。 即使未来某个 CSS 属性允许其值中包含 {}-block, 所允许的语法也会限制为 {}-block 构成整个值, 例如foo: {...};, 它保证不会是有效规则, 因为 : 后面没有 ident 或函数 来将其标记为伪类。这使我们能够在同一上下文中混合声明和规则: 我们首先尝试将某内容解析为声明, 如果这样不能得到有效声明, 则改为重新将其解析为规则。 意外无效的声明会改为解析成规则, 但这没有问题: 解析器会在声明的结束分号处停止 并将其视为无效规则。 (或者,如果属性包含 {}-block, 会在分号*之前*停止, 仍将其视为无效规则, 然后下一次尝试解析某内容时 会将孤立的分号作为无效内容丢弃。) 因此,无论哪种情况,消费的标记总量都相同。
然而,自定义属性并没有 CSSWG 仔细审查其语法。 作者可以编写一个自定义属性, 其值接受 {}-block, 甚至与其他内容组合; 如果该自定义属性随后无效 (例如由于错误编写的 var() 函数), 当它被重新解析为规则时 会在 {}-block 处提前停止。 自定义属性值中剩余的标记 随后会作为新的构造进行解析, 可能导致意外的声明或规则 被创建。
为了避免这种(诚然非常边缘的)情况, 我们从自定义属性的语法 中减去限定规则的语法; 如果你处于允许属性和规则混合的上下文中, 并且不知何故最终解析出一个 看起来像自定义属性的规则, 那就说明你弄错了, 应改为消费整个自定义属性 (一直到结束分号)。
(如果我们处于不允许属性的上下文中, 那么如果规则看起来像自定义属性, 我们就直接丢弃该规则。 这确保
--foo:hover { color: blue; }在任何地方都始终无效, 而不会为了寻找不存在的结束分号 而可能消费样式表的大量内容。)否则,从 input 消费一个 块, 并令 child rules 为结果。 如果 child rules 的第一项是一个由声明组成的列表, 将其从 child rules 中移除 并将其赋给 rule 的声明。 如果 child rules 中任何剩余项是由声明组成的列表, 将它们替换为嵌套声明规则, 并使该列表成为其唯一子项。 将 child rules 赋给 rule 的子规则。
如果 rule 在当前上下文中有效, 返回它; 否则返回一个无效规则错误。
-
- 其他任何内容
- 从 input 消费一个组件值 并将结果附加到 rule 的前奏。
5.5.4. 消费一个块
要从标记流 input 中消费一个块:
从 input 丢弃一个标记。 从 input 消费 块的内容 并令 rules 为结果。 从 input 丢弃一个标记。
返回 rules。
5.5.5. 消费块的内容
要从标记流 input 中消费 块的内容:
注:此算法返回一个列表,
由规则和列表形式的声明组成。
根据父上下文,
一个由声明组成的列表
可能在 CSSOM 中具体化为
CSSStyleDeclaration,
或具体化为一个 CSSNestedDeclarations
规则。
令 rules 为一个空列表, 其中包含规则 或由声明组成的列表。
处理 input:
- <whitespace-token>
- <semicolon-token>
- 从 input 丢弃一个标记。
- <EOF-token>
- <}-token>
- 返回 rules。
- <at-keyword-token>
-
如果 decls 非空,
将其附加到 rules,
并将 decls 设置为一个新的空列表,其中包含声明。
从 input 消费一个 at-rule, 并将 nested 设置为 true。 如果返回了一个规则, 将其附加到 rules。
- 其他任何内容
-
标记位置
input。
从 input 消费一个 声明, 并将 nested 设置为 true。 如果返回了一个声明, 将其附加到 decls, 并从 input 丢弃标记位置。
否则,从 input 恢复标记位置, 然后从 input 消费一个限定规则, 并将 nested 设置为 true, 且以 <semicolon-token> 作为 stop token。
实现注释
本规范与许多 CSS 规范一样, 编写时优先考虑可理解性 而不是效率。 许多算法, 尤其是上述“先解析为声明,再解析为规则”的行为, 如果按描述直接实现, 可能相当低效。
不过,该行为经过精心编写, 以尽可能允许“提前退出”。 特别是, 大致按可能发生退出的顺序:
-
如果第一个非空白标记 不是已识别属性名称(或自定义属性名称)的 <ident-token>, 你可以立即停止将其解析为声明 而改为重新解析为规则。 如果下一个非空白标记不是 <colon-token>, 同样可以立即停止将其解析为声明。
(也就是说,font+ ... 保证不是属性, not-a-prop-name: ... 也不是。)
-
如果前两个非空白标记 是自定义属性名称和冒号, 那么它肯定是自定义属性, 永远不会产生有效规则, 因此即使该自定义属性最终无效, 也无需尝试重新将其解析为规则。
(也就是说,--foo:hover {...} 保证是自定义属性, 而不是规则。)
-
如果前三个非空白标记 是有效属性名称、冒号以及除 <{-token> 之外的任何内容, 然后在解析声明值时遇到一个 <{-token>, 你可以立即停止将其解析为声明 而改为重新解析为规则。
(也就是说,font:bar {... 保证是无效属性。)
-
如果看到已识别的属性名称、冒号和一个 {}-block, 但该块之后的第一个非空白标记 既不是紧接着的最终分号, 也不是 !important 后跟分号, 你可以立即停止将其解析为声明 而改为重新解析为规则。
(也就是说,font: {} bar ... 保证是无效属性; 无需继续解析直到遇到分号。)
类似地, 即使解析要求被指定为 在解析过程中依赖于检查声明的语法, 一个通用处理器 若尝试在通用 CSS 语法之上 实现非 CSS 语言, 仍然只需验证声明 是否以 ident、冒号开头, 然后要么仅包含一个 {}-block, 要么完全不包含 {}-block。 在类似 foo:hover ... {} 的情况下, 它们只会比了解语法的实现 多花一点解析时间, 因为它们无法在第一个标记处提前退出。
5.5.6. 消费一个声明
给定可选的 bool nested(默认为 false),要从标记流 input 中消费一个 声明 :
令 decl 为一个新的声明, 其名称初始为空 且值设置为空列表。
-
如果下一个标记是 <ident-token>,
从 input 消费一个标记
并将 decl 的名称
设置为该标记的值。
否则, 从 input 消费错误 声明的剩余部分, 使用 nested, 然后不返回任何内容。
- 从 input 丢弃空白。
-
如果下一个标记是 <colon-token>,
从 input 丢弃一个标记。
否则, 从 input 消费错误 声明的剩余部分, 使用 nested, 然后不返回任何内容。
- 从 input 丢弃空白。
- 从 input 消费组件值列表, 使用 nested, 并以 <semicolon-token> 作为停止标记, 然后将 decl 的值设置为结果。
- 如果 decl 的值中最后两个非 <whitespace-token> 是一个值为 "!" 的 <delim-token> 后跟一个其值与 "important" ASCII 大小写不敏感匹配的 <ident-token>, 将它们从 decl 的值中移除 并设置 decl 的 important 标志。
- 当 decl 的值中的最后一项是 <whitespace-token> 时, 移除该标记。
-
如果 decl 的名称是自定义属性名称字符串,
则将 decl 的 original text
设置为原始源文本字符串中
与 decl 的值的标记
相对应的片段。
否则,如果 decl 的值包含顶层简单块, 其关联标记为 <{-token>, 并且还包含任何其他非 <whitespace-token> 值, 则不返回任何内容。 (也就是说,顶层 {}-block 仅允许 作为非自定义属性的整个值。)
否则,如果 decl 的名称与 "unicode-range" ASCII 大小写不敏感匹配, 从与消费组件值列表调用所返回标记 相对应的原始源文本字符串片段中 消费 unicode-range 描述符的值, 并将 decl 的值替换为结果。
- 如果 decl 在当前上下文中有效, 返回它; 否则不返回任何内容。
处理 input:
- <eof-token>
- <semicolon-token>
-
从 input 丢弃一个标记, 然后不返回任何内容。
- <}-token>
-
如果 nested 为 true, 不返回任何内容。 否则, 丢弃一个标记。
- 其他任何内容
-
从 input 消费一个组件值, 然后不执行任何操作。
5.5.7. 消费组件值列表
给定可选的标记 stop token 和可选的 boolean nested(默认为 false),要从标记流 input 中消费 组件值列表:
处理 input:
- <eof-token>
- stop token(如果已传入)
-
返回 values。
- <}-token>
-
如果 nested 为 true, 返回 values。
- 其他任何内容
-
从 input 消费一个组件值, 并将结果附加到 values。
5.5.8. 消费一个组件值
要从标记流 input 中消费一个 组件值:
处理 input:
- <{-token>
- <[-token>
- <(-token>
- <[-token>
-
从 input 消费一个 简单块 并返回结果。
- <function-token>
-
从 input 消费一个 函数 并返回结果。
- 其他任何内容
-
从 input 消费一个标记 并返回结果。
5.5.9. 消费一个简单块
要从标记流 input 中消费一个简单 块:
断言:input 的下一个标记是 <{-token>、 <[-token>, 或 <(-token>。
令 ending token 为下一个标记的镜像变体。 (例如,如果调用时使用的是 <[-token>, 则 ending token 为 <]-token>。)
令 block 为一个新的简单 块, 将其关联标记设置为下一个标记, 并将其值初始设置为空列表。
从 input 丢弃一个标记。
处理 input:
5.5.10. 消费一个函数
要从标记流 input 中消费一个 函数:
断言:下一个标记是 <function-token>。
从 input 消费一个标记, 并令 function 为一个新函数, 其名称等于返回标记的值, 且其值设置为空列表。
处理 input:
5.5.11. 消费 unicode-range 值
要从字符串 input string 中消费 unicode-range 描述符的值:
注:此算法之所以存在, 是由于早期 CSS 中的设计错误。 永远不应复制这种设计。
6. An+B 微语法
CSS 中有若干内容, 例如 :nth-child() 伪类, 需要指示列表中的索引。 An+B 微语法对此很有用, 它允许作者轻松指示单个元素 或列表中以固定间隔出现的所有元素。
An+B 表示法定义一个整数 步长(A)和偏移量(B), 并表示列表中的第 An+B 个元素, 对于 n 的每个正整数或零值, 列表中的第一个元素的索引为 1(而不是 0)。
对于大于 0 的 A 和 B 值, 这实际上会将列表划分为每组 A 个元素 (最后一组包含余数), 并选择每组中的第 B 个元素。
An+B 表示法还接受 even 和 odd 关键字, 它们分别与 2n 和 2n+1 具有相同含义。
A 和 B 的值可以为负数, 但只使用 An+B 的正数结果, 对于 n ≥ 0, 。
如果 A 和 B 都为 0, 则该伪类不表示列表中的任何元素。
6.1. 非正式语法说明
本节是非规范性的。
当 A 为 0 时,可以省略 An 部分 (除非 B 部分已经省略)。 当未包含 An 且 B 为非负数时, B 前面的 + 号(如果允许) 也可以省略。 在这种情况下,语法简化为只有 B。
当 A 为 1 或 -1 时,
可以从规则中省略 1。
如果 B 为 0,则会选择每第 A 个元素。 在这种情况下, 可以省略 +B(或 -B)部分, 除非 A 部分已经省略。
当 B 为负数时,其减号取代 + 号。
当 An 和 B 部分都存在时,分隔它们的 + 或 - 两侧 都允许有空白。
6.2.
<a-n-plus-b> 类型
An+B 表示法最初使用与 CSS 其余部分略有不同的标记器定义, 因此用 CSS 标记来表达时,其定义显得有些奇怪。 本节描述如何根据 CSS 标记识别 An+B 表示法 (从而为 CSS 语法用途定义 <a-n-plus-b> 类型), 以及如何解释 CSS 标记以获得 A 和 B 的值。
<a-n-plus-b> 类型定义为 (使用值与单位规范中的值定义 语法) :
<a-n-plus-b> = odd | even | <integer> | <n-dimension> | '+'?† n | -n | <ndashdigit-dimension> | '+'?† <ndashdigit-ident> | <dashndashdigit-ident> | <n-dimension> <signed-integer> | '+'?† n <signed-integer> | -n <signed-integer> | <ndash-dimension> <signless-integer> | '+'?† n- <signless-integer> | -n- <signless-integer> | <n-dimension> ['+' | '-'] <signless-integer> | '+'?† n ['+' | '-'] <signless-integer> | -n ['+' | '-'] <signless-integer>
其中:
<n-dimension>是一个 <dimension-token>,其类型标志设置为 "integer", 且其单位与 "n" ASCII 大小写不敏感匹配<ndash-dimension>是一个 <dimension-token>,其类型标志设置为 "integer", 且其单位与 "n-" ASCII 大小写不敏感匹配<ndashdigit-dimension>是一个 <dimension-token>,其类型标志设置为 "integer", 且其单位与 "n-*" ASCII 大小写不敏感匹配,其中 "*" 是由 一个或多个数字组成的序列<ndashdigit-ident>是一个 <ident-token>,其值与 "n-*" ASCII 大小写不敏感匹配,其中 "*" 是由 一个或多个数字组成的序列<dashndashdigit-ident>是一个 <ident-token>,其值与 "-n-*" ASCII 大小写不敏感匹配,其中 "*" 是由 一个或多个数字组成的序列<integer>是一个 <number-token>,其类型标志设置为 "integer"<signed-integer>是一个 <number-token>,其类型标志设置为 "integer",并具有 符号字符<signless-integer>是一个 <number-token>,其类型标志设置为 "integer",且没有 符号字符
†:当加号 (+) 位于以 "n" 开头的 ident 前面时,如上面标记的情况, 两个标记之间不得有空白, 否则这些标记不匹配上述语法。 其他任意两个标记之间允许存在空白(并会被忽略)。
产生式的各分支解释如下:
- odd
- A 为 2,B 为 1。
- even
- A 为 2,B 为 0。
<integer>- A 为 0,B 为该整数的值。
<n-dimension>'+'? n-n- A 分别为该维度的值、1 或 -1。 B 为 0。
<ndashdigit-dimension>'+'? <ndashdigit-ident>- A 分别为该维度的值或 1。 B 分别为该维度的单位或 ident 的值, 移除第一个代码点,并将剩余部分解释为十进制数。 B 为负数。
<dashndashdigit-ident>- A 为 -1。 B 为 ident 的值,其中移除前两个代码点 并将剩余部分解释为十进制数。 B 为负数。
<n-dimension> <signed-integer>'+'? n <signed-integer>-n <signed-integer>- A 分别为该维度的值、1 或 -1。 B 为该整数的值。
<ndash-dimension> <signless-integer>'+'? n- <signless-integer>-n- <signless-integer>- A 分别为该维度的值、1 或 -1。 B 为该整数值的相反数。
<n-dimension> ['+' | '-'] <signless-integer>'+'? n ['+' | '-'] <signless-integer>-n ['+' | '-'] <signless-integer>-
A 分别为该维度的值、1 或 -1。
B 为该整数的值。
如果两者之间提供的是
'-',则 B 改为该 整数值的相反数。
7. 为规则和其他值定义语法
CSS 值第 4 级 § 2 值定义 语法定义了如何为属性和其他 CSS 语法构造指定语法。
7.1. 定义块内容:<block-contents>、<declaration-list>、<qualified-rule-list>、<declaration-rule-list> 和 <rule-list> 产生式
CSS 解析器并不关心块的内容——它们都使用相同的 算法进行解析, 并且仅通过哪些构造有效来彼此区分。
编写规则语法时, <block-contents> 表示这种不区分内容的解析。 它只能作为块中的唯一值使用, 并表示不会隐式施加任何限制 来约束块中可以包含的内容。
随附的规范文本必须定义在此上下文中哪些内容有效、哪些无效。
如果任何声明有效,
并且是属性声明,
则必须定义它们是否与层叠交互;
如果交互,则必须定义其优先级
以及它们如何使用 !important。
然而,在许多情况下, 一个块无法有效包含给定类型的任何构造。 为了更明确地表示这些情况, 可以使用以下产生式
就解析而言,所有这些都与 <block-contents> 完全等价, 但随附的规范文本只需为 不会自动无效的类别定义有效性。
-
顶层 @media 的块使用 <rule-list>, 而嵌套的 [CSS-NESTING-1] 使用 <block-contents>。
<@font-face> = @font-face { <declaration-list> }
然后随附的规范文本定义块中允许的有效描述符 。
@keyframes 的语法可以写为:
<@keyframes> = @keyframes { <qualified-rule-list> } <keyframe-rule> = <keyframe-selector> { <declaration-list> }
然后随附的规范文本定义只有 <keyframe-rule> 才允许出现在 @keyframes 中, 并且 <keyframe-rule> 接受所有可动画的 CSS 属性, 以及 animation-timing-function 属性, 但它们不参与层叠。
7.2. 定义任意内容:<declaration-value> 和 <any-value> 产生式
在某些语法中, 允许语法接受任何合理的输入很有用, 然后手动对内容执行更具体的错误处理 (而不是简单地使构造无效, 因为语法不匹配通常会这样做)。
例如,自定义 属性允许任何合理的值, 因为它们可以包含其他 CSS 属性的任意片段, 或用于完全不属于现有 CSS 的内容。 再例如,媒体查询中的 <general-enclosed> 产生式 定义未来 MQ 语法所允许内容的边界, 并使用特殊逻辑来处理“未知”值。
为此,定义了两个额外的产生式:
<declaration-value> 产生式匹配由 一个或多个标记组成的任何序列, 只要该序列不包含 <bad-string-token>、 <bad-url-token>、 不匹配的 <)-token>、<]-token> 或 <}-token>, 或顶层 <semicolon-token> 标记,或值为 "!" 的 <delim-token> 标记。 它表示有效声明的值可以包含的全部内容。
<any-value> 产生式与 <declaration-value> 相同, 但还允许顶层 <semicolon-token> 标记 和值为 "!" 的 <delim-token> 标记。 它表示在任何上下文中有效 CSS 可以包含的全部内容。
8. CSS 样式表
要解析 CSS 样式表, 首先解析 样式表。 将所有所得的顶层限定规则解释为下文定义的样式规则。
如果任何样式规则无效, 或任何 at-rule 无法识别,或根据其语法或上下文无效, 则这是一个解析错误。 丢弃该规则。
8.1. 样式规则
样式规则是一个限定规则, 它将一个选择器列表 与一个属性声明列表 以及可能的一个嵌套规则列表相关联。 它们在 [CSS2] 中也称为 规则集。 CSS 层叠与继承 [CSS-CASCADE-3] 定义样式规则中的声明如何 参与层叠。
限定规则的前奏会被解析 为 <selector-list>。 如果返回失败, 则整个样式规则无效。
限定规则的块内容被解析为 <declaration-list>。 此块中的限定规则也是样式规则。 除非其他规范或本规范的未来级别另有定义, 该列表中的 at-rule 无效 并且必须被忽略。
注:[CSS-NESTING-1] 定义条件分组规则 和一些其他at-rule 允许出现在样式规则内部。
未知 CSS 属性的声明 或其值不匹配该属性定义语法的声明无效 并且必须被忽略。 样式规则内容的有效性不会影响样式规则本身的有效性。 除非另有规定,属性名称进行 ASCII 大小写不敏感匹配。
注:自定义属性 [CSS-VARIABLES] 的名称 区分大小写。
CSS 样式表顶层的限定规则是样式 规则。 其他上下文中的限定规则可能是,也可能不是样式规则, 具体由上下文定义。
例如,@media 规则 [CSS3-CONDITIONAL] 内的限定规则是 样式规则, 但 @keyframes 规则 [CSS3-ANIMATIONS] 内的限定规则不是。
8.2. At-rule
at-rule 是一个以 at-keyword 开始的规则, 因此可以将它与同一上下文中的样式 规则区分开来。
At-rule 用于:
At-rule 有多种形式,具体取决于特定规则及其用途, 但大体上分为两种: 语句 at-rule ,它们是以分号结束的较简单构造, 以及 块 at-rule ,它们以 {}-block 结束, 其中可以包含嵌套的限定规则、at-rule 或声明。
块 at-rule通常 包含一组 (通用或特定于 at-rule 的) at-rule、限定规则和/或描述符声明, 并受该 at-rule 定义的限制约束。 描述符类似于属性 (并使用相同语法声明), 但与特定类型的 at-rule 相关联, 而不是与树中的元素和框相关联。
8.3. @charset 规则
用于为样式表确定回退编码的算法 会在文件最开始的几个字节中查找特定字节序列, 其语法形式是名为 "@charset" 的at-rule。
测试
- page-utf16-css-bomless-utf16.html (实时 测试) (源代码)
- page-utf16-css-bomless-utf16be.html (实时 测试) (源代码)
- page-utf16-css-no-decl-ascii-only.html (实时 测试) (源代码)
- page-utf16-css-no-decl.html (实时 测试) (源代码)
- page-windows-1251-charset-attribute-bogus.html (实时 测试) (源代码)
- page-windows-1251-charset-attribute-empty.html (实时 测试) (源代码)
- page-windows-1251-css-at-charset-1250-charset-attribute-windows-1253.html (实时 测试) (源代码)
- page-windows-1251-css-at-charset-bogus-charset-attribute-windows-1250.html (实时 测试) (源代码)
- page-windows-1251-css-at-charset-bogus.html (实时 测试) (源代码)
- page-windows-1251-css-at-charset-utf16-ascii-only.html (实时 测试) (源代码)
- page-windows-1251-css-at-charset-utf16.html (实时 测试) (源代码)
- page-windows-1251-css-at-charset-utf16be.html (实时 测试) (源代码)
- page-windows-1251-css-at-charset-windows-1250-in-utf16.html (实时 测试) (源代码)
- page-windows-1251-css-at-charset-windows-1250-in-utf16be.html (实时 测试) (源代码)
- page-windows-1251-css-http-bogus-at-charset-windows-1250.html (实时 测试) (源代码)
- page-windows-1251-css-http-bogus.html (实时 测试) (源代码)
- page-windows-1251-css-http-windows-1250-at-charset-windows-1253.html (实时 测试) (源代码)
- page-windows-1251-css-no-decl.html (实时 测试) (源代码)
- page-windows-1251-css-utf8-bom.html (实时 测试) (源代码)
- page-windows-1252-http-windows-1251-css-utf8-bom.html (实时 测试) (源代码)
- xml-stylesheet-page-windows-1251-charset-attribute-windows-1250.xhtml (实时 测试) (源代码)
但是,并不存在实际名为 @charset 的at-rule。 当实际解析样式表时, 出现的任何 @charset 规则都必须被视为无法识别的规则, 因而在对样式表进行语法检查时作为无效规则丢弃。
注:在 CSS 2.1 中,@charset 是有效规则。 某些旧版规范可能仍会引用 @charset 规则, 并明确讨论其在样式表中的存在。
9. 序列化
本规范中描述的标记器不会为注释生成标记, 也不会以其他方式保留它们。 实现可以保留注释内容及其在标记流中的位置。 如果这样做,这些保留的信息不得对解析步骤产生任何影响。
本规范不定义通常如何序列化 CSS, 而是将该任务留给 [CSSOM] 和各个特性规范。 特别是,注释和空白的序列化方式未定义。
序列化的唯一要求是它必须能够与解析“往返”, 即解析样式表必须产生与 解析、序列化并再次解析相同的数据结构, 连续的 <whitespace-token> 除外, 它们可以折叠为单个标记。
注:此例外之所以可行,是因为 CSS 语法始终将任意数量的空白解释为与单个空格相同。
- 包含 U+005C REVERSE SOLIDUS (\) 的 <delim-token> 必须序列化为 U+005C REVERSE SOLIDUS 后跟一个换行符。 (标记器只会发出这样的标记,且其后跟随一个 <whitespace-token> ,该标记以换行符开头。)
- 类型标志为 "unrestricted" 的 <hash-token> 可能不 需要像类型标志为 "id" 的相同标记那样多的转义。
- <dimension-token> 的单位可能需要转义, 以消除与科学计数法的歧义。
-
对于任意一对连续标记,
如果第一个标记出现在下表的行标题中,
第二个标记出现在列标题中,
且所选行和列交叉对应的单元格中有 ✗,
则这对标记必须在它们之间插入注释后进行序列化。
如果标记器保留注释, 且这对标记之间原本存在注释, 则应使用保留的注释; 否则,必须插入空注释(
/**/)。 (即使下表不要求两个标记之间有注释, 也可以重新插入保留的注释。)行标题和列标题中的单个字符表示具有该值的 <delim-token>, "
(" 除外, 它表示 (-token。
| ident | 函数 | url | 错误 url | - | 数字 | 百分比 | 维度 | CDC | ( | * | % | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| ident | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ | ||
| at-keyword | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ | |||
| hash | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ | |||
| 维度 | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ | |||
| # | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ | |||
| - | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ | |||
| 数字 | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ | |||
| @ | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ | ||||||
| . | ✗ | ✗ | ✗ | |||||||||
| + | ✗ | ✗ | ✗ | |||||||||
| / | ✗ |
9.1. 序列化 <a-n-plus-b>
-
如果 A 为零, 返回 B 的序列化。
-
否则,令 result 初始为空字符串。
-
- A 为
1 -
将 "n" 附加到 result。
- A 为
-1 -
将 "-n" 附加到 result。
- A 非零
-
序列化 A 并将其附加到 result, 然后将 "n" 附加到 result。
- A 为
-
- B 大于零
-
将 "+" 附加到 result, 然后将 B 的序列化附加到 result。
- B 小于零
-
将 B 的序列化附加到 result。
-
返回 result。
10. 隐私考虑
本规范未引入新的隐私问题。
11. 安全考虑
本规范提高了安全性,因为现在已为所有输入明确定义 CSS 解析。
对于白名单/过滤器等旧解析器,如果其解析方式与本规范不同, 它们在一定程度上是不安全的, 但先前的解析规范留下了许多浏览器会作不同解释的模糊边缘情况, , 因此这些过滤器本来就可能不安全, 而本规范不会使情况恶化。
12. 更改
本节是非规范性的。
12.1. 自 2021 年 12 月 24 日候选推荐标准草案以来的更改
进行了以下实质性更改:
-
将 <an+b> 重命名为 <a-n-plus-b> (9473)
-
移除重复的标记流创建 (PR 11129)
-
合并 valdef 语法定义
-
废弃 @nest,引入“嵌套声明规则” (10386)
-
更改 block-contents 解析,以保持交错声明 与规则的相对顺序。向 Nesting 添加 @nest 以表示它们 (8738)
-
块仅允许作为属性的整个值 (9317)
-
如果你一开始就不处于允许属性的上下文中, 则不要应用贪婪的类似自定义属性行为 (9336)
-
澄清代理项只能通过直接赋值 包含代理项的 DOMString,并经由 CSS 对象模型操作产生 (PR 7997)
-
非 ASCII 代码点的定义 已受到限制,以排除一些可能引发问题的代码点。 (7129)
-
定义了
<foo()>和<@foo>产生式。 (5728) -
允许嵌套样式规则。 (7961)。
-
作为允许嵌套的一部分, 大幅重写整个解析章节。 特别是,移除了“解析规则列表”和“解析声明列表”, 改为“解析样式表内容”和“解析块内容”。 唯一额外的规范性更改是 现在分号在某些上下文中会触发略有不同的错误恢复, , 从而使诸如 @media 块的解析 无论是否嵌套都完全相同。 (8834)。
-
移除了尝试使用现有标记定义
<urange>产生式的做法, , 改为依赖从源文本进行特殊的重新解析, 专门用于解析 unicode-range 描述符时。 (8835) -
由于上述更改移除了保留标记“表示形式”的主要需要 (即解析该标记所依据的原始字符串), 因此移除了 An+B 章节中其余对“表示形式”的引用 , 改为只记录为此所必需的少量信息 (数字是否具有显式符号)。
-
明确指出某些用途 (自定义属性、unicode-range 描述符) 需要访问声明整个值的原始字符串, 并在解析器中标明发生这种情况的位置。
-
当消费块的内容时, 不再将所有声明组合在一起, 而不考虑它们相对于其他规则出现的位置, 现在声明会保持其相对于周围规则的位置。 各个规则自行决定实际如何处理这些信息。
12.2. 自 2019 年 8 月 16 日候选推荐标准以来的更改
进行了以下实质性更改:
-
添加新的§ 5.4.2 根据 CSS 语法解析逗号分隔列表算法。
-
添加新的“解析样式块内容”算法 以及对应的 <style-block> 产生式, 并定义样式规则 使用它。
-
使解析 样式表与 Fetch 相关处理保持一致。 (参见提交。)
要从 input 解析 样式表 ,给定可选的 url location :
- ……
- 创建新的样式表 ,并将其位置设置为 location(如果未传入 location,则为 null)。
- ……
进行了以下编辑性更改:
-
添加§ 8.2 At-rule,以提供 at-rule、语句 at-rule、块 at-rule和 描述符的定义。 (5633)
-
改为始终使用ident 序列这一术语, 而不是有时使用“名称”这一术语。
-
明确命名若干标记化前的处理过程, 并在解析入口点中明确引用它们 (而不是依赖笼统的“在这些算法开始时执行 X”语句)。
-
向“在它们之间插入注释”表中添加更多条目, 以正确处理 ident 现在可以以
--开头这一事实。 (6874)
12.3. 自 2014 年 2 月 20 日候选推荐标准以来的更改
进行了以下实质性更改:
-
移除 <unicode-range-token>,改为创建 <urange> 产生式。
-
包含字符串的 url() 函数现在会被解析为普通 <function-token>。 包含“原始”URL 的 url() 函数仍会特殊解析为 <url-token>。
-
修复“消费一个 URL 标记”算法中的错误, 该算法之前在尝试消费字符串之前没有消费用于开始字符串的 引号字符。
-
修复多个解析器算法中的错误, 这些错误与当前/下一个标记以及内容过早/过晚被消费有关。
-
修复标记化和解析算法中的多个错误。
-
更改类 ident 标记的定义,以允许 "--" 开始 ident。 作为其中一部分,重新排列消费一个标记中 "-" 步骤各分支的顺序, , 使 <CDC-token> 能按其本身被识别,而不是变成 -- <ident-token>。
-
当 A 为 1 或 -1 时,不序列化 <a-n-plus-b> 中的数字。
-
定义所有标记都具有表示形式。
-
修复检查两个代码点是否构成有效 转义中的小错误——后跟 EOF 的
\现在会被正确报告为不是 有效转义。 样式表末尾的\现在只会将自身作为 <delim-token> 发出。 -
@charset 不再是有效 CSS 规则(现在只有一个看起来像 名为 @charset 的规则的编码声明)
-
在解析期间修剪声明值开头/结尾的空白。
-
根据 WG 决议,移除选择器专用标记。
进行了以下编辑性更改:
-
更改“消费一个字符串标记”算法,以允许调用时不指定明确的 结束标记, 从而改为使用当前标记。 该算法的三个调用位置均改为使用这种形式。
-
对算法进行了小幅编辑性重构。
-
添加解析和解析逗号分隔的 组件值列表 API 入口点。
-
添加 <declaration-value> 和 <any-value> 产生式。
-
移除“代码点”和“代理代码点”定义,改用 Infra 标准中的相同定义。
-
澄清所有范围都包含端点。
-
向注释插入表添加一列,以处理数字标记紧邻 "%" delim 标记的情况。
12.4. 自 2013 年 11 月 5 日最终征求意见工作草案以来的更改
- 序列化章节已重写, 仅使“往返”要求具有规范性, 并将如何实现它的细节移入注释。 这些细节中的一些边缘情况也已修复。
- [ENCODING] 已 添加到规范性参考文献列表中。 它此前已经在规范性文本中被引用, 只是没有作为规范性参考文献列出。
-
在用于确定样式表回退编码的算法中,
将
@charset字节序列限制为 1024 字节。 这与 HTML 对<meta charset>的处理保持一致, 并确保该序列的大小有界。 这仅在编码标签存在前导或尾随空白时 才会产生差异:@charset " (大量空白) utf-8";
12.5. 自 2013 年 9 月 19 日工作草案以来的更改
- 添加了环境编码的概念。 行为没有改变, 但某些定义应移至相关规范中。
12.6. 自 CSS 2.1 和选择器第 3 级以来的更改
注:本规范的目标是与现实相符; 相较 CSS2.1 的更改几乎总是因为 CSS 2.1 指定的内容与实际浏览器 行为不符, 或者遗漏了某些规定。 如果某些细节与浏览器不符, 请告诉我, 因为这几乎肯定不是有意为之。
从字节流解码方面的更改:
- 仅在与 ASCII 兼容的字节模式中检测 @charset 规则。
- 忽略指定与 ASCII 不兼容编码的 @charset 规则, 因为这会导致规则本身无法正确解码。
- 字符编码改为引用 [ENCODING] 而不是 IANA 注册表。
标记化更改:
- CSS 源中的任何 U+0000 NULL 代码点都会替换为 U+FFFD REPLACEMENT CHARACTER。
- 任何求值结果为零的十六进制转义序列,例如 \0, 都会产生 U+FFFD REPLACEMENT CHARACTER,而不是 U+0000 NULL。
- 非 ASCII ident 代码点的定义已更改, 以与 HTML 的有效自定义元素名称保持一致。
- 标记化不再发出 COMMENT 或 BAD_COMMENT 标记。 BAD_COMMENT 现在被视为与普通标记相同(不是错误)。 序列化负责 根据需要在必须分隔的标记之间插入注释, 例如两个连续的 <ident-token>。
-
<unicode-range-token> 已被移除,
因为它价值较低,并且偶尔会造成实际危害。
(例如,u+a { font-weight: bold; } 是无效选择器……)
作为替代,添加了基于标记模式的 <urange> 产生式 。 从技术上说,它比 2.1 所允许的更宽松 (允许任意数量的数字和 ? 字符), 但不会以任何方式影响其实际使用。
- 在标记器中应用 EOF 错误处理规则 , 并发出普通的 <string-token> 和 <url-token> ,而不是 BAD_STRING 或 BAD_URI , 当遇到 EOF 时。
- BAD_URI 标记(现为 <bad-url-token>)是“自包含的”。 换言之,一旦标记器意识到它处于 <bad-url-token> 而不是 <url-token> 中, 它只会向前查找结束的 ), 并忽略其他所有内容。 这种行为比将其视为 <function-token> 并关注已打开的块等内容更简单。 只有 WebKit 表现出这种行为, 但似乎没有因此产生任何兼容性错误。
- 已添加 <comma-token>。
- <number-token>、<percentage-token> 和 <dimension-token> 已更改, 以将前面的 +/- 符号包含为其值的一部分 (而不是作为单独的 <delim-token>,每次在其他规范中 提及该标记时都需要手动处理)。 唯一后果是,现在无法再在符号和数字之间插入注释。
- 数字/百分比/维度支持科学计数法,以匹配 SVG, 根据 WG 决议。
- 代理项的十六进制转义现在会发出替换字符,而不是代理项。 这使实现能够安全地在内部使用 UTF-16。
解析更改:
- 任何声明列表现在也接受 at-rule,例如 @page, 根据 WG 决议。 即使尚未定义此类 at-rule, 这也会对错误处理产生影响: 无论 at-rule 有效与否,它都会在不带 <semicolon-token> 的 {} 块处结束, 并允许下一个声明开始。
-
对语法中各处出现的一些杂项“特殊”标记
(例如不匹配的 <}-token>)
的处理
已指定为至少一个浏览器所表现出的某种合理行为。
此前,在这些位置带有这些标记的样式表根本不匹配样式表语法,
,
因此其处理完全未定义。
具体而言:
- [] 块、() 块和函数现在可以包含 {} 块、<at-keyword-token> 或 <semicolon-token>
- 限定规则前奏现在可以包含分号
- 限定规则和 at-rule 前奏现在可以包含 <at-keyword-token>
An+B 相较选择器第 3 级 [SELECT] 的更改:
-
An+B 微语法现在已正式根据 CSS 标记定义,
而不是使用单独的标记器。
这导致了一些细微差异:
- 在某些情况下,减号或数字可以被转义 (当它们作为 <dimension-token> 的单位或 <ident-token> 的一部分出现时)。
致谢
感谢以下人员提供反馈和贡献: Anne van Kesteren、 David Baron、 Elika J. Etemad (fantasai)、 Henri Sivonen、 Johannes Koch、 呂康豪 (Kang-Hao Lu)、 Marc O’Morain、 Raffaello Giulietti、 Simon Pieter、 Tyler Karaszewski、 以及 Zack Weinberg。