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