OLD | NEW |
---|---|
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 |
OLD | NEW |