blob: aa70c9ab7b51423678c4c195225eac06282e5df9 [file] [log] [blame]
// Copyright 2021 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 "third_party/blink/renderer/core/layout/list_marker.h"
#include "third_party/blink/renderer/core/layout/ng/list/layout_ng_list_item.h"
#include "third_party/blink/renderer/core/layout/ng/ng_layout_test.h"
namespace blink {
// We don't test legacy layout because it's deprecated, and we don't want to
// complicate the test with the legacy LayoutListMarker here.
class ListMarkerTest : public NGLayoutTest {
protected:
LayoutObject* GetMarker(const char* list_item_id) {
LayoutNGListItem* list_item =
To<LayoutNGListItem>(GetLayoutObjectByElementId(list_item_id));
return list_item->Marker();
}
LayoutObject* GetMarker(TreeScope& scope, const char* list_item_id) {
Element* list_item = scope.getElementById(list_item_id);
return To<LayoutNGListItem>(list_item->GetLayoutObject())->Marker();
}
String GetMarkerText(TreeScope& scope, const char* list_item_id) {
return To<LayoutText>(GetMarker(scope, list_item_id)->SlowFirstChild())
->GetText();
}
String GetMarkerText(const char* list_item_id) {
return GetMarkerText(GetDocument(), list_item_id);
}
void AddCounterStyle(const AtomicString& name, const String& descriptors) {
StringBuilder declaration;
declaration.Append("@counter-style ");
declaration.Append(name);
declaration.Append("{");
declaration.Append(descriptors);
declaration.Append("}");
Element* sheet = GetDocument().CreateElementForBinding("style");
sheet->setInnerHTML(declaration.ToString());
GetDocument().body()->appendChild(sheet);
}
};
TEST_F(ListMarkerTest, AddCounterStyle) {
GetDocument().body()->setInnerHTML(R"HTML(
<style>
@counter-style foo {
system: fixed;
symbols: W X Y Z;
}
</style>
<ol>
<li id="decimal" style="list-style-type: decimal"></li>
<li id="foo" style="list-style-type: foo"></li>
<li id="bar" style="list-style-type: bar"></li>
</ol>
)HTML");
UpdateAllLifecyclePhasesForTest();
EXPECT_EQ("1. ", GetMarkerText("decimal"));
EXPECT_EQ("X. ", GetMarkerText("foo"));
EXPECT_EQ("3. ", GetMarkerText("bar"));
// Add @counter-style 'bar'. Should not affect 'decimal' and 'foo'.
AddCounterStyle("bar", "system: fixed; symbols: A B C;");
GetDocument().UpdateStyleAndLayoutTree();
EXPECT_FALSE(GetMarker("decimal")->NeedsLayout());
EXPECT_FALSE(GetMarker("foo")->NeedsLayout());
EXPECT_TRUE(GetMarker("bar")->NeedsLayout());
UpdateAllLifecyclePhasesForTest();
EXPECT_EQ("1. ", GetMarkerText("decimal"));
EXPECT_EQ("X. ", GetMarkerText("foo"));
EXPECT_EQ("C. ", GetMarkerText("bar"));
}
TEST_F(ListMarkerTest, RemoveCounterStyle) {
GetDocument().body()->setInnerHTML(R"HTML(
<style id="foo-sheet">
@counter-style foo {
system: fixed;
symbols: W X Y Z;
}
</style>
<ol>
<li id="decimal" style="list-style-type: decimal"></li>
<li id="foo" style="list-style-type: foo"></li>
</ol>
)HTML");
UpdateAllLifecyclePhasesForTest();
EXPECT_EQ("1. ", GetMarkerText("decimal"));
EXPECT_EQ("X. ", GetMarkerText("foo"));
// Remove @counter-style 'foo'. Should not affect 'decimal'.
GetElementById("foo-sheet")->remove();
GetDocument().UpdateStyleAndLayoutTree();
EXPECT_FALSE(GetMarker("decimal")->NeedsLayout());
EXPECT_TRUE(GetMarker("foo")->NeedsLayout());
UpdateAllLifecyclePhasesForTest();
EXPECT_EQ("1. ", GetMarkerText("decimal"));
EXPECT_EQ("2. ", GetMarkerText("foo"));
}
TEST_F(ListMarkerTest, OverridePredefinedCounterStyle) {
GetDocument().body()->setInnerHTML(R"HTML(
<ol>
<li id="decimal" style="list-style-type: decimal"></li>
<li id="upper-roman" style="list-style-type: upper-roman"></li>
</ol>
)HTML");
UpdateAllLifecyclePhasesForTest();
EXPECT_EQ("1. ", GetMarkerText("decimal"));
EXPECT_EQ("II. ", GetMarkerText("upper-roman"));
// Override 'upper-roman'. Should not affect 'decimal'.
AddCounterStyle("upper-roman", "system: fixed; symbols: A B C;");
GetDocument().UpdateStyleAndLayoutTree();
EXPECT_FALSE(GetMarker("decimal")->NeedsLayout());
EXPECT_TRUE(GetMarker("upper-roman")->NeedsLayout());
UpdateAllLifecyclePhasesForTest();
EXPECT_EQ("1. ", GetMarkerText("decimal"));
EXPECT_EQ("B. ", GetMarkerText("upper-roman"));
}
TEST_F(ListMarkerTest, RemoveOverrideOfPredefinedCounterStyle) {
GetDocument().body()->setInnerHTML(R"HTML(
<style id="to-remove">
@counter-style upper-roman {
system: fixed;
symbols: A B C;
}
</style>
<ol>
<li id="decimal" style="list-style-type: decimal"></li>
<li id="upper-roman" style="list-style-type: upper-roman"></li>
</ol>
)HTML");
UpdateAllLifecyclePhasesForTest();
EXPECT_EQ("1. ", GetMarkerText("decimal"));
EXPECT_EQ("B. ", GetMarkerText("upper-roman"));
// Remove override of 'upper-roman'. Should not affect 'decimal'.
GetElementById("to-remove")->remove();
GetDocument().UpdateStyleAndLayoutTree();
EXPECT_FALSE(GetMarker("decimal")->NeedsLayout());
EXPECT_TRUE(GetMarker("upper-roman")->NeedsLayout());
UpdateAllLifecyclePhasesForTest();
EXPECT_EQ("1. ", GetMarkerText("decimal"));
EXPECT_EQ("II. ", GetMarkerText("upper-roman"));
}
TEST_F(ListMarkerTest, OverrideSameScopeCounterStyle) {
GetDocument().body()->setInnerHTML(R"HTML(
<style>
@counter-style foo {
system: fixed;
symbols: W X Y Z;
}
</style>
<ol>
<li id="decimal" style="list-style-type: decimal"></li>
<li id="foo" style="list-style-type: foo"></li>
</ol>
)HTML");
UpdateAllLifecyclePhasesForTest();
EXPECT_EQ("1. ", GetMarkerText("decimal"));
EXPECT_EQ("X. ", GetMarkerText("foo"));
// Override 'foo'. Should not affect 'decimal'.
AddCounterStyle("foo", "system: fixed; symbols: A B C;");
GetDocument().UpdateStyleAndLayoutTree();
EXPECT_FALSE(GetMarker("decimal")->NeedsLayout());
EXPECT_TRUE(GetMarker("foo")->NeedsLayout());
UpdateAllLifecyclePhasesForTest();
EXPECT_EQ("1. ", GetMarkerText("decimal"));
EXPECT_EQ("B. ", GetMarkerText("foo"));
}
TEST_F(ListMarkerTest, RemoveOverrideOfSameScopeCounterStyle) {
GetDocument().body()->setInnerHTML(R"HTML(
<style>
@counter-style foo {
system: fixed;
symbols: W X Y Z;
}
</style>
<style id="to-remove">
@counter-style foo {
system: fixed;
symbols: A B C;
}
</style>
<ol>
<li id="decimal" style="list-style-type: decimal"></li>
<li id="foo" style="list-style-type: foo"></li>
</ol>
)HTML");
UpdateAllLifecyclePhasesForTest();
EXPECT_EQ("1. ", GetMarkerText("decimal"));
EXPECT_EQ("B. ", GetMarkerText("foo"));
// Remove the override of 'foo'. Should not affect 'decimal'.
GetElementById("to-remove")->remove();
GetDocument().UpdateStyleAndLayoutTree();
EXPECT_FALSE(GetMarker("decimal")->NeedsLayout());
EXPECT_TRUE(GetMarker("foo")->NeedsLayout());
UpdateAllLifecyclePhasesForTest();
EXPECT_EQ("1. ", GetMarkerText("decimal"));
EXPECT_EQ("X. ", GetMarkerText("foo"));
}
TEST_F(ListMarkerTest, ModifyShadowDOMWithOwnCounterStyles) {
GetDocument().body()->setInnerHTML(R"HTML(
<style>
@counter-style foo {
system: fixed;
symbols: W X Y Z;
}
</style>
<ol>
<li id="decimal" style="list-style-type: decimal"></li>
<li id="foo" style="list-style-type: foo"></li>
</ol>
<div id="host1"></div>
<div id="host2"></div>
)HTML");
UpdateAllLifecyclePhasesForTest();
EXPECT_EQ("1. ", GetMarkerText("decimal"));
EXPECT_EQ("X. ", GetMarkerText("foo"));
// Attach a shadow tree with counter styles. Shouldn't affect anything outside
ShadowRoot& shadow1 =
GetElementById("host1")->AttachShadowRootInternal(ShadowRootType::kOpen);
shadow1.setInnerHTML(R"HTML(
<style>
@counter-style foo {
system: fixed;
symbols: A B C;
}
</style>
<ol>
<li id="shadow-foo" style="list-style-type: foo"></li>
</ol>
)HTML");
GetDocument().UpdateStyleAndLayoutTree();
EXPECT_FALSE(GetMarker("decimal")->NeedsLayout());
EXPECT_FALSE(GetMarker("foo")->NeedsLayout());
UpdateAllLifecyclePhasesForTest();
EXPECT_EQ("1. ", GetMarkerText("decimal"));
EXPECT_EQ("X. ", GetMarkerText("foo"));
EXPECT_EQ("A. ", GetMarkerText(shadow1, "shadow-foo"));
// Attach another shadow tree with counter styles. Shouldn't affect anything
// outside.
ShadowRoot& shadow2 =
GetElementById("host2")->AttachShadowRootInternal(ShadowRootType::kOpen);
shadow2.setInnerHTML(R"HTML(
<style>
@counter-style foo {
system: fixed;
symbols: D E F;
}
</style>
<ol>
<li id="shadow-foo" style="list-style-type: foo"></li>
</ol>
)HTML");
GetDocument().UpdateStyleAndLayoutTree();
EXPECT_FALSE(GetMarker("decimal")->NeedsLayout());
EXPECT_FALSE(GetMarker("foo")->NeedsLayout());
EXPECT_FALSE(GetMarker(shadow1, "shadow-foo")->NeedsLayout());
UpdateAllLifecyclePhasesForTest();
EXPECT_EQ("1. ", GetMarkerText("decimal"));
EXPECT_EQ("X. ", GetMarkerText("foo"));
EXPECT_EQ("A. ", GetMarkerText(shadow1, "shadow-foo"));
EXPECT_EQ("D. ", GetMarkerText(shadow2, "shadow-foo"));
// Remove one of the shadow trees. Shouldn't affect anything outside.
GetElementById("host1")->remove();
GetDocument().UpdateStyleAndLayoutTree();
EXPECT_FALSE(GetMarker("decimal")->NeedsLayout());
EXPECT_FALSE(GetMarker("foo")->NeedsLayout());
EXPECT_FALSE(GetMarker(shadow2, "shadow-foo")->NeedsLayout());
UpdateAllLifecyclePhasesForTest();
EXPECT_EQ("1. ", GetMarkerText("decimal"));
EXPECT_EQ("X. ", GetMarkerText("foo"));
EXPECT_EQ("D. ", GetMarkerText(shadow2, "shadow-foo"));
}
} // namespace blink