no bug - Bumping Firefox l10n changesets r=release a=l10n-bump DONTBUILD CLOSED TREE
[gecko.git] / layout / style / CounterStyleManager.h
blobde510268692d8fb3b1c7386865807657af3b03c8
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_
9 #include "nsAtom.h"
10 #include "nsGkAtoms.h"
11 #include "nsStringFwd.h"
12 #include "nsTHashMap.h"
13 #include "nsHashKeys.h"
15 #include "nsStyleConsts.h"
17 #include "mozilla/Attributes.h"
19 class nsPresContext;
21 namespace mozilla {
23 enum class SpeakAs : uint8_t {
24 Bullets = 0,
25 Numbers = 1,
26 Words = 2,
27 Spellout = 3,
28 Other = 255
31 class WritingMode;
33 typedef int32_t CounterValue;
35 class CounterStyleManager;
36 class AnonymousCounterStyle;
38 struct NegativeType;
39 struct PadType;
41 class CounterStyle {
42 protected:
43 explicit constexpr CounterStyle(ListStyle aStyle) : mStyle(aStyle) {}
45 private:
46 CounterStyle(const CounterStyle& aOther) = delete;
47 void operator=(const CounterStyle& other) = delete;
49 public:
50 constexpr ListStyle GetStyle() const { return mStyle; }
51 bool IsNone() const { return mStyle == ListStyle::None; }
52 bool IsCustomStyle() const { return mStyle == ListStyle::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;
71 /**
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;
77 /**
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,
90 bool& aIsRTL);
91 virtual bool GetInitialCounterText(CounterValue aOrdinal,
92 WritingMode aWritingMode,
93 nsAString& aResult, bool& aIsRTL) = 0;
95 virtual AnonymousCounterStyle* AsAnonymous() { return nullptr; }
97 protected:
98 const ListStyle mStyle;
101 class AnonymousCounterStyle final : public CounterStyle {
102 public:
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 auto GetSymbols() const { return Span<const nsString>{mSymbols}; }
127 StyleCounterSystem GetSystem() const;
129 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(AnonymousCounterStyle)
131 private:
132 ~AnonymousCounterStyle() = default;
134 bool mSingleString;
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 {
143 public:
144 CounterStylePtr() : mRaw(0) {}
145 CounterStylePtr(const CounterStylePtr& aOther) : mRaw(aOther.mRaw) {
146 if (!mRaw) {
147 return;
149 switch (GetType()) {
150 case eAnonymousCounterStyle:
151 AsAnonymous()->AddRef();
152 break;
153 case eAtom:
154 AsAtom()->AddRef();
155 break;
156 default:
157 MOZ_ASSERT_UNREACHABLE("Unknown type");
158 break;
161 CounterStylePtr(CounterStylePtr&& aOther) : mRaw(aOther.mRaw) {
162 aOther.mRaw = 0;
164 ~CounterStylePtr() { Reset(); }
166 CounterStylePtr& operator=(const CounterStylePtr& aOther) {
167 if (this != &aOther) {
168 Reset();
169 new (this) CounterStylePtr(aOther);
171 return *this;
173 CounterStylePtr& operator=(CounterStylePtr&& aOther) {
174 if (this != &aOther) {
175 Reset();
176 mRaw = aOther.mRaw;
177 aOther.mRaw = 0;
179 return *this;
181 CounterStylePtr& operator=(decltype(nullptr)) {
182 Reset();
183 return *this;
185 CounterStylePtr& operator=(nsStaticAtom* aStaticAtom) {
186 Reset();
187 mRaw = reinterpret_cast<uintptr_t>(aStaticAtom) | eAtom;
188 return *this;
190 CounterStylePtr& operator=(already_AddRefed<nsAtom> aAtom) {
191 Reset();
192 mRaw = reinterpret_cast<uintptr_t>(aAtom.take()) | eAtom;
193 return *this;
195 CounterStylePtr& operator=(AnonymousCounterStyle* aCounterStyle) {
196 Reset();
197 if (aCounterStyle) {
198 CounterStyle* raw = do_AddRef(aCounterStyle).take();
199 mRaw = reinterpret_cast<uintptr_t>(raw) | eAnonymousCounterStyle;
201 return *this;
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) {
207 CounterStylePtr ret;
208 if (aStyle.IsName()) {
209 ret = do_AddRef(aStyle.AsName().AsAtom());
210 } else {
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));
221 return ret;
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; }
251 private:
252 enum Type : uintptr_t {
253 eAnonymousCounterStyle = 0,
254 eAtom = 1,
255 eMask = 1,
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); }
265 void Reset() {
266 if (!mRaw) {
267 return;
269 switch (GetType()) {
270 case eAnonymousCounterStyle:
271 AsAnonymous()->Release();
272 break;
273 case eAtom:
274 AsAtom()->Release();
275 break;
276 default:
277 MOZ_ASSERT_UNREACHABLE("Unknown type");
278 break;
280 mRaw = 0;
283 // mRaw contains the pointer, and its last bit is used to store the type of
284 // the pointer.
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).
289 uintptr_t mRaw;
292 class CounterStyleManager final {
293 private:
294 ~CounterStyleManager();
296 public:
297 explicit CounterStyleManager(nsPresContext* aPresContext);
299 void Disconnect();
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) {
315 if (aPtr.IsAtom()) {
316 return ResolveCounterStyle(aPtr.AsAtom());
318 return aPtr.AsAnonymous();
321 static CounterStyle* GetBuiltinStyle(ListStyle aStyle);
322 static CounterStyle* GetNoneStyle() {
323 return GetBuiltinStyle(ListStyle::None);
325 static CounterStyle* GetDecimalStyle() {
326 return GetBuiltinStyle(ListStyle::Decimal);
328 static CounterStyle* GetDiscStyle() {
329 return GetBuiltinStyle(ListStyle::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)
346 private:
347 void DestroyCounterStyle(CounterStyle* aCounterStyle);
349 nsPresContext* mPresContext;
350 nsTHashMap<nsRefPtrHashKey<nsAtom>, CounterStyle*> mStyles;
351 nsTArray<CounterStyle*> mRetiredStyles;
354 } // namespace mozilla
356 #endif /* !defined(mozilla_CounterStyleManager_h_) */