/src/mozilla-central/layout/style/CounterStyleManager.h
Line | Count | Source (jump to first uncovered line) |
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_ |
8 | | |
9 | | #include "nsAtom.h" |
10 | | #include "nsStringFwd.h" |
11 | | #include "nsDataHashtable.h" |
12 | | #include "nsHashKeys.h" |
13 | | |
14 | | #include "nsStyleConsts.h" |
15 | | |
16 | | #include "mozilla/Attributes.h" |
17 | | |
18 | | #include "nsCSSValue.h" |
19 | | |
20 | | class nsPresContext; |
21 | | |
22 | | namespace mozilla { |
23 | | |
24 | | class WritingMode; |
25 | | |
26 | | typedef int32_t CounterValue; |
27 | | |
28 | | class CounterStyleManager; |
29 | | class AnonymousCounterStyle; |
30 | | |
31 | | struct NegativeType; |
32 | | struct PadType; |
33 | | |
34 | | class CounterStyle |
35 | | { |
36 | | protected: |
37 | | explicit constexpr CounterStyle(int32_t aStyle) |
38 | | : mStyle(aStyle) |
39 | 0 | { |
40 | 0 | } |
41 | | |
42 | | private: |
43 | | CounterStyle(const CounterStyle& aOther) = delete; |
44 | | void operator=(const CounterStyle& other) = delete; |
45 | | |
46 | | public: |
47 | 0 | constexpr int32_t GetStyle() const { return mStyle; } |
48 | 0 | bool IsNone() const { return mStyle == NS_STYLE_LIST_STYLE_NONE; } |
49 | 0 | bool IsCustomStyle() const { return mStyle == NS_STYLE_LIST_STYLE_CUSTOM; } |
50 | | // A style is dependent if it depends on the counter style manager. |
51 | | // Custom styles are certainly dependent. In addition, some builtin |
52 | | // styles are dependent for fallback. |
53 | | bool IsDependentStyle() const; |
54 | | |
55 | | virtual nsAtom* GetStyleName() const = 0; |
56 | | virtual void GetPrefix(nsAString& aResult) = 0; |
57 | | virtual void GetSuffix(nsAString& aResult) = 0; |
58 | | void GetCounterText(CounterValue aOrdinal, |
59 | | WritingMode aWritingMode, |
60 | | nsAString& aResult, |
61 | | bool& aIsRTL); |
62 | | virtual void GetSpokenCounterText(CounterValue aOrdinal, |
63 | | WritingMode aWritingMode, |
64 | | nsAString& aResult, |
65 | | bool& aIsBullet); |
66 | | |
67 | | // XXX This method could be removed once ::-moz-list-bullet and |
68 | | // ::-moz-list-number are completely merged into ::marker. |
69 | | virtual bool IsBullet() = 0; |
70 | | |
71 | | virtual void GetNegative(NegativeType& aResult) = 0; |
72 | | /** |
73 | | * This method returns whether an ordinal is in the range of this |
74 | | * counter style. Note that, it is possible that an ordinal in range |
75 | | * is rejected by the generating algorithm. |
76 | | */ |
77 | | virtual bool IsOrdinalInRange(CounterValue aOrdinal) = 0; |
78 | | /** |
79 | | * This method returns whether an ordinal is in the default range of |
80 | | * this counter style. This is the effective range when no 'range' |
81 | | * descriptor is specified. |
82 | | */ |
83 | | virtual bool IsOrdinalInAutoRange(CounterValue aOrdinal) = 0; |
84 | | virtual void GetPad(PadType& aResult) = 0; |
85 | | virtual CounterStyle* GetFallback() = 0; |
86 | | virtual uint8_t GetSpeakAs() = 0; |
87 | | virtual bool UseNegativeSign() = 0; |
88 | | |
89 | | virtual void CallFallbackStyle(CounterValue aOrdinal, |
90 | | WritingMode aWritingMode, |
91 | | nsAString& aResult, |
92 | | bool& aIsRTL); |
93 | | virtual bool GetInitialCounterText(CounterValue aOrdinal, |
94 | | WritingMode aWritingMode, |
95 | | nsAString& aResult, |
96 | | bool& aIsRTL) = 0; |
97 | | |
98 | 0 | virtual AnonymousCounterStyle* AsAnonymous() { return nullptr; } |
99 | | |
100 | | protected: |
101 | | const int32_t mStyle; |
102 | | }; |
103 | | |
104 | | class AnonymousCounterStyle final : public CounterStyle |
105 | | { |
106 | | public: |
107 | | explicit AnonymousCounterStyle(const nsAString& aContent); |
108 | | AnonymousCounterStyle(uint8_t aSystem, nsTArray<nsString> aSymbols); |
109 | | explicit AnonymousCounterStyle(const nsCSSValue::Array* aValue); |
110 | | |
111 | | virtual nsAtom* GetStyleName() const override; |
112 | | virtual void GetPrefix(nsAString& aResult) override; |
113 | | virtual void GetSuffix(nsAString& aResult) override; |
114 | | virtual bool IsBullet() override; |
115 | | |
116 | | virtual void GetNegative(NegativeType& aResult) override; |
117 | | virtual bool IsOrdinalInRange(CounterValue aOrdinal) override; |
118 | | virtual bool IsOrdinalInAutoRange(CounterValue aOrdinal) override; |
119 | | virtual void GetPad(PadType& aResult) override; |
120 | | virtual CounterStyle* GetFallback() override; |
121 | | virtual uint8_t GetSpeakAs() override; |
122 | | virtual bool UseNegativeSign() override; |
123 | | |
124 | | virtual bool GetInitialCounterText(CounterValue aOrdinal, |
125 | | WritingMode aWritingMode, |
126 | | nsAString& aResult, |
127 | | bool& aIsRTL) override; |
128 | | |
129 | 0 | virtual AnonymousCounterStyle* AsAnonymous() override { return this; } |
130 | | |
131 | 0 | bool IsSingleString() const { return mSingleString; } |
132 | | uint8_t GetSystem() const { return mSystem; } |
133 | | const nsTArray<nsString>& GetSymbols() const { return mSymbols; } |
134 | | |
135 | | NS_INLINE_DECL_THREADSAFE_REFCOUNTING(AnonymousCounterStyle) |
136 | | |
137 | | private: |
138 | 0 | ~AnonymousCounterStyle() {} |
139 | | |
140 | | bool mSingleString; |
141 | | uint8_t mSystem; |
142 | | nsTArray<nsString> mSymbols; |
143 | | }; |
144 | | |
145 | | // A smart pointer to CounterStyle. It either owns a reference to an |
146 | | // anonymous counter style, or weakly refers to a named counter style |
147 | | // managed by counter style manager. |
148 | | class CounterStylePtr |
149 | | { |
150 | | public: |
151 | 0 | CounterStylePtr() : mRaw(0) {} |
152 | | CounterStylePtr(const CounterStylePtr& aOther) |
153 | | : mRaw(aOther.mRaw) |
154 | 0 | { |
155 | 0 | switch (GetType()) { |
156 | 0 | case eCounterStyle: |
157 | 0 | break; |
158 | 0 | case eAnonymousCounterStyle: |
159 | 0 | AsAnonymous()->AddRef(); |
160 | 0 | break; |
161 | 0 | case eUnresolvedAtom: |
162 | 0 | AsAtom()->AddRef(); |
163 | 0 | break; |
164 | 0 | case eMask: |
165 | 0 | MOZ_ASSERT_UNREACHABLE("Unknown type"); |
166 | 0 | break; |
167 | 0 | } |
168 | 0 | } |
169 | | CounterStylePtr(CounterStylePtr&& aOther) |
170 | | : mRaw(aOther.mRaw) |
171 | | { |
172 | | aOther.mRaw = 0; |
173 | | } |
174 | 0 | ~CounterStylePtr() { Reset(); } |
175 | | |
176 | | CounterStylePtr& operator=(const CounterStylePtr& aOther) |
177 | 0 | { |
178 | 0 | if (this != &aOther) { |
179 | 0 | Reset(); |
180 | 0 | new (this) CounterStylePtr(aOther); |
181 | 0 | } |
182 | 0 | return *this; |
183 | 0 | } |
184 | | CounterStylePtr& operator=(CounterStylePtr&& aOther) |
185 | | { |
186 | | if (this != &aOther) { |
187 | | Reset(); |
188 | | mRaw = aOther.mRaw; |
189 | | aOther.mRaw = 0; |
190 | | } |
191 | | return *this; |
192 | | } |
193 | | CounterStylePtr& operator=(decltype(nullptr)) |
194 | | { |
195 | | Reset(); |
196 | | return *this; |
197 | | } |
198 | | CounterStylePtr& operator=(already_AddRefed<nsAtom> aAtom) |
199 | 0 | { |
200 | 0 | Reset(); |
201 | 0 | if (nsAtom* raw = aAtom.take()) { |
202 | 0 | AssertPointerAligned(raw); |
203 | 0 | mRaw = reinterpret_cast<uintptr_t>(raw) | eUnresolvedAtom; |
204 | 0 | } |
205 | 0 | return *this; |
206 | 0 | } |
207 | | CounterStylePtr& operator=(AnonymousCounterStyle* aCounterStyle) |
208 | 0 | { |
209 | 0 | Reset(); |
210 | 0 | if (aCounterStyle) { |
211 | 0 | CounterStyle* raw = do_AddRef(aCounterStyle).take(); |
212 | 0 | AssertPointerAligned(raw); |
213 | 0 | mRaw = reinterpret_cast<uintptr_t>(raw) | eAnonymousCounterStyle; |
214 | 0 | } |
215 | 0 | return *this; |
216 | 0 | } |
217 | | CounterStylePtr& operator=(CounterStyle* aCounterStyle) |
218 | 0 | { |
219 | 0 | Reset(); |
220 | 0 | if (aCounterStyle) { |
221 | 0 | MOZ_ASSERT(!aCounterStyle->AsAnonymous()); |
222 | 0 | AssertPointerAligned(aCounterStyle); |
223 | 0 | mRaw = reinterpret_cast<uintptr_t>(aCounterStyle) | eCounterStyle; |
224 | 0 | } |
225 | 0 | return *this; |
226 | 0 | } |
227 | | |
228 | 0 | operator CounterStyle*() const & { return Get(); } |
229 | | operator CounterStyle*() const && = delete; |
230 | 0 | CounterStyle* operator->() const { return Get(); } |
231 | | explicit operator bool() const { return !!mRaw; } |
232 | | bool operator!() const { return !mRaw; } |
233 | | bool operator==(const CounterStylePtr& aOther) const |
234 | 0 | { return mRaw == aOther.mRaw; } |
235 | | bool operator!=(const CounterStylePtr& aOther) const |
236 | 0 | { return mRaw != aOther.mRaw; } |
237 | | |
238 | 0 | bool IsResolved() const { return !IsUnresolved(); } |
239 | | inline void Resolve(CounterStyleManager* aManager); |
240 | | |
241 | | nsAtom* AsAtom() const |
242 | 0 | { |
243 | 0 | MOZ_ASSERT(IsUnresolved()); |
244 | 0 | return reinterpret_cast<nsAtom*>(mRaw & ~eMask); |
245 | 0 | } |
246 | | AnonymousCounterStyle* AsAnonymous() const |
247 | 0 | { |
248 | 0 | MOZ_ASSERT(IsAnonymous()); |
249 | 0 | return static_cast<AnonymousCounterStyle*>( |
250 | 0 | reinterpret_cast<CounterStyle*>(mRaw & ~eMask)); |
251 | 0 | } |
252 | | |
253 | | private: |
254 | | CounterStyle* Get() const |
255 | 0 | { |
256 | 0 | MOZ_ASSERT(IsResolved()); |
257 | 0 | return reinterpret_cast<CounterStyle*>(mRaw & ~eMask); |
258 | 0 | } |
259 | | template<typename T> |
260 | | void AssertPointerAligned(T* aPointer) |
261 | 0 | { |
262 | 0 | // This can be checked at compile time via |
263 | 0 | // > static_assert(alignof(CounterStyle) >= 4); |
264 | 0 | // > static_assert(alignof(nsAtom) >= 4); |
265 | 0 | // but MSVC2015 doesn't support using alignof on an abstract class. |
266 | 0 | // Once we move to MSVC2017, we can replace this runtime check with |
267 | 0 | // the compile time check above. |
268 | 0 | MOZ_ASSERT(!(reinterpret_cast<uintptr_t>(aPointer) & eMask)); |
269 | 0 | } Unexecuted instantiation: void mozilla::CounterStylePtr::AssertPointerAligned<nsAtom>(nsAtom*) Unexecuted instantiation: void mozilla::CounterStylePtr::AssertPointerAligned<mozilla::CounterStyle>(mozilla::CounterStyle*) |
270 | | |
271 | | enum Type : uintptr_t { |
272 | | eCounterStyle = 0, |
273 | | eAnonymousCounterStyle = 1, |
274 | | eUnresolvedAtom = 2, |
275 | | eMask = 3, |
276 | | }; |
277 | | |
278 | 0 | Type GetType() const { return static_cast<Type>(mRaw & eMask); } |
279 | 0 | bool IsUnresolved() const { return GetType() == eUnresolvedAtom; } |
280 | | bool IsAnonymous() const { return GetType() == eAnonymousCounterStyle; } |
281 | | |
282 | | void Reset() |
283 | 0 | { |
284 | 0 | switch (GetType()) { |
285 | 0 | case eCounterStyle: |
286 | 0 | break; |
287 | 0 | case eAnonymousCounterStyle: |
288 | 0 | AsAnonymous()->Release(); |
289 | 0 | break; |
290 | 0 | case eUnresolvedAtom: |
291 | 0 | AsAtom()->Release(); |
292 | 0 | break; |
293 | 0 | case eMask: |
294 | 0 | MOZ_ASSERT_UNREACHABLE("Unknown type"); |
295 | 0 | break; |
296 | 0 | } |
297 | 0 | mRaw = 0; |
298 | 0 | } |
299 | | |
300 | | // mRaw contains the pointer, and its last two bits are used for type |
301 | | // of the pointer. |
302 | | // If the type is eUnresolvedAtom, the pointer owns a reference to an |
303 | | // nsAtom, and it needs to be resolved to a counter style before use. |
304 | | // If the type is eAnonymousCounterStyle, it owns a reference to an |
305 | | // anonymous counter style. |
306 | | // Otherwise it is a weak pointer referring a named counter style |
307 | | // managed by CounterStyleManager. |
308 | | uintptr_t mRaw; |
309 | | }; |
310 | | |
311 | | class CounterStyleManager final |
312 | | { |
313 | | private: |
314 | | ~CounterStyleManager(); |
315 | | public: |
316 | | explicit CounterStyleManager(nsPresContext* aPresContext); |
317 | | |
318 | | void Disconnect(); |
319 | | |
320 | | bool IsInitial() const |
321 | 0 | { |
322 | 0 | // only 'none', 'decimal', and 'disc' |
323 | 0 | return mStyles.Count() == 3; |
324 | 0 | } |
325 | | |
326 | | // Returns the counter style object for the given name from the style |
327 | | // table if it is already built, and nullptr otherwise. |
328 | 0 | CounterStyle* GetCounterStyle(nsAtom* aName) const { |
329 | 0 | return mStyles.Get(aName); |
330 | 0 | } |
331 | | // Same as GetCounterStyle but try to build the counter style object |
332 | | // rather than returning nullptr if that hasn't been built. |
333 | | CounterStyle* BuildCounterStyle(nsAtom* aName); |
334 | | |
335 | | static CounterStyle* GetBuiltinStyle(int32_t aStyle); |
336 | | static CounterStyle* GetNoneStyle() |
337 | 0 | { |
338 | 0 | return GetBuiltinStyle(NS_STYLE_LIST_STYLE_NONE); |
339 | 0 | } |
340 | | static CounterStyle* GetDecimalStyle() |
341 | 0 | { |
342 | 0 | return GetBuiltinStyle(NS_STYLE_LIST_STYLE_DECIMAL); |
343 | 0 | } |
344 | | static CounterStyle* GetDiscStyle() |
345 | 0 | { |
346 | 0 | return GetBuiltinStyle(NS_STYLE_LIST_STYLE_DISC); |
347 | 0 | } |
348 | | |
349 | | static nsAtom* GetStyleNameFromType(int32_t aStyle); |
350 | | |
351 | | // This method will scan all existing counter styles generated by this |
352 | | // manager, and remove or mark data dirty accordingly. It returns true |
353 | | // if any counter style is changed, false elsewise. This method should |
354 | | // be called when any counter style may be affected. |
355 | | bool NotifyRuleChanged(); |
356 | | // NotifyRuleChanged will evict no longer needed counter styles into |
357 | | // mRetiredStyles, and this function destroys all objects listed there. |
358 | | // It should be called only after no one may ever use those objects. |
359 | | void CleanRetiredStyles(); |
360 | | |
361 | 0 | nsPresContext* PresContext() const { return mPresContext; } |
362 | | |
363 | | NS_INLINE_DECL_REFCOUNTING(CounterStyleManager) |
364 | | |
365 | | private: |
366 | | void DestroyCounterStyle(CounterStyle* aCounterStyle); |
367 | | |
368 | | nsPresContext* mPresContext; |
369 | | nsDataHashtable<nsRefPtrHashKey<nsAtom>, CounterStyle*> mStyles; |
370 | | nsTArray<CounterStyle*> mRetiredStyles; |
371 | | }; |
372 | | |
373 | | void |
374 | | CounterStylePtr::Resolve(CounterStyleManager* aManager) |
375 | 0 | { |
376 | 0 | if (IsUnresolved()) { |
377 | 0 | *this = aManager->BuildCounterStyle(AsAtom()); |
378 | 0 | } |
379 | 0 | } |
380 | | |
381 | | } // namespace mozilla |
382 | | |
383 | | #endif /* !defined(mozilla_CounterStyleManager_h_) */ |