1 // Copyright 2020 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/matched_properties_cache.h"
6 
7 #include "third_party/blink/renderer/core/css/css_property_name.h"
8 #include "third_party/blink/renderer/core/css/css_test_helpers.h"
9 #include "third_party/blink/renderer/core/css/resolver/style_resolver.h"
10 #include "third_party/blink/renderer/core/html/html_element.h"
11 #include "third_party/blink/renderer/core/style/computed_style.h"
12 #include "third_party/blink/renderer/core/testing/page_test_base.h"
13 #include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
14 
15 namespace blink {
16 
17 using css_test_helpers::CreateVariableData;
18 
19 class MatchedPropertiesCacheTestKey {
20   STACK_ALLOCATED();
21 
22  public:
MatchedPropertiesCacheTestKey(String block_text,unsigned hash,const TreeScope & tree_scope)23   explicit MatchedPropertiesCacheTestKey(String block_text,
24                                          unsigned hash,
25                                          const TreeScope& tree_scope)
26       : key_(ParseBlock(block_text, tree_scope), hash) {}
27 
InnerKey() const28   const MatchedPropertiesCache::Key& InnerKey() const { return key_; }
29 
30  private:
ParseBlock(String block_text,const TreeScope & tree_scope)31   const MatchResult& ParseBlock(String block_text,
32                                 const TreeScope& tree_scope) {
33     result_.FinishAddingUARules();
34     result_.FinishAddingUserRules();
35     auto* set = css_test_helpers::ParseDeclarationBlock(block_text);
36     result_.AddMatchedProperties(set);
37     result_.FinishAddingAuthorRulesForTreeScope(tree_scope);
38     return result_;
39   }
40 
41   MatchResult result_;
42   MatchedPropertiesCache::Key key_;
43 };
44 
45 using TestKey = MatchedPropertiesCacheTestKey;
46 
47 class MatchedPropertiesCacheTestCache {
48   STACK_ALLOCATED();
49 
50  public:
MatchedPropertiesCacheTestCache(Document & document)51   explicit MatchedPropertiesCacheTestCache(Document& document)
52       : document_(document) {}
53 
~MatchedPropertiesCacheTestCache()54   ~MatchedPropertiesCacheTestCache() {
55     // Required by DCHECK in ~MatchedPropertiesCache.
56     cache_.Clear();
57   }
58 
Add(const TestKey & key,const ComputedStyle & style,const ComputedStyle & parent_style,const Vector<String> & dependencies=Vector<String> ())59   void Add(const TestKey& key,
60            const ComputedStyle& style,
61            const ComputedStyle& parent_style,
62            const Vector<String>& dependencies = Vector<String>()) {
63     HashSet<CSSPropertyName> set;
64     for (String name_string : dependencies) {
65       set.insert(
66           *CSSPropertyName::From(document_.GetExecutionContext(), name_string));
67     }
68     cache_.Add(key.InnerKey(), style, parent_style, set);
69   }
70 
Find(const TestKey & key,const ComputedStyle & style,const ComputedStyle & parent_style)71   const CachedMatchedProperties* Find(const TestKey& key,
72                                       const ComputedStyle& style,
73                                       const ComputedStyle& parent_style) {
74     StyleResolverState state(document_, *document_.body(), &parent_style,
75                              &parent_style);
76     state.SetStyle(ComputedStyle::Clone(style));
77     return cache_.Find(key.InnerKey(), state);
78   }
79 
80  private:
81   MatchedPropertiesCache cache_;
82   Document& document_;
83 };
84 
85 using TestCache = MatchedPropertiesCacheTestCache;
86 
87 class MatchedPropertiesCacheTest
88     : public PageTestBase,
89       private ScopedCSSMatchedPropertiesCacheDependenciesForTest {
90  public:
MatchedPropertiesCacheTest()91   MatchedPropertiesCacheTest()
92       : ScopedCSSMatchedPropertiesCacheDependenciesForTest(true) {}
93 
CreateStyle()94   scoped_refptr<ComputedStyle> CreateStyle() {
95     return StyleResolver::InitialStyleForElement(GetDocument());
96   }
97 };
98 
TEST_F(MatchedPropertiesCacheTest,ClearEntry)99 TEST_F(MatchedPropertiesCacheTest, ClearEntry) {
100   MatchResult result;
101   result.AddMatchedProperties(
102       css_test_helpers::ParseDeclarationBlock("top:inherit"));
103 
104   auto style = CreateStyle();
105   auto parent = CreateStyle();
106 
107   HashSet<CSSPropertyName> dependencies;
108   dependencies.insert(CSSPropertyName(CSSPropertyID::kTop));
109 
110   auto* entry = MakeGarbageCollected<CachedMatchedProperties>();
111   entry->Set(*style, *parent, result.GetMatchedProperties(), dependencies);
112 
113   EXPECT_TRUE(entry->computed_style);
114   EXPECT_TRUE(entry->parent_computed_style);
115   EXPECT_FALSE(entry->matched_properties.IsEmpty());
116   EXPECT_FALSE(entry->matched_properties_types.IsEmpty());
117   EXPECT_TRUE(entry->dependencies);
118 
119   entry->Clear();
120 
121   EXPECT_FALSE(entry->computed_style);
122   EXPECT_FALSE(entry->parent_computed_style);
123   EXPECT_TRUE(entry->matched_properties.IsEmpty());
124   EXPECT_TRUE(entry->matched_properties_types.IsEmpty());
125   EXPECT_FALSE(entry->dependencies);
126 }
127 
TEST_F(MatchedPropertiesCacheTest,NoDependencies)128 TEST_F(MatchedPropertiesCacheTest, NoDependencies) {
129   MatchResult result;
130   auto style = CreateStyle();
131   auto parent = CreateStyle();
132 
133   HashSet<CSSPropertyName> dependencies;
134 
135   auto* entry = MakeGarbageCollected<CachedMatchedProperties>();
136   entry->Set(*style, *parent, result.GetMatchedProperties(), dependencies);
137 
138   EXPECT_FALSE(entry->dependencies);
139 }
140 
TEST_F(MatchedPropertiesCacheTest,OneDependency)141 TEST_F(MatchedPropertiesCacheTest, OneDependency) {
142   MatchResult result;
143   auto style = CreateStyle();
144   auto parent = CreateStyle();
145 
146   HashSet<CSSPropertyName> dependencies;
147   dependencies.insert(CSSPropertyName(CSSPropertyID::kTop));
148 
149   auto* entry = MakeGarbageCollected<CachedMatchedProperties>();
150   entry->Set(*style, *parent, result.GetMatchedProperties(), dependencies);
151 
152   ASSERT_TRUE(entry->dependencies);
153   EXPECT_EQ("top", entry->dependencies[0]);
154   EXPECT_EQ(g_null_atom, entry->dependencies[1]);
155 }
156 
TEST_F(MatchedPropertiesCacheTest,TwoDependencies)157 TEST_F(MatchedPropertiesCacheTest, TwoDependencies) {
158   MatchResult result;
159   auto style = CreateStyle();
160   auto parent = CreateStyle();
161 
162   HashSet<CSSPropertyName> dependencies;
163   dependencies.insert(CSSPropertyName(CSSPropertyID::kTop));
164   dependencies.insert(CSSPropertyName(CSSPropertyID::kLeft));
165 
166   auto* entry = MakeGarbageCollected<CachedMatchedProperties>();
167   entry->Set(*style, *parent, result.GetMatchedProperties(), dependencies);
168 
169   ASSERT_TRUE(entry->dependencies);
170   EXPECT_TRUE(entry->dependencies[0] == "top" ||
171               entry->dependencies[0] == "left");
172   EXPECT_TRUE(entry->dependencies[1] == "top" ||
173               entry->dependencies[1] == "left");
174   EXPECT_NE(entry->dependencies[0], entry->dependencies[1]);
175   EXPECT_TRUE(entry->dependencies[2] == g_null_atom);
176 }
177 
TEST_F(MatchedPropertiesCacheTest,AllowedKeyValues)178 TEST_F(MatchedPropertiesCacheTest, AllowedKeyValues) {
179   unsigned empty = HashTraits<unsigned>::EmptyValue();
180   unsigned deleted = std::numeric_limits<unsigned>::max();
181 
182   ASSERT_EQ(0u, HashTraits<unsigned>::EmptyValue());
183   ASSERT_TRUE(HashTraits<unsigned>::IsDeletedValue(deleted));
184 
185   EXPECT_FALSE(TestKey("left:0", empty, GetDocument()).InnerKey().IsValid());
186   EXPECT_TRUE(TestKey("left:0", empty + 1, GetDocument()).InnerKey().IsValid());
187   EXPECT_TRUE(
188       TestKey("left:0", deleted - 1, GetDocument()).InnerKey().IsValid());
189   EXPECT_FALSE(TestKey("left:0", deleted, GetDocument()).InnerKey().IsValid());
190 }
191 
TEST_F(MatchedPropertiesCacheTest,InvalidKeyForUncacheableMatchResult)192 TEST_F(MatchedPropertiesCacheTest, InvalidKeyForUncacheableMatchResult) {
193   MatchResult result;
194   result.SetIsCacheable(false);
195   EXPECT_FALSE(MatchedPropertiesCache::Key(result).IsValid());
196 }
197 
TEST_F(MatchedPropertiesCacheTest,Miss)198 TEST_F(MatchedPropertiesCacheTest, Miss) {
199   TestCache cache(GetDocument());
200   TestKey key("color:red", 1, GetDocument());
201 
202   auto style = CreateStyle();
203   auto parent = CreateStyle();
204 
205   EXPECT_FALSE(cache.Find(key, *style, *parent));
206 }
207 
TEST_F(MatchedPropertiesCacheTest,Hit)208 TEST_F(MatchedPropertiesCacheTest, Hit) {
209   TestCache cache(GetDocument());
210   TestKey key("color:red", 1, GetDocument());
211 
212   auto style = CreateStyle();
213   auto parent = CreateStyle();
214 
215   cache.Add(key, *style, *parent);
216   EXPECT_TRUE(cache.Find(key, *style, *parent));
217 }
218 
TEST_F(MatchedPropertiesCacheTest,HitOnlyForAddedEntry)219 TEST_F(MatchedPropertiesCacheTest, HitOnlyForAddedEntry) {
220   TestCache cache(GetDocument());
221 
222   auto style = CreateStyle();
223   auto parent = CreateStyle();
224 
225   TestKey key1("color:red", 1, GetDocument());
226   TestKey key2("display:block", 2, GetDocument());
227 
228   cache.Add(key1, *style, *parent);
229 
230   EXPECT_TRUE(cache.Find(key1, *style, *parent));
231   EXPECT_FALSE(cache.Find(key2, *style, *parent));
232 }
233 
TEST_F(MatchedPropertiesCacheTest,HitWithStandardDependency)234 TEST_F(MatchedPropertiesCacheTest, HitWithStandardDependency) {
235   TestCache cache(GetDocument());
236 
237   auto style = CreateStyle();
238   auto parent = CreateStyle();
239 
240   TestKey key("top:inherit", 1, GetDocument());
241 
242   cache.Add(key, *style, *parent, Vector<String>{"top"});
243   EXPECT_TRUE(cache.Find(key, *style, *parent));
244 }
245 
TEST_F(MatchedPropertiesCacheTest,MissWithStandardDependency)246 TEST_F(MatchedPropertiesCacheTest, MissWithStandardDependency) {
247   TestCache cache(GetDocument());
248 
249   auto style = CreateStyle();
250 
251   auto parent1 = CreateStyle();
252   parent1->SetTop(Length(1, Length::kFixed));
253 
254   auto parent2 = CreateStyle();
255   parent2->SetTop(Length(2, Length::kFixed));
256 
257   TestKey key("top:inherit", 1, GetDocument());
258   cache.Add(key, *style, *parent1, Vector<String>{"top"});
259   EXPECT_TRUE(cache.Find(key, *style, *parent1));
260   EXPECT_FALSE(cache.Find(key, *style, *parent2));
261 }
262 
TEST_F(MatchedPropertiesCacheTest,HitWithCustomDependency)263 TEST_F(MatchedPropertiesCacheTest, HitWithCustomDependency) {
264   TestCache cache(GetDocument());
265 
266   auto style = CreateStyle();
267 
268   auto parent = CreateStyle();
269   parent->SetVariableData("--x", CreateVariableData("1px"), true);
270 
271   TestKey key("top:var(--x)", 1, GetDocument());
272 
273   cache.Add(key, *style, *parent, Vector<String>{"--x"});
274   EXPECT_TRUE(cache.Find(key, *style, *parent));
275 }
276 
TEST_F(MatchedPropertiesCacheTest,MissWithCustomDependency)277 TEST_F(MatchedPropertiesCacheTest, MissWithCustomDependency) {
278   TestCache cache(GetDocument());
279 
280   auto style = CreateStyle();
281 
282   auto parent1 = CreateStyle();
283   parent1->SetVariableData("--x", CreateVariableData("1px"), true);
284 
285   auto parent2 = CreateStyle();
286   parent2->SetVariableData("--x", CreateVariableData("2px"), true);
287 
288   TestKey key("top:var(--x)", 1, GetDocument());
289 
290   cache.Add(key, *style, *parent1, Vector<String>{"--x"});
291   EXPECT_FALSE(cache.Find(key, *style, *parent2));
292 }
293 
TEST_F(MatchedPropertiesCacheTest,HitWithMultipleCustomDependencies)294 TEST_F(MatchedPropertiesCacheTest, HitWithMultipleCustomDependencies) {
295   TestCache cache(GetDocument());
296 
297   auto style = CreateStyle();
298 
299   auto parent1 = CreateStyle();
300   parent1->SetVariableData("--x", CreateVariableData("1px"), true);
301   parent1->SetVariableData("--y", CreateVariableData("2px"), true);
302   parent1->SetVariableData("--z", CreateVariableData("3px"), true);
303 
304   auto parent2 = ComputedStyle::Clone(*parent1);
305   parent2->SetVariableData("--z", CreateVariableData("4px"), true);
306 
307   TestKey key("top:var(--x);left:var(--y)", 1, GetDocument());
308 
309   // Does not depend on --z, so doesn't matter that --z changed.
310   cache.Add(key, *style, *parent1, Vector<String>{"--x", "--y"});
311   EXPECT_TRUE(cache.Find(key, *style, *parent2));
312 }
313 
TEST_F(MatchedPropertiesCacheTest,MissWithMultipleCustomDependencies)314 TEST_F(MatchedPropertiesCacheTest, MissWithMultipleCustomDependencies) {
315   TestCache cache(GetDocument());
316 
317   auto style = CreateStyle();
318 
319   auto parent1 = CreateStyle();
320   parent1->SetVariableData("--x", CreateVariableData("1px"), true);
321   parent1->SetVariableData("--y", CreateVariableData("2px"), true);
322 
323   auto parent2 = ComputedStyle::Clone(*parent1);
324   parent2->SetVariableData("--y", CreateVariableData("3px"), true);
325 
326   TestKey key("top:var(--x);left:var(--y)", 1, GetDocument());
327 
328   cache.Add(key, *style, *parent1, Vector<String>{"--x", "--y"});
329   EXPECT_FALSE(cache.Find(key, *style, *parent2));
330 }
331 
TEST_F(MatchedPropertiesCacheTest,HitWithMixedDependencies)332 TEST_F(MatchedPropertiesCacheTest, HitWithMixedDependencies) {
333   TestCache cache(GetDocument());
334 
335   auto style = CreateStyle();
336 
337   auto parent1 = CreateStyle();
338   parent1->SetVariableData("--x", CreateVariableData("1px"), true);
339   parent1->SetVariableData("--y", CreateVariableData("2px"), true);
340   parent1->SetLeft(Length(3, Length::kFixed));
341   parent1->SetRight(Length(4, Length::kFixed));
342 
343   auto parent2 = ComputedStyle::Clone(*parent1);
344   parent2->SetVariableData("--y", CreateVariableData("5px"), true);
345   parent2->SetRight(Length(6, Length::kFixed));
346 
347   TestKey key("left:inherit;top:var(--x)", 1, GetDocument());
348 
349   cache.Add(key, *style, *parent1, Vector<String>{"left", "--x"});
350   EXPECT_TRUE(cache.Find(key, *style, *parent2));
351 }
352 
TEST_F(MatchedPropertiesCacheTest,ExplicitlyInheritedCacheable)353 TEST_F(MatchedPropertiesCacheTest, ExplicitlyInheritedCacheable) {
354   ASSERT_TRUE(GetCSSPropertyVerticalAlign().IsComputedValueComparable());
355 
356   TestCache cache(GetDocument());
357 
358   auto style = CreateStyle();
359   auto parent = CreateStyle();
360   parent->SetChildHasExplicitInheritance();
361 
362   StyleResolverState state(GetDocument(), *GetDocument().body(), parent.get(),
363                            parent.get());
364   state.SetStyle(style);
365   // Simulate explicit inheritance on vertical-align.
366   state.MarkDependency(GetCSSPropertyVerticalAlign());
367 
368   EXPECT_TRUE(MatchedPropertiesCache::IsCacheable(state));
369 }
370 
TEST_F(MatchedPropertiesCacheTest,NotCacheableWithIncomparableDependency)371 TEST_F(MatchedPropertiesCacheTest, NotCacheableWithIncomparableDependency) {
372   const CSSProperty& incomparable = GetCSSPropertyInternalEmptyLineHeight();
373   ASSERT_FALSE(incomparable.IsComputedValueComparable());
374 
375   TestCache cache(GetDocument());
376 
377   auto style = CreateStyle();
378   auto parent = CreateStyle();
379   parent->SetChildHasExplicitInheritance();
380 
381   StyleResolverState state(GetDocument(), *GetDocument().body(), parent.get(),
382                            parent.get());
383   state.SetStyle(style);
384   // Simulate explicit inheritance on the incomparable property.
385   state.MarkDependency(incomparable);
386 
387   EXPECT_FALSE(MatchedPropertiesCache::IsCacheable(state));
388 }
389 
TEST_F(MatchedPropertiesCacheTest,WritingModeCacheable)390 TEST_F(MatchedPropertiesCacheTest, WritingModeCacheable) {
391   ASSERT_NE(WritingMode::kVerticalRl,
392             ComputedStyleInitialValues::InitialWritingMode());
393 
394   TestCache cache(GetDocument());
395 
396   auto style = CreateStyle();
397   auto parent = CreateStyle();
398   style->SetWritingMode(WritingMode::kVerticalRl);
399 
400   StyleResolverState state(GetDocument(), *GetDocument().body(), parent.get(),
401                            parent.get());
402   state.SetStyle(style);
403 
404   EXPECT_TRUE(MatchedPropertiesCache::IsCacheable(state));
405 }
406 
TEST_F(MatchedPropertiesCacheTest,DirectionCacheable)407 TEST_F(MatchedPropertiesCacheTest, DirectionCacheable) {
408   ASSERT_NE(TextDirection::kRtl,
409             ComputedStyleInitialValues::InitialDirection());
410 
411   TestCache cache(GetDocument());
412 
413   auto style = CreateStyle();
414   auto parent = CreateStyle();
415   style->SetDirection(TextDirection::kRtl);
416 
417   StyleResolverState state(GetDocument(), *GetDocument().body(), parent.get(),
418                            parent.get());
419   state.SetStyle(style);
420 
421   EXPECT_TRUE(MatchedPropertiesCache::IsCacheable(state));
422 }
423 
TEST_F(MatchedPropertiesCacheTest,VarInNonInheritedPropertyCachable)424 TEST_F(MatchedPropertiesCacheTest, VarInNonInheritedPropertyCachable) {
425   TestCache cache(GetDocument());
426 
427   auto style = CreateStyle();
428   // Simulate non-inherited-property: var(--my-prop)
429   style->SetHasVariableReferenceFromNonInheritedProperty();
430 
431   auto parent = CreateStyle();
432 
433   StyleResolverState state(GetDocument(), *GetDocument().body(), parent.get(),
434                            parent.get());
435   state.SetStyle(style);
436 
437   EXPECT_TRUE(MatchedPropertiesCache::IsCacheable(state));
438 }
439 
TEST_F(MatchedPropertiesCacheTest,MaxDependencies)440 TEST_F(MatchedPropertiesCacheTest, MaxDependencies) {
441   TestCache cache(GetDocument());
442 
443   auto style = CreateStyle();
444   auto parent = CreateStyle();
445 
446   StyleResolverState state(GetDocument(), *GetDocument().body(), parent.get(),
447                            parent.get());
448   state.SetStyle(style);
449   for (size_t i = 0; i < StyleResolverState::kMaxDependencies; i++) {
450     CustomProperty property(AtomicString(String::Format("--x%zu", i)),
451                             GetDocument());
452     state.MarkDependency(property);
453     EXPECT_TRUE(MatchedPropertiesCache::IsCacheable(state));
454   }
455   CustomProperty property("--y", GetDocument());
456   state.MarkDependency(property);
457   // Limit exceeded.
458   EXPECT_FALSE(MatchedPropertiesCache::IsCacheable(state));
459 }
460 
TEST_F(MatchedPropertiesCacheTest,ExplicitlyInheritedNotCacheableWithoutFeature)461 TEST_F(MatchedPropertiesCacheTest,
462        ExplicitlyInheritedNotCacheableWithoutFeature) {
463   ScopedCSSMatchedPropertiesCacheDependenciesForTest scoped_feature(false);
464 
465   TestCache cache(GetDocument());
466 
467   auto style = CreateStyle();
468   auto parent = CreateStyle();
469   parent->SetChildHasExplicitInheritance();
470 
471   StyleResolverState state(GetDocument(), *GetDocument().body(), parent.get(),
472                            parent.get());
473   state.SetStyle(style);
474 
475   EXPECT_FALSE(MatchedPropertiesCache::IsCacheable(state));
476 }
477 
TEST_F(MatchedPropertiesCacheTest,VarInNonInheritedPropertyNotCachableWithoutFeature)478 TEST_F(MatchedPropertiesCacheTest,
479        VarInNonInheritedPropertyNotCachableWithoutFeature) {
480   ScopedCSSMatchedPropertiesCacheDependenciesForTest scoped_feature(false);
481 
482   TestCache cache(GetDocument());
483 
484   auto style = CreateStyle();
485   auto parent = CreateStyle();
486   // Simulate non-inherited-property: var(--my-prop)
487   style->SetHasVariableReferenceFromNonInheritedProperty();
488 
489   StyleResolverState state(GetDocument(), *GetDocument().body(), parent.get(),
490                            parent.get());
491   state.SetStyle(style);
492 
493   EXPECT_FALSE(MatchedPropertiesCache::IsCacheable(state));
494 }
495 
TEST_F(MatchedPropertiesCacheTest,WritingModeNotCacheableWithoutFeature)496 TEST_F(MatchedPropertiesCacheTest, WritingModeNotCacheableWithoutFeature) {
497   ScopedCSSMatchedPropertiesCacheDependenciesForTest scoped_feature(false);
498 
499   ASSERT_NE(WritingMode::kVerticalRl,
500             ComputedStyleInitialValues::InitialWritingMode());
501 
502   TestCache cache(GetDocument());
503 
504   auto style = CreateStyle();
505   auto parent = CreateStyle();
506   style->SetWritingMode(WritingMode::kVerticalRl);
507 
508   StyleResolverState state(GetDocument(), *GetDocument().body(), parent.get(),
509                            parent.get());
510   state.SetStyle(style);
511 
512   EXPECT_FALSE(MatchedPropertiesCache::IsCacheable(state));
513 }
514 
TEST_F(MatchedPropertiesCacheTest,DirectionNotCacheableWithoutFeature)515 TEST_F(MatchedPropertiesCacheTest, DirectionNotCacheableWithoutFeature) {
516   ScopedCSSMatchedPropertiesCacheDependenciesForTest scoped_feature(false);
517 
518   ASSERT_NE(TextDirection::kRtl,
519             ComputedStyleInitialValues::InitialDirection());
520 
521   TestCache cache(GetDocument());
522 
523   auto style = CreateStyle();
524   auto parent = CreateStyle();
525   style->SetDirection(TextDirection::kRtl);
526 
527   StyleResolverState state(GetDocument(), *GetDocument().body(), parent.get(),
528                            parent.get());
529   state.SetStyle(style);
530 
531   EXPECT_FALSE(MatchedPropertiesCache::IsCacheable(state));
532 }
533 
TEST_F(MatchedPropertiesCacheTest,EnsuredInDisplayNone)534 TEST_F(MatchedPropertiesCacheTest, EnsuredInDisplayNone) {
535   TestCache cache(GetDocument());
536 
537   auto style = CreateStyle();
538   auto parent = CreateStyle();
539   auto ensured_parent = CreateStyle();
540   ensured_parent->SetIsEnsuredInDisplayNone();
541 
542   TestKey key1("display:block", 1, GetDocument());
543 
544   cache.Add(key1, *style, *parent);
545   EXPECT_TRUE(cache.Find(key1, *style, *parent));
546   EXPECT_TRUE(cache.Find(key1, *style, *ensured_parent));
547 
548   cache.Add(key1, *style, *ensured_parent);
549   EXPECT_FALSE(cache.Find(key1, *style, *parent));
550   EXPECT_TRUE(cache.Find(key1, *style, *ensured_parent));
551 }
552 
TEST_F(MatchedPropertiesCacheTest,EnsuredOutsideFlatTree)553 TEST_F(MatchedPropertiesCacheTest, EnsuredOutsideFlatTree) {
554   TestCache cache(GetDocument());
555 
556   auto style = CreateStyle();
557   auto parent = CreateStyle();
558   auto ensured_style = CreateStyle();
559   ensured_style->SetIsEnsuredOutsideFlatTree();
560 
561   TestKey key1("display:block", 1, GetDocument());
562 
563   cache.Add(key1, *style, *parent);
564   EXPECT_TRUE(cache.Find(key1, *style, *parent));
565   EXPECT_TRUE(cache.Find(key1, *ensured_style, *parent));
566 
567   cache.Add(key1, *ensured_style, *parent);
568   EXPECT_FALSE(cache.Find(key1, *style, *parent));
569   EXPECT_TRUE(cache.Find(key1, *ensured_style, *parent));
570 }
571 
TEST_F(MatchedPropertiesCacheTest,EnsuredOutsideFlatTreeAndDisplayNone)572 TEST_F(MatchedPropertiesCacheTest, EnsuredOutsideFlatTreeAndDisplayNone) {
573   TestCache cache(GetDocument());
574 
575   auto parent = CreateStyle();
576   auto parent_none = CreateStyle();
577   auto style = CreateStyle();
578   auto style_flat = CreateStyle();
579   parent_none->SetIsEnsuredInDisplayNone();
580   style_flat->SetIsEnsuredOutsideFlatTree();
581 
582   TestKey key1("display:block", 1, GetDocument());
583 
584   cache.Add(key1, *style, *parent_none);
585   EXPECT_TRUE(cache.Find(key1, *style_flat, *parent));
586 
587   cache.Add(key1, *style_flat, *parent);
588   EXPECT_TRUE(cache.Find(key1, *style, *parent_none));
589 }
590 
591 }  // namespace blink
592