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

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

Issue 2632523002: [LayoutNG] Initial support for multicol, introducing NGBlockBreakToken. (Closed)
Patch Set: Created 3 years, 11 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_block_break_token.h"
7 #include "core/layout/ng/ng_box_fragment.h" 8 #include "core/layout/ng/ng_box_fragment.h"
8 #include "core/layout/ng/ng_break_token.h" 9 #include "core/layout/ng/ng_column_mapper.h"
9 #include "core/layout/ng/ng_constraint_space.h" 10 #include "core/layout/ng/ng_constraint_space.h"
10 #include "core/layout/ng/ng_constraint_space_builder.h" 11 #include "core/layout/ng/ng_constraint_space_builder.h"
11 #include "core/layout/ng/ng_fragment.h" 12 #include "core/layout/ng/ng_fragment.h"
12 #include "core/layout/ng/ng_fragment_builder.h" 13 #include "core/layout/ng/ng_fragment_builder.h"
13 #include "core/layout/ng/ng_layout_opportunity_iterator.h" 14 #include "core/layout/ng/ng_layout_opportunity_iterator.h"
14 #include "core/layout/ng/ng_length_utils.h" 15 #include "core/layout/ng/ng_length_utils.h"
15 #include "core/layout/ng/ng_out_of_flow_layout_part.h" 16 #include "core/layout/ng/ng_out_of_flow_layout_part.h"
16 #include "core/layout/ng/ng_units.h" 17 #include "core/layout/ng/ng_units.h"
17 #include "core/style/ComputedStyle.h" 18 #include "core/style/ComputedStyle.h"
18 #include "platform/LengthFunctions.h" 19 #include "platform/LengthFunctions.h"
(...skipping 248 matching lines...) Expand 10 before | Expand all | Expand 10 after
267 // If so, just leave the size as NGSizeIndefinite instead of subtracting 268 // If so, just leave the size as NGSizeIndefinite instead of subtracting
268 // borders and padding. 269 // borders and padding.
269 if (adjusted_block_size != NGSizeIndefinite) 270 if (adjusted_block_size != NGSizeIndefinite)
270 adjusted_block_size -= border_and_padding_.BlockSum(); 271 adjusted_block_size -= border_and_padding_.BlockSum();
271 272
272 space_builder_ = new NGConstraintSpaceBuilder(constraint_space_); 273 space_builder_ = new NGConstraintSpaceBuilder(constraint_space_);
273 if (Style().specifiesColumns()) { 274 if (Style().specifiesColumns()) {
274 space_builder_->SetFragmentationType(kFragmentColumn); 275 space_builder_->SetFragmentationType(kFragmentColumn);
275 adjusted_inline_size = 276 adjusted_inline_size =
276 ResolveUsedColumnInlineSize(adjusted_inline_size, Style()); 277 ResolveUsedColumnInlineSize(adjusted_inline_size, Style());
278 LayoutUnit inline_progression =
279 adjusted_inline_size + ResolveUsedColumnGap(Style());
280 fragmentainer_mapper_ =
281 new NGColumnMapper(inline_progression, adjusted_block_size);
277 } 282 }
278 space_builder_->SetAvailableSize( 283 space_builder_->SetAvailableSize(
279 NGLogicalSize(adjusted_inline_size, adjusted_block_size)); 284 NGLogicalSize(adjusted_inline_size, adjusted_block_size));
280 space_builder_->SetPercentageResolutionSize( 285 space_builder_->SetPercentageResolutionSize(
281 NGLogicalSize(adjusted_inline_size, adjusted_block_size)); 286 NGLogicalSize(adjusted_inline_size, adjusted_block_size));
282 287
283 content_size_ = border_and_padding_.block_start;
284
285 builder_ = new NGFragmentBuilder(NGPhysicalFragment::kFragmentBox); 288 builder_ = new NGFragmentBuilder(NGPhysicalFragment::kFragmentBox);
286 builder_->SetDirection(constraint_space_->Direction()); 289 builder_->SetDirection(constraint_space_->Direction());
287 builder_->SetWritingMode(constraint_space_->WritingMode()); 290 builder_->SetWritingMode(constraint_space_->WritingMode());
288 builder_->SetInlineSize(inline_size).SetBlockSize(block_size); 291 builder_->SetInlineSize(inline_size).SetBlockSize(block_size);
289 292
290 current_child_ = first_child_; 293 if (NGBlockBreakToken* token = CurrentBlockBreakToken()) {
294 // Resume after a previous break.
295 content_size_ = token->BreakOffset();
296 current_child_ = token->InputNode();
297 } else {
298 content_size_ = border_and_padding_.block_start;
299 current_child_ = first_child_;
300 }
301
291 layout_state_ = kStatePrepareForChildLayout; 302 layout_state_ = kStatePrepareForChildLayout;
292 return kNotFinished; 303 return kNotFinished;
293 } 304 }
294 case kStatePrepareForChildLayout: { 305 case kStatePrepareForChildLayout: {
295 if (current_child_) { 306 if (current_child_) {
296 EPosition position = current_child_->Style()->position(); 307 EPosition position = current_child_->Style()->position();
297 if ((position == AbsolutePosition || position == FixedPosition)) { 308 if ((position == AbsolutePosition || position == FixedPosition)) {
298 builder_->AddOutOfFlowChildCandidate(current_child_, 309 builder_->AddOutOfFlowChildCandidate(current_child_,
299 GetChildSpaceOffset()); 310 GetChildSpaceOffset());
300 current_child_ = current_child_->NextSibling(); 311 current_child_ = current_child_->NextSibling();
301 return kNotFinished; 312 return kNotFinished;
302 } 313 }
314 DCHECK(!ConstraintSpace().HasBlockFragmentation() ||
315 SpaceAvailableForCurrentChild() > LayoutUnit());
303 space_for_current_child_ = CreateConstraintSpaceForCurrentChild(); 316 space_for_current_child_ = CreateConstraintSpaceForCurrentChild();
304 *algorithm_out = NGLayoutInputNode::AlgorithmForInputNode( 317 *algorithm_out = NGLayoutInputNode::AlgorithmForInputNode(
305 current_child_, space_for_current_child_); 318 current_child_, space_for_current_child_);
306 layout_state_ = kStateChildLayout; 319 layout_state_ = kStateChildLayout;
307 return kChildAlgorithmRequired; 320 return kChildAlgorithmRequired;
308 } 321 }
309 322
310 // Prepare for kStateOutOfFlowLayout 323 // Prepare for kStateOutOfFlowLayout
311 content_size_ += border_and_padding_.block_end; 324 content_size_ += border_and_padding_.block_end;
312 325
(...skipping 16 matching lines...) Expand all
329 DCHECK(current_child_); 342 DCHECK(current_child_);
330 DCHECK(child_fragment); 343 DCHECK(child_fragment);
331 344
332 // TODO(layout_ng): Seems like a giant hack to call this here. 345 // TODO(layout_ng): Seems like a giant hack to call this here.
333 current_child_->UpdateLayoutBox(toNGPhysicalBoxFragment(child_fragment), 346 current_child_->UpdateLayoutBox(toNGPhysicalBoxFragment(child_fragment),
334 space_for_current_child_); 347 space_for_current_child_);
335 348
336 FinishCurrentChildLayout(new NGBoxFragment( 349 FinishCurrentChildLayout(new NGBoxFragment(
337 ConstraintSpace().WritingMode(), ConstraintSpace().Direction(), 350 ConstraintSpace().WritingMode(), ConstraintSpace().Direction(),
338 toNGPhysicalBoxFragment(child_fragment))); 351 toNGPhysicalBoxFragment(child_fragment)));
339 current_child_ = current_child_->NextSibling(); 352
340 layout_state_ = kStatePrepareForChildLayout; 353 if (ProceedToNextUnfinishedSibling(child_fragment))
354 layout_state_ = kStatePrepareForChildLayout;
355 else
356 layout_state_ = kStateFinalize;
341 return kNotFinished; 357 return kNotFinished;
342 } 358 }
343 case kStateOutOfFlowLayout: 359 case kStateOutOfFlowLayout:
344 if (LayoutOutOfFlowChild()) 360 if (LayoutOutOfFlowChild())
345 layout_state_ = kStateFinalize; 361 layout_state_ = kStateFinalize;
346 return kNotFinished; 362 return kNotFinished;
347 case kStateFinalize: { 363 case kStateFinalize: {
348 builder_->SetInlineOverflow(max_inline_size_) 364 builder_->SetInlineOverflow(max_inline_size_)
349 .SetBlockOverflow(content_size_); 365 .SetBlockOverflow(content_size_);
366
367 if (ConstraintSpace().HasBlockFragmentation())
368 FinalizeForFragmentation();
369
350 *fragment_out = builder_->ToBoxFragment(); 370 *fragment_out = builder_->ToBoxFragment();
351 layout_state_ = kStateInit; 371 layout_state_ = kStateInit;
352 return kNewFragment; 372 return kNewFragment;
353 } 373 }
354 }; 374 };
355 NOTREACHED(); 375 NOTREACHED();
356 *fragment_out = nullptr; 376 *fragment_out = nullptr;
357 return kNewFragment; 377 return kNewFragment;
358 } 378 }
359 379
360 void NGBlockLayoutAlgorithm::FinishCurrentChildLayout(NGFragment* fragment) { 380 void NGBlockLayoutAlgorithm::FinishCurrentChildLayout(NGFragment* fragment) {
361 NGBoxStrut child_margins = ComputeMargins( 381 NGBoxStrut child_margins = ComputeMargins(
362 *space_for_current_child_, CurrentChildStyle(), 382 *space_for_current_child_, CurrentChildStyle(),
363 constraint_space_->WritingMode(), constraint_space_->Direction()); 383 constraint_space_->WritingMode(), constraint_space_->Direction());
364 384
365 NGLogicalOffset fragment_offset; 385 NGLogicalOffset fragment_offset;
366 if (CurrentChildStyle().isFloating()) { 386 if (CurrentChildStyle().isFloating()) {
367 fragment_offset = PositionFloatFragment(*fragment, child_margins); 387 fragment_offset = PositionFloatFragment(*fragment, child_margins);
368 } else { 388 } else {
369 ApplyAutoMargins(*space_for_current_child_, CurrentChildStyle(), *fragment, 389 ApplyAutoMargins(*space_for_current_child_, CurrentChildStyle(), *fragment,
370 &child_margins); 390 &child_margins);
371 fragment_offset = PositionFragment(*fragment, child_margins); 391 fragment_offset = PositionFragment(*fragment, child_margins);
372 } 392 }
393 if (fragmentainer_mapper_)
394 fragmentainer_mapper_->ToVisualOffset(fragment_offset);
395 else
396 fragment_offset.block_offset -= PreviousBreakOffset();
373 builder_->AddChild(fragment, fragment_offset); 397 builder_->AddChild(fragment, fragment_offset);
374 } 398 }
375 399
376 bool NGBlockLayoutAlgorithm::LayoutOutOfFlowChild() { 400 bool NGBlockLayoutAlgorithm::LayoutOutOfFlowChild() {
377 if (!current_child_) { 401 if (!current_child_) {
378 if (out_of_flow_candidates_.isEmpty()) { 402 if (out_of_flow_candidates_.isEmpty()) {
379 out_of_flow_layout_ = nullptr; 403 out_of_flow_layout_ = nullptr;
380 out_of_flow_candidate_positions_.clear(); 404 out_of_flow_candidate_positions_.clear();
381 return true; 405 return true;
382 } 406 }
(...skipping 11 matching lines...) Expand all
394 NGFragment* fragment; 418 NGFragment* fragment;
395 NGLogicalOffset offset; 419 NGLogicalOffset offset;
396 if (out_of_flow_layout_->Layout(&fragment, &offset) == kNewFragment) { 420 if (out_of_flow_layout_->Layout(&fragment, &offset) == kNewFragment) {
397 // TODO(atotic) Need to adjust size of overflow rect per spec. 421 // TODO(atotic) Need to adjust size of overflow rect per spec.
398 builder_->AddChild(fragment, offset); 422 builder_->AddChild(fragment, offset);
399 current_child_ = nullptr; 423 current_child_ = nullptr;
400 } 424 }
401 return false; 425 return false;
402 } 426 }
403 427
428 bool NGBlockLayoutAlgorithm::ProceedToNextUnfinishedSibling(
429 NGPhysicalFragment* child_fragment) {
430 DCHECK(current_child_);
431 NGBlockNode* finished_child = current_child_;
432 current_child_ = current_child_->NextSibling();
433 if (!ConstraintSpace().HasBlockFragmentation() && !fragmentainer_mapper_)
434 return true;
435 // If we're resuming layout after a fragmentainer break, we need to skip
436 // siblings that we're done with. We may have been able to fully lay out some
437 // node(s) preceding a node that we had to break inside (and therefore were
438 // not able to fully lay out). This happens when we have parallel flows [1],
439 // which are caused by floats, overflow, etc.
440 //
441 // [1] https://drafts.csswg.org/css-break/#parallel-flows
442 if (CurrentBlockBreakToken()) {
443 while (current_child_ && current_child_->IsLayoutFinished())
444 current_child_ = current_child_->NextSibling();
445 }
446 LayoutUnit break_offset = NextBreakOffset();
447 bool is_out_of_space = content_size_ - PreviousBreakOffset() >= break_offset;
448 if (!HasPendingBreakToken()) {
449 bool child_broke = child_fragment->BreakToken();
450 // This block needs to break if the child broke, or if we're out of space
451 // and there's more content waiting to be laid out. Otherwise, just bail
452 // now.
453 if (!child_broke && (!is_out_of_space || !current_child_))
454 return true;
455 // Prepare a break token for this block, so that we know where to resume
456 // when the time comes for that. We may not be able to abort layout of this
457 // block right away, due to the posibility of parallel flows. We can only
458 // abort when we're out of space, or when there are no siblings left to
459 // process.
460 NGBlockBreakToken* token;
461 if (child_broke) {
462 // The child we just laid out was the first one to break. So that is
463 // where we need to resume.
464 token = new NGBlockBreakToken(finished_child, break_offset);
465 } else {
466 // Resume layout at the next sibling that needs layout.
467 DCHECK(current_child_);
468 token = new NGBlockBreakToken(current_child_, break_offset);
469 }
470 SetPendingBreakToken(token);
471 }
472
473 if (!fragmentainer_mapper_) {
eae 2017/01/12 23:36:23 When is fragmentainer_mapper_ expected to be null?
mstensho (USE GERRIT) 2017/01/13 14:30:18 Almost "always". :) It's only set on the root frag
474 if (!is_out_of_space)
475 return true;
476 // We have run out of space in this flow, so there's no work left to do for
477 // this block in this fragmentainer. We should finalize the fragment and get
478 // back to the remaining content when laying out the next fragmentainer(s).
479 return false;
480 }
481
482 if (is_out_of_space || !current_child_) {
483 NGBlockBreakToken* token = fragmentainer_mapper_->Advance();
484 DCHECK(token || !is_out_of_space);
485 if (token) {
486 break_token_ = token;
487 content_size_ = token->BreakOffset();
488 current_child_ = token->InputNode();
489 }
490 }
491 return true;
492 }
493
494 void NGBlockLayoutAlgorithm::SetPendingBreakToken(NGBlockBreakToken* token) {
495 if (fragmentainer_mapper_)
496 fragmentainer_mapper_->SetBreakToken(token);
497 else
498 builder_->SetBreakToken(token);
499 }
500
501 bool NGBlockLayoutAlgorithm::HasPendingBreakToken() const {
502 if (fragmentainer_mapper_)
503 return fragmentainer_mapper_->HasBreakToken();
504 return builder_->HasBreakToken();
505 }
506
507 void NGBlockLayoutAlgorithm::FinalizeForFragmentation() {
508 LayoutUnit block_size =
509 ComputeBlockSizeForFragment(ConstraintSpace(), Style(), content_size_);
510 LayoutUnit previous_break_offset = PreviousBreakOffset();
511 block_size -= previous_break_offset;
512 block_size = std::max(LayoutUnit(), block_size);
513 LayoutUnit space_left = ConstraintSpace().FragmentainerSpaceAvailable();
514 DCHECK_GE(space_left, LayoutUnit());
515 if (builder_->HasBreakToken()) {
516 // A break token is ready, which means that we're going to break
517 // before or inside a block-level child.
518 builder_->SetBlockSize(std::min(space_left, block_size));
519 builder_->SetBlockOverflow(space_left);
520 return;
521 }
522 if (block_size > space_left) {
523 // Need a break inside this block.
524 builder_->SetBreakToken(new NGBlockBreakToken(nullptr, NextBreakOffset()));
525 builder_->SetBlockSize(space_left);
526 builder_->SetBlockOverflow(space_left);
527 return;
528 }
529 // The end of the block fits in the current fragmentainer.
530 builder_->SetBlockSize(block_size);
531 builder_->SetBlockOverflow(content_size_ - previous_break_offset);
532 }
533
534 NGBlockBreakToken* NGBlockLayoutAlgorithm::CurrentBlockBreakToken() const {
535 NGBreakToken* token = break_token_;
536 if (!token || token->Type() != NGBreakToken::kBlockBreakToken)
537 return nullptr;
538 return toNGBlockBreakToken(token);
539 }
540
541 LayoutUnit NGBlockLayoutAlgorithm::PreviousBreakOffset() const {
542 const NGBlockBreakToken* token = CurrentBlockBreakToken();
543 return token ? token->BreakOffset() : LayoutUnit();
544 }
545
546 LayoutUnit NGBlockLayoutAlgorithm::NextBreakOffset() const {
547 if (fragmentainer_mapper_)
548 return fragmentainer_mapper_->NextBreakOffset();
549 DCHECK(ConstraintSpace().HasBlockFragmentation());
550 return PreviousBreakOffset() +
551 ConstraintSpace().FragmentainerSpaceAvailable();
552 }
553
554 LayoutUnit NGBlockLayoutAlgorithm::SpaceAvailableForCurrentChild() const {
555 LayoutUnit space_left;
556 if (fragmentainer_mapper_)
557 space_left = fragmentainer_mapper_->BlockSize();
558 else if (ConstraintSpace().HasBlockFragmentation())
559 space_left = ConstraintSpace().FragmentainerSpaceAvailable();
560 else
561 return NGSizeIndefinite;
562 space_left -= BorderEdgeForCurrentChild() - PreviousBreakOffset();
563 return space_left;
564 }
565
404 NGBoxStrut NGBlockLayoutAlgorithm::CollapseMargins( 566 NGBoxStrut NGBlockLayoutAlgorithm::CollapseMargins(
405 const NGBoxStrut& margins, 567 const NGBoxStrut& margins,
406 const NGBoxFragment& fragment) { 568 const NGBoxFragment& fragment) {
407 bool is_zero_height_box = !fragment.BlockSize() && margins.IsEmpty() && 569 bool is_zero_height_box = !fragment.BlockSize() && margins.IsEmpty() &&
408 fragment.MarginStrut().IsEmpty(); 570 fragment.MarginStrut().IsEmpty();
409 // Create the current child's margin strut from its children's margin strut or 571 // Create the current child's margin strut from its children's margin strut or
410 // use margin strut from the the last non-empty child. 572 // use margin strut from the the last non-empty child.
411 NGMarginStrut curr_margin_strut = 573 NGMarginStrut curr_margin_strut =
412 is_zero_height_box ? prev_child_margin_strut_ : fragment.MarginStrut(); 574 is_zero_height_box ? prev_child_margin_strut_ : fragment.MarginStrut();
413 575
(...skipping 123 matching lines...) Expand 10 before | Expand all | Expand 10 after
537 CurrentChildStyle().isFloating(); 699 CurrentChildStyle().isFloating();
538 DCHECK(current_child_); 700 DCHECK(current_child_);
539 space_builder_ 701 space_builder_
540 ->SetIsNewFormattingContext( 702 ->SetIsNewFormattingContext(
541 IsNewFormattingContextForInFlowBlockLevelChild(ConstraintSpace(), 703 IsNewFormattingContextForInFlowBlockLevelChild(ConstraintSpace(),
542 CurrentChildStyle())) 704 CurrentChildStyle()))
543 .SetIsShrinkToFit(shrink_to_fit) 705 .SetIsShrinkToFit(shrink_to_fit)
544 .SetWritingMode( 706 .SetWritingMode(
545 FromPlatformWritingMode(CurrentChildStyle().getWritingMode())) 707 FromPlatformWritingMode(CurrentChildStyle().getWritingMode()))
546 .SetTextDirection(CurrentChildStyle().direction()); 708 .SetTextDirection(CurrentChildStyle().direction());
709 LayoutUnit space_available = SpaceAvailableForCurrentChild();
710 space_builder_->SetFragmentainerSpaceAvailable(space_available);
547 NGConstraintSpace* child_space = space_builder_->ToConstraintSpace(); 711 NGConstraintSpace* child_space = space_builder_->ToConstraintSpace();
548 712
549 // TODO(layout-ng): Set offset through the space builder. 713 // TODO(layout-ng): Set offset through the space builder.
550 child_space->SetOffset(GetChildSpaceOffset()); 714 child_space->SetOffset(GetChildSpaceOffset());
551 return child_space; 715 return child_space;
552 } 716 }
553 717
554 DEFINE_TRACE(NGBlockLayoutAlgorithm) { 718 DEFINE_TRACE(NGBlockLayoutAlgorithm) {
555 NGLayoutAlgorithm::trace(visitor); 719 NGLayoutAlgorithm::trace(visitor);
556 visitor->trace(first_child_); 720 visitor->trace(first_child_);
557 visitor->trace(constraint_space_); 721 visitor->trace(constraint_space_);
558 visitor->trace(break_token_); 722 visitor->trace(break_token_);
559 visitor->trace(builder_); 723 visitor->trace(builder_);
560 visitor->trace(space_builder_); 724 visitor->trace(space_builder_);
561 visitor->trace(space_for_current_child_); 725 visitor->trace(space_for_current_child_);
562 visitor->trace(current_child_); 726 visitor->trace(current_child_);
563 visitor->trace(current_minmax_child_); 727 visitor->trace(current_minmax_child_);
564 visitor->trace(out_of_flow_layout_); 728 visitor->trace(out_of_flow_layout_);
565 visitor->trace(out_of_flow_candidates_); 729 visitor->trace(out_of_flow_candidates_);
730 visitor->trace(fragmentainer_mapper_);
566 } 731 }
567 732
568 } // namespace blink 733 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698