blob: 762b170aff419efd686b51fa05d7aaa9f704bd7c [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"
#include "core/layout/ng/ng_layout_opportunity_iterator.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;
}
NGLayoutOpportunity FindLayoutOpportunityForFloat(
const NGConstraintSpace* space,
const NGFragment& fragment,
const NGFloatingObject* floating_object) {
NGLogicalOffset adjusted_origin_point =
AdjustToTopEdgeAlignmentRule(*space, floating_object->origin_offset);
return FindLayoutOpportunityForFragment(
space->Exclusions().get(), floating_object->available_size,
adjusted_origin_point, floating_object->margins, fragment);
}
// Calculates the logical offset for opportunity.
NGLogicalOffset CalculateLogicalOffsetForOpportunity(
const NGLayoutOpportunity& opportunity,
const LayoutUnit float_offset,
const NGFloatingObject* floating_object) {
DCHECK(floating_object);
auto margins = floating_object->margins;
// Adjust to child's margin.
NGLogicalOffset result = margins.InlineBlockStartOffset();
// Offset from the opportunity's block/inline start.
result += opportunity.offset;
// Adjust to float: right offset if needed.
result.inline_offset += float_offset;
result -= floating_object->from_offset;
return result;
}
// 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->from_offset.inline_offset -
new_parent_space.BfcOffset().inline_offset +
float_logical_offset.inline_offset;
}
} // namespace
NGLogicalOffset PositionFloat(NGFloatingObject* floating_object,
NGConstraintSpace* new_parent_space) {
DCHECK(floating_object);
DCHECK(floating_object->fragment) << "Fragment cannot be null here";
// TODO(ikilpatrick): The writing mode switching here looks wrong.
NGBoxFragment float_fragment(
floating_object->writing_mode,
ToNGPhysicalBoxFragment(floating_object->fragment.Get()));
// Find a layout opportunity that will fit our float.
NGLayoutOpportunity opportunity = FindLayoutOpportunityForFloat(
new_parent_space, float_fragment, floating_object);
// TODO(glebl): This should check for infinite opportunity instead.
if (opportunity.IsEmpty()) {
// Because of the implementation specific of the layout opportunity iterator
// an empty opportunity can mean 2 things:
// - search for layout opportunities is exhausted.
// - opportunity has an infinite size. That's because CS is infinite.
opportunity = NGLayoutOpportunity(
NGLogicalOffset(),
NGLogicalSize(float_fragment.InlineSize(), float_fragment.BlockSize()));
}
// 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, floating_object);
UpdateFloatingObjectLeftOffset(*new_parent_space, logical_offset,
floating_object);
return logical_offset;
}
void PositionFloats(LayoutUnit origin_block_offset,
LayoutUnit from_block_offset,
const Vector<RefPtr<NGFloatingObject>>& floating_objects,
NGConstraintSpace* space) {
for (auto& floating_object : floating_objects) {
floating_object->origin_offset.block_offset = origin_block_offset;
floating_object->from_offset.block_offset = from_block_offset;
floating_object->logical_offset =
PositionFloat(floating_object.Get(), space);
}
}
} // namespace blink