1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156
|
// Copyright 2017 The Chromium Authors
// 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/relative_utils.h"
#include <optional>
#include "third_party/blink/renderer/core/layout/constraint_space.h"
#include "third_party/blink/renderer/core/layout/physical_box_fragment.h"
#include "third_party/blink/renderer/core/style/computed_style.h"
#include "third_party/blink/renderer/platform/geometry/length_functions.h"
#include "third_party/blink/renderer/platform/geometry/physical_offset.h"
#include "third_party/blink/renderer/platform/geometry/physical_size.h"
namespace blink {
LogicalOffset ComputeRelativeOffset(
const ComputedStyle& child_style,
WritingDirectionMode container_writing_direction,
const LogicalSize& available_size) {
if (child_style.GetPosition() != EPosition::kRelative)
return LogicalOffset();
const PhysicalSize physical_size = ToPhysicalSize(
available_size, container_writing_direction.GetWritingMode());
// Helper function to correctly resolve insets.
auto ResolveInset = [](const Length& length,
LayoutUnit size) -> std::optional<LayoutUnit> {
if (length.IsAuto())
return std::nullopt;
if (length.HasPercent() && size == kIndefiniteSize) {
return std::nullopt;
}
return MinimumValueForLength(length, size);
};
std::optional<LayoutUnit> left =
ResolveInset(child_style.Left(), physical_size.width);
std::optional<LayoutUnit> right =
ResolveInset(child_style.Right(), physical_size.width);
std::optional<LayoutUnit> top =
ResolveInset(child_style.Top(), physical_size.height);
std::optional<LayoutUnit> bottom =
ResolveInset(child_style.Bottom(), physical_size.height);
// Common case optimization.
if (!left && !right && !top && !bottom)
return LogicalOffset();
// Conflict resolution rules: https://www.w3.org/TR/css-position-3/#rel-pos
if (!left && !right) {
left = LayoutUnit();
right = LayoutUnit();
} else if (!left) {
left = -*right;
} else if (!right) {
right = -*left;
}
if (!top && !bottom) {
top = LayoutUnit();
bottom = LayoutUnit();
} else if (!top) {
top = -*bottom;
} else if (!bottom) {
bottom = -*top;
}
switch (container_writing_direction.GetWritingMode()) {
case WritingMode::kHorizontalTb:
return container_writing_direction.IsLtr() ? LogicalOffset(*left, *top)
: LogicalOffset(*right, *top);
case WritingMode::kVerticalRl:
case WritingMode::kSidewaysRl:
return container_writing_direction.IsLtr()
? LogicalOffset(*top, *right)
: LogicalOffset(*bottom, *right);
case WritingMode::kVerticalLr:
return container_writing_direction.IsLtr()
? LogicalOffset(*top, *left)
: LogicalOffset(*bottom, *left);
case WritingMode::kSidewaysLr:
return container_writing_direction.IsLtr() ? LogicalOffset(*bottom, *left)
: LogicalOffset(*top, *left);
default:
NOTREACHED();
}
}
LogicalOffset ComputeRelativeOffsetForBoxFragment(
const PhysicalBoxFragment& fragment,
WritingDirectionMode container_writing_direction,
const LogicalSize& available_size) {
const auto& child_style = fragment.Style();
DCHECK_EQ(child_style.GetPosition(), EPosition::kRelative);
return ComputeRelativeOffset(child_style, container_writing_direction,
available_size);
}
LogicalOffset ComputeRelativeOffsetForInline(const ConstraintSpace& space,
const ComputedStyle& child_style) {
if (child_style.GetPosition() != EPosition::kRelative)
return LogicalOffset();
// The confliction resolution rules work based off the block's writing-mode
// and direction, not the child's container. E.g.
// <span style="direction: rtl;">
// <span style="position: relative; left: 100px; right: -50px;"></span>
// </span>
// In the above example "left" wins.
const WritingDirectionMode writing_direction = space.GetWritingDirection();
LogicalOffset relative_offset = ComputeRelativeOffset(
child_style, writing_direction, space.AvailableSize());
// Lines are built in a line-logical coordinate system:
// https://drafts.csswg.org/css-writing-modes-3/#line-directions
// Reverse the offset direction if we are in a RTL, or flipped writing-mode.
if (writing_direction.IsRtl())
relative_offset.inline_offset = -relative_offset.inline_offset;
if (writing_direction.IsFlippedLines())
relative_offset.block_offset = -relative_offset.block_offset;
return relative_offset;
}
LogicalOffset ComputeRelativeOffsetForOOFInInline(
const ConstraintSpace& space,
const ComputedStyle& child_style) {
if (child_style.GetPosition() != EPosition::kRelative)
return LogicalOffset();
// The confliction resolution rules work based off the block's writing-mode
// and direction, not the child's container. E.g.
// <span style="direction: rtl;">
// <span style="position: relative; left: 100px; right: -50px;"></span>
// </span>
// In the above example "left" wins.
const WritingDirectionMode writing_direction = space.GetWritingDirection();
LogicalOffset relative_offset = ComputeRelativeOffset(
child_style, writing_direction, space.AvailableSize());
// Lines are built in a line-logical coordinate system:
// https://drafts.csswg.org/css-writing-modes-3/#line-directions
// Reverse the offset direction if we are in a RTL. We skip adjusting for
// flipped writing-mode when applying the relative position to an OOF
// positioned element.
if (writing_direction.IsRtl())
relative_offset.inline_offset = -relative_offset.inline_offset;
return relative_offset;
}
} // namespace blink
|