CounterStyleManager.cpp 65 KB

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