Commit 962a2ec6 authored by Xiaocheng Hu's avatar Xiaocheng Hu Committed by Chromium LUCI CQ

Add a dirty bit to CounterStyle to indicate style & layout invalidation

This patch introduces a dirty bit to CounterStyle to indicate whether
the information is stale. In general, a CounterStyle is dirtied if its
underlying @counter-style rule has changed, or if any other
@counter-style rule that it can reach via the 'extends' and 'fallback'
references have changed. A dirtied CounterStyle will be removed or
replaced in the corresponding CounterStyleMap.

The overall process is very similar to how FontFallbackList is dirtied
when @font-face rules or loading status are changed.

This dirty bit allows partial invalidation of style and layout after
@counter-style rule changes. A follow-up patch will implement such
partial invalidation.

Bug: 687225
Change-Id: I950af0b76381bfdb88adcb5a4ce38ca990e6b6cc
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2641157Reviewed-by: default avatarNektarios Paisios <nektar@chromium.org>
Reviewed-by: default avatarRune Lillesveen <futhark@chromium.org>
Reviewed-by: default avatarAaron Leventhal <aleventhal@chromium.org>
Reviewed-by: default avatarAnders Hartvoll Ruud <andruud@chromium.org>
Commit-Queue: Xiaocheng Hu <xiaochengh@chromium.org>
Cr-Commit-Position: refs/heads/master@{#845900}
parent 3ccfe1bd
...@@ -294,7 +294,7 @@ CounterStyle::CounterStyle(const StyleRuleCounterStyle& rule) ...@@ -294,7 +294,7 @@ CounterStyle::CounterStyle(const StyleRuleCounterStyle& rule)
// TODO(crbug.com/687225): Implement 'speak-as'. // TODO(crbug.com/687225): Implement 'speak-as'.
} }
void CounterStyle::ResolveExtends(const CounterStyle& extended) { void CounterStyle::ResolveExtends(CounterStyle& extended) {
DCHECK_NE(extended.system_, CounterStyleSystem::kUnresolvedExtends); DCHECK_NE(extended.system_, CounterStyleSystem::kUnresolvedExtends);
extended_style_ = extended; extended_style_ = extended;
...@@ -333,20 +333,6 @@ void CounterStyle::ResolveExtends(const CounterStyle& extended) { ...@@ -333,20 +333,6 @@ void CounterStyle::ResolveExtends(const CounterStyle& extended) {
// TODO(crbug.com/687225): Implement 'speak-as'. // TODO(crbug.com/687225): Implement 'speak-as'.
} }
void CounterStyle::ResetExtends() {
if (extends_name_.IsNull() || extends_name_ == "decimal" ||
extends_name_ == "disc")
return;
system_ = CounterStyleSystem::kUnresolvedExtends;
extended_style_.Clear();
}
void CounterStyle::ResetFallback() {
if (fallback_name_ == "decimal" || fallback_name_ == "disc")
return;
fallback_style_.Clear();
}
bool CounterStyle::RangeContains(int value) const { bool CounterStyle::RangeContains(int value) const {
if (range_.size()) { if (range_.size()) {
for (const auto& bounds : range_) { for (const auto& bounds : range_) {
...@@ -469,6 +455,34 @@ String CounterStyle::GenerateInitialRepresentation(int value) const { ...@@ -469,6 +455,34 @@ String CounterStyle::GenerateInitialRepresentation(int value) const {
return result.ToString(); return result.ToString();
} }
void CounterStyle::TraverseAndMarkDirtyIfNeeded(
HeapHashSet<Member<CounterStyle>>& visited_counter_styles) {
if (IsPredefined() || visited_counter_styles.Contains(this))
return;
visited_counter_styles.insert(this);
if (has_inexistent_references_) {
SetIsDirty();
return;
}
if (extended_style_) {
extended_style_->TraverseAndMarkDirtyIfNeeded(visited_counter_styles);
if (extended_style_->IsDirty()) {
SetIsDirty();
return;
}
}
if (fallback_style_) {
fallback_style_->TraverseAndMarkDirtyIfNeeded(visited_counter_styles);
if (fallback_style_->IsDirty()) {
SetIsDirty();
return;
}
}
}
void CounterStyle::Trace(Visitor* visitor) const { void CounterStyle::Trace(Visitor* visitor) const {
visitor->Trace(style_rule_); visitor->Trace(style_rule_);
visitor->Trace(extended_style_); visitor->Trace(extended_style_);
......
...@@ -31,14 +31,33 @@ class CORE_EXPORT CounterStyle final : public GarbageCollected<CounterStyle> { ...@@ -31,14 +31,33 @@ class CORE_EXPORT CounterStyle final : public GarbageCollected<CounterStyle> {
// Returns nullptr if the @counter-style rule is invalid. // Returns nullptr if the @counter-style rule is invalid.
static CounterStyle* Create(const StyleRuleCounterStyle&); static CounterStyle* Create(const StyleRuleCounterStyle&);
const StyleRuleCounterStyle& GetStyleRule() const { return *style_rule_; }
AtomicString GetName() const; AtomicString GetName() const;
CounterStyleSystem GetSystem() const { return system_; } CounterStyleSystem GetSystem() const { return system_; }
bool IsPredefined() const { return is_predefined_; }
void SetIsPredefined() { is_predefined_ = true; }
// Returns true for the predefined symbolic counter styles 'disc', 'circle', // Returns true for the predefined symbolic counter styles 'disc', 'circle',
// 'square', 'disclosure-open' and 'disclosure-closed'. // 'square', 'disclosure-open' and 'disclosure-closed'.
bool IsPredefinedSymbolMarker() const { return is_predefined_symbol_marker_; } bool IsPredefinedSymbolMarker() const { return is_predefined_symbol_marker_; }
void SetIsPredefinedSymbolMarker() { is_predefined_symbol_marker_ = true; } void SetIsPredefinedSymbolMarker() { is_predefined_symbol_marker_ = true; }
// A CounterStyle object is dirtied when the information it holds becomes
// stale, e.g., when the style rule mutated or the 'extends' or 'fallback'
// counter styles mutated, etc. Once dirtied, it will never be reused, and
// will be removed or replaced by a newly created clean CounterStyle.
// Elements using dirty CounterStyles should update style and layout.
bool IsDirty() const { return is_dirty_; }
void SetIsDirty() { is_dirty_ = true; }
void TraverseAndMarkDirtyIfNeeded(HeapHashSet<Member<CounterStyle>>& visited);
// Set to true when there's no counter style matching 'extends' or 'fallback',
// and therefore we are resorting to 'decimal'.
void SetHasInexistentReferences() { has_inexistent_references_ = true; }
// https://drafts.csswg.org/css-counter-styles/#generate-a-counter // https://drafts.csswg.org/css-counter-styles/#generate-a-counter
String GenerateRepresentation(int value) const; String GenerateRepresentation(int value) const;
...@@ -50,18 +69,12 @@ class CORE_EXPORT CounterStyle final : public GarbageCollected<CounterStyle> { ...@@ -50,18 +69,12 @@ class CORE_EXPORT CounterStyle final : public GarbageCollected<CounterStyle> {
bool HasUnresolvedExtends() const { bool HasUnresolvedExtends() const {
return system_ == CounterStyleSystem::kUnresolvedExtends; return system_ == CounterStyleSystem::kUnresolvedExtends;
} }
void ResolveExtends(const CounterStyle& extended); void ResolveExtends(CounterStyle& extended);
AtomicString GetFallbackName() const { return fallback_name_; } AtomicString GetFallbackName() const { return fallback_name_; }
const CounterStyle& GetFallbackStyle() const { return *fallback_style_; } const CounterStyle& GetFallbackStyle() const { return *fallback_style_; }
bool HasUnresolvedFallback() const { return !fallback_style_; } bool HasUnresolvedFallback() const { return !fallback_style_; }
void ResolveFallback(const CounterStyle& fallback) { void ResolveFallback(CounterStyle& fallback) { fallback_style_ = &fallback; }
fallback_style_ = &fallback;
}
// Resets the resolution of 'extends' and 'fallback' for recomputing it.
void ResetExtends();
void ResetFallback();
void Trace(Visitor*) const; void Trace(Visitor*) const;
...@@ -93,10 +106,10 @@ class CORE_EXPORT CounterStyle final : public GarbageCollected<CounterStyle> { ...@@ -93,10 +106,10 @@ class CORE_EXPORT CounterStyle final : public GarbageCollected<CounterStyle> {
CounterStyleSystem system_ = CounterStyleSystem::kSymbolic; CounterStyleSystem system_ = CounterStyleSystem::kSymbolic;
AtomicString extends_name_; AtomicString extends_name_;
Member<const CounterStyle> extended_style_; Member<CounterStyle> extended_style_;
AtomicString fallback_name_ = "decimal"; AtomicString fallback_name_ = "decimal";
Member<const CounterStyle> fallback_style_; Member<CounterStyle> fallback_style_;
// True if we are looking for a fallback counter style to generate a counter // True if we are looking for a fallback counter style to generate a counter
// value. Supports cycle detection in fallback. // value. Supports cycle detection in fallback.
...@@ -124,7 +137,10 @@ class CORE_EXPORT CounterStyle final : public GarbageCollected<CounterStyle> { ...@@ -124,7 +137,10 @@ class CORE_EXPORT CounterStyle final : public GarbageCollected<CounterStyle> {
// First symbol value, for 'fixed' system only. // First symbol value, for 'fixed' system only.
wtf_size_t first_symbol_value_ = 1; wtf_size_t first_symbol_value_ = 1;
bool is_predefined_ = false;
bool is_predefined_symbol_marker_ = false; bool is_predefined_symbol_marker_ = false;
bool has_inexistent_references_ = false;
bool is_dirty_ = false;
friend class CounterStyleMapTest; friend class CounterStyleMapTest;
}; };
......
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
#include "third_party/blink/renderer/core/css/style_engine.h" #include "third_party/blink/renderer/core/css/style_engine.h"
#include "third_party/blink/renderer/core/css/style_rule_counter_style.h" #include "third_party/blink/renderer/core/css/style_rule_counter_style.h"
#include "third_party/blink/renderer/core/dom/document.h" #include "third_party/blink/renderer/core/dom/document.h"
#include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
namespace blink { namespace blink {
...@@ -22,16 +23,23 @@ CounterStyleMap* CreateUACounterStyleMap() { ...@@ -22,16 +23,23 @@ CounterStyleMap* CreateUACounterStyleMap() {
CounterStyleMap* map = CounterStyleMap* map =
MakeGarbageCollected<CounterStyleMap>(nullptr, nullptr); MakeGarbageCollected<CounterStyleMap>(nullptr, nullptr);
map->AddCounterStyles(*CSSDefaultStyleSheets::Instance().DefaultStyle()); map->AddCounterStyles(*CSSDefaultStyleSheets::Instance().DefaultStyle());
map->SetIsPredefined();
for (const char* symbol_marker : predefined_symbol_markers) { for (const char* symbol_marker : predefined_symbol_markers) {
map->FindCounterStyleAcrossScopes(symbol_marker) map->FindCounterStyleAcrossScopes(symbol_marker)
.SetIsPredefinedSymbolMarker(); ->SetIsPredefinedSymbolMarker();
} }
map->ResolveReferences(); HeapHashSet<Member<CounterStyleMap>> dummy_visited;
map->ResolveReferences(dummy_visited);
return map; return map;
} }
} // namespace } // namespace
void CounterStyleMap::SetIsPredefined() {
for (CounterStyle* counter_style : counter_styles_.Values())
counter_style->SetIsPredefined();
}
// static // static
CounterStyleMap* CounterStyleMap::GetUACounterStyleMap() { CounterStyleMap* CounterStyleMap::GetUACounterStyleMap() {
DEFINE_STATIC_LOCAL(Persistent<CounterStyleMap>, ua_counter_style_map, DEFINE_STATIC_LOCAL(Persistent<CounterStyleMap>, ua_counter_style_map,
...@@ -79,9 +87,6 @@ void CounterStyleMap::AddCounterStyles(const RuleSet& rule_set) { ...@@ -79,9 +87,6 @@ void CounterStyleMap::AddCounterStyles(const RuleSet& rule_set) {
if (!counter_style) if (!counter_style)
continue; continue;
counter_styles_.Set(rule->GetName(), counter_style); counter_styles_.Set(rule->GetName(), counter_style);
if (counter_style->HasUnresolvedExtends() ||
counter_style->HasUnresolvedFallback())
has_unresolved_references_ = true;
} }
} }
...@@ -107,15 +112,15 @@ CounterStyleMap* CounterStyleMap::GetAncestorMap() const { ...@@ -107,15 +112,15 @@ CounterStyleMap* CounterStyleMap::GetAncestorMap() const {
return nullptr; return nullptr;
} }
CounterStyle& CounterStyleMap::FindCounterStyleAcrossScopes( CounterStyle* CounterStyleMap::FindCounterStyleAcrossScopes(
const AtomicString& name) const { const AtomicString& name) const {
if (CounterStyle* style = counter_styles_.at(name)) if (CounterStyle* style = counter_styles_.at(name))
return *style; return style;
if (CounterStyleMap* ancestor_map = GetAncestorMap()) if (CounterStyleMap* ancestor_map = GetAncestorMap())
return ancestor_map->FindCounterStyleAcrossScopes(name); return ancestor_map->FindCounterStyleAcrossScopes(name);
return CounterStyle::GetDecimal(); return nullptr;
} }
void CounterStyleMap::ResolveExtendsFor(CounterStyle& counter_style) { void CounterStyleMap::ResolveExtendsFor(CounterStyle& counter_style) {
...@@ -127,14 +132,15 @@ void CounterStyleMap::ResolveExtendsFor(CounterStyle& counter_style) { ...@@ -127,14 +132,15 @@ void CounterStyleMap::ResolveExtendsFor(CounterStyle& counter_style) {
do { do {
unresolved_styles.insert(extends_chain.back()); unresolved_styles.insert(extends_chain.back());
AtomicString extends_name = extends_chain.back()->GetExtendsName(); AtomicString extends_name = extends_chain.back()->GetExtendsName();
extends_chain.push_back(&FindCounterStyleAcrossScopes(extends_name)); extends_chain.push_back(FindCounterStyleAcrossScopes(extends_name));
} while (extends_chain.back()->HasUnresolvedExtends() && } while (extends_chain.back() &&
extends_chain.back()->HasUnresolvedExtends() &&
!unresolved_styles.Contains(extends_chain.back())); !unresolved_styles.Contains(extends_chain.back()));
// If one or more @counter-style rules form a cycle with their extends values, // If one or more @counter-style rules form a cycle with their extends values,
// all of the counter styles participating in the cycle must be treated as if // all of the counter styles participating in the cycle must be treated as if
// they were extending the 'decimal' counter style instead. // they were extending the 'decimal' counter style instead.
if (extends_chain.back()->HasUnresolvedExtends()) { if (extends_chain.back() && extends_chain.back()->HasUnresolvedExtends()) {
CounterStyle* cycle_start = extends_chain.back(); CounterStyle* cycle_start = extends_chain.back();
do { do {
extends_chain.back()->ResolveExtends(CounterStyle::GetDecimal()); extends_chain.back()->ResolveExtends(CounterStyle::GetDecimal());
...@@ -145,7 +151,13 @@ void CounterStyleMap::ResolveExtendsFor(CounterStyle& counter_style) { ...@@ -145,7 +151,13 @@ void CounterStyleMap::ResolveExtendsFor(CounterStyle& counter_style) {
CounterStyle* next = extends_chain.back(); CounterStyle* next = extends_chain.back();
while (extends_chain.size() > 1u) { while (extends_chain.size() > 1u) {
extends_chain.pop_back(); extends_chain.pop_back();
extends_chain.back()->ResolveExtends(*next); if (next) {
extends_chain.back()->ResolveExtends(*next);
} else {
extends_chain.back()->ResolveExtends(CounterStyle::GetDecimal());
extends_chain.back()->SetHasInexistentReferences();
}
next = extends_chain.back(); next = extends_chain.back();
} }
} }
...@@ -153,37 +165,68 @@ void CounterStyleMap::ResolveExtendsFor(CounterStyle& counter_style) { ...@@ -153,37 +165,68 @@ void CounterStyleMap::ResolveExtendsFor(CounterStyle& counter_style) {
void CounterStyleMap::ResolveFallbackFor(CounterStyle& counter_style) { void CounterStyleMap::ResolveFallbackFor(CounterStyle& counter_style) {
DCHECK(counter_style.HasUnresolvedFallback()); DCHECK(counter_style.HasUnresolvedFallback());
AtomicString fallback_name = counter_style.GetFallbackName(); AtomicString fallback_name = counter_style.GetFallbackName();
CounterStyle& fallback_style = FindCounterStyleAcrossScopes(fallback_name); CounterStyle* fallback_style = FindCounterStyleAcrossScopes(fallback_name);
counter_style.ResolveFallback(fallback_style); if (fallback_style) {
counter_style.ResolveFallback(*fallback_style);
} else {
counter_style.ResolveFallback(CounterStyle::GetDecimal());
counter_style.SetHasInexistentReferences();
}
} }
void CounterStyleMap::ResolveReferences() { void CounterStyleMap::ResolveReferences(
HeapHashSet<Member<CounterStyleMap>>& visited_maps) {
if (visited_maps.Contains(this))
return;
visited_maps.insert(this);
// References in ancestor scopes must be resolved first. // References in ancestor scopes must be resolved first.
if (ancestors_have_unresolved_references_) { if (CounterStyleMap* ancestor_map = GetAncestorMap())
if (CounterStyleMap* ancestor_map = GetAncestorMap()) ancestor_map->ResolveReferences(visited_maps);
ancestor_map->ResolveReferences();
ancestors_have_unresolved_references_ = false; for (CounterStyle* counter_style : counter_styles_.Values()) {
if (counter_style->HasUnresolvedExtends())
ResolveExtendsFor(*counter_style);
if (counter_style->HasUnresolvedFallback())
ResolveFallbackFor(*counter_style);
} }
}
if (!has_unresolved_references_) void CounterStyleMap::MarkDirtyCounterStyles(
return; HeapHashSet<Member<CounterStyle>>& visited_counter_styles) {
has_unresolved_references_ = false; for (CounterStyle* counter_style : counter_styles_.Values())
for (auto iter : counter_styles_) { counter_style->TraverseAndMarkDirtyIfNeeded(visited_counter_styles);
if (iter.value->HasUnresolvedExtends())
ResolveExtendsFor(*iter.value); // Replace dirty CounterStyles by clean ones with unresolved references.
if (iter.value->HasUnresolvedFallback()) for (Member<CounterStyle>& counter_style_ref : counter_styles_.Values()) {
ResolveFallbackFor(*iter.value); if (counter_style_ref->IsDirty()) {
CounterStyle* clean_style =
MakeGarbageCollected<CounterStyle>(counter_style_ref->GetStyleRule());
counter_style_ref = clean_style;
}
} }
} }
void CounterStyleMap::ResetReferences() { // static
for (auto iter : counter_styles_) { void CounterStyleMap::MarkAllDirtyCounterStyles(
CounterStyle* counter_style = iter.value; Document& document,
counter_style->ResetExtends(); const HeapHashSet<Member<TreeScope>>& active_tree_scopes) {
counter_style->ResetFallback(); // Traverse all CounterStyle objects in the document to mark dirtiness.
if (counter_style->HasUnresolvedExtends() || // We assume that there are not too many CounterStyle objects, so this won't
counter_style->HasUnresolvedFallback()) // be a performance bottleneck.
has_unresolved_references_ = true; TRACE_EVENT0("blink", "CounterStyleMap::MarkAllDirtyCounterStyles");
HeapHashSet<Member<CounterStyle>> visited_counter_styles;
if (CounterStyleMap* user_map = GetUserCounterStyleMap(document))
user_map->MarkDirtyCounterStyles(visited_counter_styles);
if (CounterStyleMap* document_map = GetAuthorCounterStyleMap(document))
document_map->MarkDirtyCounterStyles(visited_counter_styles);
for (const TreeScope* scope : active_tree_scopes) {
if (CounterStyleMap* scoped_map = GetAuthorCounterStyleMap(*scope))
scoped_map->MarkDirtyCounterStyles(visited_counter_styles);
} }
} }
...@@ -191,33 +234,39 @@ void CounterStyleMap::ResetReferences() { ...@@ -191,33 +234,39 @@ void CounterStyleMap::ResetReferences() {
void CounterStyleMap::ResolveAllReferences( void CounterStyleMap::ResolveAllReferences(
Document& document, Document& document,
const HeapHashSet<Member<TreeScope>>& active_tree_scopes) { const HeapHashSet<Member<TreeScope>>& active_tree_scopes) {
// Make sure the UA counter style map is already set up, so that we don't // Traverse all counter style maps to find and update CounterStyles that are
// enter a recursion when resolving references in user and author rules. // dirty or have unresolved references. We assume there are not too many
GetUACounterStyleMap(); // CounterStyles, so that this won't be a performance bottleneck.
TRACE_EVENT0("blink", "CounterStyleMap::ResolveAllReferences");
HeapHashSet<Member<CounterStyleMap>> visited_maps;
visited_maps.insert(GetUACounterStyleMap());
if (CounterStyleMap* user_map = GetUserCounterStyleMap(document)) if (CounterStyleMap* user_map = GetUserCounterStyleMap(document))
user_map->ResolveReferences(); user_map->ResolveReferences(visited_maps);
if (CounterStyleMap* document_map = GetAuthorCounterStyleMap(document)) if (CounterStyleMap* document_map = GetAuthorCounterStyleMap(document))
document_map->ResolveReferences(); document_map->ResolveReferences(visited_maps);
// It is hard to keep track of whether we should update references in a
// shadow tree scope. They may need update even when the active style
// sheets remain unchanged in the scope, but some ancestor scope changed.
// So we reset and re-resolve all shadow tree scopes unconditionally.
// TODO(crbug.com/687225): This might need optimizations in some cases. For
// example, we don't want to invalidate the whole document when inserting a
// web component.
for (const TreeScope* scope : active_tree_scopes) { for (const TreeScope* scope : active_tree_scopes) {
if (CounterStyleMap* scoped_map = GetAuthorCounterStyleMap(*scope)) { if (CounterStyleMap* scoped_map = GetAuthorCounterStyleMap(*scope)) {
scoped_map->ResetReferences(); scoped_map->ResolveReferences(visited_maps);
scoped_map->ancestors_have_unresolved_references_ = true;
#if DCHECK_IS_ON()
for (CounterStyle* counter_style : scoped_map->counter_styles_.Values()) {
DCHECK(!counter_style->IsDirty());
DCHECK(!counter_style->HasUnresolvedExtends());
DCHECK(!counter_style->HasUnresolvedFallback());
}
#endif
} }
} }
for (const TreeScope* scope : active_tree_scopes) { }
if (CounterStyleMap* scoped_map = GetAuthorCounterStyleMap(*scope))
scoped_map->ResolveReferences(); void CounterStyleMap::Dispose() {
} for (CounterStyle* counter_style : counter_styles_.Values())
counter_style->SetIsDirty();
counter_styles_.clear();
} }
void CounterStyleMap::Trace(Visitor* visitor) const { void CounterStyleMap::Trace(Visitor* visitor) const {
......
...@@ -26,19 +26,21 @@ class CORE_EXPORT CounterStyleMap : public GarbageCollected<CounterStyleMap> { ...@@ -26,19 +26,21 @@ class CORE_EXPORT CounterStyleMap : public GarbageCollected<CounterStyleMap> {
static CounterStyleMap* CreateUserCounterStyleMap(Document&); static CounterStyleMap* CreateUserCounterStyleMap(Document&);
static CounterStyleMap* CreateAuthorCounterStyleMap(TreeScope&); static CounterStyleMap* CreateAuthorCounterStyleMap(TreeScope&);
CounterStyle& FindCounterStyleAcrossScopes(const AtomicString& name) const; CounterStyle* FindCounterStyleAcrossScopes(const AtomicString& name) const;
void AddCounterStyles(const RuleSet&); void AddCounterStyles(const RuleSet&);
void SetIsPredefined();
// Resets all 'extends' and 'fallback' references to unresolved. Used when the void ResolveReferences(HeapHashSet<Member<CounterStyleMap>>& resolved_maps);
// counter styles in an ancestor scope are changed, which may affect the
// references in the current scope;
void ResetReferences();
void ResolveReferences();
static void ResolveAllReferences(Document&, static void ResolveAllReferences(Document&,
const HeapHashSet<Member<TreeScope>>&); const HeapHashSet<Member<TreeScope>>&);
void MarkDirtyCounterStyles(HeapHashSet<Member<CounterStyle>>& visited);
static void MarkAllDirtyCounterStyles(Document&,
const HeapHashSet<Member<TreeScope>>&);
void Dispose();
CounterStyleMap(Document* document, TreeScope* tree_scope); CounterStyleMap(Document* document, TreeScope* tree_scope);
void Trace(Visitor*) const; void Trace(Visitor*) const;
...@@ -56,9 +58,6 @@ class CORE_EXPORT CounterStyleMap : public GarbageCollected<CounterStyleMap> { ...@@ -56,9 +58,6 @@ class CORE_EXPORT CounterStyleMap : public GarbageCollected<CounterStyleMap> {
HeapHashMap<AtomicString, Member<CounterStyle>> counter_styles_; HeapHashMap<AtomicString, Member<CounterStyle>> counter_styles_;
bool has_unresolved_references_ = false;
bool ancestors_have_unresolved_references_ = false;
friend class CounterStyleMapTest; friend class CounterStyleMapTest;
}; };
......
...@@ -183,15 +183,22 @@ TEST_F(CounterStyleMapTest, UpdateReferencesInChildScope) { ...@@ -183,15 +183,22 @@ TEST_F(CounterStyleMapTest, UpdateReferencesInChildScope) {
"<style>@counter-style bar { system: extends foo; }</style>"); "<style>@counter-style bar { system: extends foo; }</style>");
UpdateAllLifecyclePhasesForTest(); UpdateAllLifecyclePhasesForTest();
const CounterStyle& foo = GetCounterStyle(GetDocument(), "foo");
const CounterStyle& bar = GetCounterStyle(shadow, "bar"); const CounterStyle& bar = GetCounterStyle(shadow, "bar");
EXPECT_EQ("foo", bar.GetExtendedStyle().GetName()); EXPECT_EQ(&foo, &bar.GetExtendedStyle());
GetDocument().QuerySelector("style")->remove(); GetDocument().QuerySelector("style")->remove();
UpdateAllLifecyclePhasesForTest(); UpdateAllLifecyclePhasesForTest();
// Counter styles in child scopes should be updated after chaning the counter // After counter style rule changes in the parent scope, the original
// styles in the parent scope. // CounterStyle for 'bar' in child scopes will be dirtied, and will be
EXPECT_EQ("decimal", bar.GetExtendedStyle().GetName()); // replaced by a new CounterStyle object.
EXPECT_TRUE(foo.IsDirty());
EXPECT_TRUE(bar.IsDirty());
const CounterStyle& new_bar = GetCounterStyle(shadow, "bar");
EXPECT_NE(&bar, &new_bar);
EXPECT_EQ("decimal", new_bar.GetExtendedStyle().GetName());
} }
} // namespace blink } // namespace blink
...@@ -20,9 +20,9 @@ class CounterStyleTest : public PageTestBase, ...@@ -20,9 +20,9 @@ class CounterStyleTest : public PageTestBase,
const CounterStyle& GetCounterStyle(const AtomicString& name) { const CounterStyle& GetCounterStyle(const AtomicString& name) {
if (const CounterStyleMap* document_map = if (const CounterStyleMap* document_map =
CounterStyleMap::GetAuthorCounterStyleMap(GetDocument())) CounterStyleMap::GetAuthorCounterStyleMap(GetDocument()))
return document_map->FindCounterStyleAcrossScopes(name); return *document_map->FindCounterStyleAcrossScopes(name);
return CounterStyleMap::GetUACounterStyleMap() return *CounterStyleMap::GetUACounterStyleMap()
->FindCounterStyleAcrossScopes(name); ->FindCounterStyleAcrossScopes(name);
} }
const CounterStyle AddCounterStyle(const AtomicString& name, const CounterStyle AddCounterStyle(const AtomicString& name,
......
...@@ -140,7 +140,8 @@ void ScopedStyleResolver::ResetStyle() { ...@@ -140,7 +140,8 @@ void ScopedStyleResolver::ResetStyle() {
viewport_dependent_media_query_results_.clear(); viewport_dependent_media_query_results_.clear();
device_dependent_media_query_results_.clear(); device_dependent_media_query_results_.clear();
keyframes_rule_map_.clear(); keyframes_rule_map_.clear();
counter_style_map_.Clear(); if (counter_style_map_)
counter_style_map_->Dispose();
slotted_rule_set_ = nullptr; slotted_rule_set_ = nullptr;
needs_append_all_sheets_ = false; needs_append_all_sheets_ = false;
} }
......
...@@ -542,6 +542,10 @@ void StyleEngine::UpdateActiveStyleSheets() { ...@@ -542,6 +542,10 @@ void StyleEngine::UpdateActiveStyleSheets() {
} }
if (RuntimeEnabledFeatures::CSSAtRuleCounterStyleEnabled()) { if (RuntimeEnabledFeatures::CSSAtRuleCounterStyleEnabled()) {
// TODO(crbug.com/687225): Add a flag to indicate whether counter styles
// need updates, so that we don't update them every time.
CounterStyleMap::MarkAllDirtyCounterStyles(GetDocument(),
active_tree_scopes_);
CounterStyleMap::ResolveAllReferences(GetDocument(), active_tree_scopes_); CounterStyleMap::ResolveAllReferences(GetDocument(), active_tree_scopes_);
} }
...@@ -1558,8 +1562,8 @@ void StyleEngine::ApplyUserRuleSetChanges( ...@@ -1558,8 +1562,8 @@ void StyleEngine::ApplyUserRuleSetChanges(
} }
if (changed_rule_flags & kCounterStyleRules) { if (changed_rule_flags & kCounterStyleRules) {
if (change == kActiveSheetsChanged) if (change == kActiveSheetsChanged && user_counter_style_map_)
user_counter_style_map_.Clear(); user_counter_style_map_->Dispose();
for (auto* it = new_style_sheets.begin(); it != new_style_sheets.end(); for (auto* it = new_style_sheets.begin(); it != new_style_sheets.end();
it++) { it++) {
...@@ -1568,10 +1572,6 @@ void StyleEngine::ApplyUserRuleSetChanges( ...@@ -1568,10 +1572,6 @@ void StyleEngine::ApplyUserRuleSetChanges(
EnsureUserCounterStyleMap().AddCounterStyles(*it->second); EnsureUserCounterStyleMap().AddCounterStyles(*it->second);
} }
if (CounterStyleMap* doc_map =
CounterStyleMap::GetAuthorCounterStyleMap(GetDocument()))
doc_map->ResetReferences();
// TODO(crbug.com/687225): Trigger style/Layout invalidations. // TODO(crbug.com/687225): Trigger style/Layout invalidations.
} }
...@@ -2391,16 +2391,22 @@ CounterStyleMap& StyleEngine::EnsureUserCounterStyleMap() { ...@@ -2391,16 +2391,22 @@ CounterStyleMap& StyleEngine::EnsureUserCounterStyleMap() {
const CounterStyle& StyleEngine::FindCounterStyleAcrossScopes( const CounterStyle& StyleEngine::FindCounterStyleAcrossScopes(
const AtomicString& name, const AtomicString& name,
const TreeScope* scope) const { const TreeScope* scope) const {
CounterStyleMap* target_map = nullptr;
while (scope) { while (scope) {
if (CounterStyleMap* map = if (CounterStyleMap* map =
CounterStyleMap::GetAuthorCounterStyleMap(*scope)) CounterStyleMap::GetAuthorCounterStyleMap(*scope)) {
return map->FindCounterStyleAcrossScopes(name); target_map = map;
break;
}
scope = scope->ParentTreeScope(); scope = scope->ParentTreeScope();
} }
if (user_counter_style_map_) if (!target_map && user_counter_style_map_)
return user_counter_style_map_->FindCounterStyleAcrossScopes(name); target_map = user_counter_style_map_;
return CounterStyleMap::GetUACounterStyleMap()->FindCounterStyleAcrossScopes( if (!target_map)
name); target_map = CounterStyleMap::GetUACounterStyleMap();
if (CounterStyle* result = target_map->FindCounterStyleAcrossScopes(name))
return *result;
return CounterStyle::GetDecimal();
} }
void StyleEngine::Trace(Visitor* visitor) const { void StyleEngine::Trace(Visitor* visitor) const {
......
...@@ -106,6 +106,8 @@ ScopedStyleResolver& TreeScope::EnsureScopedStyleResolver() { ...@@ -106,6 +106,8 @@ ScopedStyleResolver& TreeScope::EnsureScopedStyleResolver() {
} }
void TreeScope::ClearScopedStyleResolver() { void TreeScope::ClearScopedStyleResolver() {
if (scoped_style_resolver_)
scoped_style_resolver_->ResetStyle();
scoped_style_resolver_.Clear(); scoped_style_resolver_.Clear();
} }
......
...@@ -909,9 +909,8 @@ ax::mojom::blink::ListStyle AXLayoutObject::GetListStyle() const { ...@@ -909,9 +909,8 @@ ax::mojom::blink::ListStyle AXLayoutObject::GetListStyle() const {
// 'decimal-leading-zero' may be overridden by custom counter styles. We // 'decimal-leading-zero' may be overridden by custom counter styles. We
// return kNumeric only when we are using the predefined counter style. // return kNumeric only when we are using the predefined counter style.
if (!RuntimeEnabledFeatures::CSSAtRuleCounterStyleEnabled() || if (!RuntimeEnabledFeatures::CSSAtRuleCounterStyleEnabled() ||
&ListMarker::GetCounterStyle(*GetDocument(), *computed_style) == ListMarker::GetCounterStyle(*GetDocument(), *computed_style)
&CounterStyleMap::GetUACounterStyleMap() .IsPredefined())
->FindCounterStyleAcrossScopes("decimal-leading-zero"))
return ax::mojom::blink::ListStyle::kNumeric; return ax::mojom::blink::ListStyle::kNumeric;
} }
return ax::mojom::blink::ListStyle::kOther; return ax::mojom::blink::ListStyle::kOther;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment