Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(350)

Side by Side Diff: third_party/WebKit/Source/core/layout/ng/ng_block_layout_algorithm.cc

Issue 2655783006: Top down version of algorithm to position margins and floats in LayoutNG (Closed)
Patch Set: fix comments Created 3 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2016 The Chromium Authors. All rights reserved. 1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "core/layout/ng/ng_block_layout_algorithm.h" 5 #include "core/layout/ng/ng_block_layout_algorithm.h"
6 6
7 #include "core/layout/ng/ng_absolute_utils.h" 7 #include "core/layout/ng/ng_absolute_utils.h"
8 #include "core/layout/ng/ng_block_break_token.h" 8 #include "core/layout/ng/ng_block_break_token.h"
9 #include "core/layout/ng/ng_box_fragment.h" 9 #include "core/layout/ng/ng_box_fragment.h"
10 #include "core/layout/ng/ng_column_mapper.h" 10 #include "core/layout/ng/ng_column_mapper.h"
11 #include "core/layout/ng/ng_constraint_space.h" 11 #include "core/layout/ng/ng_constraint_space.h"
12 #include "core/layout/ng/ng_constraint_space_builder.h" 12 #include "core/layout/ng/ng_constraint_space_builder.h"
13 #include "core/layout/ng/ng_fragment.h" 13 #include "core/layout/ng/ng_fragment.h"
14 #include "core/layout/ng/ng_fragment_builder.h" 14 #include "core/layout/ng/ng_fragment_builder.h"
15 #include "core/layout/ng/ng_layout_opportunity_iterator.h" 15 #include "core/layout/ng/ng_layout_opportunity_iterator.h"
16 #include "core/layout/ng/ng_length_utils.h" 16 #include "core/layout/ng/ng_length_utils.h"
17 #include "core/layout/ng/ng_out_of_flow_layout_part.h" 17 #include "core/layout/ng/ng_out_of_flow_layout_part.h"
18 #include "core/layout/ng/ng_units.h" 18 #include "core/layout/ng/ng_units.h"
19 #include "core/style/ComputedStyle.h" 19 #include "core/style/ComputedStyle.h"
20 #include "platform/LengthFunctions.h" 20 #include "platform/LengthFunctions.h"
21 #include "wtf/Optional.h" 21 #include "wtf/Optional.h"
22 22
23 namespace blink { 23 namespace blink {
24 namespace { 24 namespace {
25 25
26 // Adjusts content's offset to CSS "clear" property. 26 // Adjusts content_size to CSS "clear" property.
ikilpatrick 2017/01/30 22:26:48 Adjusts content_size to respect the CSS "clear" pr
Gleb Lanbin 2017/01/31 00:25:02 Done.
27 // TODO(glebl): Support margin collapsing edge cases, e.g. margin collapsing 27 // Picks up the maximum between left/right exclusions and content_size depending
28 // should not occur if "clear" is applied to non-floating blocks. 28 // on the value of style.clear() property.
29 // TODO(layout-ng): the call to AdjustToClearance should be moved to 29 void AdjustToClearance(const std::shared_ptr<NGExclusions>& exclusions,
30 // CreateConstraintSpaceForChild once ConstraintSpaceBuilder is sharing the
31 // exclusion information between constraint spaces.
32 void AdjustToClearance(const NGConstraintSpace& space,
33 const ComputedStyle& style, 30 const ComputedStyle& style,
31 const NGLogicalOffset& from_offset,
34 LayoutUnit* content_size) { 32 LayoutUnit* content_size) {
35 const NGExclusion* right_exclusion = space.Exclusions()->last_right_float; 33 DCHECK(content_size) << "content_size cannot be null here";
36 const NGExclusion* left_exclusion = space.Exclusions()->last_left_float; 34 const NGExclusion* right_exclusion = exclusions->last_right_float;
35 const NGExclusion* left_exclusion = exclusions->last_left_float;
37 36
38 // Calculates Left/Right block end offset from left/right float exclusions or 37 // Exclusions(stored in BFC coordinates), content_size can be negative or 0
ikilpatrick 2017/01/30 22:26:48 space between "s("
ikilpatrick 2017/01/30 22:26:49 consider re-wording this line, not sure what it is
Gleb Lanbin 2017/01/31 00:25:02 I also found it's confusing. I tried to rephrase i
39 // use the default content offset position. 38 // If left or right exclusion are available then convert them to the relative
40 LayoutUnit left_block_end_offset = 39 // offset(exclusion.BlockEnd - from_offset) and compare them with content_size
ikilpatrick 2017/01/30 22:26:48 .nit period.
Gleb Lanbin 2017/01/31 00:25:02 Done.
41 left_exclusion ? left_exclusion->rect.BlockEndOffset() : *content_size; 40 LayoutUnit left_block_end_offset = *content_size;
42 LayoutUnit right_block_end_offset = 41 if (left_exclusion) {
43 right_exclusion ? right_exclusion->rect.BlockEndOffset() : *content_size; 42 left_block_end_offset = std::max(
43 left_exclusion->rect.BlockEndOffset() - from_offset.block_offset,
44 *content_size);
45 }
46 LayoutUnit right_block_end_offset = *content_size;
47 if (right_exclusion) {
48 right_block_end_offset = std::max(
49 right_exclusion->rect.BlockEndOffset() - from_offset.block_offset,
50 *content_size);
51 }
44 52
45 switch (style.clear()) { 53 switch (style.clear()) {
46 case EClear::ClearNone: 54 case EClear::ClearNone:
47 return; // nothing to do here. 55 return; // nothing to do here.
48 case EClear::ClearLeft: 56 case EClear::ClearLeft:
49 *content_size = left_block_end_offset; 57 *content_size = left_block_end_offset;
50 break; 58 break;
51 case EClear::ClearRight: 59 case EClear::ClearRight:
52 *content_size = right_block_end_offset; 60 *content_size = right_block_end_offset;
53 break; 61 break;
54 case EClear::ClearBoth: 62 case EClear::ClearBoth:
55 *content_size = std::max(left_block_end_offset, right_block_end_offset); 63 *content_size = std::max(left_block_end_offset, right_block_end_offset);
56 break; 64 break;
57 default: 65 default:
58 ASSERT_NOT_REACHED(); 66 ASSERT_NOT_REACHED();
59 } 67 }
60 } 68 }
61 69
62 LayoutUnit ComputeCollapsedMarginBlockStart(
63 const NGDeprecatedMarginStrut& prev_margin_strut,
64 const NGDeprecatedMarginStrut& curr_margin_strut) {
65 return std::max(prev_margin_strut.margin_block_end,
66 curr_margin_strut.margin_block_start) -
67 std::max(prev_margin_strut.negative_margin_block_end.abs(),
68 curr_margin_strut.negative_margin_block_start.abs());
69 }
70
71 // Creates an exclusion from the fragment that will be placed in the provided 70 // Creates an exclusion from the fragment that will be placed in the provided
72 // layout opportunity. 71 // layout opportunity.
73 NGExclusion CreateExclusion(const NGFragment& fragment, 72 NGExclusion CreateExclusion(const NGFragment& fragment,
74 const NGLayoutOpportunity& opportunity, 73 const NGLayoutOpportunity& opportunity,
75 LayoutUnit float_offset, 74 const LayoutUnit float_offset,
76 NGBoxStrut margins, 75 const NGBoxStrut& margins,
77 NGExclusion::Type exclusion_type) { 76 NGExclusion::Type exclusion_type) {
78 NGExclusion exclusion; 77 NGExclusion exclusion;
79 exclusion.type = exclusion_type; 78 exclusion.type = exclusion_type;
80 NGLogicalRect& rect = exclusion.rect; 79 NGLogicalRect& rect = exclusion.rect;
81 rect.offset = opportunity.offset; 80 rect.offset = opportunity.offset;
82 rect.offset.inline_offset += float_offset; 81 rect.offset.inline_offset += float_offset;
83 82
84 rect.size.inline_size = fragment.InlineSize(); 83 rect.size.inline_size = fragment.InlineSize() + margins.InlineSum();
85 rect.size.block_size = fragment.BlockSize(); 84 rect.size.block_size = fragment.BlockSize() + margins.BlockSum();
85 return exclusion;
86 }
86 87
87 // Adjust to child's margin. 88 // Adjusts the provided offset to the top edge alignment rule.
88 rect.size.block_size += margins.BlockSum(); 89 // Top edge alignment rule: the outer top of a floating box may not be higher
89 rect.size.inline_size += margins.InlineSum(); 90 // than the outer top of any block or floated box generated by an element
90 91 // earlier in the source document
ikilpatrick 2017/01/30 22:26:48 .nit period
Gleb Lanbin 2017/01/31 00:25:02 Done.
91 return exclusion; 92 NGLogicalOffset AdjustToTopEdgeAlignmentRule(const NGConstraintSpace& space,
93 const NGLogicalOffset& offset) {
94 NGLogicalOffset adjusted_offset = offset;
95 LayoutUnit& adjusted_block_offset = adjusted_offset.block_offset;
96 if (space.Exclusions()->last_left_float)
97 adjusted_block_offset =
98 std::max(adjusted_block_offset,
99 space.Exclusions()->last_left_float->rect.BlockStartOffset());
100 if (space.Exclusions()->last_right_float)
101 adjusted_block_offset =
102 std::max(adjusted_block_offset,
103 space.Exclusions()->last_right_float->rect.BlockStartOffset());
104 return adjusted_offset;
92 } 105 }
93 106
94 // Finds a layout opportunity for the fragment. 107 // Finds a layout opportunity for the fragment.
95 // It iterates over all layout opportunities in the constraint space and returns 108 // It iterates over all layout opportunities in the constraint space and returns
96 // the first layout opportunity that is wider than the fragment or returns the 109 // the first layout opportunity that is wider than the fragment or returns the
97 // last one which is always the widest. 110 // last one which is always the widest.
98 // 111 //
99 // @param space Constraint space that is used to find layout opportunity for 112 // @param space Constraint space that is used to find layout opportunity for
100 // the fragment. 113 // the fragment.
101 // @param fragment Fragment that needs to be placed. 114 // @param fragment Fragment that needs to be placed.
115 // @param origin_point {@code space}'s offset relative to the space that
116 // establishes a new formatting context that we're currently
117 // in and where all our exclusions reside.
102 // @param margins Margins of the fragment. 118 // @param margins Margins of the fragment.
103 // @return Layout opportunity for the fragment. 119 // @return Layout opportunity for the fragment.
104 const NGLayoutOpportunity FindLayoutOpportunityForFragment( 120 const NGLayoutOpportunity FindLayoutOpportunityForFragment(
105 NGConstraintSpace* space, 121 NGConstraintSpace* space,
106 const NGFragment& fragment, 122 const NGFragment& fragment,
123 const NGLogicalOffset& origin_point,
107 const NGBoxStrut& margins) { 124 const NGBoxStrut& margins) {
108 NGLayoutOpportunityIterator* opportunity_iter = space->LayoutOpportunities(); 125 NGLogicalOffset adjusted_origin_point =
126 AdjustToTopEdgeAlignmentRule(*space, origin_point);
127
128 NGLayoutOpportunityIterator* opportunity_iter =
129 space->LayoutOpportunities(adjusted_origin_point);
109 NGLayoutOpportunity opportunity; 130 NGLayoutOpportunity opportunity;
110 NGLayoutOpportunity opportunity_candidate = opportunity_iter->Next(); 131 NGLayoutOpportunity opportunity_candidate = opportunity_iter->Next();
111 132
112 while (!opportunity_candidate.IsEmpty()) { 133 while (!opportunity_candidate.IsEmpty()) {
113 opportunity = opportunity_candidate; 134 opportunity = opportunity_candidate;
114 // Checking opportunity's block size is not necessary as a float cannot be 135 // Checking opportunity's block size is not necessary as a float cannot be
115 // positioned on top of another float inside of the same constraint space. 136 // positioned on top of another float inside of the same constraint space.
116 auto fragment_inline_size = fragment.InlineSize() + margins.InlineSum(); 137 auto fragment_inline_size = fragment.InlineSize() + margins.InlineSum();
117 if (opportunity.size.inline_size > fragment_inline_size) 138 if (opportunity.size.inline_size >= fragment_inline_size)
118 break; 139 break;
119
120 opportunity_candidate = opportunity_iter->Next(); 140 opportunity_candidate = opportunity_iter->Next();
121 } 141 }
122
123 return opportunity; 142 return opportunity;
124 } 143 }
125 144
126 // Calculates the logical offset for opportunity. 145 // Calculates the logical offset for opportunity.
127 NGLogicalOffset CalculateLogicalOffsetForOpportunity( 146 NGLogicalOffset CalculateLogicalOffsetForOpportunity(
128 const NGLayoutOpportunity& opportunity, 147 const NGLayoutOpportunity& opportunity,
129 LayoutUnit float_offset, 148 const LayoutUnit float_offset,
130 NGBoxStrut margins) { 149 const NGBoxStrut& margins,
150 const NGLogicalOffset& space_offset) {
131 // Adjust to child's margin. 151 // Adjust to child's margin.
132 LayoutUnit inline_offset = margins.inline_start; 152 LayoutUnit inline_offset = margins.inline_start;
133 LayoutUnit block_offset = margins.block_start; 153 LayoutUnit block_offset = margins.block_start;
134 154
135 // Offset from the opportunity's block/inline start. 155 // Offset from the opportunity's block/inline start.
136 inline_offset += opportunity.offset.inline_offset; 156 inline_offset += opportunity.offset.inline_offset;
137 block_offset += opportunity.offset.block_offset; 157 block_offset += opportunity.offset.block_offset;
138 158
139 inline_offset += float_offset; 159 inline_offset += float_offset;
140 160
161 block_offset -= space_offset.block_offset;
162 inline_offset -= space_offset.inline_offset;
163
141 return NGLogicalOffset(inline_offset, block_offset); 164 return NGLogicalOffset(inline_offset, block_offset);
142 } 165 }
143 166
167 // Calculates the relative position from {@code from_offset} of the
168 // floating object that is requested to be positioned from {@code origin_point}.
169 NGLogicalOffset PositionFloat(const NGLogicalOffset& origin_point,
170 const NGLogicalOffset& from_offset,
171 NGFloatingObject* floating_object) {
172 NGConstraintSpace* float_space = floating_object->space;
173 DCHECK(floating_object->fragment) << "Fragment cannot be null here";
174 NGBoxFragment* float_fragment =
175 new NGBoxFragment(float_space->WritingMode(), float_space->Direction(),
176 toNGPhysicalBoxFragment(floating_object->fragment));
177 // Find a layout opportunity that will fit our float.
178 const NGLayoutOpportunity opportunity =
179 FindLayoutOpportunityForFragment(floating_object->space, *float_fragment,
180 origin_point, floating_object->margins);
181 DCHECK(!opportunity.IsEmpty()) << "Opportunity is empty but it shouldn't be";
182
183 // Calculate the float offset if needed.
184 LayoutUnit float_offset;
185 if (floating_object->exclusion_type == NGExclusion::kFloatRight) {
186 float_offset = opportunity.size.inline_size - float_fragment->InlineSize();
187 }
188
189 // Add the float as an exclusion.
190 const NGExclusion exclusion = CreateExclusion(
191 *float_fragment, opportunity, float_offset, floating_object->margins,
192 floating_object->exclusion_type);
193 float_space->AddExclusion(exclusion);
194
195 return CalculateLogicalOffsetForOpportunity(
196 opportunity, float_offset, floating_object->margins, from_offset);
197 }
198
199 // Positions pending floats stored on the fragment builder starting from
200 // {@code origin_point}.
201 void PositionPendingFloats(const NGLogicalOffset& origin_point,
202 NGFragmentBuilder* builder) {
203 DCHECK(builder->BfcOffset()) << "Parent BFC offset should be known here";
204 NGLogicalOffset from_offset = builder->BfcOffset().value();
205
206 for (auto& floating_object : builder->UnpositionedFloats()) {
207 NGLogicalOffset float_fragment_offset =
208 PositionFloat(origin_point, from_offset, floating_object);
209 builder->AddFloatingObject(floating_object, float_fragment_offset);
210 }
211 builder->MutableUnpositionedFloats().clear();
212 }
213
144 // Whether an in-flow block-level child creates a new formatting context. 214 // Whether an in-flow block-level child creates a new formatting context.
145 // 215 //
146 // This will *NOT* check the following cases: 216 // This will *NOT* check the following cases:
147 // - The child is out-of-flow, e.g. floating or abs-pos. 217 // - The child is out-of-flow, e.g. floating or abs-pos.
148 // - The child is a inline-level, e.g. "display: inline-block". 218 // - The child is a inline-level, e.g. "display: inline-block".
149 // - The child establishes a new formatting context, but should be a child of 219 // - The child establishes a new formatting context, but should be a child of
150 // another layout algorithm, e.g. "display: table-caption" or flex-item. 220 // another layout algorithm, e.g. "display: table-caption" or flex-item.
151 bool IsNewFormattingContextForInFlowBlockLevelChild( 221 bool IsNewFormattingContextForInFlowBlockLevelChild(
152 const NGConstraintSpace& space, 222 const NGConstraintSpace& space,
153 const ComputedStyle& style) { 223 const ComputedStyle& style) {
(...skipping 29 matching lines...) Expand all
183 PassRefPtr<const ComputedStyle> style, 253 PassRefPtr<const ComputedStyle> style,
184 NGBlockNode* first_child, 254 NGBlockNode* first_child,
185 NGConstraintSpace* constraint_space, 255 NGConstraintSpace* constraint_space,
186 NGBreakToken* break_token) 256 NGBreakToken* break_token)
187 : NGLayoutAlgorithm(kBlockLayoutAlgorithm), 257 : NGLayoutAlgorithm(kBlockLayoutAlgorithm),
188 style_(style), 258 style_(style),
189 first_child_(first_child), 259 first_child_(first_child),
190 constraint_space_(constraint_space), 260 constraint_space_(constraint_space),
191 break_token_(break_token), 261 break_token_(break_token),
192 builder_(new NGFragmentBuilder(NGPhysicalFragment::kFragmentBox, 262 builder_(new NGFragmentBuilder(NGPhysicalFragment::kFragmentBox,
193 layout_object)), 263 layout_object)) {
194 is_fragment_margin_strut_block_start_updated_(false) {
195 DCHECK(style_); 264 DCHECK(style_);
196 } 265 }
197 266
198 bool NGBlockLayoutAlgorithm::ComputeMinAndMaxContentSizes( 267 bool NGBlockLayoutAlgorithm::ComputeMinAndMaxContentSizes(
199 MinAndMaxContentSizes* sizes) const { 268 MinAndMaxContentSizes* sizes) const {
200 sizes->min_content = LayoutUnit(); 269 sizes->min_content = LayoutUnit();
201 sizes->max_content = LayoutUnit(); 270 sizes->max_content = LayoutUnit();
202 271
203 // Size-contained elements don't consider their contents for intrinsic sizing. 272 // Size-contained elements don't consider their contents for intrinsic sizing.
204 if (Style().containsSize()) 273 if (Style().containsSize())
(...skipping 10 matching lines...) Expand all
215 ComputeMinAndMaxContentContribution(*node->Style(), child_minmax); 284 ComputeMinAndMaxContentContribution(*node->Style(), child_minmax);
216 285
217 sizes->min_content = std::max(sizes->min_content, child_sizes.min_content); 286 sizes->min_content = std::max(sizes->min_content, child_sizes.min_content);
218 sizes->max_content = std::max(sizes->max_content, child_sizes.max_content); 287 sizes->max_content = std::max(sizes->max_content, child_sizes.max_content);
219 } 288 }
220 289
221 sizes->max_content = std::max(sizes->min_content, sizes->max_content); 290 sizes->max_content = std::max(sizes->min_content, sizes->max_content);
222 return true; 291 return true;
223 } 292 }
224 293
294 NGLogicalOffset NGBlockLayoutAlgorithm::PositionFragmentWithKnownBfcOffset(
ikilpatrick 2017/01/30 22:26:48 It might be worth just placing this function inlin
Gleb Lanbin 2017/01/31 00:25:02 PTAL
295 const NGLogicalOffset& offset) {
296 curr_bfc_offset_.block_offset = offset.block_offset;
297 // First block that knows its position. Update the parent's BFC offset.
298 if (!builder_->BfcOffset()) {
299 NGLogicalOffset parent_bfc_offset =
300 ConstraintSpace().IsNewFormattingContext() ? NGLogicalOffset()
301 : curr_bfc_offset_;
302 builder_->SetBfcOffset(parent_bfc_offset);
303 }
304
305 // OK to position pending floats. curr_bfc_offset_ and parent's BFC offset
306 // are accurate here.
307 PositionPendingFloats(curr_bfc_offset_, builder_);
308
309 LayoutUnit block_offset =
310 offset.block_offset - builder_->BfcOffset().value().block_offset;
311 LayoutUnit inline_offset =
312 border_and_padding_.inline_start + curr_child_margins_.inline_start;
313 return {inline_offset, block_offset};
314 }
315
225 NGPhysicalFragment* NGBlockLayoutAlgorithm::Layout() { 316 NGPhysicalFragment* NGBlockLayoutAlgorithm::Layout() {
226 WTF::Optional<MinAndMaxContentSizes> sizes; 317 WTF::Optional<MinAndMaxContentSizes> sizes;
227 if (NeedMinAndMaxContentSizes(ConstraintSpace(), Style())) { 318 if (NeedMinAndMaxContentSizes(ConstraintSpace(), Style())) {
228 // TODO(ikilpatrick): Change ComputeMinAndMaxContentSizes to return 319 // TODO(ikilpatrick): Change ComputeMinAndMaxContentSizes to return
229 // MinAndMaxContentSizes. 320 // MinAndMaxContentSizes.
230 sizes = MinAndMaxContentSizes(); 321 sizes = MinAndMaxContentSizes();
231 ComputeMinAndMaxContentSizes(&*sizes); 322 ComputeMinAndMaxContentSizes(&*sizes);
232 } 323 }
233 324
234 border_and_padding_ = 325 border_and_padding_ =
(...skipping 26 matching lines...) Expand all
261 } 352 }
262 space_builder_->SetAvailableSize( 353 space_builder_->SetAvailableSize(
263 NGLogicalSize(adjusted_inline_size, adjusted_block_size)); 354 NGLogicalSize(adjusted_inline_size, adjusted_block_size));
264 space_builder_->SetPercentageResolutionSize( 355 space_builder_->SetPercentageResolutionSize(
265 NGLogicalSize(adjusted_inline_size, adjusted_block_size)); 356 NGLogicalSize(adjusted_inline_size, adjusted_block_size));
266 357
267 builder_->SetDirection(constraint_space_->Direction()); 358 builder_->SetDirection(constraint_space_->Direction());
268 builder_->SetWritingMode(constraint_space_->WritingMode()); 359 builder_->SetWritingMode(constraint_space_->WritingMode());
269 builder_->SetInlineSize(inline_size).SetBlockSize(block_size); 360 builder_->SetInlineSize(inline_size).SetBlockSize(block_size);
270 361
362 // TODO(glebl): fix multicol after the new margin collapsing/floats algorithm
363 // based on BFCOffset is checked in.
271 if (NGBlockBreakToken* token = CurrentBlockBreakToken()) { 364 if (NGBlockBreakToken* token = CurrentBlockBreakToken()) {
272 // Resume after a previous break. 365 // Resume after a previous break.
273 content_size_ = token->BreakOffset(); 366 content_size_ = token->BreakOffset();
274 current_child_ = token->InputNode(); 367 current_child_ = token->InputNode();
275 } else { 368 } else {
276 content_size_ = border_and_padding_.block_start; 369 content_size_ = border_and_padding_.block_start;
277 current_child_ = first_child_; 370 current_child_ = first_child_;
278 } 371 }
279 372
373 curr_margin_strut_ = ConstraintSpace().MarginStrut();
374 curr_bfc_offset_ = ConstraintSpace().BfcOffset();
375
376 // Margins collapsing:
377 // Do not collapse margins between parent and its child if there is
378 // border/padding between them.
379 if (border_and_padding_.block_start) {
380 curr_bfc_offset_.block_offset += curr_margin_strut_.Sum();
381 builder_->SetBfcOffset(curr_bfc_offset_);
382 curr_margin_strut_ = NGMarginStrut();
383 }
384 curr_bfc_offset_.block_offset += content_size_;
385
280 while (current_child_) { 386 while (current_child_) {
281 EPosition position = current_child_->Style()->position(); 387 EPosition position = current_child_->Style()->position();
282 if (position == AbsolutePosition || position == FixedPosition) { 388 if (position == AbsolutePosition || position == FixedPosition) {
283 builder_->AddOutOfFlowChildCandidate(current_child_, 389 builder_->AddOutOfFlowChildCandidate(current_child_,
284 GetChildSpaceOffset()); 390 GetChildSpaceOffset());
285 current_child_ = current_child_->NextSibling(); 391 current_child_ = current_child_->NextSibling();
286 continue; 392 continue;
287 } 393 }
288 394
289 DCHECK(!ConstraintSpace().HasBlockFragmentation() || 395 DCHECK(!ConstraintSpace().HasBlockFragmentation() ||
290 SpaceAvailableForCurrentChild() > LayoutUnit()); 396 SpaceAvailableForCurrentChild() > LayoutUnit());
291 space_for_current_child_ = CreateConstraintSpaceForCurrentChild(); 397 space_for_current_child_ = CreateConstraintSpaceForCurrentChild();
292 398
293 NGPhysicalFragment* child_fragment = 399 NGPhysicalFragment* child_fragment =
294 current_child_->Layout(space_for_current_child_); 400 current_child_->Layout(space_for_current_child_);
295 401
296 FinishCurrentChildLayout(new NGBoxFragment( 402 FinishCurrentChildLayout(new NGBoxFragment(
297 ConstraintSpace().WritingMode(), ConstraintSpace().Direction(), 403 ConstraintSpace().WritingMode(), ConstraintSpace().Direction(),
298 toNGPhysicalBoxFragment(child_fragment))); 404 toNGPhysicalBoxFragment(child_fragment)));
299 405
300 if (!ProceedToNextUnfinishedSibling(child_fragment)) 406 if (!ProceedToNextUnfinishedSibling(child_fragment))
301 break; 407 break;
302 } 408 }
303 409
410 // Margins collapsing:
411 // Bottom margins of an in-flow block box doesn't collapse with its last
412 // in-flow block-level child's bottom margin if the box has bottom
413 // border/padding.
304 content_size_ += border_and_padding_.block_end; 414 content_size_ += border_and_padding_.block_end;
415 if (border_and_padding_.block_end ||
416 ConstraintSpace().IsNewFormattingContext()) {
417 content_size_ += curr_margin_strut_.Sum();
ikilpatrick 2017/01/30 22:26:48 is there a test case for this?
Gleb Lanbin 2017/01/31 00:25:02 I believe it's covered in CollapsingMarginsCase4 /
418 curr_margin_strut_ = NGMarginStrut();
419 }
305 420
306 // Recompute the block-axis size now that we know our content size. 421 // Recompute the block-axis size now that we know our content size.
307 block_size = 422 block_size =
308 ComputeBlockSizeForFragment(ConstraintSpace(), Style(), content_size_); 423 ComputeBlockSizeForFragment(ConstraintSpace(), Style(), content_size_);
309 builder_->SetBlockSize(block_size); 424 builder_->SetBlockSize(block_size);
310 425
311 LayoutOutOfFlowChildren(); 426 LayoutOutOfFlowChildren();
ikilpatrick 2017/01/30 22:26:48 you may need to rebase :)
Gleb Lanbin 2017/01/31 00:25:02 Done.
312 427
428 // Set out BFC offset if the size is not empty.
429 if (block_size && !builder_->BfcOffset()) {
430 curr_bfc_offset_.block_offset += curr_margin_strut_.Sum();
431 builder_->SetBfcOffset(curr_bfc_offset_);
432 }
433
434 // Margins collapsing:
435 // Do not collapse margins between the last in-flow child and bottom margin
436 // of its parent if the parent has height != auto()
437 if (!Style().logicalHeight().isAuto()) {
438 // TODO(glebl): handle minLogicalHeight, maxLogicalHeight.
439 curr_margin_strut_ = NGMarginStrut();
440 }
441 builder_->SetEndMarginStrut(curr_margin_strut_);
442
313 builder_->SetInlineOverflow(max_inline_size_).SetBlockOverflow(content_size_); 443 builder_->SetInlineOverflow(max_inline_size_).SetBlockOverflow(content_size_);
314 444
315 if (ConstraintSpace().HasBlockFragmentation()) 445 if (ConstraintSpace().HasBlockFragmentation())
316 FinalizeForFragmentation(); 446 FinalizeForFragmentation();
317 447
318 NGPhysicalFragment* fragment = builder_->ToBoxFragment(); 448 NGPhysicalFragment* fragment = builder_->ToBoxFragment();
319 449
320 return fragment; 450 return fragment;
321 } 451 }
322 452
323 void NGBlockLayoutAlgorithm::FinishCurrentChildLayout(NGFragment* fragment) { 453 void NGBlockLayoutAlgorithm::FinishCurrentChildLayout(
324 NGBoxStrut child_margins = ComputeMargins( 454 NGFragment* base_fragment) {
325 *space_for_current_child_, CurrentChildStyle(), 455 const NGBoxFragment& fragment = *toNGBoxFragment(base_fragment);
326 constraint_space_->WritingMode(), constraint_space_->Direction()); 456 // Pull out unpositioned floats to the current fragment. This may needed if
327 NGLogicalOffset fragment_offset; 457 // for example the child fragment could not position its floats because it's
458 // empty and therefore couldn't determine its position in space.
459 builder_->MutableUnpositionedFloats().appendVector(
460 fragment.PhysicalFragment()->UnpositionedFloats());
ikilpatrick 2017/01/30 22:26:49 is it worth adding a DCHECK if there are unpositio
Gleb Lanbin 2017/01/31 00:25:02 Done.
461
328 if (CurrentChildStyle().isFloating()) { 462 if (CurrentChildStyle().isFloating()) {
329 fragment_offset = PositionFloatFragment(*fragment, child_margins); 463 NGFloatingObject* floating_object = new NGFloatingObject(
330 } else { 464 fragment.PhysicalFragment(), space_for_current_child_, current_child_,
331 ApplyAutoMargins(*space_for_current_child_, CurrentChildStyle(), 465 CurrentChildStyle(), curr_child_margins_);
332 fragment->InlineSize(), &child_margins); 466 builder_->AddUnpositionedFloat(floating_object);
333 fragment_offset = PositionFragment(*fragment, child_margins); 467 // No need to postpone the positioning as we know the correct offset.
ikilpatrick 2017/01/30 22:26:48 // No need to postpone the positioning if we know
Gleb Lanbin 2017/01/31 00:25:02 Done.
468 if (builder_->BfcOffset()) {
469 NGLogicalOffset origin_point = curr_bfc_offset_;
470 // Adjust origin point to the margins of the last child.
471 // Example: <div style="margin-bottom: 20px"><float></div>
472 // <div style="margin-bottom: 30px"></div>
473 origin_point.block_offset += curr_margin_strut_.Sum();
474 PositionPendingFloats(origin_point, builder_);
475 }
476 return;
334 } 477 }
478
479 // Collapse empty block's margins.
480 if (!fragment.BlockSize()) {
ikilpatrick 2017/01/30 22:26:48 You can remove this? This happens below: 498 /
Gleb Lanbin 2017/01/31 00:25:02 thanks.
481 curr_margin_strut_.Append(curr_child_margins_.block_end);
482 }
483
484 NGLogicalOffset fragment_offset = {
485 border_and_padding_.inline_start + curr_child_margins_.inline_start,
486 content_size_};
487 if (fragment.BfcOffset())
488 fragment_offset =
489 PositionFragmentWithKnownBfcOffset(fragment.BfcOffset().value());
490
335 if (fragmentainer_mapper_) 491 if (fragmentainer_mapper_)
336 fragmentainer_mapper_->ToVisualOffset(fragment_offset); 492 fragmentainer_mapper_->ToVisualOffset(fragment_offset);
337 else 493 else
338 fragment_offset.block_offset -= PreviousBreakOffset(); 494 fragment_offset.block_offset -= PreviousBreakOffset();
339 builder_->AddChild(fragment, fragment_offset); 495
496 builder_->AddChild(base_fragment, fragment_offset);
497
498 // Update margin strut.
499 curr_margin_strut_ = fragment.EndMarginStrut();
500 curr_margin_strut_.Append(curr_child_margins_.block_end);
501
502 content_size_ = fragment.BlockSize() + fragment_offset.block_offset;
503 max_inline_size_ =
504 std::max(max_inline_size_, fragment.InlineSize() +
505 curr_child_margins_.InlineSum() +
506 border_and_padding_.InlineSum());
340 } 507 }
341 508
342 void NGBlockLayoutAlgorithm::LayoutOutOfFlowChildren() { 509 void NGBlockLayoutAlgorithm::LayoutOutOfFlowChildren() {
343 HeapLinkedHashSet<WeakMember<NGBlockNode>> out_of_flow_candidates; 510 HeapLinkedHashSet<WeakMember<NGBlockNode>> out_of_flow_candidates;
344 Vector<NGStaticPosition> out_of_flow_candidate_positions; 511 Vector<NGStaticPosition> out_of_flow_candidate_positions;
345 builder_->GetAndClearOutOfFlowDescendantCandidates( 512 builder_->GetAndClearOutOfFlowDescendantCandidates(
346 &out_of_flow_candidates, &out_of_flow_candidate_positions); 513 &out_of_flow_candidates, &out_of_flow_candidate_positions);
347 514
348 Member<NGOutOfFlowLayoutPart> out_of_flow_layout = 515 Member<NGOutOfFlowLayoutPart> out_of_flow_layout =
349 new NGOutOfFlowLayoutPart(&Style(), builder_->Size()); 516 new NGOutOfFlowLayoutPart(&Style(), builder_->Size());
(...skipping 148 matching lines...) Expand 10 before | Expand all | Expand 10 after
498 if (fragmentainer_mapper_) 665 if (fragmentainer_mapper_)
499 space_left = fragmentainer_mapper_->BlockSize(); 666 space_left = fragmentainer_mapper_->BlockSize();
500 else if (ConstraintSpace().HasBlockFragmentation()) 667 else if (ConstraintSpace().HasBlockFragmentation())
501 space_left = ConstraintSpace().FragmentainerSpaceAvailable(); 668 space_left = ConstraintSpace().FragmentainerSpaceAvailable();
502 else 669 else
503 return NGSizeIndefinite; 670 return NGSizeIndefinite;
504 space_left -= BorderEdgeForCurrentChild() - PreviousBreakOffset(); 671 space_left -= BorderEdgeForCurrentChild() - PreviousBreakOffset();
505 return space_left; 672 return space_left;
506 } 673 }
507 674
508 NGBoxStrut NGBlockLayoutAlgorithm::CollapseMargins(
509 const NGBoxStrut& margins,
510 const NGBoxFragment& fragment) {
511 bool is_zero_height_box = !fragment.BlockSize() && margins.IsEmpty() &&
512 fragment.MarginStrut().IsEmpty();
513 // Create the current child's margin strut from its children's margin strut or
514 // use margin strut from the the last non-empty child.
515 NGDeprecatedMarginStrut curr_margin_strut =
516 is_zero_height_box ? prev_child_margin_strut_ : fragment.MarginStrut();
517
518 // Calculate borders and padding for the current child.
519 NGBoxStrut border_and_padding =
520 ComputeBorders(CurrentChildStyle()) +
521 ComputePadding(ConstraintSpace(), CurrentChildStyle());
522
523 // Collapse BLOCK-START margins if there is no padding or border between
524 // parent (current child) and its first in-flow child.
525 if (border_and_padding.block_start) {
526 curr_margin_strut.SetMarginBlockStart(margins.block_start);
527 } else {
528 curr_margin_strut.AppendMarginBlockStart(margins.block_start);
529 }
530
531 // Collapse BLOCK-END margins if
532 // 1) there is no padding or border between parent (current child) and its
533 // first/last in-flow child
534 // 2) parent's logical height is auto.
535 if (CurrentChildStyle().logicalHeight().isAuto() &&
536 !border_and_padding.block_end) {
537 curr_margin_strut.AppendMarginBlockEnd(margins.block_end);
538 } else {
539 curr_margin_strut.SetMarginBlockEnd(margins.block_end);
540 }
541
542 NGBoxStrut result_margins;
543 // Margins of the newly established formatting context do not participate
544 // in Collapsing Margins:
545 // - Compute margins block start for adjoining blocks *including* 1st block.
546 // - Compute margins block end for the last block.
547 // - Do not set the computed margins to the parent fragment.
548 if (constraint_space_->IsNewFormattingContext()) {
549 result_margins.block_start = ComputeCollapsedMarginBlockStart(
550 prev_child_margin_strut_, curr_margin_strut);
551 bool is_last_child = !current_child_->NextSibling();
552 if (is_last_child)
553 result_margins.block_end = curr_margin_strut.BlockEndSum();
554 return result_margins;
555 }
556
557 // Zero-height boxes are ignored and do not participate in margin collapsing.
558 if (is_zero_height_box)
559 return result_margins;
560
561 // Compute the margin block start for adjoining blocks *excluding* 1st block
562 if (is_fragment_margin_strut_block_start_updated_) {
563 result_margins.block_start = ComputeCollapsedMarginBlockStart(
564 prev_child_margin_strut_, curr_margin_strut);
565 }
566
567 // Update the parent fragment's margin strut
568 UpdateMarginStrut(curr_margin_strut);
569
570 prev_child_margin_strut_ = curr_margin_strut;
571 return result_margins;
572 }
573
574 NGLogicalOffset NGBlockLayoutAlgorithm::PositionFragment(
575 const NGFragment& fragment,
576 const NGBoxStrut& margins) {
577 const NGBoxStrut collapsed_margins =
578 CollapseMargins(margins, toNGBoxFragment(fragment));
579
580 AdjustToClearance(ConstraintSpace(), CurrentChildStyle(), &content_size_);
581
582 LayoutUnit inline_offset =
583 border_and_padding_.inline_start + margins.inline_start;
584 LayoutUnit block_offset = content_size_ + collapsed_margins.block_start;
585
586 content_size_ += fragment.BlockSize() + collapsed_margins.BlockSum();
587 max_inline_size_ =
588 std::max(max_inline_size_, fragment.InlineSize() + margins.InlineSum() +
589 border_and_padding_.InlineSum());
590 return NGLogicalOffset(inline_offset, block_offset);
591 }
592
593 NGLogicalOffset NGBlockLayoutAlgorithm::PositionFloatFragment(
594 const NGFragment& fragment,
595 const NGBoxStrut& margins) {
596 // TODO(glebl@chromium.org): Support the top edge alignment rule.
597 // Find a layout opportunity that will fit our float.
598
599 // Update offset if there is a clearance.
600 NGLogicalOffset offset = CurrentChildConstraintSpace().Offset();
601 AdjustToClearance(ConstraintSpace(), CurrentChildStyle(),
602 &offset.block_offset);
603 space_for_current_child_->SetOffset(offset);
604
605 const NGLayoutOpportunity opportunity = FindLayoutOpportunityForFragment(
606 space_for_current_child_, fragment, margins);
607 DCHECK(!opportunity.IsEmpty()) << "Opportunity is empty but it shouldn't be";
608
609 NGExclusion::Type exclusion_type = NGExclusion::kFloatLeft;
610 // Calculate the float offset if needed.
611 LayoutUnit float_offset;
612 if (CurrentChildStyle().floating() == EFloat::kRight) {
613 float_offset = opportunity.size.inline_size - fragment.InlineSize();
614 exclusion_type = NGExclusion::kFloatRight;
615 }
616
617 // Add the float as an exclusion.
618 const NGExclusion exclusion = CreateExclusion(
619 fragment, opportunity, float_offset, margins, exclusion_type);
620 constraint_space_->AddExclusion(exclusion);
621
622 return CalculateLogicalOffsetForOpportunity(opportunity, float_offset,
623 margins);
624 }
625
626 void NGBlockLayoutAlgorithm::UpdateMarginStrut(
627 const NGDeprecatedMarginStrut& from) {
628 if (!is_fragment_margin_strut_block_start_updated_) {
629 builder_->SetMarginStrutBlockStart(from);
630 is_fragment_margin_strut_block_start_updated_ = true;
631 }
632 builder_->SetMarginStrutBlockEnd(from);
633 }
634
635 NGBoxStrut NGBlockLayoutAlgorithm::CalculateMargins( 675 NGBoxStrut NGBlockLayoutAlgorithm::CalculateMargins(
636 const NGConstraintSpace& space, 676 const NGConstraintSpace& space,
637 const ComputedStyle& style) { 677 const ComputedStyle& style) {
638 WTF::Optional<MinAndMaxContentSizes> sizes; 678 WTF::Optional<MinAndMaxContentSizes> sizes;
639 if (NeedMinAndMaxContentSizes(space, style)) { 679 if (NeedMinAndMaxContentSizes(space, style)) {
640 // TODO(ikilpatrick): Change ComputeMinAndMaxContentSizes to return 680 // TODO(ikilpatrick): Change ComputeMinAndMaxContentSizes to return
641 // MinAndMaxContentSizes. 681 // MinAndMaxContentSizes.
642 sizes = current_child_->ComputeMinAndMaxContentSizesSync(); 682 sizes = current_child_->ComputeMinAndMaxContentSizesSync();
643 } 683 }
644 LayoutUnit child_inline_size = 684 LayoutUnit child_inline_size =
645 ComputeInlineSizeForFragment(space, style, sizes); 685 ComputeInlineSizeForFragment(space, style, sizes);
646 NGBoxStrut margins = 686 NGBoxStrut margins =
647 ComputeMargins(space, style, space.WritingMode(), space.Direction()); 687 ComputeMargins(space, style, space.WritingMode(), space.Direction());
648 if (!style.isFloating()) { 688 if (!style.isFloating()) {
649 ApplyAutoMargins(space, style, child_inline_size, &margins); 689 ApplyAutoMargins(space, style, child_inline_size, &margins);
650 } 690 }
651 return margins; 691 return margins;
652 } 692 }
653 693
654 NGConstraintSpace* 694 NGConstraintSpace*
655 NGBlockLayoutAlgorithm::CreateConstraintSpaceForCurrentChild() { 695 NGBlockLayoutAlgorithm::CreateConstraintSpaceForCurrentChild() {
656 // TODO(layout-ng): Orthogonal children should also shrink to fit (in *their* 696 // TODO(layout-ng): Orthogonal children should also shrink to fit (in *their*
657 // inline axis) 697 // inline axis)
658 // We have to keep this commented out for now until we correctly compute 698 // We have to keep this commented out for now until we correctly compute
659 // min/max content sizes in Layout(). 699 // min/max content sizes in Layout().
660 bool shrink_to_fit = CurrentChildStyle().display() == EDisplay::InlineBlock || 700 bool shrink_to_fit = CurrentChildStyle().display() == EDisplay::InlineBlock ||
661 CurrentChildStyle().isFloating(); 701 CurrentChildStyle().isFloating();
662 DCHECK(current_child_); 702 DCHECK(current_child_);
663 space_builder_ 703 bool is_new_bfc = IsNewFormattingContextForInFlowBlockLevelChild(
664 ->SetIsNewFormattingContext( 704 ConstraintSpace(), CurrentChildStyle());
665 IsNewFormattingContextForInFlowBlockLevelChild(ConstraintSpace(), 705 space_builder_->SetIsNewFormattingContext(is_new_bfc)
666 CurrentChildStyle()))
667 .SetIsShrinkToFit(shrink_to_fit) 706 .SetIsShrinkToFit(shrink_to_fit)
668 .SetWritingMode( 707 .SetWritingMode(
669 FromPlatformWritingMode(CurrentChildStyle().getWritingMode())) 708 FromPlatformWritingMode(CurrentChildStyle().getWritingMode()))
670 .SetTextDirection(CurrentChildStyle().direction()); 709 .SetTextDirection(CurrentChildStyle().direction());
671 LayoutUnit space_available = SpaceAvailableForCurrentChild(); 710 LayoutUnit space_available = SpaceAvailableForCurrentChild();
672 space_builder_->SetFragmentainerSpaceAvailable(space_available); 711 space_builder_->SetFragmentainerSpaceAvailable(space_available);
673 712
674 curr_child_margins_ = CalculateMargins(*space_builder_->ToConstraintSpace(), 713 curr_child_margins_ = CalculateMargins(*space_builder_->ToConstraintSpace(),
675 CurrentChildStyle()); 714 CurrentChildStyle());
676 715
677 NGConstraintSpace* child_space = space_builder_->ToConstraintSpace(); 716 // Clearance :
717 // - Collapse margins
718 // - Update curr_bfc_offset and parent BFC offset if needed.
719 // - Position all pending floats as position is known now.
720 // TODO(glebl): Fix the use case with clear: left and an intruding right.
721 // https://software.hixie.ch/utilities/js/live-dom-viewer/saved/4847
722 if (CurrentChildStyle().clear()) {
723 curr_bfc_offset_.block_offset += curr_margin_strut_.Sum();
724 if (!builder_->BfcOffset()) {
725 builder_->SetBfcOffset(curr_bfc_offset_);
726 }
727 // Only collapse margins if it's an adjoining block with clearance.
728 if (!content_size_) {
729 curr_margin_strut_ = NGMarginStrut();
730 curr_child_margins_.block_start = LayoutUnit();
731 }
732 PositionPendingFloats(curr_bfc_offset_, builder_);
733 AdjustToClearance(constraint_space_->Exclusions(), CurrentChildStyle(),
734 builder_->BfcOffset().value(), &content_size_);
735 }
678 736
679 // TODO(layout-ng): Set offset through the space builder. 737 // Append the current margin strut with child's block start margin.
680 child_space->SetOffset(GetChildSpaceOffset()); 738 // Non empty border/padding use cases are handled inside of the child's
681 return child_space; 739 // layout.
740 curr_margin_strut_.Append(curr_child_margins_.block_start);
741 space_builder_->SetMarginStrut(curr_margin_strut_);
742
743 // Set estimated BFC offset to the next child's constraint space.
744 curr_bfc_offset_ = builder_->BfcOffset() ? builder_->BfcOffset().value()
745 : ConstraintSpace().BfcOffset();
746 curr_bfc_offset_.block_offset += content_size_;
747 curr_bfc_offset_.inline_offset += border_and_padding_.inline_start;
748 if (ConstraintSpace().IsNewFormattingContext()) {
749 curr_bfc_offset_.inline_offset += curr_child_margins_.inline_start;
750 }
751 space_builder_->SetBfcOffset(curr_bfc_offset_);
752
753 return space_builder_->ToConstraintSpace();
682 } 754 }
683 755
684 DEFINE_TRACE(NGBlockLayoutAlgorithm) { 756 DEFINE_TRACE(NGBlockLayoutAlgorithm) {
685 NGLayoutAlgorithm::trace(visitor); 757 NGLayoutAlgorithm::trace(visitor);
686 visitor->trace(first_child_); 758 visitor->trace(first_child_);
687 visitor->trace(constraint_space_); 759 visitor->trace(constraint_space_);
688 visitor->trace(break_token_); 760 visitor->trace(break_token_);
689 visitor->trace(builder_); 761 visitor->trace(builder_);
690 visitor->trace(space_builder_); 762 visitor->trace(space_builder_);
691 visitor->trace(space_for_current_child_); 763 visitor->trace(space_for_current_child_);
692 visitor->trace(current_child_); 764 visitor->trace(current_child_);
693 visitor->trace(fragmentainer_mapper_); 765 visitor->trace(fragmentainer_mapper_);
694 } 766 }
695 767
696 } // namespace blink 768 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698