This directory contains the implementation of Blink's new layout engine “LayoutNG”.
This document can be viewed in formatted form here.
The original design document can be seen here.
CSS has many different types of layout modes, controlled by the display
property. (In addition to this specific HTML elements have custom layout modes as well). For each different type of layout, we have a LayoutAlgorithm.
The input to an LayoutAlgorithm is the same tuple for every kind of layout:
The BlockNode which we are currently performing layout for. The following information is accessed:
The ComputedStyle for the node which we are currently performing laying for.
The list of children BlockNodees to perform layout upon, and their respective style objects.
The ConstraintSpace which represents the “space” in which the current layout should produce a PhysicalFragment.
TODO(layout-dev): BreakTokens should go here once implemented.
The current layout should not access any information outside this set, this will break invariants in the system. (As a concrete example we intend to cache PhysicalFragments based on this set, accessing additional information outside this set will break caching behaviour).
TODO(layout-dev): Document with lots of pretty pictures.
Please refer to the inline layout README.
TODO(layout-dev): Document with lots of pretty pictures.
All coordinates and sizes associated with an PhysicalFragment are physical, i.e. pure left/top offsets from the parent fragment, and sizes are expressed with widths and heights (not inline-size / block-size). No logical offsets or sizes. Writing mode and direction are resolved during layout.
TODO(layout-dev): Document with lots of pretty pictures.
Please refer to the block flow layout docs.
TODO(layout-dev): Document with lots of pretty pictures.
TODO(layout-dev): Document with lots of pretty pictures.
After layout, we cache the resulting fragment to avoid redoing all of layout the next time. You can read the full design document.
Here's how it works:
LayoutBlockFlow::SetCachedLayoutResult
from BlockNode::Layout
.BlockNode::Layout
checks at the beginning if we already have a cached result by calling LayoutBlockFlow::CachedLayoutResult
. If that returns a layout result, we return it and are done.CachedLayoutResult
will always clone the fragment (but without the offset) so that the parent algorithm can position this fragment.operator==
), if there was no break token (a current limitation, we may lift this eventually), and if the node is not marked for layout. We need the NeedsLayout()
check because we currently have no other way to ensure that relayout happens when style or children change. Eventually we need to rethink this part as we transition away from legacy layout.Design doc here.
Tutorial here.
We‘ll paint and hit-test by traversing the physical fragment tree, rather than traversing the LayoutObject
tree. This is important for block fragmentation, where a CSS layout box (LayoutObject
) may be split into multiple fragments, and it’s the relationship between the fragments (not the layout objects) that determines the offsets. In LayoutNG, there are also fragments that have no corresponding layout object - e.g. a column (or other types of fragmentainers too).
Traditionally, when doing block fragmentation (multicol) in legacy layout, we had to perform some complicated calculations, where we mapped and sliced layout objects into fragments during pre-paint. In LayoutNG this job is now as a natural part of layout. So, all we have to do for painting and hit-testing, is traverse the fragments. A fragment holds a list of child fragments and their offsets. The offsets are relative to the parent fragment. As such, it's a rather straight-forward job for pre-paint to calculate the offsets and bounding box.
The latest code coverage (from Feb 14 2017) can be found here. Here is the instruction how to generate a new result.
git clone https://github.com/DynamoRIO/dynamorio.git
npm install vinyl, npm install vinyl-fs
git clone https://github.com/mweibel/lcov-result-merger
chromium\src> ninja -C out\Debug blink_unittests
chromium\src>DynamoRIO\bin64\drrun.exe -t drcov -- .\out\Debug\blink_unittests.exe --gtest_filter=NG*
chromium\src>for %file in (*.log) do DynamoRIO\tools\bin64\drcov2lcov.exe -input %file -output %file.info -src_filter layout/ng -src_skip_filter _test
chromium\src>node lcov-result-merger\bin\lcov-result-merger.js *.info output.info
chromium\src>C:\Perl64\bin\perl.exe dynamorio.git\third_party\lcov\genhtml output.info -o output
Both layout input node subtrees and layout output physical fragment subtrees may be dumped, for debugging, logging and testing purposes.
Call LayoutInputNode::ShowNodeTree() to dump the tree to stderr.
Call PhysicalFragment::ShowFragmentTree() to dump the tree to stderr. Fragments in the subtree are not required to be marked as placed (i.e. know their offset).
A fragment tree may also be dumped to a String, by calling PhysicalFragment::DumpFragmentTree(). It takes a flag parameter, so that the output can be customized to only contain what's relevant for a given purpose.