content/base/src/nsAttrValue.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* This Source Code Form is subject to the terms of the Mozilla Public
     3  * License, v. 2.0. If a copy of the MPL was not distributed with this
     4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     6 /*
     7  * A struct that represents the value (type and actual data) of an
     8  * attribute.
     9  */
    11 #include "mozilla/DebugOnly.h"
    12 #include "mozilla/HashFunctions.h"
    14 #include "nsAttrValue.h"
    15 #include "nsAttrValueInlines.h"
    16 #include "nsIAtom.h"
    17 #include "nsUnicharUtils.h"
    18 #include "mozilla/MemoryReporting.h"
    19 #include "mozilla/css/StyleRule.h"
    20 #include "mozilla/css/Declaration.h"
    21 #include "nsContentUtils.h"
    22 #include "nsReadableUtils.h"
    23 #include "prprf.h"
    24 #include "nsHTMLCSSStyleSheet.h"
    25 #include "nsCSSParser.h"
    26 #include "nsStyledElement.h"
    27 #include "nsIURI.h"
    28 #include "nsIDocument.h"
    29 #include <algorithm>
    31 using namespace mozilla;
    33 #define MISC_STR_PTR(_cont) \
    34   reinterpret_cast<void*>((_cont)->mStringBits & NS_ATTRVALUE_POINTERVALUE_MASK)
    36 bool
    37 MiscContainer::GetString(nsAString& aString) const
    38 {
    39   void* ptr = MISC_STR_PTR(this);
    41   if (!ptr) {
    42     return false;
    43   }
    45   if (static_cast<nsAttrValue::ValueBaseType>(mStringBits &
    46                                               NS_ATTRVALUE_BASETYPE_MASK) ==
    47       nsAttrValue::eStringBase) {
    48     nsStringBuffer* buffer = static_cast<nsStringBuffer*>(ptr);
    49     if (!buffer) {
    50       return false;
    51     }
    53     buffer->ToString(buffer->StorageSize() / sizeof(char16_t) - 1, aString);
    54     return true;
    55   }
    57   nsIAtom* atom = static_cast<nsIAtom*>(ptr);
    58   if (!atom) {
    59     return false;
    60   }
    62   atom->ToString(aString);
    63   return true;
    64 }
    66 void
    67 MiscContainer::Cache()
    68 {
    69   // Not implemented for anything else yet.
    70   MOZ_ASSERT(mType == nsAttrValue::eCSSStyleRule);
    71   MOZ_ASSERT(IsRefCounted());
    72   MOZ_ASSERT(mValue.mRefCount > 0);
    73   MOZ_ASSERT(!mValue.mCached);
    75   css::StyleRule* rule = mValue.mCSSStyleRule;
    76   nsHTMLCSSStyleSheet* sheet = rule->GetHTMLCSSStyleSheet();
    77   if (!sheet) {
    78     return;
    79   }
    81   nsString str;
    82   bool gotString = GetString(str);
    83   if (!gotString) {
    84     return;
    85   }
    87   sheet->CacheStyleAttr(str, this);
    88   mValue.mCached = 1;
    90   // This has to be immutable once it goes into the cache.
    91   css::Declaration* decl = rule->GetDeclaration();
    92   if (decl) {
    93     decl->SetImmutable();
    94   }
    95 }
    97 void
    98 MiscContainer::Evict()
    99 {
   100   // Not implemented for anything else yet.
   101   MOZ_ASSERT(mType == nsAttrValue::eCSSStyleRule);
   102   MOZ_ASSERT(IsRefCounted());
   103   MOZ_ASSERT(mValue.mRefCount == 0);
   105   if (!mValue.mCached) {
   106     return;
   107   }
   109   css::StyleRule* rule = mValue.mCSSStyleRule;
   110   nsHTMLCSSStyleSheet* sheet = rule->GetHTMLCSSStyleSheet();
   111   MOZ_ASSERT(sheet);
   113   nsString str;
   114   DebugOnly<bool> gotString = GetString(str);
   115   MOZ_ASSERT(gotString);
   117   sheet->EvictStyleAttr(str, this);
   118   mValue.mCached = 0;
   119 }
   121 nsTArray<const nsAttrValue::EnumTable*>* nsAttrValue::sEnumTableArray = nullptr;
   123 nsAttrValue::nsAttrValue()
   124     : mBits(0)
   125 {
   126 }
   128 nsAttrValue::nsAttrValue(const nsAttrValue& aOther)
   129     : mBits(0)
   130 {
   131   SetTo(aOther);
   132 }
   134 nsAttrValue::nsAttrValue(const nsAString& aValue)
   135     : mBits(0)
   136 {
   137   SetTo(aValue);
   138 }
   140 nsAttrValue::nsAttrValue(nsIAtom* aValue)
   141     : mBits(0)
   142 {
   143   SetTo(aValue);
   144 }
   146 nsAttrValue::nsAttrValue(css::StyleRule* aValue, const nsAString* aSerialized)
   147     : mBits(0)
   148 {
   149   SetTo(aValue, aSerialized);
   150 }
   152 nsAttrValue::nsAttrValue(const nsIntMargin& aValue)
   153     : mBits(0)
   154 {
   155   SetTo(aValue);
   156 }
   158 nsAttrValue::~nsAttrValue()
   159 {
   160   ResetIfSet();
   161 }
   163 /* static */
   164 nsresult
   165 nsAttrValue::Init()
   166 {
   167   NS_ASSERTION(!sEnumTableArray, "nsAttrValue already initialized");
   169   sEnumTableArray = new nsTArray<const EnumTable*>;
   170   NS_ENSURE_TRUE(sEnumTableArray, NS_ERROR_OUT_OF_MEMORY);
   172   return NS_OK;
   173 }
   175 /* static */
   176 void
   177 nsAttrValue::Shutdown()
   178 {
   179   delete sEnumTableArray;
   180   sEnumTableArray = nullptr;
   181 }
   183 nsAttrValue::ValueType
   184 nsAttrValue::Type() const
   185 {
   186   switch (BaseType()) {
   187     case eIntegerBase:
   188     {
   189       return static_cast<ValueType>(mBits & NS_ATTRVALUE_INTEGERTYPE_MASK);
   190     }
   191     case eOtherBase:
   192     {
   193       return GetMiscContainer()->mType;
   194     }
   195     default:
   196     {
   197       return static_cast<ValueType>(static_cast<uint16_t>(BaseType()));
   198     }
   199   }
   200 }
   202 void
   203 nsAttrValue::Reset()
   204 {
   205   switch(BaseType()) {
   206     case eStringBase:
   207     {
   208       nsStringBuffer* str = static_cast<nsStringBuffer*>(GetPtr());
   209       if (str) {
   210         str->Release();
   211       }
   213       break;
   214     }
   215     case eOtherBase:
   216     {
   217       MiscContainer* cont = GetMiscContainer();
   218       if (cont->IsRefCounted() && cont->mValue.mRefCount > 1) {
   219         NS_RELEASE(cont);
   220         break;
   221       }
   223       delete ClearMiscContainer();
   225       break;
   226     }
   227     case eAtomBase:
   228     {
   229       nsIAtom* atom = GetAtomValue();
   230       NS_RELEASE(atom);
   232       break;
   233     }
   234     case eIntegerBase:
   235     {
   236       break;
   237     }
   238   }
   240   mBits = 0;
   241 }
   243 void
   244 nsAttrValue::SetTo(const nsAttrValue& aOther)
   245 {
   246   if (this == &aOther) {
   247     return;
   248   }
   250   switch (aOther.BaseType()) {
   251     case eStringBase:
   252     {
   253       ResetIfSet();
   254       nsStringBuffer* str = static_cast<nsStringBuffer*>(aOther.GetPtr());
   255       if (str) {
   256         str->AddRef();
   257         SetPtrValueAndType(str, eStringBase);
   258       }
   259       return;
   260     }
   261     case eOtherBase:
   262     {
   263       break;
   264     }
   265     case eAtomBase:
   266     {
   267       ResetIfSet();
   268       nsIAtom* atom = aOther.GetAtomValue();
   269       NS_ADDREF(atom);
   270       SetPtrValueAndType(atom, eAtomBase);
   271       return;
   272     }
   273     case eIntegerBase:
   274     {
   275       ResetIfSet();
   276       mBits = aOther.mBits;
   277       return;      
   278     }
   279   }
   281   MiscContainer* otherCont = aOther.GetMiscContainer();
   282   if (otherCont->IsRefCounted()) {
   283     delete ClearMiscContainer();
   284     NS_ADDREF(otherCont);
   285     SetPtrValueAndType(otherCont, eOtherBase);
   286     return;
   287   }
   289   MiscContainer* cont = EnsureEmptyMiscContainer();
   290   switch (otherCont->mType) {
   291     case eInteger:
   292     {
   293       cont->mValue.mInteger = otherCont->mValue.mInteger;
   294       break;
   295     }
   296     case eEnum:
   297     {
   298       cont->mValue.mEnumValue = otherCont->mValue.mEnumValue;
   299       break;
   300     }
   301     case ePercent:
   302     {
   303       cont->mValue.mPercent = otherCont->mValue.mPercent;
   304       break;
   305     }
   306     case eColor:
   307     {
   308       cont->mValue.mColor = otherCont->mValue.mColor;
   309       break;
   310     }
   311     case eCSSStyleRule:
   312     {
   313       MOZ_CRASH("These should be refcounted!");
   314     }
   315     case eURL:
   316     {
   317       NS_ADDREF(cont->mValue.mURL = otherCont->mValue.mURL);
   318       break;
   319     }
   320     case eImage:
   321     {
   322       NS_ADDREF(cont->mValue.mImage = otherCont->mValue.mImage);
   323       break;
   324     }
   325     case eAtomArray:
   326     {
   327       if (!EnsureEmptyAtomArray() ||
   328           !GetAtomArrayValue()->AppendElements(*otherCont->mValue.mAtomArray)) {
   329         Reset();
   330         return;
   331       }
   332       break;
   333     }
   334     case eDoubleValue:
   335     {
   336       cont->mDoubleValue = otherCont->mDoubleValue;
   337       break;
   338     }
   339     case eIntMarginValue:
   340     {
   341       if (otherCont->mValue.mIntMargin)
   342         cont->mValue.mIntMargin =
   343           new nsIntMargin(*otherCont->mValue.mIntMargin);
   344       break;
   345     }
   346     default:
   347     {
   348       if (IsSVGType(otherCont->mType)) {
   349         // All SVG types are just pointers to classes and will therefore have
   350         // the same size so it doesn't really matter which one we assign
   351         cont->mValue.mSVGAngle = otherCont->mValue.mSVGAngle;
   352       } else {
   353         NS_NOTREACHED("unknown type stored in MiscContainer");
   354       }
   355       break;
   356     }
   357   }
   359   void* otherPtr = MISC_STR_PTR(otherCont);
   360   if (otherPtr) {
   361     if (static_cast<ValueBaseType>(otherCont->mStringBits & NS_ATTRVALUE_BASETYPE_MASK) ==
   362         eStringBase) {
   363       static_cast<nsStringBuffer*>(otherPtr)->AddRef();
   364     } else {
   365       static_cast<nsIAtom*>(otherPtr)->AddRef();
   366     }
   367     cont->mStringBits = otherCont->mStringBits;
   368   }
   369   // Note, set mType after switch-case, otherwise EnsureEmptyAtomArray doesn't
   370   // work correctly.
   371   cont->mType = otherCont->mType;
   372 }
   374 void
   375 nsAttrValue::SetTo(const nsAString& aValue)
   376 {
   377   ResetIfSet();
   378   nsStringBuffer* buf = GetStringBuffer(aValue).take();
   379   if (buf) {
   380     SetPtrValueAndType(buf, eStringBase);
   381   }
   382 }
   384 void
   385 nsAttrValue::SetTo(nsIAtom* aValue)
   386 {
   387   ResetIfSet();
   388   if (aValue) {
   389     NS_ADDREF(aValue);
   390     SetPtrValueAndType(aValue, eAtomBase);
   391   }
   392 }
   394 void
   395 nsAttrValue::SetTo(int16_t aInt)
   396 {
   397   ResetIfSet();
   398   SetIntValueAndType(aInt, eInteger, nullptr);
   399 }
   401 void
   402 nsAttrValue::SetTo(int32_t aInt, const nsAString* aSerialized)
   403 {
   404   ResetIfSet();
   405   SetIntValueAndType(aInt, eInteger, aSerialized);
   406 }
   408 void
   409 nsAttrValue::SetTo(double aValue, const nsAString* aSerialized)
   410 {
   411   MiscContainer* cont = EnsureEmptyMiscContainer();
   412   cont->mDoubleValue = aValue;
   413   cont->mType = eDoubleValue;
   414   SetMiscAtomOrString(aSerialized);
   415 }
   417 void
   418 nsAttrValue::SetTo(css::StyleRule* aValue, const nsAString* aSerialized)
   419 {
   420   MiscContainer* cont = EnsureEmptyMiscContainer();
   421   MOZ_ASSERT(cont->mValue.mRefCount == 0);
   422   NS_ADDREF(cont->mValue.mCSSStyleRule = aValue);
   423   cont->mType = eCSSStyleRule;
   424   NS_ADDREF(cont);
   425   SetMiscAtomOrString(aSerialized);
   426   MOZ_ASSERT(cont->mValue.mRefCount == 1);
   427 }
   429 void
   430 nsAttrValue::SetTo(css::URLValue* aValue, const nsAString* aSerialized)
   431 {
   432   MiscContainer* cont = EnsureEmptyMiscContainer();
   433   NS_ADDREF(cont->mValue.mURL = aValue);
   434   cont->mType = eURL;
   435   SetMiscAtomOrString(aSerialized);
   436 }
   438 void
   439 nsAttrValue::SetTo(const nsIntMargin& aValue)
   440 {
   441   MiscContainer* cont = EnsureEmptyMiscContainer();
   442   cont->mValue.mIntMargin = new nsIntMargin(aValue);
   443   cont->mType = eIntMarginValue;
   444 }
   446 void
   447 nsAttrValue::SetToSerialized(const nsAttrValue& aOther)
   448 {
   449   if (aOther.Type() != nsAttrValue::eString &&
   450       aOther.Type() != nsAttrValue::eAtom) {
   451     nsAutoString val;
   452     aOther.ToString(val);
   453     SetTo(val);
   454   } else {
   455     SetTo(aOther);
   456   }
   457 }
   459 void
   460 nsAttrValue::SetTo(const nsSVGAngle& aValue, const nsAString* aSerialized)
   461 {
   462   SetSVGType(eSVGAngle, &aValue, aSerialized);
   463 }
   465 void
   466 nsAttrValue::SetTo(const nsSVGIntegerPair& aValue, const nsAString* aSerialized)
   467 {
   468   SetSVGType(eSVGIntegerPair, &aValue, aSerialized);
   469 }
   471 void
   472 nsAttrValue::SetTo(const nsSVGLength2& aValue, const nsAString* aSerialized)
   473 {
   474   SetSVGType(eSVGLength, &aValue, aSerialized);
   475 }
   477 void
   478 nsAttrValue::SetTo(const SVGLengthList& aValue,
   479                    const nsAString* aSerialized)
   480 {
   481   // While an empty string will parse as a length list, there's no need to store
   482   // it (and SetMiscAtomOrString will assert if we try)
   483   if (aSerialized && aSerialized->IsEmpty()) {
   484     aSerialized = nullptr;
   485   }
   486   SetSVGType(eSVGLengthList, &aValue, aSerialized);
   487 }
   489 void
   490 nsAttrValue::SetTo(const SVGNumberList& aValue,
   491                    const nsAString* aSerialized)
   492 {
   493   // While an empty string will parse as a number list, there's no need to store
   494   // it (and SetMiscAtomOrString will assert if we try)
   495   if (aSerialized && aSerialized->IsEmpty()) {
   496     aSerialized = nullptr;
   497   }
   498   SetSVGType(eSVGNumberList, &aValue, aSerialized);
   499 }
   501 void
   502 nsAttrValue::SetTo(const nsSVGNumberPair& aValue, const nsAString* aSerialized)
   503 {
   504   SetSVGType(eSVGNumberPair, &aValue, aSerialized);
   505 }
   507 void
   508 nsAttrValue::SetTo(const SVGPathData& aValue,
   509                    const nsAString* aSerialized)
   510 {
   511   // While an empty string will parse as path data, there's no need to store it
   512   // (and SetMiscAtomOrString will assert if we try)
   513   if (aSerialized && aSerialized->IsEmpty()) {
   514     aSerialized = nullptr;
   515   }
   516   SetSVGType(eSVGPathData, &aValue, aSerialized);
   517 }
   519 void
   520 nsAttrValue::SetTo(const SVGPointList& aValue,
   521                    const nsAString* aSerialized)
   522 {
   523   // While an empty string will parse as a point list, there's no need to store
   524   // it (and SetMiscAtomOrString will assert if we try)
   525   if (aSerialized && aSerialized->IsEmpty()) {
   526     aSerialized = nullptr;
   527   }
   528   SetSVGType(eSVGPointList, &aValue, aSerialized);
   529 }
   531 void
   532 nsAttrValue::SetTo(const SVGAnimatedPreserveAspectRatio& aValue,
   533                    const nsAString* aSerialized)
   534 {
   535   SetSVGType(eSVGPreserveAspectRatio, &aValue, aSerialized);
   536 }
   538 void
   539 nsAttrValue::SetTo(const SVGStringList& aValue,
   540                    const nsAString* aSerialized)
   541 {
   542   // While an empty string will parse as a string list, there's no need to store
   543   // it (and SetMiscAtomOrString will assert if we try)
   544   if (aSerialized && aSerialized->IsEmpty()) {
   545     aSerialized = nullptr;
   546   }
   547   SetSVGType(eSVGStringList, &aValue, aSerialized);
   548 }
   550 void
   551 nsAttrValue::SetTo(const SVGTransformList& aValue,
   552                    const nsAString* aSerialized)
   553 {
   554   // While an empty string will parse as a transform list, there's no need to
   555   // store it (and SetMiscAtomOrString will assert if we try)
   556   if (aSerialized && aSerialized->IsEmpty()) {
   557     aSerialized = nullptr;
   558   }
   559   SetSVGType(eSVGTransformList, &aValue, aSerialized);
   560 }
   562 void
   563 nsAttrValue::SetTo(const nsSVGViewBox& aValue, const nsAString* aSerialized)
   564 {
   565   SetSVGType(eSVGViewBox, &aValue, aSerialized);
   566 }
   568 void
   569 nsAttrValue::SwapValueWith(nsAttrValue& aOther)
   570 {
   571   uintptr_t tmp = aOther.mBits;
   572   aOther.mBits = mBits;
   573   mBits = tmp;
   574 }
   576 void
   577 nsAttrValue::ToString(nsAString& aResult) const
   578 {
   579   MiscContainer* cont = nullptr;
   580   if (BaseType() == eOtherBase) {
   581     cont = GetMiscContainer();
   583     if (cont->GetString(aResult)) {
   584       return;
   585     }
   586   }
   588   switch(Type()) {
   589     case eString:
   590     {
   591       nsStringBuffer* str = static_cast<nsStringBuffer*>(GetPtr());
   592       if (str) {
   593         str->ToString(str->StorageSize()/sizeof(char16_t) - 1, aResult);
   594       }
   595       else {
   596         aResult.Truncate();
   597       }
   598       break;
   599     }
   600     case eAtom:
   601     {
   602       nsIAtom *atom = static_cast<nsIAtom*>(GetPtr());
   603       atom->ToString(aResult);
   605       break;
   606     }
   607     case eInteger:
   608     {
   609       nsAutoString intStr;
   610       intStr.AppendInt(GetIntegerValue());
   611       aResult = intStr;
   613       break;
   614     }
   615 #ifdef DEBUG
   616     case eColor:
   617     {
   618       NS_NOTREACHED("color attribute without string data");
   619       aResult.Truncate();
   620       break;
   621     }
   622 #endif
   623     case eEnum:
   624     {
   625       GetEnumString(aResult, false);
   626       break;
   627     }
   628     case ePercent:
   629     {
   630       nsAutoString intStr;
   631       intStr.AppendInt(cont ? cont->mValue.mPercent : GetIntInternal());
   632       aResult = intStr + NS_LITERAL_STRING("%");
   634       break;
   635     }
   636     case eCSSStyleRule:
   637     {
   638       aResult.Truncate();
   639       MiscContainer *container = GetMiscContainer();
   640       css::Declaration *decl =
   641         container->mValue.mCSSStyleRule->GetDeclaration();
   642       if (decl) {
   643         decl->ToString(aResult);
   644       }
   645       const_cast<nsAttrValue*>(this)->SetMiscAtomOrString(&aResult);
   647       break;
   648     }
   649     case eDoubleValue:
   650     {
   651       aResult.Truncate();
   652       aResult.AppendFloat(GetDoubleValue());
   653       break;
   654     }
   655     case eSVGAngle:
   656     {
   657       SVGAttrValueWrapper::ToString(GetMiscContainer()->mValue.mSVGAngle,
   658                                     aResult);
   659       break;
   660     }
   661     case eSVGIntegerPair:
   662     {
   663       SVGAttrValueWrapper::ToString(GetMiscContainer()->mValue.mSVGIntegerPair,
   664                                     aResult);
   665       break;
   666     }
   667     case eSVGLength:
   668     {
   669       SVGAttrValueWrapper::ToString(GetMiscContainer()->mValue.mSVGLength,
   670                                     aResult);
   671       break;
   672     }
   673     case eSVGLengthList:
   674     {
   675       SVGAttrValueWrapper::ToString(GetMiscContainer()->mValue.mSVGLengthList,
   676                                     aResult);
   677       break;
   678     }
   679     case eSVGNumberList:
   680     {
   681       SVGAttrValueWrapper::ToString(GetMiscContainer()->mValue.mSVGNumberList,
   682                                     aResult);
   683       break;
   684     }
   685     case eSVGNumberPair:
   686     {
   687       SVGAttrValueWrapper::ToString(GetMiscContainer()->mValue.mSVGNumberPair,
   688                                     aResult);
   689       break;
   690     }
   691     case eSVGPathData:
   692     {
   693       SVGAttrValueWrapper::ToString(GetMiscContainer()->mValue.mSVGPathData,
   694                                     aResult);
   695       break;
   696     }
   697     case eSVGPointList:
   698     {
   699       SVGAttrValueWrapper::ToString(GetMiscContainer()->mValue.mSVGPointList,
   700                                     aResult);
   701       break;
   702     }
   703     case eSVGPreserveAspectRatio:
   704     {
   705       SVGAttrValueWrapper::ToString(GetMiscContainer()->mValue.mSVGPreserveAspectRatio,
   706                                     aResult);
   707       break;
   708     }
   709     case eSVGStringList:
   710     {
   711       SVGAttrValueWrapper::ToString(GetMiscContainer()->mValue.mSVGStringList,
   712                                     aResult);
   713       break;
   714     }
   715     case eSVGTransformList:
   716     {
   717       SVGAttrValueWrapper::ToString(GetMiscContainer()->mValue.mSVGTransformList,
   718                                     aResult);
   719       break;
   720     }
   721     case eSVGViewBox:
   722     {
   723       SVGAttrValueWrapper::ToString(GetMiscContainer()->mValue.mSVGViewBox,
   724                                     aResult);
   725       break;
   726     }
   727     default:
   728     {
   729       aResult.Truncate();
   730       break;
   731     }
   732   }
   733 }
   735 already_AddRefed<nsIAtom>
   736 nsAttrValue::GetAsAtom() const
   737 {
   738   switch (Type()) {
   739     case eString:
   740       return do_GetAtom(GetStringValue());
   742     case eAtom:
   743       {
   744         nsCOMPtr<nsIAtom> atom = GetAtomValue();
   745         return atom.forget();
   746       }
   748     default:
   749       {
   750         nsAutoString val;
   751         ToString(val);
   752         return do_GetAtom(val);
   753       }
   754   }
   755 }
   757 const nsCheapString
   758 nsAttrValue::GetStringValue() const
   759 {
   760   NS_PRECONDITION(Type() == eString, "wrong type");
   762   return nsCheapString(static_cast<nsStringBuffer*>(GetPtr()));
   763 }
   765 bool
   766 nsAttrValue::GetColorValue(nscolor& aColor) const
   767 {
   768   if (Type() != eColor) {
   769     // Unparseable value, treat as unset.
   770     NS_ASSERTION(Type() == eString, "unexpected type for color-valued attr");
   771     return false;
   772   }
   774   aColor = GetMiscContainer()->mValue.mColor;
   775   return true;
   776 }
   778 void
   779 nsAttrValue::GetEnumString(nsAString& aResult, bool aRealTag) const
   780 {
   781   NS_PRECONDITION(Type() == eEnum, "wrong type");
   783   uint32_t allEnumBits =
   784     (BaseType() == eIntegerBase) ? static_cast<uint32_t>(GetIntInternal())
   785                                    : GetMiscContainer()->mValue.mEnumValue;
   786   int16_t val = allEnumBits >> NS_ATTRVALUE_ENUMTABLEINDEX_BITS;
   787   const EnumTable* table = sEnumTableArray->
   788     ElementAt(allEnumBits & NS_ATTRVALUE_ENUMTABLEINDEX_MASK);
   790   while (table->tag) {
   791     if (table->value == val) {
   792       aResult.AssignASCII(table->tag);
   793       if (!aRealTag && allEnumBits & NS_ATTRVALUE_ENUMTABLE_VALUE_NEEDS_TO_UPPER) {
   794         nsContentUtils::ASCIIToUpper(aResult);
   795       }
   796       return;
   797     }
   798     table++;
   799   }
   801   NS_NOTREACHED("couldn't find value in EnumTable");
   802 }
   804 uint32_t
   805 nsAttrValue::GetAtomCount() const
   806 {
   807   ValueType type = Type();
   809   if (type == eAtom) {
   810     return 1;
   811   }
   813   if (type == eAtomArray) {
   814     return GetAtomArrayValue()->Length();
   815   }
   817   return 0;
   818 }
   820 nsIAtom*
   821 nsAttrValue::AtomAt(int32_t aIndex) const
   822 {
   823   NS_PRECONDITION(aIndex >= 0, "Index must not be negative");
   824   NS_PRECONDITION(GetAtomCount() > uint32_t(aIndex), "aIndex out of range");
   826   if (BaseType() == eAtomBase) {
   827     return GetAtomValue();
   828   }
   830   NS_ASSERTION(Type() == eAtomArray, "GetAtomCount must be confused");
   832   return GetAtomArrayValue()->ElementAt(aIndex);
   833 }
   835 uint32_t
   836 nsAttrValue::HashValue() const
   837 {
   838   switch(BaseType()) {
   839     case eStringBase:
   840     {
   841       nsStringBuffer* str = static_cast<nsStringBuffer*>(GetPtr());
   842       if (str) {
   843         uint32_t len = str->StorageSize()/sizeof(char16_t) - 1;
   844         return HashString(static_cast<char16_t*>(str->Data()), len);
   845       }
   847       return 0;
   848     }
   849     case eOtherBase:
   850     {
   851       break;
   852     }
   853     case eAtomBase:
   854     case eIntegerBase:
   855     {
   856       // mBits and uint32_t might have different size. This should silence
   857       // any warnings or compile-errors. This is what the implementation of
   858       // NS_PTR_TO_INT32 does to take care of the same problem.
   859       return mBits - 0;
   860     }
   861   }
   863   MiscContainer* cont = GetMiscContainer();
   864   if (static_cast<ValueBaseType>(cont->mStringBits & NS_ATTRVALUE_BASETYPE_MASK)
   865       == eAtomBase) {
   866     return cont->mStringBits - 0;
   867   }
   869   switch (cont->mType) {
   870     case eInteger:
   871     {
   872       return cont->mValue.mInteger;
   873     }
   874     case eEnum:
   875     {
   876       return cont->mValue.mEnumValue;
   877     }
   878     case ePercent:
   879     {
   880       return cont->mValue.mPercent;
   881     }
   882     case eColor:
   883     {
   884       return cont->mValue.mColor;
   885     }
   886     case eCSSStyleRule:
   887     {
   888       return NS_PTR_TO_INT32(cont->mValue.mCSSStyleRule);
   889     }
   890     // Intentionally identical, so that loading the image does not change the
   891     // hash code.
   892     case eURL:
   893     case eImage:
   894     {
   895       nsString str;
   896       ToString(str);
   897       return HashString(str);
   898     }
   899     case eAtomArray:
   900     {
   901       uint32_t hash = 0;
   902       uint32_t count = cont->mValue.mAtomArray->Length();
   903       for (nsCOMPtr<nsIAtom> *cur = cont->mValue.mAtomArray->Elements(),
   904                              *end = cur + count;
   905            cur != end; ++cur) {
   906         hash = AddToHash(hash, cur->get());
   907       }
   908       return hash;
   909     }
   910     case eDoubleValue:
   911     {
   912       // XXX this is crappy, but oh well
   913       return cont->mDoubleValue;
   914     }
   915     case eIntMarginValue:
   916     {
   917       return NS_PTR_TO_INT32(cont->mValue.mIntMargin);
   918     }
   919     default:
   920     {
   921       if (IsSVGType(cont->mType)) {
   922         // All SVG types are just pointers to classes so we can treat them alike
   923         return NS_PTR_TO_INT32(cont->mValue.mSVGAngle);
   924       }
   925       NS_NOTREACHED("unknown type stored in MiscContainer");
   926       return 0;
   927     }
   928   }
   929 }
   931 bool
   932 nsAttrValue::Equals(const nsAttrValue& aOther) const
   933 {
   934   if (BaseType() != aOther.BaseType()) {
   935     return false;
   936   }
   938   switch(BaseType()) {
   939     case eStringBase:
   940     {
   941       return GetStringValue().Equals(aOther.GetStringValue());
   942     }
   943     case eOtherBase:
   944     {
   945       break;
   946     }
   947     case eAtomBase:
   948     case eIntegerBase:
   949     {
   950       return mBits == aOther.mBits;
   951     }
   952   }
   954   MiscContainer* thisCont = GetMiscContainer();
   955   MiscContainer* otherCont = aOther.GetMiscContainer();
   956   if (thisCont == otherCont) {
   957     return true;
   958   }
   960   if (thisCont->mType != otherCont->mType) {
   961     return false;
   962   }
   964   bool needsStringComparison = false;
   966   switch (thisCont->mType) {
   967     case eInteger:
   968     {
   969       if (thisCont->mValue.mInteger == otherCont->mValue.mInteger) {
   970         needsStringComparison = true;
   971       }
   972       break;
   973     }
   974     case eEnum:
   975     {
   976       if (thisCont->mValue.mEnumValue == otherCont->mValue.mEnumValue) {
   977         needsStringComparison = true;
   978       }
   979       break;
   980     }
   981     case ePercent:
   982     {
   983       if (thisCont->mValue.mPercent == otherCont->mValue.mPercent) {
   984         needsStringComparison = true;
   985       }
   986       break;
   987     }
   988     case eColor:
   989     {
   990       if (thisCont->mValue.mColor == otherCont->mValue.mColor) {
   991         needsStringComparison = true;
   992       }
   993       break;
   994     }
   995     case eCSSStyleRule:
   996     {
   997       return thisCont->mValue.mCSSStyleRule == otherCont->mValue.mCSSStyleRule;
   998     }
   999     case eURL:
  1001       return thisCont->mValue.mURL == otherCont->mValue.mURL;
  1003     case eImage:
  1005       return thisCont->mValue.mImage == otherCont->mValue.mImage;
  1007     case eAtomArray:
  1009       // For classlists we could be insensitive to order, however
  1010       // classlists are never mapped attributes so they are never compared.
  1012       if (!(*thisCont->mValue.mAtomArray == *otherCont->mValue.mAtomArray)) {
  1013         return false;
  1016       needsStringComparison = true;
  1017       break;
  1019     case eDoubleValue:
  1021       return thisCont->mDoubleValue == otherCont->mDoubleValue;
  1023     case eIntMarginValue:
  1025       return thisCont->mValue.mIntMargin == otherCont->mValue.mIntMargin;
  1027     default:
  1029       if (IsSVGType(thisCont->mType)) {
  1030         // Currently this method is never called for nsAttrValue objects that
  1031         // point to SVG data types.
  1032         // If that changes then we probably want to add methods to the
  1033         // corresponding SVG types to compare their base values.
  1034         // As a shortcut, however, we can begin by comparing the pointers.
  1035         NS_ABORT_IF_FALSE(false, "Comparing nsAttrValues that point to SVG "
  1036           "data");
  1037         return false;
  1039       NS_NOTREACHED("unknown type stored in MiscContainer");
  1040       return false;
  1043   if (needsStringComparison) {
  1044     if (thisCont->mStringBits == otherCont->mStringBits) {
  1045       return true;
  1047     if ((static_cast<ValueBaseType>(thisCont->mStringBits & NS_ATTRVALUE_BASETYPE_MASK) ==
  1048          eStringBase) &&
  1049         (static_cast<ValueBaseType>(otherCont->mStringBits & NS_ATTRVALUE_BASETYPE_MASK) ==
  1050          eStringBase)) {
  1051       return nsCheapString(reinterpret_cast<nsStringBuffer*>(thisCont->mStringBits)).Equals(
  1052         nsCheapString(reinterpret_cast<nsStringBuffer*>(otherCont->mStringBits)));
  1055   return false;
  1058 bool
  1059 nsAttrValue::Equals(const nsAString& aValue,
  1060                     nsCaseTreatment aCaseSensitive) const
  1062   switch (BaseType()) {
  1063     case eStringBase:
  1065       nsStringBuffer* str = static_cast<nsStringBuffer*>(GetPtr());
  1066       if (str) {
  1067         nsDependentString dep(static_cast<char16_t*>(str->Data()),
  1068                               str->StorageSize()/sizeof(char16_t) - 1);
  1069         return aCaseSensitive == eCaseMatters ? aValue.Equals(dep) :
  1070           nsContentUtils::EqualsIgnoreASCIICase(aValue, dep);
  1072       return aValue.IsEmpty();
  1074     case eAtomBase:
  1075       if (aCaseSensitive == eCaseMatters) {
  1076         return static_cast<nsIAtom*>(GetPtr())->Equals(aValue);
  1078       return nsContentUtils::EqualsIgnoreASCIICase(
  1079           nsDependentAtomString(static_cast<nsIAtom*>(GetPtr())),
  1080           aValue);
  1081     default:
  1082       break;
  1085   nsAutoString val;
  1086   ToString(val);
  1087   return aCaseSensitive == eCaseMatters ? val.Equals(aValue) :
  1088     nsContentUtils::EqualsIgnoreASCIICase(val, aValue);
  1091 bool
  1092 nsAttrValue::Equals(nsIAtom* aValue, nsCaseTreatment aCaseSensitive) const
  1094   if (aCaseSensitive != eCaseMatters) {
  1095     // Need a better way to handle this!
  1096     nsAutoString value;
  1097     aValue->ToString(value);
  1098     return Equals(value, aCaseSensitive);
  1101   switch (BaseType()) {
  1102     case eStringBase:
  1104       nsStringBuffer* str = static_cast<nsStringBuffer*>(GetPtr());
  1105       if (str) {
  1106         nsDependentString dep(static_cast<char16_t*>(str->Data()),
  1107                               str->StorageSize()/sizeof(char16_t) - 1);
  1108         return aValue->Equals(dep);
  1110       return aValue == nsGkAtoms::_empty;
  1112     case eAtomBase:
  1114       return static_cast<nsIAtom*>(GetPtr()) == aValue;
  1116     default:
  1117       break;
  1120   nsAutoString val;
  1121   ToString(val);
  1122   return aValue->Equals(val);
  1125 bool
  1126 nsAttrValue::EqualsAsStrings(const nsAttrValue& aOther) const
  1128   if (Type() == aOther.Type()) {
  1129     return Equals(aOther);
  1132   // We need to serialize at least one nsAttrValue before passing to
  1133   // Equals(const nsAString&), but we can avoid unnecessarily serializing both
  1134   // by checking if one is already of a string type.
  1135   bool thisIsString = (BaseType() == eStringBase || BaseType() == eAtomBase);
  1136   const nsAttrValue& lhs = thisIsString ? *this : aOther;
  1137   const nsAttrValue& rhs = thisIsString ? aOther : *this;
  1139   switch (rhs.BaseType()) {
  1140     case eAtomBase:
  1141       return lhs.Equals(rhs.GetAtomValue(), eCaseMatters);
  1143     case eStringBase:
  1144       return lhs.Equals(rhs.GetStringValue(), eCaseMatters);
  1146     default:
  1148       nsAutoString val;
  1149       rhs.ToString(val);
  1150       return lhs.Equals(val, eCaseMatters);
  1155 bool
  1156 nsAttrValue::Contains(nsIAtom* aValue, nsCaseTreatment aCaseSensitive) const
  1158   switch (BaseType()) {
  1159     case eAtomBase:
  1161       nsIAtom* atom = GetAtomValue();
  1163       if (aCaseSensitive == eCaseMatters) {
  1164         return aValue == atom;
  1167       // For performance reasons, don't do a full on unicode case insensitive
  1168       // string comparison. This is only used for quirks mode anyway.
  1169       return
  1170         nsContentUtils::EqualsIgnoreASCIICase(nsDependentAtomString(aValue),
  1171                                               nsDependentAtomString(atom));
  1173     default:
  1175       if (Type() == eAtomArray) {
  1176         AtomArray* array = GetAtomArrayValue();
  1177         if (aCaseSensitive == eCaseMatters) {
  1178           return array->Contains(aValue);
  1181         nsDependentAtomString val1(aValue);
  1183         for (nsCOMPtr<nsIAtom> *cur = array->Elements(),
  1184                                *end = cur + array->Length();
  1185              cur != end; ++cur) {
  1186           // For performance reasons, don't do a full on unicode case
  1187           // insensitive string comparison. This is only used for quirks mode
  1188           // anyway.
  1189           if (nsContentUtils::EqualsIgnoreASCIICase(val1,
  1190                 nsDependentAtomString(*cur))) {
  1191             return true;
  1198   return false;
  1201 struct AtomArrayStringComparator {
  1202   bool Equals(nsIAtom* atom, const nsAString& string) const {
  1203     return atom->Equals(string);
  1205 };
  1207 bool
  1208 nsAttrValue::Contains(const nsAString& aValue) const
  1210   switch (BaseType()) {
  1211     case eAtomBase:
  1213       nsIAtom* atom = GetAtomValue();
  1214       return atom->Equals(aValue);
  1216     default:
  1218       if (Type() == eAtomArray) {
  1219         AtomArray* array = GetAtomArrayValue();
  1220         return array->Contains(aValue, AtomArrayStringComparator());
  1225   return false;
  1228 void
  1229 nsAttrValue::ParseAtom(const nsAString& aValue)
  1231   ResetIfSet();
  1233   nsCOMPtr<nsIAtom> atom = NS_NewAtom(aValue);
  1234   if (atom) {
  1235     SetPtrValueAndType(atom.forget().take(), eAtomBase);
  1239 void
  1240 nsAttrValue::ParseAtomArray(const nsAString& aValue)
  1242   nsAString::const_iterator iter, end;
  1243   aValue.BeginReading(iter);
  1244   aValue.EndReading(end);
  1245   bool hasSpace = false;
  1247   // skip initial whitespace
  1248   while (iter != end && nsContentUtils::IsHTMLWhitespace(*iter)) {
  1249     hasSpace = true;
  1250     ++iter;
  1253   if (iter == end) {
  1254     SetTo(aValue);
  1255     return;
  1258   nsAString::const_iterator start(iter);
  1260   // get first - and often only - atom
  1261   do {
  1262     ++iter;
  1263   } while (iter != end && !nsContentUtils::IsHTMLWhitespace(*iter));
  1265   nsCOMPtr<nsIAtom> classAtom = do_GetAtom(Substring(start, iter));
  1266   if (!classAtom) {
  1267     Reset();
  1268     return;
  1271   // skip whitespace
  1272   while (iter != end && nsContentUtils::IsHTMLWhitespace(*iter)) {
  1273     hasSpace = true;
  1274     ++iter;
  1277   if (iter == end && !hasSpace) {
  1278     // we only found one classname and there was no whitespace so
  1279     // don't bother storing a list
  1280     ResetIfSet();
  1281     nsIAtom* atom = nullptr;
  1282     classAtom.swap(atom);
  1283     SetPtrValueAndType(atom, eAtomBase);
  1284     return;
  1287   if (!EnsureEmptyAtomArray()) {
  1288     return;
  1291   AtomArray* array = GetAtomArrayValue();
  1293   if (!array->AppendElement(classAtom)) {
  1294     Reset();
  1295     return;
  1298   // parse the rest of the classnames
  1299   while (iter != end) {
  1300     start = iter;
  1302     do {
  1303       ++iter;
  1304     } while (iter != end && !nsContentUtils::IsHTMLWhitespace(*iter));
  1306     classAtom = do_GetAtom(Substring(start, iter));
  1308     if (!array->AppendElement(classAtom)) {
  1309       Reset();
  1310       return;
  1313     // skip whitespace
  1314     while (iter != end && nsContentUtils::IsHTMLWhitespace(*iter)) {
  1315       ++iter;
  1319   SetMiscAtomOrString(&aValue);
  1320   return;
  1323 void
  1324 nsAttrValue::ParseStringOrAtom(const nsAString& aValue)
  1326   uint32_t len = aValue.Length();
  1327   // Don't bother with atoms if it's an empty string since
  1328   // we can store those efficently anyway.
  1329   if (len && len <= NS_ATTRVALUE_MAX_STRINGLENGTH_ATOM) {
  1330     ParseAtom(aValue);
  1332   else {
  1333     SetTo(aValue);
  1337 void
  1338 nsAttrValue::SetIntValueAndType(int32_t aValue, ValueType aType,
  1339                                 const nsAString* aStringValue)
  1341   if (aStringValue || aValue > NS_ATTRVALUE_INTEGERTYPE_MAXVALUE ||
  1342       aValue < NS_ATTRVALUE_INTEGERTYPE_MINVALUE) {
  1343     MiscContainer* cont = EnsureEmptyMiscContainer();
  1344     switch (aType) {
  1345       case eInteger:
  1347         cont->mValue.mInteger = aValue;
  1348         break;
  1350       case ePercent:
  1352         cont->mValue.mPercent = aValue;
  1353         break;
  1355       case eEnum:
  1357         cont->mValue.mEnumValue = aValue;
  1358         break;
  1360       default:
  1362         NS_NOTREACHED("unknown integer type");
  1363         break;
  1366     cont->mType = aType;
  1367     SetMiscAtomOrString(aStringValue);
  1368   } else {
  1369     NS_ASSERTION(!mBits, "Reset before calling SetIntValueAndType!");
  1370     mBits = (aValue * NS_ATTRVALUE_INTEGERTYPE_MULTIPLIER) | aType;
  1374 int16_t
  1375 nsAttrValue::GetEnumTableIndex(const EnumTable* aTable)
  1377   int16_t index = sEnumTableArray->IndexOf(aTable);
  1378   if (index < 0) {
  1379     index = sEnumTableArray->Length();
  1380     NS_ASSERTION(index <= NS_ATTRVALUE_ENUMTABLEINDEX_MAXVALUE,
  1381         "too many enum tables");
  1382     sEnumTableArray->AppendElement(aTable);
  1385   return index;
  1388 int32_t
  1389 nsAttrValue::EnumTableEntryToValue(const EnumTable* aEnumTable,
  1390                                    const EnumTable* aTableEntry)
  1392   int16_t index = GetEnumTableIndex(aEnumTable);
  1393   int32_t value = (aTableEntry->value << NS_ATTRVALUE_ENUMTABLEINDEX_BITS) +
  1394                   index;
  1395   return value;
  1398 bool
  1399 nsAttrValue::ParseEnumValue(const nsAString& aValue,
  1400                             const EnumTable* aTable,
  1401                             bool aCaseSensitive,
  1402                             const EnumTable* aDefaultValue)
  1404   ResetIfSet();
  1405   const EnumTable* tableEntry = aTable;
  1407   while (tableEntry->tag) {
  1408     if (aCaseSensitive ? aValue.EqualsASCII(tableEntry->tag) :
  1409                          aValue.LowerCaseEqualsASCII(tableEntry->tag)) {
  1410       int32_t value = EnumTableEntryToValue(aTable, tableEntry);
  1412       bool equals = aCaseSensitive || aValue.EqualsASCII(tableEntry->tag);
  1413       if (!equals) {
  1414         nsAutoString tag;
  1415         tag.AssignASCII(tableEntry->tag);
  1416         nsContentUtils::ASCIIToUpper(tag);
  1417         if ((equals = tag.Equals(aValue))) {
  1418           value |= NS_ATTRVALUE_ENUMTABLE_VALUE_NEEDS_TO_UPPER;
  1421       SetIntValueAndType(value, eEnum, equals ? nullptr : &aValue);
  1422       NS_ASSERTION(GetEnumValue() == tableEntry->value,
  1423                    "failed to store enum properly");
  1425       return true;
  1427     tableEntry++;
  1430   if (aDefaultValue) {
  1431     NS_PRECONDITION(aTable <= aDefaultValue && aDefaultValue < tableEntry,
  1432                     "aDefaultValue not inside aTable?");
  1433     SetIntValueAndType(EnumTableEntryToValue(aTable, aDefaultValue),
  1434                        eEnum, &aValue);
  1435     return true;
  1438   return false;
  1441 bool
  1442 nsAttrValue::ParseSpecialIntValue(const nsAString& aString)
  1444   ResetIfSet();
  1446   nsresult ec;
  1447   bool strict;
  1448   bool isPercent = false;
  1449   nsAutoString tmp(aString);
  1450   int32_t originalVal = StringToInteger(aString, &strict, &ec, true, &isPercent);
  1452   if (NS_FAILED(ec)) {
  1453     return false;
  1456   int32_t val = std::max(originalVal, 0);
  1458   // % (percent)
  1459   if (isPercent || tmp.RFindChar('%') >= 0) {
  1460     isPercent = true;
  1463   strict = strict && (originalVal == val);
  1465   SetIntValueAndType(val,
  1466                      isPercent ? ePercent : eInteger,
  1467                      strict ? nullptr : &aString);
  1468   return true;
  1471 bool
  1472 nsAttrValue::ParseIntWithBounds(const nsAString& aString,
  1473                                 int32_t aMin, int32_t aMax)
  1475   NS_PRECONDITION(aMin < aMax, "bad boundaries");
  1477   ResetIfSet();
  1479   nsresult ec;
  1480   bool strict;
  1481   int32_t originalVal = StringToInteger(aString, &strict, &ec);
  1482   if (NS_FAILED(ec)) {
  1483     return false;
  1486   int32_t val = std::max(originalVal, aMin);
  1487   val = std::min(val, aMax);
  1488   strict = strict && (originalVal == val);
  1489   SetIntValueAndType(val, eInteger, strict ? nullptr : &aString);
  1491   return true;
  1494 bool
  1495 nsAttrValue::ParseNonNegativeIntValue(const nsAString& aString)
  1497   ResetIfSet();
  1499   nsresult ec;
  1500   bool strict;
  1501   int32_t originalVal = StringToInteger(aString, &strict, &ec);
  1502   if (NS_FAILED(ec) || originalVal < 0) {
  1503     return false;
  1506   SetIntValueAndType(originalVal, eInteger, strict ? nullptr : &aString);
  1508   return true;
  1511 bool
  1512 nsAttrValue::ParsePositiveIntValue(const nsAString& aString)
  1514   ResetIfSet();
  1516   nsresult ec;
  1517   bool strict;
  1518   int32_t originalVal = StringToInteger(aString, &strict, &ec);
  1519   if (NS_FAILED(ec) || originalVal <= 0) {
  1520     return false;
  1523   SetIntValueAndType(originalVal, eInteger, strict ? nullptr : &aString);
  1525   return true;
  1528 void
  1529 nsAttrValue::SetColorValue(nscolor aColor, const nsAString& aString)
  1531   nsStringBuffer* buf = GetStringBuffer(aString).take();
  1532   if (!buf) {
  1533     return;
  1536   MiscContainer* cont = EnsureEmptyMiscContainer();
  1537   cont->mValue.mColor = aColor;
  1538   cont->mType = eColor;
  1540   // Save the literal string we were passed for round-tripping.
  1541   cont->mStringBits = reinterpret_cast<uintptr_t>(buf) | eStringBase;
  1544 bool
  1545 nsAttrValue::ParseColor(const nsAString& aString)
  1547   ResetIfSet();
  1549   // FIXME (partially, at least): HTML5's algorithm says we shouldn't do
  1550   // the whitespace compression, trimming, or the test for emptiness.
  1551   // (I'm a little skeptical that we shouldn't do the whitespace
  1552   // trimming; WebKit also does it.)
  1553   nsAutoString colorStr(aString);
  1554   colorStr.CompressWhitespace(true, true);
  1555   if (colorStr.IsEmpty()) {
  1556     return false;
  1559   nscolor color;
  1560   // No color names begin with a '#'; in standards mode, all acceptable
  1561   // numeric colors do.
  1562   if (colorStr.First() == '#') {
  1563     nsDependentString withoutHash(colorStr.get() + 1, colorStr.Length() - 1);
  1564     if (NS_HexToRGB(withoutHash, &color)) {
  1565       SetColorValue(color, aString);
  1566       return true;
  1568   } else {
  1569     if (NS_ColorNameToRGB(colorStr, &color)) {
  1570       SetColorValue(color, aString);
  1571       return true;
  1575   // FIXME (maybe): HTML5 says we should handle system colors.  This
  1576   // means we probably need another storage type, since we'd need to
  1577   // handle dynamic changes.  However, I think this is a bad idea:
  1578   // http://lists.whatwg.org/pipermail/whatwg-whatwg.org/2010-May/026449.html
  1580   // Use NS_LooseHexToRGB as a fallback if nothing above worked.
  1581   if (NS_LooseHexToRGB(colorStr, &color)) {
  1582     SetColorValue(color, aString);
  1583     return true;
  1586   return false;
  1589 bool nsAttrValue::ParseDoubleValue(const nsAString& aString)
  1591   ResetIfSet();
  1593   nsresult ec;
  1594   double val = PromiseFlatString(aString).ToDouble(&ec);
  1595   if (NS_FAILED(ec)) {
  1596     return false;
  1599   MiscContainer* cont = EnsureEmptyMiscContainer();
  1600   cont->mDoubleValue = val;
  1601   cont->mType = eDoubleValue;
  1602   nsAutoString serializedFloat;
  1603   serializedFloat.AppendFloat(val);
  1604   SetMiscAtomOrString(serializedFloat.Equals(aString) ? nullptr : &aString);
  1605   return true;
  1608 bool
  1609 nsAttrValue::ParseIntMarginValue(const nsAString& aString)
  1611   ResetIfSet();
  1613   nsIntMargin margins;
  1614   if (!nsContentUtils::ParseIntMarginValue(aString, margins))
  1615     return false;
  1617   MiscContainer* cont = EnsureEmptyMiscContainer();
  1618   cont->mValue.mIntMargin = new nsIntMargin(margins);
  1619   cont->mType = eIntMarginValue;
  1620   SetMiscAtomOrString(&aString);
  1621   return true;
  1624 void
  1625 nsAttrValue::LoadImage(nsIDocument* aDocument)
  1627   NS_ASSERTION(Type() == eURL, "wrong type");
  1629 #ifdef DEBUG
  1631     nsString val;
  1632     ToString(val);
  1633     NS_ASSERTION(!val.IsEmpty(),
  1634                  "How did we end up with an empty string for eURL");
  1636 #endif
  1638   MiscContainer* cont = GetMiscContainer();
  1639   mozilla::css::URLValue* url = cont->mValue.mURL;
  1640   mozilla::css::ImageValue* image = 
  1641     new css::ImageValue(url->GetURI(), url->mString, url->mReferrer,
  1642                         url->mOriginPrincipal, aDocument);
  1644   NS_ADDREF(image);
  1645   cont->mValue.mImage = image;
  1646   NS_RELEASE(url);
  1647   cont->mType = eImage;
  1650 bool
  1651 nsAttrValue::ParseStyleAttribute(const nsAString& aString,
  1652                                  nsStyledElementNotElementCSSInlineStyle* aElement)
  1654   nsIDocument* ownerDoc = aElement->OwnerDoc();
  1655   nsHTMLCSSStyleSheet* sheet = ownerDoc->GetInlineStyleSheet();
  1656   nsCOMPtr<nsIURI> baseURI = aElement->GetBaseURI();
  1657   nsIURI* docURI = ownerDoc->GetDocumentURI();
  1659   NS_ASSERTION(aElement->NodePrincipal() == ownerDoc->NodePrincipal(),
  1660                "This is unexpected");
  1662   // If the (immutable) document URI does not match the element's base URI
  1663   // (the common case is that they do match) do not cache the rule.  This is
  1664   // because the results of the CSS parser are dependent on these URIs, and we
  1665   // do not want to have to account for the URIs in the hash lookup.
  1666   bool cachingAllowed = sheet && baseURI == docURI;
  1667   if (cachingAllowed) {
  1668     MiscContainer* cont = sheet->LookupStyleAttr(aString);
  1669     if (cont) {
  1670       // Set our MiscContainer to the cached one.
  1671       NS_ADDREF(cont);
  1672       SetPtrValueAndType(cont, eOtherBase);
  1673       return true;
  1677   css::Loader* cssLoader = ownerDoc->CSSLoader();
  1678   nsCSSParser cssParser(cssLoader);
  1680   nsRefPtr<css::StyleRule> rule;
  1681   cssParser.ParseStyleAttribute(aString, docURI, baseURI,
  1682                                 aElement->NodePrincipal(),
  1683                                 getter_AddRefs(rule));
  1684   if (rule) {
  1685     rule->SetHTMLCSSStyleSheet(sheet);
  1686     SetTo(rule, &aString);
  1687     if (cachingAllowed) {
  1688       MiscContainer* cont = GetMiscContainer();
  1689       cont->Cache();
  1692     return true;
  1695   return false;
  1698 void
  1699 nsAttrValue::SetMiscAtomOrString(const nsAString* aValue)
  1701   NS_ASSERTION(GetMiscContainer(), "Must have MiscContainer!");
  1702   NS_ASSERTION(!GetMiscContainer()->mStringBits,
  1703                "Trying to re-set atom or string!");
  1704   if (aValue) {
  1705     uint32_t len = aValue->Length();
  1706     // * We're allowing eCSSStyleRule attributes to store empty strings as it
  1707     //   can be beneficial to store an empty style attribute as a parsed rule.
  1708     // * We're allowing enumerated values because sometimes the empty
  1709     //   string corresponds to a particular enumerated value, especially
  1710     //   for enumerated values that are not limited enumerated.
  1711     // Add other types as needed.
  1712     NS_ASSERTION(len || Type() == eCSSStyleRule || Type() == eEnum,
  1713                  "Empty string?");
  1714     MiscContainer* cont = GetMiscContainer();
  1715     if (len <= NS_ATTRVALUE_MAX_STRINGLENGTH_ATOM) {
  1716       nsCOMPtr<nsIAtom> atom = NS_NewAtom(*aValue);
  1717       if (atom) {
  1718         cont->mStringBits =
  1719           reinterpret_cast<uintptr_t>(atom.forget().take()) | eAtomBase;
  1721     } else {
  1722       nsStringBuffer* buf = GetStringBuffer(*aValue).take();
  1723       if (buf) {
  1724         cont->mStringBits = reinterpret_cast<uintptr_t>(buf) | eStringBase;
  1730 void
  1731 nsAttrValue::ResetMiscAtomOrString()
  1733   MiscContainer* cont = GetMiscContainer();
  1734   void* ptr = MISC_STR_PTR(cont);
  1735   if (ptr) {
  1736     if (static_cast<ValueBaseType>(cont->mStringBits & NS_ATTRVALUE_BASETYPE_MASK) ==
  1737         eStringBase) {
  1738       static_cast<nsStringBuffer*>(ptr)->Release();
  1739     } else {
  1740       static_cast<nsIAtom*>(ptr)->Release();
  1742     cont->mStringBits = 0;
  1746 void
  1747 nsAttrValue::SetSVGType(ValueType aType, const void* aValue,
  1748                         const nsAString* aSerialized) {
  1749   NS_ABORT_IF_FALSE(IsSVGType(aType), "Not an SVG type");
  1751   MiscContainer* cont = EnsureEmptyMiscContainer();
  1752   // All SVG types are just pointers to classes so just setting any of them
  1753   // will do. We'll lose type-safety but the signature of the calling
  1754   // function should ensure we don't get anything unexpected, and once we
  1755   // stick aValue in a union we lose type information anyway.
  1756   cont->mValue.mSVGAngle = static_cast<const nsSVGAngle*>(aValue);
  1757   cont->mType = aType;
  1758   SetMiscAtomOrString(aSerialized);
  1761 MiscContainer*
  1762 nsAttrValue::ClearMiscContainer()
  1764   MiscContainer* cont = nullptr;
  1765   if (BaseType() == eOtherBase) {
  1766     cont = GetMiscContainer();
  1767     if (cont->IsRefCounted() && cont->mValue.mRefCount > 1) {
  1768       // This MiscContainer is shared, we need a new one.
  1769       NS_RELEASE(cont);
  1771       cont = new MiscContainer;
  1772       SetPtrValueAndType(cont, eOtherBase);
  1774     else {
  1775       switch (cont->mType) {
  1776         case eCSSStyleRule:
  1778           MOZ_ASSERT(cont->mValue.mRefCount == 1);
  1779           cont->Release();
  1780           cont->Evict();
  1781           NS_RELEASE(cont->mValue.mCSSStyleRule);
  1782           break;
  1784         case eURL:
  1786           NS_RELEASE(cont->mValue.mURL);
  1787           break;
  1789         case eImage:
  1791           NS_RELEASE(cont->mValue.mImage);
  1792           break;
  1794         case eAtomArray:
  1796           delete cont->mValue.mAtomArray;
  1797           break;
  1799         case eIntMarginValue:
  1801           delete cont->mValue.mIntMargin;
  1802           break;
  1804         default:
  1806           break;
  1810     ResetMiscAtomOrString();
  1812   else {
  1813     ResetIfSet();
  1816   return cont;
  1819 MiscContainer*
  1820 nsAttrValue::EnsureEmptyMiscContainer()
  1822   MiscContainer* cont = ClearMiscContainer();
  1823   if (cont) {
  1824     MOZ_ASSERT(BaseType() == eOtherBase);
  1825     ResetMiscAtomOrString();
  1826     cont = GetMiscContainer();
  1828   else {
  1829     cont = new MiscContainer;
  1830     SetPtrValueAndType(cont, eOtherBase);
  1833   return cont;
  1836 bool
  1837 nsAttrValue::EnsureEmptyAtomArray()
  1839   if (Type() == eAtomArray) {
  1840     ResetMiscAtomOrString();
  1841     GetAtomArrayValue()->Clear();
  1842     return true;
  1845   AtomArray* array = new AtomArray;
  1846   if (!array) {
  1847     Reset();
  1848     return false;
  1851   MiscContainer* cont = EnsureEmptyMiscContainer();
  1852   cont->mValue.mAtomArray = array;
  1853   cont->mType = eAtomArray;
  1855   return true;
  1858 already_AddRefed<nsStringBuffer>
  1859 nsAttrValue::GetStringBuffer(const nsAString& aValue) const
  1861   uint32_t len = aValue.Length();
  1862   if (!len) {
  1863     return nullptr;
  1866   nsRefPtr<nsStringBuffer> buf = nsStringBuffer::FromString(aValue);
  1867   if (buf && (buf->StorageSize()/sizeof(char16_t) - 1) == len) {
  1868     return buf.forget();
  1871   buf = nsStringBuffer::Alloc((len + 1) * sizeof(char16_t));
  1872   if (!buf) {
  1873     return nullptr;
  1875   char16_t *data = static_cast<char16_t*>(buf->Data());
  1876   CopyUnicodeTo(aValue, 0, data, len);
  1877   data[len] = char16_t(0);
  1878   return buf.forget();
  1881 int32_t
  1882 nsAttrValue::StringToInteger(const nsAString& aValue, bool* aStrict,
  1883                              nsresult* aErrorCode,
  1884                              bool aCanBePercent,
  1885                              bool* aIsPercent) const
  1887   *aStrict = true;
  1888   *aErrorCode = NS_ERROR_ILLEGAL_VALUE;
  1889   if (aCanBePercent) {
  1890     *aIsPercent = false;
  1893   nsAString::const_iterator iter, end;
  1894   aValue.BeginReading(iter);
  1895   aValue.EndReading(end);
  1897   while (iter != end && nsContentUtils::IsHTMLWhitespace(*iter)) {
  1898     *aStrict = false;
  1899     ++iter;
  1902   if (iter == end) {
  1903     return 0;
  1906   bool negate = false;
  1907   if (*iter == char16_t('-')) {
  1908     negate = true;
  1909     ++iter;
  1910   } else if (*iter == char16_t('+')) {
  1911     *aStrict = false;
  1912     ++iter;
  1915   int32_t value = 0;
  1916   int32_t pValue = 0; // Previous value, used to check integer overflow
  1917   while (iter != end) {
  1918     if (*iter >= char16_t('0') && *iter <= char16_t('9')) {
  1919       value = (value * 10) + (*iter - char16_t('0'));
  1920       ++iter;
  1921       // Checking for integer overflow.
  1922       if (pValue > value) {
  1923         *aStrict = false;
  1924         *aErrorCode = NS_ERROR_ILLEGAL_VALUE;
  1925         break;
  1926       } else {
  1927         pValue = value;
  1928         *aErrorCode = NS_OK;
  1930     } else if (aCanBePercent && *iter == char16_t('%')) {
  1931       ++iter;
  1932       *aIsPercent = true;
  1933       if (iter != end) {
  1934         *aStrict = false;
  1935         break;
  1937     } else {
  1938       *aStrict = false;
  1939       break;
  1942   if (negate) {
  1943     value = -value;
  1944     // Checking the special case of -0.
  1945     if (!value) {
  1946       *aStrict = false;
  1950   return value;
  1953 size_t
  1954 nsAttrValue::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
  1956   size_t n = 0;
  1958   switch (BaseType()) {
  1959     case eStringBase:
  1961       nsStringBuffer* str = static_cast<nsStringBuffer*>(GetPtr());
  1962       n += str ? str->SizeOfIncludingThisIfUnshared(aMallocSizeOf) : 0;
  1963       break;
  1965     case eOtherBase:
  1967       MiscContainer* container = GetMiscContainer();
  1968       if (!container) {
  1969         break;
  1971       n += aMallocSizeOf(container);
  1973       void* otherPtr = MISC_STR_PTR(container);
  1974       // We only count the size of the object pointed by otherPtr if it's a
  1975       // string. When it's an atom, it's counted separatly.
  1976       if (otherPtr &&
  1977           static_cast<ValueBaseType>(container->mStringBits & NS_ATTRVALUE_BASETYPE_MASK) == eStringBase) {
  1978         nsStringBuffer* str = static_cast<nsStringBuffer*>(otherPtr);
  1979         n += str ? str->SizeOfIncludingThisIfUnshared(aMallocSizeOf) : 0;
  1982       if (Type() == eCSSStyleRule && container->mValue.mCSSStyleRule) {
  1983         // TODO: mCSSStyleRule might be owned by another object which would
  1984         //       make us count them twice, bug 677493.
  1985         //n += container->mCSSStyleRule->SizeOfIncludingThis(aMallocSizeOf);
  1986       } else if (Type() == eAtomArray && container->mValue.mAtomArray) {
  1987         // Don't measure each nsIAtom, they are measured separatly.
  1988         n += container->mValue.mAtomArray->SizeOfIncludingThis(aMallocSizeOf);
  1990       break;
  1992     case eAtomBase:    // Atoms are counted separately.
  1993     case eIntegerBase: // The value is in mBits, nothing to do.
  1994       break;
  1997   return n;

mercurial