blob: 666ed04d04fecf07d0995c9aa966a5ec760307be [file] [log] [blame]
// Copyright 2024 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/css/try_value_flips.h"
#include "third_party/blink/renderer/core/css/css_flip_revert_value.h"
#include "third_party/blink/renderer/core/css/css_property_value_set.h"
#include "third_party/blink/renderer/core/css/css_test_helpers.h"
#include "third_party/blink/renderer/core/css/try_tactic_transform.h"
#include "third_party/blink/renderer/core/testing/page_test_base.h"
#include "third_party/blink/renderer/platform/text/writing_direction_mode.h"
namespace blink {
constexpr TryTacticList Tactics(TryTactic t0,
TryTactic t1 = TryTactic::kNone,
TryTactic t2 = TryTactic::kNone) {
return TryTacticList{t0, t1, t2};
}
class TryValueFlipsTest : public PageTestBase {
public:
struct ExpectedFlips {
CSSPropertyID inset_block_start = CSSPropertyID::kInsetBlockStart;
CSSPropertyID inset_block_end = CSSPropertyID::kInsetBlockEnd;
CSSPropertyID inset_inline_start = CSSPropertyID::kInsetInlineStart;
CSSPropertyID inset_inline_end = CSSPropertyID::kInsetInlineEnd;
CSSPropertyID margin_block_start = CSSPropertyID::kMarginBlockStart;
CSSPropertyID margin_block_end = CSSPropertyID::kMarginBlockEnd;
CSSPropertyID margin_inline_start = CSSPropertyID::kMarginInlineStart;
CSSPropertyID margin_inline_end = CSSPropertyID::kMarginInlineEnd;
CSSPropertyID align_self = CSSPropertyID::kAlignSelf;
CSSPropertyID justify_self = CSSPropertyID::kJustifySelf;
CSSPropertyID block_size = CSSPropertyID::kBlockSize;
CSSPropertyID inline_size = CSSPropertyID::kInlineSize;
CSSPropertyID min_block_size = CSSPropertyID::kMinBlockSize;
CSSPropertyID min_inline_size = CSSPropertyID::kMinInlineSize;
CSSPropertyID max_block_size = CSSPropertyID::kMaxBlockSize;
CSSPropertyID max_inline_size = CSSPropertyID::kMaxInlineSize;
};
// Creates a CSSPropertyValueSet that contains CSSFlipRevertValue
// for each declarations in `flips` that actually represents a flip
// (i.e. doesn't just flip to itself).
const CSSPropertyValueSet* ExpectedFlipsSet(ExpectedFlips flips) {
auto* set =
MakeGarbageCollected<MutableCSSPropertyValueSet>(kHTMLStandardMode);
auto add = [set](CSSPropertyID from, CSSPropertyID to) {
set->SetProperty(from,
*MakeGarbageCollected<cssvalue::CSSFlipRevertValue>(
to, TryTacticTransform()));
};
auto add_if_flipped = [&add](CSSPropertyID from, CSSPropertyID to) {
if (from != to) {
add(from, to);
}
};
add_if_flipped(CSSPropertyID::kInsetBlockStart, flips.inset_block_start);
add_if_flipped(CSSPropertyID::kInsetBlockEnd, flips.inset_block_end);
add_if_flipped(CSSPropertyID::kInsetInlineStart, flips.inset_inline_start);
add_if_flipped(CSSPropertyID::kInsetInlineEnd, flips.inset_inline_end);
add_if_flipped(CSSPropertyID::kMarginBlockStart, flips.margin_block_start);
add_if_flipped(CSSPropertyID::kMarginBlockEnd, flips.margin_block_end);
add_if_flipped(CSSPropertyID::kMarginInlineStart,
flips.margin_inline_start);
add_if_flipped(CSSPropertyID::kMarginInlineEnd, flips.margin_inline_end);
add(CSSPropertyID::kAlignSelf, flips.align_self);
add(CSSPropertyID::kJustifySelf, flips.justify_self);
add(CSSPropertyID::kInsetArea, CSSPropertyID::kInsetArea);
add_if_flipped(CSSPropertyID::kBlockSize, flips.block_size);
add_if_flipped(CSSPropertyID::kInlineSize, flips.inline_size);
add_if_flipped(CSSPropertyID::kMinBlockSize, flips.min_block_size);
add_if_flipped(CSSPropertyID::kMinInlineSize, flips.min_inline_size);
add_if_flipped(CSSPropertyID::kMaxBlockSize, flips.max_block_size);
add_if_flipped(CSSPropertyID::kMaxInlineSize, flips.max_inline_size);
return set;
}
// Serializes the declarations of `set` into a vector. AsText is not used,
// because it shorthandifies the declarations, which is not helpful
// for debugging failing tests.
Vector<String> DeclarationStrings(const CSSPropertyValueSet* set) {
Vector<String> result;
for (unsigned i = 0; i < set->PropertyCount(); ++i) {
CSSPropertyValueSet::PropertyReference ref = set->PropertyAt(i);
result.push_back(ref.Name().ToAtomicString() + ":" +
ref.Value().CssText());
}
return result;
}
Vector<String> ExpectedFlipsVector(ExpectedFlips flips) {
return DeclarationStrings(ExpectedFlipsSet(flips));
}
Vector<String> ActualFlipsVector(const TryTacticList& tactic_list) {
TryValueFlips flips;
return DeclarationStrings(flips.FlipSet(tactic_list));
}
};
TEST_F(TryValueFlipsTest, None) {
TryValueFlips flips;
EXPECT_FALSE(flips.FlipSet(Tactics(TryTactic::kNone)));
}
// Flips without kFlipStart:
TEST_F(TryValueFlipsTest, FlipBlock) {
EXPECT_EQ(ExpectedFlipsVector(ExpectedFlips{
.inset_block_start = CSSPropertyID::kInsetBlockEnd,
.inset_block_end = CSSPropertyID::kInsetBlockStart,
.margin_block_start = CSSPropertyID::kMarginBlockEnd,
.margin_block_end = CSSPropertyID::kMarginBlockStart,
}),
ActualFlipsVector(Tactics(TryTactic::kFlipBlock)));
}
TEST_F(TryValueFlipsTest, FlipInline) {
EXPECT_EQ(ExpectedFlipsVector(ExpectedFlips{
.inset_inline_start = CSSPropertyID::kInsetInlineEnd,
.inset_inline_end = CSSPropertyID::kInsetInlineStart,
.margin_inline_start = CSSPropertyID::kMarginInlineEnd,
.margin_inline_end = CSSPropertyID::kMarginInlineStart,
}),
ActualFlipsVector(Tactics(TryTactic::kFlipInline)));
}
TEST_F(TryValueFlipsTest, FlipBlockInline) {
EXPECT_EQ(ExpectedFlipsVector(ExpectedFlips{
.inset_block_start = CSSPropertyID::kInsetBlockEnd,
.inset_block_end = CSSPropertyID::kInsetBlockStart,
.inset_inline_start = CSSPropertyID::kInsetInlineEnd,
.inset_inline_end = CSSPropertyID::kInsetInlineStart,
.margin_block_start = CSSPropertyID::kMarginBlockEnd,
.margin_block_end = CSSPropertyID::kMarginBlockStart,
.margin_inline_start = CSSPropertyID::kMarginInlineEnd,
.margin_inline_end = CSSPropertyID::kMarginInlineStart,
}),
ActualFlipsVector(
Tactics(TryTactic::kFlipBlock, TryTactic::kFlipInline)));
}
TEST_F(TryValueFlipsTest, FlipInlineBlock) {
EXPECT_EQ(
ActualFlipsVector(Tactics(TryTactic::kFlipBlock, TryTactic::kFlipInline)),
ActualFlipsVector(
Tactics(TryTactic::kFlipInline, TryTactic::kFlipBlock)));
}
// Flips with kFlipStart:
TEST_F(TryValueFlipsTest, FlipStart) {
EXPECT_EQ(
ExpectedFlipsVector(ExpectedFlips{
.inset_block_start = CSSPropertyID::kInsetInlineStart,
.inset_block_end = CSSPropertyID::kInsetInlineEnd,
.inset_inline_start = CSSPropertyID::kInsetBlockStart,
.inset_inline_end = CSSPropertyID::kInsetBlockEnd,
.margin_block_start = CSSPropertyID::kMarginInlineStart,
.margin_block_end = CSSPropertyID::kMarginInlineEnd,
.margin_inline_start = CSSPropertyID::kMarginBlockStart,
.margin_inline_end = CSSPropertyID::kMarginBlockEnd,
// Flipped alignment:
.align_self = CSSPropertyID::kJustifySelf,
.justify_self = CSSPropertyID::kAlignSelf,
// Flipped sizing:
.block_size = CSSPropertyID::kInlineSize,
.inline_size = CSSPropertyID::kBlockSize,
.min_block_size = CSSPropertyID::kMinInlineSize,
.min_inline_size = CSSPropertyID::kMinBlockSize,
.max_block_size = CSSPropertyID::kMaxInlineSize,
.max_inline_size = CSSPropertyID::kMaxBlockSize,
}),
ActualFlipsVector(Tactics(TryTactic::kFlipBlock, TryTactic::kFlipStart,
TryTactic::kFlipInline)));
}
TEST_F(TryValueFlipsTest, FlipBlockStartInline) {
EXPECT_EQ(
ActualFlipsVector(Tactics(TryTactic::kFlipStart)),
ActualFlipsVector(Tactics(TryTactic::kFlipBlock, TryTactic::kFlipStart,
TryTactic::kFlipInline)));
}
TEST_F(TryValueFlipsTest, FlipInlineStartBlock) {
EXPECT_EQ(
ActualFlipsVector(Tactics(TryTactic::kFlipStart)),
ActualFlipsVector(Tactics(TryTactic::kFlipInline, TryTactic::kFlipStart,
TryTactic::kFlipBlock)));
}
TEST_F(TryValueFlipsTest, FlipStartBlock) {
EXPECT_EQ(
ExpectedFlipsVector(ExpectedFlips{
.inset_block_start = CSSPropertyID::kInsetInlineEnd,
.inset_block_end = CSSPropertyID::kInsetInlineStart,
.inset_inline_start = CSSPropertyID::kInsetBlockStart,
.inset_inline_end = CSSPropertyID::kInsetBlockEnd,
.margin_block_start = CSSPropertyID::kMarginInlineEnd,
.margin_block_end = CSSPropertyID::kMarginInlineStart,
.margin_inline_start = CSSPropertyID::kMarginBlockStart,
.margin_inline_end = CSSPropertyID::kMarginBlockEnd,
// Flipped alignment:
.align_self = CSSPropertyID::kJustifySelf,
.justify_self = CSSPropertyID::kAlignSelf,
// Flipped sizing:
.block_size = CSSPropertyID::kInlineSize,
.inline_size = CSSPropertyID::kBlockSize,
.min_block_size = CSSPropertyID::kMinInlineSize,
.min_inline_size = CSSPropertyID::kMinBlockSize,
.max_block_size = CSSPropertyID::kMaxInlineSize,
.max_inline_size = CSSPropertyID::kMaxBlockSize,
}),
ActualFlipsVector(Tactics(TryTactic::kFlipStart, TryTactic::kFlipBlock)));
}
TEST_F(TryValueFlipsTest, FlipInlineStart) {
EXPECT_EQ(
ActualFlipsVector(Tactics(TryTactic::kFlipStart, TryTactic::kFlipBlock)),
ActualFlipsVector(
Tactics(TryTactic::kFlipInline, TryTactic::kFlipStart)));
}
TEST_F(TryValueFlipsTest, FlipStartInline) {
EXPECT_EQ(ExpectedFlipsVector(ExpectedFlips{
.inset_block_start = CSSPropertyID::kInsetInlineStart,
.inset_block_end = CSSPropertyID::kInsetInlineEnd,
.inset_inline_start = CSSPropertyID::kInsetBlockEnd,
.inset_inline_end = CSSPropertyID::kInsetBlockStart,
.margin_block_start = CSSPropertyID::kMarginInlineStart,
.margin_block_end = CSSPropertyID::kMarginInlineEnd,
.margin_inline_start = CSSPropertyID::kMarginBlockEnd,
.margin_inline_end = CSSPropertyID::kMarginBlockStart,
// Flipped alignment:
.align_self = CSSPropertyID::kJustifySelf,
.justify_self = CSSPropertyID::kAlignSelf,
// Flipped sizing:
.block_size = CSSPropertyID::kInlineSize,
.inline_size = CSSPropertyID::kBlockSize,
.min_block_size = CSSPropertyID::kMinInlineSize,
.min_inline_size = CSSPropertyID::kMinBlockSize,
.max_block_size = CSSPropertyID::kMaxInlineSize,
.max_inline_size = CSSPropertyID::kMaxBlockSize,
}),
ActualFlipsVector(
Tactics(TryTactic::kFlipStart, TryTactic::kFlipInline)));
}
TEST_F(TryValueFlipsTest, FlipBlockStart) {
EXPECT_EQ(
ActualFlipsVector(Tactics(TryTactic::kFlipStart, TryTactic::kFlipInline)),
ActualFlipsVector(Tactics(TryTactic::kFlipBlock, TryTactic::kFlipStart)));
}
TEST_F(TryValueFlipsTest, FlipStartBlockInline) {
EXPECT_EQ(
ExpectedFlipsVector(ExpectedFlips{
.inset_block_start = CSSPropertyID::kInsetInlineEnd,
.inset_block_end = CSSPropertyID::kInsetInlineStart,
.inset_inline_start = CSSPropertyID::kInsetBlockEnd,
.inset_inline_end = CSSPropertyID::kInsetBlockStart,
.margin_block_start = CSSPropertyID::kMarginInlineEnd,
.margin_block_end = CSSPropertyID::kMarginInlineStart,
.margin_inline_start = CSSPropertyID::kMarginBlockEnd,
.margin_inline_end = CSSPropertyID::kMarginBlockStart,
// Flipped alignment:
.align_self = CSSPropertyID::kJustifySelf,
.justify_self = CSSPropertyID::kAlignSelf,
// Flipped sizing:
.block_size = CSSPropertyID::kInlineSize,
.inline_size = CSSPropertyID::kBlockSize,
.min_block_size = CSSPropertyID::kMinInlineSize,
.min_inline_size = CSSPropertyID::kMinBlockSize,
.max_block_size = CSSPropertyID::kMaxInlineSize,
.max_inline_size = CSSPropertyID::kMaxBlockSize,
}),
ActualFlipsVector(Tactics(TryTactic::kFlipStart, TryTactic::kFlipBlock,
TryTactic::kFlipInline)));
}
TEST_F(TryValueFlipsTest, FlipStartInlineBlock) {
EXPECT_EQ(
ActualFlipsVector(Tactics(TryTactic::kFlipStart, TryTactic::kFlipBlock,
TryTactic::kFlipInline)),
ActualFlipsVector(Tactics(TryTactic::kFlipStart, TryTactic::kFlipInline,
TryTactic::kFlipBlock)));
}
TEST_F(TryValueFlipsTest, FlipInlineBlockStart) {
EXPECT_EQ(
ActualFlipsVector(Tactics(TryTactic::kFlipStart, TryTactic::kFlipBlock,
TryTactic::kFlipInline)),
ActualFlipsVector(Tactics(TryTactic::kFlipInline, TryTactic::kFlipBlock,
TryTactic::kFlipStart)));
}
TEST_F(TryValueFlipsTest, FlipBlockInlineStart) {
EXPECT_EQ(
ActualFlipsVector(Tactics(TryTactic::kFlipStart, TryTactic::kFlipBlock,
TryTactic::kFlipInline)),
ActualFlipsVector(Tactics(TryTactic::kFlipBlock, TryTactic::kFlipInline,
TryTactic::kFlipStart)));
}
namespace {
struct Declaration {
STACK_ALLOCATED();
public:
CSSPropertyID property_id;
const CSSValue* value;
};
Declaration ParseDeclaration(String string) {
const CSSPropertyValueSet* set =
css_test_helpers::ParseDeclarationBlock(string);
CHECK(set);
CHECK_EQ(1u, set->PropertyCount());
CSSPropertyValueSet::PropertyReference ref = set->PropertyAt(0);
return Declaration{.property_id = ref.Name().Id(), .value = &ref.Value()};
}
} // namespace
struct FlipValueTestData {
const char* input;
const char* expected;
TryTacticList tactic;
WritingDirectionMode writing_direction =
WritingDirectionMode(WritingMode::kHorizontalTb, TextDirection::kLtr);
};
FlipValueTestData flip_value_test_data[] = {
// clang-format off
// Possible transforms (from try_tactic_transforms.h):
//
// block (1)
// inline (2)
// block inline (3)
// start (4)
// block start (5)
// inline start (6)
// block inline start (7)
// Physical anchor():
// (1)
{
.input = "left:anchor(right)",
.expected = "left:anchor(right)",
.tactic = Tactics(TryTactic::kFlipBlock)
},
{
.input = "right:anchor(left)",
.expected = "right:anchor(left)",
.tactic = Tactics(TryTactic::kFlipBlock)
},
{
.input = "top:anchor(bottom)",
.expected = "bottom:anchor(top)",
.tactic = Tactics(TryTactic::kFlipBlock)
},
{
.input = "bottom:anchor(top)",
.expected = "top:anchor(bottom)",
.tactic = Tactics(TryTactic::kFlipBlock)
},
// (2)
{
.input = "left:anchor(right)",
.expected = "right:anchor(left)",
.tactic = Tactics(TryTactic::kFlipInline)
},
{
.input = "right:anchor(left)",
.expected = "left:anchor(right)",
.tactic = Tactics(TryTactic::kFlipInline)
},
{
.input = "top:anchor(bottom)",
.expected = "top:anchor(bottom)",
.tactic = Tactics(TryTactic::kFlipInline)
},
{
.input = "bottom:anchor(top)",
.expected = "bottom:anchor(top)",
.tactic = Tactics(TryTactic::kFlipInline)
},
// (3)
{
.input = "left:anchor(right)",
.expected = "right:anchor(left)",
.tactic = Tactics(TryTactic::kFlipBlock, TryTactic::kFlipInline)
},
{
.input = "right:anchor(left)",
.expected = "left:anchor(right)",
.tactic = Tactics(TryTactic::kFlipBlock, TryTactic::kFlipInline)
},
{
.input = "top:anchor(bottom)",
.expected = "bottom:anchor(top)",
.tactic = Tactics(TryTactic::kFlipBlock, TryTactic::kFlipInline)
},
{
.input = "bottom:anchor(top)",
.expected = "top:anchor(bottom)",
.tactic = Tactics(TryTactic::kFlipBlock, TryTactic::kFlipInline)
},
// (4)
{
.input = "left:anchor(right)",
.expected = "top:anchor(bottom)",
.tactic = Tactics(TryTactic::kFlipStart)
},
{
.input = "right:anchor(left)",
.expected = "bottom:anchor(top)",
.tactic = Tactics(TryTactic::kFlipStart)
},
{
.input = "top:anchor(bottom)",
.expected = "left:anchor(right)",
.tactic = Tactics(TryTactic::kFlipStart)
},
{
.input = "bottom:anchor(top)",
.expected = "right:anchor(left)",
.tactic = Tactics(TryTactic::kFlipStart)
},
// (5)
{
.input = "left:anchor(right)",
.expected = "top:anchor(bottom)",
.tactic = Tactics(TryTactic::kFlipBlock, TryTactic::kFlipStart)
},
{
.input = "right:anchor(left)",
.expected = "bottom:anchor(top)",
.tactic = Tactics(TryTactic::kFlipBlock, TryTactic::kFlipStart)
},
{
.input = "top:anchor(bottom)",
.expected = "right:anchor(left)",
.tactic = Tactics(TryTactic::kFlipBlock, TryTactic::kFlipStart)
},
{
.input = "bottom:anchor(top)",
.expected = "left:anchor(right)",
.tactic = Tactics(TryTactic::kFlipBlock, TryTactic::kFlipStart)
},
// (6)
{
.input = "left:anchor(right)",
.expected = "bottom:anchor(top)",
.tactic = Tactics(TryTactic::kFlipInline, TryTactic::kFlipStart)
},
{
.input = "right:anchor(left)",
.expected = "top:anchor(bottom)",
.tactic = Tactics(TryTactic::kFlipInline, TryTactic::kFlipStart)
},
{
.input = "top:anchor(bottom)",
.expected = "left:anchor(right)",
.tactic = Tactics(TryTactic::kFlipInline, TryTactic::kFlipStart)
},
{
.input = "bottom:anchor(top)",
.expected = "right:anchor(left)",
.tactic = Tactics(TryTactic::kFlipInline, TryTactic::kFlipStart)
},
// (7)
{
.input = "left:anchor(right)",
.expected = "bottom:anchor(top)",
.tactic = Tactics(TryTactic::kFlipBlock,
TryTactic::kFlipInline,
TryTactic::kFlipStart)
},
{
.input = "right:anchor(left)",
.expected = "top:anchor(bottom)",
.tactic = Tactics(TryTactic::kFlipBlock,
TryTactic::kFlipInline,
TryTactic::kFlipStart)
},
{
.input = "top:anchor(bottom)",
.expected = "right:anchor(left)",
.tactic = Tactics(TryTactic::kFlipBlock,
TryTactic::kFlipInline,
TryTactic::kFlipStart)
},
{
.input = "bottom:anchor(top)",
.expected = "left:anchor(right)",
.tactic = Tactics(TryTactic::kFlipBlock,
TryTactic::kFlipInline,
TryTactic::kFlipStart)
},
// Logical anchor():
// (1)
{
.input = "left:anchor(end)",
.expected = "left:anchor(end)",
.tactic = Tactics(TryTactic::kFlipBlock)
},
{
.input = "right:anchor(start)",
.expected = "right:anchor(start)",
.tactic = Tactics(TryTactic::kFlipBlock)
},
{
.input = "top:anchor(end)",
.expected = "bottom:anchor(start)",
.tactic = Tactics(TryTactic::kFlipBlock)
},
{
.input = "bottom:anchor(start)",
.expected = "top:anchor(end)",
.tactic = Tactics(TryTactic::kFlipBlock)
},
// (2)
{
.input = "left:anchor(end)",
.expected = "right:anchor(start)",
.tactic = Tactics(TryTactic::kFlipInline)
},
{
.input = "right:anchor(start)",
.expected = "left:anchor(end)",
.tactic = Tactics(TryTactic::kFlipInline)
},
{
.input = "top:anchor(end)",
.expected = "top:anchor(end)",
.tactic = Tactics(TryTactic::kFlipInline)
},
{
.input = "bottom:anchor(start)",
.expected = "bottom:anchor(start)",
.tactic = Tactics(TryTactic::kFlipInline)
},
// (3)
{
.input = "left:anchor(end)",
.expected = "right:anchor(start)",
.tactic = Tactics(TryTactic::kFlipBlock, TryTactic::kFlipInline)
},
{
.input = "right:anchor(start)",
.expected = "left:anchor(end)",
.tactic = Tactics(TryTactic::kFlipBlock, TryTactic::kFlipInline)
},
{
.input = "top:anchor(end)",
.expected = "bottom:anchor(start)",
.tactic = Tactics(TryTactic::kFlipBlock, TryTactic::kFlipInline)
},
{
.input = "bottom:anchor(start)",
.expected = "top:anchor(end)",
.tactic = Tactics(TryTactic::kFlipBlock, TryTactic::kFlipInline)
},
// (4)
{
.input = "left:anchor(end)",
.expected = "top:anchor(end)",
.tactic = Tactics(TryTactic::kFlipStart)
},
{
.input = "right:anchor(start)",
.expected = "bottom:anchor(start)",
.tactic = Tactics(TryTactic::kFlipStart)
},
{
.input = "top:anchor(end)",
.expected = "left:anchor(end)",
.tactic = Tactics(TryTactic::kFlipStart)
},
{
.input = "bottom:anchor(start)",
.expected = "right:anchor(start)",
.tactic = Tactics(TryTactic::kFlipStart)
},
// (5)
{
.input = "left:anchor(end)",
.expected = "top:anchor(end)",
.tactic = Tactics(TryTactic::kFlipBlock, TryTactic::kFlipStart)
},
{
.input = "right:anchor(start)",
.expected = "bottom:anchor(start)",
.tactic = Tactics(TryTactic::kFlipBlock, TryTactic::kFlipStart)
},
{
.input = "top:anchor(end)",
.expected = "right:anchor(start)",
.tactic = Tactics(TryTactic::kFlipBlock, TryTactic::kFlipStart)
},
{
.input = "bottom:anchor(start)",
.expected = "left:anchor(end)",
.tactic = Tactics(TryTactic::kFlipBlock, TryTactic::kFlipStart)
},
// (6)
{
.input = "left:anchor(end)",
.expected = "bottom:anchor(start)",
.tactic = Tactics(TryTactic::kFlipInline, TryTactic::kFlipStart)
},
{
.input = "right:anchor(start)",
.expected = "top:anchor(end)",
.tactic = Tactics(TryTactic::kFlipInline, TryTactic::kFlipStart)
},
{
.input = "top:anchor(end)",
.expected = "left:anchor(end)",
.tactic = Tactics(TryTactic::kFlipInline, TryTactic::kFlipStart)
},
{
.input = "bottom:anchor(start)",
.expected = "right:anchor(start)",
.tactic = Tactics(TryTactic::kFlipInline, TryTactic::kFlipStart)
},
// (7)
{
.input = "left:anchor(end)",
.expected = "bottom:anchor(start)",
.tactic = Tactics(TryTactic::kFlipBlock,
TryTactic::kFlipInline,
TryTactic::kFlipStart)
},
{
.input = "right:anchor(start)",
.expected = "top:anchor(end)",
.tactic = Tactics(TryTactic::kFlipBlock,
TryTactic::kFlipInline,
TryTactic::kFlipStart)
},
{
.input = "top:anchor(end)",
.expected = "right:anchor(start)",
.tactic = Tactics(TryTactic::kFlipBlock,
TryTactic::kFlipInline,
TryTactic::kFlipStart)
},
{
.input = "bottom:anchor(start)",
.expected = "left:anchor(end)",
.tactic = Tactics(TryTactic::kFlipBlock,
TryTactic::kFlipInline,
TryTactic::kFlipStart)
},
// Physical anchor-size()
// (1)
{
.input = "width:anchor-size(width)",
.expected = "width:anchor-size(width)",
.tactic = Tactics(TryTactic::kFlipBlock)
},
// (2)
{
.input = "width:anchor-size(width)",
.expected = "width:anchor-size(width)",
.tactic = Tactics(TryTactic::kFlipInline)
},
// (3)
{
.input = "width:anchor-size(width)",
.expected = "width:anchor-size(width)",
.tactic = Tactics(TryTactic::kFlipBlock, TryTactic::kFlipInline)
},
// (4)
{
.input = "width:anchor-size(width)",
.expected = "height:anchor-size(height)",
.tactic = Tactics(TryTactic::kFlipStart)
},
// (5)
{
.input = "width:anchor-size(width)",
.expected = "height:anchor-size(height)",
.tactic = Tactics(TryTactic::kFlipStart)
},
// (6)
{
.input = "width:anchor-size(width)",
.expected = "height:anchor-size(height)",
.tactic = Tactics(TryTactic::kFlipStart)
},
// (7)
{
.input = "width:anchor-size(width)",
.expected = "height:anchor-size(height)",
.tactic = Tactics(TryTactic::kFlipStart)
},
// Logical anchor-size():
// (1)
{
.input = "width:anchor-size(inline)",
.expected = "width:anchor-size(inline)",
.tactic = Tactics(TryTactic::kFlipBlock)
},
// (2)
{
.input = "width:anchor-size(inline)",
.expected = "width:anchor-size(inline)",
.tactic = Tactics(TryTactic::kFlipInline)
},
// (3)
{
.input = "width:anchor-size(inline)",
.expected = "width:anchor-size(inline)",
.tactic = Tactics(TryTactic::kFlipBlock, TryTactic::kFlipInline)
},
// (4)
{
.input = "width:anchor-size(inline)",
.expected = "height:anchor-size(block)",
.tactic = Tactics(TryTactic::kFlipStart)
},
// (5)
{
.input = "width:anchor-size(inline)",
.expected = "height:anchor-size(block)",
.tactic = Tactics(TryTactic::kFlipStart)
},
// (6)
{
.input = "width:anchor-size(inline)",
.expected = "height:anchor-size(block)",
.tactic = Tactics(TryTactic::kFlipStart)
},
// (7)
{
.input = "width:anchor-size(inline)",
.expected = "height:anchor-size(block)",
.tactic = Tactics(TryTactic::kFlipStart)
},
// calc() expressions, etc:
{
.input = "left:calc(anchor(left) + 10px)",
.expected = "right:calc(anchor(right) + 10px)",
.tactic = Tactics(TryTactic::kFlipInline)
},
{
.input = "left:calc(min(anchor(left), anchor(right), 50px) + 10px)",
.expected = "right:calc(min(anchor(right), anchor(left), 50px) + 10px)",
.tactic = Tactics(TryTactic::kFlipInline)
},
{
.input = "left:calc(anchor(left, anchor(right)) + 10px)",
.expected = "right:calc(anchor(right, anchor(left)) + 10px)",
.tactic = Tactics(TryTactic::kFlipInline)
},
// Writing modes:
{
.input = "left:anchor(left)",
.expected = "right:anchor(right)",
.tactic = Tactics(TryTactic::kFlipInline),
.writing_direction = WritingDirectionMode(
WritingMode::kHorizontalTb, TextDirection::kRtl)
},
{
.input = "right:anchor(right)",
.expected = "left:anchor(left)",
.tactic = Tactics(TryTactic::kFlipInline),
.writing_direction = WritingDirectionMode(
WritingMode::kHorizontalTb, TextDirection::kRtl)
},
{
.input = "left:anchor(left)",
.expected = "right:anchor(right)",
.tactic = Tactics(TryTactic::kFlipBlock),
.writing_direction = WritingDirectionMode(
WritingMode::kVerticalLr, TextDirection::kLtr)
},
{
.input = "left:anchor(left)",
.expected = "top:anchor(top)",
.tactic = Tactics(TryTactic::kFlipBlock, TryTactic::kFlipStart),
.writing_direction = WritingDirectionMode(
WritingMode::kVerticalRl, TextDirection::kLtr)
},
// clang-format on
};
class FlipValueTest : public PageTestBase,
public testing::WithParamInterface<FlipValueTestData> {};
INSTANTIATE_TEST_SUITE_P(TryValueFlipsTest,
FlipValueTest,
testing::ValuesIn(flip_value_test_data));
TEST_P(FlipValueTest, All) {
FlipValueTestData param = GetParam();
Declaration input = ParseDeclaration(String(param.input));
Declaration expected = ParseDeclaration(String(param.expected));
TryTacticTransform transform = TryTacticTransform(param.tactic);
const CSSValue* actual_value = TryValueFlips::FlipValue(
input.property_id, input.value, transform, param.writing_direction);
ASSERT_TRUE(actual_value);
EXPECT_EQ(expected.value->CssText(), actual_value->CssText());
}
struct NoFlipValueTestData {
const char* input;
TryTacticList tactic;
WritingDirectionMode writing_direction =
WritingDirectionMode(WritingMode::kHorizontalTb, TextDirection::kLtr);
};
// These cases should cause TryValueFlips::FlipValue to return
// the incoming CSSValue instance.
NoFlipValueTestData no_flip_value_test_data[] = {
// clang-format off
{
.input = "left:10px",
.tactic = Tactics(TryTactic::kNone)
},
{
.input = "left:calc(10px + 20px)",
.tactic = Tactics(TryTactic::kNone)
},
{
.input = "left:min(10px, 20px)",
.tactic = Tactics(TryTactic::kNone)
},
{
.input = "left:anchor(left)",
.tactic = Tactics(TryTactic::kFlipBlock)
},
{
.input = "left:anchor(start)",
.tactic = Tactics(TryTactic::kFlipBlock)
},
{
.input = "top:anchor(start)",
.tactic = Tactics(TryTactic::kFlipInline)
},
{
.input = "left:anchor(self-start)",
.tactic = Tactics(TryTactic::kFlipBlock)
},
{
.input = "top:anchor(self-start)",
.tactic = Tactics(TryTactic::kFlipInline)
},
{
.input = "left:calc(anchor(left) + 10px)",
.tactic = Tactics(TryTactic::kFlipBlock)
},
{
.input = "left:calc(anchor(left) + 10px)",
.tactic = Tactics(TryTactic::kFlipBlock)
},
{
.input = "left:calc(anchor(start) + 10px)",
.tactic = Tactics(TryTactic::kFlipStart)
},
{
.input = "width:anchor-size(width)",
.tactic = Tactics(TryTactic::kNone)
},
{
.input = "width:anchor-size(width)",
.tactic = Tactics(TryTactic::kFlipBlock)
},
{
.input = "width:calc(anchor-size(width) + anchor-size(height))",
.tactic = Tactics(TryTactic::kFlipInline)
},
// clang-format on
};
class NoFlipValueTest
: public PageTestBase,
public testing::WithParamInterface<NoFlipValueTestData> {};
INSTANTIATE_TEST_SUITE_P(TryValueFlipsTest,
NoFlipValueTest,
testing::ValuesIn(no_flip_value_test_data));
TEST_P(NoFlipValueTest, All) {
NoFlipValueTestData param = GetParam();
Declaration input = ParseDeclaration(String(param.input));
TryTacticTransform transform = TryTacticTransform(param.tactic);
const CSSValue* actual_value = TryValueFlips::FlipValue(
input.property_id, input.value, transform, param.writing_direction);
ASSERT_TRUE(actual_value);
SCOPED_TRACE(testing::Message() << "Actual: " << actual_value->CssText());
SCOPED_TRACE(testing::Message() << "Expected: " << input.value->CssText());
EXPECT_EQ(input.value, actual_value);
}
} // namespace blink