blob: 4c0508edde6d304a8313a506e728b62bbfa7d1d7 [file] [log] [blame]
// 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_simplified_oof_layout_algorithm.h"
#include "third_party/blink/renderer/core/layout/geometry/writing_mode_converter.h"
#include "third_party/blink/renderer/core/layout/ng/ng_block_break_token.h"
#include "third_party/blink/renderer/core/layout/ng/ng_fragment.h"
#include "third_party/blink/renderer/core/layout/ng/ng_layout_result.h"
#include "third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h"
#include "third_party/blink/renderer/core/layout/ng/ng_relative_utils.h"
namespace blink {
NGSimplifiedOOFLayoutAlgorithm::NGSimplifiedOOFLayoutAlgorithm(
const NGLayoutAlgorithmParams& params,
const NGPhysicalBoxFragment& fragment,
bool is_new_fragment)
: NGLayoutAlgorithm(params),
writing_direction_(Style().GetWritingDirection()) {
DCHECK(fragment.IsFragmentainerBox());
DCHECK(params.space.HasKnownFragmentainerBlockSize());
container_builder_.SetBoxType(fragment.BoxType());
container_builder_.SetFragmentBlockSize(
params.space.FragmentainerBlockSize());
// Don't apply children to new fragments.
if (is_new_fragment) {
children_ = {};
iterator_ = children_.end();
container_builder_.SetIsFirstForNode(false);
return;
}
container_builder_.SetIsFirstForNode(fragment.IsFirstForNode());
// We need the previous physical container size to calculate the position of
// any child fragments.
previous_physical_container_size_ = fragment.Size();
// The OOF fragments need to be added in a particular order that matches the
// order of break tokens. Here's a list of rules to follow , in order:
// 1. If there are any already appended fragments that are a continuation of
// layout (i.e. they are the Nth fragment of a layout box, where N > 1),
// they should be appended before anything else.
// 2. If we're trying to append a fragment that is a continuation of layout
// for an OOF node (from AppendOutOfFlowResult), add it after the fragments
// added in step 1.
// 3. Add the remaining children that were not appended during step 1.
// 4. Add the OOF fragments that were not a continuation of layout, the ones
// that weren't appended in step 2.
children_ = fragment.Children();
iterator_ = children_.begin();
while (iterator_ != children_.end()) {
const auto& child_link = *iterator_;
const auto* child_fragment = To<NGPhysicalBoxFragment>(child_link.get());
if (!child_fragment->IsFirstForNode()) {
AddChildFragment(child_link);
iterator_++;
} else {
// We can break here because fragments that are a continuation of layout
// are always the first children of a physical fragment.
break;
}
}
}
scoped_refptr<const NGLayoutResult> NGSimplifiedOOFLayoutAlgorithm::Layout() {
// There might not be any children to append, whether it's because we are in a
// new fragmentainer or because they have all been added in Step 1.
if (iterator_ != children_.end()) {
// Step 3: Add the remaining children that were not added in step 1.
while (iterator_ != children_.end()) {
const auto& child_link = *iterator_;
const auto* child_fragment = To<NGPhysicalBoxFragment>(child_link.get());
DCHECK(child_fragment->IsFirstForNode());
AddChildFragment(child_link);
iterator_++;
}
// Step 4: Add the OOF fragments that aren't a continuation of layout.
for (auto result : remaining_oof_results_) {
container_builder_.AddResult(*result,
result->OutOfFlowPositionedOffset());
}
}
return container_builder_.ToBoxFragment();
}
void NGSimplifiedOOFLayoutAlgorithm::AppendOutOfFlowResult(
scoped_refptr<const NGLayoutResult> result) {
// Add the new result directly to the builder when the fragment of the result
// to append is not the first fragment of its corresponding layout box,
// meaning that it's positioned directly at the start of the fragmentainer.
// This ensures that we keep the fragments and the break tokens in order.
//
// Also add the result directly to the builder if there are no more children
// to append. This can happen when all children have been added in Step 1 or
// when we are in a new fragmentainer since a new fragmentainer doesn't have
// any child.
if (iterator_ == children_.end() ||
!To<NGPhysicalBoxFragment>(result->PhysicalFragment()).IsFirstForNode()) {
// Step 2: Add the fragments that are a continuation of layout directly to
// the builder.
container_builder_.AddResult(*result, result->OutOfFlowPositionedOffset());
return;
}
// Since there is no previous break token associated with the first fragment
// of a fragmented OOF element, we cannot append this result before any other
// children of this fragmentainer. Keep the order by adding it after.
remaining_oof_results_.push_back(result);
}
void NGSimplifiedOOFLayoutAlgorithm::AddChildFragment(const NGLink& child) {
const auto* fragment = To<NGPhysicalContainerFragment>(child.get());
// Determine the previous position in the logical coordinate system.
LogicalOffset child_offset =
WritingModeConverter(writing_direction_,
previous_physical_container_size_)
.ToLogical(child.Offset(), fragment->Size());
RemoveRelativeOffset(container_builder_, *fragment, &child_offset);
// Add the fragment to the builder.
container_builder_.AddChild(*fragment, child_offset);
}
} // namespace blink