/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* ***** BEGIN LICENSE BLOCK ***** * Version: NPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Netscape Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/NPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Mozilla Communicator client code. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the NPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the NPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #include "nsCOMPtr.h" #include "nsBulletFrame.h" #include "nsHTMLAtoms.h" #include "nsHTMLParts.h" #include "nsHTMLValue.h" #include "nsHTMLContainerFrame.h" #include "nsIFontMetrics.h" #include "nsIHTMLContent.h" #include "nsIPresContext.h" #include "nsIPresShell.h" #include "nsIDocument.h" #include "nsReflowPath.h" #include "nsIRenderingContext.h" #include "nsILoadGroup.h" #include "nsIURL.h" #include "nsNetUtil.h" #include "nsLayoutAtoms.h" #include "prprf.h" #ifdef IBMBIDI #include "nsBidiPresUtils.h" #endif // IBMBIDI #include "imgILoader.h" #include "imgIContainer.h" #include "imgIDecoderObserver.h" #include "nsIServiceManager.h" #include "nsIComponentManager.h" class nsBulletListener : public imgIDecoderObserver { public: nsBulletListener(); virtual ~nsBulletListener(); NS_DECL_ISUPPORTS NS_DECL_IMGIDECODEROBSERVER NS_DECL_IMGICONTAINEROBSERVER void SetFrame(nsBulletFrame *frame) { mFrame = frame; } private: nsBulletFrame *mFrame; }; nsBulletFrame::nsBulletFrame() : mPresContext(nsnull) { } nsBulletFrame::~nsBulletFrame() { NS_PRECONDITION(!mPresContext, "Never got destroyed properly!"); } NS_IMETHODIMP nsBulletFrame::Destroy(nsIPresContext* aPresContext) { // Stop image loading first if (mImageRequest) { mImageRequest->Cancel(NS_ERROR_FAILURE); mImageRequest = nsnull; } if (mListener) NS_REINTERPRET_CAST(nsBulletListener*, mListener.get())->SetFrame(nsnull); mPresContext = nsnull; // clear weak pointer // Let base class do the rest return nsFrame::Destroy(aPresContext); } NS_IMETHODIMP nsBulletFrame::Init(nsIPresContext* aPresContext, nsIContent* aContent, nsIFrame* aParent, nsStyleContext* aContext, nsIFrame* aPrevInFlow) { mPresContext = aPresContext; nsresult rv = nsFrame::Init(aPresContext, aContent, aParent, aContext, aPrevInFlow); const nsStyleList* myList = GetStyleList(); if (!myList->mListStyleImage.IsEmpty()) { nsCOMPtr il(do_GetService("@mozilla.org/image/loader;1", &rv)); if (NS_FAILED(rv)) return rv; nsCOMPtr loadGroup; GetLoadGroup(aPresContext, getter_AddRefs(loadGroup)); nsCOMPtr baseURI; GetBaseURI(getter_AddRefs(baseURI)); nsCOMPtr imgURI; NS_NewURI(getter_AddRefs(imgURI), myList->mListStyleImage, nsnull, baseURI); // Get the document URI for the referrer... nsCOMPtr documentURI; nsCOMPtr doc; if (mContent) { (void) mContent->GetDocument(getter_AddRefs(doc)); if (doc) { doc->GetDocumentURL(getter_AddRefs(documentURI)); } } if (!mListener) { nsBulletListener *listener; NS_NEWXPCOM(listener, nsBulletListener); NS_ADDREF(listener); listener->SetFrame(this); listener->QueryInterface(NS_GET_IID(imgIDecoderObserver), getter_AddRefs(mListener)); NS_ASSERTION(mListener, "queryinterface for the listener failed"); NS_RELEASE(listener); } // XXX: initialDocumentURI is NULL ! il->LoadImage(imgURI, nsnull, documentURI, loadGroup, mListener, aPresContext, nsIRequest::LOAD_NORMAL, nsnull, nsnull, getter_AddRefs(mImageRequest)); } return NS_OK; } #ifdef NS_DEBUG NS_IMETHODIMP nsBulletFrame::GetFrameName(nsAString& aResult) const { return MakeFrameName(NS_LITERAL_STRING("Bullet"), aResult); } #endif NS_IMETHODIMP nsBulletFrame::GetFrameType(nsIAtom** aType) const { NS_PRECONDITION(nsnull != aType, "null OUT parameter pointer"); *aType = nsLayoutAtoms::bulletFrame; NS_ADDREF(*aType); return NS_OK; } #include "nsIDOMNode.h" NS_IMETHODIMP nsBulletFrame::Paint(nsIPresContext* aPresContext, nsIRenderingContext& aRenderingContext, const nsRect& aDirtyRect, nsFramePaintLayer aWhichLayer, PRUint32 aFlags) { if (NS_FRAME_PAINT_LAYER_FOREGROUND != aWhichLayer) { return NS_OK; } PRBool isVisible; if (NS_SUCCEEDED(IsVisibleForPainting(aPresContext, aRenderingContext, PR_TRUE, &isVisible)) && isVisible) { const nsStyleList* myList = GetStyleList(); PRUint8 listStyleType = myList->mListStyleType; if (!myList->mListStyleImage.IsEmpty() && mImageRequest) { PRUint32 status; mImageRequest->GetImageStatus(&status); if (status & imgIRequest::STATUS_LOAD_COMPLETE && !(status & imgIRequest::STATUS_ERROR)) { nsCOMPtr imageCon; mImageRequest->GetImage(getter_AddRefs(imageCon)); if (imageCon) { nsRect innerArea(mPadding.left, mPadding.top, mRect.width - (mPadding.left + mPadding.right), mRect.height - (mPadding.top + mPadding.bottom)); nsPoint p(innerArea.x, innerArea.y); innerArea.x = innerArea.y = 0; aRenderingContext.DrawImage(imageCon, &innerArea, &p); return NS_OK; } } } const nsStyleFont* myFont = GetStyleFont(); const nsStyleColor* myColor = GetStyleColor(); nsCOMPtr fm; aRenderingContext.SetColor(myColor->mColor); #ifdef IBMBIDI nsCharType charType = eCharType_LeftToRight; PRUint8 level = 0; PRBool isBidiSystem = PR_FALSE; const nsStyleVisibility* vis = GetStyleVisibility(); PRUint32 hints = 0; #endif // IBMBIDI nsAutoString text; switch (listStyleType) { case NS_STYLE_LIST_STYLE_NONE: break; default: case NS_STYLE_LIST_STYLE_BASIC: case NS_STYLE_LIST_STYLE_DISC: aRenderingContext.FillEllipse(mPadding.left, mPadding.top, mRect.width - (mPadding.left + mPadding.right), mRect.height - (mPadding.top + mPadding.bottom)); break; case NS_STYLE_LIST_STYLE_CIRCLE: aRenderingContext.DrawEllipse(mPadding.left, mPadding.top, mRect.width - (mPadding.left + mPadding.right), mRect.height - (mPadding.top + mPadding.bottom)); break; case NS_STYLE_LIST_STYLE_SQUARE: aRenderingContext.FillRect(mPadding.left, mPadding.top, mRect.width - (mPadding.left + mPadding.right), mRect.height - (mPadding.top + mPadding.bottom)); break; case NS_STYLE_LIST_STYLE_DECIMAL: case NS_STYLE_LIST_STYLE_OLD_DECIMAL: case NS_STYLE_LIST_STYLE_DECIMAL_LEADING_ZERO: #ifdef IBMBIDI GetListItemText(aPresContext, *myList, text); charType = eCharType_EuropeanNumber; break; case NS_STYLE_LIST_STYLE_MOZ_ARABIC_INDIC: if (GetListItemText(aPresContext, *myList, text)) charType = eCharType_ArabicNumber; else charType = eCharType_EuropeanNumber; break; case NS_STYLE_LIST_STYLE_HEBREW: aRenderingContext.GetHints(hints); isBidiSystem = (hints & NS_RENDERING_HINT_BIDI_REORDERING); if (!isBidiSystem) { if (GetListItemText(aPresContext, *myList, text)) { charType = eCharType_RightToLeft; level = 1; } else { charType = eCharType_EuropeanNumber; } if (NS_STYLE_DIRECTION_RTL == vis->mDirection) { text.Cut(0, 1); text.Append(NS_LITERAL_STRING(".")); } break; } // else fall through #endif // IBMBIDI 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_OLD_LOWER_ROMAN: case NS_STYLE_LIST_STYLE_OLD_UPPER_ROMAN: case NS_STYLE_LIST_STYLE_OLD_LOWER_ALPHA: case NS_STYLE_LIST_STYLE_OLD_UPPER_ALPHA: case NS_STYLE_LIST_STYLE_LOWER_GREEK: #ifndef IBMBIDI case NS_STYLE_LIST_STYLE_HEBREW: #endif 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: #ifndef IBMBIDI case NS_STYLE_LIST_STYLE_MOZ_ARABIC_INDIC: #endif 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: aPresContext->GetMetricsFor(myFont->mFont, getter_AddRefs(fm)); #ifdef IBMBIDI // If we can't render our numeral using the chars in the numbering // system, we'll be using "decimal"... PRBool usedChars = #endif // IBMBIDI GetListItemText(aPresContext, *myList, text); #ifdef IBMBIDI if (!usedChars) charType = eCharType_EuropeanNumber; #endif aRenderingContext.SetFont(fm); nscoord ascent; fm->GetMaxAscent(ascent); aRenderingContext.DrawString(text, mPadding.left, mPadding.top + ascent); break; } #ifdef IBMBIDI if (charType != eCharType_LeftToRight) { aPresContext->GetMetricsFor(myFont->mFont, getter_AddRefs(fm)); aRenderingContext.SetFont(fm); nscoord ascent; fm->GetMaxAscent(ascent); nsBidiPresUtils* bidiUtils; aPresContext->GetBidiUtils(&bidiUtils); if (bidiUtils) { const PRUnichar* buffer = text.get(); PRInt32 textLength = text.Length(); if (eCharType_RightToLeft == charType) { bidiUtils->FormatUnicodeText(aPresContext, (PRUnichar*)buffer, textLength, charType, level, PR_FALSE); } else { //Mohamed aRenderingContext.GetHints(hints); isBidiSystem = (hints & NS_RENDERING_HINT_ARABIC_SHAPING); bidiUtils->FormatUnicodeText(aPresContext, (PRUnichar*)buffer, textLength, charType, level, isBidiSystem);//Mohamed } } aRenderingContext.DrawString(text, mPadding.left, mPadding.top + ascent); } #endif // IBMBIDI } DO_GLOBAL_REFLOW_COUNT_DSP("nsBulletFrame", &aRenderingContext); return NS_OK; } PRInt32 nsBulletFrame::SetListItemOrdinal(PRInt32 aNextOrdinal, PRBool* 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. nsHTMLValue value; nsCOMPtr parentContent; mParent->GetContent(getter_AddRefs(parentContent)); if (parentContent) { nsCOMPtr hc = do_QueryInterface(parentContent); if (hc) { if (NS_CONTENT_ATTR_HAS_VALUE == hc->GetHTMLAttribute(nsHTMLAtoms::value, value)) { if (eHTMLUnit_Integer == value.GetUnit()) { // Use ordinal specified by the value attribute mOrdinal = value.GetIntValue(); } } } } *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 PR_TRUE means that we * could represent mOrder in the desired numbering system. PR_FALSE * means we had to fall back to decimal */ static PRBool DecimalToText(PRInt32 ordinal, nsString& result) { char cbuf[40]; PR_snprintf(cbuf, sizeof(cbuf), "%ld", ordinal); result.AppendWithConversion(cbuf); return PR_TRUE; } static PRBool DecimalLeadingZeroToText(PRInt32 ordinal, nsString& result) { char cbuf[40]; PR_snprintf(cbuf, sizeof(cbuf), "%02ld", ordinal); result.AppendWithConversion(cbuf); return PR_TRUE; } static PRBool OtherDecimalToText(PRInt32 ordinal, PRUnichar zeroChar, nsString& result) { PRUnichar diff = zeroChar - PRUnichar('0'); DecimalToText(ordinal, result); PRUnichar* p = NS_CONST_CAST(PRUnichar*, result.get()); if (ordinal < 0) { // skip the leading '-' ++p; } for(; nsnull != *p ; p++) *p += diff; return PR_TRUE; } static PRBool 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 PR_FALSE; } PRUnichar* p = (PRUnichar*)result.get(); for(; nsnull != *p ; p++) if(*p != PRUnichar('0')) *p += diff; return PR_TRUE; } static const char gLowerRomanCharsA[] = "ixcm"; static const char gUpperRomanCharsA[] = "IXCM"; static const char gLowerRomanCharsB[] = "vld?"; static const char gUpperRomanCharsB[] = "VLD?"; static PRBool RomanToText(PRInt32 ordinal, nsString& result, const char* achars, const char* bchars) { if (ordinal < 1) { DecimalToText(ordinal, result); return PR_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])); case '2': addOn.Append(PRUnichar(achars[romanPos])); 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;n<(*dp-'5');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 PR_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 PRBool 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 PR_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 PR_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 PRBool 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 PR_FALSE; } PRUnichar c10kUnit = 0; PRUnichar cUnit = 0; PRUnichar cDigit = 0; PRUint32 ud = 0; PRUnichar buf[NUM_BUF_SIZE]; PRInt32 idx = NUM_BUF_SIZE; PRBool 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 = PR_FALSE; if(0 != cDigit) buf[--idx] = cDigit; } } else { bOutputZero = PR_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 PR_TRUE; } #define HEBREW_THROSAND_SEP 0x0020 #define HEBREW_GERESH 0x05F3 #define HEBREW_GERSHAYIM 0x05F4 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 PRBool HebrewToText(PRInt32 ordinal, nsString& result) { if (ordinal < 0) { DecimalToText(ordinal, result); return PR_FALSE; } if (ordinal == 0) { // This one is treated specially #ifdef IBMBIDI static const PRUnichar hebrewZero[] = { 0x05D0, 0x05E4, 0x05E1 }; #else static const PRUnichar hebrewZero[] = { 0x05E1, 0x05E4, 0x05D0 }; #endif // IBMBIDI result.Append(hebrewZero); return PR_TRUE; } PRBool outputSep = PR_FALSE; PRUnichar buf[NUM_BUF_SIZE]; #ifdef IBMBIDI // Changes: 1) don't reverse the text; 2) don't insert geresh/gershayim. PRInt32 idx = 0; #else PRInt32 idx = NUM_BUF_SIZE; #endif // IBMBIDI PRUnichar digit; do { PRInt32 n3 = ordinal % 1000; if(outputSep) #ifdef IBMBIDI buf[idx++] = HEBREW_THROSAND_SEP; // output thousand separator #else buf[--idx] = HEBREW_THROSAND_SEP; // output thousand separator #endif // IBMBIDI outputSep = ( n3 > 0); // request to output thousand separator next time. PRInt32 d = 0; // we need to keep track of digit got output per 3 digits, // so we can handle Gershayim and Gersh correctly // Process digit for 100 - 900 for(PRInt32 n1 = 400; n1 > 0; ) { if( n3 >= n1) { n3 -= n1; digit = gHebrewDigit[(n1/100)-1+18]; if( n3 > 0) { #ifdef IBMBIDI buf[idx++] = digit; #else buf[--idx] = digit; #endif // IBMBIDI ++d; } else { // if this is the last digit #ifdef IBMBIDI buf[idx++] = digit; #else if (d > 0) { buf[--idx] = HEBREW_GERSHAYIM; buf[--idx] = digit; } else { buf[--idx] = digit; buf[--idx] = HEBREW_GERESH; } // if #endif // IBMBIDI } // if } 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; digit = gHebrewDigit[ n2 - 1]; } else { n2 = n3 - (n3 % 10); digit = gHebrewDigit[(n2/10)-1+9]; } // if n3 -= n2; if( n3 > 0) { #ifdef IBMBIDI buf[idx++] = digit; #else buf[--idx] = digit; #endif // IBMBIDI ++d; } else { // if this is the last digit #ifdef IBMBIDI buf[idx++] = digit; #else if (d > 0) { buf[--idx] = HEBREW_GERSHAYIM; buf[--idx] = digit; } else { buf[--idx] = digit; buf[--idx] = HEBREW_GERESH; } // if #endif // IBMBIDI } // if } // if // Process digit for 1 - 9 if ( n3 > 0) { digit = gHebrewDigit[n3-1]; // must be the last digit #ifdef IBMBIDI buf[idx++] = digit; #else if (d > 0) { buf[--idx] = HEBREW_GERSHAYIM; buf[--idx] = digit; } else { buf[--idx] = digit; buf[--idx] = HEBREW_GERESH; } // if #endif // IBMBIDI } // if ordinal /= 1000; } while (ordinal >= 1); #ifdef IBMBIDI result.Append(buf, idx); #else result.Append(buf+idx,NUM_BUF_SIZE-idx); #endif // IBMBIDI return PR_TRUE; } static PRBool ArmenianToText(PRInt32 ordinal, nsString& result) { // XXXbz this system goes out to a lot further than 9999... we should fix // that. This algorithm seems broken in general. There's this business of // "7000" being special and then there's the combining accent we're supposed // to be using... if (ordinal < 1 || ordinal > 9999) { // zero or reach the limit of Armenian numbering system DecimalToText(ordinal, result); return PR_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 PR_TRUE; } static const PRUnichar gGeorgianValue [ 37 ] = { // 4 * 9 + 1 = 37 // 1 2 3 4 5 6 7 8 9 0x10A0, 0x10A1, 0x10A2, 0x10A3, 0x10A4, 0x10A5, 0x10A6, 0x10C1, 0x10A7, // 10 20 30 40 50 60 70 80 90 0x10A8, 0x10A9, 0x10AA, 0x10AB, 0x10AC, 0x10C2, 0x10AD, 0x10AE, 0x10AF, // 100 200 300 400 500 600 700 800 900 0x10B0, 0x10B1, 0x10B2, 0x10B3, 0x10C3, 0x10B4, 0x10B5, 0x10B6, 0x10B7, // 1000 2000 3000 4000 5000 6000 7000 8000 9000 0x10B8, 0x10B9, 0x10BA, 0x10BB, 0x10BC, 0x10BD, 0x10BE, 0x10C4, 0x10C5, // 10000 0x10BF }; static PRBool GeorgianToText(PRInt32 ordinal, nsString& result) { if (ordinal < 1 || ordinal > 19999) { // zero or reach the limit of Georgian numbering system DecimalToText(ordinal, result); return PR_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 PR_TRUE; } // Convert ordinal to Ethiopic numeric representation. // The detail is available at http://www.ethiopic.org/Numerals/ // The algorithm used here was almost a verbatim copy of // the pseudo-code put up there by Daniel Yacob . // Another reference is Unicode 3.0 standard section 11.1. static PRBool EthiopicToText(PRInt32 ordinal, nsString& result) { nsAutoString asciiNumberString; // decimal string representation of ordinal DecimalToText(ordinal, asciiNumberString); if (ordinal < 1) { result.Append(asciiNumberString); return PR_FALSE; } PRInt32 n = asciiNumberString.Length() - 1; // Iterate from the lowest digit to higher digits for (PRInt32 place = 0; place <= n; place++) { PRUnichar asciiTen = '0'; PRUnichar asciiOne = asciiNumberString.CharAt(n - place); place++; if (place <= n) asciiTen = asciiNumberString.CharAt(n - place); // '00' is not represented and has to be skipped. if (asciiOne == '0' && asciiTen == '0' && place < n) continue; nsAutoString ethioNumber; // calculate digits at 10^(2*place) and 10^(2*place+1) if (asciiTen > '0' || asciiOne > '1' || place == 1) { if (asciiTen > '0') { // map onto Ethiopic "tens": U+1372=Ethiopic number ten ethioNumber += (PRUnichar) ((PRInt32) asciiTen + 0x1372 - 0x31); } if (asciiOne > '0') { //map onto Ethiopic "ones": 0x1369=Ethiopic digit one ethioNumber += (PRUnichar) ((PRInt32) asciiOne + 0x1369 - 0x31); } } // Now add 'cental-place' specifiers in terms of power of hundred // if (place > 1) : The lowest two digits don't need 'cental-place' specifier if (place & 2) // if odd power of hundred ethioNumber += (PRUnichar) 0x137B; // append Ethiopic number hundred // append Ethiopic number ten thousand every four decimal digits for (PRInt32 j = 0; j < place / 4; j++) ethioNumber += (PRUnichar) 0x137C; // 0x137C = Ethiopic number ten thousand result.Insert(ethioNumber, 0); } return PR_TRUE; } PRBool nsBulletFrame::GetListItemText(nsIPresContext* aCX, const nsStyleList& aListStyle, nsString& result) { #ifdef IBMBIDI const nsStyleVisibility* vis = GetStyleVisibility(); // XXX For some of these systems, "." is wrong! This should really be // pushed down into the individual cases! if (NS_STYLE_DIRECTION_RTL == vis->mDirection) { result.Append(NS_LITERAL_STRING(".")); } #endif // IBMBIDI PRBool success = PR_TRUE; switch (aListStyle.mListStyleType) { case NS_STYLE_LIST_STYLE_DECIMAL: case NS_STYLE_LIST_STYLE_OLD_DECIMAL: default: // CSS2 say "A users agent that does not recognize a numbering system // should use 'decimal' success = DecimalToText(mOrdinal, result); break; case NS_STYLE_LIST_STYLE_DECIMAL_LEADING_ZERO: success = DecimalLeadingZeroToText(mOrdinal, result); break; case NS_STYLE_LIST_STYLE_LOWER_ROMAN: case NS_STYLE_LIST_STYLE_OLD_LOWER_ROMAN: success = RomanToText(mOrdinal, result, gLowerRomanCharsA, gLowerRomanCharsB); break; case NS_STYLE_LIST_STYLE_UPPER_ROMAN: case NS_STYLE_LIST_STYLE_OLD_UPPER_ROMAN: success = RomanToText(mOrdinal, result, gUpperRomanCharsA, gUpperRomanCharsB); break; case NS_STYLE_LIST_STYLE_LOWER_ALPHA: case NS_STYLE_LIST_STYLE_OLD_LOWER_ALPHA: success = CharListToText(mOrdinal, result, gLowerAlphaChars, ALPHA_SIZE); break; case NS_STYLE_LIST_STYLE_UPPER_ALPHA: case NS_STYLE_LIST_STYLE_OLD_UPPER_ALPHA: success = CharListToText(mOrdinal, result, gUpperAlphaChars, ALPHA_SIZE); break; case NS_STYLE_LIST_STYLE_KATAKANA: success = CharListToText(mOrdinal, result, gKatakanaChars, KATAKANA_CHARS_SIZE); break; case NS_STYLE_LIST_STYLE_HIRAGANA: success = CharListToText(mOrdinal, result, gHiraganaChars, HIRAGANA_CHARS_SIZE); break; case NS_STYLE_LIST_STYLE_KATAKANA_IROHA: success = CharListToText(mOrdinal, result, gKatakanaIrohaChars, KATAKANA_IROHA_CHARS_SIZE); break; case NS_STYLE_LIST_STYLE_HIRAGANA_IROHA: success = CharListToText(mOrdinal, result, gHiraganaIrohaChars, HIRAGANA_IROHA_CHARS_SIZE); break; case NS_STYLE_LIST_STYLE_LOWER_GREEK: success = CharListToText(mOrdinal, 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(mOrdinal, result, gCJKIdeographicDigit1, gCJKIdeographicUnit1, gCJKIdeographic10KUnit1); break; case NS_STYLE_LIST_STYLE_MOZ_TRAD_CHINESE_FORMAL: success = CJKIdeographicToText(mOrdinal, result, gCJKIdeographicDigit2, gCJKIdeographicUnit2, gCJKIdeographic10KUnit1); break; case NS_STYLE_LIST_STYLE_MOZ_SIMP_CHINESE_INFORMAL: success = CJKIdeographicToText(mOrdinal, result, gCJKIdeographicDigit1, gCJKIdeographicUnit1, gCJKIdeographic10KUnit2); break; case NS_STYLE_LIST_STYLE_MOZ_SIMP_CHINESE_FORMAL: success = CJKIdeographicToText(mOrdinal, result, gCJKIdeographicDigit3, gCJKIdeographicUnit2, gCJKIdeographic10KUnit2); break; case NS_STYLE_LIST_STYLE_MOZ_JAPANESE_INFORMAL: success = CJKIdeographicToText(mOrdinal, result, gCJKIdeographicDigit1, gCJKIdeographicUnit1, gCJKIdeographic10KUnit3); break; case NS_STYLE_LIST_STYLE_MOZ_JAPANESE_FORMAL: success = CJKIdeographicToText(mOrdinal, result, gCJKIdeographicDigit2, gCJKIdeographicUnit2, gCJKIdeographic10KUnit3); break; case NS_STYLE_LIST_STYLE_HEBREW: success = HebrewToText(mOrdinal, result); break; case NS_STYLE_LIST_STYLE_ARMENIAN: success = ArmenianToText(mOrdinal, result); break; case NS_STYLE_LIST_STYLE_GEORGIAN: success = GeorgianToText(mOrdinal, result); break; case NS_STYLE_LIST_STYLE_MOZ_ARABIC_INDIC: success = OtherDecimalToText(mOrdinal, 0x0660, result); break; case NS_STYLE_LIST_STYLE_MOZ_PERSIAN: case NS_STYLE_LIST_STYLE_MOZ_URDU: success = OtherDecimalToText(mOrdinal, 0x06f0, result); break; case NS_STYLE_LIST_STYLE_MOZ_DEVANAGARI: success = OtherDecimalToText(mOrdinal, 0x0966, result); break; case NS_STYLE_LIST_STYLE_MOZ_GURMUKHI: success = OtherDecimalToText(mOrdinal, 0x0a66, result); break; case NS_STYLE_LIST_STYLE_MOZ_GUJARATI: success = OtherDecimalToText(mOrdinal, 0x0AE6, result); break; case NS_STYLE_LIST_STYLE_MOZ_ORIYA: success = OtherDecimalToText(mOrdinal, 0x0B66, result); break; case NS_STYLE_LIST_STYLE_MOZ_KANNADA: success = OtherDecimalToText(mOrdinal, 0x0CE6, result); break; case NS_STYLE_LIST_STYLE_MOZ_MALAYALAM: success = OtherDecimalToText(mOrdinal, 0x0D66, result); break; case NS_STYLE_LIST_STYLE_MOZ_THAI: success = OtherDecimalToText(mOrdinal, 0x0E50, result); break; case NS_STYLE_LIST_STYLE_MOZ_LAO: success = OtherDecimalToText(mOrdinal, 0x0ED0, result); break; case NS_STYLE_LIST_STYLE_MOZ_MYANMAR: success = OtherDecimalToText(mOrdinal, 0x1040, result); break; case NS_STYLE_LIST_STYLE_MOZ_KHMER: success = OtherDecimalToText(mOrdinal, 0x17E0, result); break; case NS_STYLE_LIST_STYLE_MOZ_BENGALI: success = OtherDecimalToText(mOrdinal, 0x09E6, result); break; case NS_STYLE_LIST_STYLE_MOZ_TELUGU: success = OtherDecimalToText(mOrdinal, 0x0C66, result); break; case NS_STYLE_LIST_STYLE_MOZ_TAMIL: success = TamilToText(mOrdinal, result); break; case NS_STYLE_LIST_STYLE_MOZ_CJK_HEAVENLY_STEM: success = CharListToText(mOrdinal, result, gCJKHeavenlyStemChars, CJK_HEAVENLY_STEM_CHARS_SIZE); break; case NS_STYLE_LIST_STYLE_MOZ_CJK_EARTHLY_BRANCH: success = CharListToText(mOrdinal, result, gCJKEarthlyBranchChars, CJK_EARTHLY_BRANCH_CHARS_SIZE); break; case NS_STYLE_LIST_STYLE_MOZ_HANGUL: success = CharListToText(mOrdinal, result, gHangulChars, HANGUL_CHARS_SIZE); break; case NS_STYLE_LIST_STYLE_MOZ_HANGUL_CONSONANT: success = CharListToText(mOrdinal, result, gHangulConsonantChars, HANGUL_CONSONANT_CHARS_SIZE); break; case NS_STYLE_LIST_STYLE_MOZ_ETHIOPIC_HALEHAME: success = CharListToText(mOrdinal, result, gEthiopicHalehameChars, ETHIOPIC_HALEHAME_CHARS_SIZE); break; case NS_STYLE_LIST_STYLE_MOZ_ETHIOPIC_NUMERIC: success = EthiopicToText(mOrdinal, result); break; case NS_STYLE_LIST_STYLE_MOZ_ETHIOPIC_HALEHAME_AM: success = CharListToText(mOrdinal, result, gEthiopicHalehameAmChars, ETHIOPIC_HALEHAME_AM_CHARS_SIZE); break; case NS_STYLE_LIST_STYLE_MOZ_ETHIOPIC_HALEHAME_TI_ER: success = CharListToText(mOrdinal, result, gEthiopicHalehameTiErChars, ETHIOPIC_HALEHAME_TI_ER_CHARS_SIZE); break; case NS_STYLE_LIST_STYLE_MOZ_ETHIOPIC_HALEHAME_TI_ET: success = CharListToText(mOrdinal, result, gEthiopicHalehameTiEtChars, ETHIOPIC_HALEHAME_TI_ET_CHARS_SIZE); break; } // XXX For some of these systems, "." is wrong! This should really be // pushed up into the cases... #ifdef IBMBIDI if (NS_STYLE_DIRECTION_RTL != vis->mDirection) #endif // IBMBIDI result.Append(NS_LITERAL_STRING(".")); return success; } #define MIN_BULLET_SIZE 5 // from laytext.c #define MINMAX(_value,_min,_max) \ ((_value) < (_min) \ ? (_min) \ : ((_value) > (_max) \ ? (_max) \ : (_value))) void nsBulletFrame::GetDesiredSize(nsIPresContext* aCX, const nsHTMLReflowState& aReflowState, nsHTMLReflowMetrics& aMetrics) { const nsStyleList* myList = GetStyleList(); nscoord ascent; if (!myList->mListStyleImage.IsEmpty() && mImageRequest) { PRUint32 status; mImageRequest->GetImageStatus(&status); if (status & imgIRequest::STATUS_SIZE_AVAILABLE && !(status & imgIRequest::STATUS_ERROR)) { nscoord widthConstraint = NS_INTRINSICSIZE; nscoord heightConstraint = NS_INTRINSICSIZE; PRBool fixedContentWidth = PR_FALSE; PRBool fixedContentHeight = PR_FALSE; nscoord minWidth, maxWidth, minHeight, maxHeight; // Determine whether the image has fixed content width widthConstraint = aReflowState.mComputedWidth; minWidth = aReflowState.mComputedMinWidth; maxWidth = aReflowState.mComputedMaxWidth; if (widthConstraint != NS_INTRINSICSIZE) { fixedContentWidth = PR_TRUE; } // Determine whether the image has fixed content height heightConstraint = aReflowState.mComputedHeight; minHeight = aReflowState.mComputedMinHeight; maxHeight = aReflowState.mComputedMaxHeight; if (heightConstraint != NS_UNCONSTRAINEDSIZE) { fixedContentHeight = PR_TRUE; } PRBool haveComputedSize = PR_FALSE; PRBool needIntrinsicImageSize = PR_FALSE; nscoord newWidth=0, newHeight=0; if (fixedContentWidth) { newWidth = MINMAX(widthConstraint, minWidth, maxWidth); if (fixedContentHeight) { newHeight = MINMAX(heightConstraint, minHeight, maxHeight); haveComputedSize = PR_TRUE; } else { // We have a width, and an auto height. Compute height from // width once we have the intrinsic image size. if (mIntrinsicSize.height != 0) { newHeight = (mIntrinsicSize.height * newWidth) / mIntrinsicSize.width; haveComputedSize = PR_TRUE; } else { newHeight = 0; needIntrinsicImageSize = PR_TRUE; } } } else if (fixedContentHeight) { // We have a height, and an auto width. Compute width from height // once we have the intrinsic image size. newHeight = MINMAX(heightConstraint, minHeight, maxHeight); if (mIntrinsicSize.width != 0) { newWidth = (mIntrinsicSize.width * newHeight) / mIntrinsicSize.height; haveComputedSize = PR_TRUE; } else { newWidth = 0; needIntrinsicImageSize = PR_TRUE; } } else { // auto size the image if (mIntrinsicSize.width == 0 && mIntrinsicSize.height == 0) needIntrinsicImageSize = PR_TRUE; else haveComputedSize = PR_TRUE; newWidth = mIntrinsicSize.width; newHeight = mIntrinsicSize.height; } mComputedSize.width = newWidth; mComputedSize.height = newHeight; #if 0 // don't do scaled images in bullets if (mComputedSize == mIntrinsicSize) { mTransform.SetToIdentity(); } else { if (mComputedSize.width != 0 && mComputedSize.height != 0) { mTransform.SetToScale(float(mIntrinsicSize.width) / float(mComputedSize.width), float(mIntrinsicSize.height) / float(mComputedSize.height)); } } #endif aMetrics.width = mComputedSize.width; aMetrics.height = mComputedSize.height; aMetrics.ascent = aMetrics.height; aMetrics.descent = 0; return; } } const nsStyleFont* myFont = GetStyleFont(); nsCOMPtr fm; aCX->GetMetricsFor(myFont->mFont, getter_AddRefs(fm)); nscoord bulletSize; float p2t; float t2p; nsAutoString text; switch (myList->mListStyleType) { case NS_STYLE_LIST_STYLE_NONE: aMetrics.width = 0; aMetrics.height = 0; aMetrics.ascent = 0; aMetrics.descent = 0; break; case NS_STYLE_LIST_STYLE_DISC: case NS_STYLE_LIST_STYLE_CIRCLE: case NS_STYLE_LIST_STYLE_BASIC: case NS_STYLE_LIST_STYLE_SQUARE: aCX->GetTwipsToPixels(&t2p); fm->GetMaxAscent(ascent); bulletSize = NSTwipsToIntPixels( (nscoord)NSToIntRound(0.8f * (float(ascent) / 2.0f)), t2p); if (bulletSize < 1) { bulletSize = MIN_BULLET_SIZE; } aCX->GetPixelsToTwips(&p2t); bulletSize = NSIntPixelsToTwips(bulletSize, p2t); mPadding.bottom = NSIntPixelsToTwips((nscoord) NSToIntRound((float)ascent / (8.0f * p2t)),p2t); aMetrics.width = mPadding.right + bulletSize; aMetrics.height = mPadding.bottom + bulletSize; aMetrics.ascent = mPadding.bottom + bulletSize; aMetrics.descent = 0; break; default: case NS_STYLE_LIST_STYLE_DECIMAL_LEADING_ZERO: case NS_STYLE_LIST_STYLE_DECIMAL: case NS_STYLE_LIST_STYLE_OLD_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_OLD_LOWER_ROMAN: case NS_STYLE_LIST_STYLE_OLD_UPPER_ROMAN: case NS_STYLE_LIST_STYLE_OLD_LOWER_ALPHA: case NS_STYLE_LIST_STYLE_OLD_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(aCX, *myList, text); fm->GetHeight(aMetrics.height); aReflowState.rendContext->SetFont(fm); aReflowState.rendContext->GetWidth(text, aMetrics.width); aMetrics.width += mPadding.right; fm->GetMaxAscent(aMetrics.ascent); fm->GetMaxDescent(aMetrics.descent); break; } } NS_IMETHODIMP nsBulletFrame::Reflow(nsIPresContext* aPresContext, nsHTMLReflowMetrics& aMetrics, const nsHTMLReflowState& aReflowState, nsReflowStatus& aStatus) { DO_GLOBAL_REFLOW_COUNT("nsBulletFrame", aReflowState.reason); DISPLAY_REFLOW(aPresContext, this, aReflowState, aMetrics, aStatus); PRBool isStyleChange = PR_FALSE; if (eReflowReason_StyleChange == aReflowState.reason) isStyleChange = PR_TRUE; else if (eReflowReason_Incremental == aReflowState.reason) { // XXXwaterson The reflow had better be targeted at this frame. nsReflowType type; aReflowState.path->mReflowCommand->GetType(type); /* if the style changed, see if we need to load a new url */ if (eReflowType_StyleChanged == type) isStyleChange = PR_TRUE; } if (isStyleChange) { nsCOMPtr baseURI; GetBaseURI(getter_AddRefs(baseURI)); const nsStyleList* myList = GetStyleList(); if (!myList->mListStyleImage.IsEmpty()) { if (!mListener) { nsBulletListener *listener; NS_NEWXPCOM(listener, nsBulletListener); NS_ADDREF(listener); listener->SetFrame(this); listener->QueryInterface(NS_GET_IID(imgIDecoderObserver), getter_AddRefs(mListener)); NS_ASSERTION(mListener, "queryinterface for the listener failed"); NS_RELEASE(listener); } nsCOMPtr newURI; NS_NewURI(getter_AddRefs(newURI), myList->mListStyleImage, nsnull, baseURI); PRBool needNewRequest = PR_TRUE; if (mImageRequest) { // Reload the image, maybe... nsCOMPtr oldURI; mImageRequest->GetURI(getter_AddRefs(oldURI)); if (oldURI) { PRBool same; newURI->Equals(oldURI, &same); if (same) { needNewRequest = PR_FALSE; } else { mImageRequest->Cancel(NS_ERROR_FAILURE); mImageRequest = nsnull; } } } if (needNewRequest) { nsresult rv; nsCOMPtr il(do_GetService("@mozilla.org/image/loader;1", &rv)); if (NS_FAILED(rv)) return rv; nsCOMPtr loadGroup; GetLoadGroup(aPresContext, getter_AddRefs(loadGroup)); // Get the document URI for the referrer... nsCOMPtr documentURI; nsCOMPtr doc; if (mContent) { (void) mContent->GetDocument(getter_AddRefs(doc)); if (doc) { doc->GetDocumentURL(getter_AddRefs(documentURI)); } } // XXX: initialDocumentURI is NULL ! il->LoadImage(newURI, nsnull, documentURI, loadGroup, mListener, doc, nsIRequest::LOAD_NORMAL, nsnull, nsnull, getter_AddRefs(mImageRequest)); } } } // Get the base size GetDesiredSize(aPresContext, aReflowState, aMetrics); // 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; aMetrics.descent += borderPadding.bottom; if (aMetrics.mComputeMEW) { aMetrics.mMaxElementWidth = aMetrics.width; } aStatus = NS_FRAME_COMPLETE; NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aMetrics); return NS_OK; } NS_IMETHODIMP nsBulletFrame::OnStartContainer(imgIRequest *aRequest, imgIContainer *aImage) { if (!aImage) return NS_ERROR_INVALID_ARG; if (!aRequest) return NS_ERROR_INVALID_ARG; NS_ENSURE_TRUE(mPresContext, NS_ERROR_UNEXPECTED); // Why are we bothering? PRUint32 status; aRequest->GetImageStatus(&status); if (status & imgIRequest::STATUS_ERROR) { return NS_OK; } nscoord w, h; aImage->GetWidth(&w); aImage->GetHeight(&h); float p2t; mPresContext->GetPixelsToTwips(&p2t); nsSize newsize(NSIntPixelsToTwips(w, p2t), NSIntPixelsToTwips(h, p2t)); if (mIntrinsicSize != newsize) { mIntrinsicSize = newsize; // Now that the size is available (or an error occurred), trigger // a reflow of the bullet frame. nsCOMPtr shell; nsresult rv = mPresContext->GetShell(getter_AddRefs(shell)); if (NS_SUCCEEDED(rv) && shell) { NS_ASSERTION(mParent, "No parent to pass the reflow request up to."); if (mParent) { // Reflow the first child of the parent not the bullet frame. // The bullet frame is not in a line list so marking it dirty // has no effect. The reflowing of the bullet frame is done // indirectly. nsIFrame* frame = nsnull; mParent->FirstChild(mPresContext, nsnull, &frame); NS_ASSERTION(frame, "No frame to mark dirty for bullet frame."); if (frame) { nsFrameState state; frame->GetFrameState(&state); state |= NS_FRAME_IS_DIRTY; frame->SetFrameState(state); mParent->ReflowDirtyChild(shell, frame); } } } } return NS_OK; } NS_IMETHODIMP nsBulletFrame::OnDataAvailable(imgIRequest *aRequest, gfxIImageFrame *aFrame, const nsRect *aRect) { if (!aRect) return NS_ERROR_NULL_POINTER; NS_ENSURE_TRUE(mPresContext, NS_ERROR_UNEXPECTED); // Why are we bothering? // XXX Should we do anything here if an error occured in the decode? nsRect r(*aRect); /* XXX Why do we subtract 1 here? The rect is (for example): (0, 0, 600, 1).. Why do we have to make y -1? */ // The y coordinate of aRect is passed as a scanline where the first scanline is given // a value of 1. We need to convert this to the nsFrames coordinate space by subtracting // 1. r.y -= 1; float p2t; mPresContext->GetPixelsToTwips(&p2t); r.x = NSIntPixelsToTwips(r.x, p2t); r.y = NSIntPixelsToTwips(r.y, p2t); r.width = NSIntPixelsToTwips(r.width, p2t); r.height = NSIntPixelsToTwips(r.height, p2t); Invalidate(mPresContext, r, PR_FALSE); 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 NS_ENSURE_TRUE(mPresContext, NS_ERROR_UNEXPECTED); // Why are we bothering? nsCOMPtr presShell; mPresContext->GetShell(getter_AddRefs(presShell)); if (NS_FAILED(aStatus)) { // We failed to load the image. Notify the pres shell if (NS_FAILED(aStatus) && (mImageRequest == aRequest || !mImageRequest)) { imageFailed = PR_TRUE; } } #endif return NS_OK; } NS_IMETHODIMP nsBulletFrame::FrameChanged(imgIContainer *aContainer, gfxIImageFrame *aNewFrame, nsRect *aDirtyRect) { NS_ENSURE_TRUE(mPresContext, NS_ERROR_UNEXPECTED); // Why are we bothering? nsRect r(*aDirtyRect); float p2t; mPresContext->GetPixelsToTwips(&p2t); r.x = NSIntPixelsToTwips(r.x, p2t); r.y = NSIntPixelsToTwips(r.y, p2t); r.width = NSIntPixelsToTwips(r.width, p2t); r.height = NSIntPixelsToTwips(r.height, p2t); Invalidate(mPresContext, r, PR_FALSE); return NS_OK; } void nsBulletFrame::GetBaseURI(nsIURI **aURI) { NS_PRECONDITION(nsnull != aURI, "null OUT parameter pointer"); nsresult rv; nsCOMPtr baseURI; nsCOMPtr htmlContent(do_QueryInterface(mContent, &rv)); if (NS_SUCCEEDED(rv)) { htmlContent->GetBaseURL(getter_AddRefs(baseURI)); } else { nsCOMPtr doc; mContent->GetDocument(getter_AddRefs(doc)); if (doc) { doc->GetBaseURL(getter_AddRefs(baseURI)); } } *aURI = baseURI; NS_IF_ADDREF(*aURI); } void nsBulletFrame::GetLoadGroup(nsIPresContext *aPresContext, nsILoadGroup **aLoadGroup) { if (!aPresContext) return; NS_PRECONDITION(nsnull != aLoadGroup, "null OUT parameter pointer"); nsCOMPtr shell; aPresContext->GetShell(getter_AddRefs(shell)); if (!shell) return; nsCOMPtr doc; shell->GetDocument(getter_AddRefs(doc)); if (!doc) return; doc->GetDocumentLoadGroup(aLoadGroup); } NS_IMPL_ISUPPORTS2(nsBulletListener, imgIDecoderObserver, imgIContainerObserver) nsBulletListener::nsBulletListener() : mFrame(nsnull) { } nsBulletListener::~nsBulletListener() { } NS_IMETHODIMP nsBulletListener::OnStartDecode(imgIRequest *aRequest) { return NS_OK; } NS_IMETHODIMP nsBulletListener::OnStartContainer(imgIRequest *aRequest, imgIContainer *aImage) { if (!mFrame) return NS_ERROR_FAILURE; return mFrame->OnStartContainer(aRequest, aImage); } NS_IMETHODIMP nsBulletListener::OnStartFrame(imgIRequest *aRequest, gfxIImageFrame *aFrame) { return NS_OK; } NS_IMETHODIMP nsBulletListener::OnDataAvailable(imgIRequest *aRequest, gfxIImageFrame *aFrame, const nsRect *aRect) { if (!mFrame) return NS_ERROR_FAILURE; return mFrame->OnDataAvailable(aRequest, aFrame, aRect); } NS_IMETHODIMP nsBulletListener::OnStopFrame(imgIRequest *aRequest, gfxIImageFrame *aFrame) { return NS_OK; } NS_IMETHODIMP nsBulletListener::OnStopContainer(imgIRequest *aRequest, imgIContainer *aImage) { return NS_OK; } NS_IMETHODIMP nsBulletListener::OnStopDecode(imgIRequest *aRequest, nsresult status, const PRUnichar *statusArg) { if (!mFrame) return NS_ERROR_FAILURE; return mFrame->OnStopDecode(aRequest, status, statusArg); } NS_IMETHODIMP nsBulletListener::FrameChanged(imgIContainer *aContainer, gfxIImageFrame *newframe, nsRect * dirtyRect) { if (!mFrame) return NS_ERROR_FAILURE; return mFrame->FrameChanged(aContainer, newframe, dirtyRect); }