blob: 4cb444ce7ee86582d50e72b9db07d52300382bff [file] [log] [blame]
// Copyright 2019 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_layout_algorithm.h"
#include "third_party/blink/renderer/core/layout/ng/ng_block_break_token.h"
#include "third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm_utils.h"
#include "third_party/blink/renderer/core/layout/ng/ng_box_fragment.h"
#include "third_party/blink/renderer/core/layout/ng/ng_constraint_space.h"
#include "third_party/blink/renderer/core/layout/ng/ng_constraint_space_builder.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_length_utils.h"
#include "third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.h"
#include "third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h"
#include "third_party/blink/renderer/core/layout/ng/ng_space_utils.h"
namespace blink {
NGSimplifiedLayoutAlgorithm::NGSimplifiedLayoutAlgorithm(
const NGLayoutAlgorithmParams& params,
const NGLayoutResult& result)
: NGLayoutAlgorithm(params),
previous_result_(result),
border_scrollbar_padding_(params.fragment_geometry.border +
params.fragment_geometry.scrollbar +
params.fragment_geometry.padding),
writing_mode_(Style().GetWritingMode()),
direction_(Style().Direction()),
exclusion_space_(ConstraintSpace().ExclusionSpace()) {
// Currently this only supports block-flow layout due to the static-position
// calculations. If support for other layout types is added this logic will
// need to be changed.
DCHECK(Node().IsBlockFlow());
const NGPhysicalBoxFragment& physical_fragment =
To<NGPhysicalBoxFragment>(result.PhysicalFragment());
container_builder_.SetStyleVariant(physical_fragment.StyleVariant());
container_builder_.SetIsNewFormattingContext(
physical_fragment.IsBlockFormattingContextRoot());
container_builder_.SetInitialFragmentGeometry(params.fragment_geometry);
NGExclusionSpace exclusion_space = result.ExclusionSpace();
container_builder_.SetExclusionSpace(std::move(exclusion_space));
// Ensure that the parent layout hasn't asked us to move our BFC position.
DCHECK_EQ(ConstraintSpace().BfcOffset(),
previous_result_.GetConstraintSpaceForCaching().BfcOffset());
container_builder_.SetBfcLineOffset(result.BfcLineOffset());
if (result.BfcBlockOffset())
container_builder_.SetBfcBlockOffset(*result.BfcBlockOffset());
container_builder_.SetEndMarginStrut(result.EndMarginStrut());
container_builder_.SetIntrinsicBlockSize(result.IntrinsicBlockSize());
container_builder_.SetUnpositionedListMarker(result.UnpositionedListMarker());
if (result.IsEmptyBlock())
container_builder_.SetIsEmptyBlock();
if (result.IsPushedByFloats())
container_builder_.SetIsPushedByFloats();
container_builder_.AddAdjoiningFloatTypes(result.AdjoiningFloatTypes());
for (const auto& request : ConstraintSpace().BaselineRequests()) {
base::Optional<LayoutUnit> baseline = physical_fragment.Baseline(request);
if (baseline)
container_builder_.AddBaseline(request, *baseline);
}
container_builder_.SetBlockSize(ComputeBlockSizeForFragment(
ConstraintSpace(), Style(),
container_builder_.Borders() + container_builder_.Padding(),
result.IntrinsicBlockSize()));
child_available_inline_size_ =
ShrinkAvailableSize(container_builder_.InitialBorderBoxSize(),
border_scrollbar_padding_)
.inline_size;
// We need the previous physical container size to calculate the position of
// any child fragments.
previous_physical_container_size_ = physical_fragment.Size();
}
scoped_refptr<const NGLayoutResult> NGSimplifiedLayoutAlgorithm::Layout() {
const auto previous_child_fragments =
To<NGPhysicalBoxFragment>(previous_result_.PhysicalFragment()).Children();
const auto* it = previous_child_fragments.begin();
const auto* end = previous_child_fragments.end();
// We may have a list-marker as our first child. This may have been
// propagated up to this container by an arbitrary child. As we don't know
// where it came from initially add it as the first child again.
if (it != end && (*it)->IsListMarker()) {
AddChildFragment(*it, *To<NGPhysicalContainerFragment>(it->get()));
++it;
}
// Initialize the static block-offset for any OOF-positioned children.
static_block_offset_ = border_scrollbar_padding_.block_start;
for (NGLayoutInputNode child = Node().FirstChild(); child;
child = child.NextSibling()) {
// We've already dealt with any list-markers, so just skip this node.
if (child.IsListMarker())
continue;
if (child.IsOutOfFlowPositioned()) {
HandleOutOfFlowPositioned(To<NGBlockNode>(child));
continue;
}
DCHECK(it != end);
if (child.IsInline()) {
// Simplified layout will only run if none of the lineboxes are dirty.
while (it != end && (*it)->IsLineBox()) {
// NOTE: When we remove continuations it'll be necessary for lineboxes
// to keep track of any exclusions they've added (and update the
// exclusion space).
AddChildFragment(*it, *To<NGPhysicalContainerFragment>(it->get()));
++it;
}
continue;
}
DCHECK_EQ((*it)->GetLayoutObject(), child.GetLayoutBox());
// Add the (potentially updated) layout result.
scoped_refptr<const NGLayoutResult> result =
To<NGBlockNode>(child).SimplifiedLayout();
// The child may have failed "simplified" layout! (Due to adding/removing
// scrollbars). In this case we also return a nullptr, indicating a full
// layout is required.
if (!result)
return nullptr;
const NGPhysicalContainerFragment& fragment = result->PhysicalFragment();
AddChildFragment(*it, fragment);
const ComputedStyle& child_style = child.Style();
// Calculate the static block-offset for any OOF-positioned children.
NGMarginStrut margin_strut = result->EndMarginStrut();
NGBoxStrut child_margins = ComputeMarginsFor(
child_style, child_available_inline_size_, writing_mode_, direction_);
margin_strut.Append(child_margins.block_end,
child_style.HasMarginBeforeQuirk());
static_block_offset_ += margin_strut.Sum();
// Only take exclusion spaces from children which don't establish their own
// formatting context.
if (!fragment.IsBlockFormattingContextRoot())
exclusion_space_ = result->ExclusionSpace();
++it;
}
NGOutOfFlowLayoutPart(
Node(), ConstraintSpace(),
container_builder_.Borders() + container_builder_.Scrollbar(),
&container_builder_)
.Run();
return container_builder_.ToBoxFragment();
}
void NGSimplifiedLayoutAlgorithm::HandleOutOfFlowPositioned(
const NGBlockNode& child) {
LogicalOffset static_offset = {border_scrollbar_padding_.inline_start,
static_block_offset_};
if (child.Style().IsOriginalDisplayInlineType()) {
NGBfcOffset origin_bfc_offset = {
container_builder_.BfcLineOffset() +
border_scrollbar_padding_.LineLeft(direction_),
(container_builder_.BfcBlockOffset()
? *container_builder_.BfcBlockOffset()
: ConstraintSpace().BfcOffset().block_offset) +
static_block_offset_};
static_offset.inline_offset += CalculateOutOfFlowStaticInlineLevelOffset(
Style(), origin_bfc_offset, exclusion_space_,
child_available_inline_size_);
}
container_builder_.AddOutOfFlowChildCandidate(child, static_offset);
}
void NGSimplifiedLayoutAlgorithm::AddChildFragment(
const NGLinkStorage& old_fragment,
const NGPhysicalContainerFragment& new_fragment) {
DCHECK_EQ(old_fragment->Size(), new_fragment.Size());
PhysicalSize physical_child_size = new_fragment.Size();
LogicalSize child_size = physical_child_size.ConvertToLogical(writing_mode_);
// Determine the previous position in the logical coordinate system.
LogicalOffset child_offset = old_fragment.Offset().ConvertToLogical(
writing_mode_, direction_, previous_physical_container_size_,
physical_child_size);
// Add the new fragemnt to the builder.
container_builder_.AddChild(new_fragment, child_offset);
// Update the static block-offset for any OOF-positioned children.
static_block_offset_ = child_offset.block_offset + child_size.block_size;
}
} // namespace blink