summaryrefslogtreecommitdiff
path: root/chromium/third_party/blink/renderer/core/layout/ng/ng_fragment_child_iterator.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/third_party/blink/renderer/core/layout/ng/ng_fragment_child_iterator.cc')
-rw-r--r--chromium/third_party/blink/renderer/core/layout/ng/ng_fragment_child_iterator.cc212
1 files changed, 212 insertions, 0 deletions
diff --git a/chromium/third_party/blink/renderer/core/layout/ng/ng_fragment_child_iterator.cc b/chromium/third_party/blink/renderer/core/layout/ng/ng_fragment_child_iterator.cc
new file mode 100644
index 00000000000..167e14136f6
--- /dev/null
+++ b/chromium/third_party/blink/renderer/core/layout/ng/ng_fragment_child_iterator.cc
@@ -0,0 +1,212 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/core/layout/ng/ng_fragment_child_iterator.h"
+
+#include "third_party/blink/renderer/core/layout/layout_box.h"
+#include "third_party/blink/renderer/core/layout/ng/ng_block_break_token.h"
+#include "third_party/blink/renderer/core/layout/ng/ng_fragmentation_utils.h"
+#include "third_party/blink/renderer/core/layout/ng/ng_layout_input_node.h"
+#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
+
+namespace blink {
+
+NGFragmentChildIterator::NGFragmentChildIterator(
+ const NGPhysicalBoxFragment& parent,
+ const NGBlockBreakToken* parent_break_token)
+ : parent_fragment_(&parent),
+ parent_break_token_(parent_break_token),
+ is_fragmentation_context_root_(parent.IsFragmentationContextRoot()) {
+ DCHECK(RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled());
+ current_.link_.fragment = nullptr;
+ if (parent_break_token)
+ child_break_tokens_ = parent_break_token->ChildBreakTokens();
+ if (parent.HasItems()) {
+ current_.cursor_.emplace(*parent.Items());
+ current_.block_break_token_ = parent_break_token;
+ UpdateSelfFromCursor();
+ } else {
+ UpdateSelfFromFragment();
+ }
+}
+
+NGFragmentChildIterator::NGFragmentChildIterator(
+ const NGInlineCursor& parent,
+ const NGBlockBreakToken* parent_break_token,
+ base::span<const NGBreakToken* const> child_break_tokens)
+ : parent_break_token_(parent_break_token),
+ child_break_tokens_(child_break_tokens) {
+ current_.block_break_token_ = parent_break_token;
+ current_.link_.fragment = nullptr;
+ current_.cursor_ = parent.CursorForDescendants();
+ UpdateSelfFromCursor();
+}
+
+NGFragmentChildIterator NGFragmentChildIterator::Descend() const {
+ if (current_.cursor_) {
+ const NGFragmentItem* item = current_.cursor_->CurrentItem();
+ // Descend using the cursor if the current item doesn't establish a new
+ // formatting context.
+ if (!item->IsFormattingContextRoot()) {
+ return NGFragmentChildIterator(
+ *current_.cursor_,
+ current_.BlockBreakToken() ? parent_break_token_ : nullptr,
+ child_break_tokens_.subspan(child_break_token_idx_));
+ }
+ }
+ DCHECK(current_.BoxFragment());
+ return NGFragmentChildIterator(*current_.BoxFragment(),
+ current_.BlockBreakToken());
+}
+
+bool NGFragmentChildIterator::AdvanceChildFragment() {
+ DCHECK(parent_fragment_);
+ const auto children = parent_fragment_->Children();
+ const NGPhysicalBoxFragment* previous_fragment =
+ To<NGPhysicalBoxFragment>(current_.link_.fragment);
+ DCHECK(previous_fragment);
+ if (child_fragment_idx_ < children.size())
+ child_fragment_idx_++;
+ // There may be line box fragments among the children, and we're not
+ // interested in them (lines will already have been handled by the inline
+ // cursor).
+ SkipToBoxFragment();
+ if (child_fragment_idx_ >= children.size())
+ return false;
+ if (child_break_token_idx_ < child_break_tokens_.size())
+ child_break_token_idx_++;
+ UpdateSelfFromFragment(previous_fragment);
+ return true;
+}
+
+void NGFragmentChildIterator::UpdateSelfFromFragment(
+ const NGPhysicalBoxFragment* previous_fragment) {
+ DCHECK(parent_fragment_);
+ const auto children = parent_fragment_->Children();
+ if (child_fragment_idx_ >= children.size())
+ return;
+ current_.link_ = children[child_fragment_idx_];
+ DCHECK(current_.link_.fragment);
+ SkipToBlockBreakToken();
+ if (child_break_token_idx_ < child_break_tokens_.size()) {
+ current_.block_break_token_ =
+ To<NGBlockBreakToken>(child_break_tokens_[child_break_token_idx_]);
+ DCHECK(!current_.link_.fragment->GetLayoutObject() ||
+ current_.block_break_token_->InputNode().GetLayoutBox() ==
+ current_.link_.fragment->GetLayoutObject());
+ current_.break_token_for_fragmentainer_only_ = false;
+ } else if (is_fragmentation_context_root_ && previous_fragment) {
+ if (previous_fragment->IsColumnBox()) {
+ // The outgoing break token from one fragmentainer is the incoming break
+ // token to the next one. This is also true when there are column spanners
+ // between two columns (fragmentainers); the outgoing break token from the
+ // former column will be ignored by any intervening spanners, and then fed
+ // into the first column that comes after them, as an incoming break
+ // token.
+ current_.block_break_token_ =
+ To<NGBlockBreakToken>(previous_fragment->BreakToken());
+ current_.break_token_for_fragmentainer_only_ = true;
+ } else {
+ // This is a column spanner. We'll leave |current_block_break_token_|
+ // alone here, as it will be used as in incoming break token when we get
+ // to the next column.
+ DCHECK(
+ NGBlockNode(ToLayoutBox(previous_fragment->GetMutableLayoutObject()))
+ .IsColumnSpanAll());
+
+ // If the previous fragment is a column spanner, it's not expected to have
+ // a break token; if a spanner runs out of space, no columns (or spanners)
+ // would fit after it.
+ DCHECK(!previous_fragment->BreakToken());
+ }
+ } else {
+ current_.block_break_token_ = nullptr;
+ }
+}
+
+bool NGFragmentChildIterator::AdvanceWithCursor() {
+ DCHECK(current_.cursor_);
+ const NGFragmentItem* item = current_.cursor_->CurrentItem();
+ if (item->HasChildren()) {
+ // If we're advancing past a non-atomic inline, we also need to advance past
+ // any break tokens for fragments in there.
+ for (wtf_size_t remaining = item->DescendantsCount(); remaining;
+ remaining--) {
+ if (item->IsFloating()) {
+ SkipToBlockBreakToken();
+ if (child_break_token_idx_ < child_break_tokens_.size()) {
+ DCHECK_EQ(child_break_tokens_[child_break_token_idx_]
+ ->InputNode()
+ .GetLayoutBox(),
+ item->GetLayoutObject());
+ child_break_token_idx_++;
+ }
+ }
+ current_.cursor_->MoveToNext();
+ item = current_.cursor_->CurrentItem();
+ }
+ } else {
+ current_.cursor_->MoveToNext();
+ }
+ UpdateSelfFromCursor();
+ if (current_.cursor_->CurrentItem())
+ return true;
+ // If there are more items, proceed and see if we have box fragment
+ // children. There may be out-of-flow positioned child fragments.
+ if (!parent_fragment_)
+ return false;
+ current_.cursor_.reset();
+ SkipToBoxFragment();
+ UpdateSelfFromFragment();
+ return !IsAtEnd();
+}
+
+void NGFragmentChildIterator::UpdateSelfFromCursor() {
+ DCHECK(current_.cursor_);
+ // For inline items we just use the incoming break token to the containing
+ // block.
+ current_.block_break_token_ = parent_break_token_;
+ const NGFragmentItem* item = current_.cursor_->CurrentItem();
+ if (!item) {
+ current_.link_.fragment = nullptr;
+ return;
+ }
+ current_.link_ = {item->BoxFragment(), item->OffsetInContainerBlock()};
+ if (!current_.link_.fragment || !current_.link_.fragment->IsFloating()) {
+ DCHECK(!current_.link_.fragment ||
+ current_.link_.fragment->GetLayoutObject()->IsInline());
+ return;
+ }
+ if (!parent_break_token_)
+ return;
+ // Floats may fragment, in which case there's a designated break token for
+ // them.
+ SkipToBlockBreakToken();
+ if (child_break_token_idx_ >= child_break_tokens_.size()) {
+ current_.block_break_token_ = nullptr;
+ return;
+ }
+ current_.block_break_token_ =
+ To<NGBlockBreakToken>(child_break_tokens_[child_break_token_idx_]);
+ DCHECK(!current_.link_.fragment->GetLayoutObject() ||
+ current_.block_break_token_->InputNode().GetLayoutBox() ==
+ current_.link_.fragment->GetLayoutObject());
+}
+
+void NGFragmentChildIterator::SkipToBoxFragment() {
+ for (const auto children = parent_fragment_->Children();
+ child_fragment_idx_ < children.size(); child_fragment_idx_++) {
+ if (children[child_fragment_idx_].fragment->IsBox())
+ break;
+ }
+}
+
+void NGFragmentChildIterator::SkipToBlockBreakToken() {
+ // There may be inline break tokens here. Ignore them.
+ while (child_break_token_idx_ < child_break_tokens_.size() &&
+ !child_break_tokens_[child_break_token_idx_]->IsBlockType())
+ child_break_token_idx_++;
+}
+
+} // namespace blink