blob: 153484a7eb6718bc19c5df0cc1e1152db1b0898c [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/anchor_results.h"
#include "third_party/blink/renderer/core/dom/document.h"
#include "third_party/blink/renderer/core/style/scoped_css_name.h"
#include "third_party/blink/renderer/core/testing/page_test_base.h"
#include "third_party/blink/renderer/platform/geometry/calculation_expression_node.h"
namespace blink {
class AnchorResultsTest : public PageTestBase {
public:
struct Options {
STACK_ALLOCATED();
public:
AnchorScope::Mode mode = AnchorScope::Mode::kNone;
CSSAnchorQueryType query_type = CSSAnchorQueryType::kAnchor;
AnchorSpecifierValue::Type specifier_type =
AnchorSpecifierValue::Type::kDefault;
float percentage = 0;
AtomicString name;
TreeScope* tree_scope = nullptr;
absl::variant<CSSAnchorValue, CSSAnchorSizeValue> value =
CSSAnchorValue::kStart;
};
AnchorSpecifierValue* CreateAnchorSpecifierValue(
AnchorSpecifierValue::Type type,
AtomicString name,
TreeScope* tree_scope) {
switch (type) {
case AnchorSpecifierValue::Type::kDefault:
return AnchorSpecifierValue::Default();
case AnchorSpecifierValue::Type::kImplicit:
return AnchorSpecifierValue::Implicit();
case AnchorSpecifierValue::Type::kNamed:
return MakeGarbageCollected<AnchorSpecifierValue>(
*MakeGarbageCollected<ScopedCSSName>(name, tree_scope));
}
}
AnchorItem* CreateItem(Options options) {
return MakeGarbageCollected<AnchorItem>(
options.mode, AnchorQuery(options.query_type,
CreateAnchorSpecifierValue(
options.specifier_type, options.name,
options.tree_scope),
options.percentage, options.value));
}
unsigned ItemHash(Options options) { return CreateItem(options)->GetHash(); }
};
TEST_F(AnchorResultsTest, ItemEquality) {
EXPECT_EQ(*CreateItem(Options{}), *CreateItem(Options{}));
EXPECT_EQ(*CreateItem(Options{.mode = AnchorScope::Mode::kTop}),
*CreateItem(Options{.mode = AnchorScope::Mode::kTop}));
EXPECT_EQ(*CreateItem(Options{.query_type = CSSAnchorQueryType::kAnchorSize,
.value = CSSAnchorSizeValue::kWidth}),
*CreateItem(Options{.query_type = CSSAnchorQueryType::kAnchorSize,
.value = CSSAnchorSizeValue::kWidth}));
EXPECT_EQ(*CreateItem(Options{.specifier_type =
AnchorSpecifierValue::Type::kImplicit}),
*CreateItem(Options{.specifier_type =
AnchorSpecifierValue::Type::kImplicit}));
EXPECT_EQ(*CreateItem(Options{.percentage = 1.0f}),
*CreateItem(Options{.percentage = 1.0f}));
EXPECT_EQ(
*CreateItem(Options{.specifier_type = AnchorSpecifierValue::Type::kNamed,
.name = AtomicString("--foo")}),
*CreateItem(Options{.specifier_type = AnchorSpecifierValue::Type::kNamed,
.name = AtomicString("--foo")}));
ASSERT_TRUE(GetDocument().body());
EXPECT_EQ(
*CreateItem(Options{.specifier_type = AnchorSpecifierValue::Type::kNamed,
.name = g_empty_atom,
.tree_scope = &GetDocument()}),
*CreateItem(Options{.specifier_type = AnchorSpecifierValue::Type::kNamed,
.name = g_empty_atom,
.tree_scope = &GetDocument()}));
EXPECT_EQ(*CreateItem(Options{.value = CSSAnchorValue::kTop}),
*CreateItem(Options{.value = CSSAnchorValue::kTop}));
}
TEST_F(AnchorResultsTest, ItemInequality) {
EXPECT_NE(*CreateItem(Options{.query_type = CSSAnchorQueryType::kAnchorSize}),
*CreateItem(Options{}));
EXPECT_NE(*CreateItem(Options{.mode = AnchorScope::Mode::kTop}),
*CreateItem(Options{.mode = AnchorScope::Mode::kBottom}));
EXPECT_NE(*CreateItem(Options{.query_type = CSSAnchorQueryType::kAnchorSize,
.value = CSSAnchorSizeValue::kWidth}),
*CreateItem(Options{.query_type = CSSAnchorQueryType::kAnchor}));
EXPECT_NE(*CreateItem(Options{.specifier_type =
AnchorSpecifierValue::Type::kDefault}),
*CreateItem(Options{.specifier_type =
AnchorSpecifierValue::Type::kImplicit}));
EXPECT_NE(*CreateItem(Options{.percentage = 1.0f}),
*CreateItem(Options{.percentage = 2.0f}));
EXPECT_NE(
*CreateItem(Options{.specifier_type = AnchorSpecifierValue::Type::kNamed,
.name = AtomicString("--foo")}),
*CreateItem(Options{.specifier_type = AnchorSpecifierValue::Type::kNamed,
.name = AtomicString("--bar")}));
EXPECT_NE(
*CreateItem(Options{.specifier_type = AnchorSpecifierValue::Type::kNamed,
.name = g_empty_atom,
.tree_scope = &GetDocument()}),
*CreateItem(Options{.specifier_type = AnchorSpecifierValue::Type::kNamed,
.name = g_empty_atom,
.tree_scope = nullptr}));
EXPECT_NE(*CreateItem(Options{.value = CSSAnchorValue::kTop}),
*CreateItem(Options{.value = CSSAnchorValue::kLeft}));
EXPECT_NE(*CreateItem(Options{.value = CSSAnchorValue::kTop}),
*CreateItem(Options{.value = CSSAnchorSizeValue::kWidth}));
}
TEST_F(AnchorResultsTest, ItemHashEqual) {
EXPECT_EQ(ItemHash(Options{}), ItemHash(Options{}));
EXPECT_EQ(ItemHash(Options{.mode = AnchorScope::Mode::kTop}),
ItemHash(Options{.mode = AnchorScope::Mode::kTop}));
EXPECT_EQ(ItemHash(Options{.query_type = CSSAnchorQueryType::kAnchorSize,
.value = CSSAnchorSizeValue::kWidth}),
ItemHash(Options{.query_type = CSSAnchorQueryType::kAnchorSize,
.value = CSSAnchorSizeValue::kWidth}));
EXPECT_EQ(ItemHash(Options{.specifier_type =
AnchorSpecifierValue::Type::kImplicit}),
ItemHash(Options{.specifier_type =
AnchorSpecifierValue::Type::kImplicit}));
EXPECT_EQ(ItemHash(Options{.percentage = 1.0f}),
ItemHash(Options{.percentage = 1.0f}));
EXPECT_EQ(
ItemHash(Options{.specifier_type = AnchorSpecifierValue::Type::kNamed,
.name = AtomicString("--foo")}),
ItemHash(Options{.specifier_type = AnchorSpecifierValue::Type::kNamed,
.name = AtomicString("--foo")}));
ASSERT_TRUE(GetDocument().body());
EXPECT_EQ(
ItemHash(Options{.specifier_type = AnchorSpecifierValue::Type::kNamed,
.name = g_empty_atom,
.tree_scope = &GetDocument()}),
ItemHash(Options{.specifier_type = AnchorSpecifierValue::Type::kNamed,
.name = g_empty_atom,
.tree_scope = &GetDocument()}));
EXPECT_EQ(ItemHash(Options{.value = CSSAnchorValue::kTop}),
ItemHash(Options{.value = CSSAnchorValue::kTop}));
}
TEST_F(AnchorResultsTest, ItemHashNotEqual) {
EXPECT_NE(ItemHash(Options{.specifier_type =
AnchorSpecifierValue::Type::kImplicit}),
ItemHash(Options{}));
EXPECT_NE(ItemHash(Options{.mode = AnchorScope::Mode::kTop}),
ItemHash(Options{.mode = AnchorScope::Mode::kLeft}));
EXPECT_NE(ItemHash(Options{.query_type = CSSAnchorQueryType::kAnchorSize,
.value = CSSAnchorSizeValue::kWidth}),
ItemHash(Options{.query_type = CSSAnchorQueryType::kAnchor}));
EXPECT_NE(
ItemHash(Options{.specifier_type = AnchorSpecifierValue::Type::kDefault}),
ItemHash(
Options{.specifier_type = AnchorSpecifierValue::Type::kImplicit}));
EXPECT_NE(ItemHash(Options{.percentage = 1.0f}),
ItemHash(Options{.percentage = 2.0f}));
EXPECT_NE(
ItemHash(Options{.specifier_type = AnchorSpecifierValue::Type::kNamed,
.name = AtomicString("--foo")}),
ItemHash(Options{.specifier_type = AnchorSpecifierValue::Type::kNamed,
.name = AtomicString("--bar")}));
EXPECT_NE(
ItemHash(Options{.specifier_type = AnchorSpecifierValue::Type::kNamed,
.name = g_empty_atom,
.tree_scope = &GetDocument()}),
ItemHash(Options{.specifier_type = AnchorSpecifierValue::Type::kNamed,
.name = g_empty_atom,
.tree_scope = nullptr}));
EXPECT_NE(ItemHash(Options{.value = CSSAnchorValue::kTop}),
ItemHash(Options{.value = CSSAnchorValue::kLeft}));
EXPECT_NE(ItemHash(Options{.query_type = CSSAnchorQueryType::kAnchorSize,
.value = CSSAnchorSizeValue::kWidth}),
ItemHash(Options{.query_type = CSSAnchorQueryType::kAnchorSize,
.value = CSSAnchorSizeValue::kHeight}));
}
TEST_F(AnchorResultsTest, MapInsert) {
AnchorResultMap map;
const AnchorItem* item1 =
CreateItem(Options{.mode = AnchorScope::Mode::kSize,
.query_type = CSSAnchorQueryType::kAnchorSize,
.value = CSSAnchorSizeValue::kWidth});
const AnchorItem* item2 =
CreateItem(Options{.mode = AnchorScope::Mode::kSize,
.query_type = CSSAnchorQueryType::kAnchorSize,
.value = CSSAnchorSizeValue::kWidth});
const AnchorItem* item3 =
CreateItem(Options{.mode = AnchorScope::Mode::kSize,
.specifier_type = AnchorSpecifierValue::Type::kNamed,
.name = AtomicString("--foo")});
EXPECT_TRUE(map.empty());
map.Set(item1, LayoutUnit(42.0));
ASSERT_TRUE(map.Contains(item1));
ASSERT_TRUE(map.Contains(item2));
EXPECT_FALSE(map.Contains(item3));
EXPECT_EQ(LayoutUnit(42.0), map.at(item1));
EXPECT_EQ(LayoutUnit(42.0), map.at(item2));
map.Set(item2, std::nullopt);
ASSERT_TRUE(map.Contains(item1));
ASSERT_TRUE(map.Contains(item2));
EXPECT_FALSE(map.Contains(item3));
EXPECT_EQ(std::nullopt, map.at(item1));
EXPECT_EQ(std::nullopt, map.at(item2));
}
TEST_F(AnchorResultsTest, IsEmpty) {
AnchorResults results;
EXPECT_TRUE(results.IsEmpty());
}
TEST_F(AnchorResultsTest, IsNotEmpty) {
AnchorResults results;
results.Set(AnchorScope::Mode::kTop, CreateItem(Options{})->Query(),
LayoutUnit(42.0));
EXPECT_FALSE(results.IsEmpty());
}
TEST_F(AnchorResultsTest, IsAnyResultDifferent_NoDiff) {
AnchorResults results1;
results1.Set(AnchorScope::Mode::kTop, CreateItem(Options{})->Query(),
LayoutUnit(42.0));
AnchorResults results2;
results2.Set(AnchorScope::Mode::kTop, CreateItem(Options{})->Query(),
LayoutUnit(42.0));
EXPECT_FALSE(results1.IsAnyResultDifferent(&results2));
}
TEST_F(AnchorResultsTest, IsAnyResultDifferent_Empty) {
AnchorResults results1;
AnchorResults results2;
results2.Set(AnchorScope::Mode::kTop, CreateItem(Options{})->Query(),
LayoutUnit(42.0));
EXPECT_FALSE(results1.IsAnyResultDifferent(&results2));
}
TEST_F(AnchorResultsTest, IsAnyResultDifferent_Diff) {
AnchorResults results1;
results1.Set(AnchorScope::Mode::kTop, CreateItem(Options{})->Query(),
LayoutUnit(42.0));
AnchorResults results2;
results2.Set(AnchorScope::Mode::kTop, CreateItem(Options{})->Query(),
LayoutUnit(84.0));
EXPECT_TRUE(results1.IsAnyResultDifferent(&results2));
}
TEST_F(AnchorResultsTest, IsAnyResultDifferent_Missing) {
// Evaluating something causes AnchorResults to add an explicit nullopt
// for this item, making it no longer empty, and giving the subsequent
// call to IsAnyResultDifferent something to do.
AnchorResults results1;
{
AnchorScope anchor_scope(AnchorScope::Mode::kTop, &results1);
results1.Evaluate(CreateItem(Options{})->Query());
}
AnchorResults results2;
results2.Set(AnchorScope::Mode::kTop, CreateItem(Options{})->Query(),
LayoutUnit(42.0));
EXPECT_TRUE(results1.IsAnyResultDifferent(&results2));
}
TEST_F(AnchorResultsTest, Evaluate) {
AnchorResults results;
AnchorScope::Mode mode = AnchorScope::Mode::kSize;
const AnchorItem* item =
CreateItem(Options{.mode = mode,
.query_type = CSSAnchorQueryType::kAnchorSize,
.value = CSSAnchorSizeValue::kWidth});
results.Set(mode, item->Query(), LayoutUnit(42.0));
AnchorScope anchor_scope(mode, &results);
EXPECT_EQ(LayoutUnit(42.0), results.Evaluate(item->Query()));
}
TEST_F(AnchorResultsTest, EvaluateWrongMode) {
AnchorResults results;
AnchorScope::Mode mode = AnchorScope::Mode::kSize;
const AnchorItem* item =
CreateItem(Options{.mode = mode,
.query_type = CSSAnchorQueryType::kAnchorSize,
.value = CSSAnchorSizeValue::kWidth});
results.Set(mode, item->Query(), LayoutUnit(42.0));
AnchorScope anchor_scope(AnchorScope::Mode::kTop, &results);
EXPECT_EQ(std::nullopt, results.Evaluate(item->Query()));
}
} // namespace blink