Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
1 /* -*- Mode: C++; tab-width: 2; 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/. */
6 /* rendering object for list-item bullets */
8 #include "nsBulletFrame.h"
10 #include "mozilla/MathAlgorithms.h"
11 #include "nsCOMPtr.h"
12 #include "nsGkAtoms.h"
13 #include "nsGenericHTMLElement.h"
14 #include "nsAttrValueInlines.h"
15 #include "nsPresContext.h"
16 #include "nsIPresShell.h"
17 #include "nsIDocument.h"
18 #include "nsRenderingContext.h"
19 #include "prprf.h"
20 #include "nsDisplayList.h"
21 #include "nsCounterManager.h"
23 #include "imgIContainer.h"
24 #include "imgRequestProxy.h"
25 #include "nsIURI.h"
27 #include <algorithm>
29 #ifdef ACCESSIBILITY
30 #include "nsAccessibilityService.h"
31 #endif
33 using namespace mozilla;
35 NS_DECLARE_FRAME_PROPERTY(FontSizeInflationProperty, nullptr)
37 NS_IMPL_FRAMEARENA_HELPERS(nsBulletFrame)
39 #ifdef DEBUG
40 NS_QUERYFRAME_HEAD(nsBulletFrame)
41 NS_QUERYFRAME_ENTRY(nsBulletFrame)
42 NS_QUERYFRAME_TAIL_INHERITING(nsFrame)
43 #endif
45 nsBulletFrame::~nsBulletFrame()
46 {
47 }
49 void
50 nsBulletFrame::DestroyFrom(nsIFrame* aDestructRoot)
51 {
52 // Stop image loading first
53 if (mImageRequest) {
54 // Deregister our image request from the refresh driver
55 nsLayoutUtils::DeregisterImageRequest(PresContext(),
56 mImageRequest,
57 &mRequestRegistered);
58 mImageRequest->CancelAndForgetObserver(NS_ERROR_FAILURE);
59 mImageRequest = nullptr;
60 }
62 if (mListener) {
63 mListener->SetFrame(nullptr);
64 }
66 // Let base class do the rest
67 nsFrame::DestroyFrom(aDestructRoot);
68 }
70 #ifdef DEBUG_FRAME_DUMP
71 nsresult
72 nsBulletFrame::GetFrameName(nsAString& aResult) const
73 {
74 return MakeFrameName(NS_LITERAL_STRING("Bullet"), aResult);
75 }
76 #endif
78 nsIAtom*
79 nsBulletFrame::GetType() const
80 {
81 return nsGkAtoms::bulletFrame;
82 }
84 bool
85 nsBulletFrame::IsEmpty()
86 {
87 return IsSelfEmpty();
88 }
90 bool
91 nsBulletFrame::IsSelfEmpty()
92 {
93 return StyleList()->mListStyleType == NS_STYLE_LIST_STYLE_NONE;
94 }
96 /* virtual */ void
97 nsBulletFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext)
98 {
99 nsFrame::DidSetStyleContext(aOldStyleContext);
101 imgRequestProxy *newRequest = StyleList()->GetListStyleImage();
103 if (newRequest) {
105 if (!mListener) {
106 mListener = new nsBulletListener();
107 mListener->SetFrame(this);
108 }
110 bool needNewRequest = true;
112 if (mImageRequest) {
113 // Reload the image, maybe...
114 nsCOMPtr<nsIURI> oldURI;
115 mImageRequest->GetURI(getter_AddRefs(oldURI));
116 nsCOMPtr<nsIURI> newURI;
117 newRequest->GetURI(getter_AddRefs(newURI));
118 if (oldURI && newURI) {
119 bool same;
120 newURI->Equals(oldURI, &same);
121 if (same) {
122 needNewRequest = false;
123 }
124 }
125 }
127 if (needNewRequest) {
128 nsRefPtr<imgRequestProxy> oldRequest = mImageRequest;
129 newRequest->Clone(mListener, getter_AddRefs(mImageRequest));
131 // Deregister the old request. We wait until after Clone is done in case
132 // the old request and the new request are the same underlying image
133 // accessed via different URLs.
134 if (oldRequest) {
135 nsLayoutUtils::DeregisterImageRequest(PresContext(), oldRequest,
136 &mRequestRegistered);
137 oldRequest->CancelAndForgetObserver(NS_ERROR_FAILURE);
138 oldRequest = nullptr;
139 }
141 // Register the new request.
142 if (mImageRequest) {
143 nsLayoutUtils::RegisterImageRequestIfAnimated(PresContext(),
144 mImageRequest,
145 &mRequestRegistered);
146 }
147 }
148 } else {
149 // No image request on the new style context
150 if (mImageRequest) {
151 nsLayoutUtils::DeregisterImageRequest(PresContext(), mImageRequest,
152 &mRequestRegistered);
154 mImageRequest->CancelAndForgetObserver(NS_ERROR_FAILURE);
155 mImageRequest = nullptr;
156 }
157 }
159 #ifdef ACCESSIBILITY
160 // Update the list bullet accessible. If old style list isn't available then
161 // no need to update the accessible tree because it's not created yet.
162 if (aOldStyleContext) {
163 nsAccessibilityService* accService = nsIPresShell::AccService();
164 if (accService) {
165 const nsStyleList* oldStyleList = aOldStyleContext->PeekStyleList();
166 if (oldStyleList) {
167 bool hadBullet = oldStyleList->GetListStyleImage() ||
168 oldStyleList->mListStyleType != NS_STYLE_LIST_STYLE_NONE;
170 const nsStyleList* newStyleList = StyleList();
171 bool hasBullet = newStyleList->GetListStyleImage() ||
172 newStyleList->mListStyleType != NS_STYLE_LIST_STYLE_NONE;
174 if (hadBullet != hasBullet) {
175 accService->UpdateListBullet(PresContext()->GetPresShell(), mContent,
176 hasBullet);
177 }
178 }
179 }
180 }
181 #endif
182 }
184 class nsDisplayBulletGeometry : public nsDisplayItemGenericGeometry
185 {
186 public:
187 nsDisplayBulletGeometry(nsDisplayItem* aItem, nsDisplayListBuilder* aBuilder)
188 : nsDisplayItemGenericGeometry(aItem, aBuilder)
189 {
190 nsBulletFrame* f = static_cast<nsBulletFrame*>(aItem->Frame());
191 mOrdinal = f->GetOrdinal();
192 }
194 int32_t mOrdinal;
195 };
197 class nsDisplayBullet : public nsDisplayItem {
198 public:
199 nsDisplayBullet(nsDisplayListBuilder* aBuilder, nsBulletFrame* aFrame) :
200 nsDisplayItem(aBuilder, aFrame) {
201 MOZ_COUNT_CTOR(nsDisplayBullet);
202 }
203 #ifdef NS_BUILD_REFCNT_LOGGING
204 virtual ~nsDisplayBullet() {
205 MOZ_COUNT_DTOR(nsDisplayBullet);
206 }
207 #endif
209 virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder,
210 bool* aSnap) MOZ_OVERRIDE
211 {
212 *aSnap = false;
213 return mFrame->GetVisualOverflowRectRelativeToSelf() + ToReferenceFrame();
214 }
215 virtual void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
216 HitTestState* aState,
217 nsTArray<nsIFrame*> *aOutFrames) MOZ_OVERRIDE {
218 aOutFrames->AppendElement(mFrame);
219 }
220 virtual void Paint(nsDisplayListBuilder* aBuilder,
221 nsRenderingContext* aCtx) MOZ_OVERRIDE;
222 NS_DISPLAY_DECL_NAME("Bullet", TYPE_BULLET)
224 virtual nsRect GetComponentAlphaBounds(nsDisplayListBuilder* aBuilder) MOZ_OVERRIDE
225 {
226 bool snap;
227 return GetBounds(aBuilder, &snap);
228 }
230 virtual nsDisplayItemGeometry* AllocateGeometry(nsDisplayListBuilder* aBuilder) MOZ_OVERRIDE
231 {
232 return new nsDisplayBulletGeometry(this, aBuilder);
233 }
235 virtual void ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
236 const nsDisplayItemGeometry* aGeometry,
237 nsRegion *aInvalidRegion) MOZ_OVERRIDE
238 {
239 const nsDisplayBulletGeometry* geometry = static_cast<const nsDisplayBulletGeometry*>(aGeometry);
240 nsBulletFrame* f = static_cast<nsBulletFrame*>(mFrame);
242 if (f->GetOrdinal() != geometry->mOrdinal) {
243 bool snap;
244 aInvalidRegion->Or(geometry->mBounds, GetBounds(aBuilder, &snap));
245 return;
246 }
248 nsCOMPtr<imgIContainer> image = f->GetImage();
249 if (aBuilder->ShouldSyncDecodeImages() && image && !image->IsDecoded()) {
250 // If we are going to do a sync decode and we are not decoded then we are
251 // going to be drawing something different from what is currently there,
252 // so we add our bounds to the invalid region.
253 bool snap;
254 aInvalidRegion->Or(*aInvalidRegion, GetBounds(aBuilder, &snap));
255 }
257 return nsDisplayItem::ComputeInvalidationRegion(aBuilder, aGeometry, aInvalidRegion);
258 }
259 };
261 void nsDisplayBullet::Paint(nsDisplayListBuilder* aBuilder,
262 nsRenderingContext* aCtx)
263 {
264 uint32_t flags = imgIContainer::FLAG_NONE;
265 if (aBuilder->ShouldSyncDecodeImages()) {
266 flags |= imgIContainer::FLAG_SYNC_DECODE;
267 }
268 static_cast<nsBulletFrame*>(mFrame)->
269 PaintBullet(*aCtx, ToReferenceFrame(), mVisibleRect, flags);
270 }
272 void
273 nsBulletFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
274 const nsRect& aDirtyRect,
275 const nsDisplayListSet& aLists)
276 {
277 if (!IsVisibleForPainting(aBuilder))
278 return;
280 DO_GLOBAL_REFLOW_COUNT_DSP("nsBulletFrame");
282 aLists.Content()->AppendNewToTop(
283 new (aBuilder) nsDisplayBullet(aBuilder, this));
284 }
286 void
287 nsBulletFrame::PaintBullet(nsRenderingContext& aRenderingContext, nsPoint aPt,
288 const nsRect& aDirtyRect, uint32_t aFlags)
289 {
290 const nsStyleList* myList = StyleList();
291 uint8_t listStyleType = myList->mListStyleType;
293 if (myList->GetListStyleImage() && mImageRequest) {
294 uint32_t status;
295 mImageRequest->GetImageStatus(&status);
296 if (status & imgIRequest::STATUS_LOAD_COMPLETE &&
297 !(status & imgIRequest::STATUS_ERROR)) {
298 nsCOMPtr<imgIContainer> imageCon;
299 mImageRequest->GetImage(getter_AddRefs(imageCon));
300 if (imageCon) {
301 nsRect dest(mPadding.left, mPadding.top,
302 mRect.width - (mPadding.left + mPadding.right),
303 mRect.height - (mPadding.top + mPadding.bottom));
304 nsLayoutUtils::DrawSingleImage(&aRenderingContext,
305 imageCon, nsLayoutUtils::GetGraphicsFilterForFrame(this),
306 dest + aPt, aDirtyRect, nullptr, aFlags);
307 return;
308 }
309 }
310 }
312 nsRefPtr<nsFontMetrics> fm;
313 aRenderingContext.SetColor(nsLayoutUtils::GetColor(this, eCSSProperty_color));
315 mTextIsRTL = false;
317 nsAutoString text;
318 switch (listStyleType) {
319 case NS_STYLE_LIST_STYLE_NONE:
320 break;
322 default:
323 case NS_STYLE_LIST_STYLE_DISC:
324 aRenderingContext.FillEllipse(mPadding.left + aPt.x, mPadding.top + aPt.y,
325 mRect.width - (mPadding.left + mPadding.right),
326 mRect.height - (mPadding.top + mPadding.bottom));
327 break;
329 case NS_STYLE_LIST_STYLE_CIRCLE:
330 aRenderingContext.DrawEllipse(mPadding.left + aPt.x, mPadding.top + aPt.y,
331 mRect.width - (mPadding.left + mPadding.right),
332 mRect.height - (mPadding.top + mPadding.bottom));
333 break;
335 case NS_STYLE_LIST_STYLE_SQUARE:
336 {
337 nsRect rect(aPt, mRect.Size());
338 rect.Deflate(mPadding);
340 // Snap the height and the width of the rectangle to device pixels,
341 // and then center the result within the original rectangle, so that
342 // all square bullets at the same font size have the same visual
343 // size (bug 376690).
344 // FIXME: We should really only do this if we're not transformed
345 // (like gfxContext::UserToDevicePixelSnapped does).
346 nsPresContext *pc = PresContext();
347 nsRect snapRect(rect.x, rect.y,
348 pc->RoundAppUnitsToNearestDevPixels(rect.width),
349 pc->RoundAppUnitsToNearestDevPixels(rect.height));
350 snapRect.MoveBy((rect.width - snapRect.width) / 2,
351 (rect.height - snapRect.height) / 2);
352 aRenderingContext.FillRect(snapRect.x, snapRect.y,
353 snapRect.width, snapRect.height);
354 }
355 break;
357 case NS_STYLE_LIST_STYLE_DECIMAL:
358 case NS_STYLE_LIST_STYLE_DECIMAL_LEADING_ZERO:
359 case NS_STYLE_LIST_STYLE_CJK_DECIMAL:
360 case NS_STYLE_LIST_STYLE_LOWER_ROMAN:
361 case NS_STYLE_LIST_STYLE_UPPER_ROMAN:
362 case NS_STYLE_LIST_STYLE_LOWER_ALPHA:
363 case NS_STYLE_LIST_STYLE_UPPER_ALPHA:
364 case NS_STYLE_LIST_STYLE_LOWER_GREEK:
365 case NS_STYLE_LIST_STYLE_HEBREW:
366 case NS_STYLE_LIST_STYLE_ARMENIAN:
367 case NS_STYLE_LIST_STYLE_GEORGIAN:
368 case NS_STYLE_LIST_STYLE_CJK_IDEOGRAPHIC:
369 case NS_STYLE_LIST_STYLE_HIRAGANA:
370 case NS_STYLE_LIST_STYLE_KATAKANA:
371 case NS_STYLE_LIST_STYLE_HIRAGANA_IROHA:
372 case NS_STYLE_LIST_STYLE_KATAKANA_IROHA:
373 case NS_STYLE_LIST_STYLE_JAPANESE_INFORMAL:
374 case NS_STYLE_LIST_STYLE_JAPANESE_FORMAL:
375 case NS_STYLE_LIST_STYLE_KOREAN_HANGUL_FORMAL:
376 case NS_STYLE_LIST_STYLE_KOREAN_HANJA_INFORMAL:
377 case NS_STYLE_LIST_STYLE_KOREAN_HANJA_FORMAL:
378 case NS_STYLE_LIST_STYLE_SIMP_CHINESE_INFORMAL:
379 case NS_STYLE_LIST_STYLE_SIMP_CHINESE_FORMAL:
380 case NS_STYLE_LIST_STYLE_TRAD_CHINESE_INFORMAL:
381 case NS_STYLE_LIST_STYLE_TRAD_CHINESE_FORMAL:
382 case NS_STYLE_LIST_STYLE_MOZ_SIMP_CHINESE_INFORMAL:
383 case NS_STYLE_LIST_STYLE_MOZ_SIMP_CHINESE_FORMAL:
384 case NS_STYLE_LIST_STYLE_MOZ_TRAD_CHINESE_INFORMAL:
385 case NS_STYLE_LIST_STYLE_MOZ_TRAD_CHINESE_FORMAL:
386 case NS_STYLE_LIST_STYLE_MOZ_JAPANESE_INFORMAL:
387 case NS_STYLE_LIST_STYLE_MOZ_JAPANESE_FORMAL:
388 case NS_STYLE_LIST_STYLE_MOZ_CJK_HEAVENLY_STEM:
389 case NS_STYLE_LIST_STYLE_MOZ_CJK_EARTHLY_BRANCH:
390 case NS_STYLE_LIST_STYLE_MOZ_ARABIC_INDIC:
391 case NS_STYLE_LIST_STYLE_MOZ_PERSIAN:
392 case NS_STYLE_LIST_STYLE_MOZ_URDU:
393 case NS_STYLE_LIST_STYLE_MOZ_DEVANAGARI:
394 case NS_STYLE_LIST_STYLE_MOZ_GURMUKHI:
395 case NS_STYLE_LIST_STYLE_MOZ_GUJARATI:
396 case NS_STYLE_LIST_STYLE_MOZ_ORIYA:
397 case NS_STYLE_LIST_STYLE_MOZ_KANNADA:
398 case NS_STYLE_LIST_STYLE_MOZ_MALAYALAM:
399 case NS_STYLE_LIST_STYLE_MOZ_BENGALI:
400 case NS_STYLE_LIST_STYLE_MOZ_TAMIL:
401 case NS_STYLE_LIST_STYLE_MOZ_TELUGU:
402 case NS_STYLE_LIST_STYLE_MOZ_THAI:
403 case NS_STYLE_LIST_STYLE_MOZ_LAO:
404 case NS_STYLE_LIST_STYLE_MOZ_MYANMAR:
405 case NS_STYLE_LIST_STYLE_MOZ_KHMER:
406 case NS_STYLE_LIST_STYLE_MOZ_HANGUL:
407 case NS_STYLE_LIST_STYLE_MOZ_HANGUL_CONSONANT:
408 case NS_STYLE_LIST_STYLE_MOZ_ETHIOPIC_HALEHAME:
409 case NS_STYLE_LIST_STYLE_MOZ_ETHIOPIC_NUMERIC:
410 case NS_STYLE_LIST_STYLE_MOZ_ETHIOPIC_HALEHAME_AM:
411 case NS_STYLE_LIST_STYLE_MOZ_ETHIOPIC_HALEHAME_TI_ER:
412 case NS_STYLE_LIST_STYLE_MOZ_ETHIOPIC_HALEHAME_TI_ET:
413 nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm),
414 GetFontSizeInflation());
415 GetListItemText(*myList, text);
416 aRenderingContext.SetFont(fm);
417 nscoord ascent = fm->MaxAscent();
418 aRenderingContext.SetTextRunRTL(mTextIsRTL);
419 aRenderingContext.DrawString(text, mPadding.left + aPt.x,
420 mPadding.top + aPt.y + ascent);
421 break;
422 }
423 }
425 int32_t
426 nsBulletFrame::SetListItemOrdinal(int32_t aNextOrdinal,
427 bool* aChanged,
428 int32_t aIncrement)
429 {
430 MOZ_ASSERT(aIncrement == 1 || aIncrement == -1,
431 "We shouldn't have weird increments here");
433 // Assume that the ordinal comes from the caller
434 int32_t oldOrdinal = mOrdinal;
435 mOrdinal = aNextOrdinal;
437 // Try to get value directly from the list-item, if it specifies a
438 // value attribute. Note: we do this with our parent's content
439 // because our parent is the list-item.
440 nsIContent* parentContent = mParent->GetContent();
441 if (parentContent) {
442 nsGenericHTMLElement *hc =
443 nsGenericHTMLElement::FromContent(parentContent);
444 if (hc) {
445 const nsAttrValue* attr = hc->GetParsedAttr(nsGkAtoms::value);
446 if (attr && attr->Type() == nsAttrValue::eInteger) {
447 // Use ordinal specified by the value attribute
448 mOrdinal = attr->GetIntegerValue();
449 }
450 }
451 }
453 *aChanged = oldOrdinal != mOrdinal;
455 return nsCounterManager::IncrementCounter(mOrdinal, aIncrement);
456 }
459 // XXX change roman/alpha to use unsigned math so that maxint and
460 // maxnegint will work
462 /**
463 * For all functions below, a return value of true means that we
464 * could represent mOrder in the desired numbering system. false
465 * means we had to fall back to decimal
466 */
467 static bool DecimalToText(int32_t ordinal, nsString& result)
468 {
469 char cbuf[40];
470 PR_snprintf(cbuf, sizeof(cbuf), "%ld", ordinal);
471 result.AppendASCII(cbuf);
472 return true;
473 }
474 static bool DecimalLeadingZeroToText(int32_t ordinal, nsString& result)
475 {
476 char cbuf[40];
477 PR_snprintf(cbuf, sizeof(cbuf), "%02ld", ordinal);
478 result.AppendASCII(cbuf);
479 return true;
480 }
481 static bool OtherDecimalToText(int32_t ordinal, char16_t zeroChar, nsString& result)
482 {
483 char16_t diff = zeroChar - char16_t('0');
484 // We're going to be appending to whatever is in "result" already, so make
485 // sure to only munge the new bits. Note that we can't just grab the pointer
486 // to the new stuff here, since appending to the string can realloc.
487 size_t offset = result.Length();
488 DecimalToText(ordinal, result);
489 char16_t* p = result.BeginWriting() + offset;
490 if (ordinal < 0) {
491 // skip the leading '-'
492 ++p;
493 }
494 for(; '\0' != *p ; p++)
495 *p += diff;
496 return true;
497 }
498 static bool TamilToText(int32_t ordinal, nsString& result)
499 {
500 if (ordinal < 1 || ordinal > 9999) {
501 // Can't do those in this system.
502 return false;
503 }
504 char16_t diff = 0x0BE6 - char16_t('0');
505 // We're going to be appending to whatever is in "result" already, so make
506 // sure to only munge the new bits. Note that we can't just grab the pointer
507 // to the new stuff here, since appending to the string can realloc.
508 size_t offset = result.Length();
509 DecimalToText(ordinal, result);
510 char16_t* p = result.BeginWriting() + offset;
511 for(; '\0' != *p ; p++)
512 if(*p != char16_t('0'))
513 *p += diff;
514 return true;
515 }
518 static const char gLowerRomanCharsA[] = "ixcm";
519 static const char gUpperRomanCharsA[] = "IXCM";
520 static const char gLowerRomanCharsB[] = "vld";
521 static const char gUpperRomanCharsB[] = "VLD";
523 static bool RomanToText(int32_t ordinal, nsString& result, const char* achars, const char* bchars)
524 {
525 if (ordinal < 1 || ordinal > 3999) {
526 return false;
527 }
528 nsAutoString addOn, decStr;
529 decStr.AppendInt(ordinal, 10);
530 int len = decStr.Length();
531 const char16_t* dp = decStr.get();
532 const char16_t* end = dp + len;
533 int romanPos = len;
534 int n;
536 for (; dp < end; dp++) {
537 romanPos--;
538 addOn.SetLength(0);
539 switch(*dp) {
540 case '3':
541 addOn.Append(char16_t(achars[romanPos]));
542 // FALLTHROUGH
543 case '2':
544 addOn.Append(char16_t(achars[romanPos]));
545 // FALLTHROUGH
546 case '1':
547 addOn.Append(char16_t(achars[romanPos]));
548 break;
549 case '4':
550 addOn.Append(char16_t(achars[romanPos]));
551 // FALLTHROUGH
552 case '5': case '6':
553 case '7': case '8':
554 addOn.Append(char16_t(bchars[romanPos]));
555 for(n=0;'5'+n<*dp;n++) {
556 addOn.Append(char16_t(achars[romanPos]));
557 }
558 break;
559 case '9':
560 addOn.Append(char16_t(achars[romanPos]));
561 addOn.Append(char16_t(achars[romanPos+1]));
562 break;
563 default:
564 break;
565 }
566 result.Append(addOn);
567 }
568 return true;
569 }
571 #define ALPHA_SIZE 26
572 static const char16_t gLowerAlphaChars[ALPHA_SIZE] =
573 {
574 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, // A B C D E
575 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, // F G H I J
576 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, // K L M N O
577 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, // P Q R S T
578 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, // U V W X Y
579 0x007A // Z
580 };
582 static const char16_t gUpperAlphaChars[ALPHA_SIZE] =
583 {
584 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, // A B C D E
585 0x0046, 0x0047, 0x0048, 0x0049, 0x004A, // F G H I J
586 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, // K L M N O
587 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, // P Q R S T
588 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, // U V W X Y
589 0x005A // Z
590 };
593 #define KATAKANA_CHARS_SIZE 48
594 // Page 94 Writing Systems of The World
595 // after modification by momoi
596 static const char16_t gKatakanaChars[KATAKANA_CHARS_SIZE] =
597 {
598 0x30A2, 0x30A4, 0x30A6, 0x30A8, 0x30AA, // a i u e o
599 0x30AB, 0x30AD, 0x30AF, 0x30B1, 0x30B3, // ka ki ku ke ko
600 0x30B5, 0x30B7, 0x30B9, 0x30BB, 0x30BD, // sa shi su se so
601 0x30BF, 0x30C1, 0x30C4, 0x30C6, 0x30C8, // ta chi tsu te to
602 0x30CA, 0x30CB, 0x30CC, 0x30CD, 0x30CE, // na ni nu ne no
603 0x30CF, 0x30D2, 0x30D5, 0x30D8, 0x30DB, // ha hi hu he ho
604 0x30DE, 0x30DF, 0x30E0, 0x30E1, 0x30E2, // ma mi mu me mo
605 0x30E4, 0x30E6, 0x30E8, // ya yu yo
606 0x30E9, 0x30EA, 0x30EB, 0x30EC, 0x30ED, // ra ri ru re ro
607 0x30EF, 0x30F0, 0x30F1, 0x30F2, // wa (w)i (w)e (w)o
608 0x30F3 // n
609 };
611 #define HIRAGANA_CHARS_SIZE 48
612 static const char16_t gHiraganaChars[HIRAGANA_CHARS_SIZE] =
613 {
614 0x3042, 0x3044, 0x3046, 0x3048, 0x304A, // a i u e o
615 0x304B, 0x304D, 0x304F, 0x3051, 0x3053, // ka ki ku ke ko
616 0x3055, 0x3057, 0x3059, 0x305B, 0x305D, // sa shi su se so
617 0x305F, 0x3061, 0x3064, 0x3066, 0x3068, // ta chi tsu te to
618 0x306A, 0x306B, 0x306C, 0x306D, 0x306E, // na ni nu ne no
619 0x306F, 0x3072, 0x3075, 0x3078, 0x307B, // ha hi hu he ho
620 0x307E, 0x307F, 0x3080, 0x3081, 0x3082, // ma mi mu me mo
621 0x3084, 0x3086, 0x3088, // ya yu yo
622 0x3089, 0x308A, 0x308B, 0x308C, 0x308D, // ra ri ru re ro
623 0x308F, 0x3090, 0x3091, 0x3092, // wa (w)i (w)e (w)o
624 0x3093 // n
625 };
628 #define HIRAGANA_IROHA_CHARS_SIZE 47
629 // Page 94 Writing Systems of The World
630 static const char16_t gHiraganaIrohaChars[HIRAGANA_IROHA_CHARS_SIZE] =
631 {
632 0x3044, 0x308D, 0x306F, 0x306B, 0x307B, // i ro ha ni ho
633 0x3078, 0x3068, 0x3061, 0x308A, 0x306C, // he to chi ri nu
634 0x308B, 0x3092, 0x308F, 0x304B, 0x3088, // ru (w)o wa ka yo
635 0x305F, 0x308C, 0x305D, 0x3064, 0x306D, // ta re so tsu ne
636 0x306A, 0x3089, 0x3080, 0x3046, 0x3090, // na ra mu u (w)i
637 0x306E, 0x304A, 0x304F, 0x3084, 0x307E, // no o ku ya ma
638 0x3051, 0x3075, 0x3053, 0x3048, 0x3066, // ke hu ko e te
639 0x3042, 0x3055, 0x304D, 0x3086, 0x3081, // a sa ki yu me
640 0x307F, 0x3057, 0x3091, 0x3072, 0x3082, // mi shi (w)e hi mo
641 0x305B, 0x3059 // se su
642 };
644 #define KATAKANA_IROHA_CHARS_SIZE 47
645 static const char16_t gKatakanaIrohaChars[KATAKANA_IROHA_CHARS_SIZE] =
646 {
647 0x30A4, 0x30ED, 0x30CF, 0x30CB, 0x30DB, // i ro ha ni ho
648 0x30D8, 0x30C8, 0x30C1, 0x30EA, 0x30CC, // he to chi ri nu
649 0x30EB, 0x30F2, 0x30EF, 0x30AB, 0x30E8, // ru (w)o wa ka yo
650 0x30BF, 0x30EC, 0x30BD, 0x30C4, 0x30CD, // ta re so tsu ne
651 0x30CA, 0x30E9, 0x30E0, 0x30A6, 0x30F0, // na ra mu u (w)i
652 0x30CE, 0x30AA, 0x30AF, 0x30E4, 0x30DE, // no o ku ya ma
653 0x30B1, 0x30D5, 0x30B3, 0x30A8, 0x30C6, // ke hu ko e te
654 0x30A2, 0x30B5, 0x30AD, 0x30E6, 0x30E1, // a sa ki yu me
655 0x30DF, 0x30B7, 0x30F1, 0x30D2, 0x30E2, // mi shi (w)e hi mo
656 0x30BB, 0x30B9 // se su
657 };
659 #define LOWER_GREEK_CHARS_SIZE 24
660 // Note: 0x03C2 GREEK FINAL SIGMA is not used in here....
661 static const char16_t gLowerGreekChars[LOWER_GREEK_CHARS_SIZE] =
662 {
663 0x03B1, 0x03B2, 0x03B3, 0x03B4, 0x03B5, // alpha beta gamma delta epsilon
664 0x03B6, 0x03B7, 0x03B8, 0x03B9, 0x03BA, // zeta eta theta iota kappa
665 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF, // lamda mu nu xi omicron
666 0x03C0, 0x03C1, 0x03C3, 0x03C4, 0x03C5, // pi rho sigma tau upsilon
667 0x03C6, 0x03C7, 0x03C8, 0x03C9 // phi chi psi omega
668 };
670 #define CJK_HEAVENLY_STEM_CHARS_SIZE 10
671 static const char16_t gCJKHeavenlyStemChars[CJK_HEAVENLY_STEM_CHARS_SIZE] =
672 {
673 0x7532, 0x4e59, 0x4e19, 0x4e01, 0x620a,
674 0x5df1, 0x5e9a, 0x8f9b, 0x58ec, 0x7678
675 };
676 #define CJK_EARTHLY_BRANCH_CHARS_SIZE 12
677 static const char16_t gCJKEarthlyBranchChars[CJK_EARTHLY_BRANCH_CHARS_SIZE] =
678 {
679 0x5b50, 0x4e11, 0x5bc5, 0x536f, 0x8fb0, 0x5df3,
680 0x5348, 0x672a, 0x7533, 0x9149, 0x620c, 0x4ea5
681 };
682 #define HANGUL_CHARS_SIZE 14
683 static const char16_t gHangulChars[HANGUL_CHARS_SIZE] =
684 {
685 0xac00, 0xb098, 0xb2e4, 0xb77c, 0xb9c8, 0xbc14,
686 0xc0ac, 0xc544, 0xc790, 0xcc28, 0xce74, 0xd0c0,
687 0xd30c, 0xd558
688 };
689 #define HANGUL_CONSONANT_CHARS_SIZE 14
690 static const char16_t gHangulConsonantChars[HANGUL_CONSONANT_CHARS_SIZE] =
691 {
692 0x3131, 0x3134, 0x3137, 0x3139, 0x3141, 0x3142,
693 0x3145, 0x3147, 0x3148, 0x314a, 0x314b, 0x314c,
694 0x314d, 0x314e
695 };
697 // Ge'ez set of Ethiopic ordered list. There are other locale-dependent sets.
698 // For the time being, let's implement two Ge'ez sets only
699 // per Momoi san's suggestion in bug 102252.
700 // For details, refer to http://www.ethiopic.org/Collation/OrderedLists.html.
701 #define ETHIOPIC_HALEHAME_CHARS_SIZE 26
702 static const char16_t gEthiopicHalehameChars[ETHIOPIC_HALEHAME_CHARS_SIZE] =
703 {
704 0x1200, 0x1208, 0x1210, 0x1218, 0x1220, 0x1228,
705 0x1230, 0x1240, 0x1260, 0x1270, 0x1280, 0x1290,
706 0x12a0, 0x12a8, 0x12c8, 0x12d0, 0x12d8, 0x12e8,
707 0x12f0, 0x1308, 0x1320, 0x1330, 0x1338, 0x1340,
708 0x1348, 0x1350
709 };
710 #define ETHIOPIC_HALEHAME_AM_CHARS_SIZE 33
711 static const char16_t gEthiopicHalehameAmChars[ETHIOPIC_HALEHAME_AM_CHARS_SIZE] =
712 {
713 0x1200, 0x1208, 0x1210, 0x1218, 0x1220, 0x1228,
714 0x1230, 0x1238, 0x1240, 0x1260, 0x1270, 0x1278,
715 0x1280, 0x1290, 0x1298, 0x12a0, 0x12a8, 0x12b8,
716 0x12c8, 0x12d0, 0x12d8, 0x12e0, 0x12e8, 0x12f0,
717 0x1300, 0x1308, 0x1320, 0x1328, 0x1330, 0x1338,
718 0x1340, 0x1348, 0x1350
719 };
720 #define ETHIOPIC_HALEHAME_TI_ER_CHARS_SIZE 31
721 static const char16_t gEthiopicHalehameTiErChars[ETHIOPIC_HALEHAME_TI_ER_CHARS_SIZE] =
722 {
723 0x1200, 0x1208, 0x1210, 0x1218, 0x1228, 0x1230,
724 0x1238, 0x1240, 0x1250, 0x1260, 0x1270, 0x1278,
725 0x1290, 0x1298, 0x12a0, 0x12a8, 0x12b8, 0x12c8,
726 0x12d0, 0x12d8, 0x12e0, 0x12e8, 0x12f0, 0x1300,
727 0x1308, 0x1320, 0x1328, 0x1330, 0x1338, 0x1348,
728 0x1350
729 };
730 #define ETHIOPIC_HALEHAME_TI_ET_CHARS_SIZE 34
731 static const char16_t gEthiopicHalehameTiEtChars[ETHIOPIC_HALEHAME_TI_ET_CHARS_SIZE] =
732 {
733 0x1200, 0x1208, 0x1210, 0x1218, 0x1220, 0x1228,
734 0x1230, 0x1238, 0x1240, 0x1250, 0x1260, 0x1270,
735 0x1278, 0x1280, 0x1290, 0x1298, 0x12a0, 0x12a8,
736 0x12b8, 0x12c8, 0x12d0, 0x12d8, 0x12e0, 0x12e8,
737 0x12f0, 0x1300, 0x1308, 0x1320, 0x1328, 0x1330,
738 0x1338, 0x1340, 0x1348, 0x1350
739 };
742 // We know cjk-ideographic need 31 characters to display 99,999,999,999,999,999
743 // georgian needs 6 at most
744 // armenian needs 12 at most
745 // hebrew may need more...
747 #define NUM_BUF_SIZE 34
749 static bool CharListToText(int32_t ordinal, nsString& result, const char16_t* chars, int32_t aBase)
750 {
751 char16_t buf[NUM_BUF_SIZE];
752 int32_t idx = NUM_BUF_SIZE;
753 if (ordinal < 1) {
754 return false;
755 }
756 do {
757 ordinal--; // a == 0
758 int32_t cur = ordinal % aBase;
759 buf[--idx] = chars[cur];
760 ordinal /= aBase ;
761 } while ( ordinal > 0);
762 result.Append(buf+idx,NUM_BUF_SIZE-idx);
763 return true;
764 }
766 static const char16_t gCJKDecimalChars[10] =
767 {
768 0x3007, 0x4e00, 0x4e8c, 0x4e09, 0x56db,
769 0x4e94, 0x516d, 0x4e03, 0x516b, 0x4e5d
770 };
771 static bool CharListDecimalToText(int32_t ordinal, nsString& result, const char16_t* chars)
772 {
773 if (ordinal < 0) {
774 return false;
775 }
776 char16_t buf[NUM_BUF_SIZE];
777 int32_t idx = NUM_BUF_SIZE;
778 do {
779 buf[--idx] = chars[ordinal % 10];
780 ordinal /= 10;
781 } while (ordinal > 0);
782 result.Append(buf + idx, NUM_BUF_SIZE - idx);
783 return true;
784 }
786 enum CJKIdeographicLang {
787 CHINESE, KOREAN, JAPANESE
788 };
789 struct CJKIdeographicData {
790 const char16_t *negative;
791 char16_t digit[10];
792 char16_t unit[3];
793 char16_t unit10K[2];
794 uint8_t lang;
795 bool informal;
796 };
797 static const char16_t gJapaneseNegative[] = {
798 0x30de, 0x30a4, 0x30ca, 0x30b9, 0x0000
799 };
800 static const CJKIdeographicData gDataJapaneseInformal = {
801 gJapaneseNegative, // negative
802 { // digit
803 0x3007, 0x4e00, 0x4e8c, 0x4e09, 0x56db,
804 0x4e94, 0x516d, 0x4e03, 0x516b, 0x4e5d
805 },
806 { 0x5341, 0x767e, 0x5343 }, // unit
807 { 0x4e07, 0x5104 }, // unit10K
808 JAPANESE, // lang
809 true // informal
810 };
811 static const CJKIdeographicData gDataJapaneseFormal = {
812 gJapaneseNegative, // negative
813 { // digit
814 0x96f6, 0x58f1, 0x5f10, 0x53c2, 0x56db,
815 0x4f0d, 0x516d, 0x4e03, 0x516b, 0x4e5d
816 },
817 { 0x62fe, 0x767e, 0x9621 }, // unit
818 { 0x842c, 0x5104 }, // unit10K
819 JAPANESE, // lang
820 false // informal
821 };
822 static const char16_t gKoreanNegative[] = {
823 0xb9c8, 0xc774, 0xb108, 0xc2a4, 0x0020, 0x0000
824 };
825 static const CJKIdeographicData gDataKoreanHangulFormal = {
826 gKoreanNegative, // negative
827 { // digit
828 0xc601, 0xc77c, 0xc774, 0xc0bc, 0xc0ac,
829 0xc624, 0xc721, 0xce60, 0xd314, 0xad6c
830 },
831 { 0xc2ed, 0xbc31, 0xcc9c }, // unit
832 { 0xb9cc, 0xc5b5 }, // unit10K
833 KOREAN, // lang
834 false // informal
835 };
836 static const CJKIdeographicData gDataKoreanHanjaInformal = {
837 gKoreanNegative, // negative
838 { // digit
839 0x96f6, 0x4e00, 0x4e8c, 0x4e09, 0x56db,
840 0x4e94, 0x516d, 0x4e03, 0x516b, 0x4e5d
841 },
842 { 0x5341, 0x767e, 0x5343 }, // unit
843 { 0x842c, 0x5104 }, // unit10K
844 KOREAN, // lang
845 true // informal
846 };
847 static const CJKIdeographicData gDataKoreanHanjaFormal = {
848 gKoreanNegative, // negative
849 { // digit
850 0x96f6, 0x58f9, 0x8cb3, 0x53c3, 0x56db,
851 0x4e94, 0x516d, 0x4e03, 0x516b, 0x4e5d
852 },
853 { 0x62fe, 0x767e, 0x4edf }, // unit
854 { 0x842c, 0x5104 }, // unit10K
855 KOREAN, // lang
856 false // informal
857 };
858 static const char16_t gSimpChineseNegative[] = {
859 0x8d1f, 0x0000
860 };
861 static const CJKIdeographicData gDataSimpChineseInformal = {
862 gSimpChineseNegative, // negative
863 { // digit
864 0x96f6, 0x4e00, 0x4e8c, 0x4e09, 0x56db,
865 0x4e94, 0x516d, 0x4e03, 0x516b, 0x4e5d
866 },
867 { 0x5341, 0x767e, 0x5343 }, // unit
868 { 0x4e07, 0x4ebf }, // unit10K
869 CHINESE, // lang
870 true // informal
871 };
872 static const CJKIdeographicData gDataSimpChineseFormal = {
873 gSimpChineseNegative, // negative
874 { // digit
875 0x96f6, 0x58f9, 0x8d30, 0x53c1, 0x8086,
876 0x4f0d, 0x9646, 0x67d2, 0x634c, 0x7396
877 },
878 { 0x62fe, 0x4f70, 0x4edf }, // unit
879 { 0x4e07, 0x4ebf }, // unit10K
880 CHINESE, // lang
881 false // informal
882 };
883 static const char16_t gTradChineseNegative[] = {
884 0x8ca0, 0x0000
885 };
886 static const CJKIdeographicData gDataTradChineseInformal = {
887 gTradChineseNegative, // negative
888 { // digit
889 0x96f6, 0x4e00, 0x4e8c, 0x4e09, 0x56db,
890 0x4e94, 0x516d, 0x4e03, 0x516b, 0x4e5d
891 },
892 { 0x5341, 0x767e, 0x5343 }, // unit
893 { 0x842c, 0x5104 }, // unit10K
894 CHINESE, // lang
895 true // informal
896 };
897 static const CJKIdeographicData gDataTradChineseFormal = {
898 gTradChineseNegative, // negative
899 { // digit
900 0x96f6, 0x58f9, 0x8cb3, 0x53c3, 0x8086,
901 0x4f0d, 0x9678, 0x67d2, 0x634c, 0x7396
902 },
903 { 0x62fe, 0x4f70, 0x4edf }, // unit
904 { 0x842c, 0x5104 }, // unit10K
905 CHINESE, // lang
906 false // informal
907 };
909 static const bool CJKIdeographicToText(int32_t aOrdinal, nsString& result,
910 const CJKIdeographicData& data)
911 {
912 char16_t buf[NUM_BUF_SIZE];
913 int32_t idx = NUM_BUF_SIZE;
914 int32_t pos = 0;
915 bool isNegative = (aOrdinal < 0);
916 bool needZero = (aOrdinal == 0);
917 int32_t unitidx = 0, unit10Kidx = 0;
918 uint32_t ordinal = mozilla::Abs(aOrdinal);
919 do {
920 unitidx = pos % 4;
921 if (unitidx == 0) {
922 unit10Kidx = pos / 4;
923 }
924 int32_t cur = ordinal % 10;
925 if (cur == 0) {
926 if (needZero) {
927 needZero = false;
928 buf[--idx] = data.digit[0];
929 }
930 } else {
931 if (data.lang == CHINESE) {
932 needZero = true;
933 }
934 if (unit10Kidx != 0) {
935 if (data.lang == KOREAN && idx != NUM_BUF_SIZE) {
936 buf[--idx] = ' ';
937 }
938 buf[--idx] = data.unit10K[unit10Kidx - 1];
939 }
940 if (unitidx != 0) {
941 buf[--idx] = data.unit[unitidx - 1];
942 }
943 if (cur != 1) {
944 buf[--idx] = data.digit[cur];
945 } else {
946 bool needOne = true;
947 if (data.informal) {
948 switch (data.lang) {
949 case CHINESE:
950 if (unitidx == 1 &&
951 (ordinal == 1 || (pos > 4 && ordinal % 1000 == 1))) {
952 needOne = false;
953 }
954 break;
955 case JAPANESE:
956 if (unitidx > 0 &&
957 (unitidx != 3 || (pos == 3 && ordinal == 1))) {
958 needOne = false;
959 }
960 break;
961 case KOREAN:
962 if (unitidx > 0 || (pos == 4 && (ordinal % 1000) == 1)) {
963 needOne = false;
964 }
965 break;
966 }
967 }
968 if (needOne) {
969 buf[--idx] = data.digit[1];
970 }
971 }
972 unit10Kidx = 0;
973 }
974 ordinal /= 10;
975 pos++;
976 } while (ordinal > 0);
977 if (isNegative) {
978 result.Append(data.negative);
979 }
980 result.Append(buf + idx, NUM_BUF_SIZE - idx);
981 return true;
982 }
984 #define HEBREW_GERESH 0x05F3
985 static const char16_t gHebrewDigit[22] =
986 {
987 // 1 2 3 4 5 6 7 8 9
988 0x05D0, 0x05D1, 0x05D2, 0x05D3, 0x05D4, 0x05D5, 0x05D6, 0x05D7, 0x05D8,
989 // 10 20 30 40 50 60 70 80 90
990 0x05D9, 0x05DB, 0x05DC, 0x05DE, 0x05E0, 0x05E1, 0x05E2, 0x05E4, 0x05E6,
991 // 100 200 300 400
992 0x05E7, 0x05E8, 0x05E9, 0x05EA
993 };
995 static bool HebrewToText(int32_t ordinal, nsString& result)
996 {
997 if (ordinal < 1 || ordinal > 999999) {
998 return false;
999 }
1000 bool outputSep = false;
1001 nsAutoString allText, thousandsGroup;
1002 do {
1003 thousandsGroup.Truncate();
1004 int32_t n3 = ordinal % 1000;
1005 // Process digit for 100 - 900
1006 for(int32_t n1 = 400; n1 > 0; )
1007 {
1008 if( n3 >= n1)
1009 {
1010 n3 -= n1;
1011 thousandsGroup.Append(gHebrewDigit[(n1/100)-1+18]);
1012 } else {
1013 n1 -= 100;
1014 } // if
1015 } // for
1017 // Process digit for 10 - 90
1018 int32_t n2;
1019 if( n3 >= 10 )
1020 {
1021 // Special process for 15 and 16
1022 if(( 15 == n3 ) || (16 == n3)) {
1023 // Special rule for religious reason...
1024 // 15 is represented by 9 and 6, not 10 and 5
1025 // 16 is represented by 9 and 7, not 10 and 6
1026 n2 = 9;
1027 thousandsGroup.Append(gHebrewDigit[ n2 - 1]);
1028 } else {
1029 n2 = n3 - (n3 % 10);
1030 thousandsGroup.Append(gHebrewDigit[(n2/10)-1+9]);
1031 } // if
1032 n3 -= n2;
1033 } // if
1035 // Process digit for 1 - 9
1036 if ( n3 > 0)
1037 thousandsGroup.Append(gHebrewDigit[n3-1]);
1038 if (outputSep)
1039 thousandsGroup.Append((char16_t)HEBREW_GERESH);
1040 if (allText.IsEmpty())
1041 allText = thousandsGroup;
1042 else
1043 allText = thousandsGroup + allText;
1044 ordinal /= 1000;
1045 outputSep = true;
1046 } while (ordinal >= 1);
1048 result.Append(allText);
1049 return true;
1050 }
1053 static bool ArmenianToText(int32_t ordinal, nsString& result)
1054 {
1055 if (ordinal < 1 || ordinal > 9999) { // zero or reach the limit of Armenian numbering system
1056 return false;
1057 }
1059 char16_t buf[NUM_BUF_SIZE];
1060 int32_t idx = NUM_BUF_SIZE;
1061 int32_t d = 0;
1062 do {
1063 int32_t cur = ordinal % 10;
1064 if (cur > 0)
1065 {
1066 char16_t u = 0x0530 + (d * 9) + cur;
1067 buf[--idx] = u;
1068 }
1069 ++d;
1070 ordinal /= 10;
1071 } while (ordinal > 0);
1072 result.Append(buf + idx, NUM_BUF_SIZE - idx);
1073 return true;
1074 }
1077 static const char16_t gGeorgianValue [ 37 ] = { // 4 * 9 + 1 = 37
1078 // 1 2 3 4 5 6 7 8 9
1079 0x10D0, 0x10D1, 0x10D2, 0x10D3, 0x10D4, 0x10D5, 0x10D6, 0x10F1, 0x10D7,
1080 // 10 20 30 40 50 60 70 80 90
1081 0x10D8, 0x10D9, 0x10DA, 0x10DB, 0x10DC, 0x10F2, 0x10DD, 0x10DE, 0x10DF,
1082 // 100 200 300 400 500 600 700 800 900
1083 0x10E0, 0x10E1, 0x10E2, 0x10F3, 0x10E4, 0x10E5, 0x10E6, 0x10E7, 0x10E8,
1084 // 1000 2000 3000 4000 5000 6000 7000 8000 9000
1085 0x10E9, 0x10EA, 0x10EB, 0x10EC, 0x10ED, 0x10EE, 0x10F4, 0x10EF, 0x10F0,
1086 // 10000
1087 0x10F5
1088 };
1089 static bool GeorgianToText(int32_t ordinal, nsString& result)
1090 {
1091 if (ordinal < 1 || ordinal > 19999) { // zero or reach the limit of Georgian numbering system
1092 return false;
1093 }
1095 char16_t buf[NUM_BUF_SIZE];
1096 int32_t idx = NUM_BUF_SIZE;
1097 int32_t d = 0;
1098 do {
1099 int32_t cur = ordinal % 10;
1100 if (cur > 0)
1101 {
1102 char16_t u = gGeorgianValue[(d * 9 ) + ( cur - 1)];
1103 buf[--idx] = u;
1104 }
1105 ++d;
1106 ordinal /= 10;
1107 } while (ordinal > 0);
1108 result.Append(buf + idx, NUM_BUF_SIZE - idx);
1109 return true;
1110 }
1112 // Convert ordinal to Ethiopic numeric representation.
1113 // The detail is available at http://www.ethiopic.org/Numerals/
1114 // The algorithm used here is based on the pseudo-code put up there by
1115 // Daniel Yacob <yacob@geez.org>.
1116 // Another reference is Unicode 3.0 standard section 11.1.
1117 #define ETHIOPIC_ONE 0x1369
1118 #define ETHIOPIC_TEN 0x1372
1119 #define ETHIOPIC_HUNDRED 0x137B
1120 #define ETHIOPIC_TEN_THOUSAND 0x137C
1122 static bool EthiopicToText(int32_t ordinal, nsString& result)
1123 {
1124 if (ordinal < 1) {
1125 return false;
1126 }
1127 nsAutoString asciiNumberString; // decimal string representation of ordinal
1128 DecimalToText(ordinal, asciiNumberString);
1129 uint8_t asciiStringLength = asciiNumberString.Length();
1131 // If number length is odd, add a leading "0"
1132 // the leading "0" preconditions the string to always have the
1133 // leading tens place populated, this avoids a check within the loop.
1134 // If we didn't add the leading "0", decrement asciiStringLength so
1135 // it will be equivalent to a zero-based index in both cases.
1136 if (asciiStringLength & 1) {
1137 asciiNumberString.Insert(NS_LITERAL_STRING("0"), 0);
1138 } else {
1139 asciiStringLength--;
1140 }
1142 // Iterate from the highest digits to lowest
1143 // indexFromLeft indexes digits (0 = most significant)
1144 // groupIndexFromRight indexes pairs of digits (0 = least significant)
1145 for (uint8_t indexFromLeft = 0, groupIndexFromRight = asciiStringLength >> 1;
1146 indexFromLeft <= asciiStringLength;
1147 indexFromLeft += 2, groupIndexFromRight--) {
1148 uint8_t tensValue = asciiNumberString.CharAt(indexFromLeft) & 0x0F;
1149 uint8_t unitsValue = asciiNumberString.CharAt(indexFromLeft + 1) & 0x0F;
1150 uint8_t groupValue = tensValue * 10 + unitsValue;
1152 bool oddGroup = (groupIndexFromRight & 1);
1154 // we want to clear ETHIOPIC_ONE when it is superfluous
1155 if (ordinal > 1 &&
1156 groupValue == 1 && // one without a leading ten
1157 (oddGroup || indexFromLeft == 0)) { // preceding (100) or leading the sequence
1158 unitsValue = 0;
1159 }
1161 // put it all together...
1162 if (tensValue) {
1163 // map onto Ethiopic "tens":
1164 result.Append((char16_t) (tensValue + ETHIOPIC_TEN - 1));
1165 }
1166 if (unitsValue) {
1167 //map onto Ethiopic "units":
1168 result.Append((char16_t) (unitsValue + ETHIOPIC_ONE - 1));
1169 }
1170 // Add a separator for all even groups except the last,
1171 // and for odd groups with non-zero value.
1172 if (oddGroup) {
1173 if (groupValue) {
1174 result.Append((char16_t) ETHIOPIC_HUNDRED);
1175 }
1176 } else {
1177 if (groupIndexFromRight) {
1178 result.Append((char16_t) ETHIOPIC_TEN_THOUSAND);
1179 }
1180 }
1181 }
1182 return true;
1183 }
1186 /* static */ void
1187 nsBulletFrame::AppendCounterText(int32_t aListStyleType,
1188 int32_t aOrdinal,
1189 nsString& result,
1190 bool& isRTL)
1191 {
1192 bool success = true;
1193 int32_t fallback = NS_STYLE_LIST_STYLE_DECIMAL;
1194 isRTL = false;
1196 switch (aListStyleType) {
1197 case NS_STYLE_LIST_STYLE_NONE: // used by counters code only
1198 break;
1200 case NS_STYLE_LIST_STYLE_DISC: // used by counters code only
1201 // XXX We really need to do this the same way we do list bullets.
1202 result.Append(char16_t(0x2022));
1203 break;
1205 case NS_STYLE_LIST_STYLE_CIRCLE: // used by counters code only
1206 // XXX We really need to do this the same way we do list bullets.
1207 result.Append(char16_t(0x25E6));
1208 break;
1210 case NS_STYLE_LIST_STYLE_SQUARE: // used by counters code only
1211 // XXX We really need to do this the same way we do list bullets.
1212 result.Append(char16_t(0x25FE));
1213 break;
1215 case NS_STYLE_LIST_STYLE_DECIMAL:
1216 default: // CSS2 say "A users agent that does not recognize a numbering system
1217 // should use 'decimal'
1218 success = DecimalToText(aOrdinal, result);
1219 NS_ASSERTION(success, "DecimalToText must never fail");
1220 break;
1222 case NS_STYLE_LIST_STYLE_DECIMAL_LEADING_ZERO:
1223 success = DecimalLeadingZeroToText(aOrdinal, result);
1224 break;
1226 case NS_STYLE_LIST_STYLE_CJK_DECIMAL:
1227 success = CharListDecimalToText(aOrdinal, result, gCJKDecimalChars);
1228 break;
1230 case NS_STYLE_LIST_STYLE_LOWER_ROMAN:
1231 success = RomanToText(aOrdinal, result,
1232 gLowerRomanCharsA, gLowerRomanCharsB);
1233 break;
1234 case NS_STYLE_LIST_STYLE_UPPER_ROMAN:
1235 success = RomanToText(aOrdinal, result,
1236 gUpperRomanCharsA, gUpperRomanCharsB);
1237 break;
1239 case NS_STYLE_LIST_STYLE_LOWER_ALPHA:
1240 success = CharListToText(aOrdinal, result, gLowerAlphaChars, ALPHA_SIZE);
1241 break;
1243 case NS_STYLE_LIST_STYLE_UPPER_ALPHA:
1244 success = CharListToText(aOrdinal, result, gUpperAlphaChars, ALPHA_SIZE);
1245 break;
1247 case NS_STYLE_LIST_STYLE_KATAKANA:
1248 success = CharListToText(aOrdinal, result, gKatakanaChars,
1249 KATAKANA_CHARS_SIZE);
1250 break;
1252 case NS_STYLE_LIST_STYLE_HIRAGANA:
1253 success = CharListToText(aOrdinal, result, gHiraganaChars,
1254 HIRAGANA_CHARS_SIZE);
1255 break;
1257 case NS_STYLE_LIST_STYLE_KATAKANA_IROHA:
1258 success = CharListToText(aOrdinal, result, gKatakanaIrohaChars,
1259 KATAKANA_IROHA_CHARS_SIZE);
1260 break;
1262 case NS_STYLE_LIST_STYLE_HIRAGANA_IROHA:
1263 success = CharListToText(aOrdinal, result, gHiraganaIrohaChars,
1264 HIRAGANA_IROHA_CHARS_SIZE);
1265 break;
1267 case NS_STYLE_LIST_STYLE_LOWER_GREEK:
1268 success = CharListToText(aOrdinal, result, gLowerGreekChars ,
1269 LOWER_GREEK_CHARS_SIZE);
1270 break;
1272 case NS_STYLE_LIST_STYLE_CJK_IDEOGRAPHIC:
1273 case NS_STYLE_LIST_STYLE_TRAD_CHINESE_INFORMAL:
1274 case NS_STYLE_LIST_STYLE_MOZ_TRAD_CHINESE_INFORMAL:
1275 fallback = NS_STYLE_LIST_STYLE_CJK_DECIMAL;
1276 success =
1277 CJKIdeographicToText(aOrdinal, result, gDataTradChineseInformal);
1278 break;
1280 case NS_STYLE_LIST_STYLE_TRAD_CHINESE_FORMAL:
1281 case NS_STYLE_LIST_STYLE_MOZ_TRAD_CHINESE_FORMAL:
1282 fallback = NS_STYLE_LIST_STYLE_CJK_DECIMAL;
1283 success = CJKIdeographicToText(aOrdinal, result, gDataTradChineseFormal);
1284 break;
1286 case NS_STYLE_LIST_STYLE_SIMP_CHINESE_INFORMAL:
1287 case NS_STYLE_LIST_STYLE_MOZ_SIMP_CHINESE_INFORMAL:
1288 fallback = NS_STYLE_LIST_STYLE_CJK_DECIMAL;
1289 success =
1290 CJKIdeographicToText(aOrdinal, result, gDataSimpChineseInformal);
1291 break;
1293 case NS_STYLE_LIST_STYLE_SIMP_CHINESE_FORMAL:
1294 case NS_STYLE_LIST_STYLE_MOZ_SIMP_CHINESE_FORMAL:
1295 fallback = NS_STYLE_LIST_STYLE_CJK_DECIMAL;
1296 success = CJKIdeographicToText(aOrdinal, result, gDataSimpChineseFormal);
1297 break;
1299 case NS_STYLE_LIST_STYLE_JAPANESE_INFORMAL:
1300 case NS_STYLE_LIST_STYLE_MOZ_JAPANESE_INFORMAL:
1301 fallback = NS_STYLE_LIST_STYLE_CJK_DECIMAL;
1302 success = CJKIdeographicToText(aOrdinal, result, gDataJapaneseInformal);
1303 break;
1305 case NS_STYLE_LIST_STYLE_JAPANESE_FORMAL:
1306 case NS_STYLE_LIST_STYLE_MOZ_JAPANESE_FORMAL:
1307 fallback = NS_STYLE_LIST_STYLE_CJK_DECIMAL;
1308 success = CJKIdeographicToText(aOrdinal, result, gDataJapaneseFormal);
1309 break;
1311 case NS_STYLE_LIST_STYLE_KOREAN_HANGUL_FORMAL:
1312 fallback = NS_STYLE_LIST_STYLE_CJK_DECIMAL;
1313 success =
1314 CJKIdeographicToText(aOrdinal, result, gDataKoreanHangulFormal);
1315 break;
1317 case NS_STYLE_LIST_STYLE_KOREAN_HANJA_INFORMAL:
1318 fallback = NS_STYLE_LIST_STYLE_CJK_DECIMAL;
1319 success =
1320 CJKIdeographicToText(aOrdinal, result, gDataKoreanHanjaInformal);
1321 break;
1323 case NS_STYLE_LIST_STYLE_KOREAN_HANJA_FORMAL:
1324 fallback = NS_STYLE_LIST_STYLE_CJK_DECIMAL;
1325 success = CJKIdeographicToText(aOrdinal, result, gDataKoreanHanjaFormal);
1326 break;
1328 case NS_STYLE_LIST_STYLE_HEBREW:
1329 isRTL = true;
1330 success = HebrewToText(aOrdinal, result);
1331 break;
1333 case NS_STYLE_LIST_STYLE_ARMENIAN:
1334 success = ArmenianToText(aOrdinal, result);
1335 break;
1337 case NS_STYLE_LIST_STYLE_GEORGIAN:
1338 success = GeorgianToText(aOrdinal, result);
1339 break;
1341 case NS_STYLE_LIST_STYLE_MOZ_ARABIC_INDIC:
1342 success = OtherDecimalToText(aOrdinal, 0x0660, result);
1343 break;
1345 case NS_STYLE_LIST_STYLE_MOZ_PERSIAN:
1346 case NS_STYLE_LIST_STYLE_MOZ_URDU:
1347 success = OtherDecimalToText(aOrdinal, 0x06f0, result);
1348 break;
1350 case NS_STYLE_LIST_STYLE_MOZ_DEVANAGARI:
1351 success = OtherDecimalToText(aOrdinal, 0x0966, result);
1352 break;
1354 case NS_STYLE_LIST_STYLE_MOZ_GURMUKHI:
1355 success = OtherDecimalToText(aOrdinal, 0x0a66, result);
1356 break;
1358 case NS_STYLE_LIST_STYLE_MOZ_GUJARATI:
1359 success = OtherDecimalToText(aOrdinal, 0x0AE6, result);
1360 break;
1362 case NS_STYLE_LIST_STYLE_MOZ_ORIYA:
1363 success = OtherDecimalToText(aOrdinal, 0x0B66, result);
1364 break;
1366 case NS_STYLE_LIST_STYLE_MOZ_KANNADA:
1367 success = OtherDecimalToText(aOrdinal, 0x0CE6, result);
1368 break;
1370 case NS_STYLE_LIST_STYLE_MOZ_MALAYALAM:
1371 success = OtherDecimalToText(aOrdinal, 0x0D66, result);
1372 break;
1374 case NS_STYLE_LIST_STYLE_MOZ_THAI:
1375 success = OtherDecimalToText(aOrdinal, 0x0E50, result);
1376 break;
1378 case NS_STYLE_LIST_STYLE_MOZ_LAO:
1379 success = OtherDecimalToText(aOrdinal, 0x0ED0, result);
1380 break;
1382 case NS_STYLE_LIST_STYLE_MOZ_MYANMAR:
1383 success = OtherDecimalToText(aOrdinal, 0x1040, result);
1384 break;
1386 case NS_STYLE_LIST_STYLE_MOZ_KHMER:
1387 success = OtherDecimalToText(aOrdinal, 0x17E0, result);
1388 break;
1390 case NS_STYLE_LIST_STYLE_MOZ_BENGALI:
1391 success = OtherDecimalToText(aOrdinal, 0x09E6, result);
1392 break;
1394 case NS_STYLE_LIST_STYLE_MOZ_TELUGU:
1395 success = OtherDecimalToText(aOrdinal, 0x0C66, result);
1396 break;
1398 case NS_STYLE_LIST_STYLE_MOZ_TAMIL:
1399 success = TamilToText(aOrdinal, result);
1400 break;
1402 case NS_STYLE_LIST_STYLE_MOZ_CJK_HEAVENLY_STEM:
1403 fallback = NS_STYLE_LIST_STYLE_CJK_DECIMAL;
1404 success = CharListToText(aOrdinal, result, gCJKHeavenlyStemChars,
1405 CJK_HEAVENLY_STEM_CHARS_SIZE);
1406 break;
1408 case NS_STYLE_LIST_STYLE_MOZ_CJK_EARTHLY_BRANCH:
1409 fallback = NS_STYLE_LIST_STYLE_CJK_DECIMAL;
1410 success = CharListToText(aOrdinal, result, gCJKEarthlyBranchChars,
1411 CJK_EARTHLY_BRANCH_CHARS_SIZE);
1412 break;
1414 case NS_STYLE_LIST_STYLE_MOZ_HANGUL:
1415 success = CharListToText(aOrdinal, result, gHangulChars, HANGUL_CHARS_SIZE);
1416 break;
1418 case NS_STYLE_LIST_STYLE_MOZ_HANGUL_CONSONANT:
1419 success = CharListToText(aOrdinal, result, gHangulConsonantChars,
1420 HANGUL_CONSONANT_CHARS_SIZE);
1421 break;
1423 case NS_STYLE_LIST_STYLE_MOZ_ETHIOPIC_HALEHAME:
1424 success = CharListToText(aOrdinal, result, gEthiopicHalehameChars,
1425 ETHIOPIC_HALEHAME_CHARS_SIZE);
1426 break;
1428 case NS_STYLE_LIST_STYLE_MOZ_ETHIOPIC_NUMERIC:
1429 success = EthiopicToText(aOrdinal, result);
1430 break;
1432 case NS_STYLE_LIST_STYLE_MOZ_ETHIOPIC_HALEHAME_AM:
1433 success = CharListToText(aOrdinal, result, gEthiopicHalehameAmChars,
1434 ETHIOPIC_HALEHAME_AM_CHARS_SIZE);
1435 break;
1437 case NS_STYLE_LIST_STYLE_MOZ_ETHIOPIC_HALEHAME_TI_ER:
1438 success = CharListToText(aOrdinal, result, gEthiopicHalehameTiErChars,
1439 ETHIOPIC_HALEHAME_TI_ER_CHARS_SIZE);
1440 break;
1442 case NS_STYLE_LIST_STYLE_MOZ_ETHIOPIC_HALEHAME_TI_ET:
1443 success = CharListToText(aOrdinal, result, gEthiopicHalehameTiEtChars,
1444 ETHIOPIC_HALEHAME_TI_ET_CHARS_SIZE);
1445 break;
1446 }
1447 if (!success) {
1448 AppendCounterText(fallback, aOrdinal, result, isRTL);
1449 }
1450 }
1452 /* static */ void
1453 nsBulletFrame::GetListItemSuffix(int32_t aListStyleType,
1454 nsString& aResult,
1455 bool& aSuppressPadding)
1456 {
1457 aResult = '.';
1458 aSuppressPadding = false;
1460 switch (aListStyleType) {
1461 case NS_STYLE_LIST_STYLE_NONE: // used by counters code only
1462 case NS_STYLE_LIST_STYLE_DISC: // used by counters code only
1463 case NS_STYLE_LIST_STYLE_CIRCLE: // used by counters code only
1464 case NS_STYLE_LIST_STYLE_SQUARE: // used by counters code only
1465 aResult.Truncate();
1466 break;
1468 case NS_STYLE_LIST_STYLE_CJK_DECIMAL:
1469 case NS_STYLE_LIST_STYLE_CJK_IDEOGRAPHIC:
1470 case NS_STYLE_LIST_STYLE_TRAD_CHINESE_INFORMAL:
1471 case NS_STYLE_LIST_STYLE_TRAD_CHINESE_FORMAL:
1472 case NS_STYLE_LIST_STYLE_SIMP_CHINESE_INFORMAL:
1473 case NS_STYLE_LIST_STYLE_SIMP_CHINESE_FORMAL:
1474 case NS_STYLE_LIST_STYLE_JAPANESE_INFORMAL:
1475 case NS_STYLE_LIST_STYLE_JAPANESE_FORMAL:
1476 case NS_STYLE_LIST_STYLE_MOZ_TRAD_CHINESE_INFORMAL:
1477 case NS_STYLE_LIST_STYLE_MOZ_TRAD_CHINESE_FORMAL:
1478 case NS_STYLE_LIST_STYLE_MOZ_SIMP_CHINESE_INFORMAL:
1479 case NS_STYLE_LIST_STYLE_MOZ_SIMP_CHINESE_FORMAL:
1480 case NS_STYLE_LIST_STYLE_MOZ_JAPANESE_INFORMAL:
1481 case NS_STYLE_LIST_STYLE_MOZ_JAPANESE_FORMAL:
1482 case NS_STYLE_LIST_STYLE_MOZ_CJK_HEAVENLY_STEM:
1483 case NS_STYLE_LIST_STYLE_MOZ_CJK_EARTHLY_BRANCH:
1484 aResult = 0x3001;
1485 aSuppressPadding = true;
1486 break;
1488 case NS_STYLE_LIST_STYLE_KOREAN_HANGUL_FORMAL:
1489 case NS_STYLE_LIST_STYLE_KOREAN_HANJA_INFORMAL:
1490 case NS_STYLE_LIST_STYLE_KOREAN_HANJA_FORMAL:
1491 case NS_STYLE_LIST_STYLE_MOZ_HANGUL:
1492 case NS_STYLE_LIST_STYLE_MOZ_HANGUL_CONSONANT:
1493 aResult = ',';
1494 break;
1495 }
1496 }
1498 void
1499 nsBulletFrame::GetListItemText(const nsStyleList& aListStyle,
1500 nsString& result)
1501 {
1502 const nsStyleVisibility* vis = StyleVisibility();
1504 NS_ASSERTION(aListStyle.mListStyleType != NS_STYLE_LIST_STYLE_NONE &&
1505 aListStyle.mListStyleType != NS_STYLE_LIST_STYLE_DISC &&
1506 aListStyle.mListStyleType != NS_STYLE_LIST_STYLE_CIRCLE &&
1507 aListStyle.mListStyleType != NS_STYLE_LIST_STYLE_SQUARE,
1508 "we should be using specialized code for these types");
1510 result.Truncate();
1511 AppendCounterText(aListStyle.mListStyleType, mOrdinal, result, mTextIsRTL);
1513 nsAutoString suffix;
1514 GetListItemSuffix(aListStyle.mListStyleType, suffix, mSuppressPadding);
1516 // We're not going to do proper Bidi reordering on the list item marker, but
1517 // just display the whole thing as RTL or LTR, so we fake reordering by
1518 // appending the suffix to the end of the list item marker if the
1519 // directionality of the characters is the same as the style direction or
1520 // prepending it to the beginning if they are different.
1521 result = (mTextIsRTL == (vis->mDirection == NS_STYLE_DIRECTION_RTL)) ?
1522 result + suffix : suffix + result;
1523 }
1525 #define MIN_BULLET_SIZE 1
1528 void
1529 nsBulletFrame::GetDesiredSize(nsPresContext* aCX,
1530 nsRenderingContext *aRenderingContext,
1531 nsHTMLReflowMetrics& aMetrics,
1532 float aFontSizeInflation)
1533 {
1534 // Reset our padding. If we need it, we'll set it below.
1535 mPadding.SizeTo(0, 0, 0, 0);
1537 const nsStyleList* myList = StyleList();
1538 nscoord ascent;
1540 RemoveStateBits(BULLET_FRAME_IMAGE_LOADING);
1542 if (myList->GetListStyleImage() && mImageRequest) {
1543 uint32_t status;
1544 mImageRequest->GetImageStatus(&status);
1545 if (status & imgIRequest::STATUS_SIZE_AVAILABLE &&
1546 !(status & imgIRequest::STATUS_ERROR)) {
1547 // auto size the image
1548 aMetrics.Width() = mIntrinsicSize.width;
1549 aMetrics.SetTopAscent(aMetrics.Height() = mIntrinsicSize.height);
1551 AddStateBits(BULLET_FRAME_IMAGE_LOADING);
1553 return;
1554 }
1555 }
1557 // If we're getting our desired size and don't have an image, reset
1558 // mIntrinsicSize to (0,0). Otherwise, if we used to have an image, it
1559 // changed, and the new one is coming in, but we're reflowing before it's
1560 // fully there, we'll end up with mIntrinsicSize not matching our size, but
1561 // won't trigger a reflow in OnStartContainer (because mIntrinsicSize will
1562 // match the image size).
1563 mIntrinsicSize.SizeTo(0, 0);
1565 nsRefPtr<nsFontMetrics> fm;
1566 nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm),
1567 aFontSizeInflation);
1568 nscoord bulletSize;
1570 nsAutoString text;
1571 switch (myList->mListStyleType) {
1572 case NS_STYLE_LIST_STYLE_NONE:
1573 aMetrics.Width() = aMetrics.Height() = 0;
1574 aMetrics.SetTopAscent(0);
1575 break;
1577 case NS_STYLE_LIST_STYLE_DISC:
1578 case NS_STYLE_LIST_STYLE_CIRCLE:
1579 case NS_STYLE_LIST_STYLE_SQUARE:
1580 ascent = fm->MaxAscent();
1581 bulletSize = std::max(nsPresContext::CSSPixelsToAppUnits(MIN_BULLET_SIZE),
1582 NSToCoordRound(0.8f * (float(ascent) / 2.0f)));
1583 mPadding.bottom = NSToCoordRound(float(ascent) / 8.0f);
1584 aMetrics.Width() = aMetrics.Height() = bulletSize;
1585 aMetrics.SetTopAscent(bulletSize + mPadding.bottom);
1586 break;
1588 default:
1589 case NS_STYLE_LIST_STYLE_DECIMAL_LEADING_ZERO:
1590 case NS_STYLE_LIST_STYLE_DECIMAL:
1591 case NS_STYLE_LIST_STYLE_CJK_DECIMAL:
1592 case NS_STYLE_LIST_STYLE_LOWER_ROMAN:
1593 case NS_STYLE_LIST_STYLE_UPPER_ROMAN:
1594 case NS_STYLE_LIST_STYLE_LOWER_ALPHA:
1595 case NS_STYLE_LIST_STYLE_UPPER_ALPHA:
1596 case NS_STYLE_LIST_STYLE_KATAKANA:
1597 case NS_STYLE_LIST_STYLE_HIRAGANA:
1598 case NS_STYLE_LIST_STYLE_KATAKANA_IROHA:
1599 case NS_STYLE_LIST_STYLE_HIRAGANA_IROHA:
1600 case NS_STYLE_LIST_STYLE_LOWER_GREEK:
1601 case NS_STYLE_LIST_STYLE_HEBREW:
1602 case NS_STYLE_LIST_STYLE_ARMENIAN:
1603 case NS_STYLE_LIST_STYLE_GEORGIAN:
1604 case NS_STYLE_LIST_STYLE_CJK_IDEOGRAPHIC:
1605 case NS_STYLE_LIST_STYLE_JAPANESE_INFORMAL:
1606 case NS_STYLE_LIST_STYLE_JAPANESE_FORMAL:
1607 case NS_STYLE_LIST_STYLE_KOREAN_HANGUL_FORMAL:
1608 case NS_STYLE_LIST_STYLE_KOREAN_HANJA_INFORMAL:
1609 case NS_STYLE_LIST_STYLE_KOREAN_HANJA_FORMAL:
1610 case NS_STYLE_LIST_STYLE_SIMP_CHINESE_INFORMAL:
1611 case NS_STYLE_LIST_STYLE_SIMP_CHINESE_FORMAL:
1612 case NS_STYLE_LIST_STYLE_TRAD_CHINESE_INFORMAL:
1613 case NS_STYLE_LIST_STYLE_TRAD_CHINESE_FORMAL:
1614 case NS_STYLE_LIST_STYLE_MOZ_SIMP_CHINESE_INFORMAL:
1615 case NS_STYLE_LIST_STYLE_MOZ_SIMP_CHINESE_FORMAL:
1616 case NS_STYLE_LIST_STYLE_MOZ_TRAD_CHINESE_INFORMAL:
1617 case NS_STYLE_LIST_STYLE_MOZ_TRAD_CHINESE_FORMAL:
1618 case NS_STYLE_LIST_STYLE_MOZ_JAPANESE_INFORMAL:
1619 case NS_STYLE_LIST_STYLE_MOZ_JAPANESE_FORMAL:
1620 case NS_STYLE_LIST_STYLE_MOZ_CJK_HEAVENLY_STEM:
1621 case NS_STYLE_LIST_STYLE_MOZ_CJK_EARTHLY_BRANCH:
1622 case NS_STYLE_LIST_STYLE_MOZ_ARABIC_INDIC:
1623 case NS_STYLE_LIST_STYLE_MOZ_PERSIAN:
1624 case NS_STYLE_LIST_STYLE_MOZ_URDU:
1625 case NS_STYLE_LIST_STYLE_MOZ_DEVANAGARI:
1626 case NS_STYLE_LIST_STYLE_MOZ_GURMUKHI:
1627 case NS_STYLE_LIST_STYLE_MOZ_GUJARATI:
1628 case NS_STYLE_LIST_STYLE_MOZ_ORIYA:
1629 case NS_STYLE_LIST_STYLE_MOZ_KANNADA:
1630 case NS_STYLE_LIST_STYLE_MOZ_MALAYALAM:
1631 case NS_STYLE_LIST_STYLE_MOZ_BENGALI:
1632 case NS_STYLE_LIST_STYLE_MOZ_TAMIL:
1633 case NS_STYLE_LIST_STYLE_MOZ_TELUGU:
1634 case NS_STYLE_LIST_STYLE_MOZ_THAI:
1635 case NS_STYLE_LIST_STYLE_MOZ_LAO:
1636 case NS_STYLE_LIST_STYLE_MOZ_MYANMAR:
1637 case NS_STYLE_LIST_STYLE_MOZ_KHMER:
1638 case NS_STYLE_LIST_STYLE_MOZ_HANGUL:
1639 case NS_STYLE_LIST_STYLE_MOZ_HANGUL_CONSONANT:
1640 case NS_STYLE_LIST_STYLE_MOZ_ETHIOPIC_HALEHAME:
1641 case NS_STYLE_LIST_STYLE_MOZ_ETHIOPIC_NUMERIC:
1642 case NS_STYLE_LIST_STYLE_MOZ_ETHIOPIC_HALEHAME_AM:
1643 case NS_STYLE_LIST_STYLE_MOZ_ETHIOPIC_HALEHAME_TI_ER:
1644 case NS_STYLE_LIST_STYLE_MOZ_ETHIOPIC_HALEHAME_TI_ET:
1645 GetListItemText(*myList, text);
1646 aMetrics.Height() = fm->MaxHeight();
1647 aRenderingContext->SetFont(fm);
1648 aMetrics.Width() =
1649 nsLayoutUtils::GetStringWidth(this, aRenderingContext,
1650 text.get(), text.Length());
1651 aMetrics.SetTopAscent(fm->MaxAscent());
1652 break;
1653 }
1654 }
1656 nsresult
1657 nsBulletFrame::Reflow(nsPresContext* aPresContext,
1658 nsHTMLReflowMetrics& aMetrics,
1659 const nsHTMLReflowState& aReflowState,
1660 nsReflowStatus& aStatus)
1661 {
1662 DO_GLOBAL_REFLOW_COUNT("nsBulletFrame");
1663 DISPLAY_REFLOW(aPresContext, this, aReflowState, aMetrics, aStatus);
1665 float inflation = nsLayoutUtils::FontSizeInflationFor(this);
1666 SetFontSizeInflation(inflation);
1668 // Get the base size
1669 // This will also set mSuppressPadding appropriately (via GetListItemText())
1670 // for the builtin counter styles with ideographic comma as suffix where the
1671 // default padding from ua.css is not desired.
1672 GetDesiredSize(aPresContext, aReflowState.rendContext, aMetrics, inflation);
1674 // Add in the border and padding; split the top/bottom between the
1675 // ascent and descent to make things look nice
1676 const nsMargin& borderPadding = aReflowState.ComputedPhysicalBorderPadding();
1677 if (!mSuppressPadding ||
1678 aPresContext->HasAuthorSpecifiedRules(this,
1679 NS_AUTHOR_SPECIFIED_PADDING)) {
1680 mPadding.top += NSToCoordRound(borderPadding.top * inflation);
1681 mPadding.right += NSToCoordRound(borderPadding.right * inflation);
1682 mPadding.bottom += NSToCoordRound(borderPadding.bottom * inflation);
1683 mPadding.left += NSToCoordRound(borderPadding.left * inflation);
1684 }
1685 aMetrics.Width() += mPadding.left + mPadding.right;
1686 aMetrics.Height() += mPadding.top + mPadding.bottom;
1687 aMetrics.SetTopAscent(aMetrics.TopAscent() + mPadding.top);
1689 // XXX this is a bit of a hack, we're assuming that no glyphs used for bullets
1690 // overflow their font-boxes. It'll do for now; to fix it for real, we really
1691 // should rewrite all the text-handling code here to use gfxTextRun (bug
1692 // 397294).
1693 aMetrics.SetOverflowAreasToDesiredBounds();
1695 aStatus = NS_FRAME_COMPLETE;
1696 NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aMetrics);
1697 return NS_OK;
1698 }
1700 /* virtual */ nscoord
1701 nsBulletFrame::GetMinWidth(nsRenderingContext *aRenderingContext)
1702 {
1703 nsHTMLReflowMetrics metrics(GetWritingMode());
1704 DISPLAY_MIN_WIDTH(this, metrics.Width());
1705 GetDesiredSize(PresContext(), aRenderingContext, metrics, 1.0f);
1706 return metrics.Width();
1707 }
1709 /* virtual */ nscoord
1710 nsBulletFrame::GetPrefWidth(nsRenderingContext *aRenderingContext)
1711 {
1712 nsHTMLReflowMetrics metrics(GetWritingMode());
1713 DISPLAY_PREF_WIDTH(this, metrics.Width());
1714 GetDesiredSize(PresContext(), aRenderingContext, metrics, 1.0f);
1715 return metrics.Width();
1716 }
1718 NS_IMETHODIMP
1719 nsBulletFrame::Notify(imgIRequest *aRequest, int32_t aType, const nsIntRect* aData)
1720 {
1721 if (aType == imgINotificationObserver::SIZE_AVAILABLE) {
1722 nsCOMPtr<imgIContainer> image;
1723 aRequest->GetImage(getter_AddRefs(image));
1724 return OnStartContainer(aRequest, image);
1725 }
1727 if (aType == imgINotificationObserver::FRAME_UPDATE) {
1728 // The image has changed.
1729 // Invalidate the entire content area. Maybe it's not optimal but it's simple and
1730 // always correct, and I'll be a stunned mullet if it ever matters for performance
1731 InvalidateFrame();
1732 }
1734 if (aType == imgINotificationObserver::IS_ANIMATED) {
1735 // Register the image request with the refresh driver now that we know it's
1736 // animated.
1737 if (aRequest == mImageRequest) {
1738 nsLayoutUtils::RegisterImageRequest(PresContext(), mImageRequest,
1739 &mRequestRegistered);
1740 }
1741 }
1743 return NS_OK;
1744 }
1746 nsresult nsBulletFrame::OnStartContainer(imgIRequest *aRequest,
1747 imgIContainer *aImage)
1748 {
1749 if (!aImage) return NS_ERROR_INVALID_ARG;
1750 if (!aRequest) return NS_ERROR_INVALID_ARG;
1752 uint32_t status;
1753 aRequest->GetImageStatus(&status);
1754 if (status & imgIRequest::STATUS_ERROR) {
1755 return NS_OK;
1756 }
1758 nscoord w, h;
1759 aImage->GetWidth(&w);
1760 aImage->GetHeight(&h);
1762 nsPresContext* presContext = PresContext();
1764 nsSize newsize(nsPresContext::CSSPixelsToAppUnits(w),
1765 nsPresContext::CSSPixelsToAppUnits(h));
1767 if (mIntrinsicSize != newsize) {
1768 mIntrinsicSize = newsize;
1770 // Now that the size is available (or an error occurred), trigger
1771 // a reflow of the bullet frame.
1772 nsIPresShell *shell = presContext->GetPresShell();
1773 if (shell) {
1774 shell->FrameNeedsReflow(this, nsIPresShell::eStyleChange,
1775 NS_FRAME_IS_DIRTY);
1776 }
1777 }
1779 // Handle animations
1780 aImage->SetAnimationMode(presContext->ImageAnimationMode());
1781 // Ensure the animation (if any) is started. Note: There is no
1782 // corresponding call to Decrement for this. This Increment will be
1783 // 'cleaned up' by the Request when it is destroyed, but only then.
1784 aRequest->IncrementAnimationConsumers();
1786 return NS_OK;
1787 }
1789 void
1790 nsBulletFrame::GetLoadGroup(nsPresContext *aPresContext, nsILoadGroup **aLoadGroup)
1791 {
1792 if (!aPresContext)
1793 return;
1795 NS_PRECONDITION(nullptr != aLoadGroup, "null OUT parameter pointer");
1797 nsIPresShell *shell = aPresContext->GetPresShell();
1799 if (!shell)
1800 return;
1802 nsIDocument *doc = shell->GetDocument();
1803 if (!doc)
1804 return;
1806 *aLoadGroup = doc->GetDocumentLoadGroup().take();
1807 }
1809 union VoidPtrOrFloat {
1810 VoidPtrOrFloat() : p(nullptr) {}
1812 void *p;
1813 float f;
1814 };
1816 float
1817 nsBulletFrame::GetFontSizeInflation() const
1818 {
1819 if (!HasFontSizeInflation()) {
1820 return 1.0f;
1821 }
1822 VoidPtrOrFloat u;
1823 u.p = Properties().Get(FontSizeInflationProperty());
1824 return u.f;
1825 }
1827 void
1828 nsBulletFrame::SetFontSizeInflation(float aInflation)
1829 {
1830 if (aInflation == 1.0f) {
1831 if (HasFontSizeInflation()) {
1832 RemoveStateBits(BULLET_FRAME_HAS_FONT_INFLATION);
1833 Properties().Delete(FontSizeInflationProperty());
1834 }
1835 return;
1836 }
1838 AddStateBits(BULLET_FRAME_HAS_FONT_INFLATION);
1839 VoidPtrOrFloat u;
1840 u.f = aInflation;
1841 Properties().Set(FontSizeInflationProperty(), u.p);
1842 }
1844 already_AddRefed<imgIContainer>
1845 nsBulletFrame::GetImage() const
1846 {
1847 if (mImageRequest && StyleList()->GetListStyleImage()) {
1848 nsCOMPtr<imgIContainer> imageCon;
1849 mImageRequest->GetImage(getter_AddRefs(imageCon));
1850 return imageCon.forget();
1851 }
1853 return nullptr;
1854 }
1856 nscoord
1857 nsBulletFrame::GetBaseline() const
1858 {
1859 nscoord ascent = 0, bottomPadding;
1860 if (GetStateBits() & BULLET_FRAME_IMAGE_LOADING) {
1861 ascent = GetRect().height;
1862 } else {
1863 nsRefPtr<nsFontMetrics> fm;
1864 nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm),
1865 GetFontSizeInflation());
1866 const nsStyleList* myList = StyleList();
1867 switch (myList->mListStyleType) {
1868 case NS_STYLE_LIST_STYLE_NONE:
1869 break;
1871 case NS_STYLE_LIST_STYLE_DISC:
1872 case NS_STYLE_LIST_STYLE_CIRCLE:
1873 case NS_STYLE_LIST_STYLE_SQUARE:
1874 ascent = fm->MaxAscent();
1875 bottomPadding = NSToCoordRound(float(ascent) / 8.0f);
1876 ascent = std::max(nsPresContext::CSSPixelsToAppUnits(MIN_BULLET_SIZE),
1877 NSToCoordRound(0.8f * (float(ascent) / 2.0f)));
1878 ascent += bottomPadding;
1879 break;
1881 default:
1882 ascent = fm->MaxAscent();
1883 break;
1884 }
1885 }
1886 return ascent + GetUsedBorderAndPadding().top;
1887 }
1896 NS_IMPL_ISUPPORTS(nsBulletListener, imgINotificationObserver)
1898 nsBulletListener::nsBulletListener() :
1899 mFrame(nullptr)
1900 {
1901 }
1903 nsBulletListener::~nsBulletListener()
1904 {
1905 }
1907 NS_IMETHODIMP
1908 nsBulletListener::Notify(imgIRequest *aRequest, int32_t aType, const nsIntRect* aData)
1909 {
1910 if (!mFrame)
1911 return NS_ERROR_FAILURE;
1912 return mFrame->Notify(aRequest, aType, aData);
1913 }