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/. */
7 #include "CounterStyleManager.h"
9 #include "mozilla/Types.h"
10 #include "mozilla/CheckedInt.h"
11 #include "mozilla/MathAlgorithms.h"
12 #include "mozilla/ArrayUtils.h"
15 #include "nsStyleSet.h"
16 #include "nsCSSRules.h"
18 #include "nsTHashtable.h"
19 #include "nsUnicodeProperties.h"
20 #include "WritingModes.h"
32 nsString before
, after
;
41 // This limitation will be applied to some systems, and pad descriptor.
42 // Any initial representation generated by symbolic or additive which is
43 // longer than this limitation will be dropped. If any pad is longer
44 // than this, the whole counter text will be dropped as well.
45 // The spec requires user agents to support at least 60 Unicode code-
46 // points for counter text. However, this constant only limits the
47 // length in 16-bit units. So it has to be at least 120, since code-
48 // points outside the BMP will need 2 16-bit units.
49 #define LENGTH_LIMIT 150
52 GetCyclicCounterText(CounterValue aOrdinal
,
54 const nsTArray
<nsString
>& aSymbols
)
56 NS_ABORT_IF_FALSE(aSymbols
.Length() >= 1,
57 "No symbol available for cyclic counter.");
58 auto n
= aSymbols
.Length();
59 CounterValue index
= (aOrdinal
- 1) % n
;
60 aResult
= aSymbols
[index
>= 0 ? index
: index
+ n
];
65 GetFixedCounterText(CounterValue aOrdinal
,
68 const nsTArray
<nsString
>& aSymbols
)
70 CounterValue index
= aOrdinal
- aStart
;
71 if (index
>= 0 && index
< CounterValue(aSymbols
.Length())) {
72 aResult
= aSymbols
[index
];
80 GetSymbolicCounterText(CounterValue aOrdinal
,
82 const nsTArray
<nsString
>& aSymbols
)
84 NS_ABORT_IF_FALSE(aSymbols
.Length() >= 1,
85 "No symbol available for symbolic counter.");
86 NS_ABORT_IF_FALSE(aOrdinal
>= 0, "Invalid ordinal.");
92 auto n
= aSymbols
.Length();
93 const nsString
& symbol
= aSymbols
[(aOrdinal
- 1) % n
];
94 size_t len
= (aOrdinal
+ n
- 1) / n
;
95 auto symbolLength
= symbol
.Length();
96 if (symbolLength
> 0) {
97 if (len
> LENGTH_LIMIT
|| symbolLength
> LENGTH_LIMIT
||
98 len
* symbolLength
> LENGTH_LIMIT
) {
101 for (size_t i
= 0; i
< len
; ++i
) {
102 aResult
.Append(symbol
);
109 GetAlphabeticCounterText(CounterValue aOrdinal
,
110 nsSubstring
& aResult
,
111 const nsTArray
<nsString
>& aSymbols
)
113 NS_ABORT_IF_FALSE(aSymbols
.Length() >= 2,
114 "Too few symbols for alphabetic counter.");
115 NS_ABORT_IF_FALSE(aOrdinal
>= 0, "Invalid ordinal.");
120 auto n
= aSymbols
.Length();
121 // The precise length of this array should be
122 // ceil(log((double) aOrdinal / n * (n - 1) + 1) / log(n)).
123 // The max length is slightly smaller than which defined below.
124 nsAutoTArray
<int32_t, std::numeric_limits
<CounterValue
>::digits
> indexes
;
125 while (aOrdinal
> 0) {
127 indexes
.AppendElement(aOrdinal
% n
);
132 for (auto i
= indexes
.Length(); i
> 0; --i
) {
133 aResult
.Append(aSymbols
[indexes
[i
- 1]]);
139 GetNumericCounterText(CounterValue aOrdinal
,
140 nsSubstring
& aResult
,
141 const nsTArray
<nsString
>& aSymbols
)
143 NS_ABORT_IF_FALSE(aSymbols
.Length() >= 2,
144 "Too few symbols for numeric counter.");
145 NS_ABORT_IF_FALSE(aOrdinal
>= 0, "Invalid ordinal.");
148 aResult
= aSymbols
[0];
152 auto n
= aSymbols
.Length();
153 nsAutoTArray
<int32_t, std::numeric_limits
<CounterValue
>::digits
> indexes
;
154 while (aOrdinal
> 0) {
155 indexes
.AppendElement(aOrdinal
% n
);
160 for (auto i
= indexes
.Length(); i
> 0; --i
) {
161 aResult
.Append(aSymbols
[indexes
[i
- 1]]);
167 GetAdditiveCounterText(CounterValue aOrdinal
,
168 nsSubstring
& aResult
,
169 const nsTArray
<AdditiveSymbol
>& aSymbols
)
171 NS_ABORT_IF_FALSE(aOrdinal
>= 0, "Invalid ordinal.");
174 const AdditiveSymbol
& last
= aSymbols
.LastElement();
175 if (last
.weight
== 0) {
176 aResult
= last
.symbol
;
184 for (size_t i
= 0, iEnd
= aSymbols
.Length(); i
< iEnd
; ++i
) {
185 const AdditiveSymbol
& symbol
= aSymbols
[i
];
186 if (symbol
.weight
== 0) {
189 CounterValue times
= aOrdinal
/ symbol
.weight
;
191 auto symbolLength
= symbol
.symbol
.Length();
192 if (symbolLength
> 0) {
193 length
+= times
* symbolLength
;
194 if (times
> LENGTH_LIMIT
||
195 symbolLength
> LENGTH_LIMIT
||
196 length
> LENGTH_LIMIT
) {
199 for (CounterValue j
= 0; j
< times
; ++j
) {
200 aResult
.Append(symbol
.symbol
);
203 aOrdinal
-= times
* symbol
.weight
;
206 return aOrdinal
== 0;
210 DecimalToText(CounterValue aOrdinal
, nsSubstring
& aResult
)
212 // 3 for additional digit, negative sign, and null
213 char cbuf
[std::numeric_limits
<CounterValue
>::digits10
+ 3];
214 PR_snprintf(cbuf
, sizeof(cbuf
), "%ld", aOrdinal
);
215 aResult
.AssignASCII(cbuf
);
219 // We know cjk-ideographic need 31 characters to display 99,999,999,999,999,999
220 // georgian needs 6 at most
221 // armenian needs 12 at most
222 // hebrew may need more...
224 #define NUM_BUF_SIZE 34
226 enum CJKIdeographicLang
{
227 CHINESE
, KOREAN
, JAPANESE
229 struct CJKIdeographicData
{
236 static const CJKIdeographicData gDataJapaneseInformal
= {
238 0x3007, 0x4e00, 0x4e8c, 0x4e09, 0x56db,
239 0x4e94, 0x516d, 0x4e03, 0x516b, 0x4e5d
241 { 0x5341, 0x767e, 0x5343 }, // unit
242 { 0x4e07, 0x5104 }, // unit10K
246 static const CJKIdeographicData gDataJapaneseFormal
= {
248 0x96f6, 0x58f1, 0x5f10, 0x53c2, 0x56db,
249 0x4f0d, 0x516d, 0x4e03, 0x516b, 0x4e5d
251 { 0x62fe, 0x767e, 0x9621 }, // unit
252 { 0x842c, 0x5104 }, // unit10K
256 static const CJKIdeographicData gDataKoreanHangulFormal
= {
258 0xc601, 0xc77c, 0xc774, 0xc0bc, 0xc0ac,
259 0xc624, 0xc721, 0xce60, 0xd314, 0xad6c
261 { 0xc2ed, 0xbc31, 0xcc9c }, // unit
262 { 0xb9cc, 0xc5b5 }, // unit10K
266 static const CJKIdeographicData gDataKoreanHanjaInformal
= {
268 0x96f6, 0x4e00, 0x4e8c, 0x4e09, 0x56db,
269 0x4e94, 0x516d, 0x4e03, 0x516b, 0x4e5d
271 { 0x5341, 0x767e, 0x5343 }, // unit
272 { 0x842c, 0x5104 }, // unit10K
276 static const CJKIdeographicData gDataKoreanHanjaFormal
= {
278 0x96f6, 0x58f9, 0x8cb3, 0x53c3, 0x56db,
279 0x4e94, 0x516d, 0x4e03, 0x516b, 0x4e5d
281 { 0x62fe, 0x767e, 0x4edf }, // unit
282 { 0x842c, 0x5104 }, // unit10K
286 static const CJKIdeographicData gDataSimpChineseInformal
= {
288 0x96f6, 0x4e00, 0x4e8c, 0x4e09, 0x56db,
289 0x4e94, 0x516d, 0x4e03, 0x516b, 0x4e5d
291 { 0x5341, 0x767e, 0x5343 }, // unit
292 { 0x4e07, 0x4ebf }, // unit10K
296 static const CJKIdeographicData gDataSimpChineseFormal
= {
298 0x96f6, 0x58f9, 0x8d30, 0x53c1, 0x8086,
299 0x4f0d, 0x9646, 0x67d2, 0x634c, 0x7396
301 { 0x62fe, 0x4f70, 0x4edf }, // unit
302 { 0x4e07, 0x4ebf }, // unit10K
306 static const CJKIdeographicData gDataTradChineseInformal
= {
308 0x96f6, 0x4e00, 0x4e8c, 0x4e09, 0x56db,
309 0x4e94, 0x516d, 0x4e03, 0x516b, 0x4e5d
311 { 0x5341, 0x767e, 0x5343 }, // unit
312 { 0x842c, 0x5104 }, // unit10K
316 static const CJKIdeographicData gDataTradChineseFormal
= {
318 0x96f6, 0x58f9, 0x8cb3, 0x53c3, 0x8086,
319 0x4f0d, 0x9678, 0x67d2, 0x634c, 0x7396
321 { 0x62fe, 0x4f70, 0x4edf }, // unit
322 { 0x842c, 0x5104 }, // unit10K
328 CJKIdeographicToText(CounterValue aOrdinal
, nsSubstring
& aResult
,
329 const CJKIdeographicData
& data
)
331 NS_ASSERTION(aOrdinal
>= 0, "Only accept non-negative ordinal");
332 char16_t buf
[NUM_BUF_SIZE
];
333 int32_t idx
= NUM_BUF_SIZE
;
335 bool needZero
= (aOrdinal
== 0);
336 int32_t unitidx
= 0, unit10Kidx
= 0;
340 unit10Kidx
= pos
/ 4;
342 int32_t cur
= aOrdinal
% 10;
346 buf
[--idx
] = data
.digit
[0];
349 if (data
.lang
== CHINESE
) {
352 if (unit10Kidx
!= 0) {
353 if (data
.lang
== KOREAN
&& idx
!= NUM_BUF_SIZE
) {
356 buf
[--idx
] = data
.unit10K
[unit10Kidx
- 1];
359 buf
[--idx
] = data
.unit
[unitidx
- 1];
362 buf
[--idx
] = data
.digit
[cur
];
369 (aOrdinal
== 1 || (pos
> 4 && aOrdinal
% 1000 == 1))) {
375 (unitidx
!= 3 || (pos
== 3 && aOrdinal
== 1))) {
380 if (unitidx
> 0 || (pos
== 4 && (aOrdinal
% 1000) == 1)) {
387 buf
[--idx
] = data
.digit
[1];
394 } while (aOrdinal
> 0);
395 aResult
.Assign(buf
+ idx
, NUM_BUF_SIZE
- idx
);
399 #define HEBREW_GERESH 0x05F3
400 static const char16_t gHebrewDigit
[22] =
403 0x05D0, 0x05D1, 0x05D2, 0x05D3, 0x05D4, 0x05D5, 0x05D6, 0x05D7, 0x05D8,
404 // 10 20 30 40 50 60 70 80 90
405 0x05D9, 0x05DB, 0x05DC, 0x05DE, 0x05E0, 0x05E1, 0x05E2, 0x05E4, 0x05E6,
407 0x05E7, 0x05E8, 0x05E9, 0x05EA
411 HebrewToText(CounterValue aOrdinal
, nsSubstring
& aResult
)
413 if (aOrdinal
< 1 || aOrdinal
> 999999) {
417 bool outputSep
= false;
418 nsAutoString allText
, thousandsGroup
;
420 thousandsGroup
.Truncate();
421 int32_t n3
= aOrdinal
% 1000;
422 // Process digit for 100 - 900
423 for(int32_t n1
= 400; n1
> 0; )
428 thousandsGroup
.Append(gHebrewDigit
[(n1
/100)-1+18]);
434 // Process digit for 10 - 90
438 // Special process for 15 and 16
439 if(( 15 == n3
) || (16 == n3
)) {
440 // Special rule for religious reason...
441 // 15 is represented by 9 and 6, not 10 and 5
442 // 16 is represented by 9 and 7, not 10 and 6
444 thousandsGroup
.Append(gHebrewDigit
[ n2
- 1]);
447 thousandsGroup
.Append(gHebrewDigit
[(n2
/10)-1+9]);
452 // Process digit for 1 - 9
454 thousandsGroup
.Append(gHebrewDigit
[n3
-1]);
456 thousandsGroup
.Append((char16_t
)HEBREW_GERESH
);
457 if (allText
.IsEmpty())
458 allText
= thousandsGroup
;
460 allText
= thousandsGroup
+ allText
;
463 } while (aOrdinal
>= 1);
469 // Convert ordinal to Ethiopic numeric representation.
470 // The detail is available at http://www.ethiopic.org/Numerals/
471 // The algorithm used here is based on the pseudo-code put up there by
472 // Daniel Yacob <yacob@geez.org>.
473 // Another reference is Unicode 3.0 standard section 11.1.
474 #define ETHIOPIC_ONE 0x1369
475 #define ETHIOPIC_TEN 0x1372
476 #define ETHIOPIC_HUNDRED 0x137B
477 #define ETHIOPIC_TEN_THOUSAND 0x137C
480 EthiopicToText(CounterValue aOrdinal
, nsSubstring
& aResult
)
486 nsAutoString asciiNumberString
; // decimal string representation of ordinal
487 DecimalToText(aOrdinal
, asciiNumberString
);
488 uint8_t asciiStringLength
= asciiNumberString
.Length();
490 // If number length is odd, add a leading "0"
491 // the leading "0" preconditions the string to always have the
492 // leading tens place populated, this avoids a check within the loop.
493 // If we didn't add the leading "0", decrement asciiStringLength so
494 // it will be equivalent to a zero-based index in both cases.
495 if (asciiStringLength
& 1) {
496 asciiNumberString
.Insert(NS_LITERAL_STRING("0"), 0);
502 // Iterate from the highest digits to lowest
503 // indexFromLeft indexes digits (0 = most significant)
504 // groupIndexFromRight indexes pairs of digits (0 = least significant)
505 for (uint8_t indexFromLeft
= 0, groupIndexFromRight
= asciiStringLength
>> 1;
506 indexFromLeft
<= asciiStringLength
;
507 indexFromLeft
+= 2, groupIndexFromRight
--) {
508 uint8_t tensValue
= asciiNumberString
.CharAt(indexFromLeft
) & 0x0F;
509 uint8_t unitsValue
= asciiNumberString
.CharAt(indexFromLeft
+ 1) & 0x0F;
510 uint8_t groupValue
= tensValue
* 10 + unitsValue
;
512 bool oddGroup
= (groupIndexFromRight
& 1);
514 // we want to clear ETHIOPIC_ONE when it is superfluous
516 groupValue
== 1 && // one without a leading ten
517 (oddGroup
|| indexFromLeft
== 0)) { // preceding (100) or leading the sequence
521 // put it all together...
523 // map onto Ethiopic "tens":
524 aResult
.Append((char16_t
) (tensValue
+ ETHIOPIC_TEN
- 1));
527 //map onto Ethiopic "units":
528 aResult
.Append((char16_t
) (unitsValue
+ ETHIOPIC_ONE
- 1));
530 // Add a separator for all even groups except the last,
531 // and for odd groups with non-zero value.
534 aResult
.Append((char16_t
) ETHIOPIC_HUNDRED
);
537 if (groupIndexFromRight
) {
538 aResult
.Append((char16_t
) ETHIOPIC_TEN_THOUSAND
);
545 class BuiltinCounterStyle
: public CounterStyle
548 friend class CounterStyleManager
;
550 // will be initialized by CounterStyleManager::InitializeBuiltinCounterStyles
551 MOZ_CONSTEXPR
BuiltinCounterStyle()
552 : CounterStyle(NS_STYLE_LIST_STYLE_NONE
)
557 MOZ_CONSTEXPR
explicit BuiltinCounterStyle(int32_t aStyle
)
558 : CounterStyle(aStyle
)
563 virtual void GetPrefix(nsSubstring
& aResult
) MOZ_OVERRIDE
;
564 virtual void GetSuffix(nsSubstring
& aResult
) MOZ_OVERRIDE
;
565 virtual void GetSpokenCounterText(CounterValue aOrdinal
,
566 WritingMode aWritingMode
,
567 nsSubstring
& aResult
,
568 bool& aIsBullet
) MOZ_OVERRIDE
;
569 virtual bool IsBullet() MOZ_OVERRIDE
;
571 virtual void GetNegative(NegativeType
& aResult
) MOZ_OVERRIDE
;
572 virtual bool IsOrdinalInRange(CounterValue aOrdinal
) MOZ_OVERRIDE
;
573 virtual bool IsOrdinalInAutoRange(CounterValue aOrdinal
) MOZ_OVERRIDE
;
574 virtual void GetPad(PadType
& aResult
) MOZ_OVERRIDE
;
575 virtual CounterStyle
* GetFallback() MOZ_OVERRIDE
;
576 virtual uint8_t GetSpeakAs() MOZ_OVERRIDE
;
577 virtual bool UseNegativeSign() MOZ_OVERRIDE
;
579 virtual void CallFallbackStyle(CounterValue aOrdinal
,
580 WritingMode aWritingMode
,
581 nsSubstring
& aResult
,
582 bool& aIsRTL
) MOZ_OVERRIDE
;
583 virtual bool GetInitialCounterText(CounterValue aOrdinal
,
584 WritingMode aWritingMode
,
585 nsSubstring
& aResult
,
586 bool& aIsRTL
) MOZ_OVERRIDE
;
588 // Builtin counter style does not need refcount at all
589 NS_IMETHOD_(MozExternalRefCountType
) AddRef() MOZ_OVERRIDE
{ return 2; }
590 NS_IMETHOD_(MozExternalRefCountType
) Release() MOZ_OVERRIDE
{ return 2; }
594 BuiltinCounterStyle::GetPrefix(nsSubstring
& aResult
)
600 BuiltinCounterStyle::GetSuffix(nsSubstring
& aResult
)
603 case NS_STYLE_LIST_STYLE_NONE
:
607 case NS_STYLE_LIST_STYLE_DISC
:
608 case NS_STYLE_LIST_STYLE_CIRCLE
:
609 case NS_STYLE_LIST_STYLE_SQUARE
:
610 case NS_STYLE_LIST_STYLE_DISCLOSURE_CLOSED
:
611 case NS_STYLE_LIST_STYLE_DISCLOSURE_OPEN
:
615 case NS_STYLE_LIST_STYLE_TRAD_CHINESE_INFORMAL
:
616 case NS_STYLE_LIST_STYLE_TRAD_CHINESE_FORMAL
:
617 case NS_STYLE_LIST_STYLE_SIMP_CHINESE_INFORMAL
:
618 case NS_STYLE_LIST_STYLE_SIMP_CHINESE_FORMAL
:
619 case NS_STYLE_LIST_STYLE_JAPANESE_INFORMAL
:
620 case NS_STYLE_LIST_STYLE_JAPANESE_FORMAL
:
624 case NS_STYLE_LIST_STYLE_KOREAN_HANGUL_FORMAL
:
625 case NS_STYLE_LIST_STYLE_KOREAN_HANJA_INFORMAL
:
626 case NS_STYLE_LIST_STYLE_KOREAN_HANJA_FORMAL
:
627 aResult
.AssignLiteral(MOZ_UTF16(", "));
631 aResult
.AssignLiteral(MOZ_UTF16(". "));
636 static const char16_t kDiscCharacter
= 0x2022;
637 static const char16_t kCircleCharacter
= 0x25e6;
638 static const char16_t kSquareCharacter
= 0x25fe;
639 static const char16_t kRightPointingCharacter
= 0x25b8;
640 static const char16_t kLeftPointingCharacter
= 0x25c2;
641 static const char16_t kDownPointingCharacter
= 0x25be;
644 BuiltinCounterStyle::GetSpokenCounterText(CounterValue aOrdinal
,
645 WritingMode aWritingMode
,
646 nsSubstring
& aResult
,
650 case NS_STYLE_LIST_STYLE_NONE
:
651 case NS_STYLE_LIST_STYLE_DISC
:
652 case NS_STYLE_LIST_STYLE_CIRCLE
:
653 case NS_STYLE_LIST_STYLE_SQUARE
:
654 case NS_STYLE_LIST_STYLE_DISCLOSURE_CLOSED
:
655 case NS_STYLE_LIST_STYLE_DISCLOSURE_OPEN
: {
656 // Same as the initial representation
658 GetInitialCounterText(aOrdinal
, aWritingMode
, aResult
, isRTL
);
663 CounterStyle::GetSpokenCounterText(
664 aOrdinal
, aWritingMode
, aResult
, aIsBullet
);
670 BuiltinCounterStyle::IsBullet()
673 case NS_STYLE_LIST_STYLE_DISC
:
674 case NS_STYLE_LIST_STYLE_CIRCLE
:
675 case NS_STYLE_LIST_STYLE_SQUARE
:
676 case NS_STYLE_LIST_STYLE_DISCLOSURE_CLOSED
:
677 case NS_STYLE_LIST_STYLE_DISCLOSURE_OPEN
:
684 static const char16_t gJapaneseNegative
[] = {
685 0x30de, 0x30a4, 0x30ca, 0x30b9, 0x0000
687 static const char16_t gKoreanNegative
[] = {
688 0xb9c8, 0xc774, 0xb108, 0xc2a4, 0x0020, 0x0000
690 static const char16_t gSimpChineseNegative
[] = {
693 static const char16_t gTradChineseNegative
[] = {
698 BuiltinCounterStyle::GetNegative(NegativeType
& aResult
)
701 case NS_STYLE_LIST_STYLE_JAPANESE_FORMAL
:
702 case NS_STYLE_LIST_STYLE_JAPANESE_INFORMAL
:
703 aResult
.before
= gJapaneseNegative
;
706 case NS_STYLE_LIST_STYLE_KOREAN_HANGUL_FORMAL
:
707 case NS_STYLE_LIST_STYLE_KOREAN_HANJA_INFORMAL
:
708 case NS_STYLE_LIST_STYLE_KOREAN_HANJA_FORMAL
:
709 aResult
.before
= gKoreanNegative
;
712 case NS_STYLE_LIST_STYLE_SIMP_CHINESE_FORMAL
:
713 case NS_STYLE_LIST_STYLE_SIMP_CHINESE_INFORMAL
:
714 aResult
.before
= gSimpChineseNegative
;
717 case NS_STYLE_LIST_STYLE_TRAD_CHINESE_FORMAL
:
718 case NS_STYLE_LIST_STYLE_TRAD_CHINESE_INFORMAL
:
719 aResult
.before
= gTradChineseNegative
;
723 aResult
.before
.AssignLiteral(MOZ_UTF16("-"));
725 aResult
.after
.Truncate();
729 BuiltinCounterStyle::IsOrdinalInRange(CounterValue aOrdinal
)
734 case NS_STYLE_LIST_STYLE_NONE
:
735 case NS_STYLE_LIST_STYLE_DISC
:
736 case NS_STYLE_LIST_STYLE_CIRCLE
:
737 case NS_STYLE_LIST_STYLE_SQUARE
:
738 case NS_STYLE_LIST_STYLE_DISCLOSURE_CLOSED
:
739 case NS_STYLE_LIST_STYLE_DISCLOSURE_OPEN
:
741 case NS_STYLE_LIST_STYLE_DECIMAL
:
742 // use CJKIdeographicToText
743 case NS_STYLE_LIST_STYLE_JAPANESE_FORMAL
:
744 case NS_STYLE_LIST_STYLE_JAPANESE_INFORMAL
:
745 case NS_STYLE_LIST_STYLE_KOREAN_HANJA_FORMAL
:
746 case NS_STYLE_LIST_STYLE_KOREAN_HANJA_INFORMAL
:
747 case NS_STYLE_LIST_STYLE_KOREAN_HANGUL_FORMAL
:
748 case NS_STYLE_LIST_STYLE_TRAD_CHINESE_FORMAL
:
749 case NS_STYLE_LIST_STYLE_TRAD_CHINESE_INFORMAL
:
750 case NS_STYLE_LIST_STYLE_SIMP_CHINESE_FORMAL
:
751 case NS_STYLE_LIST_STYLE_SIMP_CHINESE_INFORMAL
:
754 // use EthiopicToText
755 case NS_STYLE_LIST_STYLE_ETHIOPIC_NUMERIC
:
756 return aOrdinal
>= 1;
759 case NS_STYLE_LIST_STYLE_HEBREW
:
760 return aOrdinal
>= 1 && aOrdinal
<= 999999;
765 BuiltinCounterStyle::IsOrdinalInAutoRange(CounterValue aOrdinal
)
769 case NS_STYLE_LIST_STYLE_NONE
:
770 case NS_STYLE_LIST_STYLE_DISC
:
771 case NS_STYLE_LIST_STYLE_CIRCLE
:
772 case NS_STYLE_LIST_STYLE_SQUARE
:
773 case NS_STYLE_LIST_STYLE_DISCLOSURE_CLOSED
:
774 case NS_STYLE_LIST_STYLE_DISCLOSURE_OPEN
:
776 case NS_STYLE_LIST_STYLE_DECIMAL
:
780 case NS_STYLE_LIST_STYLE_HEBREW
:
781 return aOrdinal
>= 0;
783 // complex predefined:
784 case NS_STYLE_LIST_STYLE_JAPANESE_FORMAL
:
785 case NS_STYLE_LIST_STYLE_JAPANESE_INFORMAL
:
786 case NS_STYLE_LIST_STYLE_KOREAN_HANJA_FORMAL
:
787 case NS_STYLE_LIST_STYLE_KOREAN_HANJA_INFORMAL
:
788 case NS_STYLE_LIST_STYLE_KOREAN_HANGUL_FORMAL
:
789 case NS_STYLE_LIST_STYLE_TRAD_CHINESE_FORMAL
:
790 case NS_STYLE_LIST_STYLE_TRAD_CHINESE_INFORMAL
:
791 case NS_STYLE_LIST_STYLE_SIMP_CHINESE_FORMAL
:
792 case NS_STYLE_LIST_STYLE_SIMP_CHINESE_INFORMAL
:
793 case NS_STYLE_LIST_STYLE_ETHIOPIC_NUMERIC
:
794 return IsOrdinalInRange(aOrdinal
);
797 NS_NOTREACHED("Unknown counter style");
803 BuiltinCounterStyle::GetPad(PadType
& aResult
)
806 aResult
.symbol
.Truncate();
809 /* virtual */ CounterStyle
*
810 BuiltinCounterStyle::GetFallback()
812 // Fallback of dependent builtin counter styles are handled in class
813 // DependentBuiltinCounterStyle.
814 return CounterStyleManager::GetDecimalStyle();
817 /* virtual */ uint8_t
818 BuiltinCounterStyle::GetSpeakAs()
821 case NS_STYLE_LIST_STYLE_NONE
:
822 case NS_STYLE_LIST_STYLE_DISC
:
823 case NS_STYLE_LIST_STYLE_CIRCLE
:
824 case NS_STYLE_LIST_STYLE_SQUARE
:
825 case NS_STYLE_LIST_STYLE_DISCLOSURE_CLOSED
:
826 case NS_STYLE_LIST_STYLE_DISCLOSURE_OPEN
:
827 return NS_STYLE_COUNTER_SPEAKAS_BULLETS
;
829 return NS_STYLE_COUNTER_SPEAKAS_NUMBERS
;
834 BuiltinCounterStyle::UseNegativeSign()
837 case NS_STYLE_LIST_STYLE_NONE
:
838 case NS_STYLE_LIST_STYLE_DISC
:
839 case NS_STYLE_LIST_STYLE_CIRCLE
:
840 case NS_STYLE_LIST_STYLE_SQUARE
:
841 case NS_STYLE_LIST_STYLE_DISCLOSURE_CLOSED
:
842 case NS_STYLE_LIST_STYLE_DISCLOSURE_OPEN
:
850 BuiltinCounterStyle::CallFallbackStyle(CounterValue aOrdinal
,
851 WritingMode aWritingMode
,
852 nsSubstring
& aResult
,
855 GetFallback()->GetCounterText(aOrdinal
, aWritingMode
, aResult
, aIsRTL
);
859 BuiltinCounterStyle::GetInitialCounterText(CounterValue aOrdinal
,
860 WritingMode aWritingMode
,
861 nsSubstring
& aResult
,
866 // used by counters & extends counter-style code only
867 // XXX We really need to do this the same way we do list bullets.
868 case NS_STYLE_LIST_STYLE_NONE
:
871 case NS_STYLE_LIST_STYLE_DISC
:
872 aResult
.Assign(kDiscCharacter
);
874 case NS_STYLE_LIST_STYLE_CIRCLE
:
875 aResult
.Assign(kCircleCharacter
);
877 case NS_STYLE_LIST_STYLE_SQUARE
:
878 aResult
.Assign(kSquareCharacter
);
880 case NS_STYLE_LIST_STYLE_DISCLOSURE_CLOSED
:
881 if (aWritingMode
.IsVertical()) {
882 aResult
.Assign(kDownPointingCharacter
);
883 } else if (aWritingMode
.IsBidiLTR()) {
884 aResult
.Assign(kRightPointingCharacter
);
886 aResult
.Assign(kLeftPointingCharacter
);
889 case NS_STYLE_LIST_STYLE_DISCLOSURE_OPEN
:
890 if (!aWritingMode
.IsVertical()) {
891 aResult
.Assign(kDownPointingCharacter
);
892 } else if (aWritingMode
.IsVerticalLR()) {
893 aResult
.Assign(kRightPointingCharacter
);
895 aResult
.Assign(kLeftPointingCharacter
);
899 case NS_STYLE_LIST_STYLE_DECIMAL
:
900 return DecimalToText(aOrdinal
, aResult
);
902 case NS_STYLE_LIST_STYLE_TRAD_CHINESE_INFORMAL
:
903 return CJKIdeographicToText(aOrdinal
, aResult
, gDataTradChineseInformal
);
904 case NS_STYLE_LIST_STYLE_TRAD_CHINESE_FORMAL
:
905 return CJKIdeographicToText(aOrdinal
, aResult
, gDataTradChineseFormal
);
906 case NS_STYLE_LIST_STYLE_SIMP_CHINESE_INFORMAL
:
907 return CJKIdeographicToText(aOrdinal
, aResult
, gDataSimpChineseInformal
);
908 case NS_STYLE_LIST_STYLE_SIMP_CHINESE_FORMAL
:
909 return CJKIdeographicToText(aOrdinal
, aResult
, gDataSimpChineseFormal
);
910 case NS_STYLE_LIST_STYLE_JAPANESE_INFORMAL
:
911 return CJKIdeographicToText(aOrdinal
, aResult
, gDataJapaneseInformal
);
912 case NS_STYLE_LIST_STYLE_JAPANESE_FORMAL
:
913 return CJKIdeographicToText(aOrdinal
, aResult
, gDataJapaneseFormal
);
914 case NS_STYLE_LIST_STYLE_KOREAN_HANGUL_FORMAL
:
915 return CJKIdeographicToText(aOrdinal
, aResult
, gDataKoreanHangulFormal
);
916 case NS_STYLE_LIST_STYLE_KOREAN_HANJA_INFORMAL
:
917 return CJKIdeographicToText(aOrdinal
, aResult
, gDataKoreanHanjaInformal
);
918 case NS_STYLE_LIST_STYLE_KOREAN_HANJA_FORMAL
:
919 return CJKIdeographicToText(aOrdinal
, aResult
, gDataKoreanHanjaFormal
);
921 case NS_STYLE_LIST_STYLE_HEBREW
:
923 return HebrewToText(aOrdinal
, aResult
);
925 case NS_STYLE_LIST_STYLE_ETHIOPIC_NUMERIC
:
926 return EthiopicToText(aOrdinal
, aResult
);
929 NS_NOTREACHED("Unknown builtin counter style");
934 class DependentBuiltinCounterStyle MOZ_FINAL
: public BuiltinCounterStyle
937 ~DependentBuiltinCounterStyle() {}
939 DependentBuiltinCounterStyle(int32_t aStyle
, CounterStyleManager
* aManager
)
940 : BuiltinCounterStyle(aStyle
),
943 NS_ASSERTION(IsDependentStyle(), "Not a dependent builtin style");
944 NS_ABORT_IF_FALSE(!IsCustomStyle(), "Not a builtin style");
947 virtual CounterStyle
* GetFallback() MOZ_OVERRIDE
;
949 // DependentBuiltinCounterStyle is managed in the same way as
950 // CustomCounterStyle.
951 NS_IMETHOD_(MozExternalRefCountType
) AddRef() MOZ_OVERRIDE
;
952 NS_IMETHOD_(MozExternalRefCountType
) Release() MOZ_OVERRIDE
;
954 void* operator new(size_t sz
, nsPresContext
* aPresContext
) CPP_THROW_NEW
956 return aPresContext
->PresShell()->AllocateByObjectID(
957 nsPresArena::DependentBuiltinCounterStyle_id
, sz
);
963 nsIPresShell
* shell
= mManager
->PresContext()->PresShell();
964 this->~DependentBuiltinCounterStyle();
965 shell
->FreeByObjectID(nsPresArena::DependentBuiltinCounterStyle_id
, this);
968 CounterStyleManager
* mManager
;
970 nsAutoRefCnt mRefCnt
;
974 NS_IMPL_ADDREF(DependentBuiltinCounterStyle
)
975 NS_IMPL_RELEASE_WITH_DESTROY(DependentBuiltinCounterStyle
, Destroy())
977 /* virtual */ CounterStyle
*
978 DependentBuiltinCounterStyle::GetFallback()
980 switch (GetStyle()) {
981 case NS_STYLE_LIST_STYLE_JAPANESE_INFORMAL
:
982 case NS_STYLE_LIST_STYLE_JAPANESE_FORMAL
:
983 case NS_STYLE_LIST_STYLE_KOREAN_HANGUL_FORMAL
:
984 case NS_STYLE_LIST_STYLE_KOREAN_HANJA_INFORMAL
:
985 case NS_STYLE_LIST_STYLE_KOREAN_HANJA_FORMAL
:
986 case NS_STYLE_LIST_STYLE_SIMP_CHINESE_INFORMAL
:
987 case NS_STYLE_LIST_STYLE_SIMP_CHINESE_FORMAL
:
988 case NS_STYLE_LIST_STYLE_TRAD_CHINESE_INFORMAL
:
989 case NS_STYLE_LIST_STYLE_TRAD_CHINESE_FORMAL
:
990 // These styles all have a larger range than cjk-decimal, so the
991 // only case fallback is accessed is that they are extended.
992 // Since extending styles will cache the data themselves, we need
993 // not cache it here.
994 return mManager
->BuildCounterStyle(NS_LITERAL_STRING("cjk-decimal"));
996 NS_NOTREACHED("Not a valid dependent builtin style");
997 return BuiltinCounterStyle::GetFallback();
1001 class CustomCounterStyle MOZ_FINAL
: public CounterStyle
1004 ~CustomCounterStyle() {}
1006 CustomCounterStyle(CounterStyleManager
* aManager
,
1007 nsCSSCounterStyleRule
* aRule
)
1008 : CounterStyle(NS_STYLE_LIST_STYLE_CUSTOM
),
1011 mRuleGeneration(aRule
->GetGeneration()),
1012 mSystem(aRule
->GetSystem()),
1015 mSpeakAsCounter(nullptr),
1017 mExtendsRoot(nullptr)
1021 // This method will clear all cached data in the style and update the
1022 // generation number of the rule. It should be called when the rule of
1023 // this style is changed.
1024 void ResetCachedData();
1026 // This method will reset all cached data which may depend on other
1027 // counter style. It will reset all pointers to other counter styles.
1028 // For counter style extends other, in addition, all fields will be
1029 // reset to uninitialized state. This method should be called when any
1030 // other counter style is added, removed, or changed.
1031 void ResetDependentData();
1033 nsCSSCounterStyleRule
* GetRule() const { return mRule
; }
1034 uint32_t GetRuleGeneration() const { return mRuleGeneration
; }
1036 virtual void GetPrefix(nsSubstring
& aResult
) MOZ_OVERRIDE
;
1037 virtual void GetSuffix(nsSubstring
& aResult
) MOZ_OVERRIDE
;
1038 virtual void GetSpokenCounterText(CounterValue aOrdinal
,
1039 WritingMode aWritingMode
,
1040 nsSubstring
& aResult
,
1041 bool& aIsBullet
) MOZ_OVERRIDE
;
1042 virtual bool IsBullet() MOZ_OVERRIDE
;
1044 virtual void GetNegative(NegativeType
& aResult
) MOZ_OVERRIDE
;
1045 virtual bool IsOrdinalInRange(CounterValue aOrdinal
) MOZ_OVERRIDE
;
1046 virtual bool IsOrdinalInAutoRange(CounterValue aOrdinal
) MOZ_OVERRIDE
;
1047 virtual void GetPad(PadType
& aResult
) MOZ_OVERRIDE
;
1048 virtual CounterStyle
* GetFallback() MOZ_OVERRIDE
;
1049 virtual uint8_t GetSpeakAs() MOZ_OVERRIDE
;
1050 virtual bool UseNegativeSign() MOZ_OVERRIDE
;
1052 virtual void CallFallbackStyle(CounterValue aOrdinal
,
1053 WritingMode aWritingMode
,
1054 nsSubstring
& aResult
,
1055 bool& aIsRTL
) MOZ_OVERRIDE
;
1056 virtual bool GetInitialCounterText(CounterValue aOrdinal
,
1057 WritingMode aWritingMode
,
1058 nsSubstring
& aResult
,
1059 bool& aIsRTL
) MOZ_OVERRIDE
;
1061 bool IsExtendsSystem()
1063 return mSystem
== NS_STYLE_COUNTER_SYSTEM_EXTENDS
;
1066 // CustomCounterStyle should be reference-counted because it may be
1067 // dereferenced from the manager but still referenced by nodes and
1068 // frames before the style change is propagated.
1069 NS_IMETHOD_(MozExternalRefCountType
) AddRef() MOZ_OVERRIDE
;
1070 NS_IMETHOD_(MozExternalRefCountType
) Release() MOZ_OVERRIDE
;
1072 void* operator new(size_t sz
, nsPresContext
* aPresContext
) CPP_THROW_NEW
1074 return aPresContext
->PresShell()->AllocateByObjectID(
1075 nsPresArena::CustomCounterStyle_id
, sz
);
1081 nsIPresShell
* shell
= mManager
->PresContext()->PresShell();
1082 this->~CustomCounterStyle();
1083 shell
->FreeByObjectID(nsPresArena::CustomCounterStyle_id
, this);
1086 const nsTArray
<nsString
>& GetSymbols();
1087 const nsTArray
<AdditiveSymbol
>& GetAdditiveSymbols();
1089 // The speak-as values of counter styles may form a loop, and the
1090 // loops may have complex interaction with the loop formed by
1091 // extending. To solve this problem, the computation of speak-as is
1092 // divided into two phases:
1093 // 1. figure out the raw value, by ComputeRawSpeakAs, and
1094 // 2. eliminate loop, by ComputeSpeakAs.
1095 // See comments before the definitions of these methods for details.
1096 uint8_t GetSpeakAsAutoValue();
1097 void ComputeRawSpeakAs(uint8_t& aSpeakAs
,
1098 CounterStyle
*& aSpeakAsCounter
);
1099 CounterStyle
* ComputeSpeakAs();
1101 CounterStyle
* ComputeExtends();
1102 CounterStyle
* GetExtends();
1103 CounterStyle
* GetExtendsRoot();
1105 // CounterStyleManager should always overlive any CounterStyle as it
1106 // is owned by nsPresContext, and will be released after all nodes and
1107 // frames are released.
1108 CounterStyleManager
* mManager
;
1110 nsRefPtr
<nsCSSCounterStyleRule
> mRule
;
1111 uint32_t mRuleGeneration
;
1118 FLAG_EXTENDS_VISITED
= 1 << 0,
1119 FLAG_EXTENDS_LOOP
= 1 << 1,
1120 FLAG_SPEAKAS_VISITED
= 1 << 2,
1121 FLAG_SPEAKAS_LOOP
= 1 << 3,
1123 FLAG_NEGATIVE_INITED
= 1 << 4,
1124 FLAG_PREFIX_INITED
= 1 << 5,
1125 FLAG_SUFFIX_INITED
= 1 << 6,
1126 FLAG_PAD_INITED
= 1 << 7,
1127 FLAG_SPEAKAS_INITED
= 1 << 8,
1131 // Fields below will be initialized when necessary.
1132 nsTArray
<nsString
> mSymbols
;
1133 nsTArray
<AdditiveSymbol
> mAdditiveSymbols
;
1134 NegativeType mNegative
;
1135 nsString mPrefix
, mSuffix
;
1138 // CounterStyleManager will guarantee that none of the pointers below
1139 // refers to a freed CounterStyle. There are two possible cases where
1140 // the manager will release its reference to a CounterStyle: 1. the
1141 // manager itself is released, 2. a rule is invalidated. In the first
1142 // case, all counter style are removed from the manager, and should
1143 // also have been dereferenced from other objects. All styles will be
1144 // released all together. In the second case, CounterStyleManager::
1145 // NotifyRuleChanged will guarantee that all pointers will be reset
1146 // before any CounterStyle is released.
1148 CounterStyle
* mFallback
;
1149 // This field refers to the last counter in a speak-as chain.
1150 // That counter must not speak as another counter.
1151 CounterStyle
* mSpeakAsCounter
;
1153 CounterStyle
* mExtends
;
1154 // This field refers to the last counter in the extends chain. The
1155 // counter must be either a builtin style or a style whose system is
1157 CounterStyle
* mExtendsRoot
;
1159 nsAutoRefCnt mRefCnt
;
1160 NS_DECL_OWNINGTHREAD
1163 NS_IMPL_ADDREF(CustomCounterStyle
)
1164 NS_IMPL_RELEASE_WITH_DESTROY(CustomCounterStyle
, Destroy())
1167 CustomCounterStyle::ResetCachedData()
1170 mAdditiveSymbols
.Clear();
1171 mFlags
&= ~(FLAG_NEGATIVE_INITED
|
1172 FLAG_PREFIX_INITED
|
1173 FLAG_SUFFIX_INITED
|
1175 FLAG_SPEAKAS_INITED
);
1176 mFallback
= nullptr;
1177 mSpeakAsCounter
= nullptr;
1179 mExtendsRoot
= nullptr;
1180 mRuleGeneration
= mRule
->GetGeneration();
1184 CustomCounterStyle::ResetDependentData()
1186 mFlags
&= ~FLAG_SPEAKAS_INITED
;
1187 mSpeakAsCounter
= nullptr;
1188 mFallback
= nullptr;
1190 mExtendsRoot
= nullptr;
1191 if (IsExtendsSystem()) {
1192 mFlags
&= ~(FLAG_NEGATIVE_INITED
|
1193 FLAG_PREFIX_INITED
|
1194 FLAG_SUFFIX_INITED
|
1200 CustomCounterStyle::GetPrefix(nsSubstring
& aResult
)
1202 if (!(mFlags
& FLAG_PREFIX_INITED
)) {
1203 mFlags
|= FLAG_PREFIX_INITED
;
1205 const nsCSSValue
& value
= mRule
->GetDesc(eCSSCounterDesc_Prefix
);
1206 if (value
.UnitHasStringValue()) {
1207 value
.GetStringValue(mPrefix
);
1208 } else if (IsExtendsSystem()) {
1209 GetExtends()->GetPrefix(mPrefix
);
1218 CustomCounterStyle::GetSuffix(nsSubstring
& aResult
)
1220 if (!(mFlags
& FLAG_SUFFIX_INITED
)) {
1221 mFlags
|= FLAG_SUFFIX_INITED
;
1223 const nsCSSValue
& value
= mRule
->GetDesc(eCSSCounterDesc_Suffix
);
1224 if (value
.UnitHasStringValue()) {
1225 value
.GetStringValue(mSuffix
);
1226 } else if (IsExtendsSystem()) {
1227 GetExtends()->GetSuffix(mSuffix
);
1229 mSuffix
.AssignLiteral(MOZ_UTF16(". "));
1236 CustomCounterStyle::GetSpokenCounterText(CounterValue aOrdinal
,
1237 WritingMode aWritingMode
,
1238 nsSubstring
& aResult
,
1241 if (GetSpeakAs() != NS_STYLE_COUNTER_SPEAKAS_OTHER
) {
1242 CounterStyle::GetSpokenCounterText(
1243 aOrdinal
, aWritingMode
, aResult
, aIsBullet
);
1245 NS_ABORT_IF_FALSE(mSpeakAsCounter
,
1246 "mSpeakAsCounter should have been initialized.");
1247 mSpeakAsCounter
->GetSpokenCounterText(
1248 aOrdinal
, aWritingMode
, aResult
, aIsBullet
);
1253 CustomCounterStyle::IsBullet()
1256 case NS_STYLE_COUNTER_SYSTEM_CYCLIC
:
1257 // Only use ::-moz-list-bullet for cyclic system
1259 case NS_STYLE_COUNTER_SYSTEM_EXTENDS
:
1260 return GetExtendsRoot()->IsBullet();
1267 CustomCounterStyle::GetNegative(NegativeType
& aResult
)
1269 if (!(mFlags
& FLAG_NEGATIVE_INITED
)) {
1270 mFlags
|= FLAG_NEGATIVE_INITED
;
1271 const nsCSSValue
& value
= mRule
->GetDesc(eCSSCounterDesc_Negative
);
1272 switch (value
.GetUnit()) {
1273 case eCSSUnit_Ident
:
1274 case eCSSUnit_String
:
1275 value
.GetStringValue(mNegative
.before
);
1276 mNegative
.after
.Truncate();
1278 case eCSSUnit_Pair
: {
1279 const nsCSSValuePair
& pair
= value
.GetPairValue();
1280 pair
.mXValue
.GetStringValue(mNegative
.before
);
1281 pair
.mYValue
.GetStringValue(mNegative
.after
);
1285 if (IsExtendsSystem()) {
1286 GetExtends()->GetNegative(mNegative
);
1288 mNegative
.before
.AssignLiteral(MOZ_UTF16("-"));
1289 mNegative
.after
.Truncate();
1294 aResult
= mNegative
;
1298 IsRangeValueInfinite(const nsCSSValue
& aValue
)
1300 return aValue
.GetUnit() == eCSSUnit_Enumerated
&&
1301 aValue
.GetIntValue() == NS_STYLE_COUNTER_RANGE_INFINITE
;
1305 CustomCounterStyle::IsOrdinalInRange(CounterValue aOrdinal
)
1307 const nsCSSValue
& value
= mRule
->GetDesc(eCSSCounterDesc_Range
);
1308 if (value
.GetUnit() == eCSSUnit_PairList
) {
1309 for (const nsCSSValuePairList
* item
= value
.GetPairListValue();
1310 item
!= nullptr; item
= item
->mNext
) {
1311 const nsCSSValue
& lowerBound
= item
->mXValue
;
1312 const nsCSSValue
& upperBound
= item
->mYValue
;
1313 if ((IsRangeValueInfinite(lowerBound
) ||
1314 aOrdinal
>= lowerBound
.GetIntValue()) &&
1315 (IsRangeValueInfinite(upperBound
) ||
1316 aOrdinal
<= upperBound
.GetIntValue())) {
1321 } else if (IsExtendsSystem() && value
.GetUnit() == eCSSUnit_None
) {
1322 // Only use the range of extended style when 'range' is not specified.
1323 return GetExtends()->IsOrdinalInRange(aOrdinal
);
1325 return IsOrdinalInAutoRange(aOrdinal
);
1329 CustomCounterStyle::IsOrdinalInAutoRange(CounterValue aOrdinal
)
1332 case NS_STYLE_COUNTER_SYSTEM_CYCLIC
:
1333 case NS_STYLE_COUNTER_SYSTEM_NUMERIC
:
1334 case NS_STYLE_COUNTER_SYSTEM_FIXED
:
1336 case NS_STYLE_COUNTER_SYSTEM_ALPHABETIC
:
1337 case NS_STYLE_COUNTER_SYSTEM_SYMBOLIC
:
1338 return aOrdinal
>= 1;
1339 case NS_STYLE_COUNTER_SYSTEM_ADDITIVE
:
1340 return aOrdinal
>= 0;
1341 case NS_STYLE_COUNTER_SYSTEM_EXTENDS
:
1342 return GetExtendsRoot()->IsOrdinalInAutoRange(aOrdinal
);
1344 NS_NOTREACHED("Invalid system for computing auto value.");
1350 CustomCounterStyle::GetPad(PadType
& aResult
)
1352 if (!(mFlags
& FLAG_PAD_INITED
)) {
1353 mFlags
|= FLAG_PAD_INITED
;
1354 const nsCSSValue
& value
= mRule
->GetDesc(eCSSCounterDesc_Pad
);
1355 if (value
.GetUnit() == eCSSUnit_Pair
) {
1356 const nsCSSValuePair
& pair
= value
.GetPairValue();
1357 mPad
.width
= pair
.mXValue
.GetIntValue();
1358 pair
.mYValue
.GetStringValue(mPad
.symbol
);
1359 } else if (IsExtendsSystem()) {
1360 GetExtends()->GetPad(mPad
);
1363 mPad
.symbol
.Truncate();
1369 /* virtual */ CounterStyle
*
1370 CustomCounterStyle::GetFallback()
1373 const nsCSSValue
& value
= mRule
->GetDesc(eCSSCounterDesc_Fallback
);
1374 if (value
.UnitHasStringValue()) {
1375 mFallback
= mManager
->BuildCounterStyle(
1376 nsDependentString(value
.GetStringBufferValue()));
1377 } else if (IsExtendsSystem()) {
1378 mFallback
= GetExtends()->GetFallback();
1380 mFallback
= CounterStyleManager::GetDecimalStyle();
1386 /* virtual */ uint8_t
1387 CustomCounterStyle::GetSpeakAs()
1389 if (!(mFlags
& FLAG_SPEAKAS_INITED
)) {
1396 CustomCounterStyle::UseNegativeSign()
1399 case NS_STYLE_COUNTER_SYSTEM_SYMBOLIC
:
1400 case NS_STYLE_COUNTER_SYSTEM_ALPHABETIC
:
1401 case NS_STYLE_COUNTER_SYSTEM_NUMERIC
:
1402 case NS_STYLE_COUNTER_SYSTEM_ADDITIVE
:
1404 case NS_STYLE_COUNTER_SYSTEM_EXTENDS
:
1405 return GetExtendsRoot()->UseNegativeSign();
1412 CustomCounterStyle::CallFallbackStyle(CounterValue aOrdinal
,
1413 WritingMode aWritingMode
,
1414 nsSubstring
& aResult
,
1417 CounterStyle
* fallback
= GetFallback();
1418 // If it recursively falls back to this counter style again,
1419 // it will then fallback to decimal to break the loop.
1420 mFallback
= CounterStyleManager::GetDecimalStyle();
1421 fallback
->GetCounterText(aOrdinal
, aWritingMode
, aResult
, aIsRTL
);
1422 mFallback
= fallback
;
1426 CustomCounterStyle::GetInitialCounterText(CounterValue aOrdinal
,
1427 WritingMode aWritingMode
,
1428 nsSubstring
& aResult
,
1432 case NS_STYLE_COUNTER_SYSTEM_CYCLIC
:
1433 return GetCyclicCounterText(aOrdinal
, aResult
, GetSymbols());
1434 case NS_STYLE_COUNTER_SYSTEM_FIXED
: {
1435 int32_t start
= mRule
->GetSystemArgument().GetIntValue();
1436 return GetFixedCounterText(aOrdinal
, aResult
, start
, GetSymbols());
1438 case NS_STYLE_COUNTER_SYSTEM_SYMBOLIC
:
1439 return GetSymbolicCounterText(aOrdinal
, aResult
, GetSymbols());
1440 case NS_STYLE_COUNTER_SYSTEM_ALPHABETIC
:
1441 return GetAlphabeticCounterText(aOrdinal
, aResult
, GetSymbols());
1442 case NS_STYLE_COUNTER_SYSTEM_NUMERIC
:
1443 return GetNumericCounterText(aOrdinal
, aResult
, GetSymbols());
1444 case NS_STYLE_COUNTER_SYSTEM_ADDITIVE
:
1445 return GetAdditiveCounterText(aOrdinal
, aResult
, GetAdditiveSymbols());
1446 case NS_STYLE_COUNTER_SYSTEM_EXTENDS
:
1447 return GetExtendsRoot()->
1448 GetInitialCounterText(aOrdinal
, aWritingMode
, aResult
, aIsRTL
);
1450 NS_NOTREACHED("Invalid system.");
1455 const nsTArray
<nsString
>&
1456 CustomCounterStyle::GetSymbols()
1458 if (mSymbols
.IsEmpty()) {
1459 const nsCSSValue
& values
= mRule
->GetDesc(eCSSCounterDesc_Symbols
);
1460 for (const nsCSSValueList
* item
= values
.GetListValue();
1461 item
; item
= item
->mNext
) {
1462 nsString
* symbol
= mSymbols
.AppendElement();
1463 item
->mValue
.GetStringValue(*symbol
);
1470 const nsTArray
<AdditiveSymbol
>&
1471 CustomCounterStyle::GetAdditiveSymbols()
1473 if (mAdditiveSymbols
.IsEmpty()) {
1474 const nsCSSValue
& values
= mRule
->GetDesc(eCSSCounterDesc_AdditiveSymbols
);
1475 for (const nsCSSValuePairList
* item
= values
.GetPairListValue();
1476 item
; item
= item
->mNext
) {
1477 AdditiveSymbol
* symbol
= mAdditiveSymbols
.AppendElement();
1478 symbol
->weight
= item
->mXValue
.GetIntValue();
1479 item
->mYValue
.GetStringValue(symbol
->symbol
);
1481 mAdditiveSymbols
.Compact();
1483 return mAdditiveSymbols
;
1486 // This method is used to provide the computed value for 'auto'.
1488 CustomCounterStyle::GetSpeakAsAutoValue()
1490 uint8_t system
= mSystem
;
1491 if (IsExtendsSystem()) {
1492 CounterStyle
* root
= GetExtendsRoot();
1493 if (!root
->IsCustomStyle()) {
1494 // It is safe to call GetSpeakAs on non-custom style.
1495 return root
->GetSpeakAs();
1497 system
= static_cast<CustomCounterStyle
*>(root
)->mSystem
;
1500 case NS_STYLE_COUNTER_SYSTEM_ALPHABETIC
:
1501 return NS_STYLE_COUNTER_SPEAKAS_SPELL_OUT
;
1502 case NS_STYLE_COUNTER_SYSTEM_CYCLIC
:
1503 return NS_STYLE_COUNTER_SPEAKAS_BULLETS
;
1505 return NS_STYLE_COUNTER_SPEAKAS_NUMBERS
;
1509 // This method corresponds to the first stage of computation of the
1510 // value of speak-as. It will extract the value from the rule and
1511 // possibly recursively call itself on the extended style to figure
1512 // out the raw value. To keep things clear, this method is designed to
1513 // have no side effects (but functions it calls may still affect other
1514 // fields in the style.)
1516 CustomCounterStyle::ComputeRawSpeakAs(uint8_t& aSpeakAs
,
1517 CounterStyle
*& aSpeakAsCounter
)
1519 NS_ASSERTION(!(mFlags
& FLAG_SPEAKAS_INITED
),
1520 "ComputeRawSpeakAs is called with speak-as inited.");
1522 const nsCSSValue
& value
= mRule
->GetDesc(eCSSCounterDesc_SpeakAs
);
1523 switch (value
.GetUnit()) {
1525 aSpeakAs
= GetSpeakAsAutoValue();
1527 case eCSSUnit_Enumerated
:
1528 aSpeakAs
= value
.GetIntValue();
1530 case eCSSUnit_Ident
:
1531 aSpeakAs
= NS_STYLE_COUNTER_SPEAKAS_OTHER
;
1532 aSpeakAsCounter
= mManager
->BuildCounterStyle(
1533 nsDependentString(value
.GetStringBufferValue()));
1535 case eCSSUnit_Null
: {
1536 if (!IsExtendsSystem()) {
1537 aSpeakAs
= GetSpeakAsAutoValue();
1539 CounterStyle
* extended
= GetExtends();
1540 if (!extended
->IsCustomStyle()) {
1541 // It is safe to call GetSpeakAs on non-custom style.
1542 aSpeakAs
= extended
->GetSpeakAs();
1544 CustomCounterStyle
* custom
=
1545 static_cast<CustomCounterStyle
*>(extended
);
1546 if (!(custom
->mFlags
& FLAG_SPEAKAS_INITED
)) {
1547 custom
->ComputeRawSpeakAs(aSpeakAs
, aSpeakAsCounter
);
1549 aSpeakAs
= custom
->mSpeakAs
;
1550 aSpeakAsCounter
= custom
->mSpeakAsCounter
;
1557 NS_NOTREACHED("Invalid speak-as value");
1561 // This method corresponds to the second stage of getting speak-as
1562 // related values. It will recursively figure out the final value of
1563 // mSpeakAs and mSpeakAsCounter. This method returns nullptr if the
1564 // caller is in a loop, and the root counter style in the chain
1565 // otherwise. It use the same loop detection algorithm as
1566 // CustomCounterStyle::ComputeExtends, see comments before that
1567 // method for more details.
1569 CustomCounterStyle::ComputeSpeakAs()
1571 if (mFlags
& FLAG_SPEAKAS_INITED
) {
1572 if (mSpeakAs
== NS_STYLE_COUNTER_SPEAKAS_OTHER
) {
1573 return mSpeakAsCounter
;
1578 if (mFlags
& FLAG_SPEAKAS_VISITED
) {
1580 mFlags
|= FLAG_SPEAKAS_LOOP
;
1584 CounterStyle
* speakAsCounter
;
1585 ComputeRawSpeakAs(mSpeakAs
, speakAsCounter
);
1587 bool inLoop
= false;
1588 if (mSpeakAs
!= NS_STYLE_COUNTER_SPEAKAS_OTHER
) {
1589 mSpeakAsCounter
= nullptr;
1590 } else if (!speakAsCounter
->IsCustomStyle()) {
1591 mSpeakAsCounter
= speakAsCounter
;
1593 mFlags
|= FLAG_SPEAKAS_VISITED
;
1594 CounterStyle
* target
=
1595 static_cast<CustomCounterStyle
*>(speakAsCounter
)->ComputeSpeakAs();
1596 mFlags
&= ~FLAG_SPEAKAS_VISITED
;
1599 NS_ASSERTION(!(mFlags
& FLAG_SPEAKAS_LOOP
),
1600 "Invalid state for speak-as loop detecting");
1601 mSpeakAsCounter
= target
;
1603 mSpeakAs
= GetSpeakAsAutoValue();
1604 mSpeakAsCounter
= nullptr;
1605 if (mFlags
& FLAG_SPEAKAS_LOOP
) {
1606 mFlags
&= ~FLAG_SPEAKAS_LOOP
;
1613 mFlags
|= FLAG_SPEAKAS_INITED
;
1617 return mSpeakAsCounter
? mSpeakAsCounter
: this;
1620 // This method will recursively figure out mExtends in the whole chain.
1621 // It will return nullptr if the caller is in a loop, and return this
1622 // otherwise. To detect the loop, this method marks the style VISITED
1623 // before the recursive call. When a VISITED style is reached again, the
1624 // loop is detected, and flag LOOP will be marked on the first style in
1625 // loop. mExtends of all counter styles in loop will be set to decimal
1626 // according to the spec.
1628 CustomCounterStyle::ComputeExtends()
1630 if (!IsExtendsSystem() || mExtends
) {
1633 if (mFlags
& FLAG_EXTENDS_VISITED
) {
1635 mFlags
|= FLAG_EXTENDS_LOOP
;
1639 const nsCSSValue
& value
= mRule
->GetSystemArgument();
1640 CounterStyle
* nextCounter
= mManager
->BuildCounterStyle(
1641 nsDependentString(value
.GetStringBufferValue()));
1642 CounterStyle
* target
= nextCounter
;
1643 if (nextCounter
->IsCustomStyle()) {
1644 mFlags
|= FLAG_EXTENDS_VISITED
;
1645 target
= static_cast<CustomCounterStyle
*>(nextCounter
)->ComputeExtends();
1646 mFlags
&= ~FLAG_EXTENDS_VISITED
;
1650 NS_ASSERTION(!(mFlags
& FLAG_EXTENDS_LOOP
),
1651 "Invalid state for extends loop detecting");
1652 mExtends
= nextCounter
;
1655 mExtends
= CounterStyleManager::GetDecimalStyle();
1656 if (mFlags
& FLAG_EXTENDS_LOOP
) {
1657 mFlags
&= ~FLAG_EXTENDS_LOOP
;
1666 CustomCounterStyle::GetExtends()
1669 // Any extends loop will be eliminated in the method below.
1676 CustomCounterStyle::GetExtendsRoot()
1678 if (!mExtendsRoot
) {
1679 CounterStyle
* extended
= GetExtends();
1680 mExtendsRoot
= extended
;
1681 if (extended
->IsCustomStyle()) {
1682 CustomCounterStyle
* custom
= static_cast<CustomCounterStyle
*>(extended
);
1683 if (custom
->IsExtendsSystem()) {
1684 // This will make mExtendsRoot in the whole extends chain be
1685 // set recursively, which could save work when part of a chain
1686 // is shared by multiple counter styles.
1687 mExtendsRoot
= custom
->GetExtendsRoot();
1691 return mExtendsRoot
;
1695 CounterStyle::IsDependentStyle() const
1698 // CustomCounterStyle
1699 case NS_STYLE_LIST_STYLE_CUSTOM
:
1700 // DependentBuiltinCounterStyle
1701 case NS_STYLE_LIST_STYLE_JAPANESE_INFORMAL
:
1702 case NS_STYLE_LIST_STYLE_JAPANESE_FORMAL
:
1703 case NS_STYLE_LIST_STYLE_KOREAN_HANGUL_FORMAL
:
1704 case NS_STYLE_LIST_STYLE_KOREAN_HANJA_INFORMAL
:
1705 case NS_STYLE_LIST_STYLE_KOREAN_HANJA_FORMAL
:
1706 case NS_STYLE_LIST_STYLE_SIMP_CHINESE_INFORMAL
:
1707 case NS_STYLE_LIST_STYLE_SIMP_CHINESE_FORMAL
:
1708 case NS_STYLE_LIST_STYLE_TRAD_CHINESE_INFORMAL
:
1709 case NS_STYLE_LIST_STYLE_TRAD_CHINESE_FORMAL
:
1712 // BuiltinCounterStyle
1719 CountGraphemeClusters(const nsSubstring
& aText
)
1721 using mozilla::unicode::ClusterIterator
;
1722 ClusterIterator
iter(aText
.Data(), aText
.Length());
1724 while (!iter
.AtEnd()) {
1732 CounterStyle::GetCounterText(CounterValue aOrdinal
,
1733 WritingMode aWritingMode
,
1734 nsSubstring
& aResult
,
1737 bool success
= IsOrdinalInRange(aOrdinal
);
1741 // generate initial representation
1742 bool useNegativeSign
= UseNegativeSign();
1743 nsAutoString initialText
;
1744 CounterValue ordinal
;
1745 if (!useNegativeSign
) {
1748 CheckedInt
<CounterValue
> absolute(Abs(aOrdinal
));
1749 ordinal
= absolute
.isValid() ?
1750 absolute
.value() : std::numeric_limits
<CounterValue
>::max();
1752 success
= GetInitialCounterText(
1753 ordinal
, aWritingMode
, initialText
, aIsRTL
);
1755 // add pad & negative, build the final result
1759 // We have to calculate the difference here since suffix part of negative
1760 // sign may be appended to initialText later.
1761 int32_t diff
= pad
.width
- CountGraphemeClusters(initialText
);
1763 if (useNegativeSign
&& aOrdinal
< 0) {
1764 NegativeType negative
;
1765 GetNegative(negative
);
1766 aResult
.Append(negative
.before
);
1767 // There is nothing between the suffix part of negative and initial
1768 // representation, so we append it directly here.
1769 initialText
.Append(negative
.after
);
1772 auto length
= pad
.symbol
.Length();
1773 if (diff
> LENGTH_LIMIT
|| length
> LENGTH_LIMIT
||
1774 diff
* length
> LENGTH_LIMIT
) {
1776 } else if (length
> 0) {
1777 for (int32_t i
= 0; i
< diff
; ++i
) {
1778 aResult
.Append(pad
.symbol
);
1783 aResult
.Append(initialText
);
1789 CallFallbackStyle(aOrdinal
, aWritingMode
, aResult
, aIsRTL
);
1794 CounterStyle::GetSpokenCounterText(CounterValue aOrdinal
,
1795 WritingMode aWritingMode
,
1796 nsSubstring
& aResult
,
1799 bool isRTL
; // we don't care about direction for spoken text
1801 switch (GetSpeakAs()) {
1802 case NS_STYLE_COUNTER_SPEAKAS_BULLETS
:
1803 aResult
.Assign(kDiscCharacter
);
1806 case NS_STYLE_COUNTER_SPEAKAS_NUMBERS
:
1807 DecimalToText(aOrdinal
, aResult
);
1809 case NS_STYLE_COUNTER_SPEAKAS_SPELL_OUT
:
1810 // we currently do not actually support 'spell-out',
1811 // so 'words' is used instead.
1812 case NS_STYLE_COUNTER_SPEAKAS_WORDS
:
1813 GetCounterText(aOrdinal
, WritingMode(), aResult
, isRTL
);
1815 case NS_STYLE_COUNTER_SPEAKAS_OTHER
:
1816 // This should be processed by CustomCounterStyle
1817 NS_NOTREACHED("Invalid speak-as value");
1820 NS_NOTREACHED("Unknown speak-as value");
1825 static BuiltinCounterStyle gBuiltinStyleTable
[NS_STYLE_LIST_STYLE__MAX
];
1827 CounterStyleManager::CounterStyleManager(nsPresContext
* aPresContext
)
1828 : mPresContext(aPresContext
)
1830 // Insert the static styles into cache table
1831 mCacheTable
.Put(NS_LITERAL_STRING("none"), GetNoneStyle());
1832 mCacheTable
.Put(NS_LITERAL_STRING("decimal"), GetDecimalStyle());
1835 CounterStyleManager::~CounterStyleManager()
1837 NS_ABORT_IF_FALSE(!mPresContext
, "Disconnect should have been called");
1841 CounterStyleManager::InitializeBuiltinCounterStyles()
1843 for (uint32_t i
= 0; i
< NS_STYLE_LIST_STYLE__MAX
; ++i
) {
1844 gBuiltinStyleTable
[i
].mStyle
= i
;
1849 static PLDHashOperator
1850 CheckRefCount(const nsSubstring
& aKey
,
1851 CounterStyle
* aStyle
,
1855 auto refcnt
= aStyle
->Release();
1856 NS_ASSERTION(!aStyle
->IsDependentStyle() || refcnt
== 1,
1857 "Counter style is still referenced by other objects.");
1858 return PL_DHASH_NEXT
;
1863 CounterStyleManager::Disconnect()
1866 mCacheTable
.EnumerateRead(CheckRefCount
, nullptr);
1868 mCacheTable
.Clear();
1869 mPresContext
= nullptr;
1873 CounterStyleManager::BuildCounterStyle(const nsSubstring
& aName
)
1875 CounterStyle
* data
= mCacheTable
.GetWeak(aName
);
1880 // It is intentional that the predefined names are case-insensitive
1881 // but the user-defined names case-sensitive.
1882 nsCSSCounterStyleRule
* rule
=
1883 mPresContext
->StyleSet()->CounterStyleRuleForName(mPresContext
, aName
);
1885 data
= new (mPresContext
) CustomCounterStyle(this, rule
);
1888 nsCSSKeyword keyword
= nsCSSKeywords::LookupKeyword(aName
);
1889 if (nsCSSProps::FindKeyword(keyword
, nsCSSProps::kListStyleKTable
, type
)) {
1890 if (gBuiltinStyleTable
[type
].IsDependentStyle()) {
1891 data
= new (mPresContext
) DependentBuiltinCounterStyle(type
, this);
1893 data
= GetBuiltinStyle(type
);
1898 data
= GetDecimalStyle();
1900 mCacheTable
.Put(aName
, data
);
1904 /* static */ CounterStyle
*
1905 CounterStyleManager::GetBuiltinStyle(int32_t aStyle
)
1907 NS_ABORT_IF_FALSE(0 <= aStyle
&& aStyle
< NS_STYLE_LIST_STYLE__MAX
,
1908 "Require a valid builtin style constant");
1909 NS_ABORT_IF_FALSE(!gBuiltinStyleTable
[aStyle
].IsDependentStyle(),
1910 "Cannot get dependent builtin style");
1911 return &gBuiltinStyleTable
[aStyle
];
1914 struct InvalidateOldStyleData
1916 explicit InvalidateOldStyleData(nsPresContext
* aPresContext
)
1917 : mPresContext(aPresContext
),
1922 nsPresContext
* mPresContext
;
1923 nsTArray
<nsRefPtr
<CounterStyle
>> mToBeRemoved
;
1927 static PLDHashOperator
1928 InvalidateOldStyle(const nsSubstring
& aKey
,
1929 nsRefPtr
<CounterStyle
>& aStyle
,
1932 InvalidateOldStyleData
* data
= static_cast<InvalidateOldStyleData
*>(aArg
);
1933 bool toBeUpdated
= false;
1934 bool toBeRemoved
= false;
1935 nsCSSCounterStyleRule
* newRule
= data
->mPresContext
->
1936 StyleSet()->CounterStyleRuleForName(data
->mPresContext
, aKey
);
1938 if (aStyle
->IsCustomStyle()) {
1942 if (!aStyle
->IsCustomStyle()) {
1945 CustomCounterStyle
* style
=
1946 static_cast<CustomCounterStyle
*>(aStyle
.get());
1947 if (style
->GetRule() != newRule
) {
1949 } else if (style
->GetRuleGeneration() != newRule
->GetGeneration()) {
1951 style
->ResetCachedData();
1955 data
->mChanged
= data
->mChanged
|| toBeUpdated
|| toBeRemoved
;
1957 if (aStyle
->IsDependentStyle()) {
1958 if (aStyle
->IsCustomStyle()) {
1959 // Since |aStyle| is being removed from mCacheTable, it won't be visited
1960 // by our post-removal InvalidateDependentData() traversal. So, we have
1961 // to give it a manual ResetDependentData() call. (This only really
1962 // matters if something else is holding a reference & keeping it alive.)
1963 static_cast<CustomCounterStyle
*>(aStyle
.get())->ResetDependentData();
1965 // The object has to be held here so that it will not be released
1966 // before all pointers that refer to it are reset. It will be
1967 // released when the MarkAndCleanData goes out of scope at the end
1968 // of NotifyRuleChanged().
1969 data
->mToBeRemoved
.AppendElement(aStyle
);
1971 return PL_DHASH_REMOVE
;
1973 return PL_DHASH_NEXT
;
1976 static PLDHashOperator
1977 InvalidateDependentData(const nsSubstring
& aKey
,
1978 CounterStyle
* aStyle
,
1981 if (aStyle
->IsCustomStyle()) {
1982 CustomCounterStyle
* custom
= static_cast<CustomCounterStyle
*>(aStyle
);
1983 custom
->ResetDependentData();
1985 // There is no dependent data cached in DependentBuiltinCounterStyle
1986 // instances, so we don't need to reset their data.
1987 return PL_DHASH_NEXT
;
1991 CounterStyleManager::NotifyRuleChanged()
1993 InvalidateOldStyleData
data(mPresContext
);
1994 mCacheTable
.Enumerate(InvalidateOldStyle
, &data
);
1995 if (data
.mChanged
) {
1996 mCacheTable
.EnumerateRead(InvalidateDependentData
, nullptr);
1998 return data
.mChanged
;
2001 } // namespace mozilla