1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #ifndef mozilla_CounterStyleManager_h_
7 #define mozilla_CounterStyleManager_h_
10 #include "nsGkAtoms.h"
11 #include "nsStringFwd.h"
12 #include "nsDataHashtable.h"
13 #include "nsHashKeys.h"
15 #include "nsStyleConsts.h"
17 #include "mozilla/Attributes.h"
23 enum class SpeakAs
: uint8_t {
33 typedef int32_t CounterValue
;
35 class CounterStyleManager
;
36 class AnonymousCounterStyle
;
43 explicit constexpr CounterStyle(int32_t aStyle
) : mStyle(aStyle
) {}
46 CounterStyle(const CounterStyle
& aOther
) = delete;
47 void operator=(const CounterStyle
& other
) = delete;
50 constexpr int32_t GetStyle() const { return mStyle
; }
51 bool IsNone() const { return mStyle
== NS_STYLE_LIST_STYLE_NONE
; }
52 bool IsCustomStyle() const { return mStyle
== NS_STYLE_LIST_STYLE_CUSTOM
; }
53 // A style is dependent if it depends on the counter style manager.
54 // Custom styles are certainly dependent. In addition, some builtin
55 // styles are dependent for fallback.
56 bool IsDependentStyle() const;
58 virtual void GetPrefix(nsAString
& aResult
) = 0;
59 virtual void GetSuffix(nsAString
& aResult
) = 0;
60 void GetCounterText(CounterValue aOrdinal
, WritingMode aWritingMode
,
61 nsAString
& aResult
, bool& aIsRTL
);
62 virtual void GetSpokenCounterText(CounterValue aOrdinal
,
63 WritingMode aWritingMode
,
64 nsAString
& aResult
, bool& aIsBullet
);
66 // XXX This method could be removed once ::-moz-list-bullet and
67 // ::-moz-list-number are completely merged into ::marker.
68 virtual bool IsBullet() = 0;
70 virtual void GetNegative(NegativeType
& aResult
) = 0;
72 * This method returns whether an ordinal is in the range of this
73 * counter style. Note that, it is possible that an ordinal in range
74 * is rejected by the generating algorithm.
76 virtual bool IsOrdinalInRange(CounterValue aOrdinal
) = 0;
78 * This method returns whether an ordinal is in the default range of
79 * this counter style. This is the effective range when no 'range'
80 * descriptor is specified.
82 virtual bool IsOrdinalInAutoRange(CounterValue aOrdinal
) = 0;
83 virtual void GetPad(PadType
& aResult
) = 0;
84 virtual CounterStyle
* GetFallback() = 0;
85 virtual SpeakAs
GetSpeakAs() = 0;
86 virtual bool UseNegativeSign() = 0;
88 virtual void CallFallbackStyle(CounterValue aOrdinal
,
89 WritingMode aWritingMode
, nsAString
& aResult
,
91 virtual bool GetInitialCounterText(CounterValue aOrdinal
,
92 WritingMode aWritingMode
,
93 nsAString
& aResult
, bool& aIsRTL
) = 0;
95 virtual AnonymousCounterStyle
* AsAnonymous() { return nullptr; }
101 class AnonymousCounterStyle final
: public CounterStyle
{
103 explicit AnonymousCounterStyle(const nsAString
& aContent
);
104 AnonymousCounterStyle(StyleSymbolsType
, nsTArray
<nsString
> aSymbols
);
106 virtual void GetPrefix(nsAString
& aResult
) override
;
107 virtual void GetSuffix(nsAString
& aResult
) override
;
108 virtual bool IsBullet() override
;
110 virtual void GetNegative(NegativeType
& aResult
) override
;
111 virtual bool IsOrdinalInRange(CounterValue aOrdinal
) override
;
112 virtual bool IsOrdinalInAutoRange(CounterValue aOrdinal
) override
;
113 virtual void GetPad(PadType
& aResult
) override
;
114 virtual CounterStyle
* GetFallback() override
;
115 virtual SpeakAs
GetSpeakAs() override
;
116 virtual bool UseNegativeSign() override
;
118 virtual bool GetInitialCounterText(CounterValue aOrdinal
,
119 WritingMode aWritingMode
,
120 nsAString
& aResult
, bool& aIsRTL
) override
;
122 virtual AnonymousCounterStyle
* AsAnonymous() override
{ return this; }
124 bool IsSingleString() const { return mSingleString
; }
125 Span
<const nsString
> GetSymbols() const { return MakeSpan(mSymbols
); }
127 StyleCounterSystem
GetSystem() const;
129 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(AnonymousCounterStyle
)
132 ~AnonymousCounterStyle() = default;
135 StyleSymbolsType mSymbolsType
;
136 nsTArray
<nsString
> mSymbols
;
139 // A smart pointer to CounterStyle. It either owns a reference to an
140 // anonymous counter style, or weakly refers to a named counter style
141 // managed by counter style manager.
142 class CounterStylePtr
{
144 CounterStylePtr() : mRaw(0) {}
145 CounterStylePtr(const CounterStylePtr
& aOther
) : mRaw(aOther
.mRaw
) {
150 case eAnonymousCounterStyle
:
151 AsAnonymous()->AddRef();
157 MOZ_ASSERT_UNREACHABLE("Unknown type");
161 CounterStylePtr(CounterStylePtr
&& aOther
) : mRaw(aOther
.mRaw
) {
164 ~CounterStylePtr() { Reset(); }
166 CounterStylePtr
& operator=(const CounterStylePtr
& aOther
) {
167 if (this != &aOther
) {
169 new (this) CounterStylePtr(aOther
);
173 CounterStylePtr
& operator=(CounterStylePtr
&& aOther
) {
174 if (this != &aOther
) {
181 CounterStylePtr
& operator=(decltype(nullptr)) {
185 CounterStylePtr
& operator=(nsStaticAtom
* aStaticAtom
) {
187 mRaw
= reinterpret_cast<uintptr_t>(aStaticAtom
) | eAtom
;
190 CounterStylePtr
& operator=(already_AddRefed
<nsAtom
> aAtom
) {
192 mRaw
= reinterpret_cast<uintptr_t>(aAtom
.take()) | eAtom
;
195 CounterStylePtr
& operator=(AnonymousCounterStyle
* aCounterStyle
) {
198 CounterStyle
* raw
= do_AddRef(aCounterStyle
).take();
199 mRaw
= reinterpret_cast<uintptr_t>(raw
) | eAnonymousCounterStyle
;
204 // TODO(emilio): Make CounterStyle have a single representation, either by
205 // removing CounterStylePtr or by moving this representation to Rust.
206 static CounterStylePtr
FromStyle(const StyleCounterStyle
& aStyle
) {
208 if (aStyle
.IsName()) {
209 ret
= do_AddRef(aStyle
.AsName().AsAtom());
211 StyleSymbolsType type
= aStyle
.AsSymbols()._0
;
212 Span
<const StyleSymbol
> symbols
= aStyle
.AsSymbols()._1
._0
.AsSpan();
213 nsTArray
<nsString
> transcoded(symbols
.Length());
214 for (const auto& symbol
: symbols
) {
215 MOZ_ASSERT(symbol
.IsString(), "Should not have <ident> in symbols()");
216 transcoded
.AppendElement(
217 NS_ConvertUTF8toUTF16(symbol
.AsString().AsString()));
219 ret
= new AnonymousCounterStyle(type
, std::move(transcoded
));
224 explicit operator bool() const { return !!mRaw
; }
225 bool operator!() const { return !mRaw
; }
226 bool operator==(const CounterStylePtr
& aOther
) const {
227 // FIXME(emilio): For atoms this is all right, but for symbols doesn't this
228 // cause us to compare as unequal all the time, even if the specified
229 // symbols didn't change?
230 return mRaw
== aOther
.mRaw
;
232 bool operator!=(const CounterStylePtr
& aOther
) const {
233 return mRaw
!= aOther
.mRaw
;
236 nsAtom
* AsAtom() const {
237 MOZ_ASSERT(IsAtom());
238 return reinterpret_cast<nsAtom
*>(mRaw
& ~eMask
);
240 AnonymousCounterStyle
* AsAnonymous() const {
241 MOZ_ASSERT(IsAnonymous());
242 return static_cast<AnonymousCounterStyle
*>(
243 reinterpret_cast<CounterStyle
*>(mRaw
& ~eMask
));
246 bool IsAtom() const { return GetType() == eAtom
; }
247 bool IsAnonymous() const { return GetType() == eAnonymousCounterStyle
; }
249 bool IsNone() const { return IsAtom() && AsAtom() == nsGkAtoms::none
; }
252 enum Type
: uintptr_t {
253 eAnonymousCounterStyle
= 0,
258 static_assert(alignof(CounterStyle
) >= 1 << eMask
,
259 "We're gonna tag the pointer, so it better fit");
260 static_assert(alignof(nsAtom
) >= 1 << eMask
,
261 "We're gonna tag the pointer, so it better fit");
263 Type
GetType() const { return static_cast<Type
>(mRaw
& eMask
); }
270 case eAnonymousCounterStyle
:
271 AsAnonymous()->Release();
277 MOZ_ASSERT_UNREACHABLE("Unknown type");
283 // mRaw contains the pointer, and its last bit is used to store the type of
285 // If the type is eAtom, the pointer owns a reference to an nsAtom
286 // (potentially null).
287 // If the type is eAnonymousCounterStyle, it owns a reference to an
288 // anonymous counter style (never null).
292 class CounterStyleManager final
{
294 ~CounterStyleManager();
297 explicit CounterStyleManager(nsPresContext
* aPresContext
);
301 bool IsInitial() const {
302 // only 'none', 'decimal', and 'disc'
303 return mStyles
.Count() == 3;
306 // Returns the counter style object for the given name from the style
307 // table if it is already built, and nullptr otherwise.
308 CounterStyle
* GetCounterStyle(nsAtom
* aName
) const {
309 return mStyles
.Get(aName
);
311 // Same as GetCounterStyle but try to build the counter style object
312 // rather than returning nullptr if that hasn't been built.
313 CounterStyle
* ResolveCounterStyle(nsAtom
* aName
);
314 CounterStyle
* ResolveCounterStyle(const CounterStylePtr
& aPtr
) {
316 return ResolveCounterStyle(aPtr
.AsAtom());
318 return aPtr
.AsAnonymous();
321 static CounterStyle
* GetBuiltinStyle(int32_t aStyle
);
322 static CounterStyle
* GetNoneStyle() {
323 return GetBuiltinStyle(NS_STYLE_LIST_STYLE_NONE
);
325 static CounterStyle
* GetDecimalStyle() {
326 return GetBuiltinStyle(NS_STYLE_LIST_STYLE_DECIMAL
);
328 static CounterStyle
* GetDiscStyle() {
329 return GetBuiltinStyle(NS_STYLE_LIST_STYLE_DISC
);
332 // This method will scan all existing counter styles generated by this
333 // manager, and remove or mark data dirty accordingly. It returns true
334 // if any counter style is changed, false elsewise. This method should
335 // be called when any counter style may be affected.
336 bool NotifyRuleChanged();
337 // NotifyRuleChanged will evict no longer needed counter styles into
338 // mRetiredStyles, and this function destroys all objects listed there.
339 // It should be called only after no one may ever use those objects.
340 void CleanRetiredStyles();
342 nsPresContext
* PresContext() const { return mPresContext
; }
344 NS_INLINE_DECL_REFCOUNTING(CounterStyleManager
)
347 void DestroyCounterStyle(CounterStyle
* aCounterStyle
);
349 nsPresContext
* mPresContext
;
350 nsDataHashtable
<nsRefPtrHashKey
<nsAtom
>, CounterStyle
*> mStyles
;
351 nsTArray
<CounterStyle
*> mRetiredStyles
;
354 } // namespace mozilla
356 #endif /* !defined(mozilla_CounterStyleManager_h_) */