michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: /* rendering object for list-item bullets */ michael@0: michael@0: #include "nsBulletFrame.h" michael@0: michael@0: #include "mozilla/MathAlgorithms.h" michael@0: #include "nsCOMPtr.h" michael@0: #include "nsGkAtoms.h" michael@0: #include "nsGenericHTMLElement.h" michael@0: #include "nsAttrValueInlines.h" michael@0: #include "nsPresContext.h" michael@0: #include "nsIPresShell.h" michael@0: #include "nsIDocument.h" michael@0: #include "nsRenderingContext.h" michael@0: #include "prprf.h" michael@0: #include "nsDisplayList.h" michael@0: #include "nsCounterManager.h" michael@0: michael@0: #include "imgIContainer.h" michael@0: #include "imgRequestProxy.h" michael@0: #include "nsIURI.h" michael@0: michael@0: #include michael@0: michael@0: #ifdef ACCESSIBILITY michael@0: #include "nsAccessibilityService.h" michael@0: #endif michael@0: michael@0: using namespace mozilla; michael@0: michael@0: NS_DECLARE_FRAME_PROPERTY(FontSizeInflationProperty, nullptr) michael@0: michael@0: NS_IMPL_FRAMEARENA_HELPERS(nsBulletFrame) michael@0: michael@0: #ifdef DEBUG michael@0: NS_QUERYFRAME_HEAD(nsBulletFrame) michael@0: NS_QUERYFRAME_ENTRY(nsBulletFrame) michael@0: NS_QUERYFRAME_TAIL_INHERITING(nsFrame) michael@0: #endif michael@0: michael@0: nsBulletFrame::~nsBulletFrame() michael@0: { michael@0: } michael@0: michael@0: void michael@0: nsBulletFrame::DestroyFrom(nsIFrame* aDestructRoot) michael@0: { michael@0: // Stop image loading first michael@0: if (mImageRequest) { michael@0: // Deregister our image request from the refresh driver michael@0: nsLayoutUtils::DeregisterImageRequest(PresContext(), michael@0: mImageRequest, michael@0: &mRequestRegistered); michael@0: mImageRequest->CancelAndForgetObserver(NS_ERROR_FAILURE); michael@0: mImageRequest = nullptr; michael@0: } michael@0: michael@0: if (mListener) { michael@0: mListener->SetFrame(nullptr); michael@0: } michael@0: michael@0: // Let base class do the rest michael@0: nsFrame::DestroyFrom(aDestructRoot); michael@0: } michael@0: michael@0: #ifdef DEBUG_FRAME_DUMP michael@0: nsresult michael@0: nsBulletFrame::GetFrameName(nsAString& aResult) const michael@0: { michael@0: return MakeFrameName(NS_LITERAL_STRING("Bullet"), aResult); michael@0: } michael@0: #endif michael@0: michael@0: nsIAtom* michael@0: nsBulletFrame::GetType() const michael@0: { michael@0: return nsGkAtoms::bulletFrame; michael@0: } michael@0: michael@0: bool michael@0: nsBulletFrame::IsEmpty() michael@0: { michael@0: return IsSelfEmpty(); michael@0: } michael@0: michael@0: bool michael@0: nsBulletFrame::IsSelfEmpty() michael@0: { michael@0: return StyleList()->mListStyleType == NS_STYLE_LIST_STYLE_NONE; michael@0: } michael@0: michael@0: /* virtual */ void michael@0: nsBulletFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext) michael@0: { michael@0: nsFrame::DidSetStyleContext(aOldStyleContext); michael@0: michael@0: imgRequestProxy *newRequest = StyleList()->GetListStyleImage(); michael@0: michael@0: if (newRequest) { michael@0: michael@0: if (!mListener) { michael@0: mListener = new nsBulletListener(); michael@0: mListener->SetFrame(this); michael@0: } michael@0: michael@0: bool needNewRequest = true; michael@0: michael@0: if (mImageRequest) { michael@0: // Reload the image, maybe... michael@0: nsCOMPtr oldURI; michael@0: mImageRequest->GetURI(getter_AddRefs(oldURI)); michael@0: nsCOMPtr newURI; michael@0: newRequest->GetURI(getter_AddRefs(newURI)); michael@0: if (oldURI && newURI) { michael@0: bool same; michael@0: newURI->Equals(oldURI, &same); michael@0: if (same) { michael@0: needNewRequest = false; michael@0: } michael@0: } michael@0: } michael@0: michael@0: if (needNewRequest) { michael@0: nsRefPtr oldRequest = mImageRequest; michael@0: newRequest->Clone(mListener, getter_AddRefs(mImageRequest)); michael@0: michael@0: // Deregister the old request. We wait until after Clone is done in case michael@0: // the old request and the new request are the same underlying image michael@0: // accessed via different URLs. michael@0: if (oldRequest) { michael@0: nsLayoutUtils::DeregisterImageRequest(PresContext(), oldRequest, michael@0: &mRequestRegistered); michael@0: oldRequest->CancelAndForgetObserver(NS_ERROR_FAILURE); michael@0: oldRequest = nullptr; michael@0: } michael@0: michael@0: // Register the new request. michael@0: if (mImageRequest) { michael@0: nsLayoutUtils::RegisterImageRequestIfAnimated(PresContext(), michael@0: mImageRequest, michael@0: &mRequestRegistered); michael@0: } michael@0: } michael@0: } else { michael@0: // No image request on the new style context michael@0: if (mImageRequest) { michael@0: nsLayoutUtils::DeregisterImageRequest(PresContext(), mImageRequest, michael@0: &mRequestRegistered); michael@0: michael@0: mImageRequest->CancelAndForgetObserver(NS_ERROR_FAILURE); michael@0: mImageRequest = nullptr; michael@0: } michael@0: } michael@0: michael@0: #ifdef ACCESSIBILITY michael@0: // Update the list bullet accessible. If old style list isn't available then michael@0: // no need to update the accessible tree because it's not created yet. michael@0: if (aOldStyleContext) { michael@0: nsAccessibilityService* accService = nsIPresShell::AccService(); michael@0: if (accService) { michael@0: const nsStyleList* oldStyleList = aOldStyleContext->PeekStyleList(); michael@0: if (oldStyleList) { michael@0: bool hadBullet = oldStyleList->GetListStyleImage() || michael@0: oldStyleList->mListStyleType != NS_STYLE_LIST_STYLE_NONE; michael@0: michael@0: const nsStyleList* newStyleList = StyleList(); michael@0: bool hasBullet = newStyleList->GetListStyleImage() || michael@0: newStyleList->mListStyleType != NS_STYLE_LIST_STYLE_NONE; michael@0: michael@0: if (hadBullet != hasBullet) { michael@0: accService->UpdateListBullet(PresContext()->GetPresShell(), mContent, michael@0: hasBullet); michael@0: } michael@0: } michael@0: } michael@0: } michael@0: #endif michael@0: } michael@0: michael@0: class nsDisplayBulletGeometry : public nsDisplayItemGenericGeometry michael@0: { michael@0: public: michael@0: nsDisplayBulletGeometry(nsDisplayItem* aItem, nsDisplayListBuilder* aBuilder) michael@0: : nsDisplayItemGenericGeometry(aItem, aBuilder) michael@0: { michael@0: nsBulletFrame* f = static_cast(aItem->Frame()); michael@0: mOrdinal = f->GetOrdinal(); michael@0: } michael@0: michael@0: int32_t mOrdinal; michael@0: }; michael@0: michael@0: class nsDisplayBullet : public nsDisplayItem { michael@0: public: michael@0: nsDisplayBullet(nsDisplayListBuilder* aBuilder, nsBulletFrame* aFrame) : michael@0: nsDisplayItem(aBuilder, aFrame) { michael@0: MOZ_COUNT_CTOR(nsDisplayBullet); michael@0: } michael@0: #ifdef NS_BUILD_REFCNT_LOGGING michael@0: virtual ~nsDisplayBullet() { michael@0: MOZ_COUNT_DTOR(nsDisplayBullet); michael@0: } michael@0: #endif michael@0: michael@0: virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder, michael@0: bool* aSnap) MOZ_OVERRIDE michael@0: { michael@0: *aSnap = false; michael@0: return mFrame->GetVisualOverflowRectRelativeToSelf() + ToReferenceFrame(); michael@0: } michael@0: virtual void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect, michael@0: HitTestState* aState, michael@0: nsTArray *aOutFrames) MOZ_OVERRIDE { michael@0: aOutFrames->AppendElement(mFrame); michael@0: } michael@0: virtual void Paint(nsDisplayListBuilder* aBuilder, michael@0: nsRenderingContext* aCtx) MOZ_OVERRIDE; michael@0: NS_DISPLAY_DECL_NAME("Bullet", TYPE_BULLET) michael@0: michael@0: virtual nsRect GetComponentAlphaBounds(nsDisplayListBuilder* aBuilder) MOZ_OVERRIDE michael@0: { michael@0: bool snap; michael@0: return GetBounds(aBuilder, &snap); michael@0: } michael@0: michael@0: virtual nsDisplayItemGeometry* AllocateGeometry(nsDisplayListBuilder* aBuilder) MOZ_OVERRIDE michael@0: { michael@0: return new nsDisplayBulletGeometry(this, aBuilder); michael@0: } michael@0: michael@0: virtual void ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder, michael@0: const nsDisplayItemGeometry* aGeometry, michael@0: nsRegion *aInvalidRegion) MOZ_OVERRIDE michael@0: { michael@0: const nsDisplayBulletGeometry* geometry = static_cast(aGeometry); michael@0: nsBulletFrame* f = static_cast(mFrame); michael@0: michael@0: if (f->GetOrdinal() != geometry->mOrdinal) { michael@0: bool snap; michael@0: aInvalidRegion->Or(geometry->mBounds, GetBounds(aBuilder, &snap)); michael@0: return; michael@0: } michael@0: michael@0: nsCOMPtr image = f->GetImage(); michael@0: if (aBuilder->ShouldSyncDecodeImages() && image && !image->IsDecoded()) { michael@0: // If we are going to do a sync decode and we are not decoded then we are michael@0: // going to be drawing something different from what is currently there, michael@0: // so we add our bounds to the invalid region. michael@0: bool snap; michael@0: aInvalidRegion->Or(*aInvalidRegion, GetBounds(aBuilder, &snap)); michael@0: } michael@0: michael@0: return nsDisplayItem::ComputeInvalidationRegion(aBuilder, aGeometry, aInvalidRegion); michael@0: } michael@0: }; michael@0: michael@0: void nsDisplayBullet::Paint(nsDisplayListBuilder* aBuilder, michael@0: nsRenderingContext* aCtx) michael@0: { michael@0: uint32_t flags = imgIContainer::FLAG_NONE; michael@0: if (aBuilder->ShouldSyncDecodeImages()) { michael@0: flags |= imgIContainer::FLAG_SYNC_DECODE; michael@0: } michael@0: static_cast(mFrame)-> michael@0: PaintBullet(*aCtx, ToReferenceFrame(), mVisibleRect, flags); michael@0: } michael@0: michael@0: void michael@0: nsBulletFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, michael@0: const nsRect& aDirtyRect, michael@0: const nsDisplayListSet& aLists) michael@0: { michael@0: if (!IsVisibleForPainting(aBuilder)) michael@0: return; michael@0: michael@0: DO_GLOBAL_REFLOW_COUNT_DSP("nsBulletFrame"); michael@0: michael@0: aLists.Content()->AppendNewToTop( michael@0: new (aBuilder) nsDisplayBullet(aBuilder, this)); michael@0: } michael@0: michael@0: void michael@0: nsBulletFrame::PaintBullet(nsRenderingContext& aRenderingContext, nsPoint aPt, michael@0: const nsRect& aDirtyRect, uint32_t aFlags) michael@0: { michael@0: const nsStyleList* myList = StyleList(); michael@0: uint8_t listStyleType = myList->mListStyleType; michael@0: michael@0: if (myList->GetListStyleImage() && mImageRequest) { michael@0: uint32_t status; michael@0: mImageRequest->GetImageStatus(&status); michael@0: if (status & imgIRequest::STATUS_LOAD_COMPLETE && michael@0: !(status & imgIRequest::STATUS_ERROR)) { michael@0: nsCOMPtr imageCon; michael@0: mImageRequest->GetImage(getter_AddRefs(imageCon)); michael@0: if (imageCon) { michael@0: nsRect dest(mPadding.left, mPadding.top, michael@0: mRect.width - (mPadding.left + mPadding.right), michael@0: mRect.height - (mPadding.top + mPadding.bottom)); michael@0: nsLayoutUtils::DrawSingleImage(&aRenderingContext, michael@0: imageCon, nsLayoutUtils::GetGraphicsFilterForFrame(this), michael@0: dest + aPt, aDirtyRect, nullptr, aFlags); michael@0: return; michael@0: } michael@0: } michael@0: } michael@0: michael@0: nsRefPtr fm; michael@0: aRenderingContext.SetColor(nsLayoutUtils::GetColor(this, eCSSProperty_color)); michael@0: michael@0: mTextIsRTL = false; michael@0: michael@0: nsAutoString text; michael@0: switch (listStyleType) { michael@0: case NS_STYLE_LIST_STYLE_NONE: michael@0: break; michael@0: michael@0: default: michael@0: case NS_STYLE_LIST_STYLE_DISC: michael@0: aRenderingContext.FillEllipse(mPadding.left + aPt.x, mPadding.top + aPt.y, michael@0: mRect.width - (mPadding.left + mPadding.right), michael@0: mRect.height - (mPadding.top + mPadding.bottom)); michael@0: break; michael@0: michael@0: case NS_STYLE_LIST_STYLE_CIRCLE: michael@0: aRenderingContext.DrawEllipse(mPadding.left + aPt.x, mPadding.top + aPt.y, michael@0: mRect.width - (mPadding.left + mPadding.right), michael@0: mRect.height - (mPadding.top + mPadding.bottom)); michael@0: break; michael@0: michael@0: case NS_STYLE_LIST_STYLE_SQUARE: michael@0: { michael@0: nsRect rect(aPt, mRect.Size()); michael@0: rect.Deflate(mPadding); michael@0: michael@0: // Snap the height and the width of the rectangle to device pixels, michael@0: // and then center the result within the original rectangle, so that michael@0: // all square bullets at the same font size have the same visual michael@0: // size (bug 376690). michael@0: // FIXME: We should really only do this if we're not transformed michael@0: // (like gfxContext::UserToDevicePixelSnapped does). michael@0: nsPresContext *pc = PresContext(); michael@0: nsRect snapRect(rect.x, rect.y, michael@0: pc->RoundAppUnitsToNearestDevPixels(rect.width), michael@0: pc->RoundAppUnitsToNearestDevPixels(rect.height)); michael@0: snapRect.MoveBy((rect.width - snapRect.width) / 2, michael@0: (rect.height - snapRect.height) / 2); michael@0: aRenderingContext.FillRect(snapRect.x, snapRect.y, michael@0: snapRect.width, snapRect.height); michael@0: } michael@0: break; michael@0: michael@0: case NS_STYLE_LIST_STYLE_DECIMAL: michael@0: case NS_STYLE_LIST_STYLE_DECIMAL_LEADING_ZERO: michael@0: case NS_STYLE_LIST_STYLE_CJK_DECIMAL: michael@0: case NS_STYLE_LIST_STYLE_LOWER_ROMAN: michael@0: case NS_STYLE_LIST_STYLE_UPPER_ROMAN: michael@0: case NS_STYLE_LIST_STYLE_LOWER_ALPHA: michael@0: case NS_STYLE_LIST_STYLE_UPPER_ALPHA: michael@0: case NS_STYLE_LIST_STYLE_LOWER_GREEK: michael@0: case NS_STYLE_LIST_STYLE_HEBREW: michael@0: case NS_STYLE_LIST_STYLE_ARMENIAN: michael@0: case NS_STYLE_LIST_STYLE_GEORGIAN: michael@0: case NS_STYLE_LIST_STYLE_CJK_IDEOGRAPHIC: michael@0: case NS_STYLE_LIST_STYLE_HIRAGANA: michael@0: case NS_STYLE_LIST_STYLE_KATAKANA: michael@0: case NS_STYLE_LIST_STYLE_HIRAGANA_IROHA: michael@0: case NS_STYLE_LIST_STYLE_KATAKANA_IROHA: michael@0: case NS_STYLE_LIST_STYLE_JAPANESE_INFORMAL: michael@0: case NS_STYLE_LIST_STYLE_JAPANESE_FORMAL: michael@0: case NS_STYLE_LIST_STYLE_KOREAN_HANGUL_FORMAL: michael@0: case NS_STYLE_LIST_STYLE_KOREAN_HANJA_INFORMAL: michael@0: case NS_STYLE_LIST_STYLE_KOREAN_HANJA_FORMAL: michael@0: case NS_STYLE_LIST_STYLE_SIMP_CHINESE_INFORMAL: michael@0: case NS_STYLE_LIST_STYLE_SIMP_CHINESE_FORMAL: michael@0: case NS_STYLE_LIST_STYLE_TRAD_CHINESE_INFORMAL: michael@0: case NS_STYLE_LIST_STYLE_TRAD_CHINESE_FORMAL: michael@0: case NS_STYLE_LIST_STYLE_MOZ_SIMP_CHINESE_INFORMAL: michael@0: case NS_STYLE_LIST_STYLE_MOZ_SIMP_CHINESE_FORMAL: michael@0: case NS_STYLE_LIST_STYLE_MOZ_TRAD_CHINESE_INFORMAL: michael@0: case NS_STYLE_LIST_STYLE_MOZ_TRAD_CHINESE_FORMAL: michael@0: case NS_STYLE_LIST_STYLE_MOZ_JAPANESE_INFORMAL: michael@0: case NS_STYLE_LIST_STYLE_MOZ_JAPANESE_FORMAL: michael@0: case NS_STYLE_LIST_STYLE_MOZ_CJK_HEAVENLY_STEM: michael@0: case NS_STYLE_LIST_STYLE_MOZ_CJK_EARTHLY_BRANCH: michael@0: case NS_STYLE_LIST_STYLE_MOZ_ARABIC_INDIC: michael@0: case NS_STYLE_LIST_STYLE_MOZ_PERSIAN: michael@0: case NS_STYLE_LIST_STYLE_MOZ_URDU: michael@0: case NS_STYLE_LIST_STYLE_MOZ_DEVANAGARI: michael@0: case NS_STYLE_LIST_STYLE_MOZ_GURMUKHI: michael@0: case NS_STYLE_LIST_STYLE_MOZ_GUJARATI: michael@0: case NS_STYLE_LIST_STYLE_MOZ_ORIYA: michael@0: case NS_STYLE_LIST_STYLE_MOZ_KANNADA: michael@0: case NS_STYLE_LIST_STYLE_MOZ_MALAYALAM: michael@0: case NS_STYLE_LIST_STYLE_MOZ_BENGALI: michael@0: case NS_STYLE_LIST_STYLE_MOZ_TAMIL: michael@0: case NS_STYLE_LIST_STYLE_MOZ_TELUGU: michael@0: case NS_STYLE_LIST_STYLE_MOZ_THAI: michael@0: case NS_STYLE_LIST_STYLE_MOZ_LAO: michael@0: case NS_STYLE_LIST_STYLE_MOZ_MYANMAR: michael@0: case NS_STYLE_LIST_STYLE_MOZ_KHMER: michael@0: case NS_STYLE_LIST_STYLE_MOZ_HANGUL: michael@0: case NS_STYLE_LIST_STYLE_MOZ_HANGUL_CONSONANT: michael@0: case NS_STYLE_LIST_STYLE_MOZ_ETHIOPIC_HALEHAME: michael@0: case NS_STYLE_LIST_STYLE_MOZ_ETHIOPIC_NUMERIC: michael@0: case NS_STYLE_LIST_STYLE_MOZ_ETHIOPIC_HALEHAME_AM: michael@0: case NS_STYLE_LIST_STYLE_MOZ_ETHIOPIC_HALEHAME_TI_ER: michael@0: case NS_STYLE_LIST_STYLE_MOZ_ETHIOPIC_HALEHAME_TI_ET: michael@0: nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm), michael@0: GetFontSizeInflation()); michael@0: GetListItemText(*myList, text); michael@0: aRenderingContext.SetFont(fm); michael@0: nscoord ascent = fm->MaxAscent(); michael@0: aRenderingContext.SetTextRunRTL(mTextIsRTL); michael@0: aRenderingContext.DrawString(text, mPadding.left + aPt.x, michael@0: mPadding.top + aPt.y + ascent); michael@0: break; michael@0: } michael@0: } michael@0: michael@0: int32_t michael@0: nsBulletFrame::SetListItemOrdinal(int32_t aNextOrdinal, michael@0: bool* aChanged, michael@0: int32_t aIncrement) michael@0: { michael@0: MOZ_ASSERT(aIncrement == 1 || aIncrement == -1, michael@0: "We shouldn't have weird increments here"); michael@0: michael@0: // Assume that the ordinal comes from the caller michael@0: int32_t oldOrdinal = mOrdinal; michael@0: mOrdinal = aNextOrdinal; michael@0: michael@0: // Try to get value directly from the list-item, if it specifies a michael@0: // value attribute. Note: we do this with our parent's content michael@0: // because our parent is the list-item. michael@0: nsIContent* parentContent = mParent->GetContent(); michael@0: if (parentContent) { michael@0: nsGenericHTMLElement *hc = michael@0: nsGenericHTMLElement::FromContent(parentContent); michael@0: if (hc) { michael@0: const nsAttrValue* attr = hc->GetParsedAttr(nsGkAtoms::value); michael@0: if (attr && attr->Type() == nsAttrValue::eInteger) { michael@0: // Use ordinal specified by the value attribute michael@0: mOrdinal = attr->GetIntegerValue(); michael@0: } michael@0: } michael@0: } michael@0: michael@0: *aChanged = oldOrdinal != mOrdinal; michael@0: michael@0: return nsCounterManager::IncrementCounter(mOrdinal, aIncrement); michael@0: } michael@0: michael@0: michael@0: // XXX change roman/alpha to use unsigned math so that maxint and michael@0: // maxnegint will work michael@0: michael@0: /** michael@0: * For all functions below, a return value of true means that we michael@0: * could represent mOrder in the desired numbering system. false michael@0: * means we had to fall back to decimal michael@0: */ michael@0: static bool DecimalToText(int32_t ordinal, nsString& result) michael@0: { michael@0: char cbuf[40]; michael@0: PR_snprintf(cbuf, sizeof(cbuf), "%ld", ordinal); michael@0: result.AppendASCII(cbuf); michael@0: return true; michael@0: } michael@0: static bool DecimalLeadingZeroToText(int32_t ordinal, nsString& result) michael@0: { michael@0: char cbuf[40]; michael@0: PR_snprintf(cbuf, sizeof(cbuf), "%02ld", ordinal); michael@0: result.AppendASCII(cbuf); michael@0: return true; michael@0: } michael@0: static bool OtherDecimalToText(int32_t ordinal, char16_t zeroChar, nsString& result) michael@0: { michael@0: char16_t diff = zeroChar - char16_t('0'); michael@0: // We're going to be appending to whatever is in "result" already, so make michael@0: // sure to only munge the new bits. Note that we can't just grab the pointer michael@0: // to the new stuff here, since appending to the string can realloc. michael@0: size_t offset = result.Length(); michael@0: DecimalToText(ordinal, result); michael@0: char16_t* p = result.BeginWriting() + offset; michael@0: if (ordinal < 0) { michael@0: // skip the leading '-' michael@0: ++p; michael@0: } michael@0: for(; '\0' != *p ; p++) michael@0: *p += diff; michael@0: return true; michael@0: } michael@0: static bool TamilToText(int32_t ordinal, nsString& result) michael@0: { michael@0: if (ordinal < 1 || ordinal > 9999) { michael@0: // Can't do those in this system. michael@0: return false; michael@0: } michael@0: char16_t diff = 0x0BE6 - char16_t('0'); michael@0: // We're going to be appending to whatever is in "result" already, so make michael@0: // sure to only munge the new bits. Note that we can't just grab the pointer michael@0: // to the new stuff here, since appending to the string can realloc. michael@0: size_t offset = result.Length(); michael@0: DecimalToText(ordinal, result); michael@0: char16_t* p = result.BeginWriting() + offset; michael@0: for(; '\0' != *p ; p++) michael@0: if(*p != char16_t('0')) michael@0: *p += diff; michael@0: return true; michael@0: } michael@0: michael@0: michael@0: static const char gLowerRomanCharsA[] = "ixcm"; michael@0: static const char gUpperRomanCharsA[] = "IXCM"; michael@0: static const char gLowerRomanCharsB[] = "vld"; michael@0: static const char gUpperRomanCharsB[] = "VLD"; michael@0: michael@0: static bool RomanToText(int32_t ordinal, nsString& result, const char* achars, const char* bchars) michael@0: { michael@0: if (ordinal < 1 || ordinal > 3999) { michael@0: return false; michael@0: } michael@0: nsAutoString addOn, decStr; michael@0: decStr.AppendInt(ordinal, 10); michael@0: int len = decStr.Length(); michael@0: const char16_t* dp = decStr.get(); michael@0: const char16_t* end = dp + len; michael@0: int romanPos = len; michael@0: int n; michael@0: michael@0: for (; dp < end; dp++) { michael@0: romanPos--; michael@0: addOn.SetLength(0); michael@0: switch(*dp) { michael@0: case '3': michael@0: addOn.Append(char16_t(achars[romanPos])); michael@0: // FALLTHROUGH michael@0: case '2': michael@0: addOn.Append(char16_t(achars[romanPos])); michael@0: // FALLTHROUGH michael@0: case '1': michael@0: addOn.Append(char16_t(achars[romanPos])); michael@0: break; michael@0: case '4': michael@0: addOn.Append(char16_t(achars[romanPos])); michael@0: // FALLTHROUGH michael@0: case '5': case '6': michael@0: case '7': case '8': michael@0: addOn.Append(char16_t(bchars[romanPos])); michael@0: for(n=0;'5'+n<*dp;n++) { michael@0: addOn.Append(char16_t(achars[romanPos])); michael@0: } michael@0: break; michael@0: case '9': michael@0: addOn.Append(char16_t(achars[romanPos])); michael@0: addOn.Append(char16_t(achars[romanPos+1])); michael@0: break; michael@0: default: michael@0: break; michael@0: } michael@0: result.Append(addOn); michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: #define ALPHA_SIZE 26 michael@0: static const char16_t gLowerAlphaChars[ALPHA_SIZE] = michael@0: { michael@0: 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, // A B C D E michael@0: 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, // F G H I J michael@0: 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, // K L M N O michael@0: 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, // P Q R S T michael@0: 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, // U V W X Y michael@0: 0x007A // Z michael@0: }; michael@0: michael@0: static const char16_t gUpperAlphaChars[ALPHA_SIZE] = michael@0: { michael@0: 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, // A B C D E michael@0: 0x0046, 0x0047, 0x0048, 0x0049, 0x004A, // F G H I J michael@0: 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, // K L M N O michael@0: 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, // P Q R S T michael@0: 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, // U V W X Y michael@0: 0x005A // Z michael@0: }; michael@0: michael@0: michael@0: #define KATAKANA_CHARS_SIZE 48 michael@0: // Page 94 Writing Systems of The World michael@0: // after modification by momoi michael@0: static const char16_t gKatakanaChars[KATAKANA_CHARS_SIZE] = michael@0: { michael@0: 0x30A2, 0x30A4, 0x30A6, 0x30A8, 0x30AA, // a i u e o michael@0: 0x30AB, 0x30AD, 0x30AF, 0x30B1, 0x30B3, // ka ki ku ke ko michael@0: 0x30B5, 0x30B7, 0x30B9, 0x30BB, 0x30BD, // sa shi su se so michael@0: 0x30BF, 0x30C1, 0x30C4, 0x30C6, 0x30C8, // ta chi tsu te to michael@0: 0x30CA, 0x30CB, 0x30CC, 0x30CD, 0x30CE, // na ni nu ne no michael@0: 0x30CF, 0x30D2, 0x30D5, 0x30D8, 0x30DB, // ha hi hu he ho michael@0: 0x30DE, 0x30DF, 0x30E0, 0x30E1, 0x30E2, // ma mi mu me mo michael@0: 0x30E4, 0x30E6, 0x30E8, // ya yu yo michael@0: 0x30E9, 0x30EA, 0x30EB, 0x30EC, 0x30ED, // ra ri ru re ro michael@0: 0x30EF, 0x30F0, 0x30F1, 0x30F2, // wa (w)i (w)e (w)o michael@0: 0x30F3 // n michael@0: }; michael@0: michael@0: #define HIRAGANA_CHARS_SIZE 48 michael@0: static const char16_t gHiraganaChars[HIRAGANA_CHARS_SIZE] = michael@0: { michael@0: 0x3042, 0x3044, 0x3046, 0x3048, 0x304A, // a i u e o michael@0: 0x304B, 0x304D, 0x304F, 0x3051, 0x3053, // ka ki ku ke ko michael@0: 0x3055, 0x3057, 0x3059, 0x305B, 0x305D, // sa shi su se so michael@0: 0x305F, 0x3061, 0x3064, 0x3066, 0x3068, // ta chi tsu te to michael@0: 0x306A, 0x306B, 0x306C, 0x306D, 0x306E, // na ni nu ne no michael@0: 0x306F, 0x3072, 0x3075, 0x3078, 0x307B, // ha hi hu he ho michael@0: 0x307E, 0x307F, 0x3080, 0x3081, 0x3082, // ma mi mu me mo michael@0: 0x3084, 0x3086, 0x3088, // ya yu yo michael@0: 0x3089, 0x308A, 0x308B, 0x308C, 0x308D, // ra ri ru re ro michael@0: 0x308F, 0x3090, 0x3091, 0x3092, // wa (w)i (w)e (w)o michael@0: 0x3093 // n michael@0: }; michael@0: michael@0: michael@0: #define HIRAGANA_IROHA_CHARS_SIZE 47 michael@0: // Page 94 Writing Systems of The World michael@0: static const char16_t gHiraganaIrohaChars[HIRAGANA_IROHA_CHARS_SIZE] = michael@0: { michael@0: 0x3044, 0x308D, 0x306F, 0x306B, 0x307B, // i ro ha ni ho michael@0: 0x3078, 0x3068, 0x3061, 0x308A, 0x306C, // he to chi ri nu michael@0: 0x308B, 0x3092, 0x308F, 0x304B, 0x3088, // ru (w)o wa ka yo michael@0: 0x305F, 0x308C, 0x305D, 0x3064, 0x306D, // ta re so tsu ne michael@0: 0x306A, 0x3089, 0x3080, 0x3046, 0x3090, // na ra mu u (w)i michael@0: 0x306E, 0x304A, 0x304F, 0x3084, 0x307E, // no o ku ya ma michael@0: 0x3051, 0x3075, 0x3053, 0x3048, 0x3066, // ke hu ko e te michael@0: 0x3042, 0x3055, 0x304D, 0x3086, 0x3081, // a sa ki yu me michael@0: 0x307F, 0x3057, 0x3091, 0x3072, 0x3082, // mi shi (w)e hi mo michael@0: 0x305B, 0x3059 // se su michael@0: }; michael@0: michael@0: #define KATAKANA_IROHA_CHARS_SIZE 47 michael@0: static const char16_t gKatakanaIrohaChars[KATAKANA_IROHA_CHARS_SIZE] = michael@0: { michael@0: 0x30A4, 0x30ED, 0x30CF, 0x30CB, 0x30DB, // i ro ha ni ho michael@0: 0x30D8, 0x30C8, 0x30C1, 0x30EA, 0x30CC, // he to chi ri nu michael@0: 0x30EB, 0x30F2, 0x30EF, 0x30AB, 0x30E8, // ru (w)o wa ka yo michael@0: 0x30BF, 0x30EC, 0x30BD, 0x30C4, 0x30CD, // ta re so tsu ne michael@0: 0x30CA, 0x30E9, 0x30E0, 0x30A6, 0x30F0, // na ra mu u (w)i michael@0: 0x30CE, 0x30AA, 0x30AF, 0x30E4, 0x30DE, // no o ku ya ma michael@0: 0x30B1, 0x30D5, 0x30B3, 0x30A8, 0x30C6, // ke hu ko e te michael@0: 0x30A2, 0x30B5, 0x30AD, 0x30E6, 0x30E1, // a sa ki yu me michael@0: 0x30DF, 0x30B7, 0x30F1, 0x30D2, 0x30E2, // mi shi (w)e hi mo michael@0: 0x30BB, 0x30B9 // se su michael@0: }; michael@0: michael@0: #define LOWER_GREEK_CHARS_SIZE 24 michael@0: // Note: 0x03C2 GREEK FINAL SIGMA is not used in here.... michael@0: static const char16_t gLowerGreekChars[LOWER_GREEK_CHARS_SIZE] = michael@0: { michael@0: 0x03B1, 0x03B2, 0x03B3, 0x03B4, 0x03B5, // alpha beta gamma delta epsilon michael@0: 0x03B6, 0x03B7, 0x03B8, 0x03B9, 0x03BA, // zeta eta theta iota kappa michael@0: 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF, // lamda mu nu xi omicron michael@0: 0x03C0, 0x03C1, 0x03C3, 0x03C4, 0x03C5, // pi rho sigma tau upsilon michael@0: 0x03C6, 0x03C7, 0x03C8, 0x03C9 // phi chi psi omega michael@0: }; michael@0: michael@0: #define CJK_HEAVENLY_STEM_CHARS_SIZE 10 michael@0: static const char16_t gCJKHeavenlyStemChars[CJK_HEAVENLY_STEM_CHARS_SIZE] = michael@0: { michael@0: 0x7532, 0x4e59, 0x4e19, 0x4e01, 0x620a, michael@0: 0x5df1, 0x5e9a, 0x8f9b, 0x58ec, 0x7678 michael@0: }; michael@0: #define CJK_EARTHLY_BRANCH_CHARS_SIZE 12 michael@0: static const char16_t gCJKEarthlyBranchChars[CJK_EARTHLY_BRANCH_CHARS_SIZE] = michael@0: { michael@0: 0x5b50, 0x4e11, 0x5bc5, 0x536f, 0x8fb0, 0x5df3, michael@0: 0x5348, 0x672a, 0x7533, 0x9149, 0x620c, 0x4ea5 michael@0: }; michael@0: #define HANGUL_CHARS_SIZE 14 michael@0: static const char16_t gHangulChars[HANGUL_CHARS_SIZE] = michael@0: { michael@0: 0xac00, 0xb098, 0xb2e4, 0xb77c, 0xb9c8, 0xbc14, michael@0: 0xc0ac, 0xc544, 0xc790, 0xcc28, 0xce74, 0xd0c0, michael@0: 0xd30c, 0xd558 michael@0: }; michael@0: #define HANGUL_CONSONANT_CHARS_SIZE 14 michael@0: static const char16_t gHangulConsonantChars[HANGUL_CONSONANT_CHARS_SIZE] = michael@0: { michael@0: 0x3131, 0x3134, 0x3137, 0x3139, 0x3141, 0x3142, michael@0: 0x3145, 0x3147, 0x3148, 0x314a, 0x314b, 0x314c, michael@0: 0x314d, 0x314e michael@0: }; michael@0: michael@0: // Ge'ez set of Ethiopic ordered list. There are other locale-dependent sets. michael@0: // For the time being, let's implement two Ge'ez sets only michael@0: // per Momoi san's suggestion in bug 102252. michael@0: // For details, refer to http://www.ethiopic.org/Collation/OrderedLists.html. michael@0: #define ETHIOPIC_HALEHAME_CHARS_SIZE 26 michael@0: static const char16_t gEthiopicHalehameChars[ETHIOPIC_HALEHAME_CHARS_SIZE] = michael@0: { michael@0: 0x1200, 0x1208, 0x1210, 0x1218, 0x1220, 0x1228, michael@0: 0x1230, 0x1240, 0x1260, 0x1270, 0x1280, 0x1290, michael@0: 0x12a0, 0x12a8, 0x12c8, 0x12d0, 0x12d8, 0x12e8, michael@0: 0x12f0, 0x1308, 0x1320, 0x1330, 0x1338, 0x1340, michael@0: 0x1348, 0x1350 michael@0: }; michael@0: #define ETHIOPIC_HALEHAME_AM_CHARS_SIZE 33 michael@0: static const char16_t gEthiopicHalehameAmChars[ETHIOPIC_HALEHAME_AM_CHARS_SIZE] = michael@0: { michael@0: 0x1200, 0x1208, 0x1210, 0x1218, 0x1220, 0x1228, michael@0: 0x1230, 0x1238, 0x1240, 0x1260, 0x1270, 0x1278, michael@0: 0x1280, 0x1290, 0x1298, 0x12a0, 0x12a8, 0x12b8, michael@0: 0x12c8, 0x12d0, 0x12d8, 0x12e0, 0x12e8, 0x12f0, michael@0: 0x1300, 0x1308, 0x1320, 0x1328, 0x1330, 0x1338, michael@0: 0x1340, 0x1348, 0x1350 michael@0: }; michael@0: #define ETHIOPIC_HALEHAME_TI_ER_CHARS_SIZE 31 michael@0: static const char16_t gEthiopicHalehameTiErChars[ETHIOPIC_HALEHAME_TI_ER_CHARS_SIZE] = michael@0: { michael@0: 0x1200, 0x1208, 0x1210, 0x1218, 0x1228, 0x1230, michael@0: 0x1238, 0x1240, 0x1250, 0x1260, 0x1270, 0x1278, michael@0: 0x1290, 0x1298, 0x12a0, 0x12a8, 0x12b8, 0x12c8, michael@0: 0x12d0, 0x12d8, 0x12e0, 0x12e8, 0x12f0, 0x1300, michael@0: 0x1308, 0x1320, 0x1328, 0x1330, 0x1338, 0x1348, michael@0: 0x1350 michael@0: }; michael@0: #define ETHIOPIC_HALEHAME_TI_ET_CHARS_SIZE 34 michael@0: static const char16_t gEthiopicHalehameTiEtChars[ETHIOPIC_HALEHAME_TI_ET_CHARS_SIZE] = michael@0: { michael@0: 0x1200, 0x1208, 0x1210, 0x1218, 0x1220, 0x1228, michael@0: 0x1230, 0x1238, 0x1240, 0x1250, 0x1260, 0x1270, michael@0: 0x1278, 0x1280, 0x1290, 0x1298, 0x12a0, 0x12a8, michael@0: 0x12b8, 0x12c8, 0x12d0, 0x12d8, 0x12e0, 0x12e8, michael@0: 0x12f0, 0x1300, 0x1308, 0x1320, 0x1328, 0x1330, michael@0: 0x1338, 0x1340, 0x1348, 0x1350 michael@0: }; michael@0: michael@0: michael@0: // We know cjk-ideographic need 31 characters to display 99,999,999,999,999,999 michael@0: // georgian needs 6 at most michael@0: // armenian needs 12 at most michael@0: // hebrew may need more... michael@0: michael@0: #define NUM_BUF_SIZE 34 michael@0: michael@0: static bool CharListToText(int32_t ordinal, nsString& result, const char16_t* chars, int32_t aBase) michael@0: { michael@0: char16_t buf[NUM_BUF_SIZE]; michael@0: int32_t idx = NUM_BUF_SIZE; michael@0: if (ordinal < 1) { michael@0: return false; michael@0: } michael@0: do { michael@0: ordinal--; // a == 0 michael@0: int32_t cur = ordinal % aBase; michael@0: buf[--idx] = chars[cur]; michael@0: ordinal /= aBase ; michael@0: } while ( ordinal > 0); michael@0: result.Append(buf+idx,NUM_BUF_SIZE-idx); michael@0: return true; michael@0: } michael@0: michael@0: static const char16_t gCJKDecimalChars[10] = michael@0: { michael@0: 0x3007, 0x4e00, 0x4e8c, 0x4e09, 0x56db, michael@0: 0x4e94, 0x516d, 0x4e03, 0x516b, 0x4e5d michael@0: }; michael@0: static bool CharListDecimalToText(int32_t ordinal, nsString& result, const char16_t* chars) michael@0: { michael@0: if (ordinal < 0) { michael@0: return false; michael@0: } michael@0: char16_t buf[NUM_BUF_SIZE]; michael@0: int32_t idx = NUM_BUF_SIZE; michael@0: do { michael@0: buf[--idx] = chars[ordinal % 10]; michael@0: ordinal /= 10; michael@0: } while (ordinal > 0); michael@0: result.Append(buf + idx, NUM_BUF_SIZE - idx); michael@0: return true; michael@0: } michael@0: michael@0: enum CJKIdeographicLang { michael@0: CHINESE, KOREAN, JAPANESE michael@0: }; michael@0: struct CJKIdeographicData { michael@0: const char16_t *negative; michael@0: char16_t digit[10]; michael@0: char16_t unit[3]; michael@0: char16_t unit10K[2]; michael@0: uint8_t lang; michael@0: bool informal; michael@0: }; michael@0: static const char16_t gJapaneseNegative[] = { michael@0: 0x30de, 0x30a4, 0x30ca, 0x30b9, 0x0000 michael@0: }; michael@0: static const CJKIdeographicData gDataJapaneseInformal = { michael@0: gJapaneseNegative, // negative michael@0: { // digit michael@0: 0x3007, 0x4e00, 0x4e8c, 0x4e09, 0x56db, michael@0: 0x4e94, 0x516d, 0x4e03, 0x516b, 0x4e5d michael@0: }, michael@0: { 0x5341, 0x767e, 0x5343 }, // unit michael@0: { 0x4e07, 0x5104 }, // unit10K michael@0: JAPANESE, // lang michael@0: true // informal michael@0: }; michael@0: static const CJKIdeographicData gDataJapaneseFormal = { michael@0: gJapaneseNegative, // negative michael@0: { // digit michael@0: 0x96f6, 0x58f1, 0x5f10, 0x53c2, 0x56db, michael@0: 0x4f0d, 0x516d, 0x4e03, 0x516b, 0x4e5d michael@0: }, michael@0: { 0x62fe, 0x767e, 0x9621 }, // unit michael@0: { 0x842c, 0x5104 }, // unit10K michael@0: JAPANESE, // lang michael@0: false // informal michael@0: }; michael@0: static const char16_t gKoreanNegative[] = { michael@0: 0xb9c8, 0xc774, 0xb108, 0xc2a4, 0x0020, 0x0000 michael@0: }; michael@0: static const CJKIdeographicData gDataKoreanHangulFormal = { michael@0: gKoreanNegative, // negative michael@0: { // digit michael@0: 0xc601, 0xc77c, 0xc774, 0xc0bc, 0xc0ac, michael@0: 0xc624, 0xc721, 0xce60, 0xd314, 0xad6c michael@0: }, michael@0: { 0xc2ed, 0xbc31, 0xcc9c }, // unit michael@0: { 0xb9cc, 0xc5b5 }, // unit10K michael@0: KOREAN, // lang michael@0: false // informal michael@0: }; michael@0: static const CJKIdeographicData gDataKoreanHanjaInformal = { michael@0: gKoreanNegative, // negative michael@0: { // digit michael@0: 0x96f6, 0x4e00, 0x4e8c, 0x4e09, 0x56db, michael@0: 0x4e94, 0x516d, 0x4e03, 0x516b, 0x4e5d michael@0: }, michael@0: { 0x5341, 0x767e, 0x5343 }, // unit michael@0: { 0x842c, 0x5104 }, // unit10K michael@0: KOREAN, // lang michael@0: true // informal michael@0: }; michael@0: static const CJKIdeographicData gDataKoreanHanjaFormal = { michael@0: gKoreanNegative, // negative michael@0: { // digit michael@0: 0x96f6, 0x58f9, 0x8cb3, 0x53c3, 0x56db, michael@0: 0x4e94, 0x516d, 0x4e03, 0x516b, 0x4e5d michael@0: }, michael@0: { 0x62fe, 0x767e, 0x4edf }, // unit michael@0: { 0x842c, 0x5104 }, // unit10K michael@0: KOREAN, // lang michael@0: false // informal michael@0: }; michael@0: static const char16_t gSimpChineseNegative[] = { michael@0: 0x8d1f, 0x0000 michael@0: }; michael@0: static const CJKIdeographicData gDataSimpChineseInformal = { michael@0: gSimpChineseNegative, // negative michael@0: { // digit michael@0: 0x96f6, 0x4e00, 0x4e8c, 0x4e09, 0x56db, michael@0: 0x4e94, 0x516d, 0x4e03, 0x516b, 0x4e5d michael@0: }, michael@0: { 0x5341, 0x767e, 0x5343 }, // unit michael@0: { 0x4e07, 0x4ebf }, // unit10K michael@0: CHINESE, // lang michael@0: true // informal michael@0: }; michael@0: static const CJKIdeographicData gDataSimpChineseFormal = { michael@0: gSimpChineseNegative, // negative michael@0: { // digit michael@0: 0x96f6, 0x58f9, 0x8d30, 0x53c1, 0x8086, michael@0: 0x4f0d, 0x9646, 0x67d2, 0x634c, 0x7396 michael@0: }, michael@0: { 0x62fe, 0x4f70, 0x4edf }, // unit michael@0: { 0x4e07, 0x4ebf }, // unit10K michael@0: CHINESE, // lang michael@0: false // informal michael@0: }; michael@0: static const char16_t gTradChineseNegative[] = { michael@0: 0x8ca0, 0x0000 michael@0: }; michael@0: static const CJKIdeographicData gDataTradChineseInformal = { michael@0: gTradChineseNegative, // negative michael@0: { // digit michael@0: 0x96f6, 0x4e00, 0x4e8c, 0x4e09, 0x56db, michael@0: 0x4e94, 0x516d, 0x4e03, 0x516b, 0x4e5d michael@0: }, michael@0: { 0x5341, 0x767e, 0x5343 }, // unit michael@0: { 0x842c, 0x5104 }, // unit10K michael@0: CHINESE, // lang michael@0: true // informal michael@0: }; michael@0: static const CJKIdeographicData gDataTradChineseFormal = { michael@0: gTradChineseNegative, // negative michael@0: { // digit michael@0: 0x96f6, 0x58f9, 0x8cb3, 0x53c3, 0x8086, michael@0: 0x4f0d, 0x9678, 0x67d2, 0x634c, 0x7396 michael@0: }, michael@0: { 0x62fe, 0x4f70, 0x4edf }, // unit michael@0: { 0x842c, 0x5104 }, // unit10K michael@0: CHINESE, // lang michael@0: false // informal michael@0: }; michael@0: michael@0: static const bool CJKIdeographicToText(int32_t aOrdinal, nsString& result, michael@0: const CJKIdeographicData& data) michael@0: { michael@0: char16_t buf[NUM_BUF_SIZE]; michael@0: int32_t idx = NUM_BUF_SIZE; michael@0: int32_t pos = 0; michael@0: bool isNegative = (aOrdinal < 0); michael@0: bool needZero = (aOrdinal == 0); michael@0: int32_t unitidx = 0, unit10Kidx = 0; michael@0: uint32_t ordinal = mozilla::Abs(aOrdinal); michael@0: do { michael@0: unitidx = pos % 4; michael@0: if (unitidx == 0) { michael@0: unit10Kidx = pos / 4; michael@0: } michael@0: int32_t cur = ordinal % 10; michael@0: if (cur == 0) { michael@0: if (needZero) { michael@0: needZero = false; michael@0: buf[--idx] = data.digit[0]; michael@0: } michael@0: } else { michael@0: if (data.lang == CHINESE) { michael@0: needZero = true; michael@0: } michael@0: if (unit10Kidx != 0) { michael@0: if (data.lang == KOREAN && idx != NUM_BUF_SIZE) { michael@0: buf[--idx] = ' '; michael@0: } michael@0: buf[--idx] = data.unit10K[unit10Kidx - 1]; michael@0: } michael@0: if (unitidx != 0) { michael@0: buf[--idx] = data.unit[unitidx - 1]; michael@0: } michael@0: if (cur != 1) { michael@0: buf[--idx] = data.digit[cur]; michael@0: } else { michael@0: bool needOne = true; michael@0: if (data.informal) { michael@0: switch (data.lang) { michael@0: case CHINESE: michael@0: if (unitidx == 1 && michael@0: (ordinal == 1 || (pos > 4 && ordinal % 1000 == 1))) { michael@0: needOne = false; michael@0: } michael@0: break; michael@0: case JAPANESE: michael@0: if (unitidx > 0 && michael@0: (unitidx != 3 || (pos == 3 && ordinal == 1))) { michael@0: needOne = false; michael@0: } michael@0: break; michael@0: case KOREAN: michael@0: if (unitidx > 0 || (pos == 4 && (ordinal % 1000) == 1)) { michael@0: needOne = false; michael@0: } michael@0: break; michael@0: } michael@0: } michael@0: if (needOne) { michael@0: buf[--idx] = data.digit[1]; michael@0: } michael@0: } michael@0: unit10Kidx = 0; michael@0: } michael@0: ordinal /= 10; michael@0: pos++; michael@0: } while (ordinal > 0); michael@0: if (isNegative) { michael@0: result.Append(data.negative); michael@0: } michael@0: result.Append(buf + idx, NUM_BUF_SIZE - idx); michael@0: return true; michael@0: } michael@0: michael@0: #define HEBREW_GERESH 0x05F3 michael@0: static const char16_t gHebrewDigit[22] = michael@0: { michael@0: // 1 2 3 4 5 6 7 8 9 michael@0: 0x05D0, 0x05D1, 0x05D2, 0x05D3, 0x05D4, 0x05D5, 0x05D6, 0x05D7, 0x05D8, michael@0: // 10 20 30 40 50 60 70 80 90 michael@0: 0x05D9, 0x05DB, 0x05DC, 0x05DE, 0x05E0, 0x05E1, 0x05E2, 0x05E4, 0x05E6, michael@0: // 100 200 300 400 michael@0: 0x05E7, 0x05E8, 0x05E9, 0x05EA michael@0: }; michael@0: michael@0: static bool HebrewToText(int32_t ordinal, nsString& result) michael@0: { michael@0: if (ordinal < 1 || ordinal > 999999) { michael@0: return false; michael@0: } michael@0: bool outputSep = false; michael@0: nsAutoString allText, thousandsGroup; michael@0: do { michael@0: thousandsGroup.Truncate(); michael@0: int32_t n3 = ordinal % 1000; michael@0: // Process digit for 100 - 900 michael@0: for(int32_t n1 = 400; n1 > 0; ) michael@0: { michael@0: if( n3 >= n1) michael@0: { michael@0: n3 -= n1; michael@0: thousandsGroup.Append(gHebrewDigit[(n1/100)-1+18]); michael@0: } else { michael@0: n1 -= 100; michael@0: } // if michael@0: } // for michael@0: michael@0: // Process digit for 10 - 90 michael@0: int32_t n2; michael@0: if( n3 >= 10 ) michael@0: { michael@0: // Special process for 15 and 16 michael@0: if(( 15 == n3 ) || (16 == n3)) { michael@0: // Special rule for religious reason... michael@0: // 15 is represented by 9 and 6, not 10 and 5 michael@0: // 16 is represented by 9 and 7, not 10 and 6 michael@0: n2 = 9; michael@0: thousandsGroup.Append(gHebrewDigit[ n2 - 1]); michael@0: } else { michael@0: n2 = n3 - (n3 % 10); michael@0: thousandsGroup.Append(gHebrewDigit[(n2/10)-1+9]); michael@0: } // if michael@0: n3 -= n2; michael@0: } // if michael@0: michael@0: // Process digit for 1 - 9 michael@0: if ( n3 > 0) michael@0: thousandsGroup.Append(gHebrewDigit[n3-1]); michael@0: if (outputSep) michael@0: thousandsGroup.Append((char16_t)HEBREW_GERESH); michael@0: if (allText.IsEmpty()) michael@0: allText = thousandsGroup; michael@0: else michael@0: allText = thousandsGroup + allText; michael@0: ordinal /= 1000; michael@0: outputSep = true; michael@0: } while (ordinal >= 1); michael@0: michael@0: result.Append(allText); michael@0: return true; michael@0: } michael@0: michael@0: michael@0: static bool ArmenianToText(int32_t ordinal, nsString& result) michael@0: { michael@0: if (ordinal < 1 || ordinal > 9999) { // zero or reach the limit of Armenian numbering system michael@0: return false; michael@0: } michael@0: michael@0: char16_t buf[NUM_BUF_SIZE]; michael@0: int32_t idx = NUM_BUF_SIZE; michael@0: int32_t d = 0; michael@0: do { michael@0: int32_t cur = ordinal % 10; michael@0: if (cur > 0) michael@0: { michael@0: char16_t u = 0x0530 + (d * 9) + cur; michael@0: buf[--idx] = u; michael@0: } michael@0: ++d; michael@0: ordinal /= 10; michael@0: } while (ordinal > 0); michael@0: result.Append(buf + idx, NUM_BUF_SIZE - idx); michael@0: return true; michael@0: } michael@0: michael@0: michael@0: static const char16_t gGeorgianValue [ 37 ] = { // 4 * 9 + 1 = 37 michael@0: // 1 2 3 4 5 6 7 8 9 michael@0: 0x10D0, 0x10D1, 0x10D2, 0x10D3, 0x10D4, 0x10D5, 0x10D6, 0x10F1, 0x10D7, michael@0: // 10 20 30 40 50 60 70 80 90 michael@0: 0x10D8, 0x10D9, 0x10DA, 0x10DB, 0x10DC, 0x10F2, 0x10DD, 0x10DE, 0x10DF, michael@0: // 100 200 300 400 500 600 700 800 900 michael@0: 0x10E0, 0x10E1, 0x10E2, 0x10F3, 0x10E4, 0x10E5, 0x10E6, 0x10E7, 0x10E8, michael@0: // 1000 2000 3000 4000 5000 6000 7000 8000 9000 michael@0: 0x10E9, 0x10EA, 0x10EB, 0x10EC, 0x10ED, 0x10EE, 0x10F4, 0x10EF, 0x10F0, michael@0: // 10000 michael@0: 0x10F5 michael@0: }; michael@0: static bool GeorgianToText(int32_t ordinal, nsString& result) michael@0: { michael@0: if (ordinal < 1 || ordinal > 19999) { // zero or reach the limit of Georgian numbering system michael@0: return false; michael@0: } michael@0: michael@0: char16_t buf[NUM_BUF_SIZE]; michael@0: int32_t idx = NUM_BUF_SIZE; michael@0: int32_t d = 0; michael@0: do { michael@0: int32_t cur = ordinal % 10; michael@0: if (cur > 0) michael@0: { michael@0: char16_t u = gGeorgianValue[(d * 9 ) + ( cur - 1)]; michael@0: buf[--idx] = u; michael@0: } michael@0: ++d; michael@0: ordinal /= 10; michael@0: } while (ordinal > 0); michael@0: result.Append(buf + idx, NUM_BUF_SIZE - idx); michael@0: return true; michael@0: } michael@0: michael@0: // Convert ordinal to Ethiopic numeric representation. michael@0: // The detail is available at http://www.ethiopic.org/Numerals/ michael@0: // The algorithm used here is based on the pseudo-code put up there by michael@0: // Daniel Yacob . michael@0: // Another reference is Unicode 3.0 standard section 11.1. michael@0: #define ETHIOPIC_ONE 0x1369 michael@0: #define ETHIOPIC_TEN 0x1372 michael@0: #define ETHIOPIC_HUNDRED 0x137B michael@0: #define ETHIOPIC_TEN_THOUSAND 0x137C michael@0: michael@0: static bool EthiopicToText(int32_t ordinal, nsString& result) michael@0: { michael@0: if (ordinal < 1) { michael@0: return false; michael@0: } michael@0: nsAutoString asciiNumberString; // decimal string representation of ordinal michael@0: DecimalToText(ordinal, asciiNumberString); michael@0: uint8_t asciiStringLength = asciiNumberString.Length(); michael@0: michael@0: // If number length is odd, add a leading "0" michael@0: // the leading "0" preconditions the string to always have the michael@0: // leading tens place populated, this avoids a check within the loop. michael@0: // If we didn't add the leading "0", decrement asciiStringLength so michael@0: // it will be equivalent to a zero-based index in both cases. michael@0: if (asciiStringLength & 1) { michael@0: asciiNumberString.Insert(NS_LITERAL_STRING("0"), 0); michael@0: } else { michael@0: asciiStringLength--; michael@0: } michael@0: michael@0: // Iterate from the highest digits to lowest michael@0: // indexFromLeft indexes digits (0 = most significant) michael@0: // groupIndexFromRight indexes pairs of digits (0 = least significant) michael@0: for (uint8_t indexFromLeft = 0, groupIndexFromRight = asciiStringLength >> 1; michael@0: indexFromLeft <= asciiStringLength; michael@0: indexFromLeft += 2, groupIndexFromRight--) { michael@0: uint8_t tensValue = asciiNumberString.CharAt(indexFromLeft) & 0x0F; michael@0: uint8_t unitsValue = asciiNumberString.CharAt(indexFromLeft + 1) & 0x0F; michael@0: uint8_t groupValue = tensValue * 10 + unitsValue; michael@0: michael@0: bool oddGroup = (groupIndexFromRight & 1); michael@0: michael@0: // we want to clear ETHIOPIC_ONE when it is superfluous michael@0: if (ordinal > 1 && michael@0: groupValue == 1 && // one without a leading ten michael@0: (oddGroup || indexFromLeft == 0)) { // preceding (100) or leading the sequence michael@0: unitsValue = 0; michael@0: } michael@0: michael@0: // put it all together... michael@0: if (tensValue) { michael@0: // map onto Ethiopic "tens": michael@0: result.Append((char16_t) (tensValue + ETHIOPIC_TEN - 1)); michael@0: } michael@0: if (unitsValue) { michael@0: //map onto Ethiopic "units": michael@0: result.Append((char16_t) (unitsValue + ETHIOPIC_ONE - 1)); michael@0: } michael@0: // Add a separator for all even groups except the last, michael@0: // and for odd groups with non-zero value. michael@0: if (oddGroup) { michael@0: if (groupValue) { michael@0: result.Append((char16_t) ETHIOPIC_HUNDRED); michael@0: } michael@0: } else { michael@0: if (groupIndexFromRight) { michael@0: result.Append((char16_t) ETHIOPIC_TEN_THOUSAND); michael@0: } michael@0: } michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: michael@0: /* static */ void michael@0: nsBulletFrame::AppendCounterText(int32_t aListStyleType, michael@0: int32_t aOrdinal, michael@0: nsString& result, michael@0: bool& isRTL) michael@0: { michael@0: bool success = true; michael@0: int32_t fallback = NS_STYLE_LIST_STYLE_DECIMAL; michael@0: isRTL = false; michael@0: michael@0: switch (aListStyleType) { michael@0: case NS_STYLE_LIST_STYLE_NONE: // used by counters code only michael@0: break; michael@0: michael@0: case NS_STYLE_LIST_STYLE_DISC: // used by counters code only michael@0: // XXX We really need to do this the same way we do list bullets. michael@0: result.Append(char16_t(0x2022)); michael@0: break; michael@0: michael@0: case NS_STYLE_LIST_STYLE_CIRCLE: // used by counters code only michael@0: // XXX We really need to do this the same way we do list bullets. michael@0: result.Append(char16_t(0x25E6)); michael@0: break; michael@0: michael@0: case NS_STYLE_LIST_STYLE_SQUARE: // used by counters code only michael@0: // XXX We really need to do this the same way we do list bullets. michael@0: result.Append(char16_t(0x25FE)); michael@0: break; michael@0: michael@0: case NS_STYLE_LIST_STYLE_DECIMAL: michael@0: default: // CSS2 say "A users agent that does not recognize a numbering system michael@0: // should use 'decimal' michael@0: success = DecimalToText(aOrdinal, result); michael@0: NS_ASSERTION(success, "DecimalToText must never fail"); michael@0: break; michael@0: michael@0: case NS_STYLE_LIST_STYLE_DECIMAL_LEADING_ZERO: michael@0: success = DecimalLeadingZeroToText(aOrdinal, result); michael@0: break; michael@0: michael@0: case NS_STYLE_LIST_STYLE_CJK_DECIMAL: michael@0: success = CharListDecimalToText(aOrdinal, result, gCJKDecimalChars); michael@0: break; michael@0: michael@0: case NS_STYLE_LIST_STYLE_LOWER_ROMAN: michael@0: success = RomanToText(aOrdinal, result, michael@0: gLowerRomanCharsA, gLowerRomanCharsB); michael@0: break; michael@0: case NS_STYLE_LIST_STYLE_UPPER_ROMAN: michael@0: success = RomanToText(aOrdinal, result, michael@0: gUpperRomanCharsA, gUpperRomanCharsB); michael@0: break; michael@0: michael@0: case NS_STYLE_LIST_STYLE_LOWER_ALPHA: michael@0: success = CharListToText(aOrdinal, result, gLowerAlphaChars, ALPHA_SIZE); michael@0: break; michael@0: michael@0: case NS_STYLE_LIST_STYLE_UPPER_ALPHA: michael@0: success = CharListToText(aOrdinal, result, gUpperAlphaChars, ALPHA_SIZE); michael@0: break; michael@0: michael@0: case NS_STYLE_LIST_STYLE_KATAKANA: michael@0: success = CharListToText(aOrdinal, result, gKatakanaChars, michael@0: KATAKANA_CHARS_SIZE); michael@0: break; michael@0: michael@0: case NS_STYLE_LIST_STYLE_HIRAGANA: michael@0: success = CharListToText(aOrdinal, result, gHiraganaChars, michael@0: HIRAGANA_CHARS_SIZE); michael@0: break; michael@0: michael@0: case NS_STYLE_LIST_STYLE_KATAKANA_IROHA: michael@0: success = CharListToText(aOrdinal, result, gKatakanaIrohaChars, michael@0: KATAKANA_IROHA_CHARS_SIZE); michael@0: break; michael@0: michael@0: case NS_STYLE_LIST_STYLE_HIRAGANA_IROHA: michael@0: success = CharListToText(aOrdinal, result, gHiraganaIrohaChars, michael@0: HIRAGANA_IROHA_CHARS_SIZE); michael@0: break; michael@0: michael@0: case NS_STYLE_LIST_STYLE_LOWER_GREEK: michael@0: success = CharListToText(aOrdinal, result, gLowerGreekChars , michael@0: LOWER_GREEK_CHARS_SIZE); michael@0: break; michael@0: michael@0: case NS_STYLE_LIST_STYLE_CJK_IDEOGRAPHIC: michael@0: case NS_STYLE_LIST_STYLE_TRAD_CHINESE_INFORMAL: michael@0: case NS_STYLE_LIST_STYLE_MOZ_TRAD_CHINESE_INFORMAL: michael@0: fallback = NS_STYLE_LIST_STYLE_CJK_DECIMAL; michael@0: success = michael@0: CJKIdeographicToText(aOrdinal, result, gDataTradChineseInformal); michael@0: break; michael@0: michael@0: case NS_STYLE_LIST_STYLE_TRAD_CHINESE_FORMAL: michael@0: case NS_STYLE_LIST_STYLE_MOZ_TRAD_CHINESE_FORMAL: michael@0: fallback = NS_STYLE_LIST_STYLE_CJK_DECIMAL; michael@0: success = CJKIdeographicToText(aOrdinal, result, gDataTradChineseFormal); michael@0: break; michael@0: michael@0: case NS_STYLE_LIST_STYLE_SIMP_CHINESE_INFORMAL: michael@0: case NS_STYLE_LIST_STYLE_MOZ_SIMP_CHINESE_INFORMAL: michael@0: fallback = NS_STYLE_LIST_STYLE_CJK_DECIMAL; michael@0: success = michael@0: CJKIdeographicToText(aOrdinal, result, gDataSimpChineseInformal); michael@0: break; michael@0: michael@0: case NS_STYLE_LIST_STYLE_SIMP_CHINESE_FORMAL: michael@0: case NS_STYLE_LIST_STYLE_MOZ_SIMP_CHINESE_FORMAL: michael@0: fallback = NS_STYLE_LIST_STYLE_CJK_DECIMAL; michael@0: success = CJKIdeographicToText(aOrdinal, result, gDataSimpChineseFormal); michael@0: break; michael@0: michael@0: case NS_STYLE_LIST_STYLE_JAPANESE_INFORMAL: michael@0: case NS_STYLE_LIST_STYLE_MOZ_JAPANESE_INFORMAL: michael@0: fallback = NS_STYLE_LIST_STYLE_CJK_DECIMAL; michael@0: success = CJKIdeographicToText(aOrdinal, result, gDataJapaneseInformal); michael@0: break; michael@0: michael@0: case NS_STYLE_LIST_STYLE_JAPANESE_FORMAL: michael@0: case NS_STYLE_LIST_STYLE_MOZ_JAPANESE_FORMAL: michael@0: fallback = NS_STYLE_LIST_STYLE_CJK_DECIMAL; michael@0: success = CJKIdeographicToText(aOrdinal, result, gDataJapaneseFormal); michael@0: break; michael@0: michael@0: case NS_STYLE_LIST_STYLE_KOREAN_HANGUL_FORMAL: michael@0: fallback = NS_STYLE_LIST_STYLE_CJK_DECIMAL; michael@0: success = michael@0: CJKIdeographicToText(aOrdinal, result, gDataKoreanHangulFormal); michael@0: break; michael@0: michael@0: case NS_STYLE_LIST_STYLE_KOREAN_HANJA_INFORMAL: michael@0: fallback = NS_STYLE_LIST_STYLE_CJK_DECIMAL; michael@0: success = michael@0: CJKIdeographicToText(aOrdinal, result, gDataKoreanHanjaInformal); michael@0: break; michael@0: michael@0: case NS_STYLE_LIST_STYLE_KOREAN_HANJA_FORMAL: michael@0: fallback = NS_STYLE_LIST_STYLE_CJK_DECIMAL; michael@0: success = CJKIdeographicToText(aOrdinal, result, gDataKoreanHanjaFormal); michael@0: break; michael@0: michael@0: case NS_STYLE_LIST_STYLE_HEBREW: michael@0: isRTL = true; michael@0: success = HebrewToText(aOrdinal, result); michael@0: break; michael@0: michael@0: case NS_STYLE_LIST_STYLE_ARMENIAN: michael@0: success = ArmenianToText(aOrdinal, result); michael@0: break; michael@0: michael@0: case NS_STYLE_LIST_STYLE_GEORGIAN: michael@0: success = GeorgianToText(aOrdinal, result); michael@0: break; michael@0: michael@0: case NS_STYLE_LIST_STYLE_MOZ_ARABIC_INDIC: michael@0: success = OtherDecimalToText(aOrdinal, 0x0660, result); michael@0: break; michael@0: michael@0: case NS_STYLE_LIST_STYLE_MOZ_PERSIAN: michael@0: case NS_STYLE_LIST_STYLE_MOZ_URDU: michael@0: success = OtherDecimalToText(aOrdinal, 0x06f0, result); michael@0: break; michael@0: michael@0: case NS_STYLE_LIST_STYLE_MOZ_DEVANAGARI: michael@0: success = OtherDecimalToText(aOrdinal, 0x0966, result); michael@0: break; michael@0: michael@0: case NS_STYLE_LIST_STYLE_MOZ_GURMUKHI: michael@0: success = OtherDecimalToText(aOrdinal, 0x0a66, result); michael@0: break; michael@0: michael@0: case NS_STYLE_LIST_STYLE_MOZ_GUJARATI: michael@0: success = OtherDecimalToText(aOrdinal, 0x0AE6, result); michael@0: break; michael@0: michael@0: case NS_STYLE_LIST_STYLE_MOZ_ORIYA: michael@0: success = OtherDecimalToText(aOrdinal, 0x0B66, result); michael@0: break; michael@0: michael@0: case NS_STYLE_LIST_STYLE_MOZ_KANNADA: michael@0: success = OtherDecimalToText(aOrdinal, 0x0CE6, result); michael@0: break; michael@0: michael@0: case NS_STYLE_LIST_STYLE_MOZ_MALAYALAM: michael@0: success = OtherDecimalToText(aOrdinal, 0x0D66, result); michael@0: break; michael@0: michael@0: case NS_STYLE_LIST_STYLE_MOZ_THAI: michael@0: success = OtherDecimalToText(aOrdinal, 0x0E50, result); michael@0: break; michael@0: michael@0: case NS_STYLE_LIST_STYLE_MOZ_LAO: michael@0: success = OtherDecimalToText(aOrdinal, 0x0ED0, result); michael@0: break; michael@0: michael@0: case NS_STYLE_LIST_STYLE_MOZ_MYANMAR: michael@0: success = OtherDecimalToText(aOrdinal, 0x1040, result); michael@0: break; michael@0: michael@0: case NS_STYLE_LIST_STYLE_MOZ_KHMER: michael@0: success = OtherDecimalToText(aOrdinal, 0x17E0, result); michael@0: break; michael@0: michael@0: case NS_STYLE_LIST_STYLE_MOZ_BENGALI: michael@0: success = OtherDecimalToText(aOrdinal, 0x09E6, result); michael@0: break; michael@0: michael@0: case NS_STYLE_LIST_STYLE_MOZ_TELUGU: michael@0: success = OtherDecimalToText(aOrdinal, 0x0C66, result); michael@0: break; michael@0: michael@0: case NS_STYLE_LIST_STYLE_MOZ_TAMIL: michael@0: success = TamilToText(aOrdinal, result); michael@0: break; michael@0: michael@0: case NS_STYLE_LIST_STYLE_MOZ_CJK_HEAVENLY_STEM: michael@0: fallback = NS_STYLE_LIST_STYLE_CJK_DECIMAL; michael@0: success = CharListToText(aOrdinal, result, gCJKHeavenlyStemChars, michael@0: CJK_HEAVENLY_STEM_CHARS_SIZE); michael@0: break; michael@0: michael@0: case NS_STYLE_LIST_STYLE_MOZ_CJK_EARTHLY_BRANCH: michael@0: fallback = NS_STYLE_LIST_STYLE_CJK_DECIMAL; michael@0: success = CharListToText(aOrdinal, result, gCJKEarthlyBranchChars, michael@0: CJK_EARTHLY_BRANCH_CHARS_SIZE); michael@0: break; michael@0: michael@0: case NS_STYLE_LIST_STYLE_MOZ_HANGUL: michael@0: success = CharListToText(aOrdinal, result, gHangulChars, HANGUL_CHARS_SIZE); michael@0: break; michael@0: michael@0: case NS_STYLE_LIST_STYLE_MOZ_HANGUL_CONSONANT: michael@0: success = CharListToText(aOrdinal, result, gHangulConsonantChars, michael@0: HANGUL_CONSONANT_CHARS_SIZE); michael@0: break; michael@0: michael@0: case NS_STYLE_LIST_STYLE_MOZ_ETHIOPIC_HALEHAME: michael@0: success = CharListToText(aOrdinal, result, gEthiopicHalehameChars, michael@0: ETHIOPIC_HALEHAME_CHARS_SIZE); michael@0: break; michael@0: michael@0: case NS_STYLE_LIST_STYLE_MOZ_ETHIOPIC_NUMERIC: michael@0: success = EthiopicToText(aOrdinal, result); michael@0: break; michael@0: michael@0: case NS_STYLE_LIST_STYLE_MOZ_ETHIOPIC_HALEHAME_AM: michael@0: success = CharListToText(aOrdinal, result, gEthiopicHalehameAmChars, michael@0: ETHIOPIC_HALEHAME_AM_CHARS_SIZE); michael@0: break; michael@0: michael@0: case NS_STYLE_LIST_STYLE_MOZ_ETHIOPIC_HALEHAME_TI_ER: michael@0: success = CharListToText(aOrdinal, result, gEthiopicHalehameTiErChars, michael@0: ETHIOPIC_HALEHAME_TI_ER_CHARS_SIZE); michael@0: break; michael@0: michael@0: case NS_STYLE_LIST_STYLE_MOZ_ETHIOPIC_HALEHAME_TI_ET: michael@0: success = CharListToText(aOrdinal, result, gEthiopicHalehameTiEtChars, michael@0: ETHIOPIC_HALEHAME_TI_ET_CHARS_SIZE); michael@0: break; michael@0: } michael@0: if (!success) { michael@0: AppendCounterText(fallback, aOrdinal, result, isRTL); michael@0: } michael@0: } michael@0: michael@0: /* static */ void michael@0: nsBulletFrame::GetListItemSuffix(int32_t aListStyleType, michael@0: nsString& aResult, michael@0: bool& aSuppressPadding) michael@0: { michael@0: aResult = '.'; michael@0: aSuppressPadding = false; michael@0: michael@0: switch (aListStyleType) { michael@0: case NS_STYLE_LIST_STYLE_NONE: // used by counters code only michael@0: case NS_STYLE_LIST_STYLE_DISC: // used by counters code only michael@0: case NS_STYLE_LIST_STYLE_CIRCLE: // used by counters code only michael@0: case NS_STYLE_LIST_STYLE_SQUARE: // used by counters code only michael@0: aResult.Truncate(); michael@0: break; michael@0: michael@0: case NS_STYLE_LIST_STYLE_CJK_DECIMAL: michael@0: case NS_STYLE_LIST_STYLE_CJK_IDEOGRAPHIC: michael@0: case NS_STYLE_LIST_STYLE_TRAD_CHINESE_INFORMAL: michael@0: case NS_STYLE_LIST_STYLE_TRAD_CHINESE_FORMAL: michael@0: case NS_STYLE_LIST_STYLE_SIMP_CHINESE_INFORMAL: michael@0: case NS_STYLE_LIST_STYLE_SIMP_CHINESE_FORMAL: michael@0: case NS_STYLE_LIST_STYLE_JAPANESE_INFORMAL: michael@0: case NS_STYLE_LIST_STYLE_JAPANESE_FORMAL: michael@0: case NS_STYLE_LIST_STYLE_MOZ_TRAD_CHINESE_INFORMAL: michael@0: case NS_STYLE_LIST_STYLE_MOZ_TRAD_CHINESE_FORMAL: michael@0: case NS_STYLE_LIST_STYLE_MOZ_SIMP_CHINESE_INFORMAL: michael@0: case NS_STYLE_LIST_STYLE_MOZ_SIMP_CHINESE_FORMAL: michael@0: case NS_STYLE_LIST_STYLE_MOZ_JAPANESE_INFORMAL: michael@0: case NS_STYLE_LIST_STYLE_MOZ_JAPANESE_FORMAL: michael@0: case NS_STYLE_LIST_STYLE_MOZ_CJK_HEAVENLY_STEM: michael@0: case NS_STYLE_LIST_STYLE_MOZ_CJK_EARTHLY_BRANCH: michael@0: aResult = 0x3001; michael@0: aSuppressPadding = true; michael@0: break; michael@0: michael@0: case NS_STYLE_LIST_STYLE_KOREAN_HANGUL_FORMAL: michael@0: case NS_STYLE_LIST_STYLE_KOREAN_HANJA_INFORMAL: michael@0: case NS_STYLE_LIST_STYLE_KOREAN_HANJA_FORMAL: michael@0: case NS_STYLE_LIST_STYLE_MOZ_HANGUL: michael@0: case NS_STYLE_LIST_STYLE_MOZ_HANGUL_CONSONANT: michael@0: aResult = ','; michael@0: break; michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsBulletFrame::GetListItemText(const nsStyleList& aListStyle, michael@0: nsString& result) michael@0: { michael@0: const nsStyleVisibility* vis = StyleVisibility(); michael@0: michael@0: NS_ASSERTION(aListStyle.mListStyleType != NS_STYLE_LIST_STYLE_NONE && michael@0: aListStyle.mListStyleType != NS_STYLE_LIST_STYLE_DISC && michael@0: aListStyle.mListStyleType != NS_STYLE_LIST_STYLE_CIRCLE && michael@0: aListStyle.mListStyleType != NS_STYLE_LIST_STYLE_SQUARE, michael@0: "we should be using specialized code for these types"); michael@0: michael@0: result.Truncate(); michael@0: AppendCounterText(aListStyle.mListStyleType, mOrdinal, result, mTextIsRTL); michael@0: michael@0: nsAutoString suffix; michael@0: GetListItemSuffix(aListStyle.mListStyleType, suffix, mSuppressPadding); michael@0: michael@0: // We're not going to do proper Bidi reordering on the list item marker, but michael@0: // just display the whole thing as RTL or LTR, so we fake reordering by michael@0: // appending the suffix to the end of the list item marker if the michael@0: // directionality of the characters is the same as the style direction or michael@0: // prepending it to the beginning if they are different. michael@0: result = (mTextIsRTL == (vis->mDirection == NS_STYLE_DIRECTION_RTL)) ? michael@0: result + suffix : suffix + result; michael@0: } michael@0: michael@0: #define MIN_BULLET_SIZE 1 michael@0: michael@0: michael@0: void michael@0: nsBulletFrame::GetDesiredSize(nsPresContext* aCX, michael@0: nsRenderingContext *aRenderingContext, michael@0: nsHTMLReflowMetrics& aMetrics, michael@0: float aFontSizeInflation) michael@0: { michael@0: // Reset our padding. If we need it, we'll set it below. michael@0: mPadding.SizeTo(0, 0, 0, 0); michael@0: michael@0: const nsStyleList* myList = StyleList(); michael@0: nscoord ascent; michael@0: michael@0: RemoveStateBits(BULLET_FRAME_IMAGE_LOADING); michael@0: michael@0: if (myList->GetListStyleImage() && mImageRequest) { michael@0: uint32_t status; michael@0: mImageRequest->GetImageStatus(&status); michael@0: if (status & imgIRequest::STATUS_SIZE_AVAILABLE && michael@0: !(status & imgIRequest::STATUS_ERROR)) { michael@0: // auto size the image michael@0: aMetrics.Width() = mIntrinsicSize.width; michael@0: aMetrics.SetTopAscent(aMetrics.Height() = mIntrinsicSize.height); michael@0: michael@0: AddStateBits(BULLET_FRAME_IMAGE_LOADING); michael@0: michael@0: return; michael@0: } michael@0: } michael@0: michael@0: // If we're getting our desired size and don't have an image, reset michael@0: // mIntrinsicSize to (0,0). Otherwise, if we used to have an image, it michael@0: // changed, and the new one is coming in, but we're reflowing before it's michael@0: // fully there, we'll end up with mIntrinsicSize not matching our size, but michael@0: // won't trigger a reflow in OnStartContainer (because mIntrinsicSize will michael@0: // match the image size). michael@0: mIntrinsicSize.SizeTo(0, 0); michael@0: michael@0: nsRefPtr fm; michael@0: nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm), michael@0: aFontSizeInflation); michael@0: nscoord bulletSize; michael@0: michael@0: nsAutoString text; michael@0: switch (myList->mListStyleType) { michael@0: case NS_STYLE_LIST_STYLE_NONE: michael@0: aMetrics.Width() = aMetrics.Height() = 0; michael@0: aMetrics.SetTopAscent(0); michael@0: break; michael@0: michael@0: case NS_STYLE_LIST_STYLE_DISC: michael@0: case NS_STYLE_LIST_STYLE_CIRCLE: michael@0: case NS_STYLE_LIST_STYLE_SQUARE: michael@0: ascent = fm->MaxAscent(); michael@0: bulletSize = std::max(nsPresContext::CSSPixelsToAppUnits(MIN_BULLET_SIZE), michael@0: NSToCoordRound(0.8f * (float(ascent) / 2.0f))); michael@0: mPadding.bottom = NSToCoordRound(float(ascent) / 8.0f); michael@0: aMetrics.Width() = aMetrics.Height() = bulletSize; michael@0: aMetrics.SetTopAscent(bulletSize + mPadding.bottom); michael@0: break; michael@0: michael@0: default: michael@0: case NS_STYLE_LIST_STYLE_DECIMAL_LEADING_ZERO: michael@0: case NS_STYLE_LIST_STYLE_DECIMAL: michael@0: case NS_STYLE_LIST_STYLE_CJK_DECIMAL: michael@0: case NS_STYLE_LIST_STYLE_LOWER_ROMAN: michael@0: case NS_STYLE_LIST_STYLE_UPPER_ROMAN: michael@0: case NS_STYLE_LIST_STYLE_LOWER_ALPHA: michael@0: case NS_STYLE_LIST_STYLE_UPPER_ALPHA: michael@0: case NS_STYLE_LIST_STYLE_KATAKANA: michael@0: case NS_STYLE_LIST_STYLE_HIRAGANA: michael@0: case NS_STYLE_LIST_STYLE_KATAKANA_IROHA: michael@0: case NS_STYLE_LIST_STYLE_HIRAGANA_IROHA: michael@0: case NS_STYLE_LIST_STYLE_LOWER_GREEK: michael@0: case NS_STYLE_LIST_STYLE_HEBREW: michael@0: case NS_STYLE_LIST_STYLE_ARMENIAN: michael@0: case NS_STYLE_LIST_STYLE_GEORGIAN: michael@0: case NS_STYLE_LIST_STYLE_CJK_IDEOGRAPHIC: michael@0: case NS_STYLE_LIST_STYLE_JAPANESE_INFORMAL: michael@0: case NS_STYLE_LIST_STYLE_JAPANESE_FORMAL: michael@0: case NS_STYLE_LIST_STYLE_KOREAN_HANGUL_FORMAL: michael@0: case NS_STYLE_LIST_STYLE_KOREAN_HANJA_INFORMAL: michael@0: case NS_STYLE_LIST_STYLE_KOREAN_HANJA_FORMAL: michael@0: case NS_STYLE_LIST_STYLE_SIMP_CHINESE_INFORMAL: michael@0: case NS_STYLE_LIST_STYLE_SIMP_CHINESE_FORMAL: michael@0: case NS_STYLE_LIST_STYLE_TRAD_CHINESE_INFORMAL: michael@0: case NS_STYLE_LIST_STYLE_TRAD_CHINESE_FORMAL: michael@0: case NS_STYLE_LIST_STYLE_MOZ_SIMP_CHINESE_INFORMAL: michael@0: case NS_STYLE_LIST_STYLE_MOZ_SIMP_CHINESE_FORMAL: michael@0: case NS_STYLE_LIST_STYLE_MOZ_TRAD_CHINESE_INFORMAL: michael@0: case NS_STYLE_LIST_STYLE_MOZ_TRAD_CHINESE_FORMAL: michael@0: case NS_STYLE_LIST_STYLE_MOZ_JAPANESE_INFORMAL: michael@0: case NS_STYLE_LIST_STYLE_MOZ_JAPANESE_FORMAL: michael@0: case NS_STYLE_LIST_STYLE_MOZ_CJK_HEAVENLY_STEM: michael@0: case NS_STYLE_LIST_STYLE_MOZ_CJK_EARTHLY_BRANCH: michael@0: case NS_STYLE_LIST_STYLE_MOZ_ARABIC_INDIC: michael@0: case NS_STYLE_LIST_STYLE_MOZ_PERSIAN: michael@0: case NS_STYLE_LIST_STYLE_MOZ_URDU: michael@0: case NS_STYLE_LIST_STYLE_MOZ_DEVANAGARI: michael@0: case NS_STYLE_LIST_STYLE_MOZ_GURMUKHI: michael@0: case NS_STYLE_LIST_STYLE_MOZ_GUJARATI: michael@0: case NS_STYLE_LIST_STYLE_MOZ_ORIYA: michael@0: case NS_STYLE_LIST_STYLE_MOZ_KANNADA: michael@0: case NS_STYLE_LIST_STYLE_MOZ_MALAYALAM: michael@0: case NS_STYLE_LIST_STYLE_MOZ_BENGALI: michael@0: case NS_STYLE_LIST_STYLE_MOZ_TAMIL: michael@0: case NS_STYLE_LIST_STYLE_MOZ_TELUGU: michael@0: case NS_STYLE_LIST_STYLE_MOZ_THAI: michael@0: case NS_STYLE_LIST_STYLE_MOZ_LAO: michael@0: case NS_STYLE_LIST_STYLE_MOZ_MYANMAR: michael@0: case NS_STYLE_LIST_STYLE_MOZ_KHMER: michael@0: case NS_STYLE_LIST_STYLE_MOZ_HANGUL: michael@0: case NS_STYLE_LIST_STYLE_MOZ_HANGUL_CONSONANT: michael@0: case NS_STYLE_LIST_STYLE_MOZ_ETHIOPIC_HALEHAME: michael@0: case NS_STYLE_LIST_STYLE_MOZ_ETHIOPIC_NUMERIC: michael@0: case NS_STYLE_LIST_STYLE_MOZ_ETHIOPIC_HALEHAME_AM: michael@0: case NS_STYLE_LIST_STYLE_MOZ_ETHIOPIC_HALEHAME_TI_ER: michael@0: case NS_STYLE_LIST_STYLE_MOZ_ETHIOPIC_HALEHAME_TI_ET: michael@0: GetListItemText(*myList, text); michael@0: aMetrics.Height() = fm->MaxHeight(); michael@0: aRenderingContext->SetFont(fm); michael@0: aMetrics.Width() = michael@0: nsLayoutUtils::GetStringWidth(this, aRenderingContext, michael@0: text.get(), text.Length()); michael@0: aMetrics.SetTopAscent(fm->MaxAscent()); michael@0: break; michael@0: } michael@0: } michael@0: michael@0: nsresult michael@0: nsBulletFrame::Reflow(nsPresContext* aPresContext, michael@0: nsHTMLReflowMetrics& aMetrics, michael@0: const nsHTMLReflowState& aReflowState, michael@0: nsReflowStatus& aStatus) michael@0: { michael@0: DO_GLOBAL_REFLOW_COUNT("nsBulletFrame"); michael@0: DISPLAY_REFLOW(aPresContext, this, aReflowState, aMetrics, aStatus); michael@0: michael@0: float inflation = nsLayoutUtils::FontSizeInflationFor(this); michael@0: SetFontSizeInflation(inflation); michael@0: michael@0: // Get the base size michael@0: // This will also set mSuppressPadding appropriately (via GetListItemText()) michael@0: // for the builtin counter styles with ideographic comma as suffix where the michael@0: // default padding from ua.css is not desired. michael@0: GetDesiredSize(aPresContext, aReflowState.rendContext, aMetrics, inflation); michael@0: michael@0: // Add in the border and padding; split the top/bottom between the michael@0: // ascent and descent to make things look nice michael@0: const nsMargin& borderPadding = aReflowState.ComputedPhysicalBorderPadding(); michael@0: if (!mSuppressPadding || michael@0: aPresContext->HasAuthorSpecifiedRules(this, michael@0: NS_AUTHOR_SPECIFIED_PADDING)) { michael@0: mPadding.top += NSToCoordRound(borderPadding.top * inflation); michael@0: mPadding.right += NSToCoordRound(borderPadding.right * inflation); michael@0: mPadding.bottom += NSToCoordRound(borderPadding.bottom * inflation); michael@0: mPadding.left += NSToCoordRound(borderPadding.left * inflation); michael@0: } michael@0: aMetrics.Width() += mPadding.left + mPadding.right; michael@0: aMetrics.Height() += mPadding.top + mPadding.bottom; michael@0: aMetrics.SetTopAscent(aMetrics.TopAscent() + mPadding.top); michael@0: michael@0: // XXX this is a bit of a hack, we're assuming that no glyphs used for bullets michael@0: // overflow their font-boxes. It'll do for now; to fix it for real, we really michael@0: // should rewrite all the text-handling code here to use gfxTextRun (bug michael@0: // 397294). michael@0: aMetrics.SetOverflowAreasToDesiredBounds(); michael@0: michael@0: aStatus = NS_FRAME_COMPLETE; michael@0: NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aMetrics); michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* virtual */ nscoord michael@0: nsBulletFrame::GetMinWidth(nsRenderingContext *aRenderingContext) michael@0: { michael@0: nsHTMLReflowMetrics metrics(GetWritingMode()); michael@0: DISPLAY_MIN_WIDTH(this, metrics.Width()); michael@0: GetDesiredSize(PresContext(), aRenderingContext, metrics, 1.0f); michael@0: return metrics.Width(); michael@0: } michael@0: michael@0: /* virtual */ nscoord michael@0: nsBulletFrame::GetPrefWidth(nsRenderingContext *aRenderingContext) michael@0: { michael@0: nsHTMLReflowMetrics metrics(GetWritingMode()); michael@0: DISPLAY_PREF_WIDTH(this, metrics.Width()); michael@0: GetDesiredSize(PresContext(), aRenderingContext, metrics, 1.0f); michael@0: return metrics.Width(); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBulletFrame::Notify(imgIRequest *aRequest, int32_t aType, const nsIntRect* aData) michael@0: { michael@0: if (aType == imgINotificationObserver::SIZE_AVAILABLE) { michael@0: nsCOMPtr image; michael@0: aRequest->GetImage(getter_AddRefs(image)); michael@0: return OnStartContainer(aRequest, image); michael@0: } michael@0: michael@0: if (aType == imgINotificationObserver::FRAME_UPDATE) { michael@0: // The image has changed. michael@0: // Invalidate the entire content area. Maybe it's not optimal but it's simple and michael@0: // always correct, and I'll be a stunned mullet if it ever matters for performance michael@0: InvalidateFrame(); michael@0: } michael@0: michael@0: if (aType == imgINotificationObserver::IS_ANIMATED) { michael@0: // Register the image request with the refresh driver now that we know it's michael@0: // animated. michael@0: if (aRequest == mImageRequest) { michael@0: nsLayoutUtils::RegisterImageRequest(PresContext(), mImageRequest, michael@0: &mRequestRegistered); michael@0: } michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult nsBulletFrame::OnStartContainer(imgIRequest *aRequest, michael@0: imgIContainer *aImage) michael@0: { michael@0: if (!aImage) return NS_ERROR_INVALID_ARG; michael@0: if (!aRequest) return NS_ERROR_INVALID_ARG; michael@0: michael@0: uint32_t status; michael@0: aRequest->GetImageStatus(&status); michael@0: if (status & imgIRequest::STATUS_ERROR) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: nscoord w, h; michael@0: aImage->GetWidth(&w); michael@0: aImage->GetHeight(&h); michael@0: michael@0: nsPresContext* presContext = PresContext(); michael@0: michael@0: nsSize newsize(nsPresContext::CSSPixelsToAppUnits(w), michael@0: nsPresContext::CSSPixelsToAppUnits(h)); michael@0: michael@0: if (mIntrinsicSize != newsize) { michael@0: mIntrinsicSize = newsize; michael@0: michael@0: // Now that the size is available (or an error occurred), trigger michael@0: // a reflow of the bullet frame. michael@0: nsIPresShell *shell = presContext->GetPresShell(); michael@0: if (shell) { michael@0: shell->FrameNeedsReflow(this, nsIPresShell::eStyleChange, michael@0: NS_FRAME_IS_DIRTY); michael@0: } michael@0: } michael@0: michael@0: // Handle animations michael@0: aImage->SetAnimationMode(presContext->ImageAnimationMode()); michael@0: // Ensure the animation (if any) is started. Note: There is no michael@0: // corresponding call to Decrement for this. This Increment will be michael@0: // 'cleaned up' by the Request when it is destroyed, but only then. michael@0: aRequest->IncrementAnimationConsumers(); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: nsBulletFrame::GetLoadGroup(nsPresContext *aPresContext, nsILoadGroup **aLoadGroup) michael@0: { michael@0: if (!aPresContext) michael@0: return; michael@0: michael@0: NS_PRECONDITION(nullptr != aLoadGroup, "null OUT parameter pointer"); michael@0: michael@0: nsIPresShell *shell = aPresContext->GetPresShell(); michael@0: michael@0: if (!shell) michael@0: return; michael@0: michael@0: nsIDocument *doc = shell->GetDocument(); michael@0: if (!doc) michael@0: return; michael@0: michael@0: *aLoadGroup = doc->GetDocumentLoadGroup().take(); michael@0: } michael@0: michael@0: union VoidPtrOrFloat { michael@0: VoidPtrOrFloat() : p(nullptr) {} michael@0: michael@0: void *p; michael@0: float f; michael@0: }; michael@0: michael@0: float michael@0: nsBulletFrame::GetFontSizeInflation() const michael@0: { michael@0: if (!HasFontSizeInflation()) { michael@0: return 1.0f; michael@0: } michael@0: VoidPtrOrFloat u; michael@0: u.p = Properties().Get(FontSizeInflationProperty()); michael@0: return u.f; michael@0: } michael@0: michael@0: void michael@0: nsBulletFrame::SetFontSizeInflation(float aInflation) michael@0: { michael@0: if (aInflation == 1.0f) { michael@0: if (HasFontSizeInflation()) { michael@0: RemoveStateBits(BULLET_FRAME_HAS_FONT_INFLATION); michael@0: Properties().Delete(FontSizeInflationProperty()); michael@0: } michael@0: return; michael@0: } michael@0: michael@0: AddStateBits(BULLET_FRAME_HAS_FONT_INFLATION); michael@0: VoidPtrOrFloat u; michael@0: u.f = aInflation; michael@0: Properties().Set(FontSizeInflationProperty(), u.p); michael@0: } michael@0: michael@0: already_AddRefed michael@0: nsBulletFrame::GetImage() const michael@0: { michael@0: if (mImageRequest && StyleList()->GetListStyleImage()) { michael@0: nsCOMPtr imageCon; michael@0: mImageRequest->GetImage(getter_AddRefs(imageCon)); michael@0: return imageCon.forget(); michael@0: } michael@0: michael@0: return nullptr; michael@0: } michael@0: michael@0: nscoord michael@0: nsBulletFrame::GetBaseline() const michael@0: { michael@0: nscoord ascent = 0, bottomPadding; michael@0: if (GetStateBits() & BULLET_FRAME_IMAGE_LOADING) { michael@0: ascent = GetRect().height; michael@0: } else { michael@0: nsRefPtr fm; michael@0: nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm), michael@0: GetFontSizeInflation()); michael@0: const nsStyleList* myList = StyleList(); michael@0: switch (myList->mListStyleType) { michael@0: case NS_STYLE_LIST_STYLE_NONE: michael@0: break; michael@0: michael@0: case NS_STYLE_LIST_STYLE_DISC: michael@0: case NS_STYLE_LIST_STYLE_CIRCLE: michael@0: case NS_STYLE_LIST_STYLE_SQUARE: michael@0: ascent = fm->MaxAscent(); michael@0: bottomPadding = NSToCoordRound(float(ascent) / 8.0f); michael@0: ascent = std::max(nsPresContext::CSSPixelsToAppUnits(MIN_BULLET_SIZE), michael@0: NSToCoordRound(0.8f * (float(ascent) / 2.0f))); michael@0: ascent += bottomPadding; michael@0: break; michael@0: michael@0: default: michael@0: ascent = fm->MaxAscent(); michael@0: break; michael@0: } michael@0: } michael@0: return ascent + GetUsedBorderAndPadding().top; michael@0: } michael@0: michael@0: michael@0: michael@0: michael@0: michael@0: michael@0: michael@0: michael@0: NS_IMPL_ISUPPORTS(nsBulletListener, imgINotificationObserver) michael@0: michael@0: nsBulletListener::nsBulletListener() : michael@0: mFrame(nullptr) michael@0: { michael@0: } michael@0: michael@0: nsBulletListener::~nsBulletListener() michael@0: { michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsBulletListener::Notify(imgIRequest *aRequest, int32_t aType, const nsIntRect* aData) michael@0: { michael@0: if (!mFrame) michael@0: return NS_ERROR_FAILURE; michael@0: return mFrame->Notify(aRequest, aType, aData); michael@0: }