/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Netscape
 * Communications Corporation.  Portions created by Netscape are
 * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
 * Reserved.
 */
#include "nsICSSStyleRule.h"
#include "nsICSSDeclaration.h"
#include "nsIStyleContext.h"
#include "nsIPresContext.h"
#include "nsIArena.h"
#include "nsIAtom.h"
#include "nsCRT.h"
#include "nsString.h"
#include "nsStyleConsts.h"
#include "nsUnitConversion.h"

static NS_DEFINE_IID(kIStyleRuleIID, NS_ISTYLE_RULE_IID);
static NS_DEFINE_IID(kICSSDeclarationIID, NS_ICSS_DECLARATION_IID);
static NS_DEFINE_IID(kICSSStyleRuleIID, NS_ICSS_STYLE_RULE_IID);

static NS_DEFINE_IID(kStyleFontSID, NS_STYLEFONT_SID);
static NS_DEFINE_IID(kStyleColorSID, NS_STYLECOLOR_SID);
static NS_DEFINE_IID(kStyleListSID, NS_STYLELIST_SID);
static NS_DEFINE_IID(kStyleMoleculeSID, NS_STYLEMOLECULE_SID);

static NS_DEFINE_IID(kCSSFontSID, NS_CSS_FONT_SID);
static NS_DEFINE_IID(kCSSColorSID, NS_CSS_COLOR_SID);
static NS_DEFINE_IID(kCSSTextSID, NS_CSS_TEXT_SID);
static NS_DEFINE_IID(kCSSMarginSID, NS_CSS_MARGIN_SID);
static NS_DEFINE_IID(kCSSPositionSID, NS_CSS_POSITION_SID);
static NS_DEFINE_IID(kCSSListSID, NS_CSS_LIST_SID);


// -- nsCSSSelector -------------------------------

nsCSSSelector::nsCSSSelector()
  : mTag(nsnull), mID(nsnull), mClass(nsnull), mPseudoClass(nsnull),
    mNext(nsnull)
{
}

nsCSSSelector::nsCSSSelector(nsIAtom* aTag, nsIAtom* aID, nsIAtom* aClass, nsIAtom* aPseudoClass)
  : mTag(aTag), mID(aID), mClass(aClass), mPseudoClass(aPseudoClass),
    mNext(nsnull)
{
  NS_IF_ADDREF(mTag);
  NS_IF_ADDREF(mID);
  NS_IF_ADDREF(mClass);
  NS_IF_ADDREF(mPseudoClass);
}

nsCSSSelector::nsCSSSelector(const nsCSSSelector& aCopy) 
  : mTag(aCopy.mTag), mID(aCopy.mID), mClass(aCopy.mClass), mPseudoClass(aCopy.mPseudoClass),
    mNext(nsnull)
{ // implmented to support extension to CSS2 (when we have to copy the array)
  NS_IF_ADDREF(mTag);
  NS_IF_ADDREF(mID);
  NS_IF_ADDREF(mClass);
  NS_IF_ADDREF(mPseudoClass);
}

nsCSSSelector::~nsCSSSelector()  
{  
  NS_IF_RELEASE(mTag);
  NS_IF_RELEASE(mID);
  NS_IF_RELEASE(mClass);
  NS_IF_RELEASE(mPseudoClass);
}

nsCSSSelector& nsCSSSelector::operator=(const nsCSSSelector& aCopy)
{
  NS_IF_RELEASE(mTag);
  NS_IF_RELEASE(mID);
  NS_IF_RELEASE(mClass);
  NS_IF_RELEASE(mPseudoClass);
  mTag          = aCopy.mTag;
  mID           = aCopy.mID;
  mClass        = aCopy.mClass;
  mPseudoClass  = aCopy.mPseudoClass;
  NS_IF_ADDREF(mTag);
  NS_IF_ADDREF(mID);
  NS_IF_ADDREF(mClass);
  NS_IF_ADDREF(mPseudoClass);
  return *this;
}

PRBool nsCSSSelector::Equals(const nsCSSSelector* aOther) const
{
  if (nsnull != aOther) {
    return (PRBool)((aOther->mTag == mTag) && (aOther->mID == mID) && 
                    (aOther->mClass == mClass) && (aOther->mPseudoClass == mPseudoClass));
  }
  return PR_FALSE;
}


void nsCSSSelector::Set(const nsString& aTag, const nsString& aID, 
                        const nsString& aClass, const nsString& aPseudoClass)
{
  NS_IF_RELEASE(mTag);
  NS_IF_RELEASE(mID);
  NS_IF_RELEASE(mClass);
  NS_IF_RELEASE(mPseudoClass);
  if (0 < aTag.Length()) {
    mTag = NS_NewAtom(aTag);
  }
  if (0 < aID.Length()) {
    mID = NS_NewAtom(aID);
  }
  if (0 < aClass.Length()) {
    mClass = NS_NewAtom(aClass);
  }
  if (0 < aPseudoClass.Length()) {
    mPseudoClass = NS_NewAtom(aPseudoClass);
  }
}

// -- nsCSSStyleRule -------------------------------

class CSSStyleRuleImpl : public nsICSSStyleRule {
public:
  void* operator new(size_t size);
  void* operator new(size_t size, nsIArena* aArena);
  void operator delete(void* ptr);

  CSSStyleRuleImpl(const nsCSSSelector& aSelector);

  NS_IMETHOD QueryInterface(const nsIID& aIID, void** aInstancePtr);
  NS_IMETHOD_(nsrefcnt) AddRef();
  NS_IMETHOD_(nsrefcnt) Release();

  virtual PRBool Equals(const nsIStyleRule* aRule) const;
  virtual PRUint32 HashValue(void) const;

  virtual nsCSSSelector* FirstSelector(void);
  virtual void AddSelector(const nsCSSSelector& aSelector);
  virtual void DeleteSelector(nsCSSSelector* aSelector);

  virtual nsICSSDeclaration* GetDeclaration(void) const;
  virtual void SetDeclaration(nsICSSDeclaration* aDeclaration);

  virtual PRInt32 GetWeight(void) const;
  virtual void SetWeight(PRInt32 aWeight);

  virtual nscoord CalcLength(const nsCSSValue& aValue, nsStyleFont* aFont, 
                             nsIPresContext* aPresContext);
  virtual void MapStyleInto(nsIStyleContext* aContext, nsIPresContext* aPresContext);

  virtual void List(FILE* out = stdout, PRInt32 aIndent = 0) const;

private: 
  // These are not supported and are not implemented! 
  CSSStyleRuleImpl(const CSSStyleRuleImpl& aCopy); 
  CSSStyleRuleImpl& operator=(const CSSStyleRuleImpl& aCopy); 

protected:
  virtual ~CSSStyleRuleImpl();

protected:
  PRUint32 mInHeap : 1;
  PRUint32 mRefCnt : 31;

  nsCSSSelector   mSelector;
  nsICSSDeclaration* mDeclaration;
  PRInt32         mWeight;
};


void* CSSStyleRuleImpl::operator new(size_t size)
{
  CSSStyleRuleImpl* rv = (CSSStyleRuleImpl*) ::operator new(size);
#ifdef NS_DEBUG
  if (nsnull != rv) {
    nsCRT::memset(rv, 0xEE, size);
  }
#endif
  rv->mInHeap = 1;
  return (void*) rv;
}

void* CSSStyleRuleImpl::operator new(size_t size, nsIArena* aArena)
{
  CSSStyleRuleImpl* rv = (CSSStyleRuleImpl*) aArena->Alloc(PRInt32(size));
#ifdef NS_DEBUG
  if (nsnull != rv) {
    nsCRT::memset(rv, 0xEE, size);
  }
#endif
  rv->mInHeap = 0;
  return (void*) rv;
}

void CSSStyleRuleImpl::operator delete(void* ptr)
{
  CSSStyleRuleImpl* rule = (CSSStyleRuleImpl*) ptr;
  if (nsnull != rule) {
    if (rule->mInHeap) {
      ::delete ptr;
    }
  }
}



CSSStyleRuleImpl::CSSStyleRuleImpl(const nsCSSSelector& aSelector)
  : mSelector(aSelector), mDeclaration(nsnull), mWeight(0)
{
  NS_INIT_REFCNT();
}

CSSStyleRuleImpl::~CSSStyleRuleImpl()
{
  nsCSSSelector*  next = mSelector.mNext;

  while (nsnull != next) {
    nsCSSSelector*  selector = next;
    next = selector->mNext;
    delete selector;
  }
  NS_IF_RELEASE(mDeclaration);
}

NS_IMPL_ADDREF(CSSStyleRuleImpl)
NS_IMPL_RELEASE(CSSStyleRuleImpl)

nsresult CSSStyleRuleImpl::QueryInterface(const nsIID& aIID,
                                            void** aInstancePtrResult)
{
  NS_PRECONDITION(nsnull != aInstancePtrResult, "null pointer");
  if (nsnull == aInstancePtrResult) {
    return NS_ERROR_NULL_POINTER;
  }
  static NS_DEFINE_IID(kISupportsIID, NS_ISUPPORTS_IID);
  if (aIID.Equals(kICSSStyleRuleIID)) {
    *aInstancePtrResult = (void*) ((nsICSSStyleRule*)this);
    AddRef();
    return NS_OK;
  }
  if (aIID.Equals(kIStyleRuleIID)) {
    *aInstancePtrResult = (void*) ((nsIStyleRule*)this);
    AddRef();
    return NS_OK;
  }
  if (aIID.Equals(kISupportsIID)) {
    *aInstancePtrResult = (void*) ((nsISupports*)this);
    AddRef();
    return NS_OK;
  }
  return NS_NOINTERFACE;
}


PRBool CSSStyleRuleImpl::Equals(const nsIStyleRule* aRule) const
{
  nsICSSStyleRule* iCSSRule;

  if (this == aRule) {
    return PR_TRUE;
  }

  if ((nsnull != aRule) && 
      (NS_OK == ((nsIStyleRule*)aRule)->QueryInterface(kICSSStyleRuleIID, (void**) &iCSSRule))) {

    CSSStyleRuleImpl* rule = (CSSStyleRuleImpl*)iCSSRule;
    const nsCSSSelector* local = &mSelector;
    const nsCSSSelector* other = &(rule->mSelector);
    PRBool  result = PR_TRUE;

    while ((PR_TRUE == result) && (nsnull != local) && (nsnull != other)) {
      if (! local->Equals(other)) {
        result = PR_FALSE;
      }
      local = local->mNext;
      other = other->mNext;
    }
    if ((nsnull != local) || (nsnull != other)) { // more were left
      result = PR_FALSE;
    }
    if ((rule->mDeclaration != mDeclaration) || 
        (rule->mWeight != mWeight)) {
      result = PR_FALSE;
    }
    NS_RELEASE(iCSSRule);
    return result;
  }
  return PR_FALSE;
}

PRUint32 CSSStyleRuleImpl::HashValue(void) const
{
  return (PRUint32)this;
}

nsCSSSelector* CSSStyleRuleImpl::FirstSelector(void)
{
  return &mSelector;
}

void CSSStyleRuleImpl::AddSelector(const nsCSSSelector& aSelector)
{
  nsCSSSelector*  selector = new nsCSSSelector(aSelector);
  nsCSSSelector*  last = &mSelector;

  while (nsnull != last->mNext) {
    last = last->mNext;
  }
  last->mNext = selector;
}


void CSSStyleRuleImpl::DeleteSelector(nsCSSSelector* aSelector)
{
  if (nsnull != aSelector) {
    if (&mSelector == aSelector) {  // handle first selector
      mSelector = *aSelector; // assign value
      mSelector.mNext = aSelector->mNext;
      delete aSelector;
    }
    else {
      nsCSSSelector*  selector = &mSelector;

      while (nsnull != selector->mNext) {
        if (aSelector == selector->mNext) {
          selector->mNext = aSelector->mNext;
          delete aSelector;
          return;
        }
        selector = selector->mNext;
      }
    }
  }
}

nsICSSDeclaration* CSSStyleRuleImpl::GetDeclaration(void) const
{
  NS_IF_ADDREF(mDeclaration);
  return mDeclaration;
}

void CSSStyleRuleImpl::SetDeclaration(nsICSSDeclaration* aDeclaration)
{
  NS_IF_RELEASE(mDeclaration);
  mDeclaration = aDeclaration;
  NS_IF_ADDREF(mDeclaration);
}

PRInt32 CSSStyleRuleImpl::GetWeight(void) const
{
  return mWeight;
}

void CSSStyleRuleImpl::SetWeight(PRInt32 aWeight)
{
  mWeight = aWeight;
}

nscoord CSSStyleRuleImpl::CalcLength(const nsCSSValue& aValue, nsStyleFont* aFont, 
                                     nsIPresContext* aPresContext)
{
  NS_ASSERTION(aValue.IsLengthUnit(), "not a length unit");
  if (aValue.IsFixedLengthUnit()) {
    return aValue.GetLengthTwips();
  }
  nsCSSUnit unit = aValue.GetUnit();
  switch (unit) {
    case eCSSUnit_EM:
      return aFont->mFont.size;
    case eCSSUnit_EN:
      return (aFont->mFont.size / 2);
    case eCSSUnit_XHeight:
      NS_NOTYETIMPLEMENTED("x height unit");
      return ((aFont->mFont.size / 3) * 2); // XXX HACK!
    case eCSSUnit_CapHeight:
      NS_NOTYETIMPLEMENTED("cap height unit");
      return ((aFont->mFont.size / 3) * 2); // XXX HACK!

    case eCSSUnit_Pixel:
      return (nscoord)(aPresContext->GetPixelsToTwips() * aValue.GetFloatValue());
  }
  return 0;
}

void CSSStyleRuleImpl::MapStyleInto(nsIStyleContext* aContext, nsIPresContext* aPresContext)
{
  if (nsnull != mDeclaration) {
    nsStyleFont*  font = (nsStyleFont*)aContext->GetData(kStyleFontSID);

    nsCSSFont*  ourFont;
    if (NS_OK == mDeclaration->GetData(kCSSFontSID, (nsCSSStruct**)&ourFont)) {
      if (nsnull != ourFont) {
        nsStyleFont* parentFont = font;
        nsIStyleContext* parentContext = aContext->GetParent();
        if (nsnull != parentContext) {
          parentFont = (nsStyleFont*)parentContext->GetData(kStyleFontSID);
        }

        // font-family: string list
        if (ourFont->mFamily.GetUnit() == eCSSUnit_String) {
          nsAutoString familyList;
          ourFont->mFamily.GetStringValue(familyList);
          // XXX meeds font support to determine usable fonts
          // parse up the CSS string & remove the quotes
          // XXX only does first until we can tell what are installed fonts
          nsAutoString family;
          PRInt32 index = familyList.Find(PRUnichar(','));
          if (-1 < index) {
            familyList.Left(family, index);
          }
          else {
            family.Append(familyList);
          }
          family.StripChars("\"");
          family.StripWhitespace();

          font->mFont.name = family;
        }

        // font-style: enum
        if (ourFont->mStyle.GetUnit() == eCSSUnit_Enumerated) {
          font->mFont.style = ourFont->mStyle.GetIntValue();
        }

        // font-variant: enum
        if (ourFont->mVariant.GetUnit() == eCSSUnit_Enumerated) {
          font->mFont.variant = ourFont->mVariant.GetIntValue();
        }

        // font-weight: abs, enum
        if (ourFont->mWeight.GetUnit() == eCSSUnit_Absolute) {
          font->mFont.style = ourFont->mWeight.GetIntValue();
        }
        else if (ourFont->mWeight.GetUnit() == eCSSUnit_Enumerated) {
          PRInt32 value = ourFont->mWeight.GetIntValue();
          switch (value) {
            case NS_STYLE_FONT_WEIGHT_NORMAL:
            case NS_STYLE_FONT_WEIGHT_BOLD:
              font->mFont.weight = value;
              break;
            case NS_STYLE_FONT_WEIGHT_BOLDER:
            case NS_STYLE_FONT_WEIGHT_LIGHTER:
              font->mFont.weight = (parentFont->mFont.weight + value);
              break;
          }
        }

        // font-size: enum, length, percent
        if (ourFont->mSize.GetUnit() == eCSSUnit_Enumerated) {
          static float kFontScale[7] = {
            0.5f,                 // xx-small
            0.666667f,            // x-small
            0.833333f,            // small
            1.0f,                 // medium
            1.5f,                 // large
            1.5f * 1.5f,          // x-large
            1.5f * 1.5f * 1.5f,   // xx-large
          };
          PRInt32 value = ourFont->mSize.GetIntValue();

          const nsFont& normal = aPresContext->GetDefaultFont();  // use normal font or body font??
          if ((NS_STYLE_FONT_SIZE_XXSMALL <= value) && 
              (value <= NS_STYLE_FONT_SIZE_XXLARGE)) {
            font->mFont.size = (nscoord)((float)normal.size * kFontScale[value]);
          }
          else if (NS_STYLE_FONT_SIZE_LARGER == value) {
            PRInt32 index;
            for (index = NS_STYLE_FONT_SIZE_XXSMALL;
                 index < NS_STYLE_FONT_SIZE_XXLARGE; index++)
              if (parentFont->mFont.size < (nscoord)((float)normal.size * kFontScale[index]))
                break;
            font->mFont.size = (nscoord)((float)normal.size * kFontScale[index]);
          }
          else if (NS_STYLE_FONT_SIZE_SMALLER == value) {
            PRInt32 index;
            for (index = NS_STYLE_FONT_SIZE_XXLARGE;
                 index > NS_STYLE_FONT_SIZE_XXSMALL; index--)
              if (parentFont->mFont.size > (nscoord)((float)normal.size * kFontScale[index]))
                break;
            font->mFont.size = (nscoord)((float)normal.size * kFontScale[index]);
          }
        }
        else if (ourFont->mSize.IsLengthUnit()) {
          font->mFont.size = CalcLength(ourFont->mSize, parentFont, aPresContext);
        }
        else if (ourFont->mSize.GetUnit() == eCSSUnit_Percent) {
          font->mFont.size = (nscoord)((float)(parentFont->mFont.size) * ourFont->mSize.GetFloatValue());
        }

        NS_IF_RELEASE(parentContext);
      }
    }

    nsCSSText*  ourText;
    if (NS_OK == mDeclaration->GetData(kCSSTextSID, (nsCSSStruct**)&ourText)) {
      if (nsnull != ourText) {

        // text-decoration: enum, absolute (bit field)
        if (ourText->mDecoration.GetUnit() == eCSSUnit_Enumerated) {
          font->mFont.decorations = ourText->mDecoration.GetIntValue();
        }
        else if (ourText->mDecoration.GetUnit() == eCSSUnit_Absolute) {
          font->mFont.decorations = ourText->mDecoration.GetIntValue();
        }
      }
    }


    nsCSSColor*  ourColor;
    if (NS_OK == mDeclaration->GetData(kCSSColorSID, (nsCSSStruct**)&ourColor)) {
      if (nsnull != ourColor) {
        nsStyleColor* color = (nsStyleColor*)aContext->GetData(kStyleColorSID);

        // color: color
        if (ourColor->mColor.GetUnit() == eCSSUnit_Color) {
          color->mColor = ourColor->mColor.GetColorValue();
        }

        // background-color: color, enum (flags)
        if (ourColor->mBackColor.GetUnit() == eCSSUnit_Color) {
          color->mBackgroundColor = ourColor->mBackColor.GetColorValue();
          color->mBackgroundFlags &= ~NS_STYLE_BG_COLOR_TRANSPARENT;
        }
        else if (ourColor->mBackColor.GetUnit() == eCSSUnit_Enumerated) {
          color->mBackgroundFlags |= NS_STYLE_BG_COLOR_TRANSPARENT;
        }

        // background-image: string, enum (flags)
        if (ourColor->mBackImage.GetUnit() == eCSSUnit_String) {
          ourColor->mBackImage.GetStringValue(color->mBackgroundImage);
          color->mBackgroundFlags &= ~NS_STYLE_BG_IMAGE_NONE;
        }
        else if (ourColor->mBackImage.GetUnit() == eCSSUnit_Enumerated) {
          color->mBackgroundFlags |= NS_STYLE_BG_IMAGE_NONE;
        }

        // background-repeat: enum
        if (ourColor->mBackRepeat.GetUnit() == eCSSUnit_Enumerated) {
          color->mBackgroundRepeat = ourColor->mBackRepeat.GetIntValue();
        }

        // background-attachment: enum
        if (ourColor->mBackAttachment.GetUnit() == eCSSUnit_Enumerated) {
          color->mBackgroundAttachment = ourColor->mBackAttachment.GetIntValue();
        }

        // background-position: length, percent (flags)
        if (ourColor->mBackPositionX.GetUnit() == eCSSUnit_Percent) {
          color->mBackgroundXPosition = (nscoord)(TWIPS_CONST_FLOAT * ourColor->mBackPositionX.GetFloatValue());
          color->mBackgroundFlags |= NS_STYLE_BG_X_POSITION_PCT;
          color->mBackgroundFlags &= ~NS_STYLE_BG_X_POSITION_LENGTH;
        }
        else if (ourColor->mBackPositionX.IsLengthUnit()) {
          color->mBackgroundXPosition = CalcLength(ourColor->mBackPositionX,
                                                   font, aPresContext);
          color->mBackgroundFlags |= NS_STYLE_BG_X_POSITION_LENGTH;
          color->mBackgroundFlags &= ~NS_STYLE_BG_X_POSITION_PCT;
        }
        if (ourColor->mBackPositionY.GetUnit() == eCSSUnit_Percent) {
          color->mBackgroundYPosition = (nscoord)(TWIPS_CONST_FLOAT * ourColor->mBackPositionY.GetFloatValue());
          color->mBackgroundFlags |= NS_STYLE_BG_Y_POSITION_PCT;
          color->mBackgroundFlags &= ~NS_STYLE_BG_Y_POSITION_LENGTH;
        }
        else if (ourColor->mBackPositionY.IsLengthUnit()) {
          color->mBackgroundYPosition = CalcLength(ourColor->mBackPositionY,
                                                   font, aPresContext);
          color->mBackgroundFlags |= NS_STYLE_BG_Y_POSITION_LENGTH;
          color->mBackgroundFlags &= ~NS_STYLE_BG_Y_POSITION_PCT;
        }

  // XXX: NYI        nsCSSValue mBackFilter;
      }
    }

    nsCSSMargin*  ourMargin;
    if (NS_OK == mDeclaration->GetData(kCSSMarginSID, (nsCSSStruct**)&ourMargin)) {
      if (nsnull != ourMargin) {
        nsStyleMolecule* hack = (nsStyleMolecule*)aContext->GetData(kStyleMoleculeSID);

        // margin
        if (nsnull != ourMargin->mMargin) {
          if (ourMargin->mMargin->mLeft.IsLengthUnit()) {
            hack->margin.left = CalcLength(ourMargin->mMargin->mLeft, font, aPresContext);
          } else if (ourMargin->mMargin->mLeft.GetUnit() != eCSSUnit_Null) {
            hack->margin.left = (nscoord)ourMargin->mMargin->mLeft.GetFloatValue();
          }
          if (ourMargin->mMargin->mTop.IsLengthUnit()) {
            hack->margin.top = CalcLength(ourMargin->mMargin->mTop, font, aPresContext);
          } else if (ourMargin->mMargin->mTop.GetUnit() != eCSSUnit_Null) {
            hack->margin.top = (nscoord)ourMargin->mMargin->mTop.GetFloatValue();
          }
          if (ourMargin->mMargin->mRight.IsLengthUnit()) {
            hack->margin.right = CalcLength(ourMargin->mMargin->mRight, font, aPresContext);
          } else if (ourMargin->mMargin->mRight.GetUnit() != eCSSUnit_Null) {
            hack->margin.right = (nscoord)ourMargin->mMargin->mRight.GetFloatValue();
          }
          if (ourMargin->mMargin->mBottom.IsLengthUnit()) {
            hack->margin.bottom = CalcLength(ourMargin->mMargin->mBottom, font, aPresContext);
          } else if (ourMargin->mMargin->mBottom.GetUnit() != eCSSUnit_Null) {
            hack->margin.bottom = (nscoord)ourMargin->mMargin->mBottom.GetFloatValue();
          }
        }
      }
    }

    nsCSSList* ourList;
    if (NS_OK == mDeclaration->GetData(kCSSListSID, (nsCSSStruct**)&ourList)) {
      if (nsnull != ourList) {
        nsStyleList* list = (nsStyleList*)aContext->GetData(kStyleListSID);

        // list-style-type: enum
        if (ourList->mType.GetUnit() == eCSSUnit_Enumerated) {
          list->mListStyleType = ourList->mType.GetIntValue();
        }

        // list-style-image: string
        if (ourList->mImage.GetUnit() == eCSSUnit_String) {
          ourList->mImage.GetStringValue(list->mListStyleImage);
        }
        else if (ourList->mImage.GetUnit() == eCSSUnit_Enumerated) {  // handle "none"
          list->mListStyleImage = "";
        }

        // list-style-position: enum
        if (ourList->mPosition.GetUnit() == eCSSUnit_Enumerated) {
          list->mListStyleType = ourList->mPosition.GetIntValue();
        }
      }
    }

  }

}



static void ListSelector(FILE* out, const nsCSSSelector* aSelector)
{
  nsAutoString buffer;

  if (nsnull != aSelector->mTag) {
    aSelector->mTag->ToString(buffer);
    fputs(buffer, out);
  }
  if (nsnull != aSelector->mID) {
    aSelector->mID->ToString(buffer);
    fputs("#", out);
    fputs(buffer, out);
  }
  if (nsnull != aSelector->mClass) {
    aSelector->mClass->ToString(buffer);
    fputs(".", out);
    fputs(buffer, out);
  }
  if (nsnull != aSelector->mPseudoClass) {
    aSelector->mPseudoClass->ToString(buffer);
    fputs(":", out);
    fputs(buffer, out);
  }
}

void CSSStyleRuleImpl::List(FILE* out, PRInt32 aIndent) const
{
  // Indent
  for (PRInt32 index = aIndent; --index >= 0; ) fputs("  ", out);

  const nsCSSSelector*  selector = &mSelector;

  while (nsnull != selector) {
    ListSelector(out, selector);
    fputs(" ", out);
    selector = selector->mNext;
  }

  nsAutoString buffer;

  buffer.Append("weight: ");
  buffer.Append(mWeight, 10);
  buffer.Append(" ");
  fputs(buffer, out);
  if (nsnull != mDeclaration) {
    mDeclaration->List(out);
  }
  else {
    fputs("{ null declaration }", out);
  }
  fputs("\n", out);
}

NS_HTML nsresult
  NS_NewCSSStyleRule(nsICSSStyleRule** aInstancePtrResult, const nsCSSSelector& aSelector)
{
  if (aInstancePtrResult == nsnull) {
    return NS_ERROR_NULL_POINTER;
  }

  CSSStyleRuleImpl  *it = new CSSStyleRuleImpl(aSelector);

  if (nsnull == it) {
    return NS_ERROR_OUT_OF_MEMORY;
  }

  return it->QueryInterface(kICSSStyleRuleIID, (void **) aInstancePtrResult);
}
