Chromium Code Reviews| Index: third_party/WebKit/Source/core/layout/ng/BlockLayout.md |
| diff --git a/third_party/WebKit/Source/core/layout/ng/BlockLayout.md b/third_party/WebKit/Source/core/layout/ng/BlockLayout.md |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..72e306344c2d214877111d76c06fb68136216202 |
| --- /dev/null |
| +++ b/third_party/WebKit/Source/core/layout/ng/BlockLayout.md |
| @@ -0,0 +1,137 @@ |
| +# Block Layout # |
| + |
| +This document can be viewed in formatted form [here](https://chromium.googlesource.com/chromium/src/+/master/third_party/WebKit/Source/core/layout/ng/FlowLayout.md). |
|
atotic
2017/06/10 00:26:51
s/FlowLayout/BlockLayout
ikilpatrick
2017/06/12 22:58:39
Done.
|
| + |
| +## Floats ## |
| + |
| +TODO. |
| + |
| +## An introduction to margin collapsing ## |
| + |
| +A simple way to think about margin collapsing is that it takes the maximum margin between two |
| +elements. For example: |
| + |
| +```html |
| +<!-- The divs below are 20px apart --> |
| +<div style="margin-bottom: 10px;">Hi</div> |
| +<div style="margin-top: 20px;">there</div> |
| +``` |
| + |
| +This is complicated by _negative_ margins. For example: |
| + |
| +```html |
| +<!-- The divs below are 10px apart --> |
| +<div style="margin-bottom: 20px;">Hi</div> |
| +<div style="margin-top: -10px;">there</div> |
| + |
| +<!-- The divs below are -20px apart --> |
| +<div style="margin-bottom: -20px;">Hi</div> |
| +<div style="margin-top: -10px;">there</div> |
| +``` |
| + |
| +The rule here is: `max(pos_margins) + min(neg_margins)`. This rule we'll refer to as the _margin |
| +collapsing rule_. If this only happened between top level elements it would be pretty simple, |
| +however consider the following: |
| + |
| +```html |
| +<!-- The top-level divs below are 10px apart --> |
|
atotic
2017/06/10 00:26:51
It'd be clearer if there was only 1 way to get mat
ikilpatrick
2017/06/12 22:58:38
Done.
|
| +<div style="margin-bottom: 10px"> |
| + <div style="margin-bottom: -20px"> |
| + <div style="margin-bottom: 20px">Hi</div> |
| + </div> |
| +</div> |
| +<div style="margin-top: 30px"> |
| + <div style="margin-top: -10px">there</div> |
| +</div> |
| +``` |
| + |
| +In the above example as there isn't **anything** separating the edges of two fragments the margins |
|
dgrogan
2017/06/12 18:54:40
Could you add a parenthetical after **anything** t
ikilpatrick
2017/06/12 22:58:39
Done.
|
| +stack together. There are known as **adjoining margins**. If we apply our formula to the above we |
| +get: `max(10, 20, 30) + min(-20, -10) = 10`. |
| + |
| +A useful concept is a **margin strut**. This is a pair of margins consisting of one positive and one |
| +negative margin. |
| + |
| +A margin strut allows us to keep track of the largest positive and smallest negative margin. E.g. |
| +```cpp |
| +struct MarginStrut { |
| + LayoutUnit pos_margin; |
| + LayoutUnit neg_margin; |
| + |
| + void Append(LayoutUnit margin) { |
| + if (margin < 0) |
| + neg_margin = std::min(margin, neg_margin); |
| + else |
| + pos_margin = std::max(margin, pos_margin); |
| + } |
| + |
| + LayoutUnit Sum() { return pos_margin + neg_margin; } |
| +} |
| +``` |
| + |
| +A naïve algorithm for the above case would be to _bubble_ up margins. For example each fragment |
|
dgrogan
2017/06/12 18:54:40
Which "above case" do you mean? I suspect the adjo
ikilpatrick
2017/06/12 22:58:38
Done.
|
| +would have a **margin strut** at the block-start and block-end edge. If the child fragment was |
| +**adjoining** to its parent, you simply keep track of the margins by calling `Append` on the margin |
|
dgrogan
2017/06/12 18:54:40
Could you say which of the 4 margin struts (block-
ikilpatrick
2017/06/12 22:58:38
Done? - added example code for bubbling. PTAL
|
| +strut. |
| + |
| +When it comes time to collapse the margins you can use the margin collapsing rule, e.g. |
| +```cpp |
| +MarginStrut s1 = fragment1.block_end_margin_strut; |
| +MarginStrut s2 = fragment2.block_start_margin_strut; |
| +LayoutUnit distance = |
| + std::max(s1.pos_margin, s2.pos_margin) + |
| + std::min(s1.neg_margin, s2.neg_margin); |
| +``` |
| + |
| +This would be pretty simple - however it doesn't work. As we discussed in the floats section a |
| +_child_ will position _itself_ within the BFC. If we did margin collapsing this way way we'd create |
|
dgrogan
2017/06/12 18:54:40
"way way"
ikilpatrick
2017/06/12 22:58:39
Done.
|
| +a circular dependency between layout and positioning. E.g. we need to perform layout in order to |
| +determine the block-start margin strut, which would allow us to position the fragment, which would |
| +allow us to perform layout. |
| + |
| +We **invert** the problem. A fragment now only produces an _end_ margin strut. The _start_ margin |
| +strut becomes an input as well as where the margin strut is currently positioned within the BFC. |
| +For example: |
| + |
| +```cpp |
| +Fragment* Layout(LogicalOffset bfc_estimate, MarginStrut input_strut) { |
|
kojii
2017/06/10 05:12:13
Is "bfc_estimate" the "offset to BFC without the l
ikilpatrick
2017/06/12 22:58:38
Thats correct, another way of thinking about this
|
| + MarginStrut curr_strut = input_strut; |
| + LogicalOffset curr_bfc_estimate = bfc_estimate; |
| + |
| + // We collapse the margin strut which allows us to compute our BFC offset if |
| + // we have border or padding. I.e. we don't have an adjoining margin. |
| + if (border_padding.block_start) { |
| + curr_bfc_estimate += curr_strut.Sum(); |
| + curr_strut = MarginStrut(); |
| + |
| + fragment_builder.SetBfcOffset(curr_bfc_estimate); |
| + curr_bfc_estimate += border_padding.block_start; |
| + } |
| + |
|
kojii
2017/06/10 05:12:13
IIUC, up until here is common to all algorithms, a
ikilpatrick
2017/06/12 22:58:38
This is only for block and inline, but yeah.
|
| + for (const auto& child : children) { |
| + curr_strut.Append(child.margins.block_start); |
| + const auto* fragment = child.Layout(curr_strut); |
|
kojii
2017/06/10 05:12:13
child.Layout(curr_bfc_estimate, curr_strut) ?
ikilpatrick
2017/06/12 22:58:38
Done.
|
| + |
| + curr_strut = fragment->end_margin_strut; |
| + curr_strut.Append(child.margins.block_end); |
| + |
| + curr_bfc_estimate = fragment->BfcOffset() + fragment->BlockSize(); |
| + } |
| + |
| + fragment_builder.SetEndMarginStrut(curr_strut); |
| + |
| + return fragment_builder.ToFragment(); |
| +} |
| +``` |
| + |
| +It isn't immediately obvious that this works, but if you try and work through an example manually, |
| +it'll become clearer. |
| + |
| +There are lots of different things which can "resolve" the BFC offset of an element. For example |
| +inline content (text, atomic inlines), border and padding, if a child _might_ be affected by |
| +clearance. |
| + |
| +## Zero block-size fragments ## |
| + |
| +TODO. |
| + |