gecko/layout/generic/nsBulletFrame.cpp

1679 lines
54 KiB
C++

/* -*- 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 "nsCOMPtr.h"
#include "nsBulletFrame.h"
#include "nsGkAtoms.h"
#include "nsHTMLParts.h"
#include "nsContainerFrame.h"
#include "nsGenericHTMLElement.h"
#include "nsPresContext.h"
#include "nsIPresShell.h"
#include "nsIDocument.h"
#include "nsRenderingContext.h"
#include "nsILoadGroup.h"
#include "nsIURL.h"
#include "nsNetUtil.h"
#include "prprf.h"
#include "nsDisplayList.h"
#include "imgILoader.h"
#include "imgIContainer.h"
#include "nsIServiceManager.h"
#include "nsIComponentManager.h"
#ifdef ACCESSIBILITY
#include "nsAccessibilityService.h"
#endif
using namespace mozilla;
NS_DECLARE_FRAME_PROPERTY(FontSizeInflationProperty, nsnull)
NS_IMPL_FRAMEARENA_HELPERS(nsBulletFrame)
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 = nsnull;
}
if (mListener) {
mListener->SetFrame(nsnull);
}
// Let base class do the rest
nsFrame::DestroyFrom(aDestructRoot);
}
#ifdef DEBUG
NS_IMETHODIMP
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 GetStyleList()->mListStyleType == NS_STYLE_LIST_STYLE_NONE;
}
/* virtual */ void
nsBulletFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext)
{
nsFrame::DidSetStyleContext(aOldStyleContext);
imgIRequest *newRequest = GetStyleList()->GetListStyleImage();
if (newRequest) {
if (!mListener) {
mListener = new nsBulletListener();
mListener->SetFrame(this);
}
bool needNewRequest = true;
if (mImageRequest) {
// Reload the image, maybe...
nsCOMPtr<nsIURI> oldURI;
mImageRequest->GetURI(getter_AddRefs(oldURI));
nsCOMPtr<nsIURI> newURI;
newRequest->GetURI(getter_AddRefs(newURI));
if (oldURI && newURI) {
bool same;
newURI->Equals(oldURI, &same);
if (same) {
needNewRequest = false;
} else {
nsLayoutUtils::DeregisterImageRequest(PresContext(), mImageRequest,
&mRequestRegistered);
mImageRequest->Cancel(NS_ERROR_FAILURE);
mImageRequest = nsnull;
}
}
}
if (needNewRequest) {
newRequest->Clone(mListener, getter_AddRefs(mImageRequest));
if (mImageRequest) {
nsLayoutUtils::RegisterImageRequestIfAnimated(PresContext(),
mImageRequest,
&mRequestRegistered);
}
}
} else {
// No image request on the new style context
if (mImageRequest) {
nsLayoutUtils::DeregisterImageRequest(PresContext(), mImageRequest,
&mRequestRegistered);
mImageRequest->Cancel(NS_ERROR_FAILURE);
mImageRequest = nsnull;
}
}
#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 = GetStyleList();
bool hasBullet = newStyleList->GetListStyleImage() ||
newStyleList->mListStyleType != NS_STYLE_LIST_STYLE_NONE;
if (hadBullet != hasBullet) {
accService->UpdateListBullet(PresContext()->GetPresShell(), mContent,
hasBullet);
}
}
}
}
#endif
}
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)
{
*aSnap = false;
return mFrame->GetVisualOverflowRectRelativeToSelf() + ToReferenceFrame();
}
virtual void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
HitTestState* aState, nsTArray<nsIFrame*> *aOutFrames) {
aOutFrames->AppendElement(mFrame);
}
virtual void Paint(nsDisplayListBuilder* aBuilder,
nsRenderingContext* aCtx);
NS_DISPLAY_DECL_NAME("Bullet", TYPE_BULLET)
virtual nsRect GetComponentAlphaBounds(nsDisplayListBuilder* aBuilder)
{
bool snap;
return GetBounds(aBuilder, &snap);
}
};
void nsDisplayBullet::Paint(nsDisplayListBuilder* aBuilder,
nsRenderingContext* aCtx)
{
static_cast<nsBulletFrame*>(mFrame)->
PaintBullet(*aCtx, ToReferenceFrame(), mVisibleRect);
}
NS_IMETHODIMP
nsBulletFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
const nsRect& aDirtyRect,
const nsDisplayListSet& aLists)
{
if (!IsVisibleForPainting(aBuilder))
return NS_OK;
DO_GLOBAL_REFLOW_COUNT_DSP("nsBulletFrame");
return aLists.Content()->AppendNewToTop(
new (aBuilder) nsDisplayBullet(aBuilder, this));
}
void
nsBulletFrame::PaintBullet(nsRenderingContext& aRenderingContext, nsPoint aPt,
const nsRect& aDirtyRect)
{
const nsStyleList* myList = GetStyleList();
PRUint8 listStyleType = myList->mListStyleType;
if (myList->GetListStyleImage() && mImageRequest) {
PRUint32 status;
mImageRequest->GetImageStatus(&status);
if (status & imgIRequest::STATUS_LOAD_COMPLETE &&
!(status & imgIRequest::STATUS_ERROR)) {
nsCOMPtr<imgIContainer> 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, imgIContainer::FLAG_NONE);
return;
}
}
}
nsRefPtr<nsFontMetrics> 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_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_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;
}
}
PRInt32
nsBulletFrame::SetListItemOrdinal(PRInt32 aNextOrdinal,
bool* aChanged)
{
// Assume that the ordinal comes from the caller
PRInt32 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 mOrdinal + 1;
}
// 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(PRInt32 ordinal, nsString& result)
{
char cbuf[40];
PR_snprintf(cbuf, sizeof(cbuf), "%ld", ordinal);
result.AppendASCII(cbuf);
return true;
}
static bool DecimalLeadingZeroToText(PRInt32 ordinal, nsString& result)
{
char cbuf[40];
PR_snprintf(cbuf, sizeof(cbuf), "%02ld", ordinal);
result.AppendASCII(cbuf);
return true;
}
static bool OtherDecimalToText(PRInt32 ordinal, PRUnichar zeroChar, nsString& result)
{
PRUnichar diff = zeroChar - PRUnichar('0');
DecimalToText(ordinal, result);
PRUnichar* p = result.BeginWriting();
if (ordinal < 0) {
// skip the leading '-'
++p;
}
for(; nsnull != *p ; p++)
*p += diff;
return true;
}
static bool TamilToText(PRInt32 ordinal, nsString& result)
{
PRUnichar diff = 0x0BE6 - PRUnichar('0');
DecimalToText(ordinal, result);
if (ordinal < 1 || ordinal > 9999) {
// Can't do those in this system.
return false;
}
PRUnichar* p = result.BeginWriting();
for(; nsnull != *p ; p++)
if(*p != PRUnichar('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(PRInt32 ordinal, nsString& result, const char* achars, const char* bchars)
{
if (ordinal < 1 || ordinal > 3999) {
DecimalToText(ordinal, result);
return false;
}
nsAutoString addOn, decStr;
decStr.AppendInt(ordinal, 10);
PRIntn len = decStr.Length();
const PRUnichar* dp = decStr.get();
const PRUnichar* end = dp + len;
PRIntn romanPos = len;
PRIntn n;
for (; dp < end; dp++) {
romanPos--;
addOn.SetLength(0);
switch(*dp) {
case '3':
addOn.Append(PRUnichar(achars[romanPos]));
// FALLTHROUGH
case '2':
addOn.Append(PRUnichar(achars[romanPos]));
// FALLTHROUGH
case '1':
addOn.Append(PRUnichar(achars[romanPos]));
break;
case '4':
addOn.Append(PRUnichar(achars[romanPos]));
// FALLTHROUGH
case '5': case '6':
case '7': case '8':
addOn.Append(PRUnichar(bchars[romanPos]));
for(n=0;'5'+n<*dp;n++) {
addOn.Append(PRUnichar(achars[romanPos]));
}
break;
case '9':
addOn.Append(PRUnichar(achars[romanPos]));
addOn.Append(PRUnichar(achars[romanPos+1]));
break;
default:
break;
}
result.Append(addOn);
}
return true;
}
#define ALPHA_SIZE 26
static const PRUnichar 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 PRUnichar 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 PRUnichar 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 PRUnichar 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 PRUnichar 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 PRUnichar 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 PRUnichar 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 PRUnichar 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 PRUnichar 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 PRUnichar 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 PRUnichar 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 PRUnichar 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 PRUnichar 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 PRUnichar 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 PRUnichar 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(PRInt32 ordinal, nsString& result, const PRUnichar* chars, PRInt32 aBase)
{
PRUnichar buf[NUM_BUF_SIZE];
PRInt32 idx = NUM_BUF_SIZE;
if (ordinal < 1) {
DecimalToText(ordinal, result);
return false;
}
do {
ordinal--; // a == 0
PRInt32 cur = ordinal % aBase;
buf[--idx] = chars[cur];
ordinal /= aBase ;
} while ( ordinal > 0);
result.Append(buf+idx,NUM_BUF_SIZE-idx);
return true;
}
static const PRUnichar gCJKIdeographicDigit1[10] =
{
0x96f6, 0x4e00, 0x4e8c, 0x4e09, 0x56db, // 0 - 4
0x4e94, 0x516d, 0x4e03, 0x516b, 0x4e5d // 5 - 9
};
static const PRUnichar gCJKIdeographicDigit2[10] =
{
0x96f6, 0x58f9, 0x8cb3, 0x53c3, 0x8086, // 0 - 4
0x4f0d, 0x9678, 0x67d2, 0x634c, 0x7396 // 5 - 9
};
static const PRUnichar gCJKIdeographicDigit3[10] =
{
0x96f6, 0x58f9, 0x8d30, 0x53c1, 0x8086, // 0 - 4
0x4f0d, 0x9646, 0x67d2, 0x634c, 0x7396 // 5 - 9
};
static const PRUnichar gCJKIdeographicUnit1[4] =
{
0x000, 0x5341, 0x767e, 0x5343
};
static const PRUnichar gCJKIdeographicUnit2[4] =
{
0x000, 0x62FE, 0x4F70, 0x4EDF
};
static const PRUnichar gCJKIdeographic10KUnit1[4] =
{
0x000, 0x842c, 0x5104, 0x5146
};
static const PRUnichar gCJKIdeographic10KUnit2[4] =
{
0x000, 0x4E07, 0x4ebf, 0x5146
};
static const PRUnichar gCJKIdeographic10KUnit3[4] =
{
0x000, 0x4E07, 0x5104, 0x5146
};
static const bool CJKIdeographicToText(PRInt32 ordinal, nsString& result,
const PRUnichar* digits,
const PRUnichar *unit,
const PRUnichar* unit10k)
{
// In theory, we need the following if condiction,
// However, the limit, 10 ^ 16, is greater than the max of PRUint32
// so we don't really need to test it here.
// if( ordinal > 9999999999999999)
// {
// PR_snprintf(cbuf, sizeof(cbuf), "%ld", ordinal);
// result.Append(cbuf);
// }
// else
// {
if (ordinal < 0) {
DecimalToText(ordinal, result);
return false;
}
PRUnichar c10kUnit = 0;
PRUnichar cUnit = 0;
PRUnichar cDigit = 0;
PRUint32 ud = 0;
PRUnichar buf[NUM_BUF_SIZE];
PRInt32 idx = NUM_BUF_SIZE;
bool bOutputZero = ( 0 == ordinal );
do {
if(0 == (ud % 4)) {
c10kUnit = unit10k[ud/4];
}
PRInt32 cur = ordinal % 10;
cDigit = digits[cur];
if( 0 == cur)
{
cUnit = 0;
if(bOutputZero) {
bOutputZero = false;
if(0 != cDigit)
buf[--idx] = cDigit;
}
}
else
{
bOutputZero = true;
cUnit = unit[ud%4];
if(0 != c10kUnit)
buf[--idx] = c10kUnit;
if(0 != cUnit)
buf[--idx] = cUnit;
if((0 != cDigit) &&
( (1 != cur) || (1 != (ud%4)) || ( ordinal > 10)) )
buf[--idx] = cDigit;
c10kUnit = 0;
}
ordinal /= 10;
++ud;
} while( ordinal > 0);
result.Append(buf+idx,NUM_BUF_SIZE-idx);
// }
return true;
}
#define HEBREW_GERESH 0x05F3
static const PRUnichar 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(PRInt32 ordinal, nsString& result)
{
if (ordinal < 1 || ordinal > 999999) {
DecimalToText(ordinal, result);
return false;
}
bool outputSep = false;
nsAutoString allText, thousandsGroup;
do {
thousandsGroup.Truncate();
PRInt32 n3 = ordinal % 1000;
// Process digit for 100 - 900
for(PRInt32 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
PRInt32 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((PRUnichar)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(PRInt32 ordinal, nsString& result)
{
if (ordinal < 1 || ordinal > 9999) { // zero or reach the limit of Armenian numbering system
DecimalToText(ordinal, result);
return false;
}
PRUnichar buf[NUM_BUF_SIZE];
PRInt32 idx = NUM_BUF_SIZE;
PRInt32 d = 0;
do {
PRInt32 cur = ordinal % 10;
if (cur > 0)
{
PRUnichar 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 PRUnichar 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(PRInt32 ordinal, nsString& result)
{
if (ordinal < 1 || ordinal > 19999) { // zero or reach the limit of Georgian numbering system
DecimalToText(ordinal, result);
return false;
}
PRUnichar buf[NUM_BUF_SIZE];
PRInt32 idx = NUM_BUF_SIZE;
PRInt32 d = 0;
do {
PRInt32 cur = ordinal % 10;
if (cur > 0)
{
PRUnichar 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 <yacob@geez.org>.
// 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(PRInt32 ordinal, nsString& result)
{
nsAutoString asciiNumberString; // decimal string representation of ordinal
DecimalToText(ordinal, asciiNumberString);
if (ordinal < 1) {
result.Append(asciiNumberString);
return false;
}
PRUint8 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 (PRUint8 indexFromLeft = 0, groupIndexFromRight = asciiStringLength >> 1;
indexFromLeft <= asciiStringLength;
indexFromLeft += 2, groupIndexFromRight--) {
PRUint8 tensValue = asciiNumberString.CharAt(indexFromLeft) & 0x0F;
PRUint8 unitsValue = asciiNumberString.CharAt(indexFromLeft + 1) & 0x0F;
PRUint8 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((PRUnichar) (tensValue + ETHIOPIC_TEN - 1));
}
if (unitsValue) {
//map onto Ethiopic "units":
result.Append((PRUnichar) (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((PRUnichar) ETHIOPIC_HUNDRED);
}
} else {
if (groupIndexFromRight) {
result.Append((PRUnichar) ETHIOPIC_TEN_THOUSAND);
}
}
}
return true;
}
/* static */ bool
nsBulletFrame::AppendCounterText(PRInt32 aListStyleType,
PRInt32 aOrdinal,
nsString& result)
{
bool success = true;
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(PRUnichar(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(PRUnichar(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(PRUnichar(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);
break;
case NS_STYLE_LIST_STYLE_DECIMAL_LEADING_ZERO:
success = DecimalLeadingZeroToText(aOrdinal, result);
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_MOZ_TRAD_CHINESE_INFORMAL:
success = CJKIdeographicToText(aOrdinal, result, gCJKIdeographicDigit1,
gCJKIdeographicUnit1,
gCJKIdeographic10KUnit1);
break;
case NS_STYLE_LIST_STYLE_MOZ_TRAD_CHINESE_FORMAL:
success = CJKIdeographicToText(aOrdinal, result, gCJKIdeographicDigit2,
gCJKIdeographicUnit2,
gCJKIdeographic10KUnit1);
break;
case NS_STYLE_LIST_STYLE_MOZ_SIMP_CHINESE_INFORMAL:
success = CJKIdeographicToText(aOrdinal, result, gCJKIdeographicDigit1,
gCJKIdeographicUnit1,
gCJKIdeographic10KUnit2);
break;
case NS_STYLE_LIST_STYLE_MOZ_SIMP_CHINESE_FORMAL:
success = CJKIdeographicToText(aOrdinal, result, gCJKIdeographicDigit3,
gCJKIdeographicUnit2,
gCJKIdeographic10KUnit2);
break;
case NS_STYLE_LIST_STYLE_MOZ_JAPANESE_INFORMAL:
success = CJKIdeographicToText(aOrdinal, result, gCJKIdeographicDigit1,
gCJKIdeographicUnit1,
gCJKIdeographic10KUnit3);
break;
case NS_STYLE_LIST_STYLE_MOZ_JAPANESE_FORMAL:
success = CJKIdeographicToText(aOrdinal, result, gCJKIdeographicDigit2,
gCJKIdeographicUnit2,
gCJKIdeographic10KUnit3);
break;
case NS_STYLE_LIST_STYLE_HEBREW:
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:
success = CharListToText(aOrdinal, result, gCJKHeavenlyStemChars,
CJK_HEAVENLY_STEM_CHARS_SIZE);
break;
case NS_STYLE_LIST_STYLE_MOZ_CJK_EARTHLY_BRANCH:
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;
}
return success;
}
bool
nsBulletFrame::GetListItemText(const nsStyleList& aListStyle,
nsString& result)
{
const nsStyleVisibility* vis = GetStyleVisibility();
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");
bool success =
AppendCounterText(aListStyle.mListStyleType, mOrdinal, result);
if (success && aListStyle.mListStyleType == NS_STYLE_LIST_STYLE_HEBREW)
mTextIsRTL = true;
// XXX For some of these systems, "." is wrong! This should really be
// pushed down into the individual cases!
nsString suffix = NS_LITERAL_STRING(".");
// 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;
return success;
}
#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 = GetStyleList();
nscoord ascent;
RemoveStateBits(BULLET_FRAME_IMAGE_LOADING);
if (myList->GetListStyleImage() && mImageRequest) {
PRUint32 status;
mImageRequest->GetImageStatus(&status);
if (status & imgIRequest::STATUS_SIZE_AVAILABLE &&
!(status & imgIRequest::STATUS_ERROR)) {
// auto size the image
aMetrics.width = mIntrinsicSize.width;
aMetrics.ascent = 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<nsFontMetrics> fm;
nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm),
aFontSizeInflation);
nscoord bulletSize;
nsAutoString text;
switch (myList->mListStyleType) {
case NS_STYLE_LIST_STYLE_NONE:
aMetrics.width = 0;
aMetrics.ascent = aMetrics.height = 0;
break;
case NS_STYLE_LIST_STYLE_DISC:
case NS_STYLE_LIST_STYLE_CIRCLE:
case NS_STYLE_LIST_STYLE_SQUARE:
ascent = fm->MaxAscent();
bulletSize = NS_MAX(nsPresContext::CSSPixelsToAppUnits(MIN_BULLET_SIZE),
NSToCoordRound(0.8f * (float(ascent) / 2.0f)));
mPadding.bottom = NSToCoordRound(float(ascent) / 8.0f);
aMetrics.width = mPadding.right + bulletSize;
aMetrics.ascent = aMetrics.height = mPadding.bottom + bulletSize;
break;
default:
case NS_STYLE_LIST_STYLE_DECIMAL_LEADING_ZERO:
case NS_STYLE_LIST_STYLE_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_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.width += mPadding.right;
aMetrics.ascent = fm->MaxAscent();
break;
}
}
NS_IMETHODIMP
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
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.mComputedBorderPadding;
aMetrics.width += borderPadding.left + borderPadding.right;
aMetrics.height += borderPadding.top + borderPadding.bottom;
aMetrics.ascent += borderPadding.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;
DISPLAY_MIN_WIDTH(this, metrics.width);
GetDesiredSize(PresContext(), aRenderingContext, metrics, 1.0f);
return metrics.width;
}
/* virtual */ nscoord
nsBulletFrame::GetPrefWidth(nsRenderingContext *aRenderingContext)
{
nsHTMLReflowMetrics metrics;
DISPLAY_PREF_WIDTH(this, metrics.width);
GetDesiredSize(PresContext(), aRenderingContext, metrics, 1.0f);
return metrics.width;
}
NS_IMETHODIMP nsBulletFrame::OnStartContainer(imgIRequest *aRequest,
imgIContainer *aImage)
{
if (!aImage) return NS_ERROR_INVALID_ARG;
if (!aRequest) return NS_ERROR_INVALID_ARG;
PRUint32 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;
}
NS_IMETHODIMP nsBulletFrame::OnDataAvailable(imgIRequest *aRequest,
bool aCurrentFrame,
const nsIntRect *aRect)
{
// 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();
return NS_OK;
}
NS_IMETHODIMP nsBulletFrame::OnStopDecode(imgIRequest *aRequest,
nsresult aStatus,
const PRUnichar *aStatusArg)
{
// XXX should the bulletframe do anything if the image failed to load?
// it didn't in the old code...
#if 0
if (NS_FAILED(aStatus)) {
// We failed to load the image. Notify the pres shell
if (NS_FAILED(aStatus) && (mImageRequest == aRequest || !mImageRequest)) {
imageFailed = true;
}
}
#endif
return NS_OK;
}
NS_IMETHODIMP nsBulletFrame::OnImageIsAnimated(imgIRequest* aRequest)
{
// 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;
}
NS_IMETHODIMP nsBulletFrame::FrameChanged(imgIRequest *aRequest,
imgIContainer *aContainer,
const nsIntRect *aDirtyRect)
{
// Invalidate the entire content area. Maybe it's not optimal but it's simple and
// always correct.
InvalidateFrame();
return NS_OK;
}
void
nsBulletFrame::GetLoadGroup(nsPresContext *aPresContext, nsILoadGroup **aLoadGroup)
{
if (!aPresContext)
return;
NS_PRECONDITION(nsnull != aLoadGroup, "null OUT parameter pointer");
nsIPresShell *shell = aPresContext->GetPresShell();
if (!shell)
return;
nsIDocument *doc = shell->GetDocument();
if (!doc)
return;
*aLoadGroup = doc->GetDocumentLoadGroup().get(); // already_AddRefed
}
union VoidPtrOrFloat {
VoidPtrOrFloat() : p(nsnull) {}
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);
}
nscoord
nsBulletFrame::GetBaseline() const
{
nscoord ascent = 0, bottomPadding;
if (GetStateBits() & BULLET_FRAME_IMAGE_LOADING) {
ascent = GetRect().height;
} else {
nsRefPtr<nsFontMetrics> fm;
nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm),
GetFontSizeInflation());
const nsStyleList* myList = GetStyleList();
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 = NS_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_ISUPPORTS2(nsBulletListener, imgIDecoderObserver, imgIContainerObserver)
nsBulletListener::nsBulletListener() :
mFrame(nsnull)
{
}
nsBulletListener::~nsBulletListener()
{
}
NS_IMETHODIMP nsBulletListener::OnStartContainer(imgIRequest *aRequest,
imgIContainer *aImage)
{
if (!mFrame)
return NS_ERROR_FAILURE;
return mFrame->OnStartContainer(aRequest, aImage);
}
NS_IMETHODIMP nsBulletListener::OnDataAvailable(imgIRequest *aRequest,
bool aCurrentFrame,
const nsIntRect *aRect)
{
if (!mFrame)
return NS_OK;
return mFrame->OnDataAvailable(aRequest, aCurrentFrame, aRect);
}
NS_IMETHODIMP nsBulletListener::OnStopDecode(imgIRequest *aRequest,
nsresult status,
const PRUnichar *statusArg)
{
if (!mFrame)
return NS_OK;
return mFrame->OnStopDecode(aRequest, status, statusArg);
}
NS_IMETHODIMP nsBulletListener::OnImageIsAnimated(imgIRequest *aRequest)
{
if (!mFrame)
return NS_OK;
return mFrame->OnImageIsAnimated(aRequest);
}
NS_IMETHODIMP nsBulletListener::FrameChanged(imgIRequest *aRequest,
imgIContainer *aContainer,
const nsIntRect *aDirtyRect)
{
if (!mFrame)
return NS_OK;
return mFrame->FrameChanged(aRequest, aContainer, aDirtyRect);
}