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)
// TODO(crbug.com/687225): Implement 'speak-as'.
}
void CounterStyle::ResolveExtends(const CounterStyle& extended) {
void CounterStyle::ResolveExtends(CounterStyle& extended) {
DCHECK_NE(extended.system_, CounterStyleSystem::kUnresolvedExtends);
extended_style_ = extended;
......@@ -333,20 +333,6 @@ void CounterStyle::ResolveExtends(const CounterStyle& extended) {
// 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 {
if (range_.size()) {
for (const auto& bounds : range_) {
......@@ -469,6 +455,34 @@ String CounterStyle::GenerateInitialRepresentation(int value) const {
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 {
visitor->Trace(style_rule_);
visitor->Trace(extended_style_);
......
......@@ -31,14 +31,33 @@ class CORE_EXPORT CounterStyle final : public GarbageCollected<CounterStyle> {
// Returns nullptr if the @counter-style rule is invalid.
static CounterStyle* Create(const StyleRuleCounterStyle&);
const StyleRuleCounterStyle& GetStyleRule() const { return *style_rule_; }
AtomicString GetName() const;
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',
// 'square', 'disclosure-open' and 'disclosure-closed'.
bool IsPredefinedSymbolMarker() const { return is_predefined_symbol_marker_; }
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
String GenerateRepresentation(int value) const;
......@@ -50,18 +69,12 @@ class CORE_EXPORT CounterStyle final : public GarbageCollected<CounterStyle> {
bool HasUnresolvedExtends() const {
return system_ == CounterStyleSystem::kUnresolvedExtends;
}
void ResolveExtends(const CounterStyle& extended);
void ResolveExtends(CounterStyle& extended);
AtomicString GetFallbackName() const { return fallback_name_; }
const CounterStyle& GetFallbackStyle() const { return *fallback_style_; }
bool HasUnresolvedFallback() const { return !fallback_style_; }
void ResolveFallback(const CounterStyle& fallback) {
fallback_style_ = &fallback;
}
// Resets the resolution of 'extends' and 'fallback' for recomputing it.
void ResetExtends();
void ResetFallback();
void ResolveFallback(CounterStyle& fallback) { fallback_style_ = &fallback; }
void Trace(Visitor*) const;
......@@ -93,10 +106,10 @@ class CORE_EXPORT CounterStyle final : public GarbageCollected<CounterStyle> {
CounterStyleSystem system_ = CounterStyleSystem::kSymbolic;
AtomicString extends_name_;
Member<const CounterStyle> extended_style_;
Member<CounterStyle> extended_style_;
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
// value. Supports cycle detection in fallback.
......@@ -124,7 +137,10 @@ class CORE_EXPORT CounterStyle final : public GarbageCollected<CounterStyle> {
// First symbol value, for 'fixed' system only.
wtf_size_t first_symbol_value_ = 1;
bool is_predefined_ = false;
bool is_predefined_symbol_marker_ = false;
bool has_inexistent_references_ = false;
bool is_dirty_ = false;
friend class CounterStyleMapTest;
};
......
......@@ -10,6 +10,7 @@
#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/dom/document.h"
#include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
namespace blink {
......@@ -22,16 +23,23 @@ CounterStyleMap* CreateUACounterStyleMap() {
CounterStyleMap* map =
MakeGarbageCollected<CounterStyleMap>(nullptr, nullptr);
map->AddCounterStyles(*CSSDefaultStyleSheets::Instance().DefaultStyle());
map->SetIsPredefined();
for (const char* symbol_marker : predefined_symbol_markers) {
map->FindCounterStyleAcrossScopes(symbol_marker)
.SetIsPredefinedSymbolMarker();
->SetIsPredefinedSymbolMarker();
}
map->ResolveReferences();
HeapHashSet<Member<CounterStyleMap>> dummy_visited;
map->ResolveReferences(dummy_visited);
return map;
}
} // namespace
void CounterStyleMap::SetIsPredefined() {
for (CounterStyle* counter_style : counter_styles_.Values())
counter_style->SetIsPredefined();
}
// static
CounterStyleMap* CounterStyleMap::GetUACounterStyleMap() {
DEFINE_STATIC_LOCAL(Persistent<CounterStyleMap>, ua_counter_style_map,
......@@ -79,9 +87,6 @@ void CounterStyleMap::AddCounterStyles(const RuleSet& rule_set) {
if (!counter_style)
continue;
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 {
return nullptr;
}
CounterStyle& CounterStyleMap::FindCounterStyleAcrossScopes(
CounterStyle* CounterStyleMap::FindCounterStyleAcrossScopes(
const AtomicString& name) const {
if (CounterStyle* style = counter_styles_.at(name))
return *style;
return style;
if (CounterStyleMap* ancestor_map = GetAncestorMap())
return ancestor_map->FindCounterStyleAcrossScopes(name);
return CounterStyle::GetDecimal();
return nullptr;
}
void CounterStyleMap::ResolveExtendsFor(CounterStyle& counter_style) {
......@@ -127,14 +132,15 @@ void CounterStyleMap::ResolveExtendsFor(CounterStyle& counter_style) {
do {
unresolved_styles.insert(extends_chain.back());
AtomicString extends_name = extends_chain.back()->GetExtendsName();
extends_chain.push_back(&FindCounterStyleAcrossScopes(extends_name));
} while (extends_chain.back()->HasUnresolvedExtends() &&
extends_chain.push_back(FindCounterStyleAcrossScopes(extends_name));
} while (extends_chain.back() &&
extends_chain.back()->HasUnresolvedExtends() &&
!unresolved_styles.Contains(extends_chain.back()));
// 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
// 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();
do {
extends_chain.back()->ResolveExtends(CounterStyle::GetDecimal());
......@@ -145,7 +151,13 @@ void CounterStyleMap::ResolveExtendsFor(CounterStyle& counter_style) {
CounterStyle* next = extends_chain.back();
while (extends_chain.size() > 1u) {
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();
}
}
......@@ -153,37 +165,68 @@ void CounterStyleMap::ResolveExtendsFor(CounterStyle& counter_style) {
void CounterStyleMap::ResolveFallbackFor(CounterStyle& counter_style) {
DCHECK(counter_style.HasUnresolvedFallback());
AtomicString fallback_name = counter_style.GetFallbackName();
CounterStyle& fallback_style = FindCounterStyleAcrossScopes(fallback_name);
counter_style.ResolveFallback(fallback_style);
CounterStyle* fallback_style = FindCounterStyleAcrossScopes(fallback_name);
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.
if (ancestors_have_unresolved_references_) {
if (CounterStyleMap* ancestor_map = GetAncestorMap())
ancestor_map->ResolveReferences();
ancestors_have_unresolved_references_ = false;
if (CounterStyleMap* ancestor_map = GetAncestorMap())
ancestor_map->ResolveReferences(visited_maps);
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_)
return;
has_unresolved_references_ = false;
for (auto iter : counter_styles_) {
if (iter.value->HasUnresolvedExtends())
ResolveExtendsFor(*iter.value);
if (iter.value->HasUnresolvedFallback())
ResolveFallbackFor(*iter.value);
void CounterStyleMap::MarkDirtyCounterStyles(
HeapHashSet<Member<CounterStyle>>& visited_counter_styles) {
for (CounterStyle* counter_style : counter_styles_.Values())
counter_style->TraverseAndMarkDirtyIfNeeded(visited_counter_styles);
// Replace dirty CounterStyles by clean ones with unresolved references.
for (Member<CounterStyle>& counter_style_ref : counter_styles_.Values()) {
if (counter_style_ref->IsDirty()) {
CounterStyle* clean_style =
MakeGarbageCollected<CounterStyle>(counter_style_ref->GetStyleRule());
counter_style_ref = clean_style;
}
}
}
void CounterStyleMap::ResetReferences() {
for (auto iter : counter_styles_) {
CounterStyle* counter_style = iter.value;
counter_style->ResetExtends();
counter_style->ResetFallback();
if (counter_style->HasUnresolvedExtends() ||
counter_style->HasUnresolvedFallback())
has_unresolved_references_ = true;
// static
void CounterStyleMap::MarkAllDirtyCounterStyles(
Document& document,
const HeapHashSet<Member<TreeScope>>& active_tree_scopes) {
// Traverse all CounterStyle objects in the document to mark dirtiness.
// We assume that there are not too many CounterStyle objects, so this won't
// be a performance bottleneck.
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() {
void CounterStyleMap::ResolveAllReferences(
Document& document,
const HeapHashSet<Member<TreeScope>>& active_tree_scopes) {
// Make sure the UA counter style map is already set up, so that we don't
// enter a recursion when resolving references in user and author rules.
GetUACounterStyleMap();
// Traverse all counter style maps to find and update CounterStyles that are
// dirty or have unresolved references. We assume there are not too many
// 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))
user_map->ResolveReferences();
user_map->ResolveReferences(visited_maps);
if (CounterStyleMap* document_map = GetAuthorCounterStyleMap(document))
document_map->ResolveReferences();
// 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.
document_map->ResolveReferences(visited_maps);
for (const TreeScope* scope : active_tree_scopes) {
if (CounterStyleMap* scoped_map = GetAuthorCounterStyleMap(*scope)) {
scoped_map->ResetReferences();
scoped_map->ancestors_have_unresolved_references_ = true;
scoped_map->ResolveReferences(visited_maps);
#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 {
......
......@@ -26,19 +26,21 @@ class CORE_EXPORT CounterStyleMap : public GarbageCollected<CounterStyleMap> {
static CounterStyleMap* CreateUserCounterStyleMap(Document&);
static CounterStyleMap* CreateAuthorCounterStyleMap(TreeScope&);
CounterStyle& FindCounterStyleAcrossScopes(const AtomicString& name) const;
CounterStyle* FindCounterStyleAcrossScopes(const AtomicString& name) const;
void AddCounterStyles(const RuleSet&);
void SetIsPredefined();
// Resets all 'extends' and 'fallback' references to unresolved. Used when the
// counter styles in an ancestor scope are changed, which may affect the
// references in the current scope;
void ResetReferences();
void ResolveReferences();
void ResolveReferences(HeapHashSet<Member<CounterStyleMap>>& resolved_maps);
static void ResolveAllReferences(Document&,
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);
void Trace(Visitor*) const;
......@@ -56,9 +58,6 @@ class CORE_EXPORT CounterStyleMap : public GarbageCollected<CounterStyleMap> {
HeapHashMap<AtomicString, Member<CounterStyle>> counter_styles_;
bool has_unresolved_references_ = false;
bool ancestors_have_unresolved_references_ = false;
friend class CounterStyleMapTest;
};
......
......@@ -183,15 +183,22 @@ TEST_F(CounterStyleMapTest, UpdateReferencesInChildScope) {
"<style>@counter-style bar { system: extends foo; }</style>");
UpdateAllLifecyclePhasesForTest();
const CounterStyle& foo = GetCounterStyle(GetDocument(), "foo");
const CounterStyle& bar = GetCounterStyle(shadow, "bar");
EXPECT_EQ("foo", bar.GetExtendedStyle().GetName());
EXPECT_EQ(&foo, &bar.GetExtendedStyle());
GetDocument().QuerySelector("style")->remove();
UpdateAllLifecyclePhasesForTest();
// Counter styles in child scopes should be updated after chaning the counter
// styles in the parent scope.
EXPECT_EQ("decimal", bar.GetExtendedStyle().GetName());
// After counter style rule changes in the parent scope, the original
// CounterStyle for 'bar' in child scopes will be dirtied, and will be
// 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
......@@ -20,9 +20,9 @@ class CounterStyleTest : public PageTestBase,
const CounterStyle& GetCounterStyle(const AtomicString& name) {
if (const CounterStyleMap* document_map =
CounterStyleMap::GetAuthorCounterStyleMap(GetDocument()))
return document_map->FindCounterStyleAcrossScopes(name);
return CounterStyleMap::GetUACounterStyleMap()
->FindCounterStyleAcrossScopes(name);
return *document_map->FindCounterStyleAcrossScopes(name);
return *CounterStyleMap::GetUACounterStyleMap()
->FindCounterStyleAcrossScopes(name);
}
const CounterStyle AddCounterStyle(const AtomicString& name,
......
......@@ -140,7 +140,8 @@ void ScopedStyleResolver::ResetStyle() {
viewport_dependent_media_query_results_.clear();
device_dependent_media_query_results_.clear();
keyframes_rule_map_.clear();
counter_style_map_.Clear();
if (counter_style_map_)
counter_style_map_->Dispose();
slotted_rule_set_ = nullptr;
needs_append_all_sheets_ = false;
}
......
......@@ -542,6 +542,10 @@ void StyleEngine::UpdateActiveStyleSheets() {
}
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_);
}
......@@ -1558,8 +1562,8 @@ void StyleEngine::ApplyUserRuleSetChanges(
}
if (changed_rule_flags & kCounterStyleRules) {
if (change == kActiveSheetsChanged)
user_counter_style_map_.Clear();
if (change == kActiveSheetsChanged && user_counter_style_map_)
user_counter_style_map_->Dispose();
for (auto* it = new_style_sheets.begin(); it != new_style_sheets.end();
it++) {
......@@ -1568,10 +1572,6 @@ void StyleEngine::ApplyUserRuleSetChanges(
EnsureUserCounterStyleMap().AddCounterStyles(*it->second);
}
if (CounterStyleMap* doc_map =
CounterStyleMap::GetAuthorCounterStyleMap(GetDocument()))
doc_map->ResetReferences();
// TODO(crbug.com/687225): Trigger style/Layout invalidations.
}
......@@ -2391,16 +2391,22 @@ CounterStyleMap& StyleEngine::EnsureUserCounterStyleMap() {
const CounterStyle& StyleEngine::FindCounterStyleAcrossScopes(
const AtomicString& name,
const TreeScope* scope) const {
CounterStyleMap* target_map = nullptr;
while (scope) {
if (CounterStyleMap* map =
CounterStyleMap::GetAuthorCounterStyleMap(*scope))
return map->FindCounterStyleAcrossScopes(name);
CounterStyleMap::GetAuthorCounterStyleMap(*scope)) {
target_map = map;
break;
}
scope = scope->ParentTreeScope();
}
if (user_counter_style_map_)
return user_counter_style_map_->FindCounterStyleAcrossScopes(name);
return CounterStyleMap::GetUACounterStyleMap()->FindCounterStyleAcrossScopes(
name);
if (!target_map && user_counter_style_map_)
target_map = user_counter_style_map_;
if (!target_map)
target_map = CounterStyleMap::GetUACounterStyleMap();
if (CounterStyle* result = target_map->FindCounterStyleAcrossScopes(name))
return *result;
return CounterStyle::GetDecimal();
}
void StyleEngine::Trace(Visitor* visitor) const {
......
......@@ -106,6 +106,8 @@ ScopedStyleResolver& TreeScope::EnsureScopedStyleResolver() {
}
void TreeScope::ClearScopedStyleResolver() {
if (scoped_style_resolver_)
scoped_style_resolver_->ResetStyle();
scoped_style_resolver_.Clear();
}
......
......@@ -909,9 +909,8 @@ ax::mojom::blink::ListStyle AXLayoutObject::GetListStyle() const {
// 'decimal-leading-zero' may be overridden by custom counter styles. We
// return kNumeric only when we are using the predefined counter style.
if (!RuntimeEnabledFeatures::CSSAtRuleCounterStyleEnabled() ||
&ListMarker::GetCounterStyle(*GetDocument(), *computed_style) ==
&CounterStyleMap::GetUACounterStyleMap()
->FindCounterStyleAcrossScopes("decimal-leading-zero"))
ListMarker::GetCounterStyle(*GetDocument(), *computed_style)
.IsPredefined())
return ax::mojom::blink::ListStyle::kNumeric;
}
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