layout/generic/nsBulletFrame.cpp

changeset 0
6474c204b198
equal deleted inserted replaced
-1:000000000000 0:c0c1d86f534b
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/. */
5
6 /* rendering object for list-item bullets */
7
8 #include "nsBulletFrame.h"
9
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"
22
23 #include "imgIContainer.h"
24 #include "imgRequestProxy.h"
25 #include "nsIURI.h"
26
27 #include <algorithm>
28
29 #ifdef ACCESSIBILITY
30 #include "nsAccessibilityService.h"
31 #endif
32
33 using namespace mozilla;
34
35 NS_DECLARE_FRAME_PROPERTY(FontSizeInflationProperty, nullptr)
36
37 NS_IMPL_FRAMEARENA_HELPERS(nsBulletFrame)
38
39 #ifdef DEBUG
40 NS_QUERYFRAME_HEAD(nsBulletFrame)
41 NS_QUERYFRAME_ENTRY(nsBulletFrame)
42 NS_QUERYFRAME_TAIL_INHERITING(nsFrame)
43 #endif
44
45 nsBulletFrame::~nsBulletFrame()
46 {
47 }
48
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 }
61
62 if (mListener) {
63 mListener->SetFrame(nullptr);
64 }
65
66 // Let base class do the rest
67 nsFrame::DestroyFrom(aDestructRoot);
68 }
69
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
77
78 nsIAtom*
79 nsBulletFrame::GetType() const
80 {
81 return nsGkAtoms::bulletFrame;
82 }
83
84 bool
85 nsBulletFrame::IsEmpty()
86 {
87 return IsSelfEmpty();
88 }
89
90 bool
91 nsBulletFrame::IsSelfEmpty()
92 {
93 return StyleList()->mListStyleType == NS_STYLE_LIST_STYLE_NONE;
94 }
95
96 /* virtual */ void
97 nsBulletFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext)
98 {
99 nsFrame::DidSetStyleContext(aOldStyleContext);
100
101 imgRequestProxy *newRequest = StyleList()->GetListStyleImage();
102
103 if (newRequest) {
104
105 if (!mListener) {
106 mListener = new nsBulletListener();
107 mListener->SetFrame(this);
108 }
109
110 bool needNewRequest = true;
111
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 }
126
127 if (needNewRequest) {
128 nsRefPtr<imgRequestProxy> oldRequest = mImageRequest;
129 newRequest->Clone(mListener, getter_AddRefs(mImageRequest));
130
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 }
140
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);
153
154 mImageRequest->CancelAndForgetObserver(NS_ERROR_FAILURE);
155 mImageRequest = nullptr;
156 }
157 }
158
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;
169
170 const nsStyleList* newStyleList = StyleList();
171 bool hasBullet = newStyleList->GetListStyleImage() ||
172 newStyleList->mListStyleType != NS_STYLE_LIST_STYLE_NONE;
173
174 if (hadBullet != hasBullet) {
175 accService->UpdateListBullet(PresContext()->GetPresShell(), mContent,
176 hasBullet);
177 }
178 }
179 }
180 }
181 #endif
182 }
183
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 }
193
194 int32_t mOrdinal;
195 };
196
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
208
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)
223
224 virtual nsRect GetComponentAlphaBounds(nsDisplayListBuilder* aBuilder) MOZ_OVERRIDE
225 {
226 bool snap;
227 return GetBounds(aBuilder, &snap);
228 }
229
230 virtual nsDisplayItemGeometry* AllocateGeometry(nsDisplayListBuilder* aBuilder) MOZ_OVERRIDE
231 {
232 return new nsDisplayBulletGeometry(this, aBuilder);
233 }
234
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);
241
242 if (f->GetOrdinal() != geometry->mOrdinal) {
243 bool snap;
244 aInvalidRegion->Or(geometry->mBounds, GetBounds(aBuilder, &snap));
245 return;
246 }
247
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 }
256
257 return nsDisplayItem::ComputeInvalidationRegion(aBuilder, aGeometry, aInvalidRegion);
258 }
259 };
260
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 }
271
272 void
273 nsBulletFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
274 const nsRect& aDirtyRect,
275 const nsDisplayListSet& aLists)
276 {
277 if (!IsVisibleForPainting(aBuilder))
278 return;
279
280 DO_GLOBAL_REFLOW_COUNT_DSP("nsBulletFrame");
281
282 aLists.Content()->AppendNewToTop(
283 new (aBuilder) nsDisplayBullet(aBuilder, this));
284 }
285
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;
292
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 }
311
312 nsRefPtr<nsFontMetrics> fm;
313 aRenderingContext.SetColor(nsLayoutUtils::GetColor(this, eCSSProperty_color));
314
315 mTextIsRTL = false;
316
317 nsAutoString text;
318 switch (listStyleType) {
319 case NS_STYLE_LIST_STYLE_NONE:
320 break;
321
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;
328
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;
334
335 case NS_STYLE_LIST_STYLE_SQUARE:
336 {
337 nsRect rect(aPt, mRect.Size());
338 rect.Deflate(mPadding);
339
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;
356
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 }
424
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");
432
433 // Assume that the ordinal comes from the caller
434 int32_t oldOrdinal = mOrdinal;
435 mOrdinal = aNextOrdinal;
436
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 }
452
453 *aChanged = oldOrdinal != mOrdinal;
454
455 return nsCounterManager::IncrementCounter(mOrdinal, aIncrement);
456 }
457
458
459 // XXX change roman/alpha to use unsigned math so that maxint and
460 // maxnegint will work
461
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 }
516
517
518 static const char gLowerRomanCharsA[] = "ixcm";
519 static const char gUpperRomanCharsA[] = "IXCM";
520 static const char gLowerRomanCharsB[] = "vld";
521 static const char gUpperRomanCharsB[] = "VLD";
522
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;
535
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 }
570
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 };
581
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 };
591
592
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 };
610
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 };
626
627
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 };
643
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 };
658
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 };
669
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 };
696
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 };
740
741
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...
746
747 #define NUM_BUF_SIZE 34
748
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 }
765
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 }
785
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 };
908
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 }
983
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 };
994
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
1016
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
1034
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);
1047
1048 result.Append(allText);
1049 return true;
1050 }
1051
1052
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 }
1058
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 }
1075
1076
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 }
1094
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 }
1111
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
1121
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();
1130
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 }
1141
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;
1151
1152 bool oddGroup = (groupIndexFromRight & 1);
1153
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 }
1160
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 }
1184
1185
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;
1195
1196 switch (aListStyleType) {
1197 case NS_STYLE_LIST_STYLE_NONE: // used by counters code only
1198 break;
1199
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;
1204
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;
1209
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;
1214
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;
1221
1222 case NS_STYLE_LIST_STYLE_DECIMAL_LEADING_ZERO:
1223 success = DecimalLeadingZeroToText(aOrdinal, result);
1224 break;
1225
1226 case NS_STYLE_LIST_STYLE_CJK_DECIMAL:
1227 success = CharListDecimalToText(aOrdinal, result, gCJKDecimalChars);
1228 break;
1229
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;
1238
1239 case NS_STYLE_LIST_STYLE_LOWER_ALPHA:
1240 success = CharListToText(aOrdinal, result, gLowerAlphaChars, ALPHA_SIZE);
1241 break;
1242
1243 case NS_STYLE_LIST_STYLE_UPPER_ALPHA:
1244 success = CharListToText(aOrdinal, result, gUpperAlphaChars, ALPHA_SIZE);
1245 break;
1246
1247 case NS_STYLE_LIST_STYLE_KATAKANA:
1248 success = CharListToText(aOrdinal, result, gKatakanaChars,
1249 KATAKANA_CHARS_SIZE);
1250 break;
1251
1252 case NS_STYLE_LIST_STYLE_HIRAGANA:
1253 success = CharListToText(aOrdinal, result, gHiraganaChars,
1254 HIRAGANA_CHARS_SIZE);
1255 break;
1256
1257 case NS_STYLE_LIST_STYLE_KATAKANA_IROHA:
1258 success = CharListToText(aOrdinal, result, gKatakanaIrohaChars,
1259 KATAKANA_IROHA_CHARS_SIZE);
1260 break;
1261
1262 case NS_STYLE_LIST_STYLE_HIRAGANA_IROHA:
1263 success = CharListToText(aOrdinal, result, gHiraganaIrohaChars,
1264 HIRAGANA_IROHA_CHARS_SIZE);
1265 break;
1266
1267 case NS_STYLE_LIST_STYLE_LOWER_GREEK:
1268 success = CharListToText(aOrdinal, result, gLowerGreekChars ,
1269 LOWER_GREEK_CHARS_SIZE);
1270 break;
1271
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;
1279
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;
1285
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;
1292
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;
1298
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;
1304
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;
1310
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;
1316
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;
1322
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;
1327
1328 case NS_STYLE_LIST_STYLE_HEBREW:
1329 isRTL = true;
1330 success = HebrewToText(aOrdinal, result);
1331 break;
1332
1333 case NS_STYLE_LIST_STYLE_ARMENIAN:
1334 success = ArmenianToText(aOrdinal, result);
1335 break;
1336
1337 case NS_STYLE_LIST_STYLE_GEORGIAN:
1338 success = GeorgianToText(aOrdinal, result);
1339 break;
1340
1341 case NS_STYLE_LIST_STYLE_MOZ_ARABIC_INDIC:
1342 success = OtherDecimalToText(aOrdinal, 0x0660, result);
1343 break;
1344
1345 case NS_STYLE_LIST_STYLE_MOZ_PERSIAN:
1346 case NS_STYLE_LIST_STYLE_MOZ_URDU:
1347 success = OtherDecimalToText(aOrdinal, 0x06f0, result);
1348 break;
1349
1350 case NS_STYLE_LIST_STYLE_MOZ_DEVANAGARI:
1351 success = OtherDecimalToText(aOrdinal, 0x0966, result);
1352 break;
1353
1354 case NS_STYLE_LIST_STYLE_MOZ_GURMUKHI:
1355 success = OtherDecimalToText(aOrdinal, 0x0a66, result);
1356 break;
1357
1358 case NS_STYLE_LIST_STYLE_MOZ_GUJARATI:
1359 success = OtherDecimalToText(aOrdinal, 0x0AE6, result);
1360 break;
1361
1362 case NS_STYLE_LIST_STYLE_MOZ_ORIYA:
1363 success = OtherDecimalToText(aOrdinal, 0x0B66, result);
1364 break;
1365
1366 case NS_STYLE_LIST_STYLE_MOZ_KANNADA:
1367 success = OtherDecimalToText(aOrdinal, 0x0CE6, result);
1368 break;
1369
1370 case NS_STYLE_LIST_STYLE_MOZ_MALAYALAM:
1371 success = OtherDecimalToText(aOrdinal, 0x0D66, result);
1372 break;
1373
1374 case NS_STYLE_LIST_STYLE_MOZ_THAI:
1375 success = OtherDecimalToText(aOrdinal, 0x0E50, result);
1376 break;
1377
1378 case NS_STYLE_LIST_STYLE_MOZ_LAO:
1379 success = OtherDecimalToText(aOrdinal, 0x0ED0, result);
1380 break;
1381
1382 case NS_STYLE_LIST_STYLE_MOZ_MYANMAR:
1383 success = OtherDecimalToText(aOrdinal, 0x1040, result);
1384 break;
1385
1386 case NS_STYLE_LIST_STYLE_MOZ_KHMER:
1387 success = OtherDecimalToText(aOrdinal, 0x17E0, result);
1388 break;
1389
1390 case NS_STYLE_LIST_STYLE_MOZ_BENGALI:
1391 success = OtherDecimalToText(aOrdinal, 0x09E6, result);
1392 break;
1393
1394 case NS_STYLE_LIST_STYLE_MOZ_TELUGU:
1395 success = OtherDecimalToText(aOrdinal, 0x0C66, result);
1396 break;
1397
1398 case NS_STYLE_LIST_STYLE_MOZ_TAMIL:
1399 success = TamilToText(aOrdinal, result);
1400 break;
1401
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;
1407
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;
1413
1414 case NS_STYLE_LIST_STYLE_MOZ_HANGUL:
1415 success = CharListToText(aOrdinal, result, gHangulChars, HANGUL_CHARS_SIZE);
1416 break;
1417
1418 case NS_STYLE_LIST_STYLE_MOZ_HANGUL_CONSONANT:
1419 success = CharListToText(aOrdinal, result, gHangulConsonantChars,
1420 HANGUL_CONSONANT_CHARS_SIZE);
1421 break;
1422
1423 case NS_STYLE_LIST_STYLE_MOZ_ETHIOPIC_HALEHAME:
1424 success = CharListToText(aOrdinal, result, gEthiopicHalehameChars,
1425 ETHIOPIC_HALEHAME_CHARS_SIZE);
1426 break;
1427
1428 case NS_STYLE_LIST_STYLE_MOZ_ETHIOPIC_NUMERIC:
1429 success = EthiopicToText(aOrdinal, result);
1430 break;
1431
1432 case NS_STYLE_LIST_STYLE_MOZ_ETHIOPIC_HALEHAME_AM:
1433 success = CharListToText(aOrdinal, result, gEthiopicHalehameAmChars,
1434 ETHIOPIC_HALEHAME_AM_CHARS_SIZE);
1435 break;
1436
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;
1441
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 }
1451
1452 /* static */ void
1453 nsBulletFrame::GetListItemSuffix(int32_t aListStyleType,
1454 nsString& aResult,
1455 bool& aSuppressPadding)
1456 {
1457 aResult = '.';
1458 aSuppressPadding = false;
1459
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;
1467
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;
1487
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 }
1497
1498 void
1499 nsBulletFrame::GetListItemText(const nsStyleList& aListStyle,
1500 nsString& result)
1501 {
1502 const nsStyleVisibility* vis = StyleVisibility();
1503
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");
1509
1510 result.Truncate();
1511 AppendCounterText(aListStyle.mListStyleType, mOrdinal, result, mTextIsRTL);
1512
1513 nsAutoString suffix;
1514 GetListItemSuffix(aListStyle.mListStyleType, suffix, mSuppressPadding);
1515
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 }
1524
1525 #define MIN_BULLET_SIZE 1
1526
1527
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);
1536
1537 const nsStyleList* myList = StyleList();
1538 nscoord ascent;
1539
1540 RemoveStateBits(BULLET_FRAME_IMAGE_LOADING);
1541
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);
1550
1551 AddStateBits(BULLET_FRAME_IMAGE_LOADING);
1552
1553 return;
1554 }
1555 }
1556
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);
1564
1565 nsRefPtr<nsFontMetrics> fm;
1566 nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm),
1567 aFontSizeInflation);
1568 nscoord bulletSize;
1569
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;
1576
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;
1587
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 }
1655
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);
1664
1665 float inflation = nsLayoutUtils::FontSizeInflationFor(this);
1666 SetFontSizeInflation(inflation);
1667
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);
1673
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);
1688
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();
1694
1695 aStatus = NS_FRAME_COMPLETE;
1696 NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aMetrics);
1697 return NS_OK;
1698 }
1699
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 }
1708
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 }
1717
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 }
1726
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 }
1733
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 }
1742
1743 return NS_OK;
1744 }
1745
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;
1751
1752 uint32_t status;
1753 aRequest->GetImageStatus(&status);
1754 if (status & imgIRequest::STATUS_ERROR) {
1755 return NS_OK;
1756 }
1757
1758 nscoord w, h;
1759 aImage->GetWidth(&w);
1760 aImage->GetHeight(&h);
1761
1762 nsPresContext* presContext = PresContext();
1763
1764 nsSize newsize(nsPresContext::CSSPixelsToAppUnits(w),
1765 nsPresContext::CSSPixelsToAppUnits(h));
1766
1767 if (mIntrinsicSize != newsize) {
1768 mIntrinsicSize = newsize;
1769
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 }
1778
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();
1785
1786 return NS_OK;
1787 }
1788
1789 void
1790 nsBulletFrame::GetLoadGroup(nsPresContext *aPresContext, nsILoadGroup **aLoadGroup)
1791 {
1792 if (!aPresContext)
1793 return;
1794
1795 NS_PRECONDITION(nullptr != aLoadGroup, "null OUT parameter pointer");
1796
1797 nsIPresShell *shell = aPresContext->GetPresShell();
1798
1799 if (!shell)
1800 return;
1801
1802 nsIDocument *doc = shell->GetDocument();
1803 if (!doc)
1804 return;
1805
1806 *aLoadGroup = doc->GetDocumentLoadGroup().take();
1807 }
1808
1809 union VoidPtrOrFloat {
1810 VoidPtrOrFloat() : p(nullptr) {}
1811
1812 void *p;
1813 float f;
1814 };
1815
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 }
1826
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 }
1837
1838 AddStateBits(BULLET_FRAME_HAS_FONT_INFLATION);
1839 VoidPtrOrFloat u;
1840 u.f = aInflation;
1841 Properties().Set(FontSizeInflationProperty(), u.p);
1842 }
1843
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 }
1852
1853 return nullptr;
1854 }
1855
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;
1870
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;
1880
1881 default:
1882 ascent = fm->MaxAscent();
1883 break;
1884 }
1885 }
1886 return ascent + GetUsedBorderAndPadding().top;
1887 }
1888
1889
1890
1891
1892
1893
1894
1895
1896 NS_IMPL_ISUPPORTS(nsBulletListener, imgINotificationObserver)
1897
1898 nsBulletListener::nsBulletListener() :
1899 mFrame(nullptr)
1900 {
1901 }
1902
1903 nsBulletListener::~nsBulletListener()
1904 {
1905 }
1906
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 }

mercurial