layout/generic/nsBulletFrame.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     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; )
  1008       if( n3 >= n1)
  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 )
  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;
  1053 static bool ArmenianToText(int32_t ordinal, nsString& result)
  1055   if (ordinal < 1 || ordinal > 9999) { // zero or reach the limit of Armenian numbering system
  1056     return false;
  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)
  1066       char16_t u = 0x0530 + (d * 9) + cur;
  1067       buf[--idx] = u;
  1069     ++d;
  1070     ordinal /= 10;
  1071   } while (ordinal > 0);
  1072   result.Append(buf + idx, NUM_BUF_SIZE - idx);
  1073   return true;
  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)
  1091   if (ordinal < 1 || ordinal > 19999) { // zero or reach the limit of Georgian numbering system
  1092     return false;
  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)
  1102       char16_t u = gGeorgianValue[(d * 9 ) + ( cur - 1)];
  1103       buf[--idx] = u;
  1105     ++d;
  1106     ordinal /= 10;
  1107   } while (ordinal > 0);
  1108   result.Append(buf + idx, NUM_BUF_SIZE - idx);
  1109   return true;
  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)
  1124   if (ordinal < 1) {
  1125     return false;
  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--;
  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;
  1161     // put it all together...
  1162     if (tensValue) {
  1163       // map onto Ethiopic "tens":
  1164       result.Append((char16_t) (tensValue +  ETHIOPIC_TEN - 1));
  1166     if (unitsValue) {
  1167       //map onto Ethiopic "units":
  1168       result.Append((char16_t) (unitsValue + ETHIOPIC_ONE - 1));
  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);
  1176     } else {
  1177       if (groupIndexFromRight) {
  1178         result.Append((char16_t) ETHIOPIC_TEN_THOUSAND);
  1182   return true;
  1186 /* static */ void
  1187 nsBulletFrame::AppendCounterText(int32_t aListStyleType,
  1188                                  int32_t aOrdinal,
  1189                                  nsString& result,
  1190                                  bool& isRTL)
  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;
  1447   if (!success) {
  1448     AppendCounterText(fallback, aOrdinal, result, isRTL);
  1452 /* static */ void
  1453 nsBulletFrame::GetListItemSuffix(int32_t aListStyleType,
  1454                                  nsString& aResult,
  1455                                  bool& aSuppressPadding)
  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;
  1498 void
  1499 nsBulletFrame::GetListItemText(const nsStyleList& aListStyle,
  1500                                nsString& result)
  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;
  1525 #define MIN_BULLET_SIZE 1
  1528 void
  1529 nsBulletFrame::GetDesiredSize(nsPresContext*  aCX,
  1530                               nsRenderingContext *aRenderingContext,
  1531                               nsHTMLReflowMetrics& aMetrics,
  1532                               float aFontSizeInflation)
  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;
  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;
  1656 nsresult
  1657 nsBulletFrame::Reflow(nsPresContext* aPresContext,
  1658                       nsHTMLReflowMetrics& aMetrics,
  1659                       const nsHTMLReflowState& aReflowState,
  1660                       nsReflowStatus& aStatus)
  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);
  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;
  1700 /* virtual */ nscoord
  1701 nsBulletFrame::GetMinWidth(nsRenderingContext *aRenderingContext)
  1703   nsHTMLReflowMetrics metrics(GetWritingMode());
  1704   DISPLAY_MIN_WIDTH(this, metrics.Width());
  1705   GetDesiredSize(PresContext(), aRenderingContext, metrics, 1.0f);
  1706   return metrics.Width();
  1709 /* virtual */ nscoord
  1710 nsBulletFrame::GetPrefWidth(nsRenderingContext *aRenderingContext)
  1712   nsHTMLReflowMetrics metrics(GetWritingMode());
  1713   DISPLAY_PREF_WIDTH(this, metrics.Width());
  1714   GetDesiredSize(PresContext(), aRenderingContext, metrics, 1.0f);
  1715   return metrics.Width();
  1718 NS_IMETHODIMP
  1719 nsBulletFrame::Notify(imgIRequest *aRequest, int32_t aType, const nsIntRect* aData)
  1721   if (aType == imgINotificationObserver::SIZE_AVAILABLE) {
  1722     nsCOMPtr<imgIContainer> image;
  1723     aRequest->GetImage(getter_AddRefs(image));
  1724     return OnStartContainer(aRequest, image);
  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();
  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);
  1743   return NS_OK;
  1746 nsresult nsBulletFrame::OnStartContainer(imgIRequest *aRequest,
  1747                                          imgIContainer *aImage)
  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;
  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);
  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;
  1789 void
  1790 nsBulletFrame::GetLoadGroup(nsPresContext *aPresContext, nsILoadGroup **aLoadGroup)
  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();
  1809 union VoidPtrOrFloat {
  1810   VoidPtrOrFloat() : p(nullptr) {}
  1812   void *p;
  1813   float f;
  1814 };
  1816 float
  1817 nsBulletFrame::GetFontSizeInflation() const
  1819   if (!HasFontSizeInflation()) {
  1820     return 1.0f;
  1822   VoidPtrOrFloat u;
  1823   u.p = Properties().Get(FontSizeInflationProperty());
  1824   return u.f;
  1827 void
  1828 nsBulletFrame::SetFontSizeInflation(float aInflation)
  1830   if (aInflation == 1.0f) {
  1831     if (HasFontSizeInflation()) {
  1832       RemoveStateBits(BULLET_FRAME_HAS_FONT_INFLATION);
  1833       Properties().Delete(FontSizeInflationProperty());
  1835     return;
  1838   AddStateBits(BULLET_FRAME_HAS_FONT_INFLATION);
  1839   VoidPtrOrFloat u;
  1840   u.f = aInflation;
  1841   Properties().Set(FontSizeInflationProperty(), u.p);
  1844 already_AddRefed<imgIContainer>
  1845 nsBulletFrame::GetImage() const
  1847   if (mImageRequest && StyleList()->GetListStyleImage()) {
  1848     nsCOMPtr<imgIContainer> imageCon;
  1849     mImageRequest->GetImage(getter_AddRefs(imageCon));
  1850     return imageCon.forget();
  1853   return nullptr;
  1856 nscoord
  1857 nsBulletFrame::GetBaseline() const
  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;
  1886   return ascent + GetUsedBorderAndPadding().top;
  1896 NS_IMPL_ISUPPORTS(nsBulletListener, imgINotificationObserver)
  1898 nsBulletListener::nsBulletListener() :
  1899   mFrame(nullptr)
  1903 nsBulletListener::~nsBulletListener()
  1907 NS_IMETHODIMP
  1908 nsBulletListener::Notify(imgIRequest *aRequest, int32_t aType, const nsIntRect* aData)
  1910   if (!mFrame)
  1911     return NS_ERROR_FAILURE;
  1912   return mFrame->Notify(aRequest, aType, aData);

mercurial