Ladybird 浏览器 CSS 生成文件详解:从 JSON 数据驱动到 PropertyID 等 C++ 代码的完整解析
本篇技术指南聚焦 Ladybird 浏览器中「CSS 生成文件」这一构建机制:它如何以一组 .json 数据文件为输入,通过 Meta/Generators 下的 Python 生成器在构建期自动产出 PropertyID.h、GeneratedCSSStyleProperties.cpp 等 C++ 代码。读完后,你能够完整理解每个 JSON 输入文件的字段语义与生成产物,掌握新增或修改 CSS 属性、描述符、伪元素等数据时的实操路径,并能结合生成器源码定位底层实现细节。
生成机制总览:JSON 输入、生成器与输出位置
Ladybird 会为 CSS 相关代码生成大量 C++ 代码:输入是 Libraries/LibWeb/CSS 目录下的一个或多个 .json 文件,输出是构建产物目录 Build/<build-preset>/Libraries/LibWeb/CSS/ 下的 C++ 头文件与实现文件。当你需要新增或修改某个 CSS 属性及其取值时,基本都会与这些生成文件打交道。
生成器统一位于 Meta/Generators 目录,它们作为构建流程的一部分被自动执行,日常开发中大多可以直接忽略它们。整个 CSS 生成流程由 CMake 函数 generate_css_implementation() 统一调度,该函数定义在 Meta/CMake/libweb_generators.cmake,并在 Libraries/LibWeb/CMakeLists.txt 中被调用。从该文件的调用清单看,各 JSON 输入与生成器的对应关系如下:
| JSON 输入文件 | 生成器脚本 | 生成产物 |
|---|---|---|
Descriptors.json |
generate_libweb_css_descriptors.py | CSS/DescriptorID.h、CSS/DescriptorID.cpp |
Enums.json |
generate_libweb_css_enums.py | CSS/Enums.h、CSS/Enums.cpp |
MediaFeatures.json |
generate_libweb_css_media_feature_id.py | CSS/MediaFeatureID.h |
Properties.json |
generate_libweb_css_property_id.py | CSS/PropertyID.h、CSS/PropertyID.cpp |
PseudoClasses.json |
generate_libweb_css_pseudo_class.py | CSS/PseudoClass.h |
PseudoElements.json |
generate_libweb_css_pseudo_element.py | CSS/PseudoElement.h、CSS/PseudoElement.cpp |
TransformFunctions.json |
generate_libweb_css_transform_functions.py | CSS/TransformFunctions.h、CSS/TransformFunctions.cpp |
Units.json |
generate_libweb_css_units.py | CSS/Units.h、CSS/Units.cpp |
Keywords.json |
generate_libweb_css_keyword.py | CSS/Keyword.h、CSS/Keyword.cpp |
Units.json |
generate_libweb_css_numeric_factory_methods.py | CSS/GeneratedCSSNumericFactoryMethods.{h,cpp,idl} |
Properties.json |
generate_libweb_css_style_properties.py | CSS/GeneratedCSSStyleProperties.{h,cpp,idl} |
其中 PropertyID 的生成器接收三个输入:除主输入 -j Properties.json 外,还通过 -e Enums.json 与 -g LogicalPropertyGroups.json 引入额外的数据源,并在 libweb_generators.cmake 中显式声明了对 Enums.json、LogicalPropertyGroups.json 的依赖——任一文件变更都会触发重新生成。
Properties.json:CSS 属性的完整数据源
每个 CSS 属性都在 Properties.json 中有一条条目,描述它接受哪些取值、是否可继承等元数据。该文件会生成 PropertyID.h、PropertyID.cpp、GeneratedCSSStyleProperties.h、GeneratedCSSStyleProperties.cpp 以及 GeneratedCSSStyleProperties.idl。这些数据的绝大部分可以在相应 CSS 规范中该属性的信息框(information box)里找到。
文件组织形式是单个 JSON 对象,键为属性名,值为该属性的数据。每个属性会带有下列字段中的若干个(注意:设置了 legacy-alias-for 或 logical-alias-for 的属性可以省略必填字段):
| 字段 | 是否必填 | 默认值 | 说明 | 生成的函数 |
|---|---|---|---|---|
affects-layout |
否 | true |
布尔值。修改该属性是否会触发元素布局失效。 | bool property_affects_layout(PropertyID) |
affects-stacking-context |
否 | false |
布尔值。该属性是否可能为元素创建新的层叠上下文。 | bool property_affects_stacking_context(PropertyID) |
animation-type |
是 | 无 | 字符串。属性应该如何被动画化,取值由规范定义,见下文。 | AnimationType animation_type_from_longhand_property(PropertyID) |
inherited |
是 | 无 | 布尔值。属性是否被子元素继承。仅对 longhand 属性有意义。 | bool is_inherited_property(PropertyID) |
initial |
是 | 无 | 字符串。属性未指定时的初始值。 | NonnullRefPtr<StyleValue const> property_initial_value(PropertyID) |
legacy-alias-for |
否 | 无 | 字符串。本属性是哪个属性的遗留别名,见下文。 | 无 |
logical-alias-for |
否 | 无 | 对象,结构见下文。 | bool property_is_logical_alias(PropertyID);PropertyID map_logical_alias_to_physical_property(PropertyID, LogicalAliasMappingContext const&)PropertyID map_physical_property_to_logical_alias(PropertyID, LogicalAliasMappingContext const&) |
longhands |
否 | [] |
字符串数组。若本属性是简写(shorthand),这些是它展开后的属性名。 | Vector<PropertyID> longhands_for_shorthand(PropertyID)Vector<PropertyID> expanded_longhands_for_shorthand(PropertyID)Vector<PropertyID> shorthands_for_longhand(PropertyID) |
max-values |
否 | 1 |
整数。该属性最多可解析出多少个值,例如 margin 最多可有 4 个值。 |
size_t property_maximum_value_count(PropertyID) |
multiplicity |
否 | "single" |
字符串。标识该属性是单值还是取值列表,见下文。 | bool property_is_single_valued(PropertyID)bool property_is_list_valued(PropertyID)PropertyMultiplicity property_multiplicity(PropertyID) |
percentages-resolve-to |
否 | 无 | 字符串。百分比被解析为什么类型,例如 width 的百分比会解析为 length 值。 |
Optional<ValueType> property_resolves_percentages_relative_to(PropertyID) |
positional-value-list-shorthand |
否 | false |
布尔值。该属性是否为"位置取值列表简写",见下文。 | bool property_is_positional_value_list_shorthand(PropertyID) |
quirks |
否 | [] |
字符串数组。部分属性在怪异模式(quirks mode)下有特殊行为,在此列出,见下文。 | bool property_has_quirk(PropertyID, Quirk) |
requires-computation |
是 | 无 | 字符串。描述该属性的值何时需要经过计算过程,见下文。 | bool property_requires_computation_with_inherited_value(PropertyID)bool property_requires_computation_with_initial_value(PropertyID)bool property_requires_computation_with_cascaded_value(PropertyID) |
valid-identifiers |
否 | [] |
字符串数组。属性接受的关键词,见下文。 | bool property_accepts_keyword(PropertyID, Keyword)Optional<Keyword> resolve_legacy_value_alias(PropertyID, Keyword) |
valid-types |
否 | [] |
字符串数组。属性接受的取值类型,见下文。 | bool property_accepts_type(PropertyID, ValueType) |
needs-layout-for-getcomputedstyle |
否 | false |
布尔值。该属性被 getComputedStyle() 查询前是否需要最新的布局结果。 |
bool property_needs_layout_for_getcomputedstyle(PropertyID) |
生成器 generate_libweb_css_property_id.py 对输入数据做了几项强校验,理解它们有助于编写正确的条目:
- 强制字母序:
verify_alphabetical()会检查 Properties.json 中的属性名必须按字母序排列,违反时直接报错退出(提示 "Please keep this list alphabetical!")。 - 逻辑别名展开:
replace_logical_aliases()会把每个逻辑属性组中的第一个物理属性作为"模板",深拷贝其全部字段填充到逻辑别名上,并丢弃quirks字段(quirks 不会传递给逻辑别名)。 all属性自动补全:populate_all_property_longhands()要求all属性存在,并把所有非 shorthand、非遗留别名的属性自动追加进all的longhands列表。- 可动画性检查:
is_animatable_property()要求每个属性要么声明animation-type,要么声明longhands,shorthand 的可动画性由其 longhands 决定。
animation-type 字段
Web Animations 规范定义了此处允许的取值,规范术语到 JSON 值的映射如下:
| 规范术语 | JSON 取值 |
|---|---|
| not animatable(不可动画) | none |
| discrete(离散) | discrete |
| by computed value(按计算值) | by-computed-value |
| repeatable list(可重复列表) | repeatable-list |
| (见规范正文) | custom |
legacy-alias-for 字段
(注意与下文的 logical-alias-for 区分。)
遗留名称别名(Legacy name alias)指的是:属性在规范中的名字变了,但语法没变,因此"设置旧名"被直接定义为"设置新名"。例如 font-stretch 被重命名为 font-width,所以 font-stretch 现在是 font-width 的遗留名称别名。
logical-alias-for 字段
(注意与上文的 legacy-alias-for 区分。)
逻辑别名是像 margin-block-start 这样的属性:它可能根据所应用元素的具体情况,把值赋给多个属性中的某一个(如 margin-top、margin-bottom、margin-left 或 margin-right)。
logical-alias-for 应是一个包含两个字段的对象,两者均为必填:
| 字段 | 说明 |
|---|---|
group |
字符串。该逻辑属性所属的逻辑属性组名称(见下文 LogicalPropertyGroups.json 一节)。 |
mapping |
字符串。该属性与组的关系,例如它是块的末端值时填 block-end。 |
multiplicity 字段
三个可能的取值为 "single"、"list" 和 "coordinating-list"。
多数属性表示单个"东西",它可能由多个部分组成。例如 border 接受颜色、样式和粗细,但它仍然是一条边。也有属性是列表:background 接受以逗号分隔的多个图层;counter-increment 虽然没有逗号,但它同样是递增的计数器列表。
列表也可以是指纹规范中的"协调列表属性"(coordinating list properties,即 css-values 规范中的 linked properties),此时应标记为 coordinating-list 而非 list。
positional-value-list-shorthand 字段
部分简写属性与常规简写不同:提供的值与 longhand 的映射并非 1 对 1,而是取决于提供了多少个值,例如 margin、border-width、gap 等。
这些属性在解析和序列化时有各自不同的行为,标记它们允许生成代码以统一的方式实现这类行为。
quirks 字段
取值来自 WHATWG 的 Quirks 规范中 CSS 部分,映射关系如下:
| 规范术语 | JSON 取值 |
|---|---|
| The hashless hex color quirk(无井号十六进制颜色怪异) | hashless-hex-color |
| The unitless length quirk(无单位长度怪异) | unitless-length |
requires-computation 字段
一个元素上属性的指定值(specified value)有三种确定方式:
- 继承(Inherited):例如它是继承属性且未指定值,或者指定了
inherit关键词; - 初始(Initial):例如它_不是_继承属性且未指定值,或者指定了
initial关键词; - 级联(Cascaded):基于样式为该元素级联出了一个值。
之后,系统通过执行计算过程把指定值转为计算值(computed value,见 StyleComputer.cpp 中的 StyleComputer::compute_value_of_property)。
由于某些属性在上述某些情形下,其计算值保证与指定值相同,通过声明那些_不_相同的情形,就可以把计算过程限定为仅在必要时执行:
| 取值 | 行为 |
|---|---|
always |
无论指定值如何确定,始终执行计算过程 |
non-inherited-value |
仅当指定值基于初始值或级联值确定时执行计算过程 |
cascaded-value |
仅当指定值基于级联值确定时执行计算过程 |
never |
始终跳过计算过程 |
valid-identifiers 字段
一个 CSS 关键词名列表,表示该属性接受的关键词。如果规范为这样一组关键词提供了命名,更推荐定义一个枚举并把枚举名放进 valid-types 数组。
部分属性存在遗留取值别名(legacy value alias):某个关键词被解析为另一个关键词。这可以通过 "foo>bar" 的形式支持,表示让 foo 成为 bar 的别名。
valid-types 字段
valid-types 数组列出 CSS 取值类型的名称,取自最新 CSS Values and Units 规范,并去掉名称两侧的 <>。对于数值类型,使用带括号的区间表示法(bracketed range notation),例如 width 可取任意非负长度,所以它的 valid-types 数组中有 "length [0,∞]"。对于 <custom-ident>,被排除的标识符放在 ![] 内,例如 "custom-ident ![all,none]"。
LogicalPropertyGroups.json:逻辑属性组
一组相互对应的 CSS 属性可以被归入一个所谓"逻辑属性组"(logical property group)。例如所有 margin-* 属性都在 margin 组中。
LogicalPropertyGroups.json 中的数据用于在运行时把逻辑属性(如 margin-block-start)映射到其物理对应项(如 margin-top),映射依据书写方向(writing direction)。该文件本身不直接生成任何代码,而是作为生成 PropertyID 代码的一部分被使用——这一点与 generate_css_implementation() 中 -g 参数的传入方式一致。
文件是单个对象,键为逻辑属性组名,值为对象:把物理维度、边或角映射到相关属性。具体有哪些键取决于该组——例如 size 组包含 width 和 height。
Descriptors.json:at-rule 的描述符
描述符(Descriptors)基本就是"属性",只不过面向 at-rule 而非普通样式。整体结构是单个 JSON 对象,键为 at-rule 名,值为该 at-rule 的数据,主体部分是该 at-rule 可以拥有的描述符信息。生成代码提供:
- 一个
AtRuleID枚举,主要用作解析描述符的参数,因为多个 at-rule 可能拥有同名描述符; FlyString to_string(AtRuleID),主要用于调试日志;- 一个
DescriptorID枚举,列出所有描述符; Optional<DescriptorID> descriptor_id_from_string(AtRuleID, Utf16View),用于从字符串获取某个 at-rule 下若存在的 DescriptorID;Utf16FlyString const& to_string(DescriptorID),用于序列化描述符名;bool at_rule_supports_descriptor(AtRuleID, DescriptorID),查询给定 at-rule 是否允许该描述符;RefPtr<StyleValue const> descriptor_initial_value(AtRuleID, DescriptorID),获取描述符的初始值;DescriptorMetadata get_descriptor_metadata(AtRuleID, DescriptorID),返回解析描述符时使用的元数据。
生成器 generate_libweb_css_descriptors.py 产出的 DescriptorID.h/cpp 是 CSS/Descriptor.cpp、CSSDescriptors.h 等运行时类型的数据基础。
at-rule 字段
每个 at-rule 对象包含以下字段,前两个必填:
| 字段 | 说明 |
|---|---|
spec |
字符串。定义该 at-rule 的规范 URL。 |
descriptors |
对象,键为描述符名,值为其属性对象,见下文。 |
custom-descriptors |
对象,自定义描述符(即 --foo 这类)的配置。省略表示禁止自定义描述符。 |
描述符字段
每个描述符对象可以有如下字段:
| 字段 | 默认值 | 必填 | 说明 |
|---|---|---|---|
allow-arbitrary-substitution-functions |
false |
否 | 布尔值。该描述符是否支持任意替换函数。 |
allow-css-wide-keywords |
false |
否 | 布尔值。基于 PropertyID 的解析是否包含 CSS 全局关键词(如 initial、inherit)。 |
initial |
无 | 否 | 字符串。未提供时描述符的初始值。 |
legacy-alias-for |
无 | 否 | 字符串。本描述符是哪一个描述符的别名。 |
syntax |
N/A | 是 | 字符串数组。每个字符串是规范中的一个可选取值。 |
FIXME 或 NOTE |
无 | 否 | 字符串,用于留下备注。 |
自定义描述符字段
每个自定义描述符对象包含如下字段:
| 字段 | 默认值 | 必填 | 说明 |
|---|---|---|---|
allow-arbitrary-substitution-functions |
false |
否 | 布尔值。该描述符是否支持任意替换函数。 |
allow-css-wide-keywords |
false |
否 | 布尔值。基于 PropertyID 的解析是否包含 CSS 全局关键词(如 initial、inherit)。 |
syntax |
N/A | 是 | 字符串数组。每个字符串是一个可选取值。 |
FIXME 或 NOTE |
无 | 否 | 字符串,用于留下备注。 |
Keywords.json:CSS 关键词全集
Keywords.json 是单个 JSON 字符串数组,每个元素是一个 CSS 关键词,例如 auto、none、medium 或 currentcolor。它会生成 Keyword.h 和 Keyword.cpp。任何属性或媒体特性用到的关键词都必须在此定义——这是新增 CSS 属性取值时的硬性前置条件。
生成代码提供:
- 供
KeywordStyleValue使用的Keyword枚举; Optional<Keyword> keyword_from_string(StringView),尝试把字符串转换为 Keyword;StringView string_from_keyword(Keyword),把 Keyword 转回字符串;bool is_css_wide_keyword(StringView),判断字符串是否为特殊的 "CSS 全局关键词"。
Enums.json:关键词枚举的自动封装
Enums.json 是单个 JSON 对象,键为枚举名,值为关键词名数组。它会生成 Enums.h 和 Enums.cpp。
我们经常需要定义"由若干关键词组成"的枚举。Enums.json 允许自动生成这些枚举,以及配套的 Keyword 之间相互转换、转字符串的函数。这些枚举还可以直接用于 Properties.json 中的属性定义——把枚举名放进 valid-types 数组即可。这有助于减少重复:例如所有 border-*-style 属性接受同一组关键词,于是它们统一实现为一个 line-style 枚举。
生成代码为每个枚举(以 "foo" 为例)提供:
- 一个
Foo枚举,列出其取值; Optional<Foo> keyword_to_foo(Keyword),把Keyword转换为Foo;Keyword to_keyword(Foo),把Foo转回Keyword;StringView to_string(Foo),把Foo直接转成字符串。
Enums.json 同时还是 PropertyID 生成器的输入之一(-e 参数),两者在数据层面互相咬合。
PseudoClasses.json:伪类
PseudoClasses.json 是单个 JSON 对象,键为选择器伪类名,值为包含该伪类字段信息的对象。它生成 PseudoClass.h。
每个条目包含以下属性:
| 字段 | 是否必填 | 默认值 | 说明 |
|---|---|---|---|
argument |
除非指定了 legacy-alias-for |
无 | 字符串,描述该伪类函数参数的语法;对于标识符样式的伪类留空。语法直接取自规范。 |
legacy-alias-for |
否 | 无 | 用于声明本条目应被视为指定伪类的遗留选择器别名(legacy selector alias)。 |
生成代码提供:
- 一个
PseudoClass枚举,列出所有伪类名。
PseudoElements.json:伪元素
PseudoElements.json 是单个 JSON 对象,键为伪元素名,值为包含该伪元素字段信息的对象。它生成 PseudoElement.h 和 PseudoElement.cpp。
每个条目包含以下属性:
| 字段 | 是否必填 | 默认值 | 说明 |
|---|---|---|---|
alias-for |
否 | 无 | 声明本伪元素是指定伪元素的别名。 |
function-syntax |
否 | 无 | 若为函数型伪元素,其函数参数的语法,直接抄自规范。 |
implementation |
除非 type 是 "function" |
无 | 该伪元素的实现方式,"synthetic" 或 "element-reference",见下文。 |
is-allowed-in-has |
否 | false |
是否为允许用于 :has() 的伪元素。 |
is-element-backed |
否 | false |
是否为元素支撑(element-backed)伪元素。 |
is-pseudo-root |
否 | false |
是否为伪元素根(pseudo-element root)。 |
is-tree-abiding |
否 | false |
是否为 tree-abiding 伪元素。 |
property-whitelist |
否 | 无 | 部分伪元素只允许特定属性。若有,在此数组中列出属性名,并支持若干表示属性类别的特殊值——见下文。 |
spec |
否 | 无 | 规范定义的链接,仅供参考,不会用于生成代码。 |
type |
否 | "identifier" |
伪元素类型:"identifier"、"function" 或 "both"。 |
生成代码提供:
- 一个
PseudoElement枚举,列出所有伪元素名; StringView pseudo_element_name(PseudoElement),把PseudoElement转回字符串;bool is_tree_abiding_pseudo_element(PseudoElement),判断伪元素是否为 tree-abiding;bool is_pseudo_element_root(PseudoElement),判断伪元素是否为伪元素根。
property-whitelist 字段
这是一个字符串数组。属性可以直接给出属性名(如 "color"),也可以用前导 # 表示属性类别(如 "#font-properties")——因为规范经常直接说明允许"某一组"属性而不是逐一点名。尚未支持的属性可以加 FIXME: 前缀,会被忽略。属性组与"始终适用"的属性定义在 PseudoElementPropertyGroups.txt 中,并生成进 Rust 侧的级联表。
支持的类别有:
#background-properties:background及其 longhand;#border-properties:border、border-radius及其 longhand;#custom-properties:自定义属性,即 CSS 变量;#font-properties:font、其 longhand 以及其他font-*属性;#inline-layout-properties:CSS Inline 规范中定义的属性;#inline-typesetting-properties:CSS Text 规范中定义的属性;#margin-properties:margin及其 longhand;#padding-properties:padding及其 longhand;#text-decoration-properties:CSS Text Decoration 规范中定义的属性。
implementation 字段
伪元素有两种实现方式:
- "synthetic"(合成):源元素是伪元素数据的权威方(如动画、计算样式等),并自行处理其行为(例如生布局节点)。
::first-line和::before属于此类; - "element-reference"(元素引用):伪元素引用 DOM 中另一处的"真实"元素,通常位于源元素的影子树(shadow tree)中,因此本身不是权威方。表单类伪元素(如
::placeholder)属于此类。
注意:"element-reference" 是实现细节,与规范中的 "element-backed" 伪元素概念不同。按撰写时情况,所有 "element-backed" 伪元素都是 "element-reference",但反之不成立。
MediaFeatures.json:媒体特性
MediaFeatures.json 是单个 JSON 对象,键为媒体特性名,值为包含该媒体特性字段信息的对象。它生成 MediaFeatureID.h 和 MediaFeatureID.cpp。
<media-feature> 是媒体查询可以检查的取值,在最新 Media Queries 规范的 @media 描述符表中列出。
这里的定义类似 Properties.json 定义的简化版:
| 字段 | 说明 |
|---|---|
type |
字符串。媒体特性的求值方式,discrete 或 range。 |
values |
字符串数组。直接取自规范,关键词原样保留,类型名带 <>。类型可以是 <boolean>、<integer>、<length>、<ratio> 或 <resolution>。 |
false-keywords |
字符串数组。媒体特性按 @media (foo) 求值时应视为 false 的关键词。通常只有单个值,如 "none"。 |
生成代码提供:
- 一个
MediaFeatureID枚举,列出每个媒体特性; StringView string_from_media_feature_id(MediaFeatureID),把MediaFeatureID转回字符串;bool media_feature_accepts_type(MediaFeatureID, QueryValueType),判断该媒体特性是否接受此类型的值;bool media_feature_accepts_keyword(MediaFeatureID, Keyword),判断该媒体特性是否接受此关键词;bool media_feature_keyword_is_falsey(MediaFeatureID, Keyword),判断给定关键词在布尔上下文(如@media (foo))中求值时是否视为 false。
MathFunctions.json:CSS 数学函数
MathFunctions.json 是单个 JSON 对象,描述每个 CSS 数学函数,键为函数名,值为描述该函数属性的对象。它生成 MathFunctions.h 和 MathFunctions.cpp。
每个条目有两个属性:
| 字段 | 说明 |
|---|---|
parameter-validation |
可选字符串。"same" 或 "consistent",取决于规范要求输入计算式是同一类型还是兼容类型。默认 "same"。只有一个参数时可忽略。 |
parameters |
参数定义对象数组,见下文。 |
参数定义包含以下属性:
| 字段 | 说明 |
|---|---|
name |
字符串。参数名,与规范中一致。 |
type |
字符串。参数接受的类型,单个字符串内用 | 分隔。 |
required |
布尔值。该参数是否必填。 |
生成代码提供:
- 一个
MathFunction枚举,列出数学函数; - CSS 解析器
parse_math_function()方法的实现。
TransformFunctions.json:变换函数
TransformFunctions.json 是单个 JSON 对象,描述每个 CSS 变换函数,键为函数名,值为描述该函数属性的对象。它生成 TransformFunctions.h 和 TransformFunctions.cpp。
每个条目目前只有一个 parameters 属性,为参数定义对象数组。参数定义包含:
| 字段 | 说明 |
|---|---|
type |
字符串。参数接受的类型。 |
required |
布尔值。该参数是否必填。 |
生成代码提供:
- 一个
TransformFunction枚举,列出变换函数; Optional<TransformFunction> transform_function_from_string(StringView),把字符串解析为TransformFunction;StringView to_string(TransformFunction),把TransformFunction转回字符串;TransformFunctionMetadata transform_function_metadata(TransformFunction),获取变换函数的元数据,例如参数列表。
EnvironmentVariables.json:CSS 环境变量
EnvironmentVariables.json 是单个 JSON 对象,描述每个 CSS 环境变量,键为环境变量名,值为描述变量属性的对象。它生成 EnvironmentVariable.h 和 EnvironmentVariable.cpp。
每个条目有三个属性,全部取自规范:
| 字段 | 说明 |
|---|---|
spec |
字符串。该环境变量的规范定义 URL。 |
type |
字符串。变量的 CSS 取值类型,如 <length>。 |
dimensions |
整数。变量的维度数,标量为 0。 |
生成代码提供:
- 一个
EnvironmentVariable枚举,列出环境变量; StringView to_string(EnvironmentVariable),把EnvironmentVariable转回字符串;ValueType environment_variable_type(EnvironmentVariable),获取变量的取值类型;u32 environment_variable_dimension_count(EnvironmentVariable),获取其维度数。
Units.json:维度与单位
Units.json 是 JSON 对象,键为维度类型名,值为对象;该对象的键是单位名,值是每个单位的数据。它生成 Units.h 和 Units.cpp。
每个单位包含以下属性:
| 字段 | 说明 |
|---|---|
is-canonical-unit |
布尔值,默认 false。每个维度有一个规范单位(canonical unit)。 |
number-of-canonical-unit |
数值。一个该单位等价于多少个规范单位。相对单位与规范单位本身可忽略此项。 |
is-relative-to |
字符串。某些长度单位相对字体或视口,为这类单位设置为 "font" 或 "viewport"。 |
生成代码提供:
- 一个
DimensionType枚举,列出定义了单位的每种维度类型; Optional<DimensionType> dimension_for_unit(StringView),查询某单位归属哪个维度(若有);- 为每个维度 "foo" 生成一个
FooUnit枚举,列出该维度的所有单位;对其中每个枚举……constexpr FooUnit canonical_foo_unit(),该类型的规范单位;Optional<FooUnit> string_to_foo_unit(StringView),从字符串解析单位;StringView to_string(FooUnit),序列化这些单位;bool units_are_compatible(FooUnit, FooUnit),判断两者是否兼容——即能否从一种换算到另一种;double ratio_between_units(FooUnit, FooUnit),获取把第一个单位换算为第二个单位的乘数;
bool is_absolute(LengthUnit)、bool is_font_relative(LengthUnit)、bool is_viewport_relative(LengthUnit)和bool is_relative(LengthUnit),用于判断长度单位类别。
Units.json 除了驱动 Units.{h,cpp} 的生成,还会被 generate_libweb_css_numeric_factory_methods.py 二次消费,额外生成 GeneratedCSSNumericFactoryMethods 的头文件、实现与 IDL 绑定,供网页侧构造 CSS 数值对象使用。
实战:新增或修改一个 CSS 属性时的完整路径
把上述文件串起来,一个典型的变更流程是:
- 确认属性涉及的关键词:先在 Keywords.json 中登记所有用到的关键词(属性与媒体特性共用该表,缺失会导致解析失败);
- 复用或新增枚举:若属性接受一组具名关键词,在 Enums.json 中定义枚举,把枚举名写入属性的
valid-types,而不是散列在valid-identifiers; - 登记属性条目:在 Properties.json 中按字母序新增条目,填齐
animation-type、inherited、initial、requires-computation等必填字段(生成器会校验字母序与必填项);简写属性通过longhands关联其展开项; - 涉及逻辑属性时:在 LogicalPropertyGroups.json 中确认属性组定义,用
logical-alias-for的group+mapping两个字段挂载逻辑属性,生成器会自动以组内第一个物理属性为模板补齐其余字段; - 重新构建:
generate_css_implementation()声明了对各 JSON 的依赖,任何输入变更都会自动触发对应生成器重新运行,无需手工执行生成脚本;产物落在Build/<build-preset>/Libraries/LibWeb/CSS/下参与后续编译。
小结
Ladybird 的 CSS 生成机制把"规范中的数据"(属性表、关键词表、描述符表、媒体特性表、单位表等)全部收敛到 Libraries/LibWeb/CSS 目录下的一组 JSON 文件中,由 Meta/Generators 下的 Python 脚本在 CMake 构建期统一生成 PropertyID、GeneratedCSSStyleProperties、Keyword、Enums、PseudoClass、PseudoElement、MediaFeatureID、Units、DescriptorID、TransformFunctions、MathFunctions、EnvironmentVariable 等 C++ 代码。理解"每个字段映射到哪些生成函数"是阅读与扩展这套系统的关键:无论是新增属性、登记新关键词,还是定义逻辑属性组与伪元素白名单,入口都是修改对应 JSON 后交给构建系统自动再生成,而无需手工维护任何 C++ 枚举或查询函数。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0622
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00