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

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

Powered by Google App Engine
This is Rietveld 408576698