Back to index

lightning-sunbird  0.9+nobinonly
nsAttrValue.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is mozilla.org code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * IBM Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 2003
00020  * IBM Corporation. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   IBM Corporation
00024  *
00025  * Alternatively, the contents of this file may be used under the terms of
00026  * either the GNU General Public License Version 2 or later (the "GPL"), or
00027  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00028  * in which case the provisions of the GPL or the LGPL are applicable instead
00029  * of those above. If you wish to allow use of your version of this file only
00030  * under the terms of either the GPL or the LGPL, and not to allow others to
00031  * use your version of this file under the terms of the MPL, indicate your
00032  * decision by deleting the provisions above and replace them with the notice
00033  * and other provisions required by the GPL or the LGPL. If you do not delete
00034  * the provisions above, a recipient may use your version of this file under
00035  * the terms of any one of the MPL, the GPL or the LGPL.
00036  *
00037  * ***** END LICENSE BLOCK ***** */
00038 
00039 #include "nsAttrValue.h"
00040 #include "nsIAtom.h"
00041 #include "nsUnicharUtils.h"
00042 #include "nsICSSStyleRule.h"
00043 #include "nsCSSDeclaration.h"
00044 #include "nsIHTMLDocument.h"
00045 #include "nsIDocument.h"
00046 
00047 #ifdef MOZ_SVG
00048 #include "nsISVGValue.h"
00049 #endif
00050 
00051 nsVoidArray* nsAttrValue::sEnumTableArray = nsnull;
00052 
00053 nsAttrValue::nsAttrValue()
00054     : mBits(0)
00055 {
00056 }
00057 
00058 nsAttrValue::nsAttrValue(const nsAttrValue& aOther)
00059     : mBits(0)
00060 {
00061   SetTo(aOther);
00062 }
00063 
00064 nsAttrValue::nsAttrValue(const nsAString& aValue)
00065     : mBits(0)
00066 {
00067   SetTo(aValue);
00068 }
00069 
00070 nsAttrValue::nsAttrValue(nsICSSStyleRule* aValue)
00071     : mBits(0)
00072 {
00073   SetTo(aValue);
00074 }
00075 
00076 #ifdef MOZ_SVG
00077 nsAttrValue::nsAttrValue(nsISVGValue* aValue)
00078     : mBits(0)
00079 {
00080   SetTo(aValue);
00081 }
00082 #endif
00083 
00084 nsAttrValue::~nsAttrValue()
00085 {
00086   ResetIfSet();
00087 }
00088 
00089 /* static */
00090 nsresult
00091 nsAttrValue::Init()
00092 {
00093   NS_ASSERTION(!sEnumTableArray, "nsAttrValue already initialized");
00094 
00095   sEnumTableArray = new nsVoidArray;
00096   NS_ENSURE_TRUE(sEnumTableArray, NS_ERROR_OUT_OF_MEMORY);
00097   
00098   return NS_OK;
00099 }
00100 
00101 /* static */
00102 void
00103 nsAttrValue::Shutdown()
00104 {
00105   delete sEnumTableArray;
00106   sEnumTableArray = nsnull;
00107 }
00108 
00109 nsAttrValue::ValueType
00110 nsAttrValue::Type() const
00111 {
00112   switch (BaseType()) {
00113     case eIntegerBase:
00114     {
00115       return NS_STATIC_CAST(ValueType, mBits & NS_ATTRVALUE_INTEGERTYPE_MASK);
00116     }
00117     case eOtherBase:
00118     {
00119       return GetMiscContainer()->mType;
00120     }
00121     default:
00122     {
00123       return NS_STATIC_CAST(ValueType, NS_STATIC_CAST(PRUint16, BaseType()));
00124     }
00125   }
00126 }
00127 
00128 void
00129 nsAttrValue::Reset()
00130 {
00131   switch(BaseType()) {
00132     case eStringBase:
00133     {
00134       nsStringBuffer* str = NS_STATIC_CAST(nsStringBuffer*, GetPtr());
00135       if (str) {
00136         str->Release();
00137       }
00138 
00139       break;
00140     }
00141     case eOtherBase:
00142     {
00143       EnsureEmptyMiscContainer();
00144       delete GetMiscContainer();
00145 
00146       break;
00147     }
00148     case eAtomBase:
00149     {
00150       nsIAtom* atom = GetAtomValue();
00151       NS_RELEASE(atom);
00152 
00153       break;
00154     }
00155     case eIntegerBase:
00156     {
00157       break;
00158     }
00159   }
00160 
00161   mBits = 0;
00162 }
00163 
00164 void
00165 nsAttrValue::SetTo(const nsAttrValue& aOther)
00166 {
00167   switch (aOther.BaseType()) {
00168     case eStringBase:
00169     {
00170       ResetIfSet();
00171       nsStringBuffer* str = NS_STATIC_CAST(nsStringBuffer*, aOther.GetPtr());
00172       if (str) {
00173         str->AddRef();
00174         SetPtrValueAndType(str, eStringBase);
00175       }
00176       return;
00177     }
00178     case eOtherBase:
00179     {
00180       break;
00181     }
00182     case eAtomBase:
00183     {
00184       ResetIfSet();
00185       nsIAtom* atom = aOther.GetAtomValue();
00186       NS_ADDREF(atom);
00187       SetPtrValueAndType(atom, eAtomBase);
00188       return;
00189     }
00190     case eIntegerBase:
00191     {
00192       ResetIfSet();
00193       mBits = aOther.mBits;
00194       return;      
00195     }
00196   }
00197 
00198   MiscContainer* otherCont = aOther.GetMiscContainer();
00199   switch (otherCont->mType) {
00200     case eColor:
00201     {
00202       if (EnsureEmptyMiscContainer()) {
00203         MiscContainer* cont = GetMiscContainer();
00204         cont->mColor = otherCont->mColor;
00205         cont->mType = eColor;
00206       }
00207       break;
00208     }
00209     case eCSSStyleRule:
00210     {
00211       SetTo(otherCont->mCSSStyleRule);
00212       break;
00213     }
00214     case eAtomArray:
00215     {
00216       if (!EnsureEmptyAtomArray() ||
00217           !GetAtomArrayValue()->AppendObjects(*otherCont->mAtomArray)) {
00218         Reset();
00219       }
00220       break;
00221     }
00222 #ifdef MOZ_SVG
00223     case eSVGValue:
00224     {
00225       SetTo(otherCont->mSVGValue);
00226     }
00227 #endif
00228     default:
00229     {
00230       NS_NOTREACHED("unknown type stored in MiscContainer");
00231       break;
00232     }
00233   }
00234 }
00235 
00236 void
00237 nsAttrValue::SetTo(const nsAString& aValue)
00238 {
00239   ResetIfSet();
00240   if (!aValue.IsEmpty()) {
00241     PRUint32 len = aValue.Length();
00242 
00243     nsStringBuffer* buf = nsStringBuffer::FromString(aValue);
00244     if (buf && (buf->StorageSize()/sizeof(PRUnichar) - 1) == len) {
00245       buf->AddRef();
00246       SetPtrValueAndType(buf, eStringBase);
00247       return;
00248     }
00249 
00250     buf = nsStringBuffer::Alloc((len + 1) * sizeof(PRUnichar));
00251     if (!buf) {
00252       return;
00253     }
00254     PRUnichar *data = NS_STATIC_CAST(PRUnichar*, buf->Data());
00255     CopyUnicodeTo(aValue, 0, data, len);
00256     data[len] = PRUnichar(0);
00257 
00258     SetPtrValueAndType(buf, eStringBase);
00259   }
00260 }
00261 
00262 void
00263 nsAttrValue::SetTo(PRInt16 aInt)
00264 {
00265   ResetIfSet();
00266   SetIntValueAndType(aInt, eInteger);
00267 }
00268 
00269 void
00270 nsAttrValue::SetTo(nsICSSStyleRule* aValue)
00271 {
00272   if (EnsureEmptyMiscContainer()) {
00273     MiscContainer* cont = GetMiscContainer();
00274     NS_ADDREF(cont->mCSSStyleRule = aValue);
00275     cont->mType = eCSSStyleRule;
00276   }
00277 }
00278 
00279 #ifdef MOZ_SVG
00280 void
00281 nsAttrValue::SetTo(nsISVGValue* aValue)
00282 {
00283   if (EnsureEmptyMiscContainer()) {
00284     MiscContainer* cont = GetMiscContainer();
00285     NS_ADDREF(cont->mSVGValue = aValue);
00286     cont->mType = eSVGValue;
00287   }
00288 }
00289 #endif
00290 
00291 void
00292 nsAttrValue::SwapValueWith(nsAttrValue& aOther)
00293 {
00294   PtrBits tmp = aOther.mBits;
00295   aOther.mBits = mBits;
00296   mBits = tmp;
00297 }
00298 
00299 void
00300 nsAttrValue::ToString(nsAString& aResult) const
00301 {
00302   switch(Type()) {
00303     case eString:
00304     {
00305       nsStringBuffer* str = NS_STATIC_CAST(nsStringBuffer*, GetPtr());
00306       if (str) {
00307         str->ToString(str->StorageSize()/sizeof(PRUnichar) - 1, aResult);
00308       }
00309       else {
00310         aResult.Truncate();
00311       }
00312       break;
00313     }
00314     case eAtom:
00315     {
00316       NS_STATIC_CAST(nsIAtom*, GetPtr())->ToString(aResult);
00317 
00318       break;
00319     }
00320     case eInteger:
00321     {
00322       nsAutoString intStr;
00323       intStr.AppendInt(GetIntInternal());
00324       aResult = intStr;
00325 
00326       break;
00327     }
00328     case eColor:
00329     {
00330       nscolor v;
00331       GetColorValue(v);
00332       NS_RGBToHex(v, aResult);
00333 
00334       break;
00335     }
00336     case eProportional:
00337     {
00338       nsAutoString intStr;
00339       intStr.AppendInt(GetIntInternal());
00340       aResult = intStr + NS_LITERAL_STRING("*");
00341 
00342       break;
00343     }
00344     case eEnum:
00345     {
00346       PRInt16 val = GetEnumValue();
00347       EnumTable* table = NS_STATIC_CAST(EnumTable*, sEnumTableArray->
00348           FastElementAt(GetIntInternal() & NS_ATTRVALUE_ENUMTABLEINDEX_MASK));
00349       while (table->tag) {
00350         if (table->value == val) {
00351           aResult.AssignASCII(table->tag);
00352 
00353           return;
00354         }
00355         table++;
00356       }
00357 
00358       NS_NOTREACHED("couldn't find value in EnumTable");
00359 
00360       break;
00361     }
00362     case ePercent:
00363     {
00364       nsAutoString intStr;
00365       intStr.AppendInt(GetIntInternal());
00366       aResult = intStr + NS_LITERAL_STRING("%");
00367 
00368       break;
00369     }
00370     case eCSSStyleRule:
00371     {
00372       aResult.Truncate();
00373       nsCSSDeclaration* decl = 
00374         GetMiscContainer()->mCSSStyleRule->GetDeclaration();
00375       if (decl) {
00376         decl->ToString(aResult);
00377       }
00378 
00379       break;
00380     }
00381     case eAtomArray:
00382     {
00383       MiscContainer* cont = GetMiscContainer();
00384       PRInt32 count = cont->mAtomArray->Count();
00385       if (count) {
00386         cont->mAtomArray->ObjectAt(0)->ToString(aResult);
00387         nsAutoString tmp;
00388         PRInt32 i;
00389         for (i = 1; i < count; ++i) {
00390           cont->mAtomArray->ObjectAt(i)->ToString(tmp);
00391           aResult.Append(NS_LITERAL_STRING(" ") + tmp);
00392         }
00393       }
00394       else {
00395         aResult.Truncate();
00396       }
00397       break;
00398     }
00399 #ifdef MOZ_SVG
00400     case eSVGValue:
00401     {
00402       GetMiscContainer()->mSVGValue->GetValueString(aResult);
00403     }
00404 #endif
00405   }
00406 }
00407 
00408 const nsCheapString
00409 nsAttrValue::GetStringValue() const
00410 {
00411   NS_PRECONDITION(Type() == eString, "wrong type");
00412 
00413   return nsCheapString(NS_STATIC_CAST(nsStringBuffer*, GetPtr()));
00414 }
00415 
00416 PRBool
00417 nsAttrValue::GetColorValue(nscolor& aColor) const
00418 {
00419   NS_PRECONDITION(Type() == eColor || Type() == eString, "wrong type");
00420   switch (BaseType()) {
00421     case eString:
00422     {
00423       return GetPtr() && NS_ColorNameToRGB(GetStringValue(), &aColor);
00424     }
00425     case eOtherBase:
00426     {
00427       aColor = GetMiscContainer()->mColor;
00428       
00429       break;
00430     }
00431     case eIntegerBase:
00432     {
00433       aColor = NS_STATIC_CAST(nscolor, GetIntInternal());
00434       
00435       break;
00436     }
00437     default:
00438     {
00439       NS_NOTREACHED("unexpected basetype");
00440       
00441       break;
00442     }
00443   }
00444 
00445   return PR_TRUE;
00446 }
00447 
00448 PRInt32
00449 nsAttrValue::GetAtomCount() const
00450 {
00451   ValueType type = Type();
00452 
00453   if (type == eAtom) {
00454     return 1;
00455   }
00456 
00457   if (type == eAtomArray) {
00458     return GetAtomArrayValue()->Count();
00459   }
00460 
00461   return 0;
00462 }
00463 
00464 nsIAtom*
00465 nsAttrValue::AtomAt(PRInt32 aIndex) const
00466 {
00467   NS_PRECONDITION(aIndex >= 0, "Index must not be negative");
00468   NS_PRECONDITION(GetAtomCount() > aIndex, "aIndex out of range");
00469   
00470   if (BaseType() == eAtomBase) {
00471     return GetAtomValue();
00472   }
00473 
00474   NS_ASSERTION(Type() == eAtomArray, "GetAtomCount must be confused");
00475   
00476   return GetAtomArrayValue()->ObjectAt(aIndex);
00477 }
00478 
00479 PRUint32
00480 nsAttrValue::HashValue() const
00481 {
00482   switch(BaseType()) {
00483     case eStringBase:
00484     {
00485       nsStringBuffer* str = NS_STATIC_CAST(nsStringBuffer*, GetPtr());
00486       if (str) {
00487         PRUint32 len = str->StorageSize()/sizeof(PRUnichar) - 1;
00488         return nsCRT::BufferHashCode(NS_STATIC_CAST(PRUnichar*, str->Data()), len);
00489       }
00490 
00491       return 0;
00492     }
00493     case eOtherBase:
00494     {
00495       break;
00496     }
00497     case eAtomBase:
00498     case eIntegerBase:
00499     {
00500       // mBits and PRUint32 might have different size. This should silence
00501       // any warnings or compile-errors. This is what the implementation of
00502       // NS_PTR_TO_INT32 does to take care of the same problem.
00503       return mBits - 0;
00504     }
00505   }
00506 
00507   MiscContainer* cont = GetMiscContainer();
00508   switch (cont->mType) {
00509     case eColor:
00510     {
00511       return cont->mColor;
00512     }
00513     case eCSSStyleRule:
00514     {
00515       return NS_PTR_TO_INT32(cont->mCSSStyleRule);
00516     }
00517     case eAtomArray:
00518     {
00519       PRUint32 retval = 0;
00520       PRInt32 i, count = cont->mAtomArray->Count();
00521       for (i = 0; i < count; ++i) {
00522         retval ^= NS_PTR_TO_INT32(cont->mAtomArray->ObjectAt(i));
00523       }
00524       return retval;
00525     }
00526 #ifdef MOZ_SVG
00527     case eSVGValue:
00528     {
00529       return NS_PTR_TO_INT32(cont->mSVGValue);
00530     }
00531 #endif
00532     default:
00533     {
00534       NS_NOTREACHED("unknown type stored in MiscContainer");
00535       return 0;
00536     }
00537   }
00538 }
00539 
00540 PRBool
00541 nsAttrValue::Equals(const nsAttrValue& aOther) const
00542 {
00543   if (BaseType() != aOther.BaseType()) {
00544     return PR_FALSE;
00545   }
00546 
00547   switch(BaseType()) {
00548     case eStringBase:
00549     {
00550       return GetStringValue().Equals(aOther.GetStringValue());
00551     }
00552     case eOtherBase:
00553     {
00554       break;
00555     }
00556     case eAtomBase:
00557     case eIntegerBase:
00558     {
00559       return mBits == aOther.mBits;
00560     }
00561   }
00562 
00563   MiscContainer* thisCont = GetMiscContainer();
00564   MiscContainer* otherCont = aOther.GetMiscContainer();
00565   if (thisCont->mType != otherCont->mType) {
00566     return PR_FALSE;
00567   }
00568 
00569   switch (thisCont->mType) {
00570     case eColor:
00571     {
00572       return thisCont->mColor == otherCont->mColor;
00573     }
00574     case eCSSStyleRule:
00575     {
00576       return thisCont->mCSSStyleRule == otherCont->mCSSStyleRule;
00577     }
00578     case eAtomArray:
00579     {
00580       // For classlists we could be insensitive to order, however
00581       // classlists are never mapped attributes so they are never compared.
00582 
00583       PRInt32 count = thisCont->mAtomArray->Count();
00584       if (count != otherCont->mAtomArray->Count()) {
00585         return PR_FALSE;
00586       }
00587 
00588       PRInt32 i;
00589       for (i = 0; i < count; ++i) {
00590         if (thisCont->mAtomArray->ObjectAt(i) !=
00591             otherCont->mAtomArray->ObjectAt(i)) {
00592           return PR_FALSE;
00593         }
00594       }
00595       return PR_TRUE;
00596     }
00597 #ifdef MOZ_SVG
00598     case eSVGValue:
00599     {
00600       return thisCont->mSVGValue == otherCont->mSVGValue;
00601     }
00602 #endif
00603     default:
00604     {
00605       NS_NOTREACHED("unknown type stored in MiscContainer");
00606       return PR_FALSE;
00607     }
00608   }
00609 }
00610 
00611 void
00612 nsAttrValue::ParseAtom(const nsAString& aValue)
00613 {
00614   ResetIfSet();
00615 
00616   nsIAtom* atom = NS_NewAtom(aValue);
00617   if (atom) {
00618     SetPtrValueAndType(atom, eAtomBase);
00619   }
00620 }
00621 
00622 void
00623 nsAttrValue::ParseAtomArray(const nsAString& aValue)
00624 {
00625   nsAString::const_iterator iter, end;
00626   aValue.BeginReading(iter);
00627   aValue.EndReading(end);
00628 
00629   // skip initial whitespace
00630   while (iter != end && nsCRT::IsAsciiSpace(*iter)) {
00631     ++iter;
00632   }
00633 
00634   if (iter == end) {
00635     ResetIfSet();
00636     return;
00637   }
00638 
00639   nsAString::const_iterator start(iter);
00640 
00641   // get first - and often only - atom
00642   do {
00643     ++iter;
00644   } while (iter != end && !nsCRT::IsAsciiSpace(*iter));
00645 
00646   nsCOMPtr<nsIAtom> classAtom = do_GetAtom(Substring(start, iter));
00647   if (!classAtom) {
00648     Reset();
00649     return;
00650   }
00651 
00652   // skip whitespace
00653   while (iter != end && nsCRT::IsAsciiSpace(*iter)) {
00654     ++iter;
00655   }
00656 
00657   if (iter == end) {
00658     // we only found one classname so don't bother storing a list
00659     ResetIfSet();
00660     nsIAtom* atom = nsnull;
00661     classAtom.swap(atom);
00662     SetPtrValueAndType(atom, eAtomBase);
00663     return;
00664   }
00665 
00666   if (!EnsureEmptyAtomArray()) {
00667     return;
00668   }
00669 
00670   nsCOMArray<nsIAtom>* array = GetAtomArrayValue();
00671   
00672   if (!array->AppendObject(classAtom)) {
00673     Reset();
00674     return;
00675   }
00676 
00677   // parse the rest of the classnames
00678   do {
00679     start = iter;
00680 
00681     do {
00682       ++iter;
00683     } while (iter != end && !nsCRT::IsAsciiSpace(*iter));
00684 
00685     classAtom = do_GetAtom(Substring(start, iter));
00686 
00687     if (!array->AppendObject(classAtom)) {
00688       Reset();
00689       return;
00690     }
00691 
00692     // skip whitespace
00693     while (iter != end && nsCRT::IsAsciiSpace(*iter)) {
00694       ++iter;
00695     }
00696   } while (iter != end);
00697 
00698   return;
00699 }
00700 
00701 void
00702 nsAttrValue::ParseStringOrAtom(const nsAString& aValue)
00703 {
00704   PRUint32 len = aValue.Length();
00705   // Don't bother with atoms if it's an empty string since
00706   // we can store those efficently anyway.
00707   if (len && len <= NS_ATTRVALUE_MAX_STRINGLENGTH_ATOM) {
00708     ParseAtom(aValue);
00709   }
00710   else {
00711     SetTo(aValue);
00712   }
00713 }
00714 
00715 PRBool
00716 nsAttrValue::ParseEnumValue(const nsAString& aValue,
00717                             const EnumTable* aTable,
00718                             PRBool aCaseSensitive)
00719 {
00720   ResetIfSet();
00721 
00722   // Have to const cast here since nsVoidArray can't deal with constpointers
00723   EnumTable* tableStart = NS_CONST_CAST(EnumTable*, aTable);
00724 
00725   nsAutoString val(aValue);
00726   while (aTable->tag) {
00727     if (aCaseSensitive ? val.EqualsASCII(aTable->tag) :
00728                          val.EqualsIgnoreCase(aTable->tag)) {
00729 
00730       // Find index of EnumTable
00731       PRInt16 index = sEnumTableArray->IndexOf(tableStart);
00732       if (index < 0) {
00733         index = sEnumTableArray->Count();
00734         NS_ASSERTION(index <= NS_ATTRVALUE_ENUMTABLEINDEX_MAXVALUE,
00735                      "too many enum tables");
00736         if (!sEnumTableArray->AppendElement(tableStart)) {
00737           return PR_FALSE;
00738         }
00739       }
00740 
00741       PRInt32 value = (aTable->value << NS_ATTRVALUE_ENUMTABLEINDEX_BITS) +
00742                       index;
00743 
00744       SetIntValueAndType(value, eEnum);
00745       NS_ASSERTION(GetEnumValue() == aTable->value,
00746                    "failed to store enum properly");
00747 
00748       return PR_TRUE;
00749     }
00750     aTable++;
00751   }
00752 
00753   return PR_FALSE;
00754 }
00755 
00756 PRBool
00757 nsAttrValue::ParseSpecialIntValue(const nsAString& aString,
00758                                   PRBool aCanBePercent,
00759                                   PRBool aCanBeProportional)
00760 {
00761   ResetIfSet();
00762 
00763   PRInt32 ec;
00764   nsAutoString tmp(aString);
00765   PRInt32 val = tmp.ToInteger(&ec);
00766 
00767   if (NS_FAILED(ec)) {
00768     if (aCanBeProportional) {
00769       // Even if the integer could not be parsed, it might just be "*"
00770       tmp.CompressWhitespace(PR_TRUE, PR_TRUE);
00771       if (tmp.Length() == 1 && tmp.Last() == '*') {
00772         // special case: HTML spec says a value '*' == '1*'
00773         // see http://www.w3.org/TR/html4/types.html#type-multi-length
00774         // bug 29061
00775         SetIntValueAndType(1, eProportional);
00776         return PR_TRUE;
00777       }
00778     }
00779     return PR_FALSE;
00780   }
00781 
00782   val = PR_MAX(val, 0);
00783   val = PR_MIN(val, NS_ATTRVALUE_INTEGERTYPE_MAXVALUE);
00784 
00785   // % (percent)
00786   // XXX RFindChar means that 5%x will be parsed!
00787   if (aCanBePercent && tmp.RFindChar('%') >= 0) {
00788     if (val > 100) {
00789       val = 100;
00790     }
00791     SetIntValueAndType(val, ePercent);
00792     return PR_TRUE;
00793   }
00794 
00795   // * (proportional) 
00796   // XXX RFindChar means that 5*x will be parsed!
00797   if (aCanBeProportional && tmp.RFindChar('*') >= 0) {
00798     SetIntValueAndType(val, eProportional);
00799     return PR_TRUE;
00800   }
00801 
00802   // Straight number is interpreted as integer
00803   SetIntValueAndType(val, eInteger);
00804   return PR_TRUE;
00805 }
00806 
00807 PRBool
00808 nsAttrValue::ParseIntWithBounds(const nsAString& aString,
00809                                 PRInt32 aMin, PRInt32 aMax)
00810 {
00811   NS_PRECONDITION(aMin < aMax &&
00812                   aMin >= NS_ATTRVALUE_INTEGERTYPE_MINVALUE &&
00813                   aMax <= NS_ATTRVALUE_INTEGERTYPE_MAXVALUE, "bad boundaries");
00814 
00815   ResetIfSet();
00816 
00817   PRInt32 ec;
00818   PRInt32 val = PromiseFlatString(aString).ToInteger(&ec);
00819   if (NS_FAILED(ec)) {
00820     return PR_FALSE;
00821   }
00822 
00823   val = PR_MAX(val, aMin);
00824   val = PR_MIN(val, aMax);
00825   SetIntValueAndType(val, eInteger);
00826 
00827   return PR_TRUE;
00828 }
00829 
00830 PRBool
00831 nsAttrValue::ParseColor(const nsAString& aString, nsIDocument* aDocument)
00832 {
00833   nsAutoString colorStr(aString);
00834   colorStr.CompressWhitespace(PR_TRUE, PR_TRUE);
00835   if (colorStr.IsEmpty()) {
00836     Reset();
00837     return PR_FALSE;
00838   }
00839 
00840   nscolor color;
00841   // No color names begin with a '#', but numerical colors do so
00842   // it is a very common first char
00843   if ((colorStr.CharAt(0) != '#') && NS_ColorNameToRGB(colorStr, &color)) {
00844     SetTo(colorStr);
00845     return PR_TRUE;
00846   }
00847 
00848   // Check if we are in compatibility mode
00849   // XXX evil NS_HexToRGB and NS_LooseHexToRGB take nsString as argument!
00850   nsCOMPtr<nsIHTMLDocument> doc(do_QueryInterface(aDocument));
00851   if (doc && doc->GetCompatibilityMode() == eCompatibility_NavQuirks) {
00852     NS_LooseHexToRGB(colorStr, &color);
00853   }
00854   else {
00855     if (colorStr.First() != '#') {
00856       Reset();
00857       return PR_FALSE;
00858     }
00859     colorStr.Cut(0, 1);
00860     if (!NS_HexToRGB(colorStr, &color)) {
00861       Reset();
00862       return PR_FALSE;
00863     }
00864   }
00865 
00866   PRInt32 colAsInt = NS_STATIC_CAST(PRInt32, color);
00867   PRInt32 tmp = colAsInt * NS_ATTRVALUE_INTEGERTYPE_MULTIPLIER;
00868   if (tmp / NS_ATTRVALUE_INTEGERTYPE_MULTIPLIER == colAsInt) {
00869     ResetIfSet();
00870     SetIntValueAndType(colAsInt, eColor);
00871   }
00872   else if (EnsureEmptyMiscContainer()) {
00873     MiscContainer* cont = GetMiscContainer();
00874     cont->mColor = color;
00875     cont->mType = eColor;
00876   }
00877 
00878   return PR_TRUE;
00879 }
00880 
00881 PRBool
00882 nsAttrValue::EnsureEmptyMiscContainer()
00883 {
00884   MiscContainer* cont;
00885   if (BaseType() == eOtherBase) {
00886     cont = GetMiscContainer();
00887     switch (cont->mType) {
00888       case eCSSStyleRule:
00889       {
00890         NS_RELEASE(cont->mCSSStyleRule);
00891         break;
00892       }
00893       case eAtomArray:
00894       {
00895         delete cont->mAtomArray;
00896         break;
00897       }
00898 #ifdef MOZ_SVG
00899       case eSVGValue:
00900       {
00901         NS_RELEASE(cont->mSVGValue);
00902         break;
00903       }
00904 #endif
00905       default:
00906       {
00907         break;
00908       }
00909     }
00910   }
00911   else {
00912     ResetIfSet();
00913 
00914     cont = new MiscContainer;
00915     NS_ENSURE_TRUE(cont, PR_FALSE);
00916 
00917     SetPtrValueAndType(cont, eOtherBase);
00918   }
00919 
00920   cont->mType = eColor;
00921   cont->mColor = 0;
00922 
00923   return PR_TRUE;
00924 }
00925 
00926 PRBool
00927 nsAttrValue::EnsureEmptyAtomArray()
00928 {
00929   if (Type() == eAtomArray) {
00930     GetAtomArrayValue()->Clear();
00931     return PR_TRUE;
00932   }
00933 
00934   if (!EnsureEmptyMiscContainer()) {
00935     // should already be reset
00936     return PR_FALSE;
00937   }
00938 
00939   nsCOMArray<nsIAtom>* array = new nsCOMArray<nsIAtom>;
00940   if (!array) {
00941     Reset();
00942     return PR_FALSE;
00943   }
00944 
00945   MiscContainer* cont = GetMiscContainer();
00946   cont->mAtomArray = array;
00947   cont->mType = eAtomArray;
00948 
00949   return PR_TRUE;
00950 }