tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

nsAttrValue.cpp (65615B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 /*
      8 * A struct that represents the value (type and actual data) of an
      9 * attribute.
     10 */
     11 
     12 #include "nsAttrValue.h"
     13 
     14 #include <algorithm>
     15 
     16 #include "ReferrerInfo.h"
     17 #include "mozilla/ArrayUtils.h"
     18 #include "mozilla/AttributeStyles.h"
     19 #include "mozilla/BloomFilter.h"
     20 #include "mozilla/ClearOnShutdown.h"
     21 #include "mozilla/DebugOnly.h"
     22 #include "mozilla/DeclarationBlock.h"
     23 #include "mozilla/HashFunctions.h"
     24 #include "mozilla/MemoryReporting.h"
     25 #include "mozilla/SVGAttrValueWrapper.h"
     26 #include "mozilla/ServoBindingTypes.h"
     27 #include "mozilla/ServoUtils.h"
     28 #include "mozilla/ShadowParts.h"
     29 #include "mozilla/URLExtraData.h"
     30 #include "mozilla/dom/Document.h"
     31 #include "nsAttrValueInlines.h"
     32 #include "nsContentUtils.h"
     33 #include "nsIURI.h"
     34 #include "nsReadableUtils.h"
     35 #include "nsStyledElement.h"
     36 #include "nsUnicharUtils.h"
     37 
     38 using namespace mozilla;
     39 
     40 constexpr uint32_t kMiscContainerCacheSize = 128;
     41 static void* gMiscContainerCache[kMiscContainerCacheSize];
     42 static uint32_t gMiscContainerCount = 0;
     43 
     44 /**
     45 * Global cache for eAtomArray MiscContainer objects, to speed up the parsing
     46 * of class attributes with multiple class names.
     47 * This cache doesn't keep anything alive - a MiscContainer removes itself from
     48 * the cache once its last reference is dropped.
     49 */
     50 struct AtomArrayCache {
     51  // We don't keep any strong references, neither to the atom nor to the
     52  // MiscContainer. The MiscContainer removes itself from the cache when
     53  // the last reference to it is dropped, and the atom is kept alive by
     54  // the MiscContainer.
     55  using MapType = nsTHashMap<nsAtom*, MiscContainer*>;
     56 
     57  static MiscContainer* Lookup(nsAtom* aValue) {
     58    if (auto* instance = GetInstance()) {
     59      return instance->LookupImpl(aValue);
     60    }
     61    return nullptr;
     62  }
     63 
     64  static void Insert(nsAtom* aValue, MiscContainer* aCont) {
     65    if (auto* instance = GetInstance()) {
     66      instance->InsertImpl(aValue, aCont);
     67    }
     68  }
     69 
     70  static void Remove(nsAtom* aValue) {
     71    if (auto* instance = GetInstance()) {
     72      instance->RemoveImpl(aValue);
     73    }
     74  }
     75 
     76  static AtomArrayCache* GetInstance() {
     77    static StaticAutoPtr<AtomArrayCache> sInstance;
     78    if (!sInstance && !PastShutdownPhase(ShutdownPhase::XPCOMShutdownFinal)) {
     79      sInstance = new AtomArrayCache();
     80      ClearOnShutdown(&sInstance, ShutdownPhase::XPCOMShutdownFinal);
     81    }
     82    return sInstance;
     83  }
     84 
     85 private:
     86  MiscContainer* LookupImpl(nsAtom* aValue) {
     87    auto lookupResult = mMap.Lookup(aValue);
     88    return lookupResult ? *lookupResult : nullptr;
     89  }
     90 
     91  void InsertImpl(nsAtom* aValue, MiscContainer* aCont) {
     92    MOZ_ASSERT(aCont);
     93    mMap.InsertOrUpdate(aValue, aCont);
     94  }
     95 
     96  void RemoveImpl(nsAtom* aValue) { mMap.Remove(aValue); }
     97 
     98  MapType mMap;
     99 };
    100 
    101 /* static */
    102 MiscContainer* nsAttrValue::AllocMiscContainer() {
    103  MOZ_ASSERT(NS_IsMainThread());
    104 
    105  static_assert(sizeof(gMiscContainerCache) <= 1024);
    106  static_assert(sizeof(MiscContainer) <= 32);
    107 
    108  // Allocate MiscContainer objects in batches to improve performance.
    109  if (gMiscContainerCount == 0) {
    110    for (; gMiscContainerCount < kMiscContainerCacheSize;
    111         ++gMiscContainerCount) {
    112      gMiscContainerCache[gMiscContainerCount] =
    113          moz_xmalloc(sizeof(MiscContainer));
    114    }
    115  }
    116 
    117  return new (gMiscContainerCache[--gMiscContainerCount]) MiscContainer();
    118 }
    119 
    120 /* static */
    121 void nsAttrValue::DeallocMiscContainer(MiscContainer* aCont) {
    122  MOZ_ASSERT(NS_IsMainThread());
    123  if (!aCont) {
    124    return;
    125  }
    126 
    127  aCont->~MiscContainer();
    128 
    129  if (gMiscContainerCount < kMiscContainerCacheSize) {
    130    gMiscContainerCache[gMiscContainerCount++] = aCont;
    131    return;
    132  }
    133 
    134  free(aCont);
    135 }
    136 
    137 bool MiscContainer::GetString(nsAString& aString) const {
    138  bool isString;
    139  void* ptr = GetStringOrAtomPtr(isString);
    140  if (!ptr) {
    141    return false;
    142  }
    143  if (isString) {
    144    auto* buffer = static_cast<mozilla::StringBuffer*>(ptr);
    145    aString.Assign(buffer, buffer->StorageSize() / sizeof(char16_t) - 1);
    146  } else {
    147    static_cast<nsAtom*>(ptr)->ToString(aString);
    148  }
    149  return true;
    150 }
    151 
    152 void MiscContainer::Cache() {
    153  switch (mType) {
    154    case nsAttrValue::eCSSDeclaration: {
    155      MOZ_ASSERT(IsRefCounted());
    156      MOZ_ASSERT(mValue.mRefCount > 0);
    157      MOZ_ASSERT(!mValue.mCached);
    158 
    159      AttributeStyles* attrStyles =
    160          mValue.mCSSDeclaration->GetAttributeStyles();
    161      if (!attrStyles) {
    162        return;
    163      }
    164 
    165      nsString str;
    166      bool gotString = GetString(str);
    167      if (!gotString) {
    168        return;
    169      }
    170 
    171      attrStyles->CacheStyleAttr(str, this);
    172      mValue.mCached = 1;
    173 
    174      // This has to be immutable once it goes into the cache.
    175      mValue.mCSSDeclaration->SetImmutable();
    176      break;
    177    }
    178    case nsAttrValue::eAtomArray: {
    179      MOZ_ASSERT(IsRefCounted());
    180      MOZ_ASSERT(mValue.mRefCount > 0);
    181      MOZ_ASSERT(!mValue.mCached);
    182 
    183      nsAtom* atom = GetStoredAtom();
    184      if (!atom) {
    185        return;
    186      }
    187 
    188      AtomArrayCache::Insert(atom, this);
    189      mValue.mCached = 1;
    190      break;
    191    }
    192    default:
    193      MOZ_ASSERT_UNREACHABLE("unexpected cached nsAttrValue type");
    194      break;
    195  }
    196 }
    197 
    198 void MiscContainer::Evict() {
    199  switch (mType) {
    200    case nsAttrValue::eCSSDeclaration: {
    201      MOZ_ASSERT(IsRefCounted());
    202      MOZ_ASSERT(mValue.mRefCount == 0);
    203 
    204      if (!mValue.mCached) {
    205        return;
    206      }
    207 
    208      AttributeStyles* attrStyles =
    209          mValue.mCSSDeclaration->GetAttributeStyles();
    210      MOZ_ASSERT(attrStyles);
    211 
    212      nsString str;
    213      DebugOnly<bool> gotString = GetString(str);
    214      MOZ_ASSERT(gotString);
    215 
    216      attrStyles->EvictStyleAttr(str, this);
    217      mValue.mCached = 0;
    218      break;
    219    }
    220    case nsAttrValue::eAtomArray: {
    221      MOZ_ASSERT(IsRefCounted());
    222      MOZ_ASSERT(mValue.mRefCount == 0);
    223 
    224      if (!mValue.mCached) {
    225        return;
    226      }
    227 
    228      nsAtom* atom = GetStoredAtom();
    229      MOZ_ASSERT(atom);
    230 
    231      AtomArrayCache::Remove(atom);
    232 
    233      mValue.mCached = 0;
    234      break;
    235    }
    236    default:
    237 
    238      MOZ_ASSERT_UNREACHABLE("unexpected cached nsAttrValue type");
    239      break;
    240  }
    241 }
    242 
    243 nsTArray<nsAttrValue::EnumTableSpan>* nsAttrValue::sEnumTableArray = nullptr;
    244 
    245 nsAttrValue::nsAttrValue() : mBits(0) {}
    246 
    247 nsAttrValue::nsAttrValue(const nsAttrValue& aOther) : mBits(0) {
    248  SetTo(aOther);
    249 }
    250 
    251 nsAttrValue::nsAttrValue(const nsAString& aValue) : mBits(0) { SetTo(aValue); }
    252 
    253 nsAttrValue::nsAttrValue(nsAtom* aValue) : mBits(0) { SetTo(aValue); }
    254 
    255 nsAttrValue::nsAttrValue(already_AddRefed<DeclarationBlock> aValue,
    256                         const nsAString* aSerialized)
    257    : mBits(0) {
    258  SetTo(std::move(aValue), aSerialized);
    259 }
    260 
    261 nsAttrValue::~nsAttrValue() { ResetIfSet(); }
    262 
    263 /* static */
    264 void nsAttrValue::Init() {
    265  MOZ_ASSERT(!sEnumTableArray, "nsAttrValue already initialized");
    266  sEnumTableArray = new nsTArray<EnumTableSpan>;
    267 }
    268 
    269 /* static */
    270 void nsAttrValue::Shutdown() {
    271  MOZ_ASSERT(NS_IsMainThread());
    272  delete sEnumTableArray;
    273  sEnumTableArray = nullptr;
    274 
    275  for (uint32_t i = 0; i < gMiscContainerCount; ++i) {
    276    free(gMiscContainerCache[i]);
    277  }
    278  gMiscContainerCount = 0;
    279 }
    280 
    281 void nsAttrValue::Reset() {
    282  switch (BaseType()) {
    283    case eStringBase: {
    284      if (auto* str = static_cast<mozilla::StringBuffer*>(GetPtr())) {
    285        str->Release();
    286      }
    287      break;
    288    }
    289    case eOtherBase: {
    290      MiscContainer* cont = GetMiscContainer();
    291      if (cont->IsRefCounted() && cont->mValue.mRefCount > 1) {
    292        NS_RELEASE(cont);
    293        break;
    294      }
    295 
    296      DeallocMiscContainer(ClearMiscContainer());
    297 
    298      break;
    299    }
    300    case eAtomBase: {
    301      nsAtom* atom = GetAtomValue();
    302      NS_RELEASE(atom);
    303 
    304      break;
    305    }
    306    case eIntegerBase: {
    307      break;
    308    }
    309  }
    310 
    311  mBits = 0;
    312 }
    313 
    314 void nsAttrValue::SetTo(const nsAttrValue& aOther) {
    315  if (this == &aOther) {
    316    return;
    317  }
    318 
    319  switch (aOther.BaseType()) {
    320    case eStringBase: {
    321      ResetIfSet();
    322      if (auto* str = static_cast<mozilla::StringBuffer*>(aOther.GetPtr())) {
    323        str->AddRef();
    324        SetPtrValueAndType(str, eStringBase);
    325      }
    326      return;
    327    }
    328    case eOtherBase: {
    329      break;
    330    }
    331    case eAtomBase: {
    332      ResetIfSet();
    333      nsAtom* atom = aOther.GetAtomValue();
    334      NS_ADDREF(atom);
    335      SetPtrValueAndType(atom, eAtomBase);
    336      return;
    337    }
    338    case eIntegerBase: {
    339      ResetIfSet();
    340      mBits = aOther.mBits;
    341      return;
    342    }
    343  }
    344 
    345  MiscContainer* otherCont = aOther.GetMiscContainer();
    346  if (otherCont->IsRefCounted()) {
    347    DeallocMiscContainer(ClearMiscContainer());
    348    NS_ADDREF(otherCont);
    349    SetPtrValueAndType(otherCont, eOtherBase);
    350    return;
    351  }
    352 
    353  MiscContainer* cont = EnsureEmptyMiscContainer();
    354  switch (otherCont->mType) {
    355    case eInteger: {
    356      cont->mValue.mInteger = otherCont->mValue.mInteger;
    357      break;
    358    }
    359    case eEnum: {
    360      cont->mValue.mEnumValue = otherCont->mValue.mEnumValue;
    361      break;
    362    }
    363    case ePercent: {
    364      cont->mDoubleValue = otherCont->mDoubleValue;
    365      break;
    366    }
    367    case eColor: {
    368      cont->mValue.mColor = otherCont->mValue.mColor;
    369      break;
    370    }
    371    case eAtomArray:
    372    case eShadowParts:
    373    case eCSSDeclaration: {
    374      MOZ_CRASH("These should be refcounted!");
    375    }
    376    case eURL: {
    377      NS_ADDREF(cont->mValue.mURL = otherCont->mValue.mURL);
    378      break;
    379    }
    380    case eDoubleValue: {
    381      cont->mDoubleValue = otherCont->mDoubleValue;
    382      break;
    383    }
    384    default: {
    385      if (IsSVGType(otherCont->mType)) {
    386        // All SVG types are just pointers to classes and will therefore have
    387        // the same size so it doesn't really matter which one we assign
    388        cont->mValue.mSVGLength = otherCont->mValue.mSVGLength;
    389      } else {
    390        MOZ_ASSERT_UNREACHABLE("unknown type stored in MiscContainer");
    391      }
    392      break;
    393    }
    394  }
    395 
    396  bool isString;
    397  if (void* otherPtr = otherCont->GetStringOrAtomPtr(isString)) {
    398    if (isString) {
    399      static_cast<mozilla::StringBuffer*>(otherPtr)->AddRef();
    400    } else {
    401      static_cast<nsAtom*>(otherPtr)->AddRef();
    402    }
    403    cont->SetStringBitsMainThread(otherCont->mStringBits);
    404  }
    405  // Note, set mType after switch-case, otherwise EnsureEmptyAtomArray doesn't
    406  // work correctly.
    407  cont->mType = otherCont->mType;
    408 }
    409 
    410 void nsAttrValue::SetTo(const nsAString& aValue) {
    411  ResetIfSet();
    412  mozilla::StringBuffer* buf = GetStringBuffer(aValue).take();
    413  if (buf) {
    414    SetPtrValueAndType(buf, eStringBase);
    415  }
    416 }
    417 
    418 void nsAttrValue::SetTo(nsAtom* aValue) {
    419  ResetIfSet();
    420  if (aValue) {
    421    NS_ADDREF(aValue);
    422    SetPtrValueAndType(aValue, eAtomBase);
    423  }
    424 }
    425 
    426 void nsAttrValue::SetTo(int16_t aInt) {
    427  ResetIfSet();
    428  SetIntValueAndType(aInt, eInteger, nullptr);
    429 }
    430 
    431 void nsAttrValue::SetTo(int32_t aInt, const nsAString* aSerialized) {
    432  ResetIfSet();
    433  SetIntValueAndType(aInt, eInteger, aSerialized);
    434 }
    435 
    436 void nsAttrValue::SetTo(double aValue, const nsAString* aSerialized) {
    437  MiscContainer* cont = EnsureEmptyMiscContainer();
    438  cont->mDoubleValue = aValue;
    439  cont->mType = eDoubleValue;
    440  SetMiscAtomOrString(aSerialized);
    441 }
    442 
    443 void nsAttrValue::SetTo(already_AddRefed<DeclarationBlock> aValue,
    444                        const nsAString* aSerialized) {
    445  MiscContainer* cont = EnsureEmptyMiscContainer();
    446  MOZ_ASSERT(cont->mValue.mRefCount == 0);
    447  cont->mValue.mCSSDeclaration = aValue.take();
    448  cont->mType = eCSSDeclaration;
    449  NS_ADDREF(cont);
    450  SetMiscAtomOrString(aSerialized);
    451  MOZ_ASSERT(cont->mValue.mRefCount == 1);
    452 }
    453 
    454 void nsAttrValue::SetTo(nsIURI* aValue, const nsAString* aSerialized) {
    455  MiscContainer* cont = EnsureEmptyMiscContainer();
    456  NS_ADDREF(cont->mValue.mURL = aValue);
    457  cont->mType = eURL;
    458  SetMiscAtomOrString(aSerialized);
    459 }
    460 
    461 void nsAttrValue::SetToSerialized(const nsAttrValue& aOther) {
    462  if (aOther.Type() != nsAttrValue::eString &&
    463      aOther.Type() != nsAttrValue::eAtom) {
    464    nsAutoString val;
    465    aOther.ToString(val);
    466    SetTo(val);
    467  } else {
    468    SetTo(aOther);
    469  }
    470 }
    471 
    472 void nsAttrValue::SetTo(const SVGAnimatedOrient& aValue,
    473                        const nsAString* aSerialized) {
    474  SetSVGType(eSVGOrient, &aValue, aSerialized);
    475 }
    476 
    477 void nsAttrValue::SetTo(const SVGAnimatedIntegerPair& aValue,
    478                        const nsAString* aSerialized) {
    479  SetSVGType(eSVGIntegerPair, &aValue, aSerialized);
    480 }
    481 
    482 void nsAttrValue::SetTo(const SVGAnimatedLength& aValue,
    483                        const nsAString* aSerialized) {
    484  SetSVGType(eSVGLength, &aValue, aSerialized);
    485 }
    486 
    487 void nsAttrValue::SetTo(const SVGLengthList& aValue,
    488                        const nsAString* aSerialized) {
    489  // While an empty string will parse as a length list, there's no need to store
    490  // it (and SetMiscAtomOrString will assert if we try)
    491  if (aSerialized && aSerialized->IsEmpty()) {
    492    aSerialized = nullptr;
    493  }
    494  SetSVGType(eSVGLengthList, &aValue, aSerialized);
    495 }
    496 
    497 void nsAttrValue::SetTo(const SVGNumberList& aValue,
    498                        const nsAString* aSerialized) {
    499  // While an empty string will parse as a number list, there's no need to store
    500  // it (and SetMiscAtomOrString will assert if we try)
    501  if (aSerialized && aSerialized->IsEmpty()) {
    502    aSerialized = nullptr;
    503  }
    504  SetSVGType(eSVGNumberList, &aValue, aSerialized);
    505 }
    506 
    507 void nsAttrValue::SetTo(const SVGAnimatedNumberPair& aValue,
    508                        const nsAString* aSerialized) {
    509  SetSVGType(eSVGNumberPair, &aValue, aSerialized);
    510 }
    511 
    512 void nsAttrValue::SetTo(const SVGPathData& aValue,
    513                        const nsAString* aSerialized) {
    514  // While an empty string will parse as path data, there's no need to store it
    515  // (and SetMiscAtomOrString will assert if we try)
    516  if (aSerialized && aSerialized->IsEmpty()) {
    517    aSerialized = nullptr;
    518  }
    519  SetSVGType(eSVGPathData, &aValue, aSerialized);
    520 }
    521 
    522 void nsAttrValue::SetTo(const SVGPointList& aValue,
    523                        const nsAString* aSerialized) {
    524  // While an empty string will parse as a point list, there's no need to store
    525  // it (and SetMiscAtomOrString will assert if we try)
    526  if (aSerialized && aSerialized->IsEmpty()) {
    527    aSerialized = nullptr;
    528  }
    529  SetSVGType(eSVGPointList, &aValue, aSerialized);
    530 }
    531 
    532 void nsAttrValue::SetTo(const SVGAnimatedPreserveAspectRatio& aValue,
    533                        const nsAString* aSerialized) {
    534  SetSVGType(eSVGPreserveAspectRatio, &aValue, aSerialized);
    535 }
    536 
    537 void nsAttrValue::SetTo(const SVGStringList& aValue,
    538                        const nsAString* aSerialized) {
    539  // While an empty string will parse as a string list, there's no need to store
    540  // it (and SetMiscAtomOrString will assert if we try)
    541  if (aSerialized && aSerialized->IsEmpty()) {
    542    aSerialized = nullptr;
    543  }
    544  SetSVGType(eSVGStringList, &aValue, aSerialized);
    545 }
    546 
    547 void nsAttrValue::SetTo(const SVGTransformList& aValue,
    548                        const nsAString* aSerialized) {
    549  // While an empty string will parse as a transform list, there's no need to
    550  // store it (and SetMiscAtomOrString will assert if we try)
    551  if (aSerialized && aSerialized->IsEmpty()) {
    552    aSerialized = nullptr;
    553  }
    554  SetSVGType(eSVGTransformList, &aValue, aSerialized);
    555 }
    556 
    557 void nsAttrValue::SetTo(const SVGAnimatedViewBox& aValue,
    558                        const nsAString* aSerialized) {
    559  SetSVGType(eSVGViewBox, &aValue, aSerialized);
    560 }
    561 
    562 void nsAttrValue::SwapValueWith(nsAttrValue& aOther) {
    563  uintptr_t tmp = aOther.mBits;
    564  aOther.mBits = mBits;
    565  mBits = tmp;
    566 }
    567 
    568 void nsAttrValue::RemoveDuplicatesFromAtomArray() {
    569  if (Type() != eAtomArray) {
    570    return;
    571  }
    572 
    573  const AttrAtomArray* currentAtomArray = GetMiscContainer()->mValue.mAtomArray;
    574  UniquePtr<AttrAtomArray> deduplicatedAtomArray =
    575      currentAtomArray->CreateDeduplicatedCopyIfDifferent();
    576 
    577  if (!deduplicatedAtomArray) {
    578    // No duplicates found. Leave this value unchanged.
    579    return;
    580  }
    581 
    582  // We found duplicates. Wrap the new atom array into a fresh MiscContainer,
    583  // and copy over the existing container's string or atom.
    584 
    585  MiscContainer* oldCont = GetMiscContainer();
    586  MOZ_ASSERT(oldCont->IsRefCounted());
    587 
    588  uintptr_t stringBits = 0;
    589  bool isString = false;
    590  if (void* otherPtr = oldCont->GetStringOrAtomPtr(isString)) {
    591    stringBits = oldCont->mStringBits;
    592    if (isString) {
    593      static_cast<mozilla::StringBuffer*>(otherPtr)->AddRef();
    594    } else {
    595      static_cast<nsAtom*>(otherPtr)->AddRef();
    596    }
    597  }
    598 
    599  MiscContainer* cont = EnsureEmptyMiscContainer();
    600  MOZ_ASSERT(cont->mValue.mRefCount == 0);
    601  cont->mValue.mAtomArray = deduplicatedAtomArray.release();
    602  cont->mType = eAtomArray;
    603  NS_ADDREF(cont);
    604  MOZ_ASSERT(cont->mValue.mRefCount == 1);
    605  cont->SetStringBitsMainThread(stringBits);
    606 
    607  // Don't cache the new container. It would stomp over the undeduplicated
    608  // value in the cache. But we could have a separate cache for deduplicated
    609  // atom arrays, if repeated deduplication shows up in profiles.
    610 }
    611 
    612 void nsAttrValue::ToString(nsAString& aResult) const {
    613  MiscContainer* cont = nullptr;
    614  if (BaseType() == eOtherBase) {
    615    cont = GetMiscContainer();
    616 
    617    if (cont->GetString(aResult)) {
    618      return;
    619    }
    620  }
    621 
    622  switch (Type()) {
    623    case eString: {
    624      if (auto* str = static_cast<mozilla::StringBuffer*>(GetPtr())) {
    625        aResult.Assign(str, str->StorageSize() / sizeof(char16_t) - 1);
    626      } else {
    627        aResult.Truncate();
    628      }
    629      break;
    630    }
    631    case eAtom: {
    632      auto* atom = static_cast<nsAtom*>(GetPtr());
    633      atom->ToString(aResult);
    634      break;
    635    }
    636    case eInteger: {
    637      nsAutoString intStr;
    638      intStr.AppendInt(GetIntegerValue());
    639      aResult = intStr;
    640 
    641      break;
    642    }
    643 #ifdef DEBUG
    644    case eColor: {
    645      MOZ_ASSERT_UNREACHABLE("color attribute without string data");
    646      aResult.Truncate();
    647      break;
    648    }
    649 #endif
    650    case eEnum: {
    651      GetEnumString(aResult, false);
    652      break;
    653    }
    654    case ePercent: {
    655      nsAutoString str;
    656      if (cont) {
    657        str.AppendFloat(cont->mDoubleValue);
    658      } else {
    659        str.AppendInt(GetIntInternal());
    660      }
    661      aResult = str + u"%"_ns;
    662 
    663      break;
    664    }
    665    case eCSSDeclaration: {
    666      aResult.Truncate();
    667      MiscContainer* container = GetMiscContainer();
    668      if (DeclarationBlock* decl = container->mValue.mCSSDeclaration) {
    669        nsAutoCString result;
    670        decl->ToString(result);
    671        CopyUTF8toUTF16(result, aResult);
    672      }
    673 
    674      // This can be reached during parallel selector matching with attribute
    675      // selectors on the style attribute. SetMiscAtomOrString handles this
    676      // case, and as of this writing this is the only consumer that needs it.
    677      const_cast<nsAttrValue*>(this)->SetMiscAtomOrString(&aResult);
    678 
    679      break;
    680    }
    681    case eDoubleValue: {
    682      aResult.Truncate();
    683      aResult.AppendFloat(GetDoubleValue());
    684      break;
    685    }
    686    case eSVGIntegerPair: {
    687      SVGAttrValueWrapper::ToString(
    688          GetMiscContainer()->mValue.mSVGAnimatedIntegerPair, aResult);
    689      break;
    690    }
    691    case eSVGOrient: {
    692      SVGAttrValueWrapper::ToString(
    693          GetMiscContainer()->mValue.mSVGAnimatedOrient, aResult);
    694      break;
    695    }
    696    case eSVGLength: {
    697      SVGAttrValueWrapper::ToString(GetMiscContainer()->mValue.mSVGLength,
    698                                    aResult);
    699      break;
    700    }
    701    case eSVGLengthList: {
    702      SVGAttrValueWrapper::ToString(GetMiscContainer()->mValue.mSVGLengthList,
    703                                    aResult);
    704      break;
    705    }
    706    case eSVGNumberList: {
    707      SVGAttrValueWrapper::ToString(GetMiscContainer()->mValue.mSVGNumberList,
    708                                    aResult);
    709      break;
    710    }
    711    case eSVGNumberPair: {
    712      SVGAttrValueWrapper::ToString(
    713          GetMiscContainer()->mValue.mSVGAnimatedNumberPair, aResult);
    714      break;
    715    }
    716    case eSVGPathData: {
    717      SVGAttrValueWrapper::ToString(GetMiscContainer()->mValue.mSVGPathData,
    718                                    aResult);
    719      break;
    720    }
    721    case eSVGPointList: {
    722      SVGAttrValueWrapper::ToString(GetMiscContainer()->mValue.mSVGPointList,
    723                                    aResult);
    724      break;
    725    }
    726    case eSVGPreserveAspectRatio: {
    727      SVGAttrValueWrapper::ToString(
    728          GetMiscContainer()->mValue.mSVGAnimatedPreserveAspectRatio, aResult);
    729      break;
    730    }
    731    case eSVGStringList: {
    732      SVGAttrValueWrapper::ToString(GetMiscContainer()->mValue.mSVGStringList,
    733                                    aResult);
    734      break;
    735    }
    736    case eSVGTransformList: {
    737      SVGAttrValueWrapper::ToString(
    738          GetMiscContainer()->mValue.mSVGTransformList, aResult);
    739      break;
    740    }
    741    case eSVGViewBox: {
    742      SVGAttrValueWrapper::ToString(
    743          GetMiscContainer()->mValue.mSVGAnimatedViewBox, aResult);
    744      break;
    745    }
    746    default: {
    747      aResult.Truncate();
    748      break;
    749    }
    750  }
    751 }
    752 
    753 already_AddRefed<nsAtom> nsAttrValue::GetAsAtom() const {
    754  switch (Type()) {
    755    case eString:
    756      return NS_AtomizeMainThread(GetStringValue());
    757 
    758    case eAtom: {
    759      RefPtr<nsAtom> atom = GetAtomValue();
    760      return atom.forget();
    761    }
    762 
    763    default: {
    764      nsAutoString val;
    765      ToString(val);
    766      return NS_AtomizeMainThread(val);
    767    }
    768  }
    769 }
    770 
    771 const nsCheapString nsAttrValue::GetStringValue() const {
    772  MOZ_ASSERT(Type() == eString, "wrong type");
    773 
    774  return nsCheapString(static_cast<mozilla::StringBuffer*>(GetPtr()));
    775 }
    776 
    777 bool nsAttrValue::GetColorValue(nscolor& aColor) const {
    778  if (Type() != eColor) {
    779    // Unparseable value, treat as unset.
    780    NS_ASSERTION(Type() == eString, "unexpected type for color-valued attr");
    781    return false;
    782  }
    783 
    784  aColor = GetMiscContainer()->mValue.mColor;
    785  return true;
    786 }
    787 
    788 void nsAttrValue::GetEnumString(nsAString& aResult, bool aRealTag) const {
    789  MOZ_ASSERT(Type() == eEnum, "wrong type");
    790 
    791  uint32_t allEnumBits = (BaseType() == eIntegerBase)
    792                             ? static_cast<uint32_t>(GetIntInternal())
    793                             : GetMiscContainer()->mValue.mEnumValue;
    794  int16_t val = allEnumBits >> NS_ATTRVALUE_ENUMTABLEINDEX_BITS;
    795  EnumTableSpan table = sEnumTableArray->ElementAt(
    796      allEnumBits & NS_ATTRVALUE_ENUMTABLEINDEX_MASK);
    797  for (const auto& entry : table) {
    798    if (entry.value == val) {
    799      aResult.AssignASCII(entry.tag);
    800      if (!aRealTag &&
    801          allEnumBits & NS_ATTRVALUE_ENUMTABLE_VALUE_NEEDS_TO_UPPER) {
    802        nsContentUtils::ASCIIToUpper(aResult);
    803      }
    804      return;
    805    }
    806  }
    807 
    808  MOZ_ASSERT_UNREACHABLE("couldn't find value in EnumTable");
    809 }
    810 
    811 UniquePtr<AttrAtomArray> AttrAtomArray::CreateDeduplicatedCopyIfDifferentImpl()
    812    const {
    813  MOZ_ASSERT(mMayContainDuplicates);
    814 
    815  bool usingHashTable = false;
    816  BitBloomFilter<8, nsAtom> filter;
    817  nsTHashSet<nsAtom*> hash;
    818 
    819  auto CheckDuplicate = [&](size_t i) {
    820    nsAtom* atom = mArray[i];
    821    if (!usingHashTable) {
    822      if (!filter.mightContain(atom)) {
    823        filter.add(atom);
    824        return false;
    825      }
    826      for (size_t j = 0; j < i; ++j) {
    827        hash.Insert(mArray[j]);
    828      }
    829      usingHashTable = true;
    830    }
    831    return !hash.EnsureInserted(atom);
    832  };
    833 
    834  size_t len = mArray.Length();
    835  UniquePtr<AttrAtomArray> deduplicatedArray;
    836  for (size_t i = 0; i < len; ++i) {
    837    if (!CheckDuplicate(i)) {
    838      if (deduplicatedArray) {
    839        deduplicatedArray->mArray.AppendElement(mArray[i]);
    840      }
    841      continue;
    842    }
    843    // We've found a duplicate!
    844    if (!deduplicatedArray) {
    845      // Allocate the deduplicated copy and copy the preceding elements into it.
    846      deduplicatedArray = MakeUnique<AttrAtomArray>();
    847      deduplicatedArray->mMayContainDuplicates = false;
    848      deduplicatedArray->mArray.SetCapacity(len - 1);
    849      for (size_t indexToCopy = 0; indexToCopy < i; indexToCopy++) {
    850        deduplicatedArray->mArray.AppendElement(mArray[indexToCopy]);
    851      }
    852    }
    853  }
    854 
    855  if (!deduplicatedArray) {
    856    // This AttrAtomArray doesn't contain any duplicates, cache this information
    857    // for future invocations.
    858    mMayContainDuplicates = false;
    859  }
    860  return deduplicatedArray;
    861 }
    862 
    863 uint32_t nsAttrValue::GetAtomCount() const {
    864  ValueType type = Type();
    865 
    866  if (type == eAtom) {
    867    return 1;
    868  }
    869 
    870  if (type == eAtomArray) {
    871    return GetAtomArrayValue()->mArray.Length();
    872  }
    873 
    874  return 0;
    875 }
    876 
    877 nsAtom* nsAttrValue::AtomAt(int32_t aIndex) const {
    878  MOZ_ASSERT(aIndex >= 0, "Index must not be negative");
    879  MOZ_ASSERT(GetAtomCount() > uint32_t(aIndex), "aIndex out of range");
    880 
    881  if (BaseType() == eAtomBase) {
    882    return GetAtomValue();
    883  }
    884 
    885  NS_ASSERTION(Type() == eAtomArray, "GetAtomCount must be confused");
    886  return GetAtomArrayValue()->mArray.ElementAt(aIndex);
    887 }
    888 
    889 uint32_t nsAttrValue::HashValue() const {
    890  switch (BaseType()) {
    891    case eStringBase: {
    892      if (auto* str = static_cast<mozilla::StringBuffer*>(GetPtr())) {
    893        uint32_t len = str->StorageSize() / sizeof(char16_t) - 1;
    894        return HashString(static_cast<char16_t*>(str->Data()), len);
    895      }
    896 
    897      return 0;
    898    }
    899    case eOtherBase: {
    900      break;
    901    }
    902    case eAtomBase:
    903    case eIntegerBase: {
    904      // mBits and uint32_t might have different size. This should silence
    905      // any warnings or compile-errors. This is what the implementation of
    906      // NS_PTR_TO_INT32 does to take care of the same problem.
    907      return mBits - 0;
    908    }
    909  }
    910 
    911  MiscContainer* cont = GetMiscContainer();
    912  if (static_cast<ValueBaseType>(cont->mStringBits &
    913                                 NS_ATTRVALUE_BASETYPE_MASK) == eAtomBase) {
    914    return cont->mStringBits - 0;
    915  }
    916 
    917  switch (cont->mType) {
    918    case eInteger: {
    919      return cont->mValue.mInteger;
    920    }
    921    case eEnum: {
    922      return cont->mValue.mEnumValue;
    923    }
    924    case ePercent: {
    925      return cont->mDoubleValue;
    926    }
    927    case eColor: {
    928      return cont->mValue.mColor;
    929    }
    930    case eCSSDeclaration: {
    931      return NS_PTR_TO_INT32(cont->mValue.mCSSDeclaration);
    932    }
    933    case eURL: {
    934      nsString str;
    935      ToString(str);
    936      return HashString(str);
    937    }
    938    case eAtomArray: {
    939      uint32_t hash = 0;
    940      for (const auto& atom : cont->mValue.mAtomArray->mArray) {
    941        hash = AddToHash(hash, atom.get());
    942      }
    943      return hash;
    944    }
    945    case eDoubleValue: {
    946      // XXX this is crappy, but oh well
    947      return cont->mDoubleValue;
    948    }
    949    default: {
    950      if (IsSVGType(cont->mType)) {
    951        // All SVG types are just pointers to classes so we can treat them alike
    952        return NS_PTR_TO_INT32(cont->mValue.mSVGLength);
    953      }
    954      MOZ_ASSERT_UNREACHABLE("unknown type stored in MiscContainer");
    955      return 0;
    956    }
    957  }
    958 }
    959 
    960 bool nsAttrValue::Equals(const nsAttrValue& aOther) const {
    961  if (BaseType() != aOther.BaseType()) {
    962    return false;
    963  }
    964 
    965  switch (BaseType()) {
    966    case eStringBase: {
    967      return GetStringValue().Equals(aOther.GetStringValue());
    968    }
    969    case eOtherBase: {
    970      break;
    971    }
    972    case eAtomBase:
    973    case eIntegerBase: {
    974      return mBits == aOther.mBits;
    975    }
    976  }
    977 
    978  MiscContainer* thisCont = GetMiscContainer();
    979  MiscContainer* otherCont = aOther.GetMiscContainer();
    980  if (thisCont == otherCont) {
    981    return true;
    982  }
    983 
    984  if (thisCont->mType != otherCont->mType) {
    985    return false;
    986  }
    987 
    988  bool needsStringComparison = false;
    989 
    990  switch (thisCont->mType) {
    991    case eInteger: {
    992      if (thisCont->mValue.mInteger == otherCont->mValue.mInteger) {
    993        needsStringComparison = true;
    994      }
    995      break;
    996    }
    997    case eEnum: {
    998      if (thisCont->mValue.mEnumValue == otherCont->mValue.mEnumValue) {
    999        needsStringComparison = true;
   1000      }
   1001      break;
   1002    }
   1003    case ePercent: {
   1004      if (thisCont->mDoubleValue == otherCont->mDoubleValue) {
   1005        needsStringComparison = true;
   1006      }
   1007      break;
   1008    }
   1009    case eColor: {
   1010      if (thisCont->mValue.mColor == otherCont->mValue.mColor) {
   1011        needsStringComparison = true;
   1012      }
   1013      break;
   1014    }
   1015    case eCSSDeclaration: {
   1016      return thisCont->mValue.mCSSDeclaration ==
   1017             otherCont->mValue.mCSSDeclaration;
   1018    }
   1019    case eURL: {
   1020      return thisCont->mValue.mURL == otherCont->mValue.mURL;
   1021    }
   1022    case eAtomArray: {
   1023      // For classlists we could be insensitive to order, however
   1024      // classlists are never mapped attributes so they are never compared.
   1025 
   1026      if (!(*thisCont->mValue.mAtomArray == *otherCont->mValue.mAtomArray)) {
   1027        return false;
   1028      }
   1029 
   1030      needsStringComparison = true;
   1031      break;
   1032    }
   1033    case eDoubleValue: {
   1034      return thisCont->mDoubleValue == otherCont->mDoubleValue;
   1035    }
   1036    default: {
   1037      if (IsSVGType(thisCont->mType)) {
   1038        // Currently this method is never called for nsAttrValue objects that
   1039        // point to SVG data types.
   1040        // If that changes then we probably want to add methods to the
   1041        // corresponding SVG types to compare their base values.
   1042        // As a shortcut, however, we can begin by comparing the pointers.
   1043        MOZ_ASSERT(false, "Comparing nsAttrValues that point to SVG data");
   1044        return false;
   1045      }
   1046      MOZ_ASSERT_UNREACHABLE("unknown type stored in MiscContainer");
   1047      return false;
   1048    }
   1049  }
   1050  if (needsStringComparison) {
   1051    if (thisCont->mStringBits == otherCont->mStringBits) {
   1052      return true;
   1053    }
   1054    if ((static_cast<ValueBaseType>(thisCont->mStringBits &
   1055                                    NS_ATTRVALUE_BASETYPE_MASK) ==
   1056         eStringBase) &&
   1057        (static_cast<ValueBaseType>(otherCont->mStringBits &
   1058                                    NS_ATTRVALUE_BASETYPE_MASK) ==
   1059         eStringBase)) {
   1060      return nsCheapString(reinterpret_cast<mozilla::StringBuffer*>(
   1061                               static_cast<uintptr_t>(thisCont->mStringBits)))
   1062          .Equals(nsCheapString(reinterpret_cast<mozilla::StringBuffer*>(
   1063              static_cast<uintptr_t>(otherCont->mStringBits))));
   1064    }
   1065  }
   1066  return false;
   1067 }
   1068 
   1069 bool nsAttrValue::Equals(const nsAString& aValue,
   1070                         nsCaseTreatment aCaseSensitive) const {
   1071  switch (BaseType()) {
   1072    case eStringBase: {
   1073      if (auto* str = static_cast<mozilla::StringBuffer*>(GetPtr())) {
   1074        nsDependentString dep(static_cast<char16_t*>(str->Data()),
   1075                              str->StorageSize() / sizeof(char16_t) - 1);
   1076        return aCaseSensitive == eCaseMatters
   1077                   ? aValue.Equals(dep)
   1078                   : nsContentUtils::EqualsIgnoreASCIICase(aValue, dep);
   1079      }
   1080      return aValue.IsEmpty();
   1081    }
   1082    case eAtomBase: {
   1083      auto* atom = static_cast<nsAtom*>(GetPtr());
   1084      if (aCaseSensitive == eCaseMatters) {
   1085        return atom->Equals(aValue);
   1086      }
   1087      return nsContentUtils::EqualsIgnoreASCIICase(nsDependentAtomString(atom),
   1088                                                   aValue);
   1089    }
   1090    default:
   1091      break;
   1092  }
   1093 
   1094  nsAutoString val;
   1095  ToString(val);
   1096  return aCaseSensitive == eCaseMatters
   1097             ? val.Equals(aValue)
   1098             : nsContentUtils::EqualsIgnoreASCIICase(val, aValue);
   1099 }
   1100 
   1101 bool nsAttrValue::Equals(const nsAtom* aValue,
   1102                         nsCaseTreatment aCaseSensitive) const {
   1103  switch (BaseType()) {
   1104    case eAtomBase: {
   1105      auto* atom = static_cast<nsAtom*>(GetPtr());
   1106      if (atom == aValue) {
   1107        return true;
   1108      }
   1109      if (aCaseSensitive == eCaseMatters) {
   1110        return false;
   1111      }
   1112      if (atom->IsAsciiLowercase() && aValue->IsAsciiLowercase()) {
   1113        return false;
   1114      }
   1115      return nsContentUtils::EqualsIgnoreASCIICase(
   1116          nsDependentAtomString(atom), nsDependentAtomString(aValue));
   1117    }
   1118    case eStringBase: {
   1119      if (auto* str = static_cast<mozilla::StringBuffer*>(GetPtr())) {
   1120        size_t strLen = str->StorageSize() / sizeof(char16_t) - 1;
   1121        if (aValue->GetLength() != strLen) {
   1122          return false;
   1123        }
   1124        const char16_t* strData = static_cast<char16_t*>(str->Data());
   1125        const char16_t* valData = aValue->GetUTF16String();
   1126        if (aCaseSensitive == eCaseMatters) {
   1127          // Avoid string construction / destruction for the easy case.
   1128          return ArrayEqual(strData, valData, strLen);
   1129        }
   1130        nsDependentSubstring depStr(strData, strLen);
   1131        nsDependentSubstring depVal(valData, strLen);
   1132        return nsContentUtils::EqualsIgnoreASCIICase(depStr, depVal);
   1133      }
   1134      return aValue->IsEmpty();
   1135    }
   1136    default:
   1137      break;
   1138  }
   1139 
   1140  nsAutoString val;
   1141  ToString(val);
   1142  nsDependentAtomString dep(aValue);
   1143  return aCaseSensitive == eCaseMatters
   1144             ? val.Equals(dep)
   1145             : nsContentUtils::EqualsIgnoreASCIICase(val, dep);
   1146 }
   1147 
   1148 struct HasPrefixFn {
   1149  static bool Check(const char16_t* aAttrValue, size_t aAttrLen,
   1150                    const nsAString& aSearchValue,
   1151                    nsCaseTreatment aCaseSensitive) {
   1152    if (aCaseSensitive == eCaseMatters) {
   1153      if (aSearchValue.Length() > aAttrLen) {
   1154        return false;
   1155      }
   1156      return !memcmp(aAttrValue, aSearchValue.BeginReading(),
   1157                     aSearchValue.Length() * sizeof(char16_t));
   1158    }
   1159    return StringBeginsWith(nsDependentString(aAttrValue, aAttrLen),
   1160                            aSearchValue,
   1161                            nsASCIICaseInsensitiveStringComparator);
   1162  }
   1163 };
   1164 
   1165 struct HasSuffixFn {
   1166  static bool Check(const char16_t* aAttrValue, size_t aAttrLen,
   1167                    const nsAString& aSearchValue,
   1168                    nsCaseTreatment aCaseSensitive) {
   1169    if (aCaseSensitive == eCaseMatters) {
   1170      if (aSearchValue.Length() > aAttrLen) {
   1171        return false;
   1172      }
   1173      return !memcmp(aAttrValue + aAttrLen - aSearchValue.Length(),
   1174                     aSearchValue.BeginReading(),
   1175                     aSearchValue.Length() * sizeof(char16_t));
   1176    }
   1177    return StringEndsWith(nsDependentString(aAttrValue, aAttrLen), aSearchValue,
   1178                          nsASCIICaseInsensitiveStringComparator);
   1179  }
   1180 };
   1181 
   1182 struct HasSubstringFn {
   1183  static bool Check(const char16_t* aAttrValue, size_t aAttrLen,
   1184                    const nsAString& aSearchValue,
   1185                    nsCaseTreatment aCaseSensitive) {
   1186    if (aCaseSensitive == eCaseMatters) {
   1187      if (aSearchValue.IsEmpty()) {
   1188        return true;
   1189      }
   1190      const char16_t* end = aAttrValue + aAttrLen;
   1191      return std::search(aAttrValue, end, aSearchValue.BeginReading(),
   1192                         aSearchValue.EndReading()) != end;
   1193    }
   1194    return FindInReadable(aSearchValue, nsDependentString(aAttrValue, aAttrLen),
   1195                          nsASCIICaseInsensitiveStringComparator);
   1196  }
   1197 };
   1198 
   1199 template <typename F>
   1200 bool nsAttrValue::SubstringCheck(const nsAString& aValue,
   1201                                 nsCaseTreatment aCaseSensitive) const {
   1202  switch (BaseType()) {
   1203    case eStringBase: {
   1204      if (auto* str = static_cast<mozilla::StringBuffer*>(GetPtr())) {
   1205        return F::Check(static_cast<char16_t*>(str->Data()),
   1206                        str->StorageSize() / sizeof(char16_t) - 1, aValue,
   1207                        aCaseSensitive);
   1208      }
   1209      return aValue.IsEmpty();
   1210    }
   1211    case eAtomBase: {
   1212      auto* atom = static_cast<nsAtom*>(GetPtr());
   1213      return F::Check(atom->GetUTF16String(), atom->GetLength(), aValue,
   1214                      aCaseSensitive);
   1215    }
   1216    default:
   1217      break;
   1218  }
   1219 
   1220  nsAutoString val;
   1221  ToString(val);
   1222  return F::Check(val.BeginReading(), val.Length(), aValue, aCaseSensitive);
   1223 }
   1224 
   1225 bool nsAttrValue::HasPrefix(const nsAString& aValue,
   1226                            nsCaseTreatment aCaseSensitive) const {
   1227  return SubstringCheck<HasPrefixFn>(aValue, aCaseSensitive);
   1228 }
   1229 
   1230 bool nsAttrValue::HasSuffix(const nsAString& aValue,
   1231                            nsCaseTreatment aCaseSensitive) const {
   1232  return SubstringCheck<HasSuffixFn>(aValue, aCaseSensitive);
   1233 }
   1234 
   1235 bool nsAttrValue::HasSubstring(const nsAString& aValue,
   1236                               nsCaseTreatment aCaseSensitive) const {
   1237  return SubstringCheck<HasSubstringFn>(aValue, aCaseSensitive);
   1238 }
   1239 
   1240 bool nsAttrValue::EqualsAsStrings(const nsAttrValue& aOther) const {
   1241  if (Type() == aOther.Type()) {
   1242    return Equals(aOther);
   1243  }
   1244 
   1245  // We need to serialize at least one nsAttrValue before passing to
   1246  // Equals(const nsAString&), but we can avoid unnecessarily serializing both
   1247  // by checking if one is already of a string type.
   1248  bool thisIsString = (BaseType() == eStringBase || BaseType() == eAtomBase);
   1249  const nsAttrValue& lhs = thisIsString ? *this : aOther;
   1250  const nsAttrValue& rhs = thisIsString ? aOther : *this;
   1251 
   1252  switch (rhs.BaseType()) {
   1253    case eAtomBase:
   1254      return lhs.Equals(rhs.GetAtomValue(), eCaseMatters);
   1255 
   1256    case eStringBase:
   1257      return lhs.Equals(rhs.GetStringValue(), eCaseMatters);
   1258 
   1259    default: {
   1260      nsAutoString val;
   1261      rhs.ToString(val);
   1262      return lhs.Equals(val, eCaseMatters);
   1263    }
   1264  }
   1265 }
   1266 
   1267 bool nsAttrValue::Contains(nsAtom* aValue,
   1268                           nsCaseTreatment aCaseSensitive) const {
   1269  switch (BaseType()) {
   1270    case eAtomBase: {
   1271      nsAtom* atom = GetAtomValue();
   1272      if (aCaseSensitive == eCaseMatters) {
   1273        return aValue == atom;
   1274      }
   1275 
   1276      // For performance reasons, don't do a full on unicode case insensitive
   1277      // string comparison. This is only used for quirks mode anyway.
   1278      return nsContentUtils::EqualsIgnoreASCIICase(aValue, atom);
   1279    }
   1280    default: {
   1281      if (Type() == eAtomArray) {
   1282        const AttrAtomArray* array = GetAtomArrayValue();
   1283        if (aCaseSensitive == eCaseMatters) {
   1284          return array->mArray.Contains(aValue);
   1285        }
   1286 
   1287        for (const RefPtr<nsAtom>& cur : array->mArray) {
   1288          // For performance reasons, don't do a full on unicode case
   1289          // insensitive string comparison. This is only used for quirks mode
   1290          // anyway.
   1291          if (nsContentUtils::EqualsIgnoreASCIICase(aValue, cur)) {
   1292            return true;
   1293          }
   1294        }
   1295      }
   1296    }
   1297  }
   1298 
   1299  return false;
   1300 }
   1301 
   1302 struct AtomArrayStringComparator {
   1303  bool Equals(nsAtom* atom, const nsAString& string) const {
   1304    return atom->Equals(string);
   1305  }
   1306 };
   1307 
   1308 bool nsAttrValue::Contains(const nsAString& aValue) const {
   1309  switch (BaseType()) {
   1310    case eAtomBase: {
   1311      nsAtom* atom = GetAtomValue();
   1312      return atom->Equals(aValue);
   1313    }
   1314    default: {
   1315      if (Type() == eAtomArray) {
   1316        const AttrAtomArray* array = GetAtomArrayValue();
   1317        return array->mArray.Contains(aValue, AtomArrayStringComparator());
   1318      }
   1319    }
   1320  }
   1321 
   1322  return false;
   1323 }
   1324 
   1325 void nsAttrValue::ParseAtom(const nsAString& aValue) {
   1326  ResetIfSet();
   1327 
   1328  RefPtr<nsAtom> atom = NS_Atomize(aValue);
   1329  if (atom) {
   1330    SetPtrValueAndType(atom.forget().take(), eAtomBase);
   1331  }
   1332 }
   1333 
   1334 void nsAttrValue::ParseAtomArray(nsAtom* aValue) {
   1335  if (MiscContainer* cont = AtomArrayCache::Lookup(aValue)) {
   1336    // Set our MiscContainer to the cached one.
   1337    NS_ADDREF(cont);
   1338    SetPtrValueAndType(cont, eOtherBase);
   1339    return;
   1340  }
   1341 
   1342  const char16_t* iter = aValue->GetUTF16String();
   1343  const char16_t* end = iter + aValue->GetLength();
   1344  bool hasSpace = false;
   1345 
   1346  // skip initial whitespace
   1347  while (iter != end && nsContentUtils::IsHTMLWhitespace(*iter)) {
   1348    hasSpace = true;
   1349    ++iter;
   1350  }
   1351 
   1352  if (iter == end) {
   1353    // The value is empty or only contains whitespace.
   1354    // Set this attribute to the string value.
   1355    // We don't call the SetTo(nsAtom*) overload because doing so would
   1356    // leave us with a classList of length 1.
   1357    SetTo(nsDependentAtomString(aValue));
   1358    return;
   1359  }
   1360 
   1361  const char16_t* start = iter;
   1362 
   1363  // get first - and often only - atom
   1364  do {
   1365    ++iter;
   1366  } while (iter != end && !nsContentUtils::IsHTMLWhitespace(*iter));
   1367 
   1368  RefPtr<nsAtom> classAtom = iter == end && !hasSpace
   1369                                 ? RefPtr<nsAtom>(aValue).forget()
   1370                                 : NS_AtomizeMainThread(Substring(start, iter));
   1371  if (!classAtom) {
   1372    ResetIfSet();
   1373    return;
   1374  }
   1375 
   1376  // skip whitespace
   1377  while (iter != end && nsContentUtils::IsHTMLWhitespace(*iter)) {
   1378    hasSpace = true;
   1379    ++iter;
   1380  }
   1381 
   1382  if (iter == end && !hasSpace) {
   1383    // we only found one classname and there was no whitespace so
   1384    // don't bother storing a list
   1385    ResetIfSet();
   1386    nsAtom* atom = nullptr;
   1387    classAtom.swap(atom);
   1388    SetPtrValueAndType(atom, eAtomBase);
   1389    return;
   1390  }
   1391 
   1392  // We have at least one class atom. Create a new AttrAtomArray.
   1393  AttrAtomArray* array = new AttrAtomArray;
   1394 
   1395  // XXX(Bug 1631371) Check if this should use a fallible operation as it
   1396  // pretended earlier.
   1397  array->mArray.AppendElement(std::move(classAtom));
   1398 
   1399  // parse the rest of the classnames
   1400  while (iter != end) {
   1401    start = iter;
   1402 
   1403    do {
   1404      ++iter;
   1405    } while (iter != end && !nsContentUtils::IsHTMLWhitespace(*iter));
   1406 
   1407    classAtom = NS_AtomizeMainThread(Substring(start, iter));
   1408 
   1409    // XXX(Bug 1631371) Check if this should use a fallible operation as it
   1410    // pretended earlier.
   1411    array->mArray.AppendElement(std::move(classAtom));
   1412    array->mMayContainDuplicates = true;
   1413 
   1414    // skip whitespace
   1415    while (iter != end && nsContentUtils::IsHTMLWhitespace(*iter)) {
   1416      ++iter;
   1417    }
   1418  }
   1419 
   1420  // Wrap the AtomArray into a fresh MiscContainer.
   1421  MiscContainer* cont = EnsureEmptyMiscContainer();
   1422  MOZ_ASSERT(cont->mValue.mRefCount == 0);
   1423  cont->mValue.mAtomArray = array;
   1424  cont->mType = eAtomArray;
   1425  NS_ADDREF(cont);
   1426  MOZ_ASSERT(cont->mValue.mRefCount == 1);
   1427 
   1428  // Assign the atom to the container's string bits (like SetMiscAtomOrString
   1429  // would do).
   1430  MOZ_ASSERT(!IsInServoTraversal());
   1431  aValue->AddRef();
   1432  uintptr_t bits = reinterpret_cast<uintptr_t>(aValue) | eAtomBase;
   1433  cont->SetStringBitsMainThread(bits);
   1434 
   1435  // Put the container in the cache.
   1436  cont->Cache();
   1437 }
   1438 
   1439 void nsAttrValue::ParseAtomArray(const nsAString& aValue) {
   1440  if (aValue.IsVoid()) {
   1441    ResetIfSet();
   1442  } else {
   1443    RefPtr<nsAtom> atom = NS_AtomizeMainThread(aValue);
   1444    ParseAtomArray(atom);
   1445  }
   1446 }
   1447 
   1448 void nsAttrValue::ParseStringOrAtom(const nsAString& aValue) {
   1449  uint32_t len = aValue.Length();
   1450  // Don't bother with atoms if it's an empty string since
   1451  // we can store those efficiently anyway.
   1452  if (len && len <= NS_ATTRVALUE_MAX_STRINGLENGTH_ATOM) {
   1453    ParseAtom(aValue);
   1454  } else {
   1455    SetTo(aValue);
   1456  }
   1457 }
   1458 
   1459 void nsAttrValue::ParsePartMapping(const nsAString& aValue) {
   1460  ResetIfSet();
   1461  MiscContainer* cont = EnsureEmptyMiscContainer();
   1462 
   1463  cont->mType = eShadowParts;
   1464  cont->mValue.mShadowParts = new ShadowParts(ShadowParts::Parse(aValue));
   1465  NS_ADDREF(cont);
   1466  SetMiscAtomOrString(&aValue);
   1467  MOZ_ASSERT(cont->mValue.mRefCount == 1);
   1468 }
   1469 
   1470 void nsAttrValue::SetIntValueAndType(int32_t aValue, ValueType aType,
   1471                                     const nsAString* aStringValue) {
   1472  if (aStringValue || aValue > NS_ATTRVALUE_INTEGERTYPE_MAXVALUE ||
   1473      aValue < NS_ATTRVALUE_INTEGERTYPE_MINVALUE) {
   1474    MiscContainer* cont = EnsureEmptyMiscContainer();
   1475    switch (aType) {
   1476      case eInteger: {
   1477        cont->mValue.mInteger = aValue;
   1478        break;
   1479      }
   1480      case ePercent: {
   1481        cont->mDoubleValue = aValue;
   1482        break;
   1483      }
   1484      case eEnum: {
   1485        cont->mValue.mEnumValue = aValue;
   1486        break;
   1487      }
   1488      default: {
   1489        MOZ_ASSERT_UNREACHABLE("unknown integer type");
   1490        break;
   1491      }
   1492    }
   1493    cont->mType = aType;
   1494    SetMiscAtomOrString(aStringValue);
   1495  } else {
   1496    NS_ASSERTION(!mBits, "Reset before calling SetIntValueAndType!");
   1497    mBits = (aValue * NS_ATTRVALUE_INTEGERTYPE_MULTIPLIER) | aType;
   1498  }
   1499 }
   1500 
   1501 void nsAttrValue::SetDoubleValueAndType(double aValue, ValueType aType,
   1502                                        const nsAString* aStringValue) {
   1503  MOZ_ASSERT(aType == eDoubleValue || aType == ePercent, "Unexpected type");
   1504  MiscContainer* cont = EnsureEmptyMiscContainer();
   1505  cont->mDoubleValue = aValue;
   1506  cont->mType = aType;
   1507  SetMiscAtomOrString(aStringValue);
   1508 }
   1509 
   1510 nsAtom* nsAttrValue::GetStoredAtom() const {
   1511  if (BaseType() == eAtomBase) {
   1512    return static_cast<nsAtom*>(GetPtr());
   1513  }
   1514  if (BaseType() == eOtherBase) {
   1515    return GetMiscContainer()->GetStoredAtom();
   1516  }
   1517  return nullptr;
   1518 }
   1519 
   1520 mozilla::StringBuffer* nsAttrValue::GetStoredStringBuffer() const {
   1521  if (BaseType() == eStringBase) {
   1522    return static_cast<mozilla::StringBuffer*>(GetPtr());
   1523  }
   1524  if (BaseType() == eOtherBase) {
   1525    return GetMiscContainer()->GetStoredStringBuffer();
   1526  }
   1527  return nullptr;
   1528 }
   1529 
   1530 /**
   1531 * Compares two `EnumTableItem` spans by comparing their data pointer.
   1532 */
   1533 struct EnumTablesHaveEqualContent {
   1534  bool Equals(const nsAttrValue::EnumTableSpan& aSpan1,
   1535              const nsAttrValue::EnumTableSpan& aSpan2) const {
   1536    return aSpan1.Elements() == aSpan2.Elements();
   1537  }
   1538 };
   1539 
   1540 int16_t nsAttrValue::GetEnumTableIndex(EnumTableSpan aTable) {
   1541  int16_t index =
   1542      sEnumTableArray->IndexOf(aTable, 0, EnumTablesHaveEqualContent());
   1543  if (index < 0) {
   1544    index = sEnumTableArray->Length();
   1545    NS_ASSERTION(index <= NS_ATTRVALUE_ENUMTABLEINDEX_MAXVALUE,
   1546                 "too many enum tables");
   1547    sEnumTableArray->AppendElement(aTable);
   1548  }
   1549 
   1550  return index;
   1551 }
   1552 
   1553 int32_t nsAttrValue::EnumTableEntryToValue(EnumTableSpan aEnumTable,
   1554                                           const EnumTableEntry& aTableEntry) {
   1555  int16_t index = GetEnumTableIndex(aEnumTable);
   1556  int32_t value =
   1557      (aTableEntry.value << NS_ATTRVALUE_ENUMTABLEINDEX_BITS) + index;
   1558  return value;
   1559 }
   1560 
   1561 bool nsAttrValue::ParseEnumValue(const nsAString& aValue, EnumTableSpan aTable,
   1562                                 bool aCaseSensitive,
   1563                                 const EnumTableEntry* aDefaultValue) {
   1564  ResetIfSet();
   1565  for (const auto& tableEntry : aTable) {
   1566    if (aCaseSensitive ? aValue.EqualsASCII(tableEntry.tag)
   1567                       : aValue.LowerCaseEqualsASCII(tableEntry.tag)) {
   1568      int32_t value = EnumTableEntryToValue(aTable, tableEntry);
   1569 
   1570      bool equals = aCaseSensitive || aValue.EqualsASCII(tableEntry.tag);
   1571      if (!equals) {
   1572        nsAutoString tag;
   1573        tag.AssignASCII(tableEntry.tag);
   1574        nsContentUtils::ASCIIToUpper(tag);
   1575        if ((equals = tag.Equals(aValue))) {
   1576          value |= NS_ATTRVALUE_ENUMTABLE_VALUE_NEEDS_TO_UPPER;
   1577        }
   1578      }
   1579      SetIntValueAndType(value, eEnum, equals ? nullptr : &aValue);
   1580      NS_ASSERTION(GetEnumValue() == tableEntry.value,
   1581                   "failed to store enum properly");
   1582 
   1583      return true;
   1584    }
   1585  }
   1586 
   1587  if (aDefaultValue) {
   1588    SetIntValueAndType(EnumTableEntryToValue(aTable, *aDefaultValue), eEnum,
   1589                       &aValue);
   1590    return true;
   1591  }
   1592 
   1593  return false;
   1594 }
   1595 
   1596 bool nsAttrValue::DoParseHTMLDimension(const nsAString& aInput,
   1597                                       bool aEnsureNonzero) {
   1598  ResetIfSet();
   1599 
   1600  // We don't use nsContentUtils::ParseHTMLInteger here because we
   1601  // need a bunch of behavioral differences from it.  We _could_ try to
   1602  // use it, but it would not be a great fit.
   1603 
   1604  // https://html.spec.whatwg.org/multipage/#rules-for-parsing-dimension-values
   1605 
   1606  // Steps 1 and 2.
   1607  const char16_t* position = aInput.BeginReading();
   1608  const char16_t* end = aInput.EndReading();
   1609 
   1610  // We will need to keep track of whether this was a canonical representation
   1611  // or not.  It's non-canonical if it has leading whitespace, leading '+',
   1612  // leading '0' characters, or trailing garbage.
   1613  bool canonical = true;
   1614 
   1615  // Step 3.
   1616  while (position != end && nsContentUtils::IsHTMLWhitespace(*position)) {
   1617    canonical = false;  // Leading whitespace
   1618    ++position;
   1619  }
   1620 
   1621  // Step 4.
   1622  if (position == end || *position < char16_t('0') ||
   1623      *position > char16_t('9')) {
   1624    return false;
   1625  }
   1626 
   1627  // Step 5.
   1628  CheckedInt32 value = 0;
   1629 
   1630  // Collect up leading '0' first to avoid extra branching in the main
   1631  // loop to set 'canonical' properly.
   1632  while (position != end && *position == char16_t('0')) {
   1633    canonical = false;  // Leading '0'
   1634    ++position;
   1635  }
   1636 
   1637  // Now collect up other digits.
   1638  while (position != end && *position >= char16_t('0') &&
   1639         *position <= char16_t('9')) {
   1640    value = value * 10 + (*position - char16_t('0'));
   1641    if (!value.isValid()) {
   1642      // The spec assumes we can deal with arbitrary-size integers here, but we
   1643      // really can't.  If someone sets something too big, just bail out and
   1644      // ignore it.
   1645      return false;
   1646    }
   1647    ++position;
   1648  }
   1649 
   1650  // Step 6 is implemented implicitly via the various "position != end" guards
   1651  // from this point on.
   1652 
   1653  Maybe<double> doubleValue;
   1654  // Step 7.  The return in step 7.2 is handled by just falling through to the
   1655  // code below this block when we reach end of input or a non-digit, because
   1656  // the while loop will terminate at that point.
   1657  if (position != end && *position == char16_t('.')) {
   1658    canonical = false;  // Let's not rely on double serialization reproducing
   1659                        // the string we started with.
   1660    // Step 7.1.
   1661    ++position;
   1662    // If we have a '.' _not_ followed by digits, this is not as efficient as it
   1663    // could be, because we will store as a double while we could have stored as
   1664    // an int.  But that seems like a pretty rare case.
   1665    doubleValue.emplace(value.value());
   1666    // Step 7.3.
   1667    double divisor = 1.0f;
   1668    // Step 7.4.
   1669    while (position != end && *position >= char16_t('0') &&
   1670           *position <= char16_t('9')) {
   1671      // Step 7.4.1.
   1672      divisor = divisor * 10.0f;
   1673      // Step 7.4.2.
   1674      doubleValue.ref() += (*position - char16_t('0')) / divisor;
   1675      // Step 7.4.3.
   1676      ++position;
   1677      // Step 7.4.4 and 7.4.5 are captured in the while loop condition and the
   1678      // "position != end" checks below.
   1679    }
   1680  }
   1681 
   1682  if (aEnsureNonzero && value.value() == 0 &&
   1683      (!doubleValue || *doubleValue == 0.0f)) {
   1684    // Not valid.  Just drop it.
   1685    return false;
   1686  }
   1687 
   1688  // Step 8 and the spec's early return from step 7.2.
   1689  ValueType type;
   1690  if (position != end && *position == char16_t('%')) {
   1691    type = ePercent;
   1692    ++position;
   1693  } else if (doubleValue) {
   1694    type = eDoubleValue;
   1695  } else {
   1696    type = eInteger;
   1697  }
   1698 
   1699  if (position != end) {
   1700    canonical = false;
   1701  }
   1702 
   1703  if (doubleValue) {
   1704    MOZ_ASSERT(!canonical, "We set it false above!");
   1705    SetDoubleValueAndType(*doubleValue, type, &aInput);
   1706  } else {
   1707    SetIntValueAndType(value.value(), type, canonical ? nullptr : &aInput);
   1708  }
   1709 
   1710 #ifdef DEBUG
   1711  nsAutoString str;
   1712  ToString(str);
   1713  MOZ_ASSERT(str == aInput, "We messed up our 'canonical' boolean!");
   1714 #endif
   1715 
   1716  return true;
   1717 }
   1718 
   1719 bool nsAttrValue::ParseIntWithBounds(const nsAString& aString, int32_t aMin,
   1720                                     int32_t aMax) {
   1721  MOZ_ASSERT(aMin < aMax, "bad boundaries");
   1722 
   1723  ResetIfSet();
   1724 
   1725  nsContentUtils::ParseHTMLIntegerResultFlags result;
   1726  int32_t originalVal = nsContentUtils::ParseHTMLInteger(aString, &result);
   1727  if (result & nsContentUtils::eParseHTMLInteger_Error) {
   1728    return false;
   1729  }
   1730 
   1731  int32_t val = std::max(originalVal, aMin);
   1732  val = std::min(val, aMax);
   1733  bool nonStrict =
   1734      (val != originalVal) ||
   1735      (result & nsContentUtils::eParseHTMLInteger_NonStandard) ||
   1736      (result & nsContentUtils::eParseHTMLInteger_DidNotConsumeAllInput);
   1737 
   1738  SetIntValueAndType(val, eInteger, nonStrict ? &aString : nullptr);
   1739 
   1740  return true;
   1741 }
   1742 
   1743 void nsAttrValue::ParseIntWithFallback(const nsAString& aString,
   1744                                       int32_t aDefault, int32_t aMax) {
   1745  ResetIfSet();
   1746 
   1747  nsContentUtils::ParseHTMLIntegerResultFlags result;
   1748  int32_t val = nsContentUtils::ParseHTMLInteger(aString, &result);
   1749  bool nonStrict = false;
   1750  if ((result & nsContentUtils::eParseHTMLInteger_Error) || val < 1) {
   1751    val = aDefault;
   1752    nonStrict = true;
   1753  }
   1754 
   1755  if (val > aMax) {
   1756    val = aMax;
   1757    nonStrict = true;
   1758  }
   1759 
   1760  if ((result & nsContentUtils::eParseHTMLInteger_NonStandard) ||
   1761      (result & nsContentUtils::eParseHTMLInteger_DidNotConsumeAllInput)) {
   1762    nonStrict = true;
   1763  }
   1764 
   1765  SetIntValueAndType(val, eInteger, nonStrict ? &aString : nullptr);
   1766 }
   1767 
   1768 void nsAttrValue::ParseClampedNonNegativeInt(const nsAString& aString,
   1769                                             int32_t aDefault, int32_t aMin,
   1770                                             int32_t aMax) {
   1771  ResetIfSet();
   1772 
   1773  nsContentUtils::ParseHTMLIntegerResultFlags result;
   1774  int32_t val = nsContentUtils::ParseHTMLInteger(aString, &result);
   1775  bool nonStrict =
   1776      (result & nsContentUtils::eParseHTMLInteger_NonStandard) ||
   1777      (result & nsContentUtils::eParseHTMLInteger_DidNotConsumeAllInput);
   1778 
   1779  if (result & nsContentUtils::eParseHTMLInteger_ErrorOverflow) {
   1780    if (result & nsContentUtils::eParseHTMLInteger_Negative) {
   1781      val = aDefault;
   1782    } else {
   1783      val = aMax;
   1784    }
   1785    nonStrict = true;
   1786  } else if ((result & nsContentUtils::eParseHTMLInteger_Error) || val < 0) {
   1787    val = aDefault;
   1788    nonStrict = true;
   1789  } else if (val < aMin) {
   1790    val = aMin;
   1791    nonStrict = true;
   1792  } else if (val > aMax) {
   1793    val = aMax;
   1794    nonStrict = true;
   1795  }
   1796 
   1797  SetIntValueAndType(val, eInteger, nonStrict ? &aString : nullptr);
   1798 }
   1799 
   1800 bool nsAttrValue::ParseNonNegativeIntValue(const nsAString& aString) {
   1801  ResetIfSet();
   1802 
   1803  nsContentUtils::ParseHTMLIntegerResultFlags result;
   1804  int32_t originalVal = nsContentUtils::ParseHTMLInteger(aString, &result);
   1805  if ((result & nsContentUtils::eParseHTMLInteger_Error) || originalVal < 0) {
   1806    return false;
   1807  }
   1808 
   1809  bool nonStrict =
   1810      (result & nsContentUtils::eParseHTMLInteger_NonStandard) ||
   1811      (result & nsContentUtils::eParseHTMLInteger_DidNotConsumeAllInput);
   1812 
   1813  SetIntValueAndType(originalVal, eInteger, nonStrict ? &aString : nullptr);
   1814 
   1815  return true;
   1816 }
   1817 
   1818 bool nsAttrValue::ParsePositiveIntValue(const nsAString& aString) {
   1819  ResetIfSet();
   1820 
   1821  nsContentUtils::ParseHTMLIntegerResultFlags result;
   1822  int32_t originalVal = nsContentUtils::ParseHTMLInteger(aString, &result);
   1823  if ((result & nsContentUtils::eParseHTMLInteger_Error) || originalVal <= 0) {
   1824    return false;
   1825  }
   1826 
   1827  bool nonStrict =
   1828      (result & nsContentUtils::eParseHTMLInteger_NonStandard) ||
   1829      (result & nsContentUtils::eParseHTMLInteger_DidNotConsumeAllInput);
   1830 
   1831  SetIntValueAndType(originalVal, eInteger, nonStrict ? &aString : nullptr);
   1832 
   1833  return true;
   1834 }
   1835 
   1836 bool nsAttrValue::SetColorValue(nscolor aColor, const nsAString& aString) {
   1837  mozilla::StringBuffer* buf = GetStringBuffer(aString).take();
   1838  if (!buf) {
   1839    return false;
   1840  }
   1841 
   1842  MiscContainer* cont = EnsureEmptyMiscContainer();
   1843  cont->mValue.mColor = aColor;
   1844  cont->mType = eColor;
   1845 
   1846  // Save the literal string we were passed for round-tripping.
   1847  cont->SetStringBitsMainThread(reinterpret_cast<uintptr_t>(buf) | eStringBase);
   1848  return true;
   1849 }
   1850 
   1851 bool nsAttrValue::ParseColor(const nsAString& aString) {
   1852  ResetIfSet();
   1853 
   1854  // FIXME (partially, at least): HTML5's algorithm says we shouldn't do
   1855  // the whitespace compression, trimming, or the test for emptiness.
   1856  // (I'm a little skeptical that we shouldn't do the whitespace
   1857  // trimming; WebKit also does it.)
   1858  nsAutoString colorStr(aString);
   1859  colorStr.CompressWhitespace(true, true);
   1860  if (colorStr.IsEmpty()) {
   1861    return false;
   1862  }
   1863 
   1864  nscolor color;
   1865  // No color names begin with a '#'; in standards mode, all acceptable
   1866  // numeric colors do.
   1867  if (colorStr.First() == '#') {
   1868    nsDependentString withoutHash(colorStr.get() + 1, colorStr.Length() - 1);
   1869    if (NS_HexToRGBA(withoutHash, nsHexColorType::NoAlpha, &color)) {
   1870      return SetColorValue(color, aString);
   1871    }
   1872  } else if (colorStr.LowerCaseEqualsLiteral("transparent")) {
   1873    return SetColorValue(NS_RGBA(0, 0, 0, 0), aString);
   1874  } else {
   1875    const NS_ConvertUTF16toUTF8 colorNameU8(colorStr);
   1876    if (Servo_ColorNameToRgb(&colorNameU8, &color)) {
   1877      return SetColorValue(color, aString);
   1878    }
   1879  }
   1880 
   1881  // FIXME (maybe): HTML5 says we should handle system colors.  This
   1882  // means we probably need another storage type, since we'd need to
   1883  // handle dynamic changes.  However, I think this is a bad idea:
   1884  // http://lists.whatwg.org/pipermail/whatwg-whatwg.org/2010-May/026449.html
   1885 
   1886  // Use NS_LooseHexToRGB as a fallback if nothing above worked.
   1887  if (NS_LooseHexToRGB(colorStr, &color)) {
   1888    return SetColorValue(color, aString);
   1889  }
   1890 
   1891  return false;
   1892 }
   1893 
   1894 bool nsAttrValue::ParseDoubleValue(const nsAString& aString) {
   1895  ResetIfSet();
   1896 
   1897  nsresult ec;
   1898  double val = PromiseFlatString(aString).ToDouble(&ec);
   1899  if (NS_FAILED(ec)) {
   1900    return false;
   1901  }
   1902 
   1903  MiscContainer* cont = EnsureEmptyMiscContainer();
   1904  cont->mDoubleValue = val;
   1905  cont->mType = eDoubleValue;
   1906  nsAutoString serializedFloat;
   1907  serializedFloat.AppendFloat(val);
   1908  SetMiscAtomOrString(serializedFloat.Equals(aString) ? nullptr : &aString);
   1909  return true;
   1910 }
   1911 
   1912 bool nsAttrValue::ParseStyleAttribute(const nsAString& aString,
   1913                                      nsIPrincipal* aMaybeScriptedPrincipal,
   1914                                      nsStyledElement* aElement) {
   1915  dom::Document* doc = aElement->OwnerDoc();
   1916  AttributeStyles* attrStyles = doc->GetAttributeStyles();
   1917  NS_ASSERTION(aElement->NodePrincipal() == doc->NodePrincipal(),
   1918               "This is unexpected");
   1919 
   1920  nsIPrincipal* principal = aMaybeScriptedPrincipal ? aMaybeScriptedPrincipal
   1921                                                    : aElement->NodePrincipal();
   1922  RefPtr<URLExtraData> data = aElement->GetURLDataForStyleAttr(principal);
   1923 
   1924  // If the (immutable) document URI does not match the element's base URI
   1925  // (the common case is that they do match) do not cache the rule.  This is
   1926  // because the results of the CSS parser are dependent on these URIs, and we
   1927  // do not want to have to account for the URIs in the hash lookup.
   1928  // Similarly, if the triggering principal does not match the node principal,
   1929  // do not cache the rule, since the principal will be encoded in any parsed
   1930  // URLs in the rule.
   1931  const bool cachingAllowed = attrStyles &&
   1932                              doc->GetDocumentURI() == data->BaseURI() &&
   1933                              principal == aElement->NodePrincipal();
   1934  if (cachingAllowed) {
   1935    if (MiscContainer* cont = attrStyles->LookupStyleAttr(aString)) {
   1936      // Set our MiscContainer to the cached one.
   1937      NS_ADDREF(cont);
   1938      SetPtrValueAndType(cont, eOtherBase);
   1939      return true;
   1940    }
   1941  }
   1942 
   1943  RefPtr<DeclarationBlock> decl = DeclarationBlock::FromCssText(
   1944      aString, data, doc->GetCompatibilityMode(), doc->GetExistingCSSLoader(),
   1945      StyleCssRuleType::Style);
   1946  if (!decl) {
   1947    return false;
   1948  }
   1949  decl->SetAttributeStyles(attrStyles);
   1950  SetTo(decl.forget(), &aString);
   1951 
   1952  if (cachingAllowed) {
   1953    MiscContainer* cont = GetMiscContainer();
   1954    cont->Cache();
   1955  }
   1956 
   1957  return true;
   1958 }
   1959 
   1960 void nsAttrValue::SetMiscAtomOrString(const nsAString* aValue) {
   1961  NS_ASSERTION(GetMiscContainer(), "Must have MiscContainer!");
   1962  NS_ASSERTION(!GetMiscContainer()->mStringBits || IsInServoTraversal(),
   1963               "Trying to re-set atom or string!");
   1964  if (aValue) {
   1965    uint32_t len = aValue->Length();
   1966    // * We're allowing eCSSDeclaration attributes to store empty
   1967    //   strings as it can be beneficial to store an empty style
   1968    //   attribute as a parsed rule.
   1969    // * We're allowing enumerated values because sometimes the empty
   1970    //   string corresponds to a particular enumerated value, especially
   1971    //   for enumerated values that are not limited enumerated.
   1972    // Add other types as needed.
   1973    NS_ASSERTION(len || Type() == eCSSDeclaration || Type() == eEnum,
   1974                 "Empty string?");
   1975    MiscContainer* cont = GetMiscContainer();
   1976 
   1977    if (len <= NS_ATTRVALUE_MAX_STRINGLENGTH_ATOM) {
   1978      nsAtom* atom = MOZ_LIKELY(!IsInServoTraversal())
   1979                         ? NS_AtomizeMainThread(*aValue).take()
   1980                         : NS_Atomize(*aValue).take();
   1981      NS_ENSURE_TRUE_VOID(atom);
   1982      uintptr_t bits = reinterpret_cast<uintptr_t>(atom) | eAtomBase;
   1983 
   1984      // In the common case we're not in the servo traversal, and we can just
   1985      // set the bits normally. The parallel case requires more care.
   1986      if (MOZ_LIKELY(!IsInServoTraversal())) {
   1987        cont->SetStringBitsMainThread(bits);
   1988      } else if (!cont->mStringBits.compareExchange(0, bits)) {
   1989        // We raced with somebody else setting the bits. Release our copy.
   1990        atom->Release();
   1991      }
   1992    } else {
   1993      mozilla::StringBuffer* buffer = GetStringBuffer(*aValue).take();
   1994      NS_ENSURE_TRUE_VOID(buffer);
   1995      uintptr_t bits = reinterpret_cast<uintptr_t>(buffer) | eStringBase;
   1996 
   1997      // In the common case we're not in the servo traversal, and we can just
   1998      // set the bits normally. The parallel case requires more care.
   1999      if (MOZ_LIKELY(!IsInServoTraversal())) {
   2000        cont->SetStringBitsMainThread(bits);
   2001      } else if (!cont->mStringBits.compareExchange(0, bits)) {
   2002        // We raced with somebody else setting the bits. Release our copy.
   2003        buffer->Release();
   2004      }
   2005    }
   2006  }
   2007 }
   2008 
   2009 void nsAttrValue::ResetMiscAtomOrString() {
   2010  MiscContainer* cont = GetMiscContainer();
   2011  bool isString;
   2012  if (void* ptr = cont->GetStringOrAtomPtr(isString)) {
   2013    if (isString) {
   2014      static_cast<mozilla::StringBuffer*>(ptr)->Release();
   2015    } else {
   2016      static_cast<nsAtom*>(ptr)->Release();
   2017    }
   2018    cont->SetStringBitsMainThread(0);
   2019  }
   2020 }
   2021 
   2022 void nsAttrValue::SetSVGType(ValueType aType, const void* aValue,
   2023                             const nsAString* aSerialized) {
   2024  MOZ_ASSERT(IsSVGType(aType), "Not an SVG type");
   2025 
   2026  MiscContainer* cont = EnsureEmptyMiscContainer();
   2027  // All SVG types are just pointers to classes so just setting any of them
   2028  // will do. We'll lose type-safety but the signature of the calling
   2029  // function should ensure we don't get anything unexpected, and once we
   2030  // stick aValue in a union we lose type information anyway.
   2031  cont->mValue.mSVGLength = static_cast<const SVGAnimatedLength*>(aValue);
   2032  cont->mType = aType;
   2033  SetMiscAtomOrString(aSerialized);
   2034 }
   2035 
   2036 MiscContainer* nsAttrValue::ClearMiscContainer() {
   2037  MiscContainer* cont = nullptr;
   2038  if (BaseType() == eOtherBase) {
   2039    cont = GetMiscContainer();
   2040    if (cont->IsRefCounted() && cont->mValue.mRefCount > 1) {
   2041      // This MiscContainer is shared, we need a new one.
   2042      NS_RELEASE(cont);
   2043 
   2044      cont = AllocMiscContainer();
   2045      SetPtrValueAndType(cont, eOtherBase);
   2046    } else {
   2047      switch (cont->mType) {
   2048        case eCSSDeclaration: {
   2049          MOZ_ASSERT(cont->mValue.mRefCount == 1);
   2050          cont->Release();
   2051          cont->Evict();
   2052          NS_RELEASE(cont->mValue.mCSSDeclaration);
   2053          break;
   2054        }
   2055        case eShadowParts: {
   2056          MOZ_ASSERT(cont->mValue.mRefCount == 1);
   2057          cont->Release();
   2058          delete cont->mValue.mShadowParts;
   2059          break;
   2060        }
   2061        case eURL: {
   2062          NS_RELEASE(cont->mValue.mURL);
   2063          break;
   2064        }
   2065        case eAtomArray: {
   2066          MOZ_ASSERT(cont->mValue.mRefCount == 1);
   2067          cont->Release();
   2068          cont->Evict();
   2069          delete cont->mValue.mAtomArray;
   2070          break;
   2071        }
   2072        default: {
   2073          break;
   2074        }
   2075      }
   2076    }
   2077    ResetMiscAtomOrString();
   2078  } else {
   2079    ResetIfSet();
   2080  }
   2081 
   2082  return cont;
   2083 }
   2084 
   2085 MiscContainer* nsAttrValue::EnsureEmptyMiscContainer() {
   2086  MiscContainer* cont = ClearMiscContainer();
   2087  if (cont) {
   2088    MOZ_ASSERT(BaseType() == eOtherBase);
   2089    ResetMiscAtomOrString();
   2090    cont = GetMiscContainer();
   2091  } else {
   2092    cont = AllocMiscContainer();
   2093    SetPtrValueAndType(cont, eOtherBase);
   2094  }
   2095 
   2096  return cont;
   2097 }
   2098 
   2099 already_AddRefed<mozilla::StringBuffer> nsAttrValue::GetStringBuffer(
   2100    const nsAString& aValue) const {
   2101  uint32_t len = aValue.Length();
   2102  if (!len) {
   2103    return nullptr;
   2104  }
   2105  if (mozilla::StringBuffer* buf = aValue.GetStringBuffer();
   2106      buf && (buf->StorageSize() / sizeof(char16_t) - 1) == len) {
   2107    // We can only reuse the buffer if it's exactly sized, since we rely on
   2108    // StorageSize() to get the string length in ToString().
   2109    return do_AddRef(buf);
   2110  }
   2111  return mozilla::StringBuffer::Create(aValue.Data(), aValue.Length());
   2112 }
   2113 
   2114 size_t nsAttrValue::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const {
   2115  size_t n = 0;
   2116 
   2117  switch (BaseType()) {
   2118    case eStringBase: {
   2119      mozilla::StringBuffer* str =
   2120          static_cast<mozilla::StringBuffer*>(GetPtr());
   2121      n += str ? str->SizeOfIncludingThisIfUnshared(aMallocSizeOf) : 0;
   2122      break;
   2123    }
   2124    case eOtherBase: {
   2125      MiscContainer* container = GetMiscContainer();
   2126      if (!container) {
   2127        break;
   2128      }
   2129      if (container->IsRefCounted() && container->mValue.mRefCount > 1) {
   2130        // We don't report this MiscContainer at all in order to avoid
   2131        // twice-reporting it.
   2132        // TODO DMD, bug 1027551 - figure out how to report this ref-counted
   2133        // object just once.
   2134        break;
   2135      }
   2136      n += aMallocSizeOf(container);
   2137 
   2138      // We only count the size of the object pointed by otherPtr if it's a
   2139      // string. When it's an atom, it's counted separately.
   2140      if (mozilla::StringBuffer* buf = container->GetStoredStringBuffer()) {
   2141        n += buf->SizeOfIncludingThisIfUnshared(aMallocSizeOf);
   2142      }
   2143 
   2144      if (Type() == eCSSDeclaration && container->mValue.mCSSDeclaration) {
   2145        // TODO: mCSSDeclaration might be owned by another object which
   2146        //       would make us count them twice, bug 677493.
   2147        // Bug 1281964: For DeclarationBlock if we do measure we'll
   2148        // need a way to call the Servo heap_size_of function.
   2149        // n += container->mCSSDeclaration->SizeOfIncludingThis(aMallocSizeOf);
   2150      } else if (Type() == eAtomArray && container->mValue.mAtomArray) {
   2151        // Don't measure each nsAtom, because they are measured separately.
   2152        n += container->mValue.mAtomArray->ShallowSizeOfIncludingThis(
   2153            aMallocSizeOf);
   2154      }
   2155      break;
   2156    }
   2157    case eAtomBase:     // Atoms are counted separately.
   2158    case eIntegerBase:  // The value is in mBits, nothing to do.
   2159      break;
   2160  }
   2161 
   2162  return n;
   2163 }