<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>LCUI Blog</title>
        <link>https://lcui.org/en/blog</link>
        <description>LCUI Blog</description>
        <lastBuildDate>Wed, 01 Feb 2023 00:00:00 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <language>en</language>
        <item>
            <title><![CDATA[LibCSS 的源码解析]]></title>
            <link>https://lcui.org/en/blog/2023/02/01/decoding-libcss-source-code</link>
            <guid>https://lcui.org/en/blog/2023/02/01/decoding-libcss-source-code</guid>
            <pubDate>Wed, 01 Feb 2023 00:00:00 GMT</pubDate>
            <description><![CDATA[LibCSS 是一个 CSS 解析器和选择引擎，由 C 语言编写，是 NetSurf 网页浏览器项目的一部分且可供其它基于 MIT 许可协议的软件使用。它的主要特性如下：]]></description>
            <content:encoded><![CDATA[<p>LibCSS 是一个 CSS 解析器和选择引擎，由 C 语言编写，是 <a href="http://www.netsurf-browser.org/" target="_blank" rel="noopener noreferrer">NetSurf</a> 网页浏览器项目的一部分且可供其它基于 MIT 许可协议的软件使用。它的主要特性如下：</p>
<ul>
<li>解析 CSS，无论好的还是坏的</li>
<li>简单的 C API</li>
<li>低内存占用</li>
<li>快速的选择引擎</li>
<li>可移植</li>
<li>共享库</li>
</ul>
<p>笔者之所以选择研究 LibCSS 源码，是因为在改造 CSS 库 lcui/css 时遇到了瓶颈。虽然参考 MDN 上的 <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Value_definition_syntax" target="_blank" rel="noopener noreferrer">CSS 值定义语法文档</a>设计并实现了 CSS 值定义语法的解析器和匹配器，但在解决取值的问题时一直找不到最优的方案，其难点在于 CSS 属性的值的数量和类型不是固定的，且由于 <code>background</code> 这类简写属性的存在，还得考虑如何复用取值逻辑。因此 LibCSS 作为一个 C 语言编写的且已经历过浏览器考验的项目，很适合作为笔者的研究对象。</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="用法示例">用法示例<a href="https://lcui.org/en/blog/2023/02/01/decoding-libcss-source-code#%E7%94%A8%E6%B3%95%E7%A4%BA%E4%BE%8B" class="hash-link" aria-label="Direct link to 用法示例" title="Direct link to 用法示例">​</a></h2>
<p>LibCSS 的示例程序源码在 <code>examples/exmaple1.c</code> 中，它展示了如何用 LibCSS 实现 CSS 样式的加载、选择和读取，我们先从它入手，了解 LibCSS 大致的用法和概念，以便于后续的深入研究。</p>
<p>首先从 <code>main()</code> 函数开始，开头部分的代码初始化了 CSS 字符串、样式表的创建参数。</p>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">        css_error code</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        css_stylesheet </span><span class="token operator" style="color:#393A34">*</span><span class="token plain">sheet</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token class-name">size_t</span><span class="token plain"> size</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">char</span><span class="token plain"> data</span><span class="token punctuation" style="color:#393A34">[</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"h1 { color: red } "</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token string" style="color:#e3116c">"h4 { color: #321; } "</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token string" style="color:#e3116c">"h4, h5 { color: #123456; }"</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        css_select_ctx </span><span class="token operator" style="color:#393A34">*</span><span class="token plain">select_ctx</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token class-name">uint32_t</span><span class="token plain"> count</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">unsigned</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">int</span><span class="token plain"> hh</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        css_stylesheet_params params</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        css_media media </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">type </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> CSS_MEDIA_SCREEN</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token function" style="color:#d73a49">UNUSED</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">argc</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token function" style="color:#d73a49">UNUSED</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">argv</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        params</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">params_version </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> CSS_STYLESHEET_PARAMS_VERSION_1</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        params</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">level </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> CSS_LEVEL_21</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        params</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">charset </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"UTF-8"</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        params</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">url </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"foo"</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        params</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">title </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"foo"</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        params</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">allow_quirks </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> false</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        params</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">inline_style </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> false</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        params</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">resolve </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> resolve_url</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        params</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">resolve_pw </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token constant" style="color:#36acaa">NULL</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        params</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">import </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token constant" style="color:#36acaa">NULL</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        params</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">import_pw </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token constant" style="color:#36acaa">NULL</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        params</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">color </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token constant" style="color:#36acaa">NULL</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        params</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">color_pw </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token constant" style="color:#36acaa">NULL</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        params</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">font </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token constant" style="color:#36acaa">NULL</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        params</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">font_pw </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token constant" style="color:#36acaa">NULL</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p>然后，创建样式表，解析 CSS 字符串并追加进样式表中。</p>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic">/* create a stylesheet */</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        code </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">css_stylesheet_create</span><span class="token punctuation" style="color:#393A34">(</span><span class="token operator" style="color:#393A34">&amp;</span><span class="token plain">params</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&amp;</span><span class="token plain">sheet</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">code </span><span class="token operator" style="color:#393A34">!=</span><span class="token plain"> CSS_OK</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token function" style="color:#d73a49">die</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"css_stylesheet_create"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> code</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        code </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">css_stylesheet_size</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">sheet</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&amp;</span><span class="token plain">size</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">code </span><span class="token operator" style="color:#393A34">!=</span><span class="token plain"> CSS_OK</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token function" style="color:#d73a49">die</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"css_stylesheet_size"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> code</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token function" style="color:#d73a49">printf</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"created stylesheet, size %zu\n"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> size</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic">/* parse some CSS source */</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        code </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">css_stylesheet_append_data</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">sheet</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token class-name">uint8_t</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">*</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> data</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                        </span><span class="token keyword" style="color:#00009f">sizeof</span><span class="token plain"> data</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">code </span><span class="token operator" style="color:#393A34">!=</span><span class="token plain"> CSS_OK </span><span class="token operator" style="color:#393A34">&amp;&amp;</span><span class="token plain"> code </span><span class="token operator" style="color:#393A34">!=</span><span class="token plain"> CSS_NEEDDATA</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token function" style="color:#d73a49">die</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"css_stylesheet_append_data"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> code</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        code </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">css_stylesheet_data_done</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">sheet</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">code </span><span class="token operator" style="color:#393A34">!=</span><span class="token plain"> CSS_OK</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token function" style="color:#d73a49">die</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"css_stylesheet_data_done"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> code</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        code </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">css_stylesheet_size</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">sheet</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&amp;</span><span class="token plain">size</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">code </span><span class="token operator" style="color:#393A34">!=</span><span class="token plain"> CSS_OK</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token function" style="color:#d73a49">die</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"css_stylesheet_size"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> code</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token function" style="color:#d73a49">printf</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"appended data, size now %zu\n"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> size</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p>从中我们可以看出在 LibCSS 的设计中样式解析和存储是围绕样式表进行的，<code>css_stylesheet_append_data()</code> 函数能够解析 CSS 代码字符串并将结果存储到样式表中。</p>
<p>接着是创建选择上下文，将上面新建的样式表追加进选择上下文中。</p>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic">/* prepare a selection context containing the stylesheet */</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        code </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">css_select_ctx_create</span><span class="token punctuation" style="color:#393A34">(</span><span class="token operator" style="color:#393A34">&amp;</span><span class="token plain">select_ctx</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">code </span><span class="token operator" style="color:#393A34">!=</span><span class="token plain"> CSS_OK</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token function" style="color:#d73a49">die</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"css_select_ctx_create"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> code</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        code </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">css_select_ctx_append_sheet</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">select_ctx</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> sheet</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> CSS_ORIGIN_AUTHOR</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                        </span><span class="token constant" style="color:#36acaa">NULL</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">code </span><span class="token operator" style="color:#393A34">!=</span><span class="token plain"> CSS_OK</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token function" style="color:#d73a49">die</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"css_select_ctx_append_sheet"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> code</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        code </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">css_select_ctx_count_sheets</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">select_ctx</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&amp;</span><span class="token plain">count</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">code </span><span class="token operator" style="color:#393A34">!=</span><span class="token plain"> CSS_OK</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token function" style="color:#d73a49">die</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"css_select_ctx_count_sheets"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> code</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token function" style="color:#d73a49">printf</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"created selection context with %i sheets\n"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> count</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p>最后，选择样式，输出选择结果：</p>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic">/* select style for each of h1 to h6 */</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">for</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">hh </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"> hh </span><span class="token operator" style="color:#393A34">!=</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">7</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"> hh</span><span class="token operator" style="color:#393A34">++</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                css_select_results </span><span class="token operator" style="color:#393A34">*</span><span class="token plain">style</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token keyword" style="color:#00009f">char</span><span class="token plain"> element</span><span class="token punctuation" style="color:#393A34">[</span><span class="token number" style="color:#36acaa">20</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                lwc_string </span><span class="token operator" style="color:#393A34">*</span><span class="token plain">element_name</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token class-name">uint8_t</span><span class="token plain"> color_type</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                css_color color_shade</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token comment" style="color:#999988;font-style:italic">/* in this very simple example our "document tree" is just one</span><br></span><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">                 * node and is in fact a libwapcaplet string containing the</span><br></span><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">                 * element name */</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token function" style="color:#d73a49">snprintf</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">element</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">sizeof</span><span class="token plain"> element</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"h%i"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> hh</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token function" style="color:#d73a49">lwc_intern_string</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">element</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">strlen</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">element</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&amp;</span><span class="token plain">element_name</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                code </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">css_select_style</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">select_ctx</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> element_name</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                                </span><span class="token operator" style="color:#393A34">&amp;</span><span class="token plain">media</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token constant" style="color:#36acaa">NULL</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                                </span><span class="token operator" style="color:#393A34">&amp;</span><span class="token plain">select_handler</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                                </span><span class="token operator" style="color:#393A34">&amp;</span><span class="token plain">style</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">code </span><span class="token operator" style="color:#393A34">!=</span><span class="token plain"> CSS_OK</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                        </span><span class="token function" style="color:#d73a49">die</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"css_select_style"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> code</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token function" style="color:#d73a49">lwc_string_unref</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">element_name</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                color_type </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">css_computed_color</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                                style</span><span class="token operator" style="color:#393A34">-&gt;</span><span class="token plain">styles</span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">CSS_PSEUDO_ELEMENT_NONE</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                                </span><span class="token operator" style="color:#393A34">&amp;</span><span class="token plain">color_shade</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">color_type </span><span class="token operator" style="color:#393A34">==</span><span class="token plain"> CSS_COLOR_INHERIT</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                        </span><span class="token function" style="color:#d73a49">printf</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"color of h%i is 'inherit'\n"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> hh</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token keyword" style="color:#00009f">else</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                        </span><span class="token function" style="color:#d73a49">printf</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"color of h%i is %x\n"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> hh</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> color_shade</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                code </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">css_select_results_destroy</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">style</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">code </span><span class="token operator" style="color:#393A34">!=</span><span class="token plain"> CSS_OK</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                        </span><span class="token function" style="color:#d73a49">die</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"css_computed_style_destroy"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> code</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p><code>css_select_style()</code> 用于根据给定的选择上下文和结点来选择匹配的样式。它接受 7 个参数，其中的 node 参数指定了用于选择样式的结点，handler 参数提供 node 的各种属性的获取方法。由此可见其它 UI 库若要使用 LibCSS 实现对 CSS 的支持的话是比较容易的，只需要准备好控件句柄/指针和控件的各种操作函数合集即可。</p>
<p><code>css_computed_color()</code> 用于从已计算样式中获取 color 属性的值，传入已计算样式和接受值的指针，返回值是颜色类型。从这个函数的调用代码中我们可以比较容易地推测出其它属性也有对应的以 <code>css_computed_</code> 开头命名的函数来获取值，且用法基本一样。另外，它的第一个参数 <code>style-&gt;styles</code> 是个数组且指定了 <code>CSS_PSEUDO_ELEMENT_NONE</code> 下标，这表明样式选择结果中不只有元素本身的样式，还包括其它伪元素的样式。</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="解析">解析<a href="https://lcui.org/en/blog/2023/02/01/decoding-libcss-source-code#%E8%A7%A3%E6%9E%90" class="hash-link" aria-label="Direct link to 解析" title="Direct link to 解析">​</a></h2>
<p>通过研究上述的示例代码，我们已经知道 <code>css_stylesheet_append_data()</code> 能解析 CSS 字符串，以它为起点进行查找，可以找到包括数据结构、函数、变量在内的相关依赖项：</p>
<ul>
<li><code>css_parser</code></li>
<li><code>css_lexer</code></li>
<li><code>css_language</code></li>
<li><code>css_parser_event</code></li>
<li><code>css_parser_event_handler</code></li>
<li><code>css__parser_parse_chunk()</code></li>
<li><code>parser_state</code></li>
<li><code>parseFuncs</code></li>
<li><code>css__language_create()</code></li>
<li><code>language_handle_event()</code></li>
<li><code>parseProperty</code></li>
<li><code>property_handlers</code></li>
</ul>
<p>以这些依赖项为线索继续深入研究代码，我们会发现整个解析功能涉及到语言解析器、解析器前端、解析器、解析器事件、词法分析器这些概念，源码涉及 parse.c、lex.c、langguage.c、properties.c 等文件，接下来本文将逐个讲解它们。</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="词法分析器">词法分析器<a href="https://lcui.org/en/blog/2023/02/01/decoding-libcss-source-code#%E8%AF%8D%E6%B3%95%E5%88%86%E6%9E%90%E5%99%A8" class="hash-link" aria-label="Direct link to 词法分析器" title="Direct link to 词法分析器">​</a></h3>
<p>词法分析器（Lexer）的职责是将字符串转换成一个个词法单元（Token），它基于有限状态自动机进行解析，大致的解析过程是根据字符内容更新状态，然后调用与状态对应的方法去解析后续字符。</p>
<p>词法单元的数据结构如下：</p>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">typedef</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">struct</span><span class="token plain"> </span><span class="token class-name">css_token</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        css_token_type type</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">struct</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token class-name">uint8_t</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">*</span><span class="token plain">data</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token class-name">size_t</span><span class="token plain"> len</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> data</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        lwc_string </span><span class="token operator" style="color:#393A34">*</span><span class="token plain">idata</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token class-name">uint32_t</span><span class="token plain"> col</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token class-name">uint32_t</span><span class="token plain"> line</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> css_token</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p>它包含类型、数据、行列，<code>data.data</code> 和 <code>data.len</code> 成员记录了字符串的起始位置和长度，由此可见词法单元本质上就是对字符串中的一段字符串的标记。</p>
<p>至于状态自动机，它的实现代码可以概括为：</p>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">css_error 获取词法单元</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">css_lexer </span><span class="token operator" style="color:#393A34">*</span><span class="token plain">词法分析器</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> css_token </span><span class="token operator" style="color:#393A34">*</span><span class="token operator" style="color:#393A34">*</span><span class="token plain">词法单元</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">switch</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">词法分析器</span><span class="token operator" style="color:#393A34">-&gt;</span><span class="token plain">状态</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">case</span><span class="token plain"> 初始状态：</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> 初始状态的动作</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">词法分析器</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> 词法单元</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">case</span><span class="token plain"> 状态</span><span class="token number" style="color:#36acaa">1</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> 状态</span><span class="token number" style="color:#36acaa">1</span><span class="token plain">的动作</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">词法分析器</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> 词法单元</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">case</span><span class="token plain"> 状态</span><span class="token number" style="color:#36acaa">2</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">.</span><span class="token punctuation" style="color:#393A34">.</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p>整个解析过程基于状态驱动，每种状态都有对应的动作，动作执行完后状态会被改为另一个状态，如此往复直到字符串全部解析完为止。这种运转机制，就是有限状态自动机。</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="解析器">解析器<a href="https://lcui.org/en/blog/2023/02/01/decoding-libcss-source-code#%E8%A7%A3%E6%9E%90%E5%99%A8" class="hash-link" aria-label="Direct link to 解析器" title="Direct link to 解析器">​</a></h3>
<p>解析器（Parser）的职责是解析词法单元序列表达的语义。与词法分析器相同之处是它也基于有限状态自动机，其大致的解析过程是调用词法分析器获取下个词法单元，然后根据词法单元内容更新状态，之后调用对应的子解析器。而不同之处是它的解析结果是通过事件处理函数传递的，事件枚举在 src/parse/parse.h 文件中有定义：</p>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">typedef</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">enum</span><span class="token plain"> </span><span class="token class-name">css_parser_event</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        CSS_PARSER_START_STYLESHEET</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        CSS_PARSER_END_STYLESHEET</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        CSS_PARSER_START_RULESET</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        CSS_PARSER_END_RULESET</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        CSS_PARSER_START_ATRULE</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        CSS_PARSER_END_ATRULE</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        CSS_PARSER_START_BLOCK</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        CSS_PARSER_END_BLOCK</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        CSS_PARSER_BLOCK_CONTENT</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        CSS_PARSER_END_BLOCK_CONTENT</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        CSS_PARSER_DECLARATION</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> css_parser_event</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p>从中我们可以看出事件涉及样式表、规则集、<code>@</code> 规则、块、块内容、声明，在它们开始和结束解析的时候会触发事件。</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="语言解析器">语言解析器<a href="https://lcui.org/en/blog/2023/02/01/decoding-libcss-source-code#%E8%AF%AD%E8%A8%80%E8%A7%A3%E6%9E%90%E5%99%A8" class="hash-link" aria-label="Direct link to 语言解析器" title="Direct link to 语言解析器">​</a></h3>
<p>语言解析器作为解析器的前端，负责对接属性解析器和解析器，将解析结果输出到样式表。</p>
<p>src/parse/language.c 文件中 <code>css__language_create()</code> 函数包含了语言解析器的创建过程，其中有一段是将 <code>language_handle_event</code> 函数设为解析器的事件处理器：</p>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">        params</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">event_handler</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">handler </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> language_handle_event</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        params</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">event_handler</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">pw </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> c</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        error </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">css__parser_setopt</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">parser</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> CSS_PARSER_EVENT_HANDLER</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&amp;</span><span class="token plain">params</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">error </span><span class="token operator" style="color:#393A34">!=</span><span class="token plain"> CSS_OK</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token function" style="color:#d73a49">parserutils_stack_destroy</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">c</span><span class="token operator" style="color:#393A34">-&gt;</span><span class="token plain">context</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token function" style="color:#d73a49">free</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">c</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> error</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p><code>language_handle_event</code> 函数的作用就是根据解析器的事件来对后续词法单元做进一步处理，其中包括解析选择器、解析 <code>@</code> 规则、调用属性解析器等。</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="属性解析器">属性解析器<a href="https://lcui.org/en/blog/2023/02/01/decoding-libcss-source-code#%E5%B1%9E%E6%80%A7%E8%A7%A3%E6%9E%90%E5%99%A8" class="hash-link" aria-label="Direct link to 属性解析器" title="Direct link to 属性解析器">​</a></h3>
<p>属性解析器的职责是在解析器解析样式声明时将每条属性转换成一个个连续的字节码存入样式表中。</p>
<p>LibCSS 的属性解析器源码是由生成器自动生成的，Makefile 中有具体的生成方法：</p>
<div class="language-makefile codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-makefile codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain"># Sources</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">AUTOGEN_PARSERS := $(shell $(PERL) -pe'$$_="" unless /^([^\#][^:]+):/;$$_=$$1 . " "' $(DIR)properties.gen)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># Dodgy use of define/eval to bypass DIR changing</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">define build_gen_parser</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">$(BUILDDIR)/gen_parser: $(DIR)css_property_parser_gen.c</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        $$(VQ)$$(ECHO) $$(ECHOFLAGS) " PREPARE: $$@"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        $$(Q)$$(BUILD_CC) -o $$@ $$^</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">endef</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">$(eval $(build_gen_parser))</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">define gen_prop_parser</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">$(DIR)autogenerated_$1.c: $(DIR)properties.gen $(BUILDDIR)/gen_parser</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        $$(VQ)$$(ECHO) $$(ECHOFLAGS) "GENERATE: $$@"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        $$(Q)$$(BUILDDIR)/gen_parser -o $$@ '$(shell $(GREP) "^$1:" $(DIR)properties.gen)'</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">AUTOGEN_SOURCES := $$(AUTOGEN_SOURCES) autogenerated_$1.c</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">endef</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">AUTOGEN_SOURCES :=</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">$(eval $(foreach PROP,$(AUTOGEN_PARSERS),$(call gen_prop_parser,$(PROP))))</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p>属性解析器源文件命名格式为 <code>autogenerated_${属性名}.c</code>， 都依赖生成器 <code>gen_parser</code>，make 时会调用生成器为 <code>properties.gen</code> 文件中定义的每个属性生成解析器源文件。</p>
<p><code>properties.gen</code> 文件头部有给出属性定义格式示例：</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">##Common templates</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">#</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">#property:CSS_PROP_ENUM IDENT:( INHERIT: IDENT:)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">#property:CSS_PROP_ENUM IDENT:INHERIT NUMBER:( false: RANGE: NUMBER:)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">#property:CSS_PROP_ENUM IDENT:INHERIT LENGTH_UNIT:( UNIT_HZ:PITCH_FREQUENCY ALLOW: DISALLOW: RANGE:&lt;0 LENGTH_UNIT:)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">#property:CSS_PROP_ENUM IDENT:( INHERIT: IDENT:) LENGTH_UNIT:( UNIT_HZ:PITCH_FREQUENCY ALLOW: DISALLOW: RANGE:&lt;0 LENGTH_UNIT:)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">#property:CSS_PROP_ENUM WRAP:</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p>在这种设计中，自定义属性的添加方式应该是先在 <code>properties.gen</code> 添加属性定义，然后运行 make 命令生成解析器源文件，之后重新编译 LibCSS。</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="简写属性解析器">简写属性解析器<a href="https://lcui.org/en/blog/2023/02/01/decoding-libcss-source-code#%E7%AE%80%E5%86%99%E5%B1%9E%E6%80%A7%E8%A7%A3%E6%9E%90%E5%99%A8" class="hash-link" aria-label="Direct link to 简写属性解析器" title="Direct link to 简写属性解析器">​</a></h3>
<blockquote>
<p>简写属性是可以让你同时设置好几个 CSS 属性值的 CSS 属性。使用简写属性，Web 开发人员可以编写更简洁、更具可读性的样式表，节省时间和精力。</p>
<p><strong><em>摘自 <a href="https://developer.mozilla.org/zh-CN/docs/Web/CSS/Shorthand_properties" target="_blank" rel="noopener noreferrer">CSS 的简写属性 - CSS（层叠样式表） | MDN</a></em></strong></p>
</blockquote>
<p>以简写属性 background 为例，它的解析器源文件是 parse/properties/background.c，其中的 <code>css__parse_background()</code> 函数包含了属性的解析过程。</p>
<p>函数开头处定义了几个变量，用于存储非简写属性的值和解析状态（是否需解析）。</p>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">        bool attachment </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> true</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bool color </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> true</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bool image </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> true</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bool position </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> true</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bool repeat </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> true</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        css_style </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> attachment_style</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        css_style </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> color_style</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        css_style </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> image_style</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        css_style </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> position_style</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        css_style </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> repeat_style</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p>首先是处理继承，如果属性值是 <code>inherit</code> 则为每个属性追加带 <code>inherit</code> 标记的字节码。</p>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic">/* Firstly, handle inherit */</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        token </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">parserutils_vector_peek</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">vector</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">*</span><span class="token plain">ctx</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">token </span><span class="token operator" style="color:#393A34">==</span><span class="token plain"> </span><span class="token constant" style="color:#36acaa">NULL</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> CSS_INVALID</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token function" style="color:#d73a49">is_css_inherit</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">c</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> token</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                error </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">css_stylesheet_style_inherit</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">result</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> CSS_PROP_BACKGROUND_ATTACHMENT</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">error </span><span class="token operator" style="color:#393A34">!=</span><span class="token plain"> CSS_OK</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                        </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> error</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token punctuation" style="color:#393A34">.</span><span class="token punctuation" style="color:#393A34">.</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p>给每个属性分配内存资源。</p>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic">/* allocate styles */</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        error </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">css__stylesheet_style_create</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">c</span><span class="token operator" style="color:#393A34">-&gt;</span><span class="token plain">sheet</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&amp;</span><span class="token plain">attachment_style</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">error </span><span class="token operator" style="color:#393A34">!=</span><span class="token plain"> CSS_OK</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> error</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">.</span><span class="token punctuation" style="color:#393A34">.</span><span class="token punctuation" style="color:#393A34">.</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p>遍历每个词法单元，尝试用每个非简写属性的解析函数解析它，若解析成功则标记该属性为不需要再解析。</p>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic">/* Attempt to parse the various longhand properties */</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">do</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                prev_ctx </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">*</span><span class="token plain">ctx</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                error </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> CSS_OK</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token function" style="color:#d73a49">is_css_inherit</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">c</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> token</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                        error </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> CSS_INVALID</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                        </span><span class="token keyword" style="color:#00009f">goto</span><span class="token plain"> css__parse_background_cleanup</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token comment" style="color:#999988;font-style:italic">/* Try each property parser in turn, but only if we</span><br></span><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">                 * haven't already got a value for this property.</span><br></span><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">                 */</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">attachment</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&amp;&amp;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                        </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">error </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">css__parse_background_attachment</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">c</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> vector</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> ctx</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                                                attachment_style</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">==</span><span class="token plain"> CSS_OK</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                        attachment </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> false</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">else</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">color</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&amp;&amp;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">.</span><span class="token punctuation" style="color:#393A34">.</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">while</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token operator" style="color:#393A34">*</span><span class="token plain">ctx </span><span class="token operator" style="color:#393A34">!=</span><span class="token plain"> prev_ctx </span><span class="token operator" style="color:#393A34">&amp;&amp;</span><span class="token plain"> token </span><span class="token operator" style="color:#393A34">!=</span><span class="token plain"> </span><span class="token constant" style="color:#36acaa">NULL</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">attachment</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                error </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">css__stylesheet_style_appendOPV</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">attachment_style</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                                CSS_PROP_BACKGROUND_ATTACHMENT</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                                BACKGROUND_ATTACHMENT_SCROLL</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">error </span><span class="token operator" style="color:#393A34">!=</span><span class="token plain"> CSS_OK</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                        </span><span class="token keyword" style="color:#00009f">goto</span><span class="token plain"> css__parse_background_cleanup</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">.</span><span class="token punctuation" style="color:#393A34">.</span><span class="token punctuation" style="color:#393A34">.</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p>解析完后，给未获得值的属性追加初始值：</p>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">attachment</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                error </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">css__stylesheet_style_appendOPV</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">attachment_style</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                                CSS_PROP_BACKGROUND_ATTACHMENT</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                                BACKGROUND_ATTACHMENT_SCROLL</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">error </span><span class="token operator" style="color:#393A34">!=</span><span class="token plain"> CSS_OK</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                        </span><span class="token keyword" style="color:#00009f">goto</span><span class="token plain"> css__parse_background_cleanup</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">color</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token punctuation" style="color:#393A34">.</span><span class="token punctuation" style="color:#393A34">.</span><span class="token punctuation" style="color:#393A34">.</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p>之后，将每个非简写属性值合并进结果中。</p>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">        error </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">css__stylesheet_merge_style</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">result</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> attachment_style</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">error </span><span class="token operator" style="color:#393A34">!=</span><span class="token plain"> CSS_OK</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token keyword" style="color:#00009f">goto</span><span class="token plain"> css__parse_background_cleanup</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">.</span><span class="token punctuation" style="color:#393A34">.</span><span class="token punctuation" style="color:#393A34">.</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p>最后，销毁相关数据。</p>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token function" style="color:#d73a49">css__stylesheet_style_destroy</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">attachment_style</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">.</span><span class="token punctuation" style="color:#393A34">.</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> error</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p>以上就是 background 属性的解析过程，从中我们可以看出简写属性自身并不占用样式存储空间，它会在解析阶段被分解成若干个非简写属性进行解析和存储，分解的过程就是遍历每个值然后逐个检测是否与哪个非简写属性匹配。</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="计算">计算<a href="https://lcui.org/en/blog/2023/02/01/decoding-libcss-source-code#%E8%AE%A1%E7%AE%97" class="hash-link" aria-label="Direct link to 计�算" title="Direct link to 计算">​</a></h2>
<p><code>css_select_style()</code> 函数实现了样式选择和计算功能，它所解决的问题可分为六类：指示、选择、层叠、内联、初始值、绝对值。值得注意的是，该函数的注释中有提到它生成的已计算样式还不能立即使用，需要调用 <code>css_computed_style_compose()</code> 获取完整的已计算样式，这种两步式样式计算方法旨在允许客户端存储部分已计算样式，并在布局变动时高效地更新节点的完整已计算样式。</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="指示">指示<a href="https://lcui.org/en/blog/2023/02/01/decoding-libcss-source-code#%E6%8C%87%E7%A4%BA" class="hash-link" aria-label="Direct link to 指示" title="Direct link to 指示">​</a></h3>
<p>指示（Hinting）用于让元素对样式做自定义的控制。<code>css_select_style()</code> 函数在初始化选择状态后会调用 <code>node_presentational_hint</code> 回调函数获取元素的展现指示，这些指示的作用相当于元素内部自定义了部分属性的初始值，典型的例子就是 canvas 元素，它给 width 和 height 属性指示的值分别是 300 和 150。</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="选择">选择<a href="https://lcui.org/en/blog/2023/02/01/decoding-libcss-source-code#%E9%80%89%E6%8B%A9" class="hash-link" aria-label="Direct link to 选择" title="Direct link to 选择">​</a></h3>
<p>选择（Selection）是 CSS 引擎的核心功能，其作用是从样式表中查找与元素匹配的规则集。由于选择引擎并不在本文的研究范围内，本文不会继续对它做进一步的讲解，如需了解更多，请自行查阅 LibCSS 代码库中的 src/select/select.c 文件。</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="层叠">层叠<a href="https://lcui.org/en/blog/2023/02/01/decoding-libcss-source-code#%E5%B1%82%E5%8F%A0" class="hash-link" aria-label="Direct link to 层叠" title="Direct link to 层叠">​</a></h3>
<p>层叠（Cascade）的作用是为了从匹配到的规则集中选取权重最高的规则。<code>css_select_style()</code> 在查找到匹配的规则后会调用 <code>cascade_style()</code> 函数层叠样式，其实现代码如下：</p>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">css_error </span><span class="token function" style="color:#d73a49">cascade_style</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> css_style </span><span class="token operator" style="color:#393A34">*</span><span class="token plain">style</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> css_select_state </span><span class="token operator" style="color:#393A34">*</span><span class="token plain">state</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        css_style s </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">*</span><span class="token plain">style</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">while</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">s</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">used </span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token class-name">opcode_t</span><span class="token plain"> op</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                css_error error</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token class-name">css_code_t</span><span class="token plain"> opv </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">*</span><span class="token plain">s</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">bytecode</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token function" style="color:#d73a49">advance_bytecode</span><span class="token punctuation" style="color:#393A34">(</span><span class="token operator" style="color:#393A34">&amp;</span><span class="token plain">s</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">sizeof</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">opv</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                op </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">getOpcode</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">opv</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                error </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> prop_dispatch</span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">op</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">cascade</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">opv</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&amp;</span><span class="token plain">s</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> state</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">error </span><span class="token operator" style="color:#393A34">!=</span><span class="token plain"> CSS_OK</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                        </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> error</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> CSS_OK</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p>从这段简短的代码中我们可以看出，层叠的过程就是遍历字节码然后调用属性调度表中注册的 cascade 函数。以 width 属性为例，它的层叠操作由 select/properties/width.c 中的 <code>css__cascade_width()</code> 函数实现，该函数内部调用了 <code>css__cascade_length_auto()</code> 函数：</p>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">css_error </span><span class="token function" style="color:#d73a49">css__cascade_length_auto</span><span class="token punctuation" style="color:#393A34">(</span><span class="token class-name">uint32_t</span><span class="token plain"> opv</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> css_style </span><span class="token operator" style="color:#393A34">*</span><span class="token plain">style</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                css_select_state </span><span class="token operator" style="color:#393A34">*</span><span class="token plain">state</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token function" style="color:#d73a49">css_error</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token operator" style="color:#393A34">*</span><span class="token plain">fun</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">css_computed_style </span><span class="token operator" style="color:#393A34">*</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token class-name">uint8_t</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> css_fixed</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                                css_unit</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token class-name">uint16_t</span><span class="token plain"> value </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> CSS_BOTTOM_INHERIT</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        css_fixed length </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token class-name">uint32_t</span><span class="token plain"> unit </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> UNIT_PX</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token function" style="color:#d73a49">isInherit</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">opv</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">==</span><span class="token plain"> false</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token keyword" style="color:#00009f">switch</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token function" style="color:#d73a49">getValue</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">opv</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token keyword" style="color:#00009f">case</span><span class="token plain"> BOTTOM_SET</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                        value </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> CSS_BOTTOM_SET</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                        length </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">*</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">css_fixed </span><span class="token operator" style="color:#393A34">*</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> style</span><span class="token operator" style="color:#393A34">-&gt;</span><span class="token plain">bytecode</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                        </span><span class="token function" style="color:#d73a49">advance_bytecode</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">style</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">sizeof</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">length</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                        unit </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">*</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token class-name">uint32_t</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">*</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> style</span><span class="token operator" style="color:#393A34">-&gt;</span><span class="token plain">bytecode</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                        </span><span class="token function" style="color:#d73a49">advance_bytecode</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">style</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">sizeof</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">unit</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                        </span><span class="token keyword" style="color:#00009f">break</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token keyword" style="color:#00009f">case</span><span class="token plain"> BOTTOM_AUTO</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                        value </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> CSS_BOTTOM_AUTO</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                        </span><span class="token keyword" style="color:#00009f">break</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        unit </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">css__to_css_unit</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">unit</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token function" style="color:#d73a49">css__outranks_existing</span><span class="token punctuation" style="color:#393A34">(</span><span class="token function" style="color:#d73a49">getOpcode</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">opv</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">isImportant</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">opv</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> state</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                        </span><span class="token function" style="color:#d73a49">isInherit</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">opv</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">fun</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">state</span><span class="token operator" style="color:#393A34">-&gt;</span><span class="token plain">computed</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> value</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> length</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> unit</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> CSS_OK</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p>当属性值未指定为继承时，对字节码中表达的值进行转换，若当前属性值的权重在已有属性值之上则调用 <code>fun</code> 指向的回调函数来设置值，这个 <code>fun</code> 指针在 <code>css__cascade_width()</code> 中被赋值为 <code>set_width</code>：</p>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">static</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">inline</span><span class="token plain"> css_error </span><span class="token function" style="color:#d73a49">set_width</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">css_computed_style </span><span class="token operator" style="color:#393A34">*</span><span class="token plain">style</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token class-name">uint8_t</span><span class="token plain"> type</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                css_fixed length</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> css_unit unit</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token class-name">uint32_t</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">*</span><span class="token plain">bits</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bits </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&amp;</span><span class="token plain">style</span><span class="token operator" style="color:#393A34">-&gt;</span><span class="token plain">i</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">bits</span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">WIDTH_INDEX</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic">/* 7bits: uuuuutt : unit | type */</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token operator" style="color:#393A34">*</span><span class="token plain">bits </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token operator" style="color:#393A34">*</span><span class="token plain">bits </span><span class="token operator" style="color:#393A34">&amp;</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">~</span><span class="token plain">WIDTH_MASK</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token class-name">uint32_t</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">type </span><span class="token operator" style="color:#393A34">&amp;</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0x3</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">unit </span><span class="token operator" style="color:#393A34">&lt;&lt;</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">2</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                        </span><span class="token operator" style="color:#393A34">&lt;&lt;</span><span class="token plain"> WIDTH_SHIFT</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        style</span><span class="token operator" style="color:#393A34">-&gt;</span><span class="token plain">i</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">width </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> length</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> CSS_OK</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="内联">内联<a href="https://lcui.org/en/blog/2023/02/01/decoding-libcss-source-code#%E5%86%85%E8%81%94" class="hash-link" aria-label="Direct link to 内联" title="Direct link to 内联">​</a></h3>
<p>内联样式（Inline style）的优先级高于选择的样式，它层叠在选择的样式之上，<code>css_select_style()</code> 对它的处理同样是调用 <code>cascade_style()</code> 函数。</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="初始值">初始值<a href="https://lcui.org/en/blog/2023/02/01/decoding-libcss-source-code#%E5%88%9D%E5%A7%8B%E5%80%BC" class="hash-link" aria-label="Direct link to 初始值" title="Direct link to 初始值">​</a></h3>
<p>当样式层叠完后，未设置值的属性会被设置初始值。</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="绝对值">绝对值<a href="https://lcui.org/en/blog/2023/02/01/decoding-libcss-source-code#%E7%BB%9D%E5%AF%B9%E5%80%BC" class="hash-link" aria-label="Direct link to 绝对值" title="Direct link to 绝对值">​</a></h3>
<p>绝对值是可以直接使用的值，除此之外还有相对值。CSS 的部分属性支持使用相对单位的值，例如：<code>font-size: smaller</code>、<code>color: currentColor</code>、<code>line-height: 1.6</code>、<code>padding-left: 1em</code>，这些值并不能直接使用，需要在它们依赖的值都是绝对值时才能确定，因此样式计算过程中会有一个将相对值转换为绝对值的计算过程。</p>
<p>除了 <code>css_select_style()</code> 会在元素为根元素时为其计算绝对值外，我们也可以手动调用 <code>css_computed_style_compose()</code> 函数来完成这一计算。</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="已计算样式">已计算样式<a href="https://lcui.org/en/blog/2023/02/01/decoding-libcss-source-code#%E5%B7%B2%E8%AE%A1%E7%AE%97%E6%A0%B7%E5%BC%8F" class="hash-link" aria-label="Direct link to 已计算样式" title="Direct link to 已计算样式">​</a></h2>
<p>src/select/computed.c 文件的内容可划分为属性访问器、计算函数、内部函数这三个部分。</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="属性访问器">属性访问器<a href="https://lcui.org/en/blog/2023/02/01/decoding-libcss-source-code#%E5%B1%9E%E6%80%A7%E8%AE%BF%E9%97%AE%E5%99%A8" class="hash-link" aria-label="Direct link to 属性访问器" title="Direct link to 属性访问器">​</a></h3>
<p>属性访问器是一系列名称以 <code>css_computed_</code> 开头的函数，用于从已计算样式结构体中获取特定属性的值。它们的返回值类型都为 <code>uint8_t</code>，表示属性值的类型，传入的参数用于接收值，例如：</p>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token class-name">uint8_t</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">css_computed_width</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> css_computed_style </span><span class="token operator" style="color:#393A34">*</span><span class="token plain">style</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                css_fixed </span><span class="token operator" style="color:#393A34">*</span><span class="token plain">length</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> css_unit </span><span class="token operator" style="color:#393A34">*</span><span class="token plain">unit</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">get_width</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">style</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> length</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> unit</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p>对于 left 这种在特定情况下依赖 position 和 right 属性才能计算的属性，它的访问器代码是这样的：</p>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token class-name">uint8_t</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">css_computed_left</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> css_computed_style </span><span class="token operator" style="color:#393A34">*</span><span class="token plain">style</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                css_fixed </span><span class="token operator" style="color:#393A34">*</span><span class="token plain">length</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> css_unit </span><span class="token operator" style="color:#393A34">*</span><span class="token plain">unit</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token class-name">uint8_t</span><span class="token plain"> position </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">css_computed_position</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">style</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token class-name">uint8_t</span><span class="token plain"> left </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">get_left</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">style</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> length</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> unit</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic">/* Fix up, based on computed position */</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">position </span><span class="token operator" style="color:#393A34">==</span><span class="token plain"> CSS_POSITION_STATIC</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token comment" style="color:#999988;font-style:italic">/* Static -&gt; auto */</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                left </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> CSS_LEFT_AUTO</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">else</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">position </span><span class="token operator" style="color:#393A34">==</span><span class="token plain"> CSS_POSITION_RELATIVE</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token comment" style="color:#999988;font-style:italic">/* Relative -&gt; follow $9.4.3 */</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token class-name">uint8_t</span><span class="token plain"> right </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">get_right_bits</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">style</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">left </span><span class="token operator" style="color:#393A34">==</span><span class="token plain"> CSS_LEFT_AUTO </span><span class="token operator" style="color:#393A34">&amp;&amp;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">right </span><span class="token operator" style="color:#393A34">&amp;</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0x3</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">==</span><span class="token plain"> CSS_RIGHT_AUTO</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                        </span><span class="token comment" style="color:#999988;font-style:italic">/* Both auto =&gt; 0px */</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                        </span><span class="token operator" style="color:#393A34">*</span><span class="token plain">length </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                        </span><span class="token operator" style="color:#393A34">*</span><span class="token plain">unit </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> CSS_UNIT_PX</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">else</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">left </span><span class="token operator" style="color:#393A34">==</span><span class="token plain"> CSS_LEFT_AUTO</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                        </span><span class="token comment" style="color:#999988;font-style:italic">/* Left is auto =&gt; -right */</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                        </span><span class="token operator" style="color:#393A34">*</span><span class="token plain">length </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">-</span><span class="token plain">style</span><span class="token operator" style="color:#393A34">-&gt;</span><span class="token plain">i</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">right</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                        </span><span class="token operator" style="color:#393A34">*</span><span class="token plain">unit </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">css_unit</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">right </span><span class="token operator" style="color:#393A34">&gt;&gt;</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">2</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">else</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                        </span><span class="token comment" style="color:#999988;font-style:italic">/** \todo Consider containing block's direction</span><br></span><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">                         * if overconstrained */</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                left </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> CSS_LEFT_SET</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> left</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="计算函数">计算函数<a href="https://lcui.org/en/blog/2023/02/01/decoding-libcss-source-code#%E8%AE%A1%E7%AE%97%E5%87%BD%E6%95%B0" class="hash-link" aria-label="Direct link to 计算函数" title="Direct link to 计算函数">​</a></h3>
<p>计算函数是一系名称以 <code>compute_</code> 开头的函数，它们主要被<code>css__compute_absolute_values()</code> 函数调用，其中大部分用于计算某类属性的绝对值。</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="存储">存储<a href="https://lcui.org/en/blog/2023/02/01/decoding-libcss-source-code#%E5%AD%98%E5%82%A8" class="hash-link" aria-label="Direct link to 存储" title="Direct link to 存储">​</a></h2>
<p>LibCSS 的低内存占用主要体现在样式存储上，CSS 字符串中的样式属性声明经过解析后会转换成内存空间利用率高的字节码，而已计算样式虽然因包含所有属性而占用较大内存，但也做了一些优化，它的头部集中存储了所有属性值的类型数据。</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="字节码的存储格式">字节码的存储格式<a href="https://lcui.org/en/blog/2023/02/01/decoding-libcss-source-code#%E5%AD%97%EF%BF%BD%E8%8A%82%E7%A0%81%E7%9A%84%E5%AD%98%E5%82%A8%E6%A0%BC%E5%BC%8F" class="hash-link" aria-label="Direct link to 字节码的存储格式" title="Direct link to 字节码的存储格式">​</a></h3>
<p>字节码是 <code>unit32_t</code> 类型，表达了 CSS 属性的代码、值、是否重要、是否继承，bytecode.h 中的 <code>buildOPV()</code> 函数定义代码展示了它们是如何组成字节码的：</p>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">static</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">inline</span><span class="token plain"> </span><span class="token class-name">css_code_t</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">buildOPV</span><span class="token punctuation" style="color:#393A34">(</span><span class="token class-name">opcode_t</span><span class="token plain"> opcode</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token class-name">uint8_t</span><span class="token plain"> flags</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token class-name">uint16_t</span><span class="token plain"> value</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">opcode </span><span class="token operator" style="color:#393A34">&amp;</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0x3ff</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">flags </span><span class="token operator" style="color:#393A34">&lt;&lt;</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">10</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">value </span><span class="token operator" style="color:#393A34">&amp;</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0x3fff</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&lt;&lt;</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">18</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p>从中我们可以看出字节码在内存中的布局：</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">| opcode    | flags      | value      |</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">| 1 ~ 11bit | 12 ~ 13bit | 14 ~ 32bit |</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p>对于有多种类型值的属性，它们的 value 字段会被用来表达数据类型，而实际值则存储在追加的数据段中，每个值占用一个字节码的空间，格式如下：</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">| CSS_PROP_XXXX | flags | XXXXX_SET | 实际值1 | 实际值2 | ... |</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">| 32bits                            | 32bits | 32bits  |     |</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p>以 width 属性为例，<code>width: 10px</code> 的字节码格式是：</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">| CSS_PROP_WIDTH | flags | BOTTOM_SET | FIXED (10) | UNIT (px) |</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">| 32bits                              | 32bits     | 32bits    |</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p>而 <code>width: auto</code> 的字节码格式是：</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">| CSS_PROP_WIDTH | flags | BOTTOM_AUTO |</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">| 32bits                               |</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p>test/dump.h 文件展示了如何理解字节码并将其输出为 CSS 代码字符串，我们可以通过查阅它来了解更多的字节码操作方法。</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="已计算样式的存储格式">已计算样式的存储格式<a href="https://lcui.org/en/blog/2023/02/01/decoding-libcss-source-code#%E5%B7%B2%E8%AE%A1%E7%AE%97%E6%A0%B7%E5%BC%8F%E7%9A%84%E5%AD%98%E5%82%A8%E6%A0%BC%E5%BC%8F" class="hash-link" aria-label="Direct link to 已计算样式的存储格式" title="Direct link to 已计算样式的存储格式">​</a></h3>
<p>已计算样式的数据结构定义如下：</p>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">struct</span><span class="token plain"> </span><span class="token class-name">css_computed_style</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">struct</span><span class="token plain"> </span><span class="token class-name">css_computed_style_i</span><span class="token plain"> i</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        css_computed_content_item </span><span class="token operator" style="color:#393A34">*</span><span class="token plain">content</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        css_computed_counter </span><span class="token operator" style="color:#393A34">*</span><span class="token plain">counter_increment</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        css_computed_counter </span><span class="token operator" style="color:#393A34">*</span><span class="token plain">counter_reset</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        lwc_string </span><span class="token operator" style="color:#393A34">*</span><span class="token operator" style="color:#393A34">*</span><span class="token plain">cursor</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        lwc_string </span><span class="token operator" style="color:#393A34">*</span><span class="token operator" style="color:#393A34">*</span><span class="token plain">font_family</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        lwc_string </span><span class="token operator" style="color:#393A34">*</span><span class="token operator" style="color:#393A34">*</span><span class="token plain">quotes</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">struct</span><span class="token plain"> </span><span class="token class-name">css_computed_style</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">*</span><span class="token plain">next</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token class-name">uint32_t</span><span class="token plain"> count</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token class-name">uint32_t</span><span class="token plain"> bin</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p>除去外面的 <code>cursor</code> 和 <code>font_family</code> 等属性外，其它属性值都存储在 <code>css_computed_style_i</code> 结构体中，而其中的 <code>uint32_t bits[15]</code> 成员用于存储这些值的类型信息，以比特位为最小粒度来为值类型分配存储空间，具体分配细节在它的注释中有说明：</p>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">/*</span><br></span><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic"> * Bit allocations:</span><br></span><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic"> * </span><br></span><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic"> * 0  ccccccccoooooooobbbbbbbbrrrrrrrr</span><br></span><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic"> * column_rule_width; outline_width; border_left_width; border_bottom_width</span><br></span><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic"> * </span><br></span><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic"> * 1  vvvvvvvvvbbbbbbbboooooooolllllll</span><br></span><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic"> * vertical_align; border_top_width; border_right_width; letter_spacing</span><br></span><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic"> * </span><br></span><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic"> * 2  ccccccccccccccccccccccccccpppppp</span><br></span><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic"> * clip; padding_top</span><br></span><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic"> * </span><br></span><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic"> * 3  mmmmmmmaaaaaaarrrrrrrttttttddddd</span><br></span><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic"> * max_width; margin_bottom; margin_left; text_indent; display</span><br></span><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic"> */</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p>部分属性的值类型只占一个比特位，例如 opacity 和 windows 属性：</p>
<div class="language-c codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-c codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">/*</span><br></span><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic"> * 14 llcbqopwfrue....................</span><br></span><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic"> * list_style_position; counter_increment; background_image; quotes; order;</span><br></span><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic"> * opacity; widows; flex_grow; orphans; counter_reset; flex_shrink</span><br></span><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic"> */</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p>properties.h 文件中定义了每个属性的值类型枚举，它们的取值范围都很小，因此很容易做到仅占用几个比特位空间。</p>
<p>由于这种占用若干个比特位的数据不便于直接读写，LibCSS 分别在 src/select/autogenerated_propget.h 和 src/select/autogenerated_propset.h 中为每个属性定义了读写辅助函数，它们的实现代码大同小异，都是先确定属性在 bits 中的位置然后通过位运算来读取和写入数据。</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="总结">总结<a href="https://lcui.org/en/blog/2023/02/01/decoding-libcss-source-code#%E6%80%BB%E7%BB%93" class="hash-link" aria-label="Direct link to 总结" title="Direct link to 总结">​</a></h2>
<p>通过上述的分析，笔者得到的 lcui/css 与 LibCSS 的差异如下：</p>
<table><thead><tr><th width="200">差异点</th><th>LibCSS </th><th>lcui/css</th></tr></thead><tbody><tr><td><p>解析功能设计</p></td><td><p>由词法解析器、语法解析器、属性解析器和语言解析器组成</p></td><td><p>由语言解析器、CSS 属性表、CSS 值定义表组成</p></td></tr><tr><td><p>属性声明的解析过程</p></td><td><p>语法解析器触发事件通知语言解析器调用对应的属性解析器进行解析</p></td><td><p>解析器从属性表中获取属性的值定义，然后从字符串中提取与值定义相匹配的值</p></td></tr><tr><td><p>添加自定义属性解析器</p></td><td><p>需要修改和重新编译 LibCSS 的源代码</p></td><td><p>在运行时调用相关函数添加自定义属性</p></td></tr><tr><td><p>样式表</p></td><td><p>样式数据都存在样式表中，提供相关操作函数</p></td><td><p>不存在样式表的概念，加载的样式数据都存储在全局数据表中</p></td></tr><tr><td><p>样式查询范围</p></td><td><p>一个或多个样式表</p></td><td><p>全局</p></td></tr><tr><td><p>样式选择函数的用法</p></td><td><p>传入结点 (node) 及其操作方法集 (handler)</p></td><td><p>传入选择器 (selector)，该参数是组件父子关系链的精简版，每个结点包含组件的 id、type、class 等属性</p></td></tr><tr><td><p>样式选择能力</p></td><td><p>较为全面</p></td><td><p>只支持 id、type、class、<!-- -->:first-child<!-- -->、<!-- -->:last-child<!-- -->，这类简单的选择符</p></td></tr><tr><td><p>样式选择优化</p></td><td><p>为节点提供 bloom 过滤器，根据节点的祖先元素名、类名和 id 名生成，可用于获取和缓存已查询的样式</p></td><td><p>虽然库内部没有做优化，但在 lcui/ui 库中有实现与 LibCSS 的 bloom 过滤器相似的优化</p></td></tr><tr><td><p>样式计算</p></td><td><p>采用两步式样式计算方法，第一步计算继承值、初始值和层叠样式，第二步计算绝对值，这种方法旨在允许客户端存储部分已计算样式，并在布局变动时高效地更新节点的完整已计算样式</p></td><td><p>无</p></td></tr><tr><td><p>属性访问方法</p></td><td><p>调用属性访问函数获取属性值</p></td><td><p>直接访问结构体成员，无相关辅助函数</p></td></tr><tr><td><p>属性值存储方式</p></td><td><p>用若干个比特位记录值的类型和单位，若有其它值则会在另一块空间中存储值，这样能做到让部分属性的值只占用若干个比特位，内存利用率较高</p></td><td><p>属性的类型、值和单位都是独立的结构体成员，每个属性的值至少占用 24 字节，内存利用率低</p></td></tr></tbody></table>
<p>LibCSS 在解析器的设计上明显优于 lcui/css，造成这一差距的原因在于笔者并未学习过编译原理并掌握相关设计模式，正因为如此，lcui/css 所采用的设计模式在经历过数次改进和重写才只达到现在这种程度，虽然看上去变得更成熟稳定了，但与 LibCSS 的设计模式相比只是个低配版。</p>
<p>现在看来，与其浪费大量时间基于最初的自认为很简单的实现方案持续改进，还不如花少量时间学习并应用现有且成熟的设计模式。不过这也体现了理论知识储备的重要性，它能让你快速找到合适的解决方案，不用为了重复解决别人早已解决过的问题而浪费过多时间去研究、实践和改进。</p>
<p>当然，不只是 CSS 解析器这块功能存在问题，由于笔者在开发 CSS 库之前并未系统地学习过 CSS 相关术语和概念，CSS 库的内部接口设计和命名都存在一些问题。</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="瓶颈问题的解决方法">瓶颈问题的解决方法<a href="https://lcui.org/en/blog/2023/02/01/decoding-libcss-source-code#%E7%93%B6%E9%A2%88%E9%97%AE%E9%A2%98%E7%9A%84%E8%A7%A3%E5%86%B3%E6%96%B9%E6%B3%95" class="hash-link" aria-label="Direct link to 瓶颈问题的解决方法" title="Direct link to 瓶颈问题的解决方法">​</a></h3>
<p>如何提取值？笔者在研究 LibCSS 之前想到的解决方案是：先定义一个数组，包含值类型与目标值的指针，然后遍历这个数组找出类型匹配的值并将之写入到目标值的指针指向的空间内。伪代码如下：</p>
<div class="language-ts codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-ts codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">interface</span><span class="token plain"> </span><span class="token class-name">CSSValueResolveRule</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        type</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> CSSValueType</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        key</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token builtin">string</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">resolveCSSValue</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">input</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> CSSValue</span><span class="token punctuation" style="color:#393A34">[</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> rules</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> CSSValueResolveRule</span><span class="token punctuation" style="color:#393A34">[</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> output</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> Record</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token builtin">string</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> CSSValue</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> resolvedValues </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">new</span><span class="token plain"> </span><span class="token class-name builtin">Array</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">input</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">length</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">fill</span><span class="token punctuation" style="color:#393A34">(</span><span class="token boolean" style="color:#36acaa">false</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        ruless</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">forEach</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">rule</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                input</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">some</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">value</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> i</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                        </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token operator" style="color:#393A34">!</span><span class="token plain">resolvedValues</span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">i</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&amp;&amp;</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">matchCSSValueType</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">value</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> rule</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">type</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                                output</span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">rule</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">key</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> value</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                                resolvedValues</span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">i</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">true</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                                </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">true</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                        </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                        </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">false</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> resolvedValues</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">every</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">Boolean</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p>然而一个属性可以有多个值且值可以有多种类型，这种方案并不适用。</p>
<p>如果考虑多值多类型的情况，那就还要解决匹配度的问题，例如简写属性 margin 的四种写法：<code>margin: 4px</code>、<code>margin: 4px 8px</code>、<code>margin: 4px 8px 4px</code>、<code>margin: 4px 8px 4px 8px</code>，同样是 length 类型的值，不同数量有不同的效果。这些值的数量校验在值匹配器中已经做过了，那么，是否可以改进值匹配器使之支持自定义值的提取方式？如果不改进，那在值提取器和属性层叠函数里做重复的值校验有什么意义？一想到这里，笔者发现要解决的问题越来越复杂，而解决这些问题所带来的收益与投入的成本并不匹配，于是决定放弃思考，改为研究 LibCSS 的做法。</p>
<p>经过上述对 LibCSS 的研究，鉴于值提取方式的优化成本过高，笔者决定改用常规的方式来提取值，即便各个简写属性的值提取实现代码是相似的。</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="改进计划">改进计划<a href="https://lcui.org/en/blog/2023/02/01/decoding-libcss-source-code#%E6%94%B9%E8%BF%9B%E8%AE%A1%E5%88%92" class="hash-link" aria-label="Direct link to 改进计划" title="Direct link to 改进计划">​</a></h3>
<p>LibCSS 在解析器、选择引擎和数据结构等方面的设计为 lcui/css 未来的改进提供了可靠的参考，目前可做的改进有：</p>
<ul>
<li>改用字节码组代替链表来存储 CSS 规则</li>
<li>引入样式表、选择上下文、已计算样式等概念</li>
<li>将样式计算函数改成与 <code>css_select_style()</code> 相似的用法</li>
<li>将 lcui/ui 库中的样式选择优化整合进 lcui/css 库中</li>
<li>将 lcui/ui 库中的样式计算逻辑整合进 lcui/css 库中</li>
<li>优化已计算值的存储方式，减少内存占用</li>
<li>增加绝对值计算流程</li>
<li>增加属性访问器</li>
</ul>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="libcss-缺失的属性">LibCSS 缺失的属性<a href="https://lcui.org/en/blog/2023/02/01/decoding-libcss-source-code#libcss-%E7%BC%BA%E5%A4%B1%E7%9A%84%E5%B1%9E%E6%80%A7" class="hash-link" aria-label="Direct link to LibCSS 缺失的属性" title="Direct link to LibCSS 缺失的属性">​</a></h3>
<p>在笔者研究属性解析器的过程中有注意到一些属性没有解析器，甚至连包含相关关键字的代码都没有，例如：</p>
<ul>
<li>background-size</li>
<li>border-radius</li>
<li>box-shadow</li>
</ul>
<p>或许是因为 NetSurf 浏览器还不支持绘制自定义尺寸的背景图、圆角边框和阴影吧。</p>]]></content:encoded>
        </item>
    </channel>
</rss>