
SerenityOS LibWeb CSS 代碼生成體系從 JSON 定義到 C 實現【免費下載鏈接】serenityThe Serenity Operating System 項目地址: https://gitcode.com/GitHub_Trending/se/serenity導讀SerenityOS 的 LibWeb 引擎在構建時會從一組 JSON 文件中批量生成大量 CSS 相關 C 代碼。這些文件定義了每個 CSS 屬性的取值、繼承性、初始值、動畫類型以及關鍵字、枚舉、偽類、媒體特性、數學函數與變換函數等元數據。本文以 CSSGeneratedFiles.md 為主線完整梳理 7 個 JSON 輸入文件的結構與字段語義并結合 LibWeb/CSS 源碼目錄與 LibWeb 代碼生成器 中的實現細節講解如何為瀏覽器引擎新增或修改一個 CSS 屬性及其取值。讀完本文你將掌握 LibWeb 中 CSS 元數據的組織方式、生成器的產出清單以及參與 CSS 規范實現時的標準工作流。一、整體架構JSON 定義、生成器與構建集成LibWeb 的 CSS 實現采用數據驅動 構建期代碼生成的模式輸入一個或多個.json文件位于 Userland/Libraries/LibWeb/CSS目前包含Properties.json、Keywords.json、Enums.json、PseudoClasses.json、MediaFeatures.json、MathFunctions.json、TransformFunctions.json另外倉庫中還存在EasingFunctions.json。生成器位于 Meta/Lagom/Tools/CodeGenerators/LibWeb包含GenerateCSSPropertyID.cpp、GenerateCSSKeyword.cpp、GenerateCSSEnums.cpp、GenerateCSSPseudoClass.cpp、GenerateCSSMediaFeatureID.cpp、GenerateCSSMathFunctions.cpp、GenerateCSSTransformFunctions.cpp、GenerateCSSStyleProperties.cpp等。輸出生成結果落在構建目錄Build/build-preset/Lagom/Userland/Libraries/LibWeb/CSS/下如PropertyID.h/cpp、Keyword.h/cpp等。這些生成器會在構建過程中自動運行通常開發者無需手動干預。但當你需要新增或修改一個 CSS 屬性及其取值時就必然要與這些 JSON 文件打交道。生成器內部通過AK::SourceGenerator輸出 C 代碼并借助 GeneratorUtil.h 等公共工具完成頭文件守衛、write_if_changed之類的落盤邏輯參見 GenerateCSSPropertyID.cpp。二、Properties.jsonCSS 屬性的注冊中心Properties.json約 2800 行為每個 CSS 屬性維護一條記錄描述它接受哪些值、是否繼承、初始值等元數據。它會生成以下文件PropertyID.h/PropertyID.cpp屬性 ID 枚舉及各種查詢函數GeneratedCSSStyleProperties.h/GeneratedCSSStyleProperties.cpp綁定到 WebIDL 的屬性訪問器GeneratedCSSStyleProperties.idl文件結構是一個 JSON 對象鍵為屬性名值為該屬性的數據。大多數元數據都來自對應 CSS 規范中該屬性的信息框information box。每個屬性會帶有下表的部分字段注意帶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是布爾值。屬性是否被子元素繼承bool is_inherited_property(PropertyID)initial是字符串。未指定時屬性的初始值NonnullRefPtrCSSStyleValue property_initial_value(JS::Realm, PropertyID)legacy-alias-for否無字符串。該屬性所指向的舊名別名屬性見下文logical-alias-for否無字符串數組。該屬性所指向的邏輯別名屬性見下文longhands否[]字符串數組。若是簡寫屬性shorthand列出其展開的子屬性VectorPropertyID longhands_for_shorthand(PropertyID)max-values否1整數。該屬性最多可解析多少個值例如margin最多 4 個size_t property_maximum_value_count(PropertyID)percentages-resolve-to否無字符串。百分比解析成什么類型例如width的百分比解析為lengthOptionalValueType property_resolves_percentages_relative_to(PropertyID)quirks否[]字符串數組。屬性在 quirks 模式下的特殊行為見下文bool property_has_quirk(PropertyID, Quirk)valid-identifiers否[]字符串數組。屬性接受哪些關鍵字。更推薦定義枚舉并把枚舉名寫進valid-typesbool property_accepts_keyword(PropertyID, Keyword)valid-types否[]字符串數組。屬性接受哪些值類型見下文bool property_accepts_type(PropertyID, ValueType)從源碼看生成器會逐個遍歷該 JSON 對象先判斷屬性是否設置了legacy-alias-forGenerateCSSPropertyID.cpp處理邏輯別名再檢查longhandsGenerateCSSPropertyID.cpp與valid-types數組GenerateCSSPropertyID.cpp最終為每個屬性產出對應的枚舉成員、初始值函數與類型判定函數。倉庫中的真實示例可以直觀印證字段用法。例如兼容性前綴屬性的定義非常精簡-webkit-align-content: { legacy-alias-for: align-content }而一個完整屬性則同時攜帶動畫類型、繼承性與初始值例如color一類animation-type: by-computed-value, inherited: true, initial: currentColor, valid-types: [ ... ]對應 Properties.json 附近的color定義animation簡寫屬性則使用initial: none 0s ease 1 normal running 0s none這樣的復合初始值見 Properties.json。2.1animation-type屬性如何被動畫化該字段的合法取值由 Web Animations 規范定義JSON 值與規范術語的對應關系如下規范術語JSON 值not animatablenonediscretediscreteby computed valueby-computed-valuerepeatable listrepeatable-list見規范正文custom從倉庫數據看color是by-computed-value按計算值平滑過渡align-content等布局相關屬性是discrete離散跳變animation-duration則是none不可動畫。2.2legacy-alias-for與logical-alias-for兩個名字相似但概念不同的別名舊名別名legacy name alias屬性在規范中的名字發生了變化但語法沒有變因此設置舊名等同于直接設置新名。例如font-stretch被重命名為font-width于是font-stretch成為font-width的舊名別名。倉庫中大量-webkit-*屬性就是這類別名的典型-webkit-align-content、-webkit-animation、-webkit-border-radius等全部通過legacy-alias-for指回標準屬性名見 Properties.json。邏輯別名logical alias例如margin-block-start它會根據應用到的元素把值賦給margin-top、margin-bottom、margin-left或margin-right中的某一個。因此需要在logical-alias-for中列出所有可能被其指向的屬性。2.3quirksQuirks 模式下的特殊行為Quirks 規范定義了以下兩種特殊行為規范術語JSON 值The hashless hex color quirkhashless-hex-colorThe unitless length quirkunitless-length例如在 quirks 模式下允許background-color: f00這種省略#的十六進制顏色寫法或width: 10這種省略單位的長度寫法。是否啟用這些寬松解析正是由該字段驅動。2.4valid-types值類型與帶括號區間記法valid-types數組列出的是 CSS Values and Units 規范中定義的值類型名去掉尖括號后的名字。對數值類型項目使用帶括號區間記法bracketed range notation例如width可以接受任意非負長度因此其valid-types數組中含有length [0,∞]。這種寫法讓生成器可以直接生成帶范圍約束的解析與校驗邏輯將規范約束落到類型系統層面。三、Keywords.json全局關鍵字注冊表Keywords.json共 430 行是一個純字符串數組每個元素是一個 CSS 關鍵字例如auto、none、medium、currentcolor。它會生成Keyword.h與Keyword.cpp。任何屬性或媒體特性用到的關鍵字都必須在這里登記。除了標準關鍵字倉庫中還登記了一批內部關鍵字例如-libweb-center、-libweb-left、-libweb-link以及一系列-libweb-palette-*關鍵字如-libweb-palette-base、-libweb-palette-selection它們用于把 SerenityOS 系統調色板暴露給 Web 內容見 Keywords.json。這說明了該文件的擴展邊界不僅服務標準 CSS也承載瀏覽器自身的私有擴展。生成的代碼提供Keyword枚舉供CSSKeywordValue使用OptionalKeyword keyword_from_string(StringView)嘗試把字符串轉成KeywordStringView string_from_keyword(Keyword)把Keyword轉回字符串bool is_css_wide_keyword(StringView)判斷字符串是否為特殊的 CSS-wide keywords如inherit、initial、unset、revert四、Enums.json一鍵生成關鍵字集合枚舉Enums.json共 521 行是一個 JSON 對象鍵是枚舉名值是關鍵字名數組。它生成Enums.h與Enums.cpp。很多屬性需要接受一組固定的關鍵字逐個重復書寫valid-identifiers容易出錯且冗長。Enums.json允許自動生成這類枚舉以及枚舉與Keyword、字符串之間的互轉函數。生成的枚舉還可以直接通過枚舉名出現在Properties.json的valid-types數組中從而在屬性定義中被復用。典型的例子是border-*-style系列屬性接受同一組關鍵字因此被實現為line-style枚舉見 Enums.json。倉庫數據還顯示align-content、align-items、align-self等各自的取值集合也都以枚舉形式集中定義見 Enums.json。以枚舉 foo 為例每個枚舉生成的代碼包括枚舉類型FooOptionalFoo keyword_to_foo(Keyword)把Keyword轉換為FooKeyword to_keyword(Foo)把Foo轉回KeywordStringView to_string(Foo)直接把Foo轉成字符串五、PseudoClasses.json偽類元數據PseudoClasses.json共 146 行是一個 JSON 對象鍵為選擇器偽類名值為描述該偽類的對象。它生成PseudoClass.h與PseudoClass.cpp。每個條目只有一個必填字段argument它是偽類函數參數的語法grammar字符串對標識符式偽類如:hover、:active則為空字符串。語法直接取自規范。倉庫中的實例active: { argument: }, dir: { argument: ident }, has: { argument: forgiving-relative-selector-list }, host: { argument: compound-selector? }, is: { argument: forgiving-selector-list }, lang: { argument: language-ranges }分別見 PseudoClasses.json、PseudoClasses.json、PseudoClasses.json、PseudoClasses.json、PseudoClasses.json、PseudoClasses.json。從中可以看到:has()、:is()這類接受寬松選擇器列表的新偽類與:hover這類無參偽類的差別——argument直接承載了后續解析函數所需的關鍵信息。生成的代碼提供PseudoClass枚舉列出所有偽類名OptionalPseudoClass pseudo_class_from_string(StringView)把字符串解析為PseudoClassStringView pseudo_class_name(PseudoClass)把PseudoClass轉回字符串PseudoClassMetadata結構體保存 JSON 文件中的數據PseudoClassMetadata pseudo_class_metadata(PseudoClass)獲取該元數據六、MediaFeatures.jsonmedia可查詢的媒體特性MediaFeatures.json共 261 行是一個 JSON 對象鍵為媒體特性名值為描述該特性的對象。它生成MediaFeatureID.h與MediaFeatureID.cpp。media-feature是媒體查詢可以檢查的取值列在最新 Media Queries 規范的media描述符表中。這里的定義可以看作Properties.json定義的簡化版本字段描述type字符串。媒體特性的求值方式discrete離散或range范圍values字符串數組。直接取自規范關鍵字原樣保留類型名帶。類型可以是boolean、integer、length、ratio或resolution倉庫中的真實定義示例any-hover: { type: discrete, values: [none, hover] }, color: { type: range, values: [integer] }, color-gamut: { type: discrete, values: [srgb, p3, rec2020] }, aspect-ratio:{ type: range, values: [ratio] }分別見 MediaFeatures.json、MediaFeatures.json、MediaFeatures.json、MediaFeatures.json。color使用range類型表示顏色位深為 nany-hover使用discrete表示設備是否支持懸停二者求值邏輯截然不同。生成的代碼提供MediaFeatureValueType枚舉列出可能的取值類型MediaFeatureID枚舉列出每個媒體特性OptionalMediaFeatureID media_feature_id_from_string(StringView)字符串轉MediaFeatureIDStringView string_from_media_feature_id(MediaFeatureID)MediaFeatureID轉回字符串bool media_feature_type_is_range(MediaFeatureID)判斷是否為range類型區別于discretebool media_feature_accepts_type(MediaFeatureID, MediaFeatureValueType)是否接受該值類型bool media_feature_accepts_keyword(MediaFeatureID, Keyword)是否接受該關鍵字七、MathFunctions.jsonCSS 數學函數MathFunctions.json共 232 行是一個 JSON 對象描述每個 CSS 數學函數鍵為函數名值為描述函數屬性的對象。它生成MathFunctions.h與MathFunctions.cpp。每個條目目前只有一個屬性parameters即參數定義對象數組。參數定義具有以下字段字段描述name字符串。參數名與規范一致type字符串。參數可接受類型單個字符串用\|分隔required布爾值。該參數是否必填倉庫中的示例abs: { parameters: [ { name: value, type: number|dimension|percentage, required: true } ] }, atan2: { parameters: [ { name: y, type: number|dimension|percentage, required: true }, { name: x, type: number|dimension|percentage, required: true } ] }, clamp: { parameters: [ { name: min, ... }, { name: central, ... }, ... ] }見 MathFunctions.json、MathFunctions.json、MathFunctions.json。atan2(y, x)的兩個參數都是必填的數值類參數abs(value)接受數值、維度或百分比。生成的代碼提供MathFunction枚舉列出全部數學函數CSS Parser 的parse_math_function()方法的實現也就是說MathFunctions.json不止生成數據還會直接生成解析器的函數體把支持哪些數學函數、每個函數接受什么參數編譯進解析流程。八、TransformFunctions.jsonCSS 變換函數TransformFunctions.json共 290 行是一個 JSON 對象描述每個 CSS 變換函數鍵為函數名值為描述函數屬性的對象。它生成TransformFunctions.h與TransformFunctions.cpp。每個條目目前只有一個屬性parameters參數定義對象數組參數定義字段如下字段描述type字符串。參數可接受類型required布爾值。該參數是否必填與數學函數不同變換函數的參數定義不攜帶name字段。倉庫中的示例——matrix()有 6 個必填number參數matrix3d()則有 16 個matrix: { parameters: [ { type: number, required: true }, { type: number, required: true }, ... 共 6 個 ... ] }, matrix3d: { parameters: [ ... 共 16 個 ... ] }見 TransformFunctions.json、TransformFunctions.json。生成的代碼提供TransformFunction枚舉列出全部變換函數OptionalTransformFunction transform_function_from_string(StringView)把字符串解析為TransformFunctionStringView to_string(TransformFunction)把TransformFunction轉回字符串TransformFunctionMetadata transform_function_metadata(TransformFunction)獲取函數元數據如參數列表九、生成器實現要點代碼生成器統一位于 Meta/Lagom/Tools/CodeGenerators/LibWeb每個 JSON 文件對應一個GenerateCSS*工具JSON 輸入生成器主要輸出Properties.jsonGenerateCSSPropertyID.cppPropertyID.h/cpp、GeneratedCSSStyleProperties.h/cpp/idlKeywords.jsonGenerateCSSKeyword.cppKeyword.h/cppEnums.jsonGenerateCSSEnums.cppEnums.h/cppPseudoClasses.jsonGenerateCSSPseudoClass.cppPseudoClass.h/cppMediaFeatures.jsonGenerateCSSMediaFeatureID.cppMediaFeatureID.h/cppMathFunctions.jsonGenerateCSSMathFunctions.cppMathFunctions.h/cppTransformFunctions.jsonGenerateCSSTransformFunctions.cppTransformFunctions.h/cpp從實現細節看以 GenerateCSSPropertyID.cpp 為例生成器以LibMain/Main.h為入口使用LibCore::ArgsParser解析命令行參數通過AK::SourceGenerator組織輸出文本見 GenerateCSSPropertyID.cpp。輸出代碼中注入必要的頭文件如AK/NonnullRefPtr.h、LibJS/Forward.h、LibWeb/Forward.h保證生成的.h/.cpp可以獨立編譯見 GenerateCSSPropertyID.cpp。生成的.cpp還會#include LibWeb/CSS/Enums.h把Enums.json生成的枚舉直接嵌入屬性類型判定邏輯見 GenerateCSSPropertyID.cpp——這印證了Enums.json與Properties.json之間的聯動關系。生成器會自動跳過只含legacy-alias-for的別名屬性避免為別名生成重復的完整定義。由于生成是構建期自動完成的修改 JSON 后重新構建即可看到新生成的代碼出現在Build/build-preset/Lagom/Userland/Libraries/LibWeb/CSS/中。日常開發中基本無需手工觸碰生成產物。十、實戰工作流如何新增一個 CSS 屬性或關鍵字綜合以上內容在 SerenityOS/LibWeb 中新增一個 CSS 能力通常遵循以下步驟登記關鍵字如果新屬性用到的新關鍵字尚未收錄先在 Keywords.json 的字符串數組中追加所有屬性、媒體特性共用的關鍵字池。定義枚舉可選若屬性接受一組固定關鍵字如新的border-*-style同類屬性在 Enums.json 中添加枚舉定義便于在多個屬性間復用。注冊屬性在 Properties.json 中為屬性新增條目按需填寫必填字段animation-type、inherited、initial以及valid-types/valid-identifiers、longhands、max-values、percentages-resolve-to、quirks等可選字段。新增偽類/媒體特性/函數視情況在對應的 PseudoClasses.json填argument語法、MediaFeatures.json填type與values、MathFunctions.json 或 TransformFunctions.json填parameters中登記。重新構建構建系統會自動運行對應生成器產出新的枚舉、ID 與解析函數此后即可在解析器、樣式計算與布局代碼中消費這些生成接口。查閱生成產物如需確認生成結果查看Build/build-preset/Lagom/Userland/Libraries/LibWeb/CSS/下的新文件。這套流程的每個環節都有源碼級支撐關鍵字注冊表、枚舉復用、屬性元數據、偽類語法、媒體特性類型、數學/變換函數參數全部由統一的 JSON 數據驅動最終在構建期被Meta/Lagom/Tools/CodeGenerators/LibWeb下對應的生成器轉換為可編譯、可調用的 C 代碼。這也是 LibWeb 能夠以較低維護成本跟上 CSS 規范演進的關鍵機制之一。【免費下載鏈接】serenityThe Serenity Operating System 項目地址: https://gitcode.com/GitHub_Trending/se/serenity創作聲明:本文部分內容由AI輔助生成(AIGC),僅供參考