blob: 61b987bb7d44fb52fa76f33f1a59422dd9ad1ce9 [file] [log] [blame]
// Copyright 2017 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 "core/layout/ng/ng_floats_utils.h"
#include "core/layout/ng/ng_box_fragment.h"
namespace blink {
namespace {
// Adjusts the provided offset to the top edge alignment rule.
// Top edge alignment rule: the outer top of a floating box may not be higher
// than the outer top of any block or floated box generated by an element
// earlier in the source document.
NGLogicalOffset AdjustToTopEdgeAlignmentRule(const NGConstraintSpace& space,
const NGLogicalOffset& offset) {
NGLogicalOffset adjusted_offset = offset;
LayoutUnit& adjusted_block_offset = adjusted_offset.block_offset;
if (space.Exclusions()->last_left_float)
adjusted_block_offset =
std::max(adjusted_block_offset,
space.Exclusions()->last_left_float->rect.BlockStartOffset());
if (space.Exclusions()->last_right_float)
adjusted_block_offset =
std::max(adjusted_block_offset,
space.Exclusions()->last_right_float->rect.BlockStartOffset());
return adjusted_offset;
}
// Finds a layout opportunity for the fragment.
// It iterates over all layout opportunities in the constraint space and returns
// the first layout opportunity that is wider than the fragment or returns the
// last one which is always the widest.
//
// @param space Constraint space that is used to find layout opportunity for
// the fragment.
// @param fragment Fragment that needs to be placed.
// @param origin_point {@code space}'s offset relative to the space that
// establishes a new formatting context that we're currently
// in and where all our exclusions reside.
// @param margins Margins of the fragment.
// @return Layout opportunity for the fragment.
const NGLayoutOpportunity FindLayoutOpportunityForFragment(
const NGConstraintSpace* space,
const NGFragment& fragment,
const NGLogicalOffset& origin_point,
const NGBoxStrut& margins) {
NGLogicalOffset adjusted_origin_point =
AdjustToTopEdgeAlignmentRule(*space, origin_point);
NGLayoutOpportunityIterator opportunity_iter(space, adjusted_origin_point);
NGLayoutOpportunity opportunity;
NGLayoutOpportunity opportunity_candidate = opportunity_iter.Next();
while (!opportunity_candidate.IsEmpty()) {
opportunity = opportunity_candidate;
// Checking opportunity's block size is not necessary as a float cannot be
// positioned on top of another float inside of the same constraint space.
auto fragment_inline_size = fragment.InlineSize() + margins.InlineSum();
if (opportunity.size.inline_size >= fragment_inline_size)
break;
opportunity_candidate = opportunity_iter.Next();
}
return opportunity;
}
// Calculates the logical offset for opportunity.
NGLogicalOffset CalculateLogicalOffsetForOpportunity(
const NGLayoutOpportunity& opportunity,
const LayoutUnit float_offset,
const NGLogicalOffset& from_offset,
NGFloatingObject* floating_object) {
DCHECK(floating_object);
auto margins = floating_object->margins;
// Adjust to child's margin.
LayoutUnit inline_offset = margins.inline_start;
LayoutUnit block_offset = margins.block_start;
// Offset from the opportunity's block/inline start.
inline_offset += opportunity.offset.inline_offset;
block_offset += opportunity.offset.block_offset;
// Adjust to float: right offset if needed.
inline_offset += float_offset;
block_offset -= from_offset.block_offset;
inline_offset -= from_offset.inline_offset;
return NGLogicalOffset(inline_offset, block_offset);
}
// Creates an exclusion from the fragment that will be placed in the provided
// layout opportunity.
NGExclusion CreateExclusion(const NGFragment& fragment,
const NGLayoutOpportunity& opportunity,
const LayoutUnit float_offset,
const NGBoxStrut& margins,
NGExclusion::Type exclusion_type) {
NGExclusion exclusion;
exclusion.type = exclusion_type;
NGLogicalRect& rect = exclusion.rect;
rect.offset = opportunity.offset;
rect.offset.inline_offset += float_offset;
rect.size.inline_size = fragment.InlineSize() + margins.InlineSum();
rect.size.block_size = fragment.BlockSize() + margins.BlockSum();
return exclusion;
}
// Updates the Floating Object's left offset from the provided parent_space
// and {@code floating_object}'s space and margins.
void UpdateFloatingObjectLeftOffset(const NGConstraintSpace& new_parent_space,
const NGLogicalOffset& float_logical_offset,
NGFloatingObject* floating_object) {
DCHECK(floating_object);
// TODO(glebl): We should use physical offset here.
floating_object->left_offset =
floating_object->original_parent_space->BfcOffset().inline_offset -
new_parent_space.BfcOffset().inline_offset +
float_logical_offset.inline_offset;
}
} // namespace
// Calculates the relative position from {@code from_offset} of the
// floating object that is requested to be positioned from {@code origin_point}.
NGLogicalOffset PositionFloat(const NGLogicalOffset& origin_point,
const NGLogicalOffset& from_offset,
NGFloatingObject* floating_object,
NGConstraintSpace* new_parent_space) {
DCHECK(floating_object);
const auto* float_space = floating_object->space.get();
DCHECK(floating_object->fragment) << "Fragment cannot be null here";
// TODO(ikilpatrick): The writing mode switching here looks wrong.
NGBoxFragment float_fragment(
float_space->WritingMode(),
toNGPhysicalBoxFragment(floating_object->fragment.get()));
// Find a layout opportunity that will fit our float.
const NGLayoutOpportunity opportunity = FindLayoutOpportunityForFragment(
float_space, float_fragment, origin_point, floating_object->margins);
DCHECK(!opportunity.IsEmpty()) << "Opportunity is empty but it shouldn't be";
// Calculate the float offset if needed.
LayoutUnit float_offset;
if (floating_object->exclusion_type == NGExclusion::kFloatRight) {
LayoutUnit float_margin_box_inline_size =
float_fragment.InlineSize() + floating_object->margins.InlineSum();
float_offset = opportunity.size.inline_size - float_margin_box_inline_size;
}
// Add the float as an exclusion.
const NGExclusion exclusion = CreateExclusion(
float_fragment, opportunity, float_offset, floating_object->margins,
floating_object->exclusion_type);
new_parent_space->AddExclusion(exclusion);
NGLogicalOffset logical_offset = CalculateLogicalOffsetForOpportunity(
opportunity, float_offset, from_offset, floating_object);
UpdateFloatingObjectLeftOffset(*new_parent_space, logical_offset,
floating_object);
return logical_offset;
}
} // namespace blink