blob: 0803cfc37303a5b91859b431d31f31a4bb4202aa [file] [log] [blame]
// Copyright 2020 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/css/resolver/matched_properties_cache.h"
#include "third_party/blink/renderer/core/css/css_property_name.h"
#include "third_party/blink/renderer/core/css/css_test_helpers.h"
#include "third_party/blink/renderer/core/css/resolver/style_resolver.h"
#include "third_party/blink/renderer/core/html/html_element.h"
#include "third_party/blink/renderer/core/style/computed_style.h"
#include "third_party/blink/renderer/core/testing/page_test_base.h"
namespace blink {
using css_test_helpers::CreateVariableData;
class MatchedPropertiesCacheTestKey {
STACK_ALLOCATED();
public:
explicit MatchedPropertiesCacheTestKey(String block_text,
unsigned hash,
const TreeScope& tree_scope)
: key_(ParseBlock(block_text, tree_scope), hash) {}
const MatchedPropertiesCache::Key& InnerKey() const { return key_; }
private:
const MatchResult& ParseBlock(String block_text,
const TreeScope& tree_scope) {
result_.FinishAddingUARules();
result_.FinishAddingUserRules();
auto* set = css_test_helpers::ParseDeclarationBlock(block_text);
result_.AddMatchedProperties(set);
result_.FinishAddingAuthorRulesForTreeScope(tree_scope);
return result_;
}
MatchResult result_;
MatchedPropertiesCache::Key key_;
};
using TestKey = MatchedPropertiesCacheTestKey;
class MatchedPropertiesCacheTestCache {
STACK_ALLOCATED();
public:
explicit MatchedPropertiesCacheTestCache(Document& document)
: document_(document) {}
~MatchedPropertiesCacheTestCache() {
// Required by DCHECK in ~MatchedPropertiesCache.
cache_.Clear();
}
void Add(const TestKey& key,
const ComputedStyle& style,
const ComputedStyle& parent_style) {
cache_.Add(key.InnerKey(), style, parent_style);
}
const CachedMatchedProperties* Find(const TestKey& key,
const ComputedStyle& style,
const ComputedStyle& parent_style) {
StyleResolverState state(document_, *document_.body(), &parent_style,
&parent_style);
state.SetStyle(ComputedStyle::Clone(style));
return cache_.Find(key.InnerKey(), state);
}
private:
MatchedPropertiesCache cache_;
Document& document_;
};
using TestCache = MatchedPropertiesCacheTestCache;
class MatchedPropertiesCacheTest : public PageTestBase {
public:
scoped_refptr<ComputedStyle> CreateStyle() {
return StyleResolver::InitialStyleForElement(GetDocument());
}
};
TEST_F(MatchedPropertiesCacheTest, AllowedKeyValues) {
unsigned empty = HashTraits<unsigned>::EmptyValue();
unsigned deleted = std::numeric_limits<unsigned>::max();
ASSERT_EQ(0u, HashTraits<unsigned>::EmptyValue());
ASSERT_TRUE(HashTraits<unsigned>::IsDeletedValue(deleted));
EXPECT_FALSE(TestKey("left:0", empty, GetDocument()).InnerKey().IsValid());
EXPECT_TRUE(TestKey("left:0", empty + 1, GetDocument()).InnerKey().IsValid());
EXPECT_TRUE(
TestKey("left:0", deleted - 1, GetDocument()).InnerKey().IsValid());
EXPECT_FALSE(TestKey("left:0", deleted, GetDocument()).InnerKey().IsValid());
}
TEST_F(MatchedPropertiesCacheTest, InvalidKeyForUncacheableMatchResult) {
MatchResult result;
result.SetIsCacheable(false);
EXPECT_FALSE(MatchedPropertiesCache::Key(result).IsValid());
}
TEST_F(MatchedPropertiesCacheTest, Miss) {
TestCache cache(GetDocument());
TestKey key("color:red", 1, GetDocument());
auto style = CreateStyle();
auto parent = CreateStyle();
EXPECT_FALSE(cache.Find(key, *style, *parent));
}
TEST_F(MatchedPropertiesCacheTest, Hit) {
TestCache cache(GetDocument());
TestKey key("color:red", 1, GetDocument());
auto style = CreateStyle();
auto parent = CreateStyle();
cache.Add(key, *style, *parent);
EXPECT_TRUE(cache.Find(key, *style, *parent));
}
TEST_F(MatchedPropertiesCacheTest, HitOnlyForAddedEntry) {
TestCache cache(GetDocument());
auto style = CreateStyle();
auto parent = CreateStyle();
TestKey key1("color:red", 1, GetDocument());
TestKey key2("display:block", 2, GetDocument());
cache.Add(key1, *style, *parent);
EXPECT_TRUE(cache.Find(key1, *style, *parent));
EXPECT_FALSE(cache.Find(key2, *style, *parent));
}
TEST_F(MatchedPropertiesCacheTest, EnsuredInDisplayNone) {
TestCache cache(GetDocument());
auto style = CreateStyle();
auto parent = CreateStyle();
auto ensured_parent = CreateStyle();
ensured_parent->SetIsEnsuredInDisplayNone();
TestKey key1("display:block", 1, GetDocument());
cache.Add(key1, *style, *parent);
EXPECT_TRUE(cache.Find(key1, *style, *parent));
EXPECT_TRUE(cache.Find(key1, *style, *ensured_parent));
cache.Add(key1, *style, *ensured_parent);
EXPECT_FALSE(cache.Find(key1, *style, *parent));
EXPECT_TRUE(cache.Find(key1, *style, *ensured_parent));
}
TEST_F(MatchedPropertiesCacheTest, EnsuredOutsideFlatTree) {
TestCache cache(GetDocument());
auto style = CreateStyle();
auto parent = CreateStyle();
auto ensured_style = CreateStyle();
ensured_style->SetIsEnsuredOutsideFlatTree();
TestKey key1("display:block", 1, GetDocument());
cache.Add(key1, *style, *parent);
EXPECT_TRUE(cache.Find(key1, *style, *parent));
EXPECT_TRUE(cache.Find(key1, *ensured_style, *parent));
cache.Add(key1, *ensured_style, *parent);
EXPECT_FALSE(cache.Find(key1, *style, *parent));
EXPECT_TRUE(cache.Find(key1, *ensured_style, *parent));
}
TEST_F(MatchedPropertiesCacheTest, EnsuredOutsideFlatTreeAndDisplayNone) {
TestCache cache(GetDocument());
auto parent = CreateStyle();
auto parent_none = CreateStyle();
auto style = CreateStyle();
auto style_flat = CreateStyle();
parent_none->SetIsEnsuredInDisplayNone();
style_flat->SetIsEnsuredOutsideFlatTree();
TestKey key1("display:block", 1, GetDocument());
cache.Add(key1, *style, *parent_none);
EXPECT_TRUE(cache.Find(key1, *style_flat, *parent));
cache.Add(key1, *style_flat, *parent);
EXPECT_TRUE(cache.Find(key1, *style, *parent_none));
}
TEST_F(MatchedPropertiesCacheTest, WritingModeDependency) {
TestCache cache(GetDocument());
auto parent_a = CreateStyle();
auto parent_b = CreateStyle();
auto style_a = CreateStyle();
auto style_b = CreateStyle();
parent_a->SetWritingMode(WritingMode::kHorizontalTb);
parent_b->SetWritingMode(WritingMode::kVerticalRl);
TestKey key("display:block", 1, GetDocument());
cache.Add(key, *style_a, *parent_a);
EXPECT_TRUE(cache.Find(key, *style_a, *parent_a));
EXPECT_TRUE(cache.Find(key, *style_b, *parent_a));
EXPECT_FALSE(cache.Find(key, *style_b, *parent_b));
}
TEST_F(MatchedPropertiesCacheTest, DirectionDependency) {
TestCache cache(GetDocument());
auto parent_a = CreateStyle();
auto parent_b = CreateStyle();
auto style_a = CreateStyle();
auto style_b = CreateStyle();
parent_a->SetDirection(TextDirection::kLtr);
parent_b->SetDirection(TextDirection::kRtl);
TestKey key("display:block", 1, GetDocument());
cache.Add(key, *style_a, *parent_a);
EXPECT_TRUE(cache.Find(key, *style_a, *parent_a));
EXPECT_TRUE(cache.Find(key, *style_b, *parent_a));
EXPECT_FALSE(cache.Find(key, *style_b, *parent_b));
}
TEST_F(MatchedPropertiesCacheTest, VariableDependency) {
TestCache cache(GetDocument());
auto parent_a = CreateStyle();
auto parent_b = CreateStyle();
auto style_a = CreateStyle();
auto style_b = CreateStyle();
parent_a->SetVariableData("--x", CreateVariableData("1px"), true);
parent_b->SetVariableData("--x", CreateVariableData("2px"), true);
style_a->SetHasVariableReferenceFromNonInheritedProperty();
style_b->SetHasVariableReferenceFromNonInheritedProperty();
TestKey key("top:var(--x)", 1, GetDocument());
cache.Add(key, *style_a, *parent_a);
EXPECT_TRUE(cache.Find(key, *style_a, *parent_a));
EXPECT_TRUE(cache.Find(key, *style_b, *parent_a));
EXPECT_FALSE(cache.Find(key, *style_b, *parent_b));
}
TEST_F(MatchedPropertiesCacheTest, VariableDependencyNoVars) {
TestCache cache(GetDocument());
auto parent_a = CreateStyle();
auto parent_b = CreateStyle();
auto style_a = CreateStyle();
auto style_b = CreateStyle();
style_a->SetHasVariableReferenceFromNonInheritedProperty();
style_b->SetHasVariableReferenceFromNonInheritedProperty();
TestKey key("top:var(--x)", 1, GetDocument());
cache.Add(key, *style_a, *parent_a);
// parent_a/b both have no variables, so this should be a cache hit.
EXPECT_TRUE(cache.Find(key, *style_a, *parent_a));
EXPECT_TRUE(cache.Find(key, *style_b, *parent_a));
EXPECT_TRUE(cache.Find(key, *style_b, *parent_b));
}
TEST_F(MatchedPropertiesCacheTest, NoVariableDependency) {
TestCache cache(GetDocument());
auto parent_a = CreateStyle();
auto parent_b = CreateStyle();
auto style_a = CreateStyle();
auto style_b = CreateStyle();
parent_a->SetVariableData("--x", CreateVariableData("1px"), true);
parent_b->SetVariableData("--x", CreateVariableData("2px"), true);
TestKey key("top:var(--x)", 1, GetDocument());
cache.Add(key, *style_a, *parent_a);
// parent_a/b both have variables, but style_a/b is not marked as
// depending on them.
EXPECT_TRUE(cache.Find(key, *style_a, *parent_a));
EXPECT_TRUE(cache.Find(key, *style_b, *parent_a));
EXPECT_TRUE(cache.Find(key, *style_b, *parent_b));
}
} // namespace blink