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