This document describes the Layout Formatting Context display tree and related functionality (aka “LFC Display”).
The display tree is intended to be a fully-resolved, isolated tree that is suitable for painting and hit-testing.
Fully-resolved means that all style values have been resolved to the values use for painting. For example, colors in the display tree are those which result from the application of visited link rules, and from the application of -apple-color-filter
.
Isolated means that a display tree is a stand-alone data structure that can be painted without reference to data structures share with layout. For example, the display tree does not use RenderStyle
once built, but constructs its own display styles at tree-building time.
All the geometry in the display tree is already in painting coordinates: pixel snapping happens at tree building time.
In general the tree has been designed to push as much of the complexity to tree-building time as possible, so that painting is fast.
The tree objects themselves are intentionally lightweight, mostly data-only objects. Complex tree-walking code lives in external objects that know how to do certain things, like paint the box tree.
Boxes in the display tree are intended to be fairly generic, and not have properties which are very CSS- or SVG-specific. It's hoped that SVG and CSS rendering share many of the same box classes.
There isn't necessarily a 1:1 relationship between Layout::Box
and Display::Box
. The display tree may elide boxes that paint nothing, or may create extra boxes for functionality like scrolling.
Represents a presentation of a display tree. Is the entry point for hit-testing.
Currently FrameView
owns a Display::View
.
Owns the hierarchy of Display::Box
objects via Display::StackingItem
objects, and associated tree-related data structures. It should be possible for a Display::Tree
to exist independently of a Display::View
.
Display::TreeBuilder
is a short-lived class that knows how to build a Display::StackingItem
/Display::Box
tree from a Layout::Box
tree, LayoutState
and style. It maintains various bits of state in a stack as it's building the display tree in order to track containing blocks etc. Display::TreeBuilder
knows about hierarchy, not geometry.
Display::BoxFactory
actually creates the Display::Box
objects and their related objects like Display::BoxGeometry
. It exists to keep the style and geometry code out of Display::TreeBuilder
. It only ever deals with a single box at a time.
Display::Box
is the basic building block for the display tree; it represents some rectangular region, often with painted content. There are subclasses for text, images etc. Display::Box
has sibling pointers and a parent pointer. Display::ContainerBox
is used for boxes with children, so has a pointer to its first child.
The hierarchy of Display::Box
objects forms a tree, but it's not a complete tree, because each Display::StackingItem
owns a part of the tree, and the box objects in those subtrees are not connected.
This class exists to represent parts of the box tree that are painted atomically, as specified in CSS 2.2 Appendix E. There is always a Display::StackingItem
at the root (via which the Display::Tree
owns the actual box tree), and CSS positioning and other styles which cause CSS stacking context trigger the creation of a Display::StackingItem
. Display::StackingItem
objects that represent CSS stacking contexts have positive and negative z-order lists which contain owning references to the descendant Display::StackingItem
objects. These lists are built and sorted at tree-building time.
Display::Style
is the display tree's equivalent of RenderStyle
but with some important differences. Every Display::Box
has a Display::Style
, so it should be relatively small. It must only contain data that is independent of box geometry; i.e. it must not contain any Length
values (which can hide things like calc()
values that must have been already resolved). It should be shareable between boxes with the same appearance, but possibly different sizes. All colors should be their final used values, i.e. after resolving visitedDependentColor()
and applying -apple-color-filter
.
These classes store geometry-dependent data specific to a single Display::Box
, and are only allocated for boxes that have the relevant styles (like visible borders, or border-radius).
Display::BoxDecorationData
stores information about backgrounds and borders.
Display::BoxRareGeometry
stores border-radius (here because a box with overflow:hidden clips to its border-radius even without any border), and transforms.
Display::CSSPainter
knows how to walk the trees of Display::StackingItem
and their Display::Box
objects in order to implement CSS painting. It knows how to apply clipping, opacity and filters.
For painting individual boxes, Display::CSSPainter
makes use of Display::BoxDecorationPainter
which knows how to paint CSS borders and backgrounds.
Display::CSSPainter
objects exist on the stack only during painting; they are not long-lived.
It's intended that SVG would be painted by a Display::SVGPainter
class at some point.
Hit-testing is very similar to painting, so that code lives in Display::CSSPainter
too. Maybe it needs a different name.
The entry point to painting some subset of the display tree is a GraphicsLayerClient
's paintContents()
paint callback. Eventually the display tree will be presented via a hierarchy of GraphicsLayers; for now, there is a single GraphicsLayer at the root.
To paint the tree, the static CSSPainter::paintTree()
function is called, and Display::CSSPainter
traverses the Display::StackingItem
hierarchy and paints their Display::Box
objects, making use of Display::BoxDecorationPainter
on boxes with box decorations.
At CSS/SVG boundaries a new painter object will be created on the stack to paint the inner content.
The destination GraphicsContext
is passed around as an argument to painting functions, wrapped up in a PaintingContext
struct that also has the deviceScaleFactor
and will probably include a dirty rect.
Note: this is subject to change. The code needs to make it hard to use the absolute rects in the wrong way.
Display boxes store their position and size as an absolute (i.e. document-relative) FloatRect
(already pixel-snapped). CSS box-model boxes, which have things like borders and border-radius, also store rects for their padding and content boxes. Thus no paint offsets have to be tracked during painting.
Some boxes act as coordinate boundaries; namely those with transforms, and those which are affected by scrolling. The “absolute” rects are computed before accounting for scrolling and transforms, so code that needs to map a box's rect into view coordinate will need to take those into account.
Clipping is a source of complexity in the display tree because CSS clipping does not trigger CSS stacking context; the clipping tree follows the containing block tree, which is different from paint order. For painting normal in-flow content we can just apply clipping at paint time, but when painting out-of-flow content, we have to be able to compute the clip from ancestors. Such out-of-flow content is always represented by the root box of a Display::StackingItem
.
At tree-building time Display::TreeBuilder
keeps track of containing blocks, so for boxes which initiate out-of-order painting (i.e. those which are the root box of a Display::StackingItem
) Display::BoxFactory
can ask the containing block for the clip that should be applied.
Clips are store in Display::BoxClip
which tracks an intersection rect, and, if necessary, a set of rounded rects needed to apply border-radius clips from ancestors. Display::BoxClip
objects are shared between parent/child Display::BoxModelObject
objects that share the same clip (i.e. have no overflow of their own).
Note: this is preliminary
A Layout::Box
with overflow:auto
or overflow:scroll
is represented with a hierarchy of three display boxes:
container box scrolling container box scrolled contents box
(and possibly additional boxes for scrollbars and the scroll corner).
The container box exists to paint box decorations on the scrolling element. The “scrolling container” box has geometry of the container's content box, and exists to represent the scroll offset. The “scrolled contents” box paints the scrolled content, and has the size of the layout overflow.
It is a goal of the display tree that scrolling should not invalidate any geometry on the subtree (thus avoiding post-scroll tree walks to fix up geometry, which are known to be a source of performance issues in rendering code).
Note: explain how scrolling interacts with positioned elements.
Note: write me