/* -*- 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"
#include "nsContentUtils.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);

  nsIURI *imgURI = GetStyleList()->mListStyleImage;
  if (imgURI) {
    nsCOMPtr<imgILoader> il(do_GetService("@mozilla.org/image/loader;1", &rv));
    if (NS_FAILED(rv))
      return rv;

    nsCOMPtr<nsILoadGroup> loadGroup;
    GetLoadGroup(aPresContext, getter_AddRefs(loadGroup));

    // Get the document URI for the referrer...
    nsIURI *documentURI = nsnull;
    nsCOMPtr<nsIDocument> doc;
    if (mContent) {
      doc = mContent->GetDocument();
      if (doc) {
        documentURI = doc->GetDocumentURI();
      }
    }

    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);
    }

    if (imgURI && NS_SUCCEEDED(nsContentUtils::CanLoadImage(imgURI, doc, doc))) {
    // 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

nsIAtom*
nsBulletFrame::GetType() const
{
  return nsLayoutAtoms::bulletFrame;
}

#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 && 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 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<nsIFontMetrics> 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_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;
  nsIContent* parentContent = mParent->GetContent();
  if (parentContent) {
    nsCOMPtr<nsIHTMLContent> 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 = result.BeginWriting();
   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 = result.BeginWriting();
   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 || ordinal > 3999) {
    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 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 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;
  }
  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;

    PRBool 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 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)
{
  // Reset our padding.  If we need it, we'll set it below.
  mPadding.SizeTo(0, 0, 0, 0);
  
  const nsStyleList* myList = GetStyleList();
  nscoord ascent;

  if (myList->mListStyleImage && 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<nsIFontMetrics> 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_SQUARE:
      t2p = aCX->TwipsToPixels();
      fm->GetMaxAscent(ascent);
      bulletSize = NSTwipsToIntPixels(
        (nscoord)NSToIntRound(0.8f * (float(ascent) / 2.0f)), t2p);
      if (bulletSize < 1) {
        bulletSize = MIN_BULLET_SIZE;
      }
      p2t = aCX->PixelsToTwips();
      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) {
    nsIURI *newURI = GetStyleList()->mListStyleImage;

    if (newURI) {

      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);
      }

      PRBool needNewRequest = PR_TRUE;

      if (mImageRequest) {
        // Reload the image, maybe...
        nsCOMPtr<nsIURI> 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<imgILoader> il(do_GetService("@mozilla.org/image/loader;1", &rv));
        if (NS_FAILED(rv))
          return rv;

        nsCOMPtr<nsILoadGroup> loadGroup;
        GetLoadGroup(aPresContext, getter_AddRefs(loadGroup));

        // Get the document URI for the referrer...
        nsIURI* documentURI = nsnull;
        nsCOMPtr<nsIDocument> doc;
        if (mContent) {
          doc = mContent->GetDocument();
          if (doc) {
            documentURI = doc->GetDocumentURI();
          }
        }


        // 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;
  p2t = mPresContext->PixelsToTwips();

  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.
    nsIPresShell *shell = mPresContext->GetPresShell();
    if (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 = mParent->GetFirstChild(nsnull);
        NS_ASSERTION(frame, "No frame to mark dirty for bullet frame.");
        if (frame) {
          frame->AddStateBits(NS_FRAME_IS_DIRTY);
          mParent->ReflowDirtyChild(shell, frame);
        }
      }
    }
  }

  // Handle animations
  aImage->SetAnimationMode(mPresContext->ImageAnimationMode());
  // Ensure the animation (if any) is started.
  aImage->StartAnimation();

  
  return NS_OK;
}

NS_IMETHODIMP nsBulletFrame::OnDataAvailable(imgIRequest *aRequest,
                                             gfxIImageFrame *aFrame,
                                             const nsRect *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
  Invalidate(nsRect(0, 0, mRect.width, mRect.height), 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?
  
  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)
{
  // Invalidate the entire content area. Maybe it's not optimal but it's simple and
  // always correct.
  Invalidate(nsRect(0, 0, mRect.width, mRect.height), PR_FALSE);

  return NS_OK;
}

void
nsBulletFrame::GetLoadGroup(nsIPresContext *aPresContext, nsILoadGroup **aLoadGroup)
{
  if (!aPresContext)
    return;

  NS_PRECONDITION(nsnull != aLoadGroup, "null OUT parameter pointer");

  nsIPresShell *shell = aPresContext->GetPresShell();

  if (!shell)
    return;

  nsCOMPtr<nsIDocument> doc;
  shell->GetDocument(getter_AddRefs(doc));
  if (!doc)
    return;

  *aLoadGroup = doc->GetDocumentLoadGroup().get();  // already_AddRefed
}








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);
}
