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/. */
8 * A struct that represents the value (type and actual data) of an
12 #include "mozilla/DebugOnly.h"
13 #include "mozilla/HashFunctions.h"
15 #include "nsAttrValue.h"
16 #include "nsAttrValueInlines.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/DeclarationBlock.h"
24 #include "nsContentUtils.h"
25 #include "nsReadableUtils.h"
26 #include "nsHTMLCSSStyleSheet.h"
27 #include "nsStyledElement.h"
29 #include "mozilla/dom/Document.h"
32 using namespace mozilla
;
34 #define MISC_STR_PTR(_cont) \
35 reinterpret_cast<void*>((_cont)->mStringBits & NS_ATTRVALUE_POINTERVALUE_MASK)
38 MiscContainer
* nsAttrValue::AllocMiscContainer() {
39 MOZ_ASSERT(NS_IsMainThread());
40 MiscContainer
* cont
= nullptr;
41 Swap(cont
, sMiscContainerCache
);
44 return new (cont
) MiscContainer
;
47 return new MiscContainer
;
51 void nsAttrValue::DeallocMiscContainer(MiscContainer
* aCont
) {
52 MOZ_ASSERT(NS_IsMainThread());
57 if (!sMiscContainerCache
) {
58 aCont
->~MiscContainer();
59 sMiscContainerCache
= aCont
;
65 bool MiscContainer::GetString(nsAString
& aString
) const {
66 void* ptr
= MISC_STR_PTR(this);
72 if (static_cast<nsAttrValue::ValueBaseType
>(mStringBits
&
73 NS_ATTRVALUE_BASETYPE_MASK
) ==
74 nsAttrValue::eStringBase
) {
75 nsStringBuffer
* buffer
= static_cast<nsStringBuffer
*>(ptr
);
80 buffer
->ToString(buffer
->StorageSize() / sizeof(char16_t
) - 1, aString
);
84 nsAtom
* atom
= static_cast<nsAtom
*>(ptr
);
89 atom
->ToString(aString
);
93 void MiscContainer::Cache() {
94 // Not implemented for anything else yet.
95 if (mType
!= nsAttrValue::eCSSDeclaration
) {
96 MOZ_ASSERT_UNREACHABLE("unexpected cached nsAttrValue type");
100 MOZ_ASSERT(IsRefCounted());
101 MOZ_ASSERT(mValue
.mRefCount
> 0);
102 MOZ_ASSERT(!mValue
.mCached
);
104 nsHTMLCSSStyleSheet
* sheet
= mValue
.mCSSDeclaration
->GetHTMLCSSStyleSheet();
110 bool gotString
= GetString(str
);
115 sheet
->CacheStyleAttr(str
, this);
118 // This has to be immutable once it goes into the cache.
119 mValue
.mCSSDeclaration
->SetImmutable();
122 void MiscContainer::Evict() {
123 // Not implemented for anything else yet.
124 if (mType
!= nsAttrValue::eCSSDeclaration
) {
125 MOZ_ASSERT_UNREACHABLE("unexpected cached nsAttrValue type");
128 MOZ_ASSERT(IsRefCounted());
129 MOZ_ASSERT(mValue
.mRefCount
== 0);
131 if (!mValue
.mCached
) {
135 nsHTMLCSSStyleSheet
* sheet
= mValue
.mCSSDeclaration
->GetHTMLCSSStyleSheet();
139 DebugOnly
<bool> gotString
= GetString(str
);
140 MOZ_ASSERT(gotString
);
142 sheet
->EvictStyleAttr(str
, this);
146 nsTArray
<const nsAttrValue::EnumTable
*>* nsAttrValue::sEnumTableArray
= nullptr;
147 MiscContainer
* nsAttrValue::sMiscContainerCache
= nullptr;
149 nsAttrValue::nsAttrValue() : mBits(0) {}
151 nsAttrValue::nsAttrValue(const nsAttrValue
& aOther
) : mBits(0) {
155 nsAttrValue::nsAttrValue(const nsAString
& aValue
) : mBits(0) { SetTo(aValue
); }
157 nsAttrValue::nsAttrValue(nsAtom
* aValue
) : mBits(0) { SetTo(aValue
); }
159 nsAttrValue::nsAttrValue(already_AddRefed
<DeclarationBlock
> aValue
,
160 const nsAString
* aSerialized
)
162 SetTo(std::move(aValue
), aSerialized
);
165 nsAttrValue::nsAttrValue(const nsIntMargin
& aValue
) : mBits(0) {
169 nsAttrValue::~nsAttrValue() { ResetIfSet(); }
172 nsresult
nsAttrValue::Init() {
173 NS_ASSERTION(!sEnumTableArray
, "nsAttrValue already initialized");
174 sEnumTableArray
= new nsTArray
<const EnumTable
*>;
179 void nsAttrValue::Shutdown() {
180 MOZ_ASSERT(NS_IsMainThread());
181 delete sEnumTableArray
;
182 sEnumTableArray
= nullptr;
183 // The MiscContainer pointed to by sMiscContainerCache has already
184 // be destructed so `delete sMiscContainerCache` is
185 // dangerous. Invoke `operator delete` to free the memory.
186 ::operator delete(sMiscContainerCache
);
187 sMiscContainerCache
= nullptr;
190 void nsAttrValue::Reset() {
191 switch (BaseType()) {
193 nsStringBuffer
* str
= static_cast<nsStringBuffer
*>(GetPtr());
201 MiscContainer
* cont
= GetMiscContainer();
202 if (cont
->IsRefCounted() && cont
->mValue
.mRefCount
> 1) {
207 DeallocMiscContainer(ClearMiscContainer());
212 nsAtom
* atom
= GetAtomValue();
225 void nsAttrValue::SetTo(const nsAttrValue
& aOther
) {
226 if (this == &aOther
) {
230 switch (aOther
.BaseType()) {
233 nsStringBuffer
* str
= static_cast<nsStringBuffer
*>(aOther
.GetPtr());
236 SetPtrValueAndType(str
, eStringBase
);
245 nsAtom
* atom
= aOther
.GetAtomValue();
247 SetPtrValueAndType(atom
, eAtomBase
);
252 mBits
= aOther
.mBits
;
257 MiscContainer
* otherCont
= aOther
.GetMiscContainer();
258 if (otherCont
->IsRefCounted()) {
259 DeallocMiscContainer(ClearMiscContainer());
260 NS_ADDREF(otherCont
);
261 SetPtrValueAndType(otherCont
, eOtherBase
);
265 MiscContainer
* cont
= EnsureEmptyMiscContainer();
266 switch (otherCont
->mType
) {
268 cont
->mValue
.mInteger
= otherCont
->mValue
.mInteger
;
272 cont
->mValue
.mEnumValue
= otherCont
->mValue
.mEnumValue
;
276 cont
->mValue
.mPercent
= otherCont
->mValue
.mPercent
;
280 cont
->mValue
.mColor
= otherCont
->mValue
.mColor
;
283 case eCSSDeclaration
: {
284 MOZ_CRASH("These should be refcounted!");
287 NS_ADDREF(cont
->mValue
.mURL
= otherCont
->mValue
.mURL
);
291 if (!EnsureEmptyAtomArray() ||
292 !GetAtomArrayValue()->AppendElements(*otherCont
->mValue
.mAtomArray
)) {
299 cont
->mDoubleValue
= otherCont
->mDoubleValue
;
302 case eIntMarginValue
: {
303 if (otherCont
->mValue
.mIntMargin
)
304 cont
->mValue
.mIntMargin
=
305 new nsIntMargin(*otherCont
->mValue
.mIntMargin
);
309 if (IsSVGType(otherCont
->mType
)) {
310 // All SVG types are just pointers to classes and will therefore have
311 // the same size so it doesn't really matter which one we assign
312 cont
->mValue
.mSVGLength
= otherCont
->mValue
.mSVGLength
;
314 MOZ_ASSERT_UNREACHABLE("unknown type stored in MiscContainer");
320 void* otherPtr
= MISC_STR_PTR(otherCont
);
322 if (static_cast<ValueBaseType
>(otherCont
->mStringBits
&
323 NS_ATTRVALUE_BASETYPE_MASK
) == eStringBase
) {
324 static_cast<nsStringBuffer
*>(otherPtr
)->AddRef();
326 static_cast<nsAtom
*>(otherPtr
)->AddRef();
328 cont
->SetStringBitsMainThread(otherCont
->mStringBits
);
330 // Note, set mType after switch-case, otherwise EnsureEmptyAtomArray doesn't
332 cont
->mType
= otherCont
->mType
;
335 void nsAttrValue::SetTo(const nsAString
& aValue
) {
337 nsStringBuffer
* buf
= GetStringBuffer(aValue
).take();
339 SetPtrValueAndType(buf
, eStringBase
);
343 void nsAttrValue::SetTo(nsAtom
* aValue
) {
347 SetPtrValueAndType(aValue
, eAtomBase
);
351 void nsAttrValue::SetTo(int16_t aInt
) {
353 SetIntValueAndType(aInt
, eInteger
, nullptr);
356 void nsAttrValue::SetTo(int32_t aInt
, const nsAString
* aSerialized
) {
358 SetIntValueAndType(aInt
, eInteger
, aSerialized
);
361 void nsAttrValue::SetTo(double aValue
, const nsAString
* aSerialized
) {
362 MiscContainer
* cont
= EnsureEmptyMiscContainer();
363 cont
->mDoubleValue
= aValue
;
364 cont
->mType
= eDoubleValue
;
365 SetMiscAtomOrString(aSerialized
);
368 void nsAttrValue::SetTo(already_AddRefed
<DeclarationBlock
> aValue
,
369 const nsAString
* aSerialized
) {
370 MiscContainer
* cont
= EnsureEmptyMiscContainer();
371 MOZ_ASSERT(cont
->mValue
.mRefCount
== 0);
372 cont
->mValue
.mCSSDeclaration
= aValue
.take();
373 cont
->mType
= eCSSDeclaration
;
375 SetMiscAtomOrString(aSerialized
);
376 MOZ_ASSERT(cont
->mValue
.mRefCount
== 1);
379 void nsAttrValue::SetTo(nsIURI
* aValue
, const nsAString
* aSerialized
) {
380 MiscContainer
* cont
= EnsureEmptyMiscContainer();
381 NS_ADDREF(cont
->mValue
.mURL
= aValue
);
383 SetMiscAtomOrString(aSerialized
);
386 void nsAttrValue::SetTo(const nsIntMargin
& aValue
) {
387 MiscContainer
* cont
= EnsureEmptyMiscContainer();
388 cont
->mValue
.mIntMargin
= new nsIntMargin(aValue
);
389 cont
->mType
= eIntMarginValue
;
392 void nsAttrValue::SetToSerialized(const nsAttrValue
& aOther
) {
393 if (aOther
.Type() != nsAttrValue::eString
&&
394 aOther
.Type() != nsAttrValue::eAtom
) {
396 aOther
.ToString(val
);
403 void nsAttrValue::SetTo(const SVGAnimatedOrient
& aValue
,
404 const nsAString
* aSerialized
) {
405 SetSVGType(eSVGOrient
, &aValue
, aSerialized
);
408 void nsAttrValue::SetTo(const SVGAnimatedIntegerPair
& aValue
,
409 const nsAString
* aSerialized
) {
410 SetSVGType(eSVGIntegerPair
, &aValue
, aSerialized
);
413 void nsAttrValue::SetTo(const SVGAnimatedLength
& aValue
,
414 const nsAString
* aSerialized
) {
415 SetSVGType(eSVGLength
, &aValue
, aSerialized
);
418 void nsAttrValue::SetTo(const SVGLengthList
& aValue
,
419 const nsAString
* aSerialized
) {
420 // While an empty string will parse as a length list, there's no need to store
421 // it (and SetMiscAtomOrString will assert if we try)
422 if (aSerialized
&& aSerialized
->IsEmpty()) {
423 aSerialized
= nullptr;
425 SetSVGType(eSVGLengthList
, &aValue
, aSerialized
);
428 void nsAttrValue::SetTo(const SVGNumberList
& aValue
,
429 const nsAString
* aSerialized
) {
430 // While an empty string will parse as a number list, there's no need to store
431 // it (and SetMiscAtomOrString will assert if we try)
432 if (aSerialized
&& aSerialized
->IsEmpty()) {
433 aSerialized
= nullptr;
435 SetSVGType(eSVGNumberList
, &aValue
, aSerialized
);
438 void nsAttrValue::SetTo(const SVGAnimatedNumberPair
& aValue
,
439 const nsAString
* aSerialized
) {
440 SetSVGType(eSVGNumberPair
, &aValue
, aSerialized
);
443 void nsAttrValue::SetTo(const SVGPathData
& aValue
,
444 const nsAString
* aSerialized
) {
445 // While an empty string will parse as path data, there's no need to store it
446 // (and SetMiscAtomOrString will assert if we try)
447 if (aSerialized
&& aSerialized
->IsEmpty()) {
448 aSerialized
= nullptr;
450 SetSVGType(eSVGPathData
, &aValue
, aSerialized
);
453 void nsAttrValue::SetTo(const SVGPointList
& aValue
,
454 const nsAString
* aSerialized
) {
455 // While an empty string will parse as a point list, there's no need to store
456 // it (and SetMiscAtomOrString will assert if we try)
457 if (aSerialized
&& aSerialized
->IsEmpty()) {
458 aSerialized
= nullptr;
460 SetSVGType(eSVGPointList
, &aValue
, aSerialized
);
463 void nsAttrValue::SetTo(const SVGAnimatedPreserveAspectRatio
& aValue
,
464 const nsAString
* aSerialized
) {
465 SetSVGType(eSVGPreserveAspectRatio
, &aValue
, aSerialized
);
468 void nsAttrValue::SetTo(const SVGStringList
& aValue
,
469 const nsAString
* aSerialized
) {
470 // While an empty string will parse as a string list, there's no need to store
471 // it (and SetMiscAtomOrString will assert if we try)
472 if (aSerialized
&& aSerialized
->IsEmpty()) {
473 aSerialized
= nullptr;
475 SetSVGType(eSVGStringList
, &aValue
, aSerialized
);
478 void nsAttrValue::SetTo(const SVGTransformList
& aValue
,
479 const nsAString
* aSerialized
) {
480 // While an empty string will parse as a transform list, there's no need to
481 // store it (and SetMiscAtomOrString will assert if we try)
482 if (aSerialized
&& aSerialized
->IsEmpty()) {
483 aSerialized
= nullptr;
485 SetSVGType(eSVGTransformList
, &aValue
, aSerialized
);
488 void nsAttrValue::SetTo(const SVGAnimatedViewBox
& aValue
,
489 const nsAString
* aSerialized
) {
490 SetSVGType(eSVGViewBox
, &aValue
, aSerialized
);
493 void nsAttrValue::SwapValueWith(nsAttrValue
& aOther
) {
494 uintptr_t tmp
= aOther
.mBits
;
495 aOther
.mBits
= mBits
;
499 void nsAttrValue::ToString(nsAString
& aResult
) const {
500 MiscContainer
* cont
= nullptr;
501 if (BaseType() == eOtherBase
) {
502 cont
= GetMiscContainer();
504 if (cont
->GetString(aResult
)) {
511 nsStringBuffer
* str
= static_cast<nsStringBuffer
*>(GetPtr());
513 str
->ToString(str
->StorageSize() / sizeof(char16_t
) - 1, aResult
);
520 nsAtom
* atom
= static_cast<nsAtom
*>(GetPtr());
521 atom
->ToString(aResult
);
527 intStr
.AppendInt(GetIntegerValue());
534 MOZ_ASSERT_UNREACHABLE("color attribute without string data");
540 GetEnumString(aResult
, false);
545 intStr
.AppendInt(cont
? cont
->mValue
.mPercent
: GetIntInternal());
546 aResult
= intStr
+ NS_LITERAL_STRING("%");
550 case eCSSDeclaration
: {
552 MiscContainer
* container
= GetMiscContainer();
553 if (DeclarationBlock
* decl
= container
->mValue
.mCSSDeclaration
) {
554 decl
->ToString(aResult
);
557 // This can be reached during parallel selector matching with attribute
558 // selectors on the style attribute. SetMiscAtomOrString handles this
559 // case, and as of this writing this is the only consumer that needs it.
560 const_cast<nsAttrValue
*>(this)->SetMiscAtomOrString(&aResult
);
566 aResult
.AppendFloat(GetDoubleValue());
569 case eSVGIntegerPair
: {
570 SVGAttrValueWrapper::ToString(
571 GetMiscContainer()->mValue
.mSVGAnimatedIntegerPair
, aResult
);
575 SVGAttrValueWrapper::ToString(
576 GetMiscContainer()->mValue
.mSVGAnimatedOrient
, aResult
);
580 SVGAttrValueWrapper::ToString(GetMiscContainer()->mValue
.mSVGLength
,
584 case eSVGLengthList
: {
585 SVGAttrValueWrapper::ToString(GetMiscContainer()->mValue
.mSVGLengthList
,
589 case eSVGNumberList
: {
590 SVGAttrValueWrapper::ToString(GetMiscContainer()->mValue
.mSVGNumberList
,
594 case eSVGNumberPair
: {
595 SVGAttrValueWrapper::ToString(
596 GetMiscContainer()->mValue
.mSVGAnimatedNumberPair
, aResult
);
600 SVGAttrValueWrapper::ToString(GetMiscContainer()->mValue
.mSVGPathData
,
604 case eSVGPointList
: {
605 SVGAttrValueWrapper::ToString(GetMiscContainer()->mValue
.mSVGPointList
,
609 case eSVGPreserveAspectRatio
: {
610 SVGAttrValueWrapper::ToString(
611 GetMiscContainer()->mValue
.mSVGAnimatedPreserveAspectRatio
, aResult
);
614 case eSVGStringList
: {
615 SVGAttrValueWrapper::ToString(GetMiscContainer()->mValue
.mSVGStringList
,
619 case eSVGTransformList
: {
620 SVGAttrValueWrapper::ToString(
621 GetMiscContainer()->mValue
.mSVGTransformList
, aResult
);
625 SVGAttrValueWrapper::ToString(
626 GetMiscContainer()->mValue
.mSVGAnimatedViewBox
, aResult
);
636 already_AddRefed
<nsAtom
> nsAttrValue::GetAsAtom() const {
639 return NS_AtomizeMainThread(GetStringValue());
642 RefPtr
<nsAtom
> atom
= GetAtomValue();
643 return atom
.forget();
649 return NS_AtomizeMainThread(val
);
654 const nsCheapString
nsAttrValue::GetStringValue() const {
655 MOZ_ASSERT(Type() == eString
, "wrong type");
657 return nsCheapString(static_cast<nsStringBuffer
*>(GetPtr()));
660 bool nsAttrValue::GetColorValue(nscolor
& aColor
) const {
661 if (Type() != eColor
) {
662 // Unparseable value, treat as unset.
663 NS_ASSERTION(Type() == eString
, "unexpected type for color-valued attr");
667 aColor
= GetMiscContainer()->mValue
.mColor
;
671 void nsAttrValue::GetEnumString(nsAString
& aResult
, bool aRealTag
) const {
672 MOZ_ASSERT(Type() == eEnum
, "wrong type");
674 uint32_t allEnumBits
= (BaseType() == eIntegerBase
)
675 ? static_cast<uint32_t>(GetIntInternal())
676 : GetMiscContainer()->mValue
.mEnumValue
;
677 int16_t val
= allEnumBits
>> NS_ATTRVALUE_ENUMTABLEINDEX_BITS
;
678 const EnumTable
* table
= sEnumTableArray
->ElementAt(
679 allEnumBits
& NS_ATTRVALUE_ENUMTABLEINDEX_MASK
);
682 if (table
->value
== val
) {
683 aResult
.AssignASCII(table
->tag
);
685 allEnumBits
& NS_ATTRVALUE_ENUMTABLE_VALUE_NEEDS_TO_UPPER
) {
686 nsContentUtils::ASCIIToUpper(aResult
);
693 MOZ_ASSERT_UNREACHABLE("couldn't find value in EnumTable");
696 uint32_t nsAttrValue::GetAtomCount() const {
697 ValueType type
= Type();
703 if (type
== eAtomArray
) {
704 return GetAtomArrayValue()->Length();
710 nsAtom
* nsAttrValue::AtomAt(int32_t aIndex
) const {
711 MOZ_ASSERT(aIndex
>= 0, "Index must not be negative");
712 MOZ_ASSERT(GetAtomCount() > uint32_t(aIndex
), "aIndex out of range");
714 if (BaseType() == eAtomBase
) {
715 return GetAtomValue();
718 NS_ASSERTION(Type() == eAtomArray
, "GetAtomCount must be confused");
720 return GetAtomArrayValue()->ElementAt(aIndex
);
723 uint32_t nsAttrValue::HashValue() const {
724 switch (BaseType()) {
726 nsStringBuffer
* str
= static_cast<nsStringBuffer
*>(GetPtr());
728 uint32_t len
= str
->StorageSize() / sizeof(char16_t
) - 1;
729 return HashString(static_cast<char16_t
*>(str
->Data()), len
);
739 // mBits and uint32_t might have different size. This should silence
740 // any warnings or compile-errors. This is what the implementation of
741 // NS_PTR_TO_INT32 does to take care of the same problem.
746 MiscContainer
* cont
= GetMiscContainer();
747 if (static_cast<ValueBaseType
>(cont
->mStringBits
&
748 NS_ATTRVALUE_BASETYPE_MASK
) == eAtomBase
) {
749 return cont
->mStringBits
- 0;
752 switch (cont
->mType
) {
754 return cont
->mValue
.mInteger
;
757 return cont
->mValue
.mEnumValue
;
760 return cont
->mValue
.mPercent
;
763 return cont
->mValue
.mColor
;
765 case eCSSDeclaration
: {
766 return NS_PTR_TO_INT32(cont
->mValue
.mCSSDeclaration
);
771 return HashString(str
);
775 uint32_t count
= cont
->mValue
.mAtomArray
->Length();
776 for (RefPtr
<nsAtom
>*cur
= cont
->mValue
.mAtomArray
->Elements(),
779 hash
= AddToHash(hash
, cur
->get());
784 // XXX this is crappy, but oh well
785 return cont
->mDoubleValue
;
787 case eIntMarginValue
: {
788 return NS_PTR_TO_INT32(cont
->mValue
.mIntMargin
);
791 if (IsSVGType(cont
->mType
)) {
792 // All SVG types are just pointers to classes so we can treat them alike
793 return NS_PTR_TO_INT32(cont
->mValue
.mSVGLength
);
795 MOZ_ASSERT_UNREACHABLE("unknown type stored in MiscContainer");
801 bool nsAttrValue::Equals(const nsAttrValue
& aOther
) const {
802 if (BaseType() != aOther
.BaseType()) {
806 switch (BaseType()) {
808 return GetStringValue().Equals(aOther
.GetStringValue());
815 return mBits
== aOther
.mBits
;
819 MiscContainer
* thisCont
= GetMiscContainer();
820 MiscContainer
* otherCont
= aOther
.GetMiscContainer();
821 if (thisCont
== otherCont
) {
825 if (thisCont
->mType
!= otherCont
->mType
) {
829 bool needsStringComparison
= false;
831 switch (thisCont
->mType
) {
833 if (thisCont
->mValue
.mInteger
== otherCont
->mValue
.mInteger
) {
834 needsStringComparison
= true;
839 if (thisCont
->mValue
.mEnumValue
== otherCont
->mValue
.mEnumValue
) {
840 needsStringComparison
= true;
845 if (thisCont
->mValue
.mPercent
== otherCont
->mValue
.mPercent
) {
846 needsStringComparison
= true;
851 if (thisCont
->mValue
.mColor
== otherCont
->mValue
.mColor
) {
852 needsStringComparison
= true;
856 case eCSSDeclaration
: {
857 return thisCont
->mValue
.mCSSDeclaration
==
858 otherCont
->mValue
.mCSSDeclaration
;
861 return thisCont
->mValue
.mURL
== otherCont
->mValue
.mURL
;
864 // For classlists we could be insensitive to order, however
865 // classlists are never mapped attributes so they are never compared.
867 if (!(*thisCont
->mValue
.mAtomArray
== *otherCont
->mValue
.mAtomArray
)) {
871 needsStringComparison
= true;
875 return thisCont
->mDoubleValue
== otherCont
->mDoubleValue
;
877 case eIntMarginValue
: {
878 return thisCont
->mValue
.mIntMargin
== otherCont
->mValue
.mIntMargin
;
881 if (IsSVGType(thisCont
->mType
)) {
882 // Currently this method is never called for nsAttrValue objects that
883 // point to SVG data types.
884 // If that changes then we probably want to add methods to the
885 // corresponding SVG types to compare their base values.
886 // As a shortcut, however, we can begin by comparing the pointers.
887 MOZ_ASSERT(false, "Comparing nsAttrValues that point to SVG data");
890 MOZ_ASSERT_UNREACHABLE("unknown type stored in MiscContainer");
894 if (needsStringComparison
) {
895 if (thisCont
->mStringBits
== otherCont
->mStringBits
) {
898 if ((static_cast<ValueBaseType
>(thisCont
->mStringBits
&
899 NS_ATTRVALUE_BASETYPE_MASK
) ==
901 (static_cast<ValueBaseType
>(otherCont
->mStringBits
&
902 NS_ATTRVALUE_BASETYPE_MASK
) ==
904 return nsCheapString(reinterpret_cast<nsStringBuffer
*>(
905 static_cast<uintptr_t>(thisCont
->mStringBits
)))
906 .Equals(nsCheapString(reinterpret_cast<nsStringBuffer
*>(
907 static_cast<uintptr_t>(otherCont
->mStringBits
))));
913 bool nsAttrValue::Equals(const nsAString
& aValue
,
914 nsCaseTreatment aCaseSensitive
) const {
915 switch (BaseType()) {
917 nsStringBuffer
* str
= static_cast<nsStringBuffer
*>(GetPtr());
919 nsDependentString
dep(static_cast<char16_t
*>(str
->Data()),
920 str
->StorageSize() / sizeof(char16_t
) - 1);
921 return aCaseSensitive
== eCaseMatters
923 : nsContentUtils::EqualsIgnoreASCIICase(aValue
, dep
);
925 return aValue
.IsEmpty();
928 if (aCaseSensitive
== eCaseMatters
) {
929 return static_cast<nsAtom
*>(GetPtr())->Equals(aValue
);
931 return nsContentUtils::EqualsIgnoreASCIICase(
932 nsDependentAtomString(static_cast<nsAtom
*>(GetPtr())), aValue
);
939 return aCaseSensitive
== eCaseMatters
941 : nsContentUtils::EqualsIgnoreASCIICase(val
, aValue
);
944 bool nsAttrValue::Equals(const nsAtom
* aValue
,
945 nsCaseTreatment aCaseSensitive
) const {
946 if (aCaseSensitive
!= eCaseMatters
) {
947 // Need a better way to handle this!
949 aValue
->ToString(value
);
950 return Equals(value
, aCaseSensitive
);
953 switch (BaseType()) {
955 nsStringBuffer
* str
= static_cast<nsStringBuffer
*>(GetPtr());
957 nsDependentString
dep(static_cast<char16_t
*>(str
->Data()),
958 str
->StorageSize() / sizeof(char16_t
) - 1);
959 return aValue
->Equals(dep
);
961 return aValue
== nsGkAtoms::_empty
;
964 return static_cast<nsAtom
*>(GetPtr()) == aValue
;
972 return aValue
->Equals(val
);
975 bool nsAttrValue::EqualsAsStrings(const nsAttrValue
& aOther
) const {
976 if (Type() == aOther
.Type()) {
977 return Equals(aOther
);
980 // We need to serialize at least one nsAttrValue before passing to
981 // Equals(const nsAString&), but we can avoid unnecessarily serializing both
982 // by checking if one is already of a string type.
983 bool thisIsString
= (BaseType() == eStringBase
|| BaseType() == eAtomBase
);
984 const nsAttrValue
& lhs
= thisIsString
? *this : aOther
;
985 const nsAttrValue
& rhs
= thisIsString
? aOther
: *this;
987 switch (rhs
.BaseType()) {
989 return lhs
.Equals(rhs
.GetAtomValue(), eCaseMatters
);
992 return lhs
.Equals(rhs
.GetStringValue(), eCaseMatters
);
997 return lhs
.Equals(val
, eCaseMatters
);
1002 bool nsAttrValue::Contains(nsAtom
* aValue
,
1003 nsCaseTreatment aCaseSensitive
) const {
1004 switch (BaseType()) {
1006 nsAtom
* atom
= GetAtomValue();
1007 if (aCaseSensitive
== eCaseMatters
) {
1008 return aValue
== atom
;
1011 // For performance reasons, don't do a full on unicode case insensitive
1012 // string comparison. This is only used for quirks mode anyway.
1013 return nsContentUtils::EqualsIgnoreASCIICase(aValue
, atom
);
1016 if (Type() == eAtomArray
) {
1017 AtomArray
* array
= GetAtomArrayValue();
1018 if (aCaseSensitive
== eCaseMatters
) {
1019 return array
->Contains(aValue
);
1022 for (RefPtr
<nsAtom
>& cur
: *array
) {
1023 // For performance reasons, don't do a full on unicode case
1024 // insensitive string comparison. This is only used for quirks mode
1026 if (nsContentUtils::EqualsIgnoreASCIICase(aValue
, cur
)) {
1037 struct AtomArrayStringComparator
{
1038 bool Equals(nsAtom
* atom
, const nsAString
& string
) const {
1039 return atom
->Equals(string
);
1043 bool nsAttrValue::Contains(const nsAString
& aValue
) const {
1044 switch (BaseType()) {
1046 nsAtom
* atom
= GetAtomValue();
1047 return atom
->Equals(aValue
);
1050 if (Type() == eAtomArray
) {
1051 AtomArray
* array
= GetAtomArrayValue();
1052 return array
->Contains(aValue
, AtomArrayStringComparator());
1060 void nsAttrValue::ParseAtom(const nsAString
& aValue
) {
1063 RefPtr
<nsAtom
> atom
= NS_Atomize(aValue
);
1065 SetPtrValueAndType(atom
.forget().take(), eAtomBase
);
1069 void nsAttrValue::ParseAtomArray(const nsAString
& aValue
) {
1070 nsAString::const_iterator iter
, end
;
1071 aValue
.BeginReading(iter
);
1072 aValue
.EndReading(end
);
1073 bool hasSpace
= false;
1075 // skip initial whitespace
1076 while (iter
!= end
&& nsContentUtils::IsHTMLWhitespace(*iter
)) {
1086 nsAString::const_iterator
start(iter
);
1088 // get first - and often only - atom
1091 } while (iter
!= end
&& !nsContentUtils::IsHTMLWhitespace(*iter
));
1093 RefPtr
<nsAtom
> classAtom
= NS_AtomizeMainThread(Substring(start
, iter
));
1100 while (iter
!= end
&& nsContentUtils::IsHTMLWhitespace(*iter
)) {
1105 if (iter
== end
&& !hasSpace
) {
1106 // we only found one classname and there was no whitespace so
1107 // don't bother storing a list
1109 nsAtom
* atom
= nullptr;
1110 classAtom
.swap(atom
);
1111 SetPtrValueAndType(atom
, eAtomBase
);
1115 if (!EnsureEmptyAtomArray()) {
1119 AtomArray
* array
= GetAtomArrayValue();
1121 if (!array
->AppendElement(std::move(classAtom
))) {
1126 // parse the rest of the classnames
1127 while (iter
!= end
) {
1132 } while (iter
!= end
&& !nsContentUtils::IsHTMLWhitespace(*iter
));
1134 classAtom
= NS_AtomizeMainThread(Substring(start
, iter
));
1136 if (!array
->AppendElement(std::move(classAtom
))) {
1142 while (iter
!= end
&& nsContentUtils::IsHTMLWhitespace(*iter
)) {
1147 SetMiscAtomOrString(&aValue
);
1150 void nsAttrValue::ParseStringOrAtom(const nsAString
& aValue
) {
1151 uint32_t len
= aValue
.Length();
1152 // Don't bother with atoms if it's an empty string since
1153 // we can store those efficently anyway.
1154 if (len
&& len
<= NS_ATTRVALUE_MAX_STRINGLENGTH_ATOM
) {
1161 void nsAttrValue::SetIntValueAndType(int32_t aValue
, ValueType aType
,
1162 const nsAString
* aStringValue
) {
1163 if (aStringValue
|| aValue
> NS_ATTRVALUE_INTEGERTYPE_MAXVALUE
||
1164 aValue
< NS_ATTRVALUE_INTEGERTYPE_MINVALUE
) {
1165 MiscContainer
* cont
= EnsureEmptyMiscContainer();
1168 cont
->mValue
.mInteger
= aValue
;
1172 cont
->mValue
.mPercent
= aValue
;
1176 cont
->mValue
.mEnumValue
= aValue
;
1180 MOZ_ASSERT_UNREACHABLE("unknown integer type");
1184 cont
->mType
= aType
;
1185 SetMiscAtomOrString(aStringValue
);
1187 NS_ASSERTION(!mBits
, "Reset before calling SetIntValueAndType!");
1188 mBits
= (aValue
* NS_ATTRVALUE_INTEGERTYPE_MULTIPLIER
) | aType
;
1192 int16_t nsAttrValue::GetEnumTableIndex(const EnumTable
* aTable
) {
1193 int16_t index
= sEnumTableArray
->IndexOf(aTable
);
1195 index
= sEnumTableArray
->Length();
1196 NS_ASSERTION(index
<= NS_ATTRVALUE_ENUMTABLEINDEX_MAXVALUE
,
1197 "too many enum tables");
1198 sEnumTableArray
->AppendElement(aTable
);
1204 int32_t nsAttrValue::EnumTableEntryToValue(const EnumTable
* aEnumTable
,
1205 const EnumTable
* aTableEntry
) {
1206 int16_t index
= GetEnumTableIndex(aEnumTable
);
1208 (aTableEntry
->value
<< NS_ATTRVALUE_ENUMTABLEINDEX_BITS
) + index
;
1212 bool nsAttrValue::ParseEnumValue(const nsAString
& aValue
,
1213 const EnumTable
* aTable
, bool aCaseSensitive
,
1214 const EnumTable
* aDefaultValue
) {
1216 const EnumTable
* tableEntry
= aTable
;
1218 while (tableEntry
->tag
) {
1219 if (aCaseSensitive
? aValue
.EqualsASCII(tableEntry
->tag
)
1220 : aValue
.LowerCaseEqualsASCII(tableEntry
->tag
)) {
1221 int32_t value
= EnumTableEntryToValue(aTable
, tableEntry
);
1223 bool equals
= aCaseSensitive
|| aValue
.EqualsASCII(tableEntry
->tag
);
1226 tag
.AssignASCII(tableEntry
->tag
);
1227 nsContentUtils::ASCIIToUpper(tag
);
1228 if ((equals
= tag
.Equals(aValue
))) {
1229 value
|= NS_ATTRVALUE_ENUMTABLE_VALUE_NEEDS_TO_UPPER
;
1232 SetIntValueAndType(value
, eEnum
, equals
? nullptr : &aValue
);
1233 NS_ASSERTION(GetEnumValue() == tableEntry
->value
,
1234 "failed to store enum properly");
1241 if (aDefaultValue
) {
1242 MOZ_ASSERT(aTable
<= aDefaultValue
&& aDefaultValue
< tableEntry
,
1243 "aDefaultValue not inside aTable?");
1244 SetIntValueAndType(EnumTableEntryToValue(aTable
, aDefaultValue
), eEnum
,
1252 bool nsAttrValue::ParseSpecialIntValue(const nsAString
& aString
) {
1255 nsAutoString
tmp(aString
);
1256 nsContentUtils::ParseHTMLIntegerResultFlags result
;
1257 int32_t originalVal
= nsContentUtils::ParseHTMLInteger(aString
, &result
);
1259 if (result
& nsContentUtils::eParseHTMLInteger_Error
) {
1263 bool isPercent
= result
& nsContentUtils::eParseHTMLInteger_IsPercent
;
1264 int32_t val
= std::max(originalVal
, 0);
1266 val
!= originalVal
||
1267 (result
& nsContentUtils::eParseHTMLInteger_NonStandard
) ||
1268 (result
& nsContentUtils::eParseHTMLInteger_DidNotConsumeAllInput
);
1271 if (isPercent
|| tmp
.RFindChar('%') >= 0) {
1275 SetIntValueAndType(val
, isPercent
? ePercent
: eInteger
,
1276 nonStrict
? &aString
: nullptr);
1280 bool nsAttrValue::ParseIntWithBounds(const nsAString
& aString
, int32_t aMin
,
1282 MOZ_ASSERT(aMin
< aMax
, "bad boundaries");
1286 nsContentUtils::ParseHTMLIntegerResultFlags result
;
1287 int32_t originalVal
= nsContentUtils::ParseHTMLInteger(aString
, &result
);
1288 if (result
& nsContentUtils::eParseHTMLInteger_Error
) {
1292 int32_t val
= std::max(originalVal
, aMin
);
1293 val
= std::min(val
, aMax
);
1295 (val
!= originalVal
) ||
1296 (result
& nsContentUtils::eParseHTMLInteger_IsPercent
) ||
1297 (result
& nsContentUtils::eParseHTMLInteger_NonStandard
) ||
1298 (result
& nsContentUtils::eParseHTMLInteger_DidNotConsumeAllInput
);
1300 SetIntValueAndType(val
, eInteger
, nonStrict
? &aString
: nullptr);
1305 void nsAttrValue::ParseIntWithFallback(const nsAString
& aString
,
1306 int32_t aDefault
, int32_t aMax
) {
1309 nsContentUtils::ParseHTMLIntegerResultFlags result
;
1310 int32_t val
= nsContentUtils::ParseHTMLInteger(aString
, &result
);
1311 bool nonStrict
= false;
1312 if ((result
& nsContentUtils::eParseHTMLInteger_Error
) || val
< 1) {
1322 if ((result
& nsContentUtils::eParseHTMLInteger_IsPercent
) ||
1323 (result
& nsContentUtils::eParseHTMLInteger_NonStandard
) ||
1324 (result
& nsContentUtils::eParseHTMLInteger_DidNotConsumeAllInput
)) {
1328 SetIntValueAndType(val
, eInteger
, nonStrict
? &aString
: nullptr);
1331 void nsAttrValue::ParseClampedNonNegativeInt(const nsAString
& aString
,
1332 int32_t aDefault
, int32_t aMin
,
1336 nsContentUtils::ParseHTMLIntegerResultFlags result
;
1337 int32_t val
= nsContentUtils::ParseHTMLInteger(aString
, &result
);
1339 (result
& nsContentUtils::eParseHTMLInteger_IsPercent
) ||
1340 (result
& nsContentUtils::eParseHTMLInteger_NonStandard
) ||
1341 (result
& nsContentUtils::eParseHTMLInteger_DidNotConsumeAllInput
);
1343 if (result
& nsContentUtils::eParseHTMLInteger_ErrorOverflow
) {
1344 if (result
& nsContentUtils::eParseHTMLInteger_Negative
) {
1350 } else if ((result
& nsContentUtils::eParseHTMLInteger_Error
) || val
< 0) {
1353 } else if (val
< aMin
) {
1356 } else if (val
> aMax
) {
1361 SetIntValueAndType(val
, eInteger
, nonStrict
? &aString
: nullptr);
1364 bool nsAttrValue::ParseNonNegativeIntValue(const nsAString
& aString
) {
1367 nsContentUtils::ParseHTMLIntegerResultFlags result
;
1368 int32_t originalVal
= nsContentUtils::ParseHTMLInteger(aString
, &result
);
1369 if ((result
& nsContentUtils::eParseHTMLInteger_Error
) || originalVal
< 0) {
1374 (result
& nsContentUtils::eParseHTMLInteger_IsPercent
) ||
1375 (result
& nsContentUtils::eParseHTMLInteger_NonStandard
) ||
1376 (result
& nsContentUtils::eParseHTMLInteger_DidNotConsumeAllInput
);
1378 SetIntValueAndType(originalVal
, eInteger
, nonStrict
? &aString
: nullptr);
1383 bool nsAttrValue::ParsePositiveIntValue(const nsAString
& aString
) {
1386 nsContentUtils::ParseHTMLIntegerResultFlags result
;
1387 int32_t originalVal
= nsContentUtils::ParseHTMLInteger(aString
, &result
);
1388 if ((result
& nsContentUtils::eParseHTMLInteger_Error
) || originalVal
<= 0) {
1393 (result
& nsContentUtils::eParseHTMLInteger_IsPercent
) ||
1394 (result
& nsContentUtils::eParseHTMLInteger_NonStandard
) ||
1395 (result
& nsContentUtils::eParseHTMLInteger_DidNotConsumeAllInput
);
1397 SetIntValueAndType(originalVal
, eInteger
, nonStrict
? &aString
: nullptr);
1402 void nsAttrValue::SetColorValue(nscolor aColor
, const nsAString
& aString
) {
1403 nsStringBuffer
* buf
= GetStringBuffer(aString
).take();
1408 MiscContainer
* cont
= EnsureEmptyMiscContainer();
1409 cont
->mValue
.mColor
= aColor
;
1410 cont
->mType
= eColor
;
1412 // Save the literal string we were passed for round-tripping.
1413 cont
->SetStringBitsMainThread(reinterpret_cast<uintptr_t>(buf
) | eStringBase
);
1416 bool nsAttrValue::ParseColor(const nsAString
& aString
) {
1419 // FIXME (partially, at least): HTML5's algorithm says we shouldn't do
1420 // the whitespace compression, trimming, or the test for emptiness.
1421 // (I'm a little skeptical that we shouldn't do the whitespace
1422 // trimming; WebKit also does it.)
1423 nsAutoString
colorStr(aString
);
1424 colorStr
.CompressWhitespace(true, true);
1425 if (colorStr
.IsEmpty()) {
1430 // No color names begin with a '#'; in standards mode, all acceptable
1431 // numeric colors do.
1432 if (colorStr
.First() == '#') {
1433 nsDependentString
withoutHash(colorStr
.get() + 1, colorStr
.Length() - 1);
1434 if (NS_HexToRGBA(withoutHash
, nsHexColorType::NoAlpha
, &color
)) {
1435 SetColorValue(color
, aString
);
1439 if (NS_ColorNameToRGB(colorStr
, &color
)) {
1440 SetColorValue(color
, aString
);
1445 // FIXME (maybe): HTML5 says we should handle system colors. This
1446 // means we probably need another storage type, since we'd need to
1447 // handle dynamic changes. However, I think this is a bad idea:
1448 // http://lists.whatwg.org/pipermail/whatwg-whatwg.org/2010-May/026449.html
1450 // Use NS_LooseHexToRGB as a fallback if nothing above worked.
1451 if (NS_LooseHexToRGB(colorStr
, &color
)) {
1452 SetColorValue(color
, aString
);
1459 bool nsAttrValue::ParseDoubleValue(const nsAString
& aString
) {
1463 double val
= PromiseFlatString(aString
).ToDouble(&ec
);
1464 if (NS_FAILED(ec
)) {
1468 MiscContainer
* cont
= EnsureEmptyMiscContainer();
1469 cont
->mDoubleValue
= val
;
1470 cont
->mType
= eDoubleValue
;
1471 nsAutoString serializedFloat
;
1472 serializedFloat
.AppendFloat(val
);
1473 SetMiscAtomOrString(serializedFloat
.Equals(aString
) ? nullptr : &aString
);
1477 bool nsAttrValue::ParseIntMarginValue(const nsAString
& aString
) {
1480 nsIntMargin margins
;
1481 if (!nsContentUtils::ParseIntMarginValue(aString
, margins
)) return false;
1483 MiscContainer
* cont
= EnsureEmptyMiscContainer();
1484 cont
->mValue
.mIntMargin
= new nsIntMargin(margins
);
1485 cont
->mType
= eIntMarginValue
;
1486 SetMiscAtomOrString(&aString
);
1490 bool nsAttrValue::ParseStyleAttribute(const nsAString
& aString
,
1491 nsIPrincipal
* aMaybeScriptedPrincipal
,
1492 nsStyledElement
* aElement
) {
1493 dom::Document
* ownerDoc
= aElement
->OwnerDoc();
1494 nsHTMLCSSStyleSheet
* sheet
= ownerDoc
->GetInlineStyleSheet();
1495 nsIURI
* baseURI
= aElement
->GetBaseURIForStyleAttr();
1496 nsIURI
* docURI
= ownerDoc
->GetDocumentURI();
1498 NS_ASSERTION(aElement
->NodePrincipal() == ownerDoc
->NodePrincipal(),
1499 "This is unexpected");
1501 nsIPrincipal
* principal
= aMaybeScriptedPrincipal
? aMaybeScriptedPrincipal
1502 : aElement
->NodePrincipal();
1504 // If the (immutable) document URI does not match the element's base URI
1505 // (the common case is that they do match) do not cache the rule. This is
1506 // because the results of the CSS parser are dependent on these URIs, and we
1507 // do not want to have to account for the URIs in the hash lookup.
1508 // Similarly, if the triggering principal does not match the node principal,
1509 // do not cache the rule, since the principal will be encoded in any parsed
1510 // URLs in the rule.
1511 const bool cachingAllowed
=
1512 sheet
&& baseURI
== docURI
&& principal
== aElement
->NodePrincipal();
1513 if (cachingAllowed
) {
1514 MiscContainer
* cont
= sheet
->LookupStyleAttr(aString
);
1516 // Set our MiscContainer to the cached one.
1518 SetPtrValueAndType(cont
, eOtherBase
);
1523 RefPtr
<URLExtraData
> data
= new URLExtraData(baseURI
, docURI
, principal
,
1524 ownerDoc
->GetReferrerPolicy());
1525 RefPtr
<DeclarationBlock
> decl
= DeclarationBlock::FromCssText(
1526 aString
, data
, ownerDoc
->GetCompatibilityMode(), ownerDoc
->CSSLoader());
1530 decl
->SetHTMLCSSStyleSheet(sheet
);
1531 SetTo(decl
.forget(), &aString
);
1533 if (cachingAllowed
) {
1534 MiscContainer
* cont
= GetMiscContainer();
1541 void nsAttrValue::SetMiscAtomOrString(const nsAString
* aValue
) {
1542 NS_ASSERTION(GetMiscContainer(), "Must have MiscContainer!");
1543 NS_ASSERTION(!GetMiscContainer()->mStringBits
|| IsInServoTraversal(),
1544 "Trying to re-set atom or string!");
1546 uint32_t len
= aValue
->Length();
1547 // * We're allowing eCSSDeclaration attributes to store empty
1548 // strings as it can be beneficial to store an empty style
1549 // attribute as a parsed rule.
1550 // * We're allowing enumerated values because sometimes the empty
1551 // string corresponds to a particular enumerated value, especially
1552 // for enumerated values that are not limited enumerated.
1553 // Add other types as needed.
1554 NS_ASSERTION(len
|| Type() == eCSSDeclaration
|| Type() == eEnum
,
1556 MiscContainer
* cont
= GetMiscContainer();
1558 if (len
<= NS_ATTRVALUE_MAX_STRINGLENGTH_ATOM
) {
1559 nsAtom
* atom
= MOZ_LIKELY(!IsInServoTraversal())
1560 ? NS_AtomizeMainThread(*aValue
).take()
1561 : NS_Atomize(*aValue
).take();
1562 NS_ENSURE_TRUE_VOID(atom
);
1563 uintptr_t bits
= reinterpret_cast<uintptr_t>(atom
) | eAtomBase
;
1565 // In the common case we're not in the servo traversal, and we can just
1566 // set the bits normally. The parallel case requires more care.
1567 if (MOZ_LIKELY(!IsInServoTraversal())) {
1568 cont
->SetStringBitsMainThread(bits
);
1569 } else if (!cont
->mStringBits
.compareExchange(0, bits
)) {
1570 // We raced with somebody else setting the bits. Release our copy.
1574 nsStringBuffer
* buffer
= GetStringBuffer(*aValue
).take();
1575 NS_ENSURE_TRUE_VOID(buffer
);
1576 uintptr_t bits
= reinterpret_cast<uintptr_t>(buffer
) | eStringBase
;
1578 // In the common case we're not in the servo traversal, and we can just
1579 // set the bits normally. The parallel case requires more care.
1580 if (MOZ_LIKELY(!IsInServoTraversal())) {
1581 cont
->SetStringBitsMainThread(bits
);
1582 } else if (!cont
->mStringBits
.compareExchange(0, bits
)) {
1583 // We raced with somebody else setting the bits. Release our copy.
1590 void nsAttrValue::ResetMiscAtomOrString() {
1591 MiscContainer
* cont
= GetMiscContainer();
1592 void* ptr
= MISC_STR_PTR(cont
);
1594 if (static_cast<ValueBaseType
>(cont
->mStringBits
&
1595 NS_ATTRVALUE_BASETYPE_MASK
) == eStringBase
) {
1596 static_cast<nsStringBuffer
*>(ptr
)->Release();
1598 static_cast<nsAtom
*>(ptr
)->Release();
1600 cont
->SetStringBitsMainThread(0);
1604 void nsAttrValue::SetSVGType(ValueType aType
, const void* aValue
,
1605 const nsAString
* aSerialized
) {
1606 MOZ_ASSERT(IsSVGType(aType
), "Not an SVG type");
1608 MiscContainer
* cont
= EnsureEmptyMiscContainer();
1609 // All SVG types are just pointers to classes so just setting any of them
1610 // will do. We'll lose type-safety but the signature of the calling
1611 // function should ensure we don't get anything unexpected, and once we
1612 // stick aValue in a union we lose type information anyway.
1613 cont
->mValue
.mSVGLength
= static_cast<const SVGAnimatedLength
*>(aValue
);
1614 cont
->mType
= aType
;
1615 SetMiscAtomOrString(aSerialized
);
1618 MiscContainer
* nsAttrValue::ClearMiscContainer() {
1619 MiscContainer
* cont
= nullptr;
1620 if (BaseType() == eOtherBase
) {
1621 cont
= GetMiscContainer();
1622 if (cont
->IsRefCounted() && cont
->mValue
.mRefCount
> 1) {
1623 // This MiscContainer is shared, we need a new one.
1626 cont
= AllocMiscContainer();
1627 SetPtrValueAndType(cont
, eOtherBase
);
1629 switch (cont
->mType
) {
1630 case eCSSDeclaration
: {
1631 MOZ_ASSERT(cont
->mValue
.mRefCount
== 1);
1634 NS_RELEASE(cont
->mValue
.mCSSDeclaration
);
1638 NS_RELEASE(cont
->mValue
.mURL
);
1642 delete cont
->mValue
.mAtomArray
;
1645 case eIntMarginValue
: {
1646 delete cont
->mValue
.mIntMargin
;
1654 ResetMiscAtomOrString();
1662 MiscContainer
* nsAttrValue::EnsureEmptyMiscContainer() {
1663 MiscContainer
* cont
= ClearMiscContainer();
1665 MOZ_ASSERT(BaseType() == eOtherBase
);
1666 ResetMiscAtomOrString();
1667 cont
= GetMiscContainer();
1669 cont
= AllocMiscContainer();
1670 SetPtrValueAndType(cont
, eOtherBase
);
1676 bool nsAttrValue::EnsureEmptyAtomArray() {
1677 if (Type() == eAtomArray
) {
1678 ResetMiscAtomOrString();
1679 GetAtomArrayValue()->Clear();
1683 MiscContainer
* cont
= EnsureEmptyMiscContainer();
1684 cont
->mValue
.mAtomArray
= new AtomArray
;
1685 cont
->mType
= eAtomArray
;
1690 already_AddRefed
<nsStringBuffer
> nsAttrValue::GetStringBuffer(
1691 const nsAString
& aValue
) const {
1692 uint32_t len
= aValue
.Length();
1697 RefPtr
<nsStringBuffer
> buf
= nsStringBuffer::FromString(aValue
);
1698 if (buf
&& (buf
->StorageSize() / sizeof(char16_t
) - 1) == len
) {
1699 return buf
.forget();
1702 buf
= nsStringBuffer::Alloc((len
+ 1) * sizeof(char16_t
));
1706 char16_t
* data
= static_cast<char16_t
*>(buf
->Data());
1707 CopyUnicodeTo(aValue
, 0, data
, len
);
1708 data
[len
] = char16_t(0);
1709 return buf
.forget();
1712 size_t nsAttrValue::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf
) const {
1715 switch (BaseType()) {
1717 nsStringBuffer
* str
= static_cast<nsStringBuffer
*>(GetPtr());
1718 n
+= str
? str
->SizeOfIncludingThisIfUnshared(aMallocSizeOf
) : 0;
1722 MiscContainer
* container
= GetMiscContainer();
1726 if (container
->IsRefCounted() && container
->mValue
.mRefCount
> 1) {
1727 // We don't report this MiscContainer at all in order to avoid
1728 // twice-reporting it.
1729 // TODO DMD, bug 1027551 - figure out how to report this ref-counted
1730 // object just once.
1733 n
+= aMallocSizeOf(container
);
1735 void* otherPtr
= MISC_STR_PTR(container
);
1736 // We only count the size of the object pointed by otherPtr if it's a
1737 // string. When it's an atom, it's counted separatly.
1738 if (otherPtr
&& static_cast<ValueBaseType
>(container
->mStringBits
&
1739 NS_ATTRVALUE_BASETYPE_MASK
) ==
1741 nsStringBuffer
* str
= static_cast<nsStringBuffer
*>(otherPtr
);
1742 n
+= str
? str
->SizeOfIncludingThisIfUnshared(aMallocSizeOf
) : 0;
1745 if (Type() == eCSSDeclaration
&& container
->mValue
.mCSSDeclaration
) {
1746 // TODO: mCSSDeclaration might be owned by another object which
1747 // would make us count them twice, bug 677493.
1748 // Bug 1281964: For DeclarationBlock if we do measure we'll
1749 // need a way to call the Servo heap_size_of function.
1750 // n += container->mCSSDeclaration->SizeOfIncludingThis(aMallocSizeOf);
1751 } else if (Type() == eAtomArray
&& container
->mValue
.mAtomArray
) {
1752 // Don't measure each nsAtom, they are measured separatly.
1753 n
+= container
->mValue
.mAtomArray
->ShallowSizeOfIncludingThis(
1758 case eAtomBase
: // Atoms are counted separately.
1759 case eIntegerBase
: // The value is in mBits, nothing to do.