LFC 显示树¶
布局格式化上下文显示树及相关功能(也称为“LFC 显示”)。
基本概念¶
显示树旨在成为一个完全解析的、独立的树,适用于绘制和命中测试。
完全解析意味着所有样式值都已解析为用于绘制的值。例如,显示树中的颜色是应用访问链接规则和应用 -apple-color-filter
后的结果。
独立意味着显示树是一个独立的数据结构,可以在不参考与布局共享的数据结构的情况下进行绘制。例如,显示树一旦构建完成,就不再使用 RenderStyle
,而是在树构建时构建自己的显示样式。
显示树中的所有几何结构都已处于绘制坐标中:像素对齐在树构建时发生。
总的来说,该树的设计目标是将尽可能多的复杂性推到树构建阶段,以便快速绘制。
树对象本身是特意设计为轻量级的,主要为纯数据对象。复杂的树遍历代码存在于知道如何执行某些操作(如绘制盒子树)的外部对象中。
显示树中的盒子旨在是相当通用的,不具有非常特定于 CSS 或 SVG 的属性。希望 SVG 和 CSS 渲染能共享许多相同的盒子类。
Layout::Box
和 Display::Box
之间不一定存在一对一的关系。显示树可能会省略不绘制任何内容的盒子,或者为滚动等功能创建额外的盒子。
相关类¶
Display::View¶
表示显示树的呈现。是命中测试的入口点。
目前 FrameView
拥有一个 Display::View
。
Display::Tree¶
通过 Display::StackingItem
对象拥有 Display::Box
对象的层级结构,以及相关的树数据结构。Display::Tree
应该能够独立于 Display::View
存在。
Display::TreeBuilder¶
Display::TreeBuilder
是一个生命周期短暂的类,它知道如何从 Layout::Box
树、LayoutState
和样式构建 Display::StackingItem
/Display::Box
树。在构建显示树时,它会在栈中维护各种状态位,以跟踪包含块等。Display::TreeBuilder
了解层级结构,而不了解几何结构。
Display::BoxFactory¶
Display::BoxFactory
实际创建 Display::Box
对象及其相关对象,如 Display::BoxGeometry
。它的存在是为了将样式和几何代码与 Display::TreeBuilder
分离。它一次只处理一个盒子。
Display::Box¶
Display::Box
是显示树的基本构建块;它表示某个矩形区域,通常带有绘制的内容。有用于文本、图像等的子类。Display::Box
具有兄弟指针和父指针。Display::ContainerBox
用于带有子级的盒子,因此具有指向其第一个子级的指针。
Display::Box
对象的层级结构形成一棵树,但它不是一棵完整的树,因为每个 Display::StackingItem
拥有树的一部分,并且这些子树中的盒子对象并未连接。
Display::StackingItem¶
此类的存在是为了表示盒子树中按照 CSS 2.2 附录 E 规定原子性绘制的部分。根部总会有一个 Display::StackingItem
(Display::Tree
通过它拥有实际的盒子树),以及导致 CSS 堆叠上下文的 CSS 定位和其他样式会触发 Display::StackingItem
的创建。表示 CSS 堆叠上下文的 Display::StackingItem
对象具有包含指向后代 Display::StackingItem
对象的拥有引用的正 z 序列表和负 z 序列表。这些列表在树构建时构建和排序。
Display::Style¶
Display::Style
是显示树中与 RenderStyle
等价的类,但有一些重要的区别。每个 Display::Box
都有一个 Display::Style
,因此它应该相对较小。它必须只包含独立于盒子几何结构的数据;即,它不能包含任何 Length
值(这些值可能隐藏像 calc()
这样必须已解析的值)。它应该可以在具有相同外观但可能尺寸不同的盒子之间共享。所有颜色都应是最终使用的值,即在解析 visitedDependentColor()
并应用 -apple-color-filter
之后的值。
Display::BoxRareGeometry / Display::BoxDecorationData¶
这些类存储特定于单个 Display::Box
的依赖于几何结构的数据,并且只为具有相关样式(如可见边框或边框半径)的盒子分配。
Display::BoxDecorationData
存储有关背景和边框的信息。
Display::BoxRareGeometry
存储边框半径(此处是因为即使没有边框,具有 overflow:hidden 的盒子也会根据其边框半径进行裁剪),以及变换。
Display::CSSPainter¶
Display::CSSPainter
知道如何遍历 Display::StackingItem
及其 Display::Box
对象的树,以实现 CSS 绘制。它知道如何应用裁剪、不透明度和滤镜。
为了绘制单个盒子,Display::CSSPainter
利用 Display::BoxDecorationPainter
,后者知道如何绘制 CSS 边框和背景。
Display::CSSPainter
对象仅在绘制期间存在于栈上;它们不是长期存在的。
预计 SVG 将在某个时候由 Display::SVGPainter
类绘制。
命中测试与绘制非常相似,因此该代码也存在于 Display::CSSPainter
中。也许它需要一个不同的名称。
绘制¶
绘制显示树某个子集的入口点是 GraphicsLayerClient
的 paintContents()
绘制回调。最终,显示树将通过 GraphicsLayers 的层级结构呈现;目前,根部只有一个 GraphicsLayer。
为了绘制树,调用静态函数 CSSPainter::paintTree()
,然后 Display::CSSPainter
遍历 Display::StackingItem
层级并绘制其 Display::Box
对象,同时对带有盒子装饰的盒子使用 Display::BoxDecorationPainter
。
在 CSS/SVG 边界处,将在栈上创建一个新的绘制器对象来绘制内部内容。
目标 GraphicsContext
作为参数传递给绘制函数,并封装在一个 PaintingContext
结构体中,该结构体还包含 deviceScaleFactor
,并且可能包含一个脏矩形。
几何¶
Note: this is subject to change. The code needs to make it hard to use the absolute rects in the wrong way.
显示盒子将其位置和大小存储为绝对(即文档相对)FloatRect
(已进行像素对齐)。CSS 盒模型盒子(具有边框和边框半径等)也存储其内边距和内容盒的矩形。因此在绘制期间无需跟踪任何绘制偏移量。
一些盒子充当坐标边界;即那些具有变换的盒子,以及受滚动影响的盒子。"绝对"矩形是在考虑滚动和变换之前计算的,因此需要将盒子的矩形映射到视图坐标的代码需要考虑这些因素。
裁剪¶
裁剪是显示树中的复杂性来源,因为 CSS 裁剪不会触发 CSS 堆叠上下文;裁剪树遵循包含块树,这与绘制顺序不同。对于绘制正常的流内内容,我们可以在绘制时直接应用裁剪,但绘制流外内容时,我们必须能够从祖先计算裁剪。此类流外内容始终由 Display::StackingItem
的根盒子表示。
在树构建时,Display::TreeBuilder
会跟踪包含块,因此对于启动乱序绘制的盒子(即作为 Display::StackingItem
根盒子的那些),Display::BoxFactory
可以要求包含块提供应应用的裁剪。
裁剪存储在 Display::BoxClip
中,它跟踪一个交集矩形,并且在必要时,跟踪一组用于应用来自祖先的边框半径裁剪的圆角矩形。Display::BoxClip
对象在共享相同裁剪(即没有自身溢出)的父/子 Display::BoxModelObject
对象之间共享。
滚动¶
Note: this is preliminary
具有 overflow:auto
或 overflow:scroll
的 Layout::Box
以三层显示盒子层级表示
container box
scrolling container box
scrolled contents box
(可能还有用于滚动条和滚动角的额外盒子)。
容器盒子用于在滚动元素上绘制盒子装饰。“滚动容器”盒子具有容器内容盒子的几何结构,并用于表示滚动偏移量。“滚动内容”盒子绘制滚动内容,并具有布局溢出的尺寸。
显示树的目标是滚动不应使子树上的任何几何结构失效(从而避免滚动后遍历树以修复几何结构,这种遍历已知是渲染代码中性能问题的来源)。
Note: explain how scrolling interacts with positioned elements.
合成¶
Note: write me