CounterStyleManager.cpp (62003B)
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 7 #include "CounterStyleManager.h" 8 9 #include <type_traits> 10 11 #include "mozilla/ArenaObjectID.h" 12 #include "mozilla/ArrayUtils.h" 13 #include "mozilla/CheckedInt.h" 14 #include "mozilla/MathAlgorithms.h" 15 #include "mozilla/PresShell.h" 16 #include "mozilla/ServoBindings.h" 17 #include "mozilla/ServoStyleSet.h" 18 #include "mozilla/WritingModes.h" 19 #include "nsPresContext.h" 20 #include "nsPresContextInlines.h" 21 #include "nsString.h" 22 #include "nsTArray.h" 23 #include "nsTHashtable.h" 24 #include "nsUnicodeProperties.h" 25 26 namespace mozilla { 27 28 using AdditiveSymbol = StyleAdditiveSymbol; 29 30 struct NegativeType { 31 nsString before, after; 32 }; 33 34 struct PadType { 35 int32_t width; 36 nsString symbol; 37 }; 38 39 // This limitation will be applied to some systems, and pad descriptor. 40 // Any initial representation generated by symbolic or additive which is 41 // longer than this limitation will be dropped. If any pad is longer 42 // than this, the whole counter text will be dropped as well. 43 // The spec requires user agents to support at least 60 Unicode code- 44 // points for counter text. However, this constant only limits the 45 // length in 16-bit units. So it has to be at least 120, since code- 46 // points outside the BMP will need 2 16-bit units. 47 #define LENGTH_LIMIT 150 48 49 static void SymbolToString(const StyleSymbol& aSymbol, nsAString& aResult) { 50 if (aSymbol.IsIdent()) { 51 return aSymbol.AsIdent().AsAtom()->ToString(aResult); 52 } 53 MOZ_ASSERT(aSymbol.IsString()); 54 return CopyUTF8toUTF16(aSymbol.AsString().AsString(), aResult); 55 } 56 57 static size_t SymbolLength(const StyleSymbol& aSymbol) { 58 if (aSymbol.IsIdent()) { 59 return aSymbol.AsIdent().AsAtom()->GetLength(); 60 } 61 MOZ_ASSERT(aSymbol.IsString()); 62 return aSymbol.AsString().AsString().Length(); 63 } 64 65 static bool GetCyclicCounterText(CounterValue aOrdinal, nsAString& aResult, 66 Span<const StyleSymbol> aSymbols) { 67 MOZ_ASSERT(aSymbols.Length() >= 1, "No symbol available for cyclic counter."); 68 auto n = CounterValue(aSymbols.Length()); 69 CounterValue index = (aOrdinal - 1) % n; 70 SymbolToString(aSymbols[index >= 0 ? index : index + n], aResult); 71 return true; 72 } 73 74 static bool GetFixedCounterText(CounterValue aOrdinal, nsAString& aResult, 75 CounterValue aStart, 76 Span<const StyleSymbol> aSymbols) { 77 CounterValue index = aOrdinal - aStart; 78 if (index >= 0 && index < CounterValue(aSymbols.Length())) { 79 SymbolToString(aSymbols[index], aResult); 80 return true; 81 } 82 return false; 83 } 84 85 static bool GetSymbolicCounterText(CounterValue aOrdinal, nsAString& aResult, 86 Span<const StyleSymbol> aSymbols) { 87 MOZ_ASSERT(aSymbols.Length() >= 1, 88 "No symbol available for symbolic counter."); 89 MOZ_ASSERT(aOrdinal >= 0, "Invalid ordinal."); 90 if (aOrdinal == 0) { 91 return false; 92 } 93 94 aResult.Truncate(); 95 auto n = aSymbols.Length(); 96 const StyleSymbol& symbol = aSymbols[(aOrdinal - 1) % n]; 97 size_t len = (aOrdinal + n - 1) / n; 98 auto symbolLength = SymbolLength(symbol); 99 if (symbolLength > 0) { 100 if (len > LENGTH_LIMIT || symbolLength > LENGTH_LIMIT || 101 len * symbolLength > LENGTH_LIMIT) { 102 return false; 103 } 104 nsAutoString str; 105 SymbolToString(symbol, str); 106 for (size_t i = 0; i < len; ++i) { 107 aResult.Append(str); 108 } 109 } 110 return true; 111 } 112 113 static bool GetAlphabeticCounterText(CounterValue aOrdinal, nsAString& aResult, 114 Span<const StyleSymbol> aSymbols) { 115 MOZ_ASSERT(aSymbols.Length() >= 2, "Too few symbols for alphabetic counter."); 116 MOZ_ASSERT(aOrdinal >= 0, "Invalid ordinal."); 117 if (aOrdinal == 0) { 118 return false; 119 } 120 121 auto n = aSymbols.Length(); 122 // The precise length of this array should be 123 // ceil(log((double) aOrdinal / n * (n - 1) + 1) / log(n)). 124 // The max length is slightly smaller than which defined below. 125 AutoTArray<int32_t, std::numeric_limits<CounterValue>::digits> indexes; 126 while (aOrdinal > 0) { 127 --aOrdinal; 128 indexes.AppendElement(aOrdinal % n); 129 aOrdinal /= n; 130 } 131 132 aResult.Truncate(); 133 for (auto i = indexes.Length(); i > 0; --i) { 134 const auto& symbol = aSymbols[indexes[i - 1]]; 135 if (symbol.IsIdent()) { 136 aResult.Append(nsDependentAtomString(symbol.AsIdent().AsAtom())); 137 } else { 138 AppendUTF8toUTF16(symbol.AsString().AsString(), aResult); 139 } 140 } 141 return true; 142 } 143 144 static bool GetNumericCounterText(CounterValue aOrdinal, nsAString& aResult, 145 Span<const StyleSymbol> aSymbols) { 146 MOZ_ASSERT(aSymbols.Length() >= 2, "Too few symbols for numeric counter."); 147 MOZ_ASSERT(aOrdinal >= 0, "Invalid ordinal."); 148 149 if (aOrdinal == 0) { 150 SymbolToString(aSymbols[0], aResult); 151 return true; 152 } 153 154 auto n = aSymbols.Length(); 155 AutoTArray<int32_t, std::numeric_limits<CounterValue>::digits> indexes; 156 while (aOrdinal > 0) { 157 indexes.AppendElement(aOrdinal % n); 158 aOrdinal /= n; 159 } 160 161 aResult.Truncate(); 162 for (auto i = indexes.Length(); i > 0; --i) { 163 const auto& symbol = aSymbols[indexes[i - 1]]; 164 if (symbol.IsIdent()) { 165 aResult.Append(nsDependentAtomString(symbol.AsIdent().AsAtom())); 166 } else { 167 AppendUTF8toUTF16(symbol.AsString().AsString(), aResult); 168 } 169 } 170 return true; 171 } 172 173 static bool GetAdditiveCounterText(CounterValue aOrdinal, nsAString& aResult, 174 Span<const AdditiveSymbol> aSymbols) { 175 MOZ_ASSERT(aOrdinal >= 0, "Invalid ordinal."); 176 177 if (aOrdinal == 0) { 178 const AdditiveSymbol& last = aSymbols[aSymbols.Length() - 1]; 179 if (last.weight == 0) { 180 aResult = last.symbol; 181 return true; 182 } 183 return false; 184 } 185 186 aResult.Truncate(); 187 size_t length = 0; 188 for (size_t i = 0, iEnd = aSymbols.Length(); i < iEnd; ++i) { 189 const AdditiveSymbol& symbol = aSymbols[i]; 190 if (symbol.weight == 0) { 191 break; 192 } 193 CounterValue times = aOrdinal / symbol.weight; 194 if (times > 0) { 195 auto symbolLength = symbol.symbol.Length(); 196 if (symbolLength > 0) { 197 length += times * symbolLength; 198 if (times > LENGTH_LIMIT || symbolLength > LENGTH_LIMIT || 199 length > LENGTH_LIMIT) { 200 return false; 201 } 202 for (CounterValue j = 0; j < times; ++j) { 203 aResult.Append(symbol.symbol); 204 } 205 } 206 aOrdinal -= times * symbol.weight; 207 } 208 } 209 return aOrdinal == 0; 210 } 211 212 static bool DecimalToText(CounterValue aOrdinal, nsAString& aResult) { 213 aResult.AppendInt(aOrdinal); 214 return true; 215 } 216 217 // We know cjk-ideographic need 31 characters to display 99,999,999,999,999,999 218 // georgian needs 6 at most 219 // armenian needs 12 at most 220 // hebrew may need more... 221 222 #define NUM_BUF_SIZE 34 223 224 enum CJKIdeographicLang { CHINESE, KOREAN, JAPANESE }; 225 struct CJKIdeographicData { 226 char16_t digit[10]; 227 char16_t unit[3]; 228 char16_t unit10K[2]; 229 uint8_t lang; 230 bool informal; 231 }; 232 static const CJKIdeographicData gDataJapaneseInformal = { 233 {0x3007, 0x4e00, 0x4e8c, 0x4e09, 0x56db, 0x4e94, 0x516d, 0x4e03, 0x516b, 234 0x4e5d}, // digit 235 {0x5341, 0x767e, 0x5343}, // unit 236 {0x4e07, 0x5104}, // unit10K 237 JAPANESE, // lang 238 true // informal 239 }; 240 static const CJKIdeographicData gDataJapaneseFormal = { 241 {0x96f6, 0x58f1, 0x5f10, 0x53c2, 0x56db, 0x4f0d, 0x516d, 0x4e03, 0x516b, 242 0x4e5d}, // digit 243 {0x62fe, 0x767e, 0x9621}, // unit 244 {0x842c, 0x5104}, // unit10K 245 JAPANESE, // lang 246 false // informal 247 }; 248 static const CJKIdeographicData gDataKoreanHangulFormal = { 249 {0xc601, 0xc77c, 0xc774, 0xc0bc, 0xc0ac, 0xc624, 0xc721, 0xce60, 0xd314, 250 0xad6c}, // digit 251 {0xc2ed, 0xbc31, 0xcc9c}, // unit 252 {0xb9cc, 0xc5b5}, // unit10K 253 KOREAN, // lang 254 false // informal 255 }; 256 static const CJKIdeographicData gDataKoreanHanjaInformal = { 257 {0x96f6, 0x4e00, 0x4e8c, 0x4e09, 0x56db, 0x4e94, 0x516d, 0x4e03, 0x516b, 258 0x4e5d}, // digit 259 {0x5341, 0x767e, 0x5343}, // unit 260 {0x842c, 0x5104}, // unit10K 261 KOREAN, // lang 262 true // informal 263 }; 264 static const CJKIdeographicData gDataKoreanHanjaFormal = { 265 {0x96f6, 0x58f9, 0x8cb3, 0x53c3, 0x56db, 0x4e94, 0x516d, 0x4e03, 0x516b, 266 0x4e5d}, // digit 267 {0x62fe, 0x767e, 0x4edf}, // unit 268 {0x842c, 0x5104}, // unit10K 269 KOREAN, // lang 270 false // informal 271 }; 272 static const CJKIdeographicData gDataSimpChineseInformal = { 273 {0x96f6, 0x4e00, 0x4e8c, 0x4e09, 0x56db, 0x4e94, 0x516d, 0x4e03, 0x516b, 274 0x4e5d}, // digit 275 {0x5341, 0x767e, 0x5343}, // unit 276 {0x4e07, 0x4ebf}, // unit10K 277 CHINESE, // lang 278 true // informal 279 }; 280 static const CJKIdeographicData gDataSimpChineseFormal = { 281 {0x96f6, 0x58f9, 0x8d30, 0x53c1, 0x8086, 0x4f0d, 0x9646, 0x67d2, 0x634c, 282 0x7396}, // digit 283 {0x62fe, 0x4f70, 0x4edf}, // unit 284 {0x4e07, 0x4ebf}, // unit10K 285 CHINESE, // lang 286 false // informal 287 }; 288 static const CJKIdeographicData gDataTradChineseInformal = { 289 {0x96f6, 0x4e00, 0x4e8c, 0x4e09, 0x56db, 0x4e94, 0x516d, 0x4e03, 0x516b, 290 0x4e5d}, // digit 291 {0x5341, 0x767e, 0x5343}, // unit 292 {0x842c, 0x5104}, // unit10K 293 CHINESE, // lang 294 true // informal 295 }; 296 static const CJKIdeographicData gDataTradChineseFormal = { 297 {0x96f6, 0x58f9, 0x8cb3, 0x53c3, 0x8086, 0x4f0d, 0x9678, 0x67d2, 0x634c, 298 0x7396}, // digit 299 {0x62fe, 0x4f70, 0x4edf}, // unit 300 {0x842c, 0x5104}, // unit10K 301 CHINESE, // lang 302 false // informal 303 }; 304 305 static bool CJKIdeographicToText(CounterValue aOrdinal, nsAString& aResult, 306 const CJKIdeographicData& data) { 307 NS_ASSERTION(aOrdinal >= 0, "Only accept non-negative ordinal"); 308 char16_t buf[NUM_BUF_SIZE]; 309 int32_t idx = NUM_BUF_SIZE; 310 int32_t pos = 0; 311 bool needZero = (aOrdinal == 0); 312 int32_t unitidx = 0, unit10Kidx = 0; 313 do { 314 unitidx = pos % 4; 315 if (unitidx == 0) { 316 unit10Kidx = pos / 4; 317 } 318 auto cur = static_cast<std::make_unsigned_t<CounterValue>>(aOrdinal) % 10; 319 if (cur == 0) { 320 if (needZero) { 321 needZero = false; 322 buf[--idx] = data.digit[0]; 323 } 324 } else { 325 if (data.lang == CHINESE) { 326 needZero = true; 327 } 328 if (unit10Kidx != 0) { 329 if (data.lang == KOREAN && idx != NUM_BUF_SIZE) { 330 buf[--idx] = ' '; 331 } 332 buf[--idx] = data.unit10K[unit10Kidx - 1]; 333 } 334 if (unitidx != 0) { 335 buf[--idx] = data.unit[unitidx - 1]; 336 } 337 if (cur != 1) { 338 buf[--idx] = data.digit[cur]; 339 } else { 340 bool needOne = true; 341 if (data.informal) { 342 switch (data.lang) { 343 case CHINESE: 344 if (unitidx == 1 && 345 (aOrdinal == 1 || (pos > 4 && aOrdinal % 1000 == 1))) { 346 needOne = false; 347 } 348 break; 349 case JAPANESE: 350 if (unitidx > 0 && 351 (unitidx != 3 || (pos == 3 && aOrdinal == 1))) { 352 needOne = false; 353 } 354 break; 355 case KOREAN: 356 if (unitidx > 0 || (pos == 4 && (aOrdinal % 1000) == 1)) { 357 needOne = false; 358 } 359 break; 360 } 361 } 362 if (needOne) { 363 buf[--idx] = data.digit[1]; 364 } 365 } 366 unit10Kidx = 0; 367 } 368 aOrdinal /= 10; 369 pos++; 370 } while (aOrdinal > 0); 371 aResult.Assign(buf + idx, NUM_BUF_SIZE - idx); 372 return true; 373 } 374 375 #define HEBREW_GERESH 0x05F3 376 static const char16_t gHebrewDigit[22] = { 377 // 1 2 3 4 5 6 7 8 9 378 0x05D0, 0x05D1, 0x05D2, 0x05D3, 0x05D4, 0x05D5, 0x05D6, 0x05D7, 0x05D8, 379 // 10 20 30 40 50 60 70 80 90 380 0x05D9, 0x05DB, 0x05DC, 0x05DE, 0x05E0, 0x05E1, 0x05E2, 0x05E4, 0x05E6, 381 // 100 200 300 400 382 0x05E7, 0x05E8, 0x05E9, 0x05EA}; 383 384 static bool HebrewToText(CounterValue aOrdinal, nsAString& aResult) { 385 if (aOrdinal < 1 || aOrdinal > 999999) { 386 return false; 387 } 388 389 bool outputSep = false; 390 nsAutoString allText, thousandsGroup; 391 do { 392 thousandsGroup.Truncate(); 393 int32_t n3 = aOrdinal % 1000; 394 // Process digit for 100 - 900 395 for (int32_t n1 = 400; n1 > 0;) { 396 if (n3 >= n1) { 397 n3 -= n1; 398 thousandsGroup.Append(gHebrewDigit[(n1 / 100) - 1 + 18]); 399 } else { 400 n1 -= 100; 401 } // if 402 } // for 403 404 // Process digit for 10 - 90 405 int32_t n2; 406 if (n3 >= 10) { 407 // Special process for 15 and 16 408 if ((15 == n3) || (16 == n3)) { 409 // Special rule for religious reason... 410 // 15 is represented by 9 and 6, not 10 and 5 411 // 16 is represented by 9 and 7, not 10 and 6 412 n2 = 9; 413 thousandsGroup.Append(gHebrewDigit[n2 - 1]); 414 } else { 415 n2 = n3 - (n3 % 10); 416 thousandsGroup.Append(gHebrewDigit[(n2 / 10) - 1 + 9]); 417 } // if 418 n3 -= n2; 419 } // if 420 421 // Process digit for 1 - 9 422 if (n3 > 0) { 423 thousandsGroup.Append(gHebrewDigit[n3 - 1]); 424 } 425 if (outputSep) { 426 thousandsGroup.Append((char16_t)HEBREW_GERESH); 427 } 428 if (allText.IsEmpty()) { 429 allText = thousandsGroup; 430 } else { 431 allText = thousandsGroup + allText; 432 } 433 aOrdinal /= 1000; 434 outputSep = true; 435 } while (aOrdinal >= 1); 436 437 aResult = allText; 438 return true; 439 } 440 441 // Convert ordinal to Ethiopic numeric representation. 442 // The detail is available at http://www.ethiopic.org/Numerals/ 443 // The algorithm used here is based on the pseudo-code put up there by 444 // Daniel Yacob <yacob@geez.org>. 445 // Another reference is Unicode 3.0 standard section 11.1. 446 #define ETHIOPIC_ONE 0x1369 447 #define ETHIOPIC_TEN 0x1372 448 #define ETHIOPIC_HUNDRED 0x137B 449 #define ETHIOPIC_TEN_THOUSAND 0x137C 450 451 static bool EthiopicToText(CounterValue aOrdinal, nsAString& aResult) { 452 if (aOrdinal < 1) { 453 return false; 454 } 455 456 nsAutoString asciiNumberString; // decimal string representation of ordinal 457 DecimalToText(aOrdinal, asciiNumberString); 458 uint8_t asciiStringLength = asciiNumberString.Length(); 459 460 // If number length is odd, add a leading "0" 461 // the leading "0" preconditions the string to always have the 462 // leading tens place populated, this avoids a check within the loop. 463 // If we didn't add the leading "0", decrement asciiStringLength so 464 // it will be equivalent to a zero-based index in both cases. 465 if (asciiStringLength & 1) { 466 asciiNumberString.InsertLiteral(u"0", 0); 467 } else { 468 asciiStringLength--; 469 } 470 471 aResult.Truncate(); 472 // Iterate from the highest digits to lowest 473 // indexFromLeft indexes digits (0 = most significant) 474 // groupIndexFromRight indexes pairs of digits (0 = least significant) 475 for (uint8_t indexFromLeft = 0, groupIndexFromRight = asciiStringLength >> 1; 476 indexFromLeft <= asciiStringLength; 477 indexFromLeft += 2, groupIndexFromRight--) { 478 uint8_t tensValue = asciiNumberString.CharAt(indexFromLeft) & 0x0F; 479 uint8_t unitsValue = asciiNumberString.CharAt(indexFromLeft + 1) & 0x0F; 480 uint8_t groupValue = tensValue * 10 + unitsValue; 481 482 bool oddGroup = (groupIndexFromRight & 1); 483 484 // we want to clear ETHIOPIC_ONE when it is superfluous 485 if (aOrdinal > 1 && groupValue == 1 && // one without a leading ten 486 (oddGroup || 487 indexFromLeft == 0)) { // preceding (100) or leading the sequence 488 unitsValue = 0; 489 } 490 491 // put it all together... 492 if (tensValue) { 493 // map onto Ethiopic "tens": 494 aResult.Append((char16_t)(tensValue + ETHIOPIC_TEN - 1)); 495 } 496 if (unitsValue) { 497 // map onto Ethiopic "units": 498 aResult.Append((char16_t)(unitsValue + ETHIOPIC_ONE - 1)); 499 } 500 // Add a separator for all even groups except the last, 501 // and for odd groups with non-zero value. 502 if (oddGroup) { 503 if (groupValue) { 504 aResult.Append((char16_t)ETHIOPIC_HUNDRED); 505 } 506 } else { 507 if (groupIndexFromRight) { 508 aResult.Append((char16_t)ETHIOPIC_TEN_THOUSAND); 509 } 510 } 511 } 512 return true; 513 } 514 515 static SpeakAs GetDefaultSpeakAsForSystem(StyleCounterSystem aSystem) { 516 MOZ_ASSERT(aSystem != StyleCounterSystem::Extends, 517 "Extends system does not have static default speak-as"); 518 switch (aSystem) { 519 case StyleCounterSystem::Alphabetic: 520 return SpeakAs::Spellout; 521 case StyleCounterSystem::Cyclic: 522 return SpeakAs::Bullets; 523 default: 524 return SpeakAs::Numbers; 525 } 526 } 527 528 static bool SystemUsesNegativeSign(StyleCounterSystem aSystem) { 529 MOZ_ASSERT(aSystem != StyleCounterSystem::Extends, 530 "Cannot check this for extending style"); 531 switch (aSystem) { 532 case StyleCounterSystem::Symbolic: 533 case StyleCounterSystem::Alphabetic: 534 case StyleCounterSystem::Numeric: 535 case StyleCounterSystem::Additive: 536 return true; 537 default: 538 return false; 539 } 540 } 541 542 class BuiltinCounterStyle : public CounterStyle { 543 public: 544 constexpr BuiltinCounterStyle(ListStyle aStyle, nsStaticAtom* aName) 545 : CounterStyle(aStyle), mName(aName) {} 546 547 nsStaticAtom* GetStyleName() const { return mName; } 548 549 void GetPrefix(nsAString& aResult) override; 550 void GetSuffix(nsAString& aResult) override; 551 void GetSpokenCounterText(CounterValue aOrdinal, WritingMode aWritingMode, 552 nsAString& aResult, bool& aIsBullet) override; 553 bool IsBullet() override; 554 555 void GetNegative(NegativeType& aResult) override; 556 bool IsOrdinalInRange(CounterValue aOrdinal) override; 557 bool IsOrdinalInAutoRange(CounterValue aOrdinal) override; 558 void GetPad(PadType& aResult) override; 559 CounterStyle* GetFallback() override; 560 SpeakAs GetSpeakAs() override; 561 bool UseNegativeSign() override; 562 563 bool GetInitialCounterText(CounterValue aOrdinal, WritingMode aWritingMode, 564 nsAString& aResult, bool& aIsRTL) override; 565 566 protected: 567 constexpr BuiltinCounterStyle(const BuiltinCounterStyle& aOther) 568 : CounterStyle(aOther.mStyle), mName(aOther.mName) {} 569 570 private: 571 nsStaticAtom* mName; 572 }; 573 574 /* virtual */ 575 void BuiltinCounterStyle::GetPrefix(nsAString& aResult) { aResult.Truncate(); } 576 577 /* virtual */ 578 void BuiltinCounterStyle::GetSuffix(nsAString& aResult) { 579 switch (mStyle) { 580 case ListStyle::None: 581 aResult.Truncate(); 582 break; 583 584 case ListStyle::Disc: 585 case ListStyle::Circle: 586 case ListStyle::Square: 587 case ListStyle::DisclosureClosed: 588 case ListStyle::DisclosureOpen: 589 case ListStyle::EthiopicNumeric: 590 aResult = ' '; 591 break; 592 593 case ListStyle::TradChineseInformal: 594 case ListStyle::TradChineseFormal: 595 case ListStyle::SimpChineseInformal: 596 case ListStyle::SimpChineseFormal: 597 case ListStyle::JapaneseInformal: 598 case ListStyle::JapaneseFormal: 599 aResult = 0x3001; 600 break; 601 602 case ListStyle::KoreanHangulFormal: 603 case ListStyle::KoreanHanjaInformal: 604 case ListStyle::KoreanHanjaFormal: 605 aResult.AssignLiteral(u", "); 606 break; 607 608 default: 609 aResult.AssignLiteral(u". "); 610 break; 611 } 612 } 613 614 static const char16_t kDiscCharacter = 0x2022; 615 static const char16_t kCircleCharacter = 0x25e6; 616 static const char16_t kSquareCharacter = 0x25aa; 617 static const char16_t kRightPointingCharacter = 0x25b8; 618 static const char16_t kLeftPointingCharacter = 0x25c2; 619 static const char16_t kDownPointingCharacter = 0x25be; 620 static const char16_t kUpPointingCharacter = 0x25b4; 621 622 /* virtual */ 623 void BuiltinCounterStyle::GetSpokenCounterText(CounterValue aOrdinal, 624 WritingMode aWritingMode, 625 nsAString& aResult, 626 bool& aIsBullet) { 627 switch (mStyle) { 628 case ListStyle::None: 629 case ListStyle::Disc: 630 case ListStyle::Circle: 631 case ListStyle::Square: 632 case ListStyle::DisclosureClosed: 633 case ListStyle::DisclosureOpen: { 634 // Same as the initial representation 635 bool isRTL; 636 GetInitialCounterText(aOrdinal, aWritingMode, aResult, isRTL); 637 aIsBullet = true; 638 break; 639 } 640 default: 641 CounterStyle::GetSpokenCounterText(aOrdinal, aWritingMode, aResult, 642 aIsBullet); 643 break; 644 } 645 } 646 647 /* virtual */ 648 bool BuiltinCounterStyle::IsBullet() { 649 switch (mStyle) { 650 case ListStyle::Disc: 651 case ListStyle::Circle: 652 case ListStyle::Square: 653 case ListStyle::DisclosureClosed: 654 case ListStyle::DisclosureOpen: 655 return true; 656 default: 657 return false; 658 } 659 } 660 661 static const char16_t gJapaneseNegative[] = {0x30de, 0x30a4, 0x30ca, 0x30b9, 662 0x0000}; 663 static const char16_t gKoreanNegative[] = {0xb9c8, 0xc774, 0xb108, 664 0xc2a4, 0x0020, 0x0000}; 665 static const char16_t gSimpChineseNegative[] = {0x8d1f, 0x0000}; 666 static const char16_t gTradChineseNegative[] = {0x8ca0, 0x0000}; 667 668 /* virtual */ 669 void BuiltinCounterStyle::GetNegative(NegativeType& aResult) { 670 switch (mStyle) { 671 case ListStyle::JapaneseFormal: 672 case ListStyle::JapaneseInformal: 673 aResult.before = gJapaneseNegative; 674 break; 675 676 case ListStyle::KoreanHangulFormal: 677 case ListStyle::KoreanHanjaInformal: 678 case ListStyle::KoreanHanjaFormal: 679 aResult.before = gKoreanNegative; 680 break; 681 682 case ListStyle::SimpChineseFormal: 683 case ListStyle::SimpChineseInformal: 684 aResult.before = gSimpChineseNegative; 685 break; 686 687 case ListStyle::TradChineseFormal: 688 case ListStyle::TradChineseInformal: 689 aResult.before = gTradChineseNegative; 690 break; 691 692 default: 693 aResult.before.AssignLiteral(u"-"); 694 } 695 aResult.after.Truncate(); 696 } 697 698 /* virtual */ 699 bool BuiltinCounterStyle::IsOrdinalInRange(CounterValue aOrdinal) { 700 switch (mStyle) { 701 default: 702 // cyclic 703 case ListStyle::None: 704 case ListStyle::Disc: 705 case ListStyle::Circle: 706 case ListStyle::Square: 707 case ListStyle::DisclosureClosed: 708 case ListStyle::DisclosureOpen: 709 // use DecimalToText 710 case ListStyle::Decimal: 711 // use CJKIdeographicToText 712 case ListStyle::JapaneseFormal: 713 case ListStyle::JapaneseInformal: 714 case ListStyle::KoreanHanjaFormal: 715 case ListStyle::KoreanHanjaInformal: 716 case ListStyle::KoreanHangulFormal: 717 case ListStyle::TradChineseFormal: 718 case ListStyle::TradChineseInformal: 719 case ListStyle::SimpChineseFormal: 720 case ListStyle::SimpChineseInformal: 721 return true; 722 723 // use EthiopicToText 724 case ListStyle::EthiopicNumeric: 725 return aOrdinal >= 1; 726 727 // use HebrewToText 728 case ListStyle::Hebrew: 729 return aOrdinal >= 1 && aOrdinal <= 999999; 730 } 731 } 732 733 /* virtual */ 734 bool BuiltinCounterStyle::IsOrdinalInAutoRange(CounterValue aOrdinal) { 735 switch (mStyle) { 736 // cyclic: 737 case ListStyle::None: 738 case ListStyle::Disc: 739 case ListStyle::Circle: 740 case ListStyle::Square: 741 case ListStyle::DisclosureClosed: 742 case ListStyle::DisclosureOpen: 743 // numeric: 744 case ListStyle::Decimal: 745 return true; 746 747 // additive: 748 case ListStyle::Hebrew: 749 return aOrdinal >= 0; 750 751 // complex predefined: 752 case ListStyle::JapaneseFormal: 753 case ListStyle::JapaneseInformal: 754 case ListStyle::KoreanHanjaFormal: 755 case ListStyle::KoreanHanjaInformal: 756 case ListStyle::KoreanHangulFormal: 757 case ListStyle::TradChineseFormal: 758 case ListStyle::TradChineseInformal: 759 case ListStyle::SimpChineseFormal: 760 case ListStyle::SimpChineseInformal: 761 case ListStyle::EthiopicNumeric: 762 return IsOrdinalInRange(aOrdinal); 763 764 default: 765 MOZ_ASSERT_UNREACHABLE("Unknown counter style"); 766 return false; 767 } 768 } 769 770 /* virtual */ 771 void BuiltinCounterStyle::GetPad(PadType& aResult) { 772 aResult.width = 0; 773 aResult.symbol.Truncate(); 774 } 775 776 /* virtual */ 777 CounterStyle* BuiltinCounterStyle::GetFallback() { 778 // Fallback of dependent builtin counter styles are handled in class 779 // DependentBuiltinCounterStyle. 780 return CounterStyleManager::GetDecimalStyle(); 781 } 782 783 /* virtual */ 784 SpeakAs BuiltinCounterStyle::GetSpeakAs() { 785 switch (mStyle) { 786 case ListStyle::None: 787 case ListStyle::Disc: 788 case ListStyle::Circle: 789 case ListStyle::Square: 790 case ListStyle::DisclosureClosed: 791 case ListStyle::DisclosureOpen: 792 return SpeakAs::Bullets; 793 default: 794 return SpeakAs::Numbers; 795 } 796 } 797 798 /* virtual */ 799 bool BuiltinCounterStyle::UseNegativeSign() { 800 switch (mStyle) { 801 case ListStyle::None: 802 case ListStyle::Disc: 803 case ListStyle::Circle: 804 case ListStyle::Square: 805 case ListStyle::DisclosureClosed: 806 case ListStyle::DisclosureOpen: 807 return false; 808 default: 809 return true; 810 } 811 } 812 813 /* virtual */ 814 bool BuiltinCounterStyle::GetInitialCounterText(CounterValue aOrdinal, 815 WritingMode aWritingMode, 816 nsAString& aResult, 817 bool& aIsRTL) { 818 aIsRTL = false; 819 switch (mStyle) { 820 // used by counters & extends counter-style code only 821 // XXX We really need to do this the same way we do list bullets. 822 case ListStyle::None: 823 aResult.Truncate(); 824 return true; 825 case ListStyle::Disc: 826 aResult.Assign(kDiscCharacter); 827 return true; 828 case ListStyle::Circle: 829 aResult.Assign(kCircleCharacter); 830 return true; 831 case ListStyle::Square: 832 aResult.Assign(kSquareCharacter); 833 return true; 834 case ListStyle::DisclosureClosed: 835 if (aWritingMode.IsVertical()) { 836 if (aWritingMode.IsBidiLTR()) { 837 aResult.Assign(kDownPointingCharacter); 838 } else { 839 aResult.Assign(kUpPointingCharacter); 840 } 841 } else if (aWritingMode.IsBidiLTR()) { 842 aResult.Assign(kRightPointingCharacter); 843 } else { 844 aResult.Assign(kLeftPointingCharacter); 845 } 846 return true; 847 case ListStyle::DisclosureOpen: 848 if (!aWritingMode.IsVertical()) { 849 aResult.Assign(kDownPointingCharacter); 850 } else if (aWritingMode.IsVerticalLR()) { 851 aResult.Assign(kRightPointingCharacter); 852 } else { 853 aResult.Assign(kLeftPointingCharacter); 854 } 855 return true; 856 857 case ListStyle::Decimal: 858 return DecimalToText(aOrdinal, aResult); 859 860 case ListStyle::TradChineseInformal: 861 return CJKIdeographicToText(aOrdinal, aResult, gDataTradChineseInformal); 862 case ListStyle::TradChineseFormal: 863 return CJKIdeographicToText(aOrdinal, aResult, gDataTradChineseFormal); 864 case ListStyle::SimpChineseInformal: 865 return CJKIdeographicToText(aOrdinal, aResult, gDataSimpChineseInformal); 866 case ListStyle::SimpChineseFormal: 867 return CJKIdeographicToText(aOrdinal, aResult, gDataSimpChineseFormal); 868 case ListStyle::JapaneseInformal: 869 return CJKIdeographicToText(aOrdinal, aResult, gDataJapaneseInformal); 870 case ListStyle::JapaneseFormal: 871 return CJKIdeographicToText(aOrdinal, aResult, gDataJapaneseFormal); 872 case ListStyle::KoreanHangulFormal: 873 return CJKIdeographicToText(aOrdinal, aResult, gDataKoreanHangulFormal); 874 case ListStyle::KoreanHanjaInformal: 875 return CJKIdeographicToText(aOrdinal, aResult, gDataKoreanHanjaInformal); 876 case ListStyle::KoreanHanjaFormal: 877 return CJKIdeographicToText(aOrdinal, aResult, gDataKoreanHanjaFormal); 878 879 case ListStyle::Hebrew: 880 aIsRTL = true; 881 return HebrewToText(aOrdinal, aResult); 882 883 case ListStyle::EthiopicNumeric: 884 return EthiopicToText(aOrdinal, aResult); 885 886 default: 887 MOZ_ASSERT_UNREACHABLE("Unknown builtin counter style"); 888 return false; 889 } 890 } 891 892 static constexpr BuiltinCounterStyle gBuiltinStyleTable[] = { 893 #define BUILTIN_COUNTER_STYLE(value_, atom_) \ 894 {ListStyle::value_, nsGkAtoms::atom_}, 895 #include "BuiltinCounterStyleList.h" 896 #undef BUILTIN_COUNTER_STYLE 897 }; 898 899 #define BUILTIN_COUNTER_STYLE(value_, atom_) \ 900 static_assert( \ 901 gBuiltinStyleTable[static_cast<size_t>(ListStyle::value_)].GetStyle() == \ 902 ListStyle::value_, \ 903 "Builtin counter style " #atom_ " has unmatched index and value."); 904 #include "BuiltinCounterStyleList.h" 905 #undef BUILTIN_COUNTER_STYLE 906 907 class DependentBuiltinCounterStyle final : public BuiltinCounterStyle { 908 public: 909 DependentBuiltinCounterStyle(ListStyle aStyle, CounterStyleManager* aManager) 910 : BuiltinCounterStyle(gBuiltinStyleTable[static_cast<size_t>(aStyle)]), 911 mManager(aManager) { 912 NS_ASSERTION(IsDependentStyle(), "Not a dependent builtin style"); 913 MOZ_ASSERT(!IsCustomStyle(), "Not a builtin style"); 914 } 915 916 virtual CounterStyle* GetFallback() override; 917 918 void* operator new(size_t sz, nsPresContext* aPresContext) { 919 return aPresContext->PresShell()->AllocateByObjectID( 920 eArenaObjectID_DependentBuiltinCounterStyle, sz); 921 } 922 923 void Destroy() { 924 PresShell* presShell = mManager->PresContext()->PresShell(); 925 this->~DependentBuiltinCounterStyle(); 926 presShell->FreeByObjectID(eArenaObjectID_DependentBuiltinCounterStyle, 927 this); 928 } 929 930 private: 931 ~DependentBuiltinCounterStyle() = default; 932 933 CounterStyleManager* mManager; 934 }; 935 936 /* virtual */ 937 CounterStyle* DependentBuiltinCounterStyle::GetFallback() { 938 switch (GetStyle()) { 939 case ListStyle::JapaneseInformal: 940 case ListStyle::JapaneseFormal: 941 case ListStyle::KoreanHangulFormal: 942 case ListStyle::KoreanHanjaInformal: 943 case ListStyle::KoreanHanjaFormal: 944 case ListStyle::SimpChineseInformal: 945 case ListStyle::SimpChineseFormal: 946 case ListStyle::TradChineseInformal: 947 case ListStyle::TradChineseFormal: 948 // These styles all have a larger range than cjk-decimal, so the 949 // only case fallback is accessed is that they are extended. 950 // Since extending styles will cache the data themselves, we need 951 // not cache it here. 952 return mManager->ResolveCounterStyle(nsGkAtoms::cjk_decimal); 953 default: 954 MOZ_ASSERT_UNREACHABLE("Not a valid dependent builtin style"); 955 return BuiltinCounterStyle::GetFallback(); 956 } 957 } 958 959 class CustomCounterStyle final : public CounterStyle { 960 public: 961 CustomCounterStyle(CounterStyleManager* aManager, 962 const StyleLockedCounterStyleRule* aRule) 963 : CounterStyle(ListStyle::Custom), 964 mManager(aManager), 965 mRule(aRule), 966 mRuleGeneration(Servo_CounterStyleRule_GetGeneration(aRule)), 967 mSystem(Servo_CounterStyleRule_GetSystem(aRule)), 968 mFlags(0), 969 mFallback(nullptr), 970 mSpeakAsCounter(nullptr), 971 mExtends(nullptr), 972 mExtendsRoot(nullptr) {} 973 974 // This method will clear all cached data in the style and update the 975 // generation number of the rule. It should be called when the rule of 976 // this style is changed. 977 void ResetCachedData(); 978 979 // This method will reset all cached data which may depend on other 980 // counter style. It will reset all pointers to other counter styles. 981 // For counter style extends other, in addition, all fields will be 982 // reset to uninitialized state. This method should be called when any 983 // other counter style is added, removed, or changed. 984 void ResetDependentData(); 985 986 const StyleLockedCounterStyleRule* GetRule() const { return mRule; } 987 uint32_t GetRuleGeneration() const { return mRuleGeneration; } 988 989 void GetPrefix(nsAString& aResult) override; 990 void GetSuffix(nsAString& aResult) override; 991 void GetSpokenCounterText(CounterValue aOrdinal, WritingMode aWritingMode, 992 nsAString& aResult, bool& aIsBullet) override; 993 bool IsBullet() override; 994 995 void GetNegative(NegativeType& aResult) override; 996 bool IsOrdinalInRange(CounterValue aOrdinal) override; 997 bool IsOrdinalInAutoRange(CounterValue aOrdinal) override; 998 void GetPad(PadType& aResult) override; 999 CounterStyle* GetFallback() override; 1000 SpeakAs GetSpeakAs() override; 1001 bool UseNegativeSign() override; 1002 1003 void CallFallbackStyle(CounterValue aOrdinal, WritingMode aWritingMode, 1004 nsAString& aResult, bool& aIsRTL) override; 1005 bool GetInitialCounterText(CounterValue aOrdinal, WritingMode aWritingMode, 1006 nsAString& aResult, bool& aIsRTL) override; 1007 1008 bool IsExtendsSystem() { return mSystem == StyleCounterSystem::Extends; } 1009 1010 void* operator new(size_t sz, nsPresContext* aPresContext) { 1011 return aPresContext->PresShell()->AllocateByObjectID( 1012 eArenaObjectID_CustomCounterStyle, sz); 1013 } 1014 1015 void Destroy() { 1016 PresShell* presShell = mManager->PresContext()->PresShell(); 1017 this->~CustomCounterStyle(); 1018 presShell->FreeByObjectID(eArenaObjectID_CustomCounterStyle, this); 1019 } 1020 1021 private: 1022 ~CustomCounterStyle() = default; 1023 1024 Span<const StyleSymbol> GetSymbols(); 1025 Span<const AdditiveSymbol> GetAdditiveSymbols(); 1026 1027 // The speak-as values of counter styles may form a loop, and the 1028 // loops may have complex interaction with the loop formed by 1029 // extending. To solve this problem, the computation of speak-as is 1030 // divided into two phases: 1031 // 1. figure out the raw value, by ComputeRawSpeakAs, and 1032 // 2. eliminate loop, by ComputeSpeakAs. 1033 // See comments before the definitions of these methods for details. 1034 SpeakAs GetSpeakAsAutoValue(); 1035 void ComputeRawSpeakAs(SpeakAs& aSpeakAs, CounterStyle*& aSpeakAsCounter); 1036 CounterStyle* ComputeSpeakAs(); 1037 1038 CounterStyle* ComputeExtends(); 1039 CounterStyle* GetExtends(); 1040 CounterStyle* GetExtendsRoot(); 1041 1042 // CounterStyleManager should always overlive any CounterStyle as it 1043 // is owned by nsPresContext, and will be released after all nodes and 1044 // frames are released. 1045 CounterStyleManager* mManager; 1046 1047 RefPtr<const StyleLockedCounterStyleRule> mRule; 1048 uint32_t mRuleGeneration; 1049 1050 StyleCounterSystem mSystem; 1051 // GetSpeakAs will ensure that private member mSpeakAs is initialized before 1052 // used 1053 MOZ_INIT_OUTSIDE_CTOR SpeakAs mSpeakAs; 1054 1055 enum { 1056 // loop detection 1057 FLAG_EXTENDS_VISITED = 1 << 0, 1058 FLAG_EXTENDS_LOOP = 1 << 1, 1059 FLAG_SPEAKAS_VISITED = 1 << 2, 1060 FLAG_SPEAKAS_LOOP = 1 << 3, 1061 // field status 1062 FLAG_NEGATIVE_INITED = 1 << 4, 1063 FLAG_PREFIX_INITED = 1 << 5, 1064 FLAG_SUFFIX_INITED = 1 << 6, 1065 FLAG_PAD_INITED = 1 << 7, 1066 FLAG_SPEAKAS_INITED = 1 << 8, 1067 }; 1068 uint16_t mFlags; 1069 1070 // Fields below will be initialized when necessary. 1071 StyleOwnedSlice<AdditiveSymbol> mAdditiveSymbols; 1072 NegativeType mNegative; 1073 nsString mPrefix, mSuffix; 1074 PadType mPad; 1075 1076 // CounterStyleManager will guarantee that none of the pointers below 1077 // refers to a freed CounterStyle. There are two possible cases where 1078 // the manager will release its reference to a CounterStyle: 1. the 1079 // manager itself is released, 2. a rule is invalidated. In the first 1080 // case, all counter style are removed from the manager, and should 1081 // also have been dereferenced from other objects. All styles will be 1082 // released all together. In the second case, CounterStyleManager:: 1083 // NotifyRuleChanged will guarantee that all pointers will be reset 1084 // before any CounterStyle is released. 1085 1086 CounterStyle* mFallback; 1087 // This field refers to the last counter in a speak-as chain. 1088 // That counter must not speak as another counter. 1089 CounterStyle* mSpeakAsCounter; 1090 1091 CounterStyle* mExtends; 1092 // This field refers to the last counter in the extends chain. The 1093 // counter must be either a builtin style or a style whose system is 1094 // not 'extends'. 1095 CounterStyle* mExtendsRoot; 1096 }; 1097 1098 void CustomCounterStyle::ResetCachedData() { 1099 mAdditiveSymbols.Clear(); 1100 mFlags &= ~(FLAG_NEGATIVE_INITED | FLAG_PREFIX_INITED | FLAG_SUFFIX_INITED | 1101 FLAG_PAD_INITED | FLAG_SPEAKAS_INITED); 1102 mFallback = nullptr; 1103 mSpeakAsCounter = nullptr; 1104 mExtends = nullptr; 1105 mExtendsRoot = nullptr; 1106 mRuleGeneration = Servo_CounterStyleRule_GetGeneration(mRule); 1107 } 1108 1109 void CustomCounterStyle::ResetDependentData() { 1110 mFlags &= ~FLAG_SPEAKAS_INITED; 1111 mSpeakAsCounter = nullptr; 1112 mFallback = nullptr; 1113 mExtends = nullptr; 1114 mExtendsRoot = nullptr; 1115 if (IsExtendsSystem()) { 1116 mFlags &= ~(FLAG_NEGATIVE_INITED | FLAG_PREFIX_INITED | FLAG_SUFFIX_INITED | 1117 FLAG_PAD_INITED); 1118 } 1119 } 1120 1121 /* virtual */ 1122 void CustomCounterStyle::GetPrefix(nsAString& aResult) { 1123 if (!(mFlags & FLAG_PREFIX_INITED)) { 1124 mFlags |= FLAG_PREFIX_INITED; 1125 1126 if (!Servo_CounterStyleRule_GetPrefix(mRule, &mPrefix)) { 1127 if (IsExtendsSystem()) { 1128 GetExtends()->GetPrefix(mPrefix); 1129 } else { 1130 mPrefix.Truncate(); 1131 } 1132 } 1133 } 1134 aResult = mPrefix; 1135 } 1136 1137 /* virtual */ 1138 void CustomCounterStyle::GetSuffix(nsAString& aResult) { 1139 if (!(mFlags & FLAG_SUFFIX_INITED)) { 1140 mFlags |= FLAG_SUFFIX_INITED; 1141 1142 if (!Servo_CounterStyleRule_GetSuffix(mRule, &mSuffix)) { 1143 if (IsExtendsSystem()) { 1144 GetExtends()->GetSuffix(mSuffix); 1145 } else { 1146 mSuffix.AssignLiteral(u". "); 1147 } 1148 } 1149 } 1150 aResult = mSuffix; 1151 } 1152 1153 /* virtual */ 1154 void CustomCounterStyle::GetSpokenCounterText(CounterValue aOrdinal, 1155 WritingMode aWritingMode, 1156 nsAString& aResult, 1157 bool& aIsBullet) { 1158 if (GetSpeakAs() != SpeakAs::Other) { 1159 CounterStyle::GetSpokenCounterText(aOrdinal, aWritingMode, aResult, 1160 aIsBullet); 1161 } else { 1162 MOZ_ASSERT(mSpeakAsCounter, 1163 "mSpeakAsCounter should have been initialized."); 1164 mSpeakAsCounter->GetSpokenCounterText(aOrdinal, aWritingMode, aResult, 1165 aIsBullet); 1166 } 1167 } 1168 1169 /* virtual */ 1170 bool CustomCounterStyle::IsBullet() { 1171 switch (mSystem) { 1172 case StyleCounterSystem::Cyclic: 1173 // Only use ::-moz-list-bullet for cyclic system 1174 return true; 1175 case StyleCounterSystem::Extends: 1176 return GetExtendsRoot()->IsBullet(); 1177 default: 1178 return false; 1179 } 1180 } 1181 1182 /* virtual */ 1183 void CustomCounterStyle::GetNegative(NegativeType& aResult) { 1184 if (!(mFlags & FLAG_NEGATIVE_INITED)) { 1185 mFlags |= FLAG_NEGATIVE_INITED; 1186 if (!Servo_CounterStyleRule_GetNegative(mRule, &mNegative.before, 1187 &mNegative.after)) { 1188 if (IsExtendsSystem()) { 1189 GetExtends()->GetNegative(mNegative); 1190 } else { 1191 mNegative.before.AssignLiteral(u"-"); 1192 mNegative.after.Truncate(); 1193 } 1194 } 1195 } 1196 aResult = mNegative; 1197 } 1198 1199 /* virtual */ 1200 bool CustomCounterStyle::IsOrdinalInRange(CounterValue aOrdinal) { 1201 auto inRange = Servo_CounterStyleRule_IsInRange(mRule, aOrdinal); 1202 switch (inRange) { 1203 case StyleIsOrdinalInRange::InRange: 1204 return true; 1205 case StyleIsOrdinalInRange::NotInRange: 1206 return false; 1207 case StyleIsOrdinalInRange::NoOrdinalSpecified: 1208 if (IsExtendsSystem()) { 1209 return GetExtends()->IsOrdinalInRange(aOrdinal); 1210 } 1211 break; 1212 case StyleIsOrdinalInRange::Auto: 1213 break; 1214 default: 1215 MOZ_ASSERT_UNREACHABLE("Unkown result from IsInRange?"); 1216 } 1217 return IsOrdinalInAutoRange(aOrdinal); 1218 } 1219 1220 /* virtual */ 1221 bool CustomCounterStyle::IsOrdinalInAutoRange(CounterValue aOrdinal) { 1222 switch (mSystem) { 1223 case StyleCounterSystem::Cyclic: 1224 case StyleCounterSystem::Numeric: 1225 case StyleCounterSystem::Fixed: 1226 return true; 1227 case StyleCounterSystem::Alphabetic: 1228 case StyleCounterSystem::Symbolic: 1229 return aOrdinal >= 1; 1230 case StyleCounterSystem::Additive: 1231 return aOrdinal >= 0; 1232 case StyleCounterSystem::Extends: 1233 return GetExtendsRoot()->IsOrdinalInAutoRange(aOrdinal); 1234 default: 1235 MOZ_ASSERT_UNREACHABLE("Invalid system for computing auto value."); 1236 return false; 1237 } 1238 } 1239 1240 /* virtual */ 1241 void CustomCounterStyle::GetPad(PadType& aResult) { 1242 if (!(mFlags & FLAG_PAD_INITED)) { 1243 mFlags |= FLAG_PAD_INITED; 1244 if (!Servo_CounterStyleRule_GetPad(mRule, &mPad.width, &mPad.symbol)) { 1245 if (IsExtendsSystem()) { 1246 GetExtends()->GetPad(mPad); 1247 } else { 1248 mPad.width = 0; 1249 mPad.symbol.Truncate(); 1250 } 1251 } 1252 } 1253 aResult = mPad; 1254 } 1255 1256 /* virtual */ 1257 CounterStyle* CustomCounterStyle::GetFallback() { 1258 if (!mFallback) { 1259 mFallback = CounterStyleManager::GetDecimalStyle(); 1260 if (nsAtom* fallback = Servo_CounterStyleRule_GetFallback(mRule)) { 1261 mFallback = mManager->ResolveCounterStyle(fallback); 1262 } else if (IsExtendsSystem()) { 1263 mFallback = GetExtends()->GetFallback(); 1264 } 1265 } 1266 return mFallback; 1267 } 1268 1269 /* virtual */ 1270 SpeakAs CustomCounterStyle::GetSpeakAs() { 1271 if (!(mFlags & FLAG_SPEAKAS_INITED)) { 1272 ComputeSpeakAs(); 1273 } 1274 return mSpeakAs; 1275 } 1276 1277 /* virtual */ 1278 bool CustomCounterStyle::UseNegativeSign() { 1279 if (mSystem == StyleCounterSystem::Extends) { 1280 return GetExtendsRoot()->UseNegativeSign(); 1281 } 1282 return SystemUsesNegativeSign(mSystem); 1283 } 1284 1285 /* virtual */ 1286 void CustomCounterStyle::CallFallbackStyle(CounterValue aOrdinal, 1287 WritingMode aWritingMode, 1288 nsAString& aResult, bool& aIsRTL) { 1289 CounterStyle* fallback = GetFallback(); 1290 // If it recursively falls back to this counter style again, 1291 // it will then fallback to decimal to break the loop. 1292 mFallback = CounterStyleManager::GetDecimalStyle(); 1293 fallback->GetCounterText(aOrdinal, aWritingMode, aResult, aIsRTL); 1294 mFallback = fallback; 1295 } 1296 1297 /* virtual */ 1298 bool CustomCounterStyle::GetInitialCounterText(CounterValue aOrdinal, 1299 WritingMode aWritingMode, 1300 nsAString& aResult, 1301 bool& aIsRTL) { 1302 switch (mSystem) { 1303 case StyleCounterSystem::Cyclic: 1304 return GetCyclicCounterText(aOrdinal, aResult, GetSymbols()); 1305 case StyleCounterSystem::Fixed: { 1306 int32_t start = Servo_CounterStyleRule_GetFixedFirstValue(mRule); 1307 return GetFixedCounterText(aOrdinal, aResult, start, GetSymbols()); 1308 } 1309 case StyleCounterSystem::Symbolic: 1310 return GetSymbolicCounterText(aOrdinal, aResult, GetSymbols()); 1311 case StyleCounterSystem::Alphabetic: 1312 return GetAlphabeticCounterText(aOrdinal, aResult, GetSymbols()); 1313 case StyleCounterSystem::Numeric: 1314 return GetNumericCounterText(aOrdinal, aResult, GetSymbols()); 1315 case StyleCounterSystem::Additive: 1316 return GetAdditiveCounterText(aOrdinal, aResult, GetAdditiveSymbols()); 1317 case StyleCounterSystem::Extends: 1318 return GetExtendsRoot()->GetInitialCounterText(aOrdinal, aWritingMode, 1319 aResult, aIsRTL); 1320 default: 1321 MOZ_ASSERT_UNREACHABLE("Invalid system."); 1322 return false; 1323 } 1324 } 1325 1326 Span<const StyleSymbol> CustomCounterStyle::GetSymbols() { 1327 size_t count = 0; 1328 const StyleSymbol* ptr = Servo_CounterStyleRule_GetSymbols(mRule, &count); 1329 return Span(ptr, count); 1330 } 1331 1332 Span<const AdditiveSymbol> CustomCounterStyle::GetAdditiveSymbols() { 1333 if (mAdditiveSymbols.IsEmpty()) { 1334 Servo_CounterStyleRule_GetAdditiveSymbols(mRule, &mAdditiveSymbols); 1335 } 1336 return mAdditiveSymbols.AsSpan(); 1337 } 1338 1339 // This method is used to provide the computed value for 'auto'. 1340 SpeakAs CustomCounterStyle::GetSpeakAsAutoValue() { 1341 auto system = mSystem; 1342 if (IsExtendsSystem()) { 1343 CounterStyle* root = GetExtendsRoot(); 1344 if (!root->IsCustomStyle()) { 1345 // It is safe to call GetSpeakAs on non-custom style. 1346 return root->GetSpeakAs(); 1347 } 1348 system = static_cast<CustomCounterStyle*>(root)->mSystem; 1349 } 1350 return GetDefaultSpeakAsForSystem(system); 1351 } 1352 1353 // This method corresponds to the first stage of computation of the 1354 // value of speak-as. It will extract the value from the rule and 1355 // possibly recursively call itself on the extended style to figure 1356 // out the raw value. To keep things clear, this method is designed to 1357 // have no side effects (but functions it calls may still affect other 1358 // fields in the style.) 1359 void CustomCounterStyle::ComputeRawSpeakAs(SpeakAs& aSpeakAs, 1360 CounterStyle*& aSpeakAsCounter) { 1361 NS_ASSERTION(!(mFlags & FLAG_SPEAKAS_INITED), 1362 "ComputeRawSpeakAs is called with speak-as inited."); 1363 1364 auto speakAs = StyleCounterSpeakAs::None(); 1365 Servo_CounterStyleRule_GetSpeakAs(mRule, &speakAs); 1366 switch (speakAs.tag) { 1367 case StyleCounterSpeakAs::Tag::Auto: 1368 aSpeakAs = GetSpeakAsAutoValue(); 1369 break; 1370 case StyleCounterSpeakAs::Tag::Bullets: 1371 aSpeakAs = SpeakAs::Bullets; 1372 break; 1373 case StyleCounterSpeakAs::Tag::Numbers: 1374 aSpeakAs = SpeakAs::Numbers; 1375 break; 1376 case StyleCounterSpeakAs::Tag::Words: 1377 aSpeakAs = SpeakAs::Words; 1378 break; 1379 case StyleCounterSpeakAs::Tag::Ident: 1380 aSpeakAs = SpeakAs::Other; 1381 aSpeakAsCounter = mManager->ResolveCounterStyle(speakAs.AsIdent()); 1382 break; 1383 case StyleCounterSpeakAs::Tag::None: { 1384 if (!IsExtendsSystem()) { 1385 aSpeakAs = GetSpeakAsAutoValue(); 1386 } else { 1387 CounterStyle* extended = GetExtends(); 1388 if (!extended->IsCustomStyle()) { 1389 // It is safe to call GetSpeakAs on non-custom style. 1390 aSpeakAs = extended->GetSpeakAs(); 1391 } else { 1392 CustomCounterStyle* custom = 1393 static_cast<CustomCounterStyle*>(extended); 1394 if (!(custom->mFlags & FLAG_SPEAKAS_INITED)) { 1395 custom->ComputeRawSpeakAs(aSpeakAs, aSpeakAsCounter); 1396 } else { 1397 aSpeakAs = custom->mSpeakAs; 1398 aSpeakAsCounter = custom->mSpeakAsCounter; 1399 } 1400 } 1401 } 1402 break; 1403 } 1404 default: 1405 MOZ_ASSERT_UNREACHABLE("Invalid speak-as value"); 1406 } 1407 } 1408 1409 // This method corresponds to the second stage of getting speak-as 1410 // related values. It will recursively figure out the final value of 1411 // mSpeakAs and mSpeakAsCounter. This method returns nullptr if the 1412 // caller is in a loop, and the root counter style in the chain 1413 // otherwise. It use the same loop detection algorithm as 1414 // CustomCounterStyle::ComputeExtends, see comments before that 1415 // method for more details. 1416 CounterStyle* CustomCounterStyle::ComputeSpeakAs() { 1417 if (mFlags & FLAG_SPEAKAS_INITED) { 1418 if (mSpeakAs == SpeakAs::Other) { 1419 return mSpeakAsCounter; 1420 } 1421 return this; 1422 } 1423 1424 if (mFlags & FLAG_SPEAKAS_VISITED) { 1425 // loop detected 1426 mFlags |= FLAG_SPEAKAS_LOOP; 1427 return nullptr; 1428 } 1429 1430 CounterStyle* speakAsCounter; 1431 ComputeRawSpeakAs(mSpeakAs, speakAsCounter); 1432 1433 bool inLoop = false; 1434 if (mSpeakAs != SpeakAs::Other) { 1435 mSpeakAsCounter = nullptr; 1436 } else if (!speakAsCounter->IsCustomStyle()) { 1437 mSpeakAsCounter = speakAsCounter; 1438 } else { 1439 mFlags |= FLAG_SPEAKAS_VISITED; 1440 CounterStyle* target = 1441 static_cast<CustomCounterStyle*>(speakAsCounter)->ComputeSpeakAs(); 1442 mFlags &= ~FLAG_SPEAKAS_VISITED; 1443 1444 if (target) { 1445 NS_ASSERTION(!(mFlags & FLAG_SPEAKAS_LOOP), 1446 "Invalid state for speak-as loop detecting"); 1447 mSpeakAsCounter = target; 1448 } else { 1449 mSpeakAs = GetSpeakAsAutoValue(); 1450 mSpeakAsCounter = nullptr; 1451 if (mFlags & FLAG_SPEAKAS_LOOP) { 1452 mFlags &= ~FLAG_SPEAKAS_LOOP; 1453 } else { 1454 inLoop = true; 1455 } 1456 } 1457 } 1458 1459 mFlags |= FLAG_SPEAKAS_INITED; 1460 if (inLoop) { 1461 return nullptr; 1462 } 1463 return mSpeakAsCounter ? mSpeakAsCounter : this; 1464 } 1465 1466 // This method will recursively figure out mExtends in the whole chain. 1467 // It will return nullptr if the caller is in a loop, and return this 1468 // otherwise. To detect the loop, this method marks the style VISITED 1469 // before the recursive call. When a VISITED style is reached again, the 1470 // loop is detected, and flag LOOP will be marked on the first style in 1471 // loop. mExtends of all counter styles in loop will be set to decimal 1472 // according to the spec. 1473 CounterStyle* CustomCounterStyle::ComputeExtends() { 1474 if (!IsExtendsSystem() || mExtends) { 1475 return this; 1476 } 1477 if (mFlags & FLAG_EXTENDS_VISITED) { 1478 // loop detected 1479 mFlags |= FLAG_EXTENDS_LOOP; 1480 return nullptr; 1481 } 1482 1483 nsAtom* extended = Servo_CounterStyleRule_GetExtended(mRule); 1484 CounterStyle* nextCounter = mManager->ResolveCounterStyle(extended); 1485 CounterStyle* target = nextCounter; 1486 if (nextCounter->IsCustomStyle()) { 1487 mFlags |= FLAG_EXTENDS_VISITED; 1488 target = static_cast<CustomCounterStyle*>(nextCounter)->ComputeExtends(); 1489 mFlags &= ~FLAG_EXTENDS_VISITED; 1490 } 1491 1492 if (target) { 1493 NS_ASSERTION(!(mFlags & FLAG_EXTENDS_LOOP), 1494 "Invalid state for extends loop detecting"); 1495 mExtends = nextCounter; 1496 return this; 1497 } else { 1498 mExtends = CounterStyleManager::GetDecimalStyle(); 1499 if (mFlags & FLAG_EXTENDS_LOOP) { 1500 mFlags &= ~FLAG_EXTENDS_LOOP; 1501 return this; 1502 } else { 1503 return nullptr; 1504 } 1505 } 1506 } 1507 1508 CounterStyle* CustomCounterStyle::GetExtends() { 1509 if (!mExtends) { 1510 // Any extends loop will be eliminated in the method below. 1511 ComputeExtends(); 1512 } 1513 return mExtends; 1514 } 1515 1516 CounterStyle* CustomCounterStyle::GetExtendsRoot() { 1517 if (!mExtendsRoot) { 1518 CounterStyle* extended = GetExtends(); 1519 mExtendsRoot = extended; 1520 if (extended->IsCustomStyle()) { 1521 CustomCounterStyle* custom = static_cast<CustomCounterStyle*>(extended); 1522 if (custom->IsExtendsSystem()) { 1523 // This will make mExtendsRoot in the whole extends chain be 1524 // set recursively, which could save work when part of a chain 1525 // is shared by multiple counter styles. 1526 mExtendsRoot = custom->GetExtendsRoot(); 1527 } 1528 } 1529 } 1530 return mExtendsRoot; 1531 } 1532 1533 AnonymousCounterStyle::AnonymousCounterStyle(StyleSymbolsType aType, 1534 Span<const StyleSymbol> aSymbols) 1535 : CounterStyle(ListStyle::Custom), 1536 mSymbolsType(aType), 1537 mSymbols(aSymbols) {} 1538 1539 /* virtual */ 1540 void AnonymousCounterStyle::GetPrefix(nsAString& aResult) { 1541 aResult.Truncate(); 1542 } 1543 1544 /* virtual */ 1545 void AnonymousCounterStyle::GetSuffix(nsAString& aResult) { aResult = ' '; } 1546 1547 /* virtual */ 1548 bool AnonymousCounterStyle::IsBullet() { 1549 // Only use ::-moz-list-bullet for cyclic system 1550 return mSymbolsType == StyleSymbolsType::Cyclic; 1551 } 1552 1553 /* virtual */ 1554 void AnonymousCounterStyle::GetNegative(NegativeType& aResult) { 1555 aResult.before.AssignLiteral(u"-"); 1556 aResult.after.Truncate(); 1557 } 1558 1559 /* virtual */ 1560 bool AnonymousCounterStyle::IsOrdinalInRange(CounterValue aOrdinal) { 1561 switch (mSymbolsType) { 1562 case StyleSymbolsType::Cyclic: 1563 case StyleSymbolsType::Numeric: 1564 case StyleSymbolsType::Fixed: 1565 return true; 1566 case StyleSymbolsType::Alphabetic: 1567 case StyleSymbolsType::Symbolic: 1568 return aOrdinal >= 1; 1569 default: 1570 MOZ_ASSERT_UNREACHABLE("Invalid system."); 1571 return false; 1572 } 1573 } 1574 1575 /* virtual */ 1576 bool AnonymousCounterStyle::IsOrdinalInAutoRange(CounterValue aOrdinal) { 1577 return AnonymousCounterStyle::IsOrdinalInRange(aOrdinal); 1578 } 1579 1580 /* virtual */ 1581 void AnonymousCounterStyle::GetPad(PadType& aResult) { 1582 aResult.width = 0; 1583 aResult.symbol.Truncate(); 1584 } 1585 1586 /* virtual */ 1587 CounterStyle* AnonymousCounterStyle::GetFallback() { 1588 return CounterStyleManager::GetDecimalStyle(); 1589 } 1590 1591 StyleCounterSystem AnonymousCounterStyle::GetSystem() const { 1592 switch (mSymbolsType) { 1593 case StyleSymbolsType::Cyclic: 1594 return StyleCounterSystem::Cyclic; 1595 case StyleSymbolsType::Numeric: 1596 return StyleCounterSystem::Numeric; 1597 case StyleSymbolsType::Fixed: 1598 return StyleCounterSystem::Fixed; 1599 case StyleSymbolsType::Alphabetic: 1600 return StyleCounterSystem::Alphabetic; 1601 case StyleSymbolsType::Symbolic: 1602 return StyleCounterSystem::Symbolic; 1603 } 1604 MOZ_ASSERT_UNREACHABLE("Unknown symbols() type"); 1605 return StyleCounterSystem::Cyclic; 1606 } 1607 1608 /* virtual */ 1609 SpeakAs AnonymousCounterStyle::GetSpeakAs() { 1610 return GetDefaultSpeakAsForSystem(GetSystem()); 1611 } 1612 1613 /* virtual */ 1614 bool AnonymousCounterStyle::UseNegativeSign() { 1615 return SystemUsesNegativeSign(GetSystem()); 1616 } 1617 1618 /* virtual */ 1619 bool AnonymousCounterStyle::GetInitialCounterText(CounterValue aOrdinal, 1620 WritingMode aWritingMode, 1621 nsAString& aResult, 1622 bool& aIsRTL) { 1623 switch (mSymbolsType) { 1624 case StyleSymbolsType::Cyclic: 1625 return GetCyclicCounterText(aOrdinal, aResult, mSymbols); 1626 case StyleSymbolsType::Numeric: 1627 return GetNumericCounterText(aOrdinal, aResult, mSymbols); 1628 case StyleSymbolsType::Fixed: 1629 return GetFixedCounterText(aOrdinal, aResult, 1, mSymbols); 1630 case StyleSymbolsType::Alphabetic: 1631 return GetAlphabeticCounterText(aOrdinal, aResult, mSymbols); 1632 case StyleSymbolsType::Symbolic: 1633 return GetSymbolicCounterText(aOrdinal, aResult, mSymbols); 1634 } 1635 MOZ_ASSERT_UNREACHABLE("Invalid system."); 1636 return false; 1637 } 1638 1639 bool CounterStyle::IsDependentStyle() const { 1640 switch (mStyle) { 1641 // CustomCounterStyle 1642 case ListStyle::Custom: 1643 // DependentBuiltinCounterStyle 1644 case ListStyle::JapaneseInformal: 1645 case ListStyle::JapaneseFormal: 1646 case ListStyle::KoreanHangulFormal: 1647 case ListStyle::KoreanHanjaInformal: 1648 case ListStyle::KoreanHanjaFormal: 1649 case ListStyle::SimpChineseInformal: 1650 case ListStyle::SimpChineseFormal: 1651 case ListStyle::TradChineseInformal: 1652 case ListStyle::TradChineseFormal: 1653 return true; 1654 1655 // BuiltinCounterStyle 1656 default: 1657 return false; 1658 } 1659 } 1660 1661 void CounterStyle::GetCounterText(CounterValue aOrdinal, 1662 WritingMode aWritingMode, nsAString& aResult, 1663 bool& aIsRTL) { 1664 bool success = IsOrdinalInRange(aOrdinal); 1665 aIsRTL = false; 1666 1667 if (success) { 1668 // generate initial representation 1669 bool useNegativeSign = UseNegativeSign(); 1670 nsAutoString initialText; 1671 CounterValue ordinal; 1672 if (!useNegativeSign) { 1673 ordinal = aOrdinal; 1674 } else { 1675 CheckedInt<CounterValue> absolute(Abs(aOrdinal)); 1676 ordinal = absolute.isValid() ? absolute.value() 1677 : std::numeric_limits<CounterValue>::max(); 1678 } 1679 success = GetInitialCounterText(ordinal, aWritingMode, initialText, aIsRTL); 1680 1681 // add pad & negative, build the final result 1682 if (success) { 1683 aResult.Truncate(); 1684 if (useNegativeSign && aOrdinal < 0) { 1685 NegativeType negative; 1686 GetNegative(negative); 1687 aResult.Append(negative.before); 1688 // There is nothing between the suffix part of negative and initial 1689 // representation, so we append it directly here. 1690 initialText.Append(negative.after); 1691 } 1692 PadType pad; 1693 GetPad(pad); 1694 int32_t diff = 1695 pad.width - 1696 narrow_cast<int32_t>(unicode::CountGraphemeClusters(initialText) + 1697 unicode::CountGraphemeClusters(aResult)); 1698 if (diff > 0) { 1699 auto length = pad.symbol.Length(); 1700 if (diff > LENGTH_LIMIT || length > LENGTH_LIMIT || 1701 diff * length > LENGTH_LIMIT) { 1702 success = false; 1703 } else if (length > 0) { 1704 for (int32_t i = 0; i < diff; ++i) { 1705 aResult.Append(pad.symbol); 1706 } 1707 } 1708 } 1709 if (success) { 1710 aResult.Append(initialText); 1711 } 1712 } 1713 } 1714 1715 if (!success) { 1716 CallFallbackStyle(aOrdinal, aWritingMode, aResult, aIsRTL); 1717 } 1718 } 1719 1720 /* virtual */ 1721 void CounterStyle::GetSpokenCounterText(CounterValue aOrdinal, 1722 WritingMode aWritingMode, 1723 nsAString& aResult, bool& aIsBullet) { 1724 bool isRTL; // we don't care about direction for spoken text 1725 aIsBullet = false; 1726 switch (GetSpeakAs()) { 1727 case SpeakAs::Bullets: 1728 aResult.Assign(kDiscCharacter); 1729 aIsBullet = true; 1730 break; 1731 case SpeakAs::Numbers: 1732 DecimalToText(aOrdinal, aResult); 1733 break; 1734 case SpeakAs::Spellout: 1735 // we currently do not actually support 'spell-out', 1736 // so 'words' is used instead. 1737 case SpeakAs::Words: 1738 GetCounterText(aOrdinal, WritingMode(), aResult, isRTL); 1739 break; 1740 case SpeakAs::Other: 1741 // This should be processed by CustomCounterStyle 1742 MOZ_ASSERT_UNREACHABLE("Invalid speak-as value"); 1743 break; 1744 default: 1745 MOZ_ASSERT_UNREACHABLE("Unknown speak-as value"); 1746 break; 1747 } 1748 } 1749 1750 /* virtual */ 1751 void CounterStyle::CallFallbackStyle(CounterValue aOrdinal, 1752 WritingMode aWritingMode, 1753 nsAString& aResult, bool& aIsRTL) { 1754 GetFallback()->GetCounterText(aOrdinal, aWritingMode, aResult, aIsRTL); 1755 } 1756 1757 CounterStyleManager::CounterStyleManager(nsPresContext* aPresContext) 1758 : mPresContext(aPresContext) { 1759 // Insert the static styles into cache table 1760 mStyles.InsertOrUpdate(nsGkAtoms::none, GetNoneStyle()); 1761 mStyles.InsertOrUpdate(nsGkAtoms::decimal, GetDecimalStyle()); 1762 mStyles.InsertOrUpdate(nsGkAtoms::disc, GetDiscStyle()); 1763 } 1764 1765 CounterStyleManager::~CounterStyleManager() { 1766 MOZ_ASSERT(!mPresContext, "Disconnect should have been called"); 1767 } 1768 1769 void CounterStyleManager::DestroyCounterStyle(CounterStyle* aCounterStyle) { 1770 if (aCounterStyle->IsCustomStyle()) { 1771 MOZ_ASSERT(!aCounterStyle->AsAnonymous(), 1772 "Anonymous counter styles " 1773 "are not managed by CounterStyleManager"); 1774 static_cast<CustomCounterStyle*>(aCounterStyle)->Destroy(); 1775 } else if (aCounterStyle->IsDependentStyle()) { 1776 static_cast<DependentBuiltinCounterStyle*>(aCounterStyle)->Destroy(); 1777 } else { 1778 MOZ_ASSERT_UNREACHABLE("Builtin counter styles should not be destroyed"); 1779 } 1780 } 1781 1782 void CounterStyleManager::Disconnect() { 1783 CleanRetiredStyles(); 1784 for (CounterStyle* style : mStyles.Values()) { 1785 if (style->IsDependentStyle()) { 1786 DestroyCounterStyle(style); 1787 } 1788 } 1789 mStyles.Clear(); 1790 mPresContext = nullptr; 1791 } 1792 1793 CounterStyle* CounterStyleManager::ResolveCounterStyle(nsAtom* aName) { 1794 MOZ_ASSERT(NS_IsMainThread()); 1795 CounterStyle* data = GetCounterStyle(aName); 1796 if (data) { 1797 return data; 1798 } 1799 1800 // Names are compared case-sensitively here. Predefined names should 1801 // have been lowercased by the parser. 1802 ServoStyleSet* styleSet = mPresContext->StyleSet(); 1803 auto* rule = styleSet->CounterStyleRuleForName(aName); 1804 if (rule) { 1805 MOZ_ASSERT(Servo_CounterStyleRule_GetName(rule) == aName); 1806 data = new (mPresContext) CustomCounterStyle(this, rule); 1807 } else { 1808 for (const BuiltinCounterStyle& item : gBuiltinStyleTable) { 1809 if (item.GetStyleName() == aName) { 1810 const auto style = item.GetStyle(); 1811 data = item.IsDependentStyle() 1812 ? new (mPresContext) 1813 DependentBuiltinCounterStyle(style, this) 1814 : GetBuiltinStyle(style); 1815 break; 1816 } 1817 } 1818 } 1819 if (!data) { 1820 data = GetDecimalStyle(); 1821 } 1822 mStyles.InsertOrUpdate(aName, data); 1823 return data; 1824 } 1825 1826 /* static */ 1827 CounterStyle* CounterStyleManager::GetBuiltinStyle(ListStyle aStyle) { 1828 MOZ_ASSERT(size_t(aStyle) < std::size(gBuiltinStyleTable), 1829 "Require a valid builtin style constant"); 1830 MOZ_ASSERT(!gBuiltinStyleTable[size_t(aStyle)].IsDependentStyle(), 1831 "Cannot get dependent builtin style"); 1832 // No method of BuiltinCounterStyle mutates the struct itself, so it 1833 // should be fine to cast const away. 1834 return const_cast<BuiltinCounterStyle*>(&gBuiltinStyleTable[size_t(aStyle)]); 1835 } 1836 1837 bool CounterStyleManager::NotifyRuleChanged() { 1838 bool changed = false; 1839 for (auto iter = mStyles.Iter(); !iter.Done(); iter.Next()) { 1840 CounterStyle* style = iter.Data(); 1841 bool toBeUpdated = false; 1842 bool toBeRemoved = false; 1843 ServoStyleSet* styleSet = mPresContext->StyleSet(); 1844 auto* newRule = styleSet->CounterStyleRuleForName(iter.Key()); 1845 if (!newRule) { 1846 if (style->IsCustomStyle()) { 1847 toBeRemoved = true; 1848 } 1849 } else { 1850 if (!style->IsCustomStyle()) { 1851 toBeRemoved = true; 1852 } else { 1853 auto custom = static_cast<CustomCounterStyle*>(style); 1854 if (custom->GetRule() != newRule) { 1855 toBeRemoved = true; 1856 } else { 1857 auto generation = Servo_CounterStyleRule_GetGeneration(newRule); 1858 if (custom->GetRuleGeneration() != generation) { 1859 toBeUpdated = true; 1860 custom->ResetCachedData(); 1861 } 1862 } 1863 } 1864 } 1865 changed = changed || toBeUpdated || toBeRemoved; 1866 if (toBeRemoved) { 1867 if (style->IsDependentStyle()) { 1868 // Add object to retired list so we can clean them up later. 1869 mRetiredStyles.AppendElement(style); 1870 } 1871 iter.Remove(); 1872 } 1873 } 1874 1875 if (changed) { 1876 for (CounterStyle* style : mStyles.Values()) { 1877 if (style->IsCustomStyle()) { 1878 CustomCounterStyle* custom = static_cast<CustomCounterStyle*>(style); 1879 custom->ResetDependentData(); 1880 } 1881 // There is no dependent data cached in DependentBuiltinCounterStyle 1882 // instances, so we don't need to reset their data. 1883 } 1884 } 1885 return changed; 1886 } 1887 1888 void CounterStyleManager::CleanRetiredStyles() { 1889 nsTArray<CounterStyle*> list(std::move(mRetiredStyles)); 1890 for (CounterStyle* style : list) { 1891 DestroyCounterStyle(style); 1892 } 1893 } 1894 1895 } // namespace mozilla