1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "third_party/blink/renderer/core/css/resolver/style_resolver.h"
6
7 #include "testing/gtest/include/gtest/gtest.h"
8 #include "third_party/blink/renderer/core/animation/element_animations.h"
9 #include "third_party/blink/renderer/core/css/style_engine.h"
10 #include "third_party/blink/renderer/core/dom/document.h"
11 #include "third_party/blink/renderer/core/dom/node_computed_style.h"
12 #include "third_party/blink/renderer/core/dom/shadow_root.h"
13 #include "third_party/blink/renderer/core/dom/text.h"
14 #include "third_party/blink/renderer/core/testing/page_test_base.h"
15 #include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
16
17 namespace blink {
18
19 class StyleResolverTest : public PageTestBase {
20 public:
StyleForId(AtomicString id)21 scoped_refptr<ComputedStyle> StyleForId(AtomicString id) {
22 Element* element = GetDocument().getElementById(id);
23 StyleResolver* resolver = GetStyleEngine().Resolver();
24 DCHECK(resolver);
25 auto style = resolver->StyleForElement(element);
26 DCHECK(style);
27 return style;
28 }
29
30 protected:
31 };
32
TEST_F(StyleResolverTest,StyleForTextInDisplayNone)33 TEST_F(StyleResolverTest, StyleForTextInDisplayNone) {
34 GetDocument().documentElement()->setInnerHTML(R"HTML(
35 <body style="display:none">Text</body>
36 )HTML");
37
38 UpdateAllLifecyclePhasesForTest();
39
40 GetDocument().body()->EnsureComputedStyle();
41
42 ASSERT_TRUE(GetDocument().body()->GetComputedStyle());
43 EXPECT_TRUE(
44 GetDocument().body()->GetComputedStyle()->IsEnsuredInDisplayNone());
45 EXPECT_FALSE(GetStyleEngine().Resolver()->StyleForText(
46 To<Text>(GetDocument().body()->firstChild())));
47 }
48
TEST_F(StyleResolverTest,AnimationBaseComputedStyle)49 TEST_F(StyleResolverTest, AnimationBaseComputedStyle) {
50 GetDocument().documentElement()->setInnerHTML(R"HTML(
51 <style>
52 html { font-size: 10px; }
53 body { font-size: 20px; }
54 </style>
55 <div id="div">Test</div>
56 )HTML");
57 UpdateAllLifecyclePhasesForTest();
58
59 Element* div = GetDocument().getElementById("div");
60 StyleResolver* resolver = GetStyleEngine().Resolver();
61 ASSERT_TRUE(resolver);
62 ElementAnimations& animations = div->EnsureElementAnimations();
63 animations.SetAnimationStyleChange(true);
64
65 ASSERT_TRUE(resolver->StyleForElement(div));
66 EXPECT_EQ(20, resolver->StyleForElement(div)->FontSize());
67 ASSERT_TRUE(animations.BaseComputedStyle());
68 EXPECT_EQ(20, animations.BaseComputedStyle()->FontSize());
69
70 // Getting style with customized parent style should not affect cached
71 // animation base computed style.
72 const ComputedStyle* parent_style =
73 GetDocument().documentElement()->GetComputedStyle();
74 EXPECT_EQ(
75 10,
76 resolver->StyleForElement(div, parent_style, parent_style)->FontSize());
77 ASSERT_TRUE(animations.BaseComputedStyle());
78 EXPECT_EQ(20, animations.BaseComputedStyle()->FontSize());
79 EXPECT_EQ(20, resolver->StyleForElement(div)->FontSize());
80 }
81
TEST_F(StyleResolverTest,ShadowDOMV0Crash)82 TEST_F(StyleResolverTest, ShadowDOMV0Crash) {
83 GetDocument().documentElement()->setInnerHTML(R"HTML(
84 <style>
85 span { display: contents; }
86 </style>
87 <summary><span id="outer"><span id="inner"></b></b></summary>
88 )HTML");
89
90 Element* outer = GetDocument().getElementById("outer");
91 Element* inner = GetDocument().getElementById("inner");
92 ShadowRoot& outer_root = outer->CreateV0ShadowRootForTesting();
93 ShadowRoot& inner_root = inner->CreateV0ShadowRootForTesting();
94 outer_root.setInnerHTML("<content>");
95 inner_root.setInnerHTML("<span>");
96
97 // Test passes if it doesn't crash.
98 UpdateAllLifecyclePhasesForTest();
99 }
100
TEST_F(StyleResolverTest,HasEmUnits)101 TEST_F(StyleResolverTest, HasEmUnits) {
102 GetDocument().documentElement()->setInnerHTML("<div id=div>Test</div>");
103 UpdateAllLifecyclePhasesForTest();
104 EXPECT_FALSE(StyleForId("div")->HasEmUnits());
105
106 GetDocument().documentElement()->setInnerHTML(
107 "<div id=div style='width:1em'>Test</div>");
108 UpdateAllLifecyclePhasesForTest();
109 EXPECT_TRUE(StyleForId("div")->HasEmUnits());
110 }
111
TEST_F(StyleResolverTest,BasePresentIfFontRelativeUnitsAbsent)112 TEST_F(StyleResolverTest, BasePresentIfFontRelativeUnitsAbsent) {
113 GetDocument().documentElement()->setInnerHTML("<div id=div>Test</div>");
114 UpdateAllLifecyclePhasesForTest();
115
116 Element* div = GetDocument().getElementById("div");
117 StyleResolver* resolver = GetStyleEngine().Resolver();
118 ASSERT_TRUE(resolver);
119 ElementAnimations& animations = div->EnsureElementAnimations();
120 animations.SetAnimationStyleChange(true);
121 // We're animating a font affecting property, but we should still be able to
122 // use the base computed style optimization, since no font-relative units
123 // exist in the base.
124 animations.SetHasFontAffectingAnimation();
125
126 EXPECT_TRUE(resolver->StyleForElement(div));
127 EXPECT_TRUE(animations.BaseComputedStyle());
128 }
129
TEST_F(StyleResolverTest,NoCrashWhenAnimatingWithoutCascade)130 TEST_F(StyleResolverTest, NoCrashWhenAnimatingWithoutCascade) {
131 ScopedCSSCascadeForTest scoped_cascade(false);
132
133 GetDocument().documentElement()->setInnerHTML(R"HTML(
134 <style>
135 @keyframes test {
136 from { width: 10px; }
137 to { width: 20px; }
138 }
139 div {
140 animation: test 1s;
141 }
142 </style>
143 <div id="div">Test</div>
144 )HTML");
145 UpdateAllLifecyclePhasesForTest();
146 }
147
148 class StyleResolverFontRelativeUnitTest
149 : public testing::WithParamInterface<const char*>,
150 public StyleResolverTest {};
151
TEST_P(StyleResolverFontRelativeUnitTest,NoBaseIfFontRelativeUnitPresent)152 TEST_P(StyleResolverFontRelativeUnitTest, NoBaseIfFontRelativeUnitPresent) {
153 GetDocument().documentElement()->setInnerHTML(
154 String::Format("<div id=div style='width:1%s'>Test</div>", GetParam()));
155 UpdateAllLifecyclePhasesForTest();
156
157 Element* div = GetDocument().getElementById("div");
158 ElementAnimations& animations = div->EnsureElementAnimations();
159 animations.SetAnimationStyleChange(true);
160 animations.SetHasFontAffectingAnimation();
161
162 EXPECT_TRUE(StyleForId("div")->HasFontRelativeUnits());
163 EXPECT_FALSE(animations.BaseComputedStyle());
164 }
165
TEST_P(StyleResolverFontRelativeUnitTest,BasePresentIfNoFontAffectingAnimation)166 TEST_P(StyleResolverFontRelativeUnitTest,
167 BasePresentIfNoFontAffectingAnimation) {
168 GetDocument().documentElement()->setInnerHTML(
169 String::Format("<div id=div style='width:1%s'>Test</div>", GetParam()));
170 UpdateAllLifecyclePhasesForTest();
171
172 Element* div = GetDocument().getElementById("div");
173 ElementAnimations& animations = div->EnsureElementAnimations();
174 animations.SetAnimationStyleChange(true);
175
176 EXPECT_TRUE(StyleForId("div")->HasFontRelativeUnits());
177 EXPECT_TRUE(animations.BaseComputedStyle());
178 }
179
180 INSTANTIATE_TEST_SUITE_P(All,
181 StyleResolverFontRelativeUnitTest,
182 testing::Values("em", "rem", "ex", "ch"));
183
184 } // namespace blink
185