Bug 1890277: part 2) Add `require-trusted-types-for` directive to CSP parser, guarded...
[gecko.git] / dom / base / nsAttrValue.cpp
blob01efef2eb9d147f1206a2e6da762b95be1ff959b
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/. */
7 /*
8 * A struct that represents the value (type and actual data) of an
9 * attribute.
12 #include "mozilla/ArrayUtils.h"
13 #include "mozilla/DebugOnly.h"
14 #include "mozilla/HashFunctions.h"
16 #include "nsAttrValue.h"
17 #include "nsAttrValueInlines.h"
18 #include "nsUnicharUtils.h"
19 #include "mozilla/AttributeStyles.h"
20 #include "mozilla/ClearOnShutdown.h"
21 #include "mozilla/BloomFilter.h"
22 #include "mozilla/DeclarationBlock.h"
23 #include "mozilla/MemoryReporting.h"
24 #include "mozilla/ServoBindingTypes.h"
25 #include "mozilla/ServoUtils.h"
26 #include "mozilla/ShadowParts.h"
27 #include "mozilla/SVGAttrValueWrapper.h"
28 #include "mozilla/URLExtraData.h"
29 #include "mozilla/dom/Document.h"
30 #include "nsContentUtils.h"
31 #include "nsReadableUtils.h"
32 #include "nsStyledElement.h"
33 #include "nsIURI.h"
34 #include "ReferrerInfo.h"
35 #include <algorithm>
37 using namespace mozilla;
39 constexpr uint32_t kMiscContainerCacheSize = 128;
40 static void* gMiscContainerCache[kMiscContainerCacheSize];
41 static uint32_t gMiscContainerCount = 0;
43 /**
44 * Global cache for eAtomArray MiscContainer objects, to speed up the parsing
45 * of class attributes with multiple class names.
46 * This cache doesn't keep anything alive - a MiscContainer removes itself from
47 * the cache once its last reference is dropped.
49 struct AtomArrayCache {
50 // We don't keep any strong references, neither to the atom nor to the
51 // MiscContainer. The MiscContainer removes itself from the cache when
52 // the last reference to it is dropped, and the atom is kept alive by
53 // the MiscContainer.
54 using MapType = nsTHashMap<nsAtom*, MiscContainer*>;
56 static MiscContainer* Lookup(nsAtom* aValue) {
57 if (auto* instance = GetInstance()) {
58 return instance->LookupImpl(aValue);
60 return nullptr;
63 static void Insert(nsAtom* aValue, MiscContainer* aCont) {
64 if (auto* instance = GetInstance()) {
65 instance->InsertImpl(aValue, aCont);
69 static void Remove(nsAtom* aValue) {
70 if (auto* instance = GetInstance()) {
71 instance->RemoveImpl(aValue);
75 static AtomArrayCache* GetInstance() {
76 static StaticAutoPtr<AtomArrayCache> sInstance;
77 if (!sInstance && !PastShutdownPhase(ShutdownPhase::XPCOMShutdownFinal)) {
78 sInstance = new AtomArrayCache();
79 ClearOnShutdown(&sInstance, ShutdownPhase::XPCOMShutdownFinal);
81 return sInstance;
84 private:
85 MiscContainer* LookupImpl(nsAtom* aValue) {
86 auto lookupResult = mMap.Lookup(aValue);
87 return lookupResult ? *lookupResult : nullptr;
90 void InsertImpl(nsAtom* aValue, MiscContainer* aCont) {
91 MOZ_ASSERT(aCont);
92 mMap.InsertOrUpdate(aValue, aCont);
95 void RemoveImpl(nsAtom* aValue) { mMap.Remove(aValue); }
97 MapType mMap;
100 /* static */
101 MiscContainer* nsAttrValue::AllocMiscContainer() {
102 MOZ_ASSERT(NS_IsMainThread());
104 static_assert(sizeof(gMiscContainerCache) <= 1024);
105 static_assert(sizeof(MiscContainer) <= 32);
107 // Allocate MiscContainer objects in batches to improve performance.
108 if (gMiscContainerCount == 0) {
109 for (; gMiscContainerCount < kMiscContainerCacheSize;
110 ++gMiscContainerCount) {
111 gMiscContainerCache[gMiscContainerCount] =
112 moz_xmalloc(sizeof(MiscContainer));
116 return new (gMiscContainerCache[--gMiscContainerCount]) MiscContainer();
119 /* static */
120 void nsAttrValue::DeallocMiscContainer(MiscContainer* aCont) {
121 MOZ_ASSERT(NS_IsMainThread());
122 if (!aCont) {
123 return;
126 aCont->~MiscContainer();
128 if (gMiscContainerCount < kMiscContainerCacheSize) {
129 gMiscContainerCache[gMiscContainerCount++] = aCont;
130 return;
133 free(aCont);
136 bool MiscContainer::GetString(nsAString& aString) const {
137 bool isString;
138 void* ptr = GetStringOrAtomPtr(isString);
139 if (!ptr) {
140 return false;
142 if (isString) {
143 auto* buffer = static_cast<nsStringBuffer*>(ptr);
144 buffer->ToString(buffer->StorageSize() / sizeof(char16_t) - 1, aString);
145 } else {
146 static_cast<nsAtom*>(ptr)->ToString(aString);
148 return true;
151 void MiscContainer::Cache() {
152 switch (mType) {
153 case nsAttrValue::eCSSDeclaration: {
154 MOZ_ASSERT(IsRefCounted());
155 MOZ_ASSERT(mValue.mRefCount > 0);
156 MOZ_ASSERT(!mValue.mCached);
158 AttributeStyles* attrStyles =
159 mValue.mCSSDeclaration->GetAttributeStyles();
160 if (!attrStyles) {
161 return;
164 nsString str;
165 bool gotString = GetString(str);
166 if (!gotString) {
167 return;
170 attrStyles->CacheStyleAttr(str, this);
171 mValue.mCached = 1;
173 // This has to be immutable once it goes into the cache.
174 mValue.mCSSDeclaration->SetImmutable();
175 break;
177 case nsAttrValue::eAtomArray: {
178 MOZ_ASSERT(IsRefCounted());
179 MOZ_ASSERT(mValue.mRefCount > 0);
180 MOZ_ASSERT(!mValue.mCached);
182 nsAtom* atom = GetStoredAtom();
183 if (!atom) {
184 return;
187 AtomArrayCache::Insert(atom, this);
188 mValue.mCached = 1;
189 break;
191 default:
192 MOZ_ASSERT_UNREACHABLE("unexpected cached nsAttrValue type");
193 break;
197 void MiscContainer::Evict() {
198 switch (mType) {
199 case nsAttrValue::eCSSDeclaration: {
200 MOZ_ASSERT(IsRefCounted());
201 MOZ_ASSERT(mValue.mRefCount == 0);
203 if (!mValue.mCached) {
204 return;
207 AttributeStyles* attrStyles =
208 mValue.mCSSDeclaration->GetAttributeStyles();
209 MOZ_ASSERT(attrStyles);
211 nsString str;
212 DebugOnly<bool> gotString = GetString(str);
213 MOZ_ASSERT(gotString);
215 attrStyles->EvictStyleAttr(str, this);
216 mValue.mCached = 0;
217 break;
219 case nsAttrValue::eAtomArray: {
220 MOZ_ASSERT(IsRefCounted());
221 MOZ_ASSERT(mValue.mRefCount == 0);
223 if (!mValue.mCached) {
224 return;
227 nsAtom* atom = GetStoredAtom();
228 MOZ_ASSERT(atom);
230 AtomArrayCache::Remove(atom);
232 mValue.mCached = 0;
233 break;
235 default:
237 MOZ_ASSERT_UNREACHABLE("unexpected cached nsAttrValue type");
238 break;
242 nsTArray<const nsAttrValue::EnumTable*>* nsAttrValue::sEnumTableArray = nullptr;
244 nsAttrValue::nsAttrValue() : mBits(0) {}
246 nsAttrValue::nsAttrValue(const nsAttrValue& aOther) : mBits(0) {
247 SetTo(aOther);
250 nsAttrValue::nsAttrValue(const nsAString& aValue) : mBits(0) { SetTo(aValue); }
252 nsAttrValue::nsAttrValue(nsAtom* aValue) : mBits(0) { SetTo(aValue); }
254 nsAttrValue::nsAttrValue(already_AddRefed<DeclarationBlock> aValue,
255 const nsAString* aSerialized)
256 : mBits(0) {
257 SetTo(std::move(aValue), aSerialized);
260 nsAttrValue::~nsAttrValue() { ResetIfSet(); }
262 /* static */
263 void nsAttrValue::Init() {
264 MOZ_ASSERT(!sEnumTableArray, "nsAttrValue already initialized");
265 sEnumTableArray = new nsTArray<const EnumTable*>;
268 /* static */
269 void nsAttrValue::Shutdown() {
270 MOZ_ASSERT(NS_IsMainThread());
271 delete sEnumTableArray;
272 sEnumTableArray = nullptr;
274 for (uint32_t i = 0; i < gMiscContainerCount; ++i) {
275 free(gMiscContainerCache[i]);
277 gMiscContainerCount = 0;
280 void nsAttrValue::Reset() {
281 switch (BaseType()) {
282 case eStringBase: {
283 nsStringBuffer* str = static_cast<nsStringBuffer*>(GetPtr());
284 if (str) {
285 str->Release();
288 break;
290 case eOtherBase: {
291 MiscContainer* cont = GetMiscContainer();
292 if (cont->IsRefCounted() && cont->mValue.mRefCount > 1) {
293 NS_RELEASE(cont);
294 break;
297 DeallocMiscContainer(ClearMiscContainer());
299 break;
301 case eAtomBase: {
302 nsAtom* atom = GetAtomValue();
303 NS_RELEASE(atom);
305 break;
307 case eIntegerBase: {
308 break;
312 mBits = 0;
315 void nsAttrValue::SetTo(const nsAttrValue& aOther) {
316 if (this == &aOther) {
317 return;
320 switch (aOther.BaseType()) {
321 case eStringBase: {
322 ResetIfSet();
323 nsStringBuffer* str = static_cast<nsStringBuffer*>(aOther.GetPtr());
324 if (str) {
325 str->AddRef();
326 SetPtrValueAndType(str, eStringBase);
328 return;
330 case eOtherBase: {
331 break;
333 case eAtomBase: {
334 ResetIfSet();
335 nsAtom* atom = aOther.GetAtomValue();
336 NS_ADDREF(atom);
337 SetPtrValueAndType(atom, eAtomBase);
338 return;
340 case eIntegerBase: {
341 ResetIfSet();
342 mBits = aOther.mBits;
343 return;
347 MiscContainer* otherCont = aOther.GetMiscContainer();
348 if (otherCont->IsRefCounted()) {
349 DeallocMiscContainer(ClearMiscContainer());
350 NS_ADDREF(otherCont);
351 SetPtrValueAndType(otherCont, eOtherBase);
352 return;
355 MiscContainer* cont = EnsureEmptyMiscContainer();
356 switch (otherCont->mType) {
357 case eInteger: {
358 cont->mValue.mInteger = otherCont->mValue.mInteger;
359 break;
361 case eEnum: {
362 cont->mValue.mEnumValue = otherCont->mValue.mEnumValue;
363 break;
365 case ePercent: {
366 cont->mDoubleValue = otherCont->mDoubleValue;
367 break;
369 case eColor: {
370 cont->mValue.mColor = otherCont->mValue.mColor;
371 break;
373 case eAtomArray:
374 case eShadowParts:
375 case eCSSDeclaration: {
376 MOZ_CRASH("These should be refcounted!");
378 case eURL: {
379 NS_ADDREF(cont->mValue.mURL = otherCont->mValue.mURL);
380 break;
382 case eDoubleValue: {
383 cont->mDoubleValue = otherCont->mDoubleValue;
384 break;
386 default: {
387 if (IsSVGType(otherCont->mType)) {
388 // All SVG types are just pointers to classes and will therefore have
389 // the same size so it doesn't really matter which one we assign
390 cont->mValue.mSVGLength = otherCont->mValue.mSVGLength;
391 } else {
392 MOZ_ASSERT_UNREACHABLE("unknown type stored in MiscContainer");
394 break;
398 bool isString;
399 if (void* otherPtr = otherCont->GetStringOrAtomPtr(isString)) {
400 if (isString) {
401 static_cast<nsStringBuffer*>(otherPtr)->AddRef();
402 } else {
403 static_cast<nsAtom*>(otherPtr)->AddRef();
405 cont->SetStringBitsMainThread(otherCont->mStringBits);
407 // Note, set mType after switch-case, otherwise EnsureEmptyAtomArray doesn't
408 // work correctly.
409 cont->mType = otherCont->mType;
412 void nsAttrValue::SetTo(const nsAString& aValue) {
413 ResetIfSet();
414 nsStringBuffer* buf = GetStringBuffer(aValue).take();
415 if (buf) {
416 SetPtrValueAndType(buf, eStringBase);
420 void nsAttrValue::SetTo(nsAtom* aValue) {
421 ResetIfSet();
422 if (aValue) {
423 NS_ADDREF(aValue);
424 SetPtrValueAndType(aValue, eAtomBase);
428 void nsAttrValue::SetTo(int16_t aInt) {
429 ResetIfSet();
430 SetIntValueAndType(aInt, eInteger, nullptr);
433 void nsAttrValue::SetTo(int32_t aInt, const nsAString* aSerialized) {
434 ResetIfSet();
435 SetIntValueAndType(aInt, eInteger, aSerialized);
438 void nsAttrValue::SetTo(double aValue, const nsAString* aSerialized) {
439 MiscContainer* cont = EnsureEmptyMiscContainer();
440 cont->mDoubleValue = aValue;
441 cont->mType = eDoubleValue;
442 SetMiscAtomOrString(aSerialized);
445 void nsAttrValue::SetTo(already_AddRefed<DeclarationBlock> aValue,
446 const nsAString* aSerialized) {
447 MiscContainer* cont = EnsureEmptyMiscContainer();
448 MOZ_ASSERT(cont->mValue.mRefCount == 0);
449 cont->mValue.mCSSDeclaration = aValue.take();
450 cont->mType = eCSSDeclaration;
451 NS_ADDREF(cont);
452 SetMiscAtomOrString(aSerialized);
453 MOZ_ASSERT(cont->mValue.mRefCount == 1);
456 void nsAttrValue::SetTo(nsIURI* aValue, const nsAString* aSerialized) {
457 MiscContainer* cont = EnsureEmptyMiscContainer();
458 NS_ADDREF(cont->mValue.mURL = aValue);
459 cont->mType = eURL;
460 SetMiscAtomOrString(aSerialized);
463 void nsAttrValue::SetToSerialized(const nsAttrValue& aOther) {
464 if (aOther.Type() != nsAttrValue::eString &&
465 aOther.Type() != nsAttrValue::eAtom) {
466 nsAutoString val;
467 aOther.ToString(val);
468 SetTo(val);
469 } else {
470 SetTo(aOther);
474 void nsAttrValue::SetTo(const SVGAnimatedOrient& aValue,
475 const nsAString* aSerialized) {
476 SetSVGType(eSVGOrient, &aValue, aSerialized);
479 void nsAttrValue::SetTo(const SVGAnimatedIntegerPair& aValue,
480 const nsAString* aSerialized) {
481 SetSVGType(eSVGIntegerPair, &aValue, aSerialized);
484 void nsAttrValue::SetTo(const SVGAnimatedLength& aValue,
485 const nsAString* aSerialized) {
486 SetSVGType(eSVGLength, &aValue, aSerialized);
489 void nsAttrValue::SetTo(const SVGLengthList& aValue,
490 const nsAString* aSerialized) {
491 // While an empty string will parse as a length list, there's no need to store
492 // it (and SetMiscAtomOrString will assert if we try)
493 if (aSerialized && aSerialized->IsEmpty()) {
494 aSerialized = nullptr;
496 SetSVGType(eSVGLengthList, &aValue, aSerialized);
499 void nsAttrValue::SetTo(const SVGNumberList& aValue,
500 const nsAString* aSerialized) {
501 // While an empty string will parse as a number list, there's no need to store
502 // it (and SetMiscAtomOrString will assert if we try)
503 if (aSerialized && aSerialized->IsEmpty()) {
504 aSerialized = nullptr;
506 SetSVGType(eSVGNumberList, &aValue, aSerialized);
509 void nsAttrValue::SetTo(const SVGAnimatedNumberPair& aValue,
510 const nsAString* aSerialized) {
511 SetSVGType(eSVGNumberPair, &aValue, aSerialized);
514 void nsAttrValue::SetTo(const SVGPathData& aValue,
515 const nsAString* aSerialized) {
516 // While an empty string will parse as path data, there's no need to store it
517 // (and SetMiscAtomOrString will assert if we try)
518 if (aSerialized && aSerialized->IsEmpty()) {
519 aSerialized = nullptr;
521 SetSVGType(eSVGPathData, &aValue, aSerialized);
524 void nsAttrValue::SetTo(const SVGPointList& aValue,
525 const nsAString* aSerialized) {
526 // While an empty string will parse as a point list, there's no need to store
527 // it (and SetMiscAtomOrString will assert if we try)
528 if (aSerialized && aSerialized->IsEmpty()) {
529 aSerialized = nullptr;
531 SetSVGType(eSVGPointList, &aValue, aSerialized);
534 void nsAttrValue::SetTo(const SVGAnimatedPreserveAspectRatio& aValue,
535 const nsAString* aSerialized) {
536 SetSVGType(eSVGPreserveAspectRatio, &aValue, aSerialized);
539 void nsAttrValue::SetTo(const SVGStringList& aValue,
540 const nsAString* aSerialized) {
541 // While an empty string will parse as a string list, there's no need to store
542 // it (and SetMiscAtomOrString will assert if we try)
543 if (aSerialized && aSerialized->IsEmpty()) {
544 aSerialized = nullptr;
546 SetSVGType(eSVGStringList, &aValue, aSerialized);
549 void nsAttrValue::SetTo(const SVGTransformList& aValue,
550 const nsAString* aSerialized) {
551 // While an empty string will parse as a transform list, there's no need to
552 // store it (and SetMiscAtomOrString will assert if we try)
553 if (aSerialized && aSerialized->IsEmpty()) {
554 aSerialized = nullptr;
556 SetSVGType(eSVGTransformList, &aValue, aSerialized);
559 void nsAttrValue::SetTo(const SVGAnimatedViewBox& aValue,
560 const nsAString* aSerialized) {
561 SetSVGType(eSVGViewBox, &aValue, aSerialized);
564 void nsAttrValue::SwapValueWith(nsAttrValue& aOther) {
565 uintptr_t tmp = aOther.mBits;
566 aOther.mBits = mBits;
567 mBits = tmp;
570 void nsAttrValue::RemoveDuplicatesFromAtomArray() {
571 if (Type() != eAtomArray) {
572 return;
575 const AttrAtomArray* currentAtomArray = GetMiscContainer()->mValue.mAtomArray;
576 UniquePtr<AttrAtomArray> deduplicatedAtomArray =
577 currentAtomArray->CreateDeduplicatedCopyIfDifferent();
579 if (!deduplicatedAtomArray) {
580 // No duplicates found. Leave this value unchanged.
581 return;
584 // We found duplicates. Wrap the new atom array into a fresh MiscContainer,
585 // and copy over the existing container's string or atom.
587 MiscContainer* oldCont = GetMiscContainer();
588 MOZ_ASSERT(oldCont->IsRefCounted());
590 uintptr_t stringBits = 0;
591 bool isString = false;
592 if (void* otherPtr = oldCont->GetStringOrAtomPtr(isString)) {
593 stringBits = oldCont->mStringBits;
594 if (isString) {
595 static_cast<nsStringBuffer*>(otherPtr)->AddRef();
596 } else {
597 static_cast<nsAtom*>(otherPtr)->AddRef();
601 MiscContainer* cont = EnsureEmptyMiscContainer();
602 MOZ_ASSERT(cont->mValue.mRefCount == 0);
603 cont->mValue.mAtomArray = deduplicatedAtomArray.release();
604 cont->mType = eAtomArray;
605 NS_ADDREF(cont);
606 MOZ_ASSERT(cont->mValue.mRefCount == 1);
607 cont->SetStringBitsMainThread(stringBits);
609 // Don't cache the new container. It would stomp over the undeduplicated
610 // value in the cache. But we could have a separate cache for deduplicated
611 // atom arrays, if repeated deduplication shows up in profiles.
614 void nsAttrValue::ToString(nsAString& aResult) const {
615 MiscContainer* cont = nullptr;
616 if (BaseType() == eOtherBase) {
617 cont = GetMiscContainer();
619 if (cont->GetString(aResult)) {
620 return;
624 switch (Type()) {
625 case eString: {
626 nsStringBuffer* str = static_cast<nsStringBuffer*>(GetPtr());
627 if (str) {
628 str->ToString(str->StorageSize() / sizeof(char16_t) - 1, aResult);
629 } else {
630 aResult.Truncate();
632 break;
634 case eAtom: {
635 nsAtom* atom = static_cast<nsAtom*>(GetPtr());
636 atom->ToString(aResult);
638 break;
640 case eInteger: {
641 nsAutoString intStr;
642 intStr.AppendInt(GetIntegerValue());
643 aResult = intStr;
645 break;
647 #ifdef DEBUG
648 case eColor: {
649 MOZ_ASSERT_UNREACHABLE("color attribute without string data");
650 aResult.Truncate();
651 break;
653 #endif
654 case eEnum: {
655 GetEnumString(aResult, false);
656 break;
658 case ePercent: {
659 nsAutoString str;
660 if (cont) {
661 str.AppendFloat(cont->mDoubleValue);
662 } else {
663 str.AppendInt(GetIntInternal());
665 aResult = str + u"%"_ns;
667 break;
669 case eCSSDeclaration: {
670 aResult.Truncate();
671 MiscContainer* container = GetMiscContainer();
672 if (DeclarationBlock* decl = container->mValue.mCSSDeclaration) {
673 nsAutoCString result;
674 decl->ToString(result);
675 CopyUTF8toUTF16(result, aResult);
678 // This can be reached during parallel selector matching with attribute
679 // selectors on the style attribute. SetMiscAtomOrString handles this
680 // case, and as of this writing this is the only consumer that needs it.
681 const_cast<nsAttrValue*>(this)->SetMiscAtomOrString(&aResult);
683 break;
685 case eDoubleValue: {
686 aResult.Truncate();
687 aResult.AppendFloat(GetDoubleValue());
688 break;
690 case eSVGIntegerPair: {
691 SVGAttrValueWrapper::ToString(
692 GetMiscContainer()->mValue.mSVGAnimatedIntegerPair, aResult);
693 break;
695 case eSVGOrient: {
696 SVGAttrValueWrapper::ToString(
697 GetMiscContainer()->mValue.mSVGAnimatedOrient, aResult);
698 break;
700 case eSVGLength: {
701 SVGAttrValueWrapper::ToString(GetMiscContainer()->mValue.mSVGLength,
702 aResult);
703 break;
705 case eSVGLengthList: {
706 SVGAttrValueWrapper::ToString(GetMiscContainer()->mValue.mSVGLengthList,
707 aResult);
708 break;
710 case eSVGNumberList: {
711 SVGAttrValueWrapper::ToString(GetMiscContainer()->mValue.mSVGNumberList,
712 aResult);
713 break;
715 case eSVGNumberPair: {
716 SVGAttrValueWrapper::ToString(
717 GetMiscContainer()->mValue.mSVGAnimatedNumberPair, aResult);
718 break;
720 case eSVGPathData: {
721 SVGAttrValueWrapper::ToString(GetMiscContainer()->mValue.mSVGPathData,
722 aResult);
723 break;
725 case eSVGPointList: {
726 SVGAttrValueWrapper::ToString(GetMiscContainer()->mValue.mSVGPointList,
727 aResult);
728 break;
730 case eSVGPreserveAspectRatio: {
731 SVGAttrValueWrapper::ToString(
732 GetMiscContainer()->mValue.mSVGAnimatedPreserveAspectRatio, aResult);
733 break;
735 case eSVGStringList: {
736 SVGAttrValueWrapper::ToString(GetMiscContainer()->mValue.mSVGStringList,
737 aResult);
738 break;
740 case eSVGTransformList: {
741 SVGAttrValueWrapper::ToString(
742 GetMiscContainer()->mValue.mSVGTransformList, aResult);
743 break;
745 case eSVGViewBox: {
746 SVGAttrValueWrapper::ToString(
747 GetMiscContainer()->mValue.mSVGAnimatedViewBox, aResult);
748 break;
750 default: {
751 aResult.Truncate();
752 break;
757 already_AddRefed<nsAtom> nsAttrValue::GetAsAtom() const {
758 switch (Type()) {
759 case eString:
760 return NS_AtomizeMainThread(GetStringValue());
762 case eAtom: {
763 RefPtr<nsAtom> atom = GetAtomValue();
764 return atom.forget();
767 default: {
768 nsAutoString val;
769 ToString(val);
770 return NS_AtomizeMainThread(val);
775 const nsCheapString nsAttrValue::GetStringValue() const {
776 MOZ_ASSERT(Type() == eString, "wrong type");
778 return nsCheapString(static_cast<nsStringBuffer*>(GetPtr()));
781 bool nsAttrValue::GetColorValue(nscolor& aColor) const {
782 if (Type() != eColor) {
783 // Unparseable value, treat as unset.
784 NS_ASSERTION(Type() == eString, "unexpected type for color-valued attr");
785 return false;
788 aColor = GetMiscContainer()->mValue.mColor;
789 return true;
792 void nsAttrValue::GetEnumString(nsAString& aResult, bool aRealTag) const {
793 MOZ_ASSERT(Type() == eEnum, "wrong type");
795 uint32_t allEnumBits = (BaseType() == eIntegerBase)
796 ? static_cast<uint32_t>(GetIntInternal())
797 : GetMiscContainer()->mValue.mEnumValue;
798 int16_t val = allEnumBits >> NS_ATTRVALUE_ENUMTABLEINDEX_BITS;
799 const EnumTable* table = sEnumTableArray->ElementAt(
800 allEnumBits & NS_ATTRVALUE_ENUMTABLEINDEX_MASK);
802 while (table->tag) {
803 if (table->value == val) {
804 aResult.AssignASCII(table->tag);
805 if (!aRealTag &&
806 allEnumBits & NS_ATTRVALUE_ENUMTABLE_VALUE_NEEDS_TO_UPPER) {
807 nsContentUtils::ASCIIToUpper(aResult);
809 return;
811 table++;
814 MOZ_ASSERT_UNREACHABLE("couldn't find value in EnumTable");
817 UniquePtr<AttrAtomArray> AttrAtomArray::CreateDeduplicatedCopyIfDifferentImpl()
818 const {
819 MOZ_ASSERT(mMayContainDuplicates);
821 bool usingHashTable = false;
822 BitBloomFilter<8, nsAtom> filter;
823 nsTHashSet<nsAtom*> hash;
825 auto CheckDuplicate = [&](size_t i) {
826 nsAtom* atom = mArray[i];
827 if (!usingHashTable) {
828 if (!filter.mightContain(atom)) {
829 filter.add(atom);
830 return false;
832 for (size_t j = 0; j < i; ++j) {
833 hash.Insert(mArray[j]);
835 usingHashTable = true;
837 return !hash.EnsureInserted(atom);
840 size_t len = mArray.Length();
841 UniquePtr<AttrAtomArray> deduplicatedArray;
842 for (size_t i = 0; i < len; ++i) {
843 if (!CheckDuplicate(i)) {
844 if (deduplicatedArray) {
845 deduplicatedArray->mArray.AppendElement(mArray[i]);
847 continue;
849 // We've found a duplicate!
850 if (!deduplicatedArray) {
851 // Allocate the deduplicated copy and copy the preceding elements into it.
852 deduplicatedArray = MakeUnique<AttrAtomArray>();
853 deduplicatedArray->mMayContainDuplicates = false;
854 deduplicatedArray->mArray.SetCapacity(len - 1);
855 for (size_t indexToCopy = 0; indexToCopy < i; indexToCopy++) {
856 deduplicatedArray->mArray.AppendElement(mArray[indexToCopy]);
861 if (!deduplicatedArray) {
862 // This AttrAtomArray doesn't contain any duplicates, cache this information
863 // for future invocations.
864 mMayContainDuplicates = false;
866 return deduplicatedArray;
869 uint32_t nsAttrValue::GetAtomCount() const {
870 ValueType type = Type();
872 if (type == eAtom) {
873 return 1;
876 if (type == eAtomArray) {
877 return GetAtomArrayValue()->mArray.Length();
880 return 0;
883 nsAtom* nsAttrValue::AtomAt(int32_t aIndex) const {
884 MOZ_ASSERT(aIndex >= 0, "Index must not be negative");
885 MOZ_ASSERT(GetAtomCount() > uint32_t(aIndex), "aIndex out of range");
887 if (BaseType() == eAtomBase) {
888 return GetAtomValue();
891 NS_ASSERTION(Type() == eAtomArray, "GetAtomCount must be confused");
892 return GetAtomArrayValue()->mArray.ElementAt(aIndex);
895 uint32_t nsAttrValue::HashValue() const {
896 switch (BaseType()) {
897 case eStringBase: {
898 nsStringBuffer* str = static_cast<nsStringBuffer*>(GetPtr());
899 if (str) {
900 uint32_t len = str->StorageSize() / sizeof(char16_t) - 1;
901 return HashString(static_cast<char16_t*>(str->Data()), len);
904 return 0;
906 case eOtherBase: {
907 break;
909 case eAtomBase:
910 case eIntegerBase: {
911 // mBits and uint32_t might have different size. This should silence
912 // any warnings or compile-errors. This is what the implementation of
913 // NS_PTR_TO_INT32 does to take care of the same problem.
914 return mBits - 0;
918 MiscContainer* cont = GetMiscContainer();
919 if (static_cast<ValueBaseType>(cont->mStringBits &
920 NS_ATTRVALUE_BASETYPE_MASK) == eAtomBase) {
921 return cont->mStringBits - 0;
924 switch (cont->mType) {
925 case eInteger: {
926 return cont->mValue.mInteger;
928 case eEnum: {
929 return cont->mValue.mEnumValue;
931 case ePercent: {
932 return cont->mDoubleValue;
934 case eColor: {
935 return cont->mValue.mColor;
937 case eCSSDeclaration: {
938 return NS_PTR_TO_INT32(cont->mValue.mCSSDeclaration);
940 case eURL: {
941 nsString str;
942 ToString(str);
943 return HashString(str);
945 case eAtomArray: {
946 uint32_t hash = 0;
947 for (const auto& atom : cont->mValue.mAtomArray->mArray) {
948 hash = AddToHash(hash, atom.get());
950 return hash;
952 case eDoubleValue: {
953 // XXX this is crappy, but oh well
954 return cont->mDoubleValue;
956 default: {
957 if (IsSVGType(cont->mType)) {
958 // All SVG types are just pointers to classes so we can treat them alike
959 return NS_PTR_TO_INT32(cont->mValue.mSVGLength);
961 MOZ_ASSERT_UNREACHABLE("unknown type stored in MiscContainer");
962 return 0;
967 bool nsAttrValue::Equals(const nsAttrValue& aOther) const {
968 if (BaseType() != aOther.BaseType()) {
969 return false;
972 switch (BaseType()) {
973 case eStringBase: {
974 return GetStringValue().Equals(aOther.GetStringValue());
976 case eOtherBase: {
977 break;
979 case eAtomBase:
980 case eIntegerBase: {
981 return mBits == aOther.mBits;
985 MiscContainer* thisCont = GetMiscContainer();
986 MiscContainer* otherCont = aOther.GetMiscContainer();
987 if (thisCont == otherCont) {
988 return true;
991 if (thisCont->mType != otherCont->mType) {
992 return false;
995 bool needsStringComparison = false;
997 switch (thisCont->mType) {
998 case eInteger: {
999 if (thisCont->mValue.mInteger == otherCont->mValue.mInteger) {
1000 needsStringComparison = true;
1002 break;
1004 case eEnum: {
1005 if (thisCont->mValue.mEnumValue == otherCont->mValue.mEnumValue) {
1006 needsStringComparison = true;
1008 break;
1010 case ePercent: {
1011 if (thisCont->mDoubleValue == otherCont->mDoubleValue) {
1012 needsStringComparison = true;
1014 break;
1016 case eColor: {
1017 if (thisCont->mValue.mColor == otherCont->mValue.mColor) {
1018 needsStringComparison = true;
1020 break;
1022 case eCSSDeclaration: {
1023 return thisCont->mValue.mCSSDeclaration ==
1024 otherCont->mValue.mCSSDeclaration;
1026 case eURL: {
1027 return thisCont->mValue.mURL == otherCont->mValue.mURL;
1029 case eAtomArray: {
1030 // For classlists we could be insensitive to order, however
1031 // classlists are never mapped attributes so they are never compared.
1033 if (!(*thisCont->mValue.mAtomArray == *otherCont->mValue.mAtomArray)) {
1034 return false;
1037 needsStringComparison = true;
1038 break;
1040 case eDoubleValue: {
1041 return thisCont->mDoubleValue == otherCont->mDoubleValue;
1043 default: {
1044 if (IsSVGType(thisCont->mType)) {
1045 // Currently this method is never called for nsAttrValue objects that
1046 // point to SVG data types.
1047 // If that changes then we probably want to add methods to the
1048 // corresponding SVG types to compare their base values.
1049 // As a shortcut, however, we can begin by comparing the pointers.
1050 MOZ_ASSERT(false, "Comparing nsAttrValues that point to SVG data");
1051 return false;
1053 MOZ_ASSERT_UNREACHABLE("unknown type stored in MiscContainer");
1054 return false;
1057 if (needsStringComparison) {
1058 if (thisCont->mStringBits == otherCont->mStringBits) {
1059 return true;
1061 if ((static_cast<ValueBaseType>(thisCont->mStringBits &
1062 NS_ATTRVALUE_BASETYPE_MASK) ==
1063 eStringBase) &&
1064 (static_cast<ValueBaseType>(otherCont->mStringBits &
1065 NS_ATTRVALUE_BASETYPE_MASK) ==
1066 eStringBase)) {
1067 return nsCheapString(reinterpret_cast<nsStringBuffer*>(
1068 static_cast<uintptr_t>(thisCont->mStringBits)))
1069 .Equals(nsCheapString(reinterpret_cast<nsStringBuffer*>(
1070 static_cast<uintptr_t>(otherCont->mStringBits))));
1073 return false;
1076 bool nsAttrValue::Equals(const nsAString& aValue,
1077 nsCaseTreatment aCaseSensitive) const {
1078 switch (BaseType()) {
1079 case eStringBase: {
1080 if (auto* str = static_cast<nsStringBuffer*>(GetPtr())) {
1081 nsDependentString dep(static_cast<char16_t*>(str->Data()),
1082 str->StorageSize() / sizeof(char16_t) - 1);
1083 return aCaseSensitive == eCaseMatters
1084 ? aValue.Equals(dep)
1085 : nsContentUtils::EqualsIgnoreASCIICase(aValue, dep);
1087 return aValue.IsEmpty();
1089 case eAtomBase: {
1090 auto* atom = static_cast<nsAtom*>(GetPtr());
1091 if (aCaseSensitive == eCaseMatters) {
1092 return atom->Equals(aValue);
1094 return nsContentUtils::EqualsIgnoreASCIICase(nsDependentAtomString(atom),
1095 aValue);
1097 default:
1098 break;
1101 nsAutoString val;
1102 ToString(val);
1103 return aCaseSensitive == eCaseMatters
1104 ? val.Equals(aValue)
1105 : nsContentUtils::EqualsIgnoreASCIICase(val, aValue);
1108 bool nsAttrValue::Equals(const nsAtom* aValue,
1109 nsCaseTreatment aCaseSensitive) const {
1110 switch (BaseType()) {
1111 case eAtomBase: {
1112 auto* atom = static_cast<nsAtom*>(GetPtr());
1113 if (atom == aValue) {
1114 return true;
1116 if (aCaseSensitive == eCaseMatters) {
1117 return false;
1119 if (atom->IsAsciiLowercase() && aValue->IsAsciiLowercase()) {
1120 return false;
1122 return nsContentUtils::EqualsIgnoreASCIICase(
1123 nsDependentAtomString(atom), nsDependentAtomString(aValue));
1125 case eStringBase: {
1126 if (auto* str = static_cast<nsStringBuffer*>(GetPtr())) {
1127 size_t strLen = str->StorageSize() / sizeof(char16_t) - 1;
1128 if (aValue->GetLength() != strLen) {
1129 return false;
1131 const char16_t* strData = static_cast<char16_t*>(str->Data());
1132 const char16_t* valData = aValue->GetUTF16String();
1133 if (aCaseSensitive == eCaseMatters) {
1134 // Avoid string construction / destruction for the easy case.
1135 return ArrayEqual(strData, valData, strLen);
1137 nsDependentSubstring depStr(strData, strLen);
1138 nsDependentSubstring depVal(valData, strLen);
1139 return nsContentUtils::EqualsIgnoreASCIICase(depStr, depVal);
1141 return aValue->IsEmpty();
1143 default:
1144 break;
1147 nsAutoString val;
1148 ToString(val);
1149 nsDependentAtomString dep(aValue);
1150 return aCaseSensitive == eCaseMatters
1151 ? val.Equals(dep)
1152 : nsContentUtils::EqualsIgnoreASCIICase(val, dep);
1155 struct HasPrefixFn {
1156 static bool Check(const char16_t* aAttrValue, size_t aAttrLen,
1157 const nsAString& aSearchValue,
1158 nsCaseTreatment aCaseSensitive) {
1159 if (aCaseSensitive == eCaseMatters) {
1160 if (aSearchValue.Length() > aAttrLen) {
1161 return false;
1163 return !memcmp(aAttrValue, aSearchValue.BeginReading(),
1164 aSearchValue.Length() * sizeof(char16_t));
1166 return StringBeginsWith(nsDependentString(aAttrValue, aAttrLen),
1167 aSearchValue,
1168 nsASCIICaseInsensitiveStringComparator);
1172 struct HasSuffixFn {
1173 static bool Check(const char16_t* aAttrValue, size_t aAttrLen,
1174 const nsAString& aSearchValue,
1175 nsCaseTreatment aCaseSensitive) {
1176 if (aCaseSensitive == eCaseMatters) {
1177 if (aSearchValue.Length() > aAttrLen) {
1178 return false;
1180 return !memcmp(aAttrValue + aAttrLen - aSearchValue.Length(),
1181 aSearchValue.BeginReading(),
1182 aSearchValue.Length() * sizeof(char16_t));
1184 return StringEndsWith(nsDependentString(aAttrValue, aAttrLen), aSearchValue,
1185 nsASCIICaseInsensitiveStringComparator);
1189 struct HasSubstringFn {
1190 static bool Check(const char16_t* aAttrValue, size_t aAttrLen,
1191 const nsAString& aSearchValue,
1192 nsCaseTreatment aCaseSensitive) {
1193 if (aCaseSensitive == eCaseMatters) {
1194 if (aSearchValue.IsEmpty()) {
1195 return true;
1197 const char16_t* end = aAttrValue + aAttrLen;
1198 return std::search(aAttrValue, end, aSearchValue.BeginReading(),
1199 aSearchValue.EndReading()) != end;
1201 return FindInReadable(aSearchValue, nsDependentString(aAttrValue, aAttrLen),
1202 nsASCIICaseInsensitiveStringComparator);
1206 template <typename F>
1207 bool nsAttrValue::SubstringCheck(const nsAString& aValue,
1208 nsCaseTreatment aCaseSensitive) const {
1209 switch (BaseType()) {
1210 case eStringBase: {
1211 auto str = static_cast<nsStringBuffer*>(GetPtr());
1212 if (str) {
1213 return F::Check(static_cast<char16_t*>(str->Data()),
1214 str->StorageSize() / sizeof(char16_t) - 1, aValue,
1215 aCaseSensitive);
1217 return aValue.IsEmpty();
1219 case eAtomBase: {
1220 auto atom = static_cast<nsAtom*>(GetPtr());
1221 return F::Check(atom->GetUTF16String(), atom->GetLength(), aValue,
1222 aCaseSensitive);
1224 default:
1225 break;
1228 nsAutoString val;
1229 ToString(val);
1230 return F::Check(val.BeginReading(), val.Length(), aValue, aCaseSensitive);
1233 bool nsAttrValue::HasPrefix(const nsAString& aValue,
1234 nsCaseTreatment aCaseSensitive) const {
1235 return SubstringCheck<HasPrefixFn>(aValue, aCaseSensitive);
1238 bool nsAttrValue::HasSuffix(const nsAString& aValue,
1239 nsCaseTreatment aCaseSensitive) const {
1240 return SubstringCheck<HasSuffixFn>(aValue, aCaseSensitive);
1243 bool nsAttrValue::HasSubstring(const nsAString& aValue,
1244 nsCaseTreatment aCaseSensitive) const {
1245 return SubstringCheck<HasSubstringFn>(aValue, aCaseSensitive);
1248 bool nsAttrValue::EqualsAsStrings(const nsAttrValue& aOther) const {
1249 if (Type() == aOther.Type()) {
1250 return Equals(aOther);
1253 // We need to serialize at least one nsAttrValue before passing to
1254 // Equals(const nsAString&), but we can avoid unnecessarily serializing both
1255 // by checking if one is already of a string type.
1256 bool thisIsString = (BaseType() == eStringBase || BaseType() == eAtomBase);
1257 const nsAttrValue& lhs = thisIsString ? *this : aOther;
1258 const nsAttrValue& rhs = thisIsString ? aOther : *this;
1260 switch (rhs.BaseType()) {
1261 case eAtomBase:
1262 return lhs.Equals(rhs.GetAtomValue(), eCaseMatters);
1264 case eStringBase:
1265 return lhs.Equals(rhs.GetStringValue(), eCaseMatters);
1267 default: {
1268 nsAutoString val;
1269 rhs.ToString(val);
1270 return lhs.Equals(val, eCaseMatters);
1275 bool nsAttrValue::Contains(nsAtom* aValue,
1276 nsCaseTreatment aCaseSensitive) const {
1277 switch (BaseType()) {
1278 case eAtomBase: {
1279 nsAtom* atom = GetAtomValue();
1280 if (aCaseSensitive == eCaseMatters) {
1281 return aValue == atom;
1284 // For performance reasons, don't do a full on unicode case insensitive
1285 // string comparison. This is only used for quirks mode anyway.
1286 return nsContentUtils::EqualsIgnoreASCIICase(aValue, atom);
1288 default: {
1289 if (Type() == eAtomArray) {
1290 const AttrAtomArray* array = GetAtomArrayValue();
1291 if (aCaseSensitive == eCaseMatters) {
1292 return array->mArray.Contains(aValue);
1295 for (const RefPtr<nsAtom>& cur : array->mArray) {
1296 // For performance reasons, don't do a full on unicode case
1297 // insensitive string comparison. This is only used for quirks mode
1298 // anyway.
1299 if (nsContentUtils::EqualsIgnoreASCIICase(aValue, cur)) {
1300 return true;
1307 return false;
1310 struct AtomArrayStringComparator {
1311 bool Equals(nsAtom* atom, const nsAString& string) const {
1312 return atom->Equals(string);
1316 bool nsAttrValue::Contains(const nsAString& aValue) const {
1317 switch (BaseType()) {
1318 case eAtomBase: {
1319 nsAtom* atom = GetAtomValue();
1320 return atom->Equals(aValue);
1322 default: {
1323 if (Type() == eAtomArray) {
1324 const AttrAtomArray* array = GetAtomArrayValue();
1325 return array->mArray.Contains(aValue, AtomArrayStringComparator());
1330 return false;
1333 void nsAttrValue::ParseAtom(const nsAString& aValue) {
1334 ResetIfSet();
1336 RefPtr<nsAtom> atom = NS_Atomize(aValue);
1337 if (atom) {
1338 SetPtrValueAndType(atom.forget().take(), eAtomBase);
1342 void nsAttrValue::ParseAtomArray(nsAtom* aValue) {
1343 if (MiscContainer* cont = AtomArrayCache::Lookup(aValue)) {
1344 // Set our MiscContainer to the cached one.
1345 NS_ADDREF(cont);
1346 SetPtrValueAndType(cont, eOtherBase);
1347 return;
1350 const char16_t* iter = aValue->GetUTF16String();
1351 const char16_t* end = iter + aValue->GetLength();
1352 bool hasSpace = false;
1354 // skip initial whitespace
1355 while (iter != end && nsContentUtils::IsHTMLWhitespace(*iter)) {
1356 hasSpace = true;
1357 ++iter;
1360 if (iter == end) {
1361 // The value is empty or only contains whitespace.
1362 // Set this attribute to the string value.
1363 // We don't call the SetTo(nsAtom*) overload because doing so would
1364 // leave us with a classList of length 1.
1365 SetTo(nsDependentAtomString(aValue));
1366 return;
1369 const char16_t* start = iter;
1371 // get first - and often only - atom
1372 do {
1373 ++iter;
1374 } while (iter != end && !nsContentUtils::IsHTMLWhitespace(*iter));
1376 RefPtr<nsAtom> classAtom = iter == end && !hasSpace
1377 ? RefPtr<nsAtom>(aValue).forget()
1378 : NS_AtomizeMainThread(Substring(start, iter));
1379 if (!classAtom) {
1380 ResetIfSet();
1381 return;
1384 // skip whitespace
1385 while (iter != end && nsContentUtils::IsHTMLWhitespace(*iter)) {
1386 hasSpace = true;
1387 ++iter;
1390 if (iter == end && !hasSpace) {
1391 // we only found one classname and there was no whitespace so
1392 // don't bother storing a list
1393 ResetIfSet();
1394 nsAtom* atom = nullptr;
1395 classAtom.swap(atom);
1396 SetPtrValueAndType(atom, eAtomBase);
1397 return;
1400 // We have at least one class atom. Create a new AttrAtomArray.
1401 AttrAtomArray* array = new AttrAtomArray;
1403 // XXX(Bug 1631371) Check if this should use a fallible operation as it
1404 // pretended earlier.
1405 array->mArray.AppendElement(std::move(classAtom));
1407 // parse the rest of the classnames
1408 while (iter != end) {
1409 start = iter;
1411 do {
1412 ++iter;
1413 } while (iter != end && !nsContentUtils::IsHTMLWhitespace(*iter));
1415 classAtom = NS_AtomizeMainThread(Substring(start, iter));
1417 // XXX(Bug 1631371) Check if this should use a fallible operation as it
1418 // pretended earlier.
1419 array->mArray.AppendElement(std::move(classAtom));
1420 array->mMayContainDuplicates = true;
1422 // skip whitespace
1423 while (iter != end && nsContentUtils::IsHTMLWhitespace(*iter)) {
1424 ++iter;
1428 // Wrap the AtomArray into a fresh MiscContainer.
1429 MiscContainer* cont = EnsureEmptyMiscContainer();
1430 MOZ_ASSERT(cont->mValue.mRefCount == 0);
1431 cont->mValue.mAtomArray = array;
1432 cont->mType = eAtomArray;
1433 NS_ADDREF(cont);
1434 MOZ_ASSERT(cont->mValue.mRefCount == 1);
1436 // Assign the atom to the container's string bits (like SetMiscAtomOrString
1437 // would do).
1438 MOZ_ASSERT(!IsInServoTraversal());
1439 aValue->AddRef();
1440 uintptr_t bits = reinterpret_cast<uintptr_t>(aValue) | eAtomBase;
1441 cont->SetStringBitsMainThread(bits);
1443 // Put the container in the cache.
1444 cont->Cache();
1447 void nsAttrValue::ParseAtomArray(const nsAString& aValue) {
1448 if (aValue.IsVoid()) {
1449 ResetIfSet();
1450 } else {
1451 RefPtr<nsAtom> atom = NS_AtomizeMainThread(aValue);
1452 ParseAtomArray(atom);
1456 void nsAttrValue::ParseStringOrAtom(const nsAString& aValue) {
1457 uint32_t len = aValue.Length();
1458 // Don't bother with atoms if it's an empty string since
1459 // we can store those efficiently anyway.
1460 if (len && len <= NS_ATTRVALUE_MAX_STRINGLENGTH_ATOM) {
1461 ParseAtom(aValue);
1462 } else {
1463 SetTo(aValue);
1467 void nsAttrValue::ParsePartMapping(const nsAString& aValue) {
1468 ResetIfSet();
1469 MiscContainer* cont = EnsureEmptyMiscContainer();
1471 cont->mType = eShadowParts;
1472 cont->mValue.mShadowParts = new ShadowParts(ShadowParts::Parse(aValue));
1473 NS_ADDREF(cont);
1474 SetMiscAtomOrString(&aValue);
1475 MOZ_ASSERT(cont->mValue.mRefCount == 1);
1478 void nsAttrValue::SetIntValueAndType(int32_t aValue, ValueType aType,
1479 const nsAString* aStringValue) {
1480 if (aStringValue || aValue > NS_ATTRVALUE_INTEGERTYPE_MAXVALUE ||
1481 aValue < NS_ATTRVALUE_INTEGERTYPE_MINVALUE) {
1482 MiscContainer* cont = EnsureEmptyMiscContainer();
1483 switch (aType) {
1484 case eInteger: {
1485 cont->mValue.mInteger = aValue;
1486 break;
1488 case ePercent: {
1489 cont->mDoubleValue = aValue;
1490 break;
1492 case eEnum: {
1493 cont->mValue.mEnumValue = aValue;
1494 break;
1496 default: {
1497 MOZ_ASSERT_UNREACHABLE("unknown integer type");
1498 break;
1501 cont->mType = aType;
1502 SetMiscAtomOrString(aStringValue);
1503 } else {
1504 NS_ASSERTION(!mBits, "Reset before calling SetIntValueAndType!");
1505 mBits = (aValue * NS_ATTRVALUE_INTEGERTYPE_MULTIPLIER) | aType;
1509 void nsAttrValue::SetDoubleValueAndType(double aValue, ValueType aType,
1510 const nsAString* aStringValue) {
1511 MOZ_ASSERT(aType == eDoubleValue || aType == ePercent, "Unexpected type");
1512 MiscContainer* cont = EnsureEmptyMiscContainer();
1513 cont->mDoubleValue = aValue;
1514 cont->mType = aType;
1515 SetMiscAtomOrString(aStringValue);
1518 nsAtom* nsAttrValue::GetStoredAtom() const {
1519 if (BaseType() == eAtomBase) {
1520 return static_cast<nsAtom*>(GetPtr());
1522 if (BaseType() == eOtherBase) {
1523 return GetMiscContainer()->GetStoredAtom();
1525 return nullptr;
1528 nsStringBuffer* nsAttrValue::GetStoredStringBuffer() const {
1529 if (BaseType() == eStringBase) {
1530 return static_cast<nsStringBuffer*>(GetPtr());
1532 if (BaseType() == eOtherBase) {
1533 return GetMiscContainer()->GetStoredStringBuffer();
1535 return nullptr;
1538 int16_t nsAttrValue::GetEnumTableIndex(const EnumTable* aTable) {
1539 int16_t index = sEnumTableArray->IndexOf(aTable);
1540 if (index < 0) {
1541 index = sEnumTableArray->Length();
1542 NS_ASSERTION(index <= NS_ATTRVALUE_ENUMTABLEINDEX_MAXVALUE,
1543 "too many enum tables");
1544 sEnumTableArray->AppendElement(aTable);
1547 return index;
1550 int32_t nsAttrValue::EnumTableEntryToValue(const EnumTable* aEnumTable,
1551 const EnumTable* aTableEntry) {
1552 int16_t index = GetEnumTableIndex(aEnumTable);
1553 int32_t value =
1554 (aTableEntry->value << NS_ATTRVALUE_ENUMTABLEINDEX_BITS) + index;
1555 return value;
1558 bool nsAttrValue::ParseEnumValue(const nsAString& aValue,
1559 const EnumTable* aTable, bool aCaseSensitive,
1560 const EnumTable* aDefaultValue) {
1561 ResetIfSet();
1562 const EnumTable* tableEntry = aTable;
1564 while (tableEntry->tag) {
1565 if (aCaseSensitive ? aValue.EqualsASCII(tableEntry->tag)
1566 : aValue.LowerCaseEqualsASCII(tableEntry->tag)) {
1567 int32_t value = EnumTableEntryToValue(aTable, tableEntry);
1569 bool equals = aCaseSensitive || aValue.EqualsASCII(tableEntry->tag);
1570 if (!equals) {
1571 nsAutoString tag;
1572 tag.AssignASCII(tableEntry->tag);
1573 nsContentUtils::ASCIIToUpper(tag);
1574 if ((equals = tag.Equals(aValue))) {
1575 value |= NS_ATTRVALUE_ENUMTABLE_VALUE_NEEDS_TO_UPPER;
1578 SetIntValueAndType(value, eEnum, equals ? nullptr : &aValue);
1579 NS_ASSERTION(GetEnumValue() == tableEntry->value,
1580 "failed to store enum properly");
1582 return true;
1584 tableEntry++;
1587 if (aDefaultValue) {
1588 MOZ_ASSERT(aTable <= aDefaultValue && aDefaultValue < tableEntry,
1589 "aDefaultValue not inside aTable?");
1590 SetIntValueAndType(EnumTableEntryToValue(aTable, aDefaultValue), eEnum,
1591 &aValue);
1592 return true;
1595 return false;
1598 bool nsAttrValue::DoParseHTMLDimension(const nsAString& aInput,
1599 bool aEnsureNonzero) {
1600 ResetIfSet();
1602 // We don't use nsContentUtils::ParseHTMLInteger here because we
1603 // need a bunch of behavioral differences from it. We _could_ try to
1604 // use it, but it would not be a great fit.
1606 // https://html.spec.whatwg.org/multipage/#rules-for-parsing-dimension-values
1608 // Steps 1 and 2.
1609 const char16_t* position = aInput.BeginReading();
1610 const char16_t* end = aInput.EndReading();
1612 // We will need to keep track of whether this was a canonical representation
1613 // or not. It's non-canonical if it has leading whitespace, leading '+',
1614 // leading '0' characters, or trailing garbage.
1615 bool canonical = true;
1617 // Step 3.
1618 while (position != end && nsContentUtils::IsHTMLWhitespace(*position)) {
1619 canonical = false; // Leading whitespace
1620 ++position;
1623 // Step 4.
1624 if (position == end || *position < char16_t('0') ||
1625 *position > char16_t('9')) {
1626 return false;
1629 // Step 5.
1630 CheckedInt32 value = 0;
1632 // Collect up leading '0' first to avoid extra branching in the main
1633 // loop to set 'canonical' properly.
1634 while (position != end && *position == char16_t('0')) {
1635 canonical = false; // Leading '0'
1636 ++position;
1639 // Now collect up other digits.
1640 while (position != end && *position >= char16_t('0') &&
1641 *position <= char16_t('9')) {
1642 value = value * 10 + (*position - char16_t('0'));
1643 if (!value.isValid()) {
1644 // The spec assumes we can deal with arbitrary-size integers here, but we
1645 // really can't. If someone sets something too big, just bail out and
1646 // ignore it.
1647 return false;
1649 ++position;
1652 // Step 6 is implemented implicitly via the various "position != end" guards
1653 // from this point on.
1655 Maybe<double> doubleValue;
1656 // Step 7. The return in step 7.2 is handled by just falling through to the
1657 // code below this block when we reach end of input or a non-digit, because
1658 // the while loop will terminate at that point.
1659 if (position != end && *position == char16_t('.')) {
1660 canonical = false; // Let's not rely on double serialization reproducing
1661 // the string we started with.
1662 // Step 7.1.
1663 ++position;
1664 // If we have a '.' _not_ followed by digits, this is not as efficient as it
1665 // could be, because we will store as a double while we could have stored as
1666 // an int. But that seems like a pretty rare case.
1667 doubleValue.emplace(value.value());
1668 // Step 7.3.
1669 double divisor = 1.0f;
1670 // Step 7.4.
1671 while (position != end && *position >= char16_t('0') &&
1672 *position <= char16_t('9')) {
1673 // Step 7.4.1.
1674 divisor = divisor * 10.0f;
1675 // Step 7.4.2.
1676 doubleValue.ref() += (*position - char16_t('0')) / divisor;
1677 // Step 7.4.3.
1678 ++position;
1679 // Step 7.4.4 and 7.4.5 are captured in the while loop condition and the
1680 // "position != end" checks below.
1684 if (aEnsureNonzero && value.value() == 0 &&
1685 (!doubleValue || *doubleValue == 0.0f)) {
1686 // Not valid. Just drop it.
1687 return false;
1690 // Step 8 and the spec's early return from step 7.2.
1691 ValueType type;
1692 if (position != end && *position == char16_t('%')) {
1693 type = ePercent;
1694 ++position;
1695 } else if (doubleValue) {
1696 type = eDoubleValue;
1697 } else {
1698 type = eInteger;
1701 if (position != end) {
1702 canonical = false;
1705 if (doubleValue) {
1706 MOZ_ASSERT(!canonical, "We set it false above!");
1707 SetDoubleValueAndType(*doubleValue, type, &aInput);
1708 } else {
1709 SetIntValueAndType(value.value(), type, canonical ? nullptr : &aInput);
1712 #ifdef DEBUG
1713 nsAutoString str;
1714 ToString(str);
1715 MOZ_ASSERT(str == aInput, "We messed up our 'canonical' boolean!");
1716 #endif
1718 return true;
1721 bool nsAttrValue::ParseIntWithBounds(const nsAString& aString, int32_t aMin,
1722 int32_t aMax) {
1723 MOZ_ASSERT(aMin < aMax, "bad boundaries");
1725 ResetIfSet();
1727 nsContentUtils::ParseHTMLIntegerResultFlags result;
1728 int32_t originalVal = nsContentUtils::ParseHTMLInteger(aString, &result);
1729 if (result & nsContentUtils::eParseHTMLInteger_Error) {
1730 return false;
1733 int32_t val = std::max(originalVal, aMin);
1734 val = std::min(val, aMax);
1735 bool nonStrict =
1736 (val != originalVal) ||
1737 (result & nsContentUtils::eParseHTMLInteger_NonStandard) ||
1738 (result & nsContentUtils::eParseHTMLInteger_DidNotConsumeAllInput);
1740 SetIntValueAndType(val, eInteger, nonStrict ? &aString : nullptr);
1742 return true;
1745 void nsAttrValue::ParseIntWithFallback(const nsAString& aString,
1746 int32_t aDefault, int32_t aMax) {
1747 ResetIfSet();
1749 nsContentUtils::ParseHTMLIntegerResultFlags result;
1750 int32_t val = nsContentUtils::ParseHTMLInteger(aString, &result);
1751 bool nonStrict = false;
1752 if ((result & nsContentUtils::eParseHTMLInteger_Error) || val < 1) {
1753 val = aDefault;
1754 nonStrict = true;
1757 if (val > aMax) {
1758 val = aMax;
1759 nonStrict = true;
1762 if ((result & nsContentUtils::eParseHTMLInteger_NonStandard) ||
1763 (result & nsContentUtils::eParseHTMLInteger_DidNotConsumeAllInput)) {
1764 nonStrict = true;
1767 SetIntValueAndType(val, eInteger, nonStrict ? &aString : nullptr);
1770 void nsAttrValue::ParseClampedNonNegativeInt(const nsAString& aString,
1771 int32_t aDefault, int32_t aMin,
1772 int32_t aMax) {
1773 ResetIfSet();
1775 nsContentUtils::ParseHTMLIntegerResultFlags result;
1776 int32_t val = nsContentUtils::ParseHTMLInteger(aString, &result);
1777 bool nonStrict =
1778 (result & nsContentUtils::eParseHTMLInteger_NonStandard) ||
1779 (result & nsContentUtils::eParseHTMLInteger_DidNotConsumeAllInput);
1781 if (result & nsContentUtils::eParseHTMLInteger_ErrorOverflow) {
1782 if (result & nsContentUtils::eParseHTMLInteger_Negative) {
1783 val = aDefault;
1784 } else {
1785 val = aMax;
1787 nonStrict = true;
1788 } else if ((result & nsContentUtils::eParseHTMLInteger_Error) || val < 0) {
1789 val = aDefault;
1790 nonStrict = true;
1791 } else if (val < aMin) {
1792 val = aMin;
1793 nonStrict = true;
1794 } else if (val > aMax) {
1795 val = aMax;
1796 nonStrict = true;
1799 SetIntValueAndType(val, eInteger, nonStrict ? &aString : nullptr);
1802 bool nsAttrValue::ParseNonNegativeIntValue(const nsAString& aString) {
1803 ResetIfSet();
1805 nsContentUtils::ParseHTMLIntegerResultFlags result;
1806 int32_t originalVal = nsContentUtils::ParseHTMLInteger(aString, &result);
1807 if ((result & nsContentUtils::eParseHTMLInteger_Error) || originalVal < 0) {
1808 return false;
1811 bool nonStrict =
1812 (result & nsContentUtils::eParseHTMLInteger_NonStandard) ||
1813 (result & nsContentUtils::eParseHTMLInteger_DidNotConsumeAllInput);
1815 SetIntValueAndType(originalVal, eInteger, nonStrict ? &aString : nullptr);
1817 return true;
1820 bool nsAttrValue::ParsePositiveIntValue(const nsAString& aString) {
1821 ResetIfSet();
1823 nsContentUtils::ParseHTMLIntegerResultFlags result;
1824 int32_t originalVal = nsContentUtils::ParseHTMLInteger(aString, &result);
1825 if ((result & nsContentUtils::eParseHTMLInteger_Error) || originalVal <= 0) {
1826 return false;
1829 bool nonStrict =
1830 (result & nsContentUtils::eParseHTMLInteger_NonStandard) ||
1831 (result & nsContentUtils::eParseHTMLInteger_DidNotConsumeAllInput);
1833 SetIntValueAndType(originalVal, eInteger, nonStrict ? &aString : nullptr);
1835 return true;
1838 void nsAttrValue::SetColorValue(nscolor aColor, const nsAString& aString) {
1839 nsStringBuffer* buf = GetStringBuffer(aString).take();
1840 if (!buf) {
1841 return;
1844 MiscContainer* cont = EnsureEmptyMiscContainer();
1845 cont->mValue.mColor = aColor;
1846 cont->mType = eColor;
1848 // Save the literal string we were passed for round-tripping.
1849 cont->SetStringBitsMainThread(reinterpret_cast<uintptr_t>(buf) | eStringBase);
1852 bool nsAttrValue::ParseColor(const nsAString& aString) {
1853 ResetIfSet();
1855 // FIXME (partially, at least): HTML5's algorithm says we shouldn't do
1856 // the whitespace compression, trimming, or the test for emptiness.
1857 // (I'm a little skeptical that we shouldn't do the whitespace
1858 // trimming; WebKit also does it.)
1859 nsAutoString colorStr(aString);
1860 colorStr.CompressWhitespace(true, true);
1861 if (colorStr.IsEmpty()) {
1862 return false;
1865 nscolor color;
1866 // No color names begin with a '#'; in standards mode, all acceptable
1867 // numeric colors do.
1868 if (colorStr.First() == '#') {
1869 nsDependentString withoutHash(colorStr.get() + 1, colorStr.Length() - 1);
1870 if (NS_HexToRGBA(withoutHash, nsHexColorType::NoAlpha, &color)) {
1871 SetColorValue(color, aString);
1872 return true;
1874 } else if (colorStr.LowerCaseEqualsLiteral("transparent")) {
1875 SetColorValue(NS_RGBA(0, 0, 0, 0), aString);
1876 return true;
1877 } else {
1878 const NS_ConvertUTF16toUTF8 colorNameU8(colorStr);
1879 if (Servo_ColorNameToRgb(&colorNameU8, &color)) {
1880 SetColorValue(color, aString);
1881 return true;
1885 // FIXME (maybe): HTML5 says we should handle system colors. This
1886 // means we probably need another storage type, since we'd need to
1887 // handle dynamic changes. However, I think this is a bad idea:
1888 // http://lists.whatwg.org/pipermail/whatwg-whatwg.org/2010-May/026449.html
1890 // Use NS_LooseHexToRGB as a fallback if nothing above worked.
1891 if (NS_LooseHexToRGB(colorStr, &color)) {
1892 SetColorValue(color, aString);
1893 return true;
1896 return false;
1899 bool nsAttrValue::ParseDoubleValue(const nsAString& aString) {
1900 ResetIfSet();
1902 nsresult ec;
1903 double val = PromiseFlatString(aString).ToDouble(&ec);
1904 if (NS_FAILED(ec)) {
1905 return false;
1908 MiscContainer* cont = EnsureEmptyMiscContainer();
1909 cont->mDoubleValue = val;
1910 cont->mType = eDoubleValue;
1911 nsAutoString serializedFloat;
1912 serializedFloat.AppendFloat(val);
1913 SetMiscAtomOrString(serializedFloat.Equals(aString) ? nullptr : &aString);
1914 return true;
1917 bool nsAttrValue::ParseStyleAttribute(const nsAString& aString,
1918 nsIPrincipal* aMaybeScriptedPrincipal,
1919 nsStyledElement* aElement) {
1920 dom::Document* doc = aElement->OwnerDoc();
1921 AttributeStyles* attrStyles = doc->GetAttributeStyles();
1922 NS_ASSERTION(aElement->NodePrincipal() == doc->NodePrincipal(),
1923 "This is unexpected");
1925 nsIPrincipal* principal = aMaybeScriptedPrincipal ? aMaybeScriptedPrincipal
1926 : aElement->NodePrincipal();
1927 RefPtr<URLExtraData> data = aElement->GetURLDataForStyleAttr(principal);
1929 // If the (immutable) document URI does not match the element's base URI
1930 // (the common case is that they do match) do not cache the rule. This is
1931 // because the results of the CSS parser are dependent on these URIs, and we
1932 // do not want to have to account for the URIs in the hash lookup.
1933 // Similarly, if the triggering principal does not match the node principal,
1934 // do not cache the rule, since the principal will be encoded in any parsed
1935 // URLs in the rule.
1936 const bool cachingAllowed = attrStyles &&
1937 doc->GetDocumentURI() == data->BaseURI() &&
1938 principal == aElement->NodePrincipal();
1939 if (cachingAllowed) {
1940 if (MiscContainer* cont = attrStyles->LookupStyleAttr(aString)) {
1941 // Set our MiscContainer to the cached one.
1942 NS_ADDREF(cont);
1943 SetPtrValueAndType(cont, eOtherBase);
1944 return true;
1948 RefPtr<DeclarationBlock> decl =
1949 DeclarationBlock::FromCssText(aString, data, doc->GetCompatibilityMode(),
1950 doc->CSSLoader(), StyleCssRuleType::Style);
1951 if (!decl) {
1952 return false;
1954 decl->SetAttributeStyles(attrStyles);
1955 SetTo(decl.forget(), &aString);
1957 if (cachingAllowed) {
1958 MiscContainer* cont = GetMiscContainer();
1959 cont->Cache();
1962 return true;
1965 void nsAttrValue::SetMiscAtomOrString(const nsAString* aValue) {
1966 NS_ASSERTION(GetMiscContainer(), "Must have MiscContainer!");
1967 NS_ASSERTION(!GetMiscContainer()->mStringBits || IsInServoTraversal(),
1968 "Trying to re-set atom or string!");
1969 if (aValue) {
1970 uint32_t len = aValue->Length();
1971 // * We're allowing eCSSDeclaration attributes to store empty
1972 // strings as it can be beneficial to store an empty style
1973 // attribute as a parsed rule.
1974 // * We're allowing enumerated values because sometimes the empty
1975 // string corresponds to a particular enumerated value, especially
1976 // for enumerated values that are not limited enumerated.
1977 // Add other types as needed.
1978 NS_ASSERTION(len || Type() == eCSSDeclaration || Type() == eEnum,
1979 "Empty string?");
1980 MiscContainer* cont = GetMiscContainer();
1982 if (len <= NS_ATTRVALUE_MAX_STRINGLENGTH_ATOM) {
1983 nsAtom* atom = MOZ_LIKELY(!IsInServoTraversal())
1984 ? NS_AtomizeMainThread(*aValue).take()
1985 : NS_Atomize(*aValue).take();
1986 NS_ENSURE_TRUE_VOID(atom);
1987 uintptr_t bits = reinterpret_cast<uintptr_t>(atom) | eAtomBase;
1989 // In the common case we're not in the servo traversal, and we can just
1990 // set the bits normally. The parallel case requires more care.
1991 if (MOZ_LIKELY(!IsInServoTraversal())) {
1992 cont->SetStringBitsMainThread(bits);
1993 } else if (!cont->mStringBits.compareExchange(0, bits)) {
1994 // We raced with somebody else setting the bits. Release our copy.
1995 atom->Release();
1997 } else {
1998 nsStringBuffer* buffer = GetStringBuffer(*aValue).take();
1999 NS_ENSURE_TRUE_VOID(buffer);
2000 uintptr_t bits = reinterpret_cast<uintptr_t>(buffer) | eStringBase;
2002 // In the common case we're not in the servo traversal, and we can just
2003 // set the bits normally. The parallel case requires more care.
2004 if (MOZ_LIKELY(!IsInServoTraversal())) {
2005 cont->SetStringBitsMainThread(bits);
2006 } else if (!cont->mStringBits.compareExchange(0, bits)) {
2007 // We raced with somebody else setting the bits. Release our copy.
2008 buffer->Release();
2014 void nsAttrValue::ResetMiscAtomOrString() {
2015 MiscContainer* cont = GetMiscContainer();
2016 bool isString;
2017 if (void* ptr = cont->GetStringOrAtomPtr(isString)) {
2018 if (isString) {
2019 static_cast<nsStringBuffer*>(ptr)->Release();
2020 } else {
2021 static_cast<nsAtom*>(ptr)->Release();
2023 cont->SetStringBitsMainThread(0);
2027 void nsAttrValue::SetSVGType(ValueType aType, const void* aValue,
2028 const nsAString* aSerialized) {
2029 MOZ_ASSERT(IsSVGType(aType), "Not an SVG type");
2031 MiscContainer* cont = EnsureEmptyMiscContainer();
2032 // All SVG types are just pointers to classes so just setting any of them
2033 // will do. We'll lose type-safety but the signature of the calling
2034 // function should ensure we don't get anything unexpected, and once we
2035 // stick aValue in a union we lose type information anyway.
2036 cont->mValue.mSVGLength = static_cast<const SVGAnimatedLength*>(aValue);
2037 cont->mType = aType;
2038 SetMiscAtomOrString(aSerialized);
2041 MiscContainer* nsAttrValue::ClearMiscContainer() {
2042 MiscContainer* cont = nullptr;
2043 if (BaseType() == eOtherBase) {
2044 cont = GetMiscContainer();
2045 if (cont->IsRefCounted() && cont->mValue.mRefCount > 1) {
2046 // This MiscContainer is shared, we need a new one.
2047 NS_RELEASE(cont);
2049 cont = AllocMiscContainer();
2050 SetPtrValueAndType(cont, eOtherBase);
2051 } else {
2052 switch (cont->mType) {
2053 case eCSSDeclaration: {
2054 MOZ_ASSERT(cont->mValue.mRefCount == 1);
2055 cont->Release();
2056 cont->Evict();
2057 NS_RELEASE(cont->mValue.mCSSDeclaration);
2058 break;
2060 case eShadowParts: {
2061 MOZ_ASSERT(cont->mValue.mRefCount == 1);
2062 cont->Release();
2063 delete cont->mValue.mShadowParts;
2064 break;
2066 case eURL: {
2067 NS_RELEASE(cont->mValue.mURL);
2068 break;
2070 case eAtomArray: {
2071 MOZ_ASSERT(cont->mValue.mRefCount == 1);
2072 cont->Release();
2073 cont->Evict();
2074 delete cont->mValue.mAtomArray;
2075 break;
2077 default: {
2078 break;
2082 ResetMiscAtomOrString();
2083 } else {
2084 ResetIfSet();
2087 return cont;
2090 MiscContainer* nsAttrValue::EnsureEmptyMiscContainer() {
2091 MiscContainer* cont = ClearMiscContainer();
2092 if (cont) {
2093 MOZ_ASSERT(BaseType() == eOtherBase);
2094 ResetMiscAtomOrString();
2095 cont = GetMiscContainer();
2096 } else {
2097 cont = AllocMiscContainer();
2098 SetPtrValueAndType(cont, eOtherBase);
2101 return cont;
2104 already_AddRefed<nsStringBuffer> nsAttrValue::GetStringBuffer(
2105 const nsAString& aValue) const {
2106 uint32_t len = aValue.Length();
2107 if (!len) {
2108 return nullptr;
2111 RefPtr<nsStringBuffer> buf = nsStringBuffer::FromString(aValue);
2112 if (buf && (buf->StorageSize() / sizeof(char16_t) - 1) == len) {
2113 // We can only reuse the buffer if it's exactly sized, since we rely on
2114 // StorageSize() to get the string length in ToString().
2115 return buf.forget();
2117 return nsStringBuffer::Create(aValue.Data(), aValue.Length());
2120 size_t nsAttrValue::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const {
2121 size_t n = 0;
2123 switch (BaseType()) {
2124 case eStringBase: {
2125 nsStringBuffer* str = static_cast<nsStringBuffer*>(GetPtr());
2126 n += str ? str->SizeOfIncludingThisIfUnshared(aMallocSizeOf) : 0;
2127 break;
2129 case eOtherBase: {
2130 MiscContainer* container = GetMiscContainer();
2131 if (!container) {
2132 break;
2134 if (container->IsRefCounted() && container->mValue.mRefCount > 1) {
2135 // We don't report this MiscContainer at all in order to avoid
2136 // twice-reporting it.
2137 // TODO DMD, bug 1027551 - figure out how to report this ref-counted
2138 // object just once.
2139 break;
2141 n += aMallocSizeOf(container);
2143 // We only count the size of the object pointed by otherPtr if it's a
2144 // string. When it's an atom, it's counted separatly.
2145 if (nsStringBuffer* buf = container->GetStoredStringBuffer()) {
2146 n += buf->SizeOfIncludingThisIfUnshared(aMallocSizeOf);
2149 if (Type() == eCSSDeclaration && container->mValue.mCSSDeclaration) {
2150 // TODO: mCSSDeclaration might be owned by another object which
2151 // would make us count them twice, bug 677493.
2152 // Bug 1281964: For DeclarationBlock if we do measure we'll
2153 // need a way to call the Servo heap_size_of function.
2154 // n += container->mCSSDeclaration->SizeOfIncludingThis(aMallocSizeOf);
2155 } else if (Type() == eAtomArray && container->mValue.mAtomArray) {
2156 // Don't measure each nsAtom, they are measured separatly.
2157 n += container->mValue.mAtomArray->mArray.ShallowSizeOfIncludingThis(
2158 aMallocSizeOf);
2160 break;
2162 case eAtomBase: // Atoms are counted separately.
2163 case eIntegerBase: // The value is in mBits, nothing to do.
2164 break;
2167 return n;