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