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/BloomFilter.h"
20 #include "mozilla/CORSMode.h"
21 #include "mozilla/MemoryReporting.h"
22 #include "mozilla/ServoBindingTypes.h"
23 #include "mozilla/ServoUtils.h"
24 #include "mozilla/ShadowParts.h"
25 #include "mozilla/SVGAttrValueWrapper.h"
26 #include "mozilla/DeclarationBlock.h"
27 #include "mozilla/dom/CSSRuleBinding.h"
28 #include "mozilla/dom/Document.h"
29 #include "nsContentUtils.h"
30 #include "nsReadableUtils.h"
31 #include "nsHTMLCSSStyleSheet.h"
32 #include "nsStyledElement.h"
34 #include "ReferrerInfo.h"
37 using namespace mozilla
;
39 #define MISC_STR_PTR(_cont) \
40 reinterpret_cast<void*>((_cont)->mStringBits & NS_ATTRVALUE_POINTERVALUE_MASK)
43 MiscContainer
* nsAttrValue::AllocMiscContainer() {
44 MOZ_ASSERT(NS_IsMainThread());
45 MiscContainer
* cont
= nullptr;
46 std::swap(cont
, sMiscContainerCache
);
49 return new (cont
) MiscContainer
;
52 return new MiscContainer
;
56 void nsAttrValue::DeallocMiscContainer(MiscContainer
* aCont
) {
57 MOZ_ASSERT(NS_IsMainThread());
62 if (!sMiscContainerCache
) {
63 aCont
->~MiscContainer();
64 sMiscContainerCache
= aCont
;
70 bool MiscContainer::GetString(nsAString
& aString
) const {
71 void* ptr
= MISC_STR_PTR(this);
77 if (static_cast<nsAttrValue::ValueBaseType
>(mStringBits
&
78 NS_ATTRVALUE_BASETYPE_MASK
) ==
79 nsAttrValue::eStringBase
) {
80 nsStringBuffer
* buffer
= static_cast<nsStringBuffer
*>(ptr
);
85 buffer
->ToString(buffer
->StorageSize() / sizeof(char16_t
) - 1, aString
);
89 nsAtom
* atom
= static_cast<nsAtom
*>(ptr
);
94 atom
->ToString(aString
);
98 void MiscContainer::Cache() {
99 // Not implemented for anything else yet.
100 if (mType
!= nsAttrValue::eCSSDeclaration
) {
101 MOZ_ASSERT_UNREACHABLE("unexpected cached nsAttrValue type");
105 MOZ_ASSERT(IsRefCounted());
106 MOZ_ASSERT(mValue
.mRefCount
> 0);
107 MOZ_ASSERT(!mValue
.mCached
);
109 nsHTMLCSSStyleSheet
* sheet
= mValue
.mCSSDeclaration
->GetHTMLCSSStyleSheet();
115 bool gotString
= GetString(str
);
120 sheet
->CacheStyleAttr(str
, this);
123 // This has to be immutable once it goes into the cache.
124 mValue
.mCSSDeclaration
->SetImmutable();
127 void MiscContainer::Evict() {
128 // Not implemented for anything else yet.
129 if (mType
!= nsAttrValue::eCSSDeclaration
) {
130 MOZ_ASSERT_UNREACHABLE("unexpected cached nsAttrValue type");
133 MOZ_ASSERT(IsRefCounted());
134 MOZ_ASSERT(mValue
.mRefCount
== 0);
136 if (!mValue
.mCached
) {
140 nsHTMLCSSStyleSheet
* sheet
= mValue
.mCSSDeclaration
->GetHTMLCSSStyleSheet();
144 DebugOnly
<bool> gotString
= GetString(str
);
145 MOZ_ASSERT(gotString
);
147 sheet
->EvictStyleAttr(str
, this);
151 nsTArray
<const nsAttrValue::EnumTable
*>* nsAttrValue::sEnumTableArray
= nullptr;
152 MiscContainer
* nsAttrValue::sMiscContainerCache
= nullptr;
154 nsAttrValue::nsAttrValue() : mBits(0) {}
156 nsAttrValue::nsAttrValue(const nsAttrValue
& aOther
) : mBits(0) {
160 nsAttrValue::nsAttrValue(const nsAString
& aValue
) : mBits(0) { SetTo(aValue
); }
162 nsAttrValue::nsAttrValue(nsAtom
* aValue
) : mBits(0) { SetTo(aValue
); }
164 nsAttrValue::nsAttrValue(already_AddRefed
<DeclarationBlock
> aValue
,
165 const nsAString
* aSerialized
)
167 SetTo(std::move(aValue
), aSerialized
);
170 nsAttrValue::nsAttrValue(const nsIntMargin
& aValue
) : mBits(0) {
174 nsAttrValue::~nsAttrValue() { ResetIfSet(); }
177 void nsAttrValue::Init() {
178 MOZ_ASSERT(!sEnumTableArray
, "nsAttrValue already initialized");
179 sEnumTableArray
= new nsTArray
<const EnumTable
*>;
183 void nsAttrValue::Shutdown() {
184 MOZ_ASSERT(NS_IsMainThread());
185 delete sEnumTableArray
;
186 sEnumTableArray
= nullptr;
187 // The MiscContainer pointed to by sMiscContainerCache has already
188 // be destructed so `delete sMiscContainerCache` is
189 // dangerous. Invoke `operator delete` to free the memory.
190 ::operator delete(sMiscContainerCache
);
191 sMiscContainerCache
= nullptr;
194 void nsAttrValue::Reset() {
195 switch (BaseType()) {
197 nsStringBuffer
* str
= static_cast<nsStringBuffer
*>(GetPtr());
205 MiscContainer
* cont
= GetMiscContainer();
206 if (cont
->IsRefCounted() && cont
->mValue
.mRefCount
> 1) {
211 DeallocMiscContainer(ClearMiscContainer());
216 nsAtom
* atom
= GetAtomValue();
229 void nsAttrValue::SetTo(const nsAttrValue
& aOther
) {
230 if (this == &aOther
) {
234 switch (aOther
.BaseType()) {
237 nsStringBuffer
* str
= static_cast<nsStringBuffer
*>(aOther
.GetPtr());
240 SetPtrValueAndType(str
, eStringBase
);
249 nsAtom
* atom
= aOther
.GetAtomValue();
251 SetPtrValueAndType(atom
, eAtomBase
);
256 mBits
= aOther
.mBits
;
261 MiscContainer
* otherCont
= aOther
.GetMiscContainer();
262 if (otherCont
->IsRefCounted()) {
263 DeallocMiscContainer(ClearMiscContainer());
264 NS_ADDREF(otherCont
);
265 SetPtrValueAndType(otherCont
, eOtherBase
);
269 MiscContainer
* cont
= EnsureEmptyMiscContainer();
270 switch (otherCont
->mType
) {
272 cont
->mValue
.mInteger
= otherCont
->mValue
.mInteger
;
276 cont
->mValue
.mEnumValue
= otherCont
->mValue
.mEnumValue
;
280 cont
->mDoubleValue
= otherCont
->mDoubleValue
;
284 cont
->mValue
.mColor
= otherCont
->mValue
.mColor
;
288 case eCSSDeclaration
: {
289 MOZ_CRASH("These should be refcounted!");
292 NS_ADDREF(cont
->mValue
.mURL
= otherCont
->mValue
.mURL
);
296 if (!EnsureEmptyAtomArray()) {
300 // XXX(Bug 1631371) Check if this should use a fallible operation as it
301 // pretended earlier.
302 *GetAtomArrayValue() = otherCont
->mValue
.mAtomArray
->Clone();
307 cont
->mDoubleValue
= otherCont
->mDoubleValue
;
310 case eIntMarginValue
: {
311 if (otherCont
->mValue
.mIntMargin
) {
312 cont
->mValue
.mIntMargin
=
313 new nsIntMargin(*otherCont
->mValue
.mIntMargin
);
318 if (IsSVGType(otherCont
->mType
)) {
319 // All SVG types are just pointers to classes and will therefore have
320 // the same size so it doesn't really matter which one we assign
321 cont
->mValue
.mSVGLength
= otherCont
->mValue
.mSVGLength
;
323 MOZ_ASSERT_UNREACHABLE("unknown type stored in MiscContainer");
329 void* otherPtr
= MISC_STR_PTR(otherCont
);
331 if (static_cast<ValueBaseType
>(otherCont
->mStringBits
&
332 NS_ATTRVALUE_BASETYPE_MASK
) == eStringBase
) {
333 static_cast<nsStringBuffer
*>(otherPtr
)->AddRef();
335 static_cast<nsAtom
*>(otherPtr
)->AddRef();
337 cont
->SetStringBitsMainThread(otherCont
->mStringBits
);
339 // Note, set mType after switch-case, otherwise EnsureEmptyAtomArray doesn't
341 cont
->mType
= otherCont
->mType
;
344 void nsAttrValue::SetTo(const nsAString
& aValue
) {
346 nsStringBuffer
* buf
= GetStringBuffer(aValue
).take();
348 SetPtrValueAndType(buf
, eStringBase
);
352 void nsAttrValue::SetTo(nsAtom
* aValue
) {
356 SetPtrValueAndType(aValue
, eAtomBase
);
360 void nsAttrValue::SetTo(int16_t aInt
) {
362 SetIntValueAndType(aInt
, eInteger
, nullptr);
365 void nsAttrValue::SetTo(int32_t aInt
, const nsAString
* aSerialized
) {
367 SetIntValueAndType(aInt
, eInteger
, aSerialized
);
370 void nsAttrValue::SetTo(double aValue
, const nsAString
* aSerialized
) {
371 MiscContainer
* cont
= EnsureEmptyMiscContainer();
372 cont
->mDoubleValue
= aValue
;
373 cont
->mType
= eDoubleValue
;
374 SetMiscAtomOrString(aSerialized
);
377 void nsAttrValue::SetTo(already_AddRefed
<DeclarationBlock
> aValue
,
378 const nsAString
* aSerialized
) {
379 MiscContainer
* cont
= EnsureEmptyMiscContainer();
380 MOZ_ASSERT(cont
->mValue
.mRefCount
== 0);
381 cont
->mValue
.mCSSDeclaration
= aValue
.take();
382 cont
->mType
= eCSSDeclaration
;
384 SetMiscAtomOrString(aSerialized
);
385 MOZ_ASSERT(cont
->mValue
.mRefCount
== 1);
388 void nsAttrValue::SetTo(nsIURI
* aValue
, const nsAString
* aSerialized
) {
389 MiscContainer
* cont
= EnsureEmptyMiscContainer();
390 NS_ADDREF(cont
->mValue
.mURL
= aValue
);
392 SetMiscAtomOrString(aSerialized
);
395 void nsAttrValue::SetTo(const nsIntMargin
& aValue
) {
396 MiscContainer
* cont
= EnsureEmptyMiscContainer();
397 cont
->mValue
.mIntMargin
= new nsIntMargin(aValue
);
398 cont
->mType
= eIntMarginValue
;
401 void nsAttrValue::SetToSerialized(const nsAttrValue
& aOther
) {
402 if (aOther
.Type() != nsAttrValue::eString
&&
403 aOther
.Type() != nsAttrValue::eAtom
) {
405 aOther
.ToString(val
);
412 void nsAttrValue::SetTo(const SVGAnimatedOrient
& aValue
,
413 const nsAString
* aSerialized
) {
414 SetSVGType(eSVGOrient
, &aValue
, aSerialized
);
417 void nsAttrValue::SetTo(const SVGAnimatedIntegerPair
& aValue
,
418 const nsAString
* aSerialized
) {
419 SetSVGType(eSVGIntegerPair
, &aValue
, aSerialized
);
422 void nsAttrValue::SetTo(const SVGAnimatedLength
& aValue
,
423 const nsAString
* aSerialized
) {
424 SetSVGType(eSVGLength
, &aValue
, aSerialized
);
427 void nsAttrValue::SetTo(const SVGLengthList
& aValue
,
428 const nsAString
* aSerialized
) {
429 // While an empty string will parse as a length list, there's no need to store
430 // it (and SetMiscAtomOrString will assert if we try)
431 if (aSerialized
&& aSerialized
->IsEmpty()) {
432 aSerialized
= nullptr;
434 SetSVGType(eSVGLengthList
, &aValue
, aSerialized
);
437 void nsAttrValue::SetTo(const SVGNumberList
& aValue
,
438 const nsAString
* aSerialized
) {
439 // While an empty string will parse as a number list, there's no need to store
440 // it (and SetMiscAtomOrString will assert if we try)
441 if (aSerialized
&& aSerialized
->IsEmpty()) {
442 aSerialized
= nullptr;
444 SetSVGType(eSVGNumberList
, &aValue
, aSerialized
);
447 void nsAttrValue::SetTo(const SVGAnimatedNumberPair
& aValue
,
448 const nsAString
* aSerialized
) {
449 SetSVGType(eSVGNumberPair
, &aValue
, aSerialized
);
452 void nsAttrValue::SetTo(const SVGPathData
& aValue
,
453 const nsAString
* aSerialized
) {
454 // While an empty string will parse as path data, there's no need to store it
455 // (and SetMiscAtomOrString will assert if we try)
456 if (aSerialized
&& aSerialized
->IsEmpty()) {
457 aSerialized
= nullptr;
459 SetSVGType(eSVGPathData
, &aValue
, aSerialized
);
462 void nsAttrValue::SetTo(const SVGPointList
& aValue
,
463 const nsAString
* aSerialized
) {
464 // While an empty string will parse as a point list, there's no need to store
465 // it (and SetMiscAtomOrString will assert if we try)
466 if (aSerialized
&& aSerialized
->IsEmpty()) {
467 aSerialized
= nullptr;
469 SetSVGType(eSVGPointList
, &aValue
, aSerialized
);
472 void nsAttrValue::SetTo(const SVGAnimatedPreserveAspectRatio
& aValue
,
473 const nsAString
* aSerialized
) {
474 SetSVGType(eSVGPreserveAspectRatio
, &aValue
, aSerialized
);
477 void nsAttrValue::SetTo(const SVGStringList
& aValue
,
478 const nsAString
* aSerialized
) {
479 // While an empty string will parse as a string list, there's no need to store
480 // it (and SetMiscAtomOrString will assert if we try)
481 if (aSerialized
&& aSerialized
->IsEmpty()) {
482 aSerialized
= nullptr;
484 SetSVGType(eSVGStringList
, &aValue
, aSerialized
);
487 void nsAttrValue::SetTo(const SVGTransformList
& aValue
,
488 const nsAString
* aSerialized
) {
489 // While an empty string will parse as a transform list, there's no need to
490 // store it (and SetMiscAtomOrString will assert if we try)
491 if (aSerialized
&& aSerialized
->IsEmpty()) {
492 aSerialized
= nullptr;
494 SetSVGType(eSVGTransformList
, &aValue
, aSerialized
);
497 void nsAttrValue::SetTo(const SVGAnimatedViewBox
& aValue
,
498 const nsAString
* aSerialized
) {
499 SetSVGType(eSVGViewBox
, &aValue
, aSerialized
);
502 void nsAttrValue::SwapValueWith(nsAttrValue
& aOther
) {
503 uintptr_t tmp
= aOther
.mBits
;
504 aOther
.mBits
= mBits
;
508 void nsAttrValue::ToString(nsAString
& aResult
) const {
509 MiscContainer
* cont
= nullptr;
510 if (BaseType() == eOtherBase
) {
511 cont
= GetMiscContainer();
513 if (cont
->GetString(aResult
)) {
520 nsStringBuffer
* str
= static_cast<nsStringBuffer
*>(GetPtr());
522 str
->ToString(str
->StorageSize() / sizeof(char16_t
) - 1, aResult
);
529 nsAtom
* atom
= static_cast<nsAtom
*>(GetPtr());
530 atom
->ToString(aResult
);
536 intStr
.AppendInt(GetIntegerValue());
543 MOZ_ASSERT_UNREACHABLE("color attribute without string data");
549 GetEnumString(aResult
, false);
555 str
.AppendFloat(cont
->mDoubleValue
);
557 str
.AppendInt(GetIntInternal());
559 aResult
= str
+ u
"%"_ns
;
563 case eCSSDeclaration
: {
565 MiscContainer
* container
= GetMiscContainer();
566 if (DeclarationBlock
* decl
= container
->mValue
.mCSSDeclaration
) {
567 nsAutoCString result
;
568 decl
->ToString(result
);
569 CopyUTF8toUTF16(result
, aResult
);
572 // This can be reached during parallel selector matching with attribute
573 // selectors on the style attribute. SetMiscAtomOrString handles this
574 // case, and as of this writing this is the only consumer that needs it.
575 const_cast<nsAttrValue
*>(this)->SetMiscAtomOrString(&aResult
);
581 aResult
.AppendFloat(GetDoubleValue());
584 case eSVGIntegerPair
: {
585 SVGAttrValueWrapper::ToString(
586 GetMiscContainer()->mValue
.mSVGAnimatedIntegerPair
, aResult
);
590 SVGAttrValueWrapper::ToString(
591 GetMiscContainer()->mValue
.mSVGAnimatedOrient
, aResult
);
595 SVGAttrValueWrapper::ToString(GetMiscContainer()->mValue
.mSVGLength
,
599 case eSVGLengthList
: {
600 SVGAttrValueWrapper::ToString(GetMiscContainer()->mValue
.mSVGLengthList
,
604 case eSVGNumberList
: {
605 SVGAttrValueWrapper::ToString(GetMiscContainer()->mValue
.mSVGNumberList
,
609 case eSVGNumberPair
: {
610 SVGAttrValueWrapper::ToString(
611 GetMiscContainer()->mValue
.mSVGAnimatedNumberPair
, aResult
);
615 SVGAttrValueWrapper::ToString(GetMiscContainer()->mValue
.mSVGPathData
,
619 case eSVGPointList
: {
620 SVGAttrValueWrapper::ToString(GetMiscContainer()->mValue
.mSVGPointList
,
624 case eSVGPreserveAspectRatio
: {
625 SVGAttrValueWrapper::ToString(
626 GetMiscContainer()->mValue
.mSVGAnimatedPreserveAspectRatio
, aResult
);
629 case eSVGStringList
: {
630 SVGAttrValueWrapper::ToString(GetMiscContainer()->mValue
.mSVGStringList
,
634 case eSVGTransformList
: {
635 SVGAttrValueWrapper::ToString(
636 GetMiscContainer()->mValue
.mSVGTransformList
, aResult
);
640 SVGAttrValueWrapper::ToString(
641 GetMiscContainer()->mValue
.mSVGAnimatedViewBox
, aResult
);
651 already_AddRefed
<nsAtom
> nsAttrValue::GetAsAtom() const {
654 return NS_AtomizeMainThread(GetStringValue());
657 RefPtr
<nsAtom
> atom
= GetAtomValue();
658 return atom
.forget();
664 return NS_AtomizeMainThread(val
);
669 const nsCheapString
nsAttrValue::GetStringValue() const {
670 MOZ_ASSERT(Type() == eString
, "wrong type");
672 return nsCheapString(static_cast<nsStringBuffer
*>(GetPtr()));
675 bool nsAttrValue::GetColorValue(nscolor
& aColor
) const {
676 if (Type() != eColor
) {
677 // Unparseable value, treat as unset.
678 NS_ASSERTION(Type() == eString
, "unexpected type for color-valued attr");
682 aColor
= GetMiscContainer()->mValue
.mColor
;
686 void nsAttrValue::GetEnumString(nsAString
& aResult
, bool aRealTag
) const {
687 MOZ_ASSERT(Type() == eEnum
, "wrong type");
689 uint32_t allEnumBits
= (BaseType() == eIntegerBase
)
690 ? static_cast<uint32_t>(GetIntInternal())
691 : GetMiscContainer()->mValue
.mEnumValue
;
692 int16_t val
= allEnumBits
>> NS_ATTRVALUE_ENUMTABLEINDEX_BITS
;
693 const EnumTable
* table
= sEnumTableArray
->ElementAt(
694 allEnumBits
& NS_ATTRVALUE_ENUMTABLEINDEX_MASK
);
697 if (table
->value
== val
) {
698 aResult
.AssignASCII(table
->tag
);
700 allEnumBits
& NS_ATTRVALUE_ENUMTABLE_VALUE_NEEDS_TO_UPPER
) {
701 nsContentUtils::ASCIIToUpper(aResult
);
708 MOZ_ASSERT_UNREACHABLE("couldn't find value in EnumTable");
711 void AttrAtomArray::DoRemoveDuplicates() {
712 MOZ_ASSERT(mMayContainDuplicates
);
714 bool usingHashTable
= false;
715 BitBloomFilter
<8, nsAtom
> filter
;
716 nsTHashSet
<nsPtrHashKey
<nsAtom
>> hash
;
718 auto CheckDuplicate
= [&](size_t i
) {
719 nsAtom
* atom
= mArray
[i
];
720 if (!usingHashTable
) {
721 if (!filter
.mightContain(atom
)) {
725 for (size_t j
= 0; j
< i
; ++j
) {
726 hash
.Insert(mArray
[j
]);
728 usingHashTable
= true;
730 return !hash
.EnsureInserted(atom
);
733 size_t len
= mArray
.Length();
734 for (size_t i
= 0; i
< len
; ++i
) {
735 if (!CheckDuplicate(i
)) {
738 mArray
.RemoveElementAt(i
);
743 mMayContainDuplicates
= false;
746 uint32_t nsAttrValue::GetAtomCount() const {
747 ValueType type
= Type();
753 if (type
== eAtomArray
) {
754 return GetAtomArrayValue()->mArray
.Length();
760 nsAtom
* nsAttrValue::AtomAt(int32_t aIndex
) const {
761 MOZ_ASSERT(aIndex
>= 0, "Index must not be negative");
762 MOZ_ASSERT(GetAtomCount() > uint32_t(aIndex
), "aIndex out of range");
764 if (BaseType() == eAtomBase
) {
765 return GetAtomValue();
768 NS_ASSERTION(Type() == eAtomArray
, "GetAtomCount must be confused");
769 return GetAtomArrayValue()->mArray
.ElementAt(aIndex
);
772 uint32_t nsAttrValue::HashValue() const {
773 switch (BaseType()) {
775 nsStringBuffer
* str
= static_cast<nsStringBuffer
*>(GetPtr());
777 uint32_t len
= str
->StorageSize() / sizeof(char16_t
) - 1;
778 return HashString(static_cast<char16_t
*>(str
->Data()), len
);
788 // mBits and uint32_t might have different size. This should silence
789 // any warnings or compile-errors. This is what the implementation of
790 // NS_PTR_TO_INT32 does to take care of the same problem.
795 MiscContainer
* cont
= GetMiscContainer();
796 if (static_cast<ValueBaseType
>(cont
->mStringBits
&
797 NS_ATTRVALUE_BASETYPE_MASK
) == eAtomBase
) {
798 return cont
->mStringBits
- 0;
801 switch (cont
->mType
) {
803 return cont
->mValue
.mInteger
;
806 return cont
->mValue
.mEnumValue
;
809 return cont
->mDoubleValue
;
812 return cont
->mValue
.mColor
;
814 case eCSSDeclaration
: {
815 return NS_PTR_TO_INT32(cont
->mValue
.mCSSDeclaration
);
820 return HashString(str
);
824 for (const auto& atom
: cont
->mValue
.mAtomArray
->mArray
) {
825 hash
= AddToHash(hash
, atom
.get());
830 // XXX this is crappy, but oh well
831 return cont
->mDoubleValue
;
833 case eIntMarginValue
: {
834 return NS_PTR_TO_INT32(cont
->mValue
.mIntMargin
);
837 if (IsSVGType(cont
->mType
)) {
838 // All SVG types are just pointers to classes so we can treat them alike
839 return NS_PTR_TO_INT32(cont
->mValue
.mSVGLength
);
841 MOZ_ASSERT_UNREACHABLE("unknown type stored in MiscContainer");
847 bool nsAttrValue::Equals(const nsAttrValue
& aOther
) const {
848 if (BaseType() != aOther
.BaseType()) {
852 switch (BaseType()) {
854 return GetStringValue().Equals(aOther
.GetStringValue());
861 return mBits
== aOther
.mBits
;
865 MiscContainer
* thisCont
= GetMiscContainer();
866 MiscContainer
* otherCont
= aOther
.GetMiscContainer();
867 if (thisCont
== otherCont
) {
871 if (thisCont
->mType
!= otherCont
->mType
) {
875 bool needsStringComparison
= false;
877 switch (thisCont
->mType
) {
879 if (thisCont
->mValue
.mInteger
== otherCont
->mValue
.mInteger
) {
880 needsStringComparison
= true;
885 if (thisCont
->mValue
.mEnumValue
== otherCont
->mValue
.mEnumValue
) {
886 needsStringComparison
= true;
891 if (thisCont
->mDoubleValue
== otherCont
->mDoubleValue
) {
892 needsStringComparison
= true;
897 if (thisCont
->mValue
.mColor
== otherCont
->mValue
.mColor
) {
898 needsStringComparison
= true;
902 case eCSSDeclaration
: {
903 return thisCont
->mValue
.mCSSDeclaration
==
904 otherCont
->mValue
.mCSSDeclaration
;
907 return thisCont
->mValue
.mURL
== otherCont
->mValue
.mURL
;
910 // For classlists we could be insensitive to order, however
911 // classlists are never mapped attributes so they are never compared.
913 if (!(*thisCont
->mValue
.mAtomArray
== *otherCont
->mValue
.mAtomArray
)) {
917 needsStringComparison
= true;
921 return thisCont
->mDoubleValue
== otherCont
->mDoubleValue
;
923 case eIntMarginValue
: {
924 return thisCont
->mValue
.mIntMargin
== otherCont
->mValue
.mIntMargin
;
927 if (IsSVGType(thisCont
->mType
)) {
928 // Currently this method is never called for nsAttrValue objects that
929 // point to SVG data types.
930 // If that changes then we probably want to add methods to the
931 // corresponding SVG types to compare their base values.
932 // As a shortcut, however, we can begin by comparing the pointers.
933 MOZ_ASSERT(false, "Comparing nsAttrValues that point to SVG data");
936 MOZ_ASSERT_UNREACHABLE("unknown type stored in MiscContainer");
940 if (needsStringComparison
) {
941 if (thisCont
->mStringBits
== otherCont
->mStringBits
) {
944 if ((static_cast<ValueBaseType
>(thisCont
->mStringBits
&
945 NS_ATTRVALUE_BASETYPE_MASK
) ==
947 (static_cast<ValueBaseType
>(otherCont
->mStringBits
&
948 NS_ATTRVALUE_BASETYPE_MASK
) ==
950 return nsCheapString(reinterpret_cast<nsStringBuffer
*>(
951 static_cast<uintptr_t>(thisCont
->mStringBits
)))
952 .Equals(nsCheapString(reinterpret_cast<nsStringBuffer
*>(
953 static_cast<uintptr_t>(otherCont
->mStringBits
))));
959 bool nsAttrValue::Equals(const nsAString
& aValue
,
960 nsCaseTreatment aCaseSensitive
) const {
961 switch (BaseType()) {
963 nsStringBuffer
* str
= static_cast<nsStringBuffer
*>(GetPtr());
965 nsDependentString
dep(static_cast<char16_t
*>(str
->Data()),
966 str
->StorageSize() / sizeof(char16_t
) - 1);
967 return aCaseSensitive
== eCaseMatters
969 : nsContentUtils::EqualsIgnoreASCIICase(aValue
, dep
);
971 return aValue
.IsEmpty();
974 if (aCaseSensitive
== eCaseMatters
) {
975 return static_cast<nsAtom
*>(GetPtr())->Equals(aValue
);
977 return nsContentUtils::EqualsIgnoreASCIICase(
978 nsDependentAtomString(static_cast<nsAtom
*>(GetPtr())), aValue
);
985 return aCaseSensitive
== eCaseMatters
987 : nsContentUtils::EqualsIgnoreASCIICase(val
, aValue
);
990 bool nsAttrValue::Equals(const nsAtom
* aValue
,
991 nsCaseTreatment aCaseSensitive
) const {
992 if (aCaseSensitive
!= eCaseMatters
) {
993 // Need a better way to handle this!
995 aValue
->ToString(value
);
996 return Equals(value
, aCaseSensitive
);
999 switch (BaseType()) {
1001 nsStringBuffer
* str
= static_cast<nsStringBuffer
*>(GetPtr());
1003 nsDependentString
dep(static_cast<char16_t
*>(str
->Data()),
1004 str
->StorageSize() / sizeof(char16_t
) - 1);
1005 return aValue
->Equals(dep
);
1007 return aValue
== nsGkAtoms::_empty
;
1010 return static_cast<nsAtom
*>(GetPtr()) == aValue
;
1018 return aValue
->Equals(val
);
1021 struct HasPrefixFn
{
1022 static bool Check(const char16_t
* aAttrValue
, size_t aAttrLen
,
1023 const nsAString
& aSearchValue
,
1024 nsCaseTreatment aCaseSensitive
) {
1025 if (aCaseSensitive
== eCaseMatters
) {
1026 if (aSearchValue
.Length() > aAttrLen
) {
1029 return !memcmp(aAttrValue
, aSearchValue
.BeginReading(),
1030 aSearchValue
.Length() * sizeof(char16_t
));
1032 return StringBeginsWith(nsDependentString(aAttrValue
, aAttrLen
),
1034 nsASCIICaseInsensitiveStringComparator
);
1038 struct HasSuffixFn
{
1039 static bool Check(const char16_t
* aAttrValue
, size_t aAttrLen
,
1040 const nsAString
& aSearchValue
,
1041 nsCaseTreatment aCaseSensitive
) {
1042 if (aCaseSensitive
== eCaseMatters
) {
1043 if (aSearchValue
.Length() > aAttrLen
) {
1046 return !memcmp(aAttrValue
+ aAttrLen
- aSearchValue
.Length(),
1047 aSearchValue
.BeginReading(),
1048 aSearchValue
.Length() * sizeof(char16_t
));
1050 return StringEndsWith(nsDependentString(aAttrValue
, aAttrLen
), aSearchValue
,
1051 nsASCIICaseInsensitiveStringComparator
);
1055 struct HasSubstringFn
{
1056 static bool Check(const char16_t
* aAttrValue
, size_t aAttrLen
,
1057 const nsAString
& aSearchValue
,
1058 nsCaseTreatment aCaseSensitive
) {
1059 if (aCaseSensitive
== eCaseMatters
) {
1060 if (aSearchValue
.IsEmpty()) {
1063 const char16_t
* end
= aAttrValue
+ aAttrLen
;
1064 return std::search(aAttrValue
, end
, aSearchValue
.BeginReading(),
1065 aSearchValue
.EndReading()) != end
;
1067 return FindInReadable(aSearchValue
, nsDependentString(aAttrValue
, aAttrLen
),
1068 nsASCIICaseInsensitiveStringComparator
);
1072 template <typename F
>
1073 bool nsAttrValue::SubstringCheck(const nsAString
& aValue
,
1074 nsCaseTreatment aCaseSensitive
) const {
1075 switch (BaseType()) {
1077 auto str
= static_cast<nsStringBuffer
*>(GetPtr());
1079 return F::Check(static_cast<char16_t
*>(str
->Data()),
1080 str
->StorageSize() / sizeof(char16_t
) - 1, aValue
,
1083 return aValue
.IsEmpty();
1086 auto atom
= static_cast<nsAtom
*>(GetPtr());
1087 return F::Check(atom
->GetUTF16String(), atom
->GetLength(), aValue
,
1096 return F::Check(val
.BeginReading(), val
.Length(), aValue
, aCaseSensitive
);
1099 bool nsAttrValue::HasPrefix(const nsAString
& aValue
,
1100 nsCaseTreatment aCaseSensitive
) const {
1101 return SubstringCheck
<HasPrefixFn
>(aValue
, aCaseSensitive
);
1104 bool nsAttrValue::HasSuffix(const nsAString
& aValue
,
1105 nsCaseTreatment aCaseSensitive
) const {
1106 return SubstringCheck
<HasSuffixFn
>(aValue
, aCaseSensitive
);
1109 bool nsAttrValue::HasSubstring(const nsAString
& aValue
,
1110 nsCaseTreatment aCaseSensitive
) const {
1111 return SubstringCheck
<HasSubstringFn
>(aValue
, aCaseSensitive
);
1114 bool nsAttrValue::EqualsAsStrings(const nsAttrValue
& aOther
) const {
1115 if (Type() == aOther
.Type()) {
1116 return Equals(aOther
);
1119 // We need to serialize at least one nsAttrValue before passing to
1120 // Equals(const nsAString&), but we can avoid unnecessarily serializing both
1121 // by checking if one is already of a string type.
1122 bool thisIsString
= (BaseType() == eStringBase
|| BaseType() == eAtomBase
);
1123 const nsAttrValue
& lhs
= thisIsString
? *this : aOther
;
1124 const nsAttrValue
& rhs
= thisIsString
? aOther
: *this;
1126 switch (rhs
.BaseType()) {
1128 return lhs
.Equals(rhs
.GetAtomValue(), eCaseMatters
);
1131 return lhs
.Equals(rhs
.GetStringValue(), eCaseMatters
);
1136 return lhs
.Equals(val
, eCaseMatters
);
1141 bool nsAttrValue::Contains(nsAtom
* aValue
,
1142 nsCaseTreatment aCaseSensitive
) const {
1143 switch (BaseType()) {
1145 nsAtom
* atom
= GetAtomValue();
1146 if (aCaseSensitive
== eCaseMatters
) {
1147 return aValue
== atom
;
1150 // For performance reasons, don't do a full on unicode case insensitive
1151 // string comparison. This is only used for quirks mode anyway.
1152 return nsContentUtils::EqualsIgnoreASCIICase(aValue
, atom
);
1155 if (Type() == eAtomArray
) {
1156 AttrAtomArray
* array
= GetAtomArrayValue();
1157 if (aCaseSensitive
== eCaseMatters
) {
1158 return array
->mArray
.Contains(aValue
);
1161 for (RefPtr
<nsAtom
>& cur
: array
->mArray
) {
1162 // For performance reasons, don't do a full on unicode case
1163 // insensitive string comparison. This is only used for quirks mode
1165 if (nsContentUtils::EqualsIgnoreASCIICase(aValue
, cur
)) {
1176 struct AtomArrayStringComparator
{
1177 bool Equals(nsAtom
* atom
, const nsAString
& string
) const {
1178 return atom
->Equals(string
);
1182 bool nsAttrValue::Contains(const nsAString
& aValue
) const {
1183 switch (BaseType()) {
1185 nsAtom
* atom
= GetAtomValue();
1186 return atom
->Equals(aValue
);
1189 if (Type() == eAtomArray
) {
1190 AttrAtomArray
* array
= GetAtomArrayValue();
1191 return array
->mArray
.Contains(aValue
, AtomArrayStringComparator());
1199 void nsAttrValue::ParseAtom(const nsAString
& aValue
) {
1202 RefPtr
<nsAtom
> atom
= NS_Atomize(aValue
);
1204 SetPtrValueAndType(atom
.forget().take(), eAtomBase
);
1208 void nsAttrValue::ParseAtomArray(const nsAString
& aValue
) {
1209 nsAString::const_iterator iter
, end
;
1210 aValue
.BeginReading(iter
);
1211 aValue
.EndReading(end
);
1212 bool hasSpace
= false;
1214 // skip initial whitespace
1215 while (iter
!= end
&& nsContentUtils::IsHTMLWhitespace(*iter
)) {
1225 nsAString::const_iterator
start(iter
);
1227 // get first - and often only - atom
1230 } while (iter
!= end
&& !nsContentUtils::IsHTMLWhitespace(*iter
));
1232 RefPtr
<nsAtom
> classAtom
= NS_AtomizeMainThread(Substring(start
, iter
));
1239 while (iter
!= end
&& nsContentUtils::IsHTMLWhitespace(*iter
)) {
1244 if (iter
== end
&& !hasSpace
) {
1245 // we only found one classname and there was no whitespace so
1246 // don't bother storing a list
1248 nsAtom
* atom
= nullptr;
1249 classAtom
.swap(atom
);
1250 SetPtrValueAndType(atom
, eAtomBase
);
1254 if (!EnsureEmptyAtomArray()) {
1258 AttrAtomArray
* array
= GetAtomArrayValue();
1260 // XXX(Bug 1631371) Check if this should use a fallible operation as it
1261 // pretended earlier.
1262 array
->mArray
.AppendElement(std::move(classAtom
));
1264 // parse the rest of the classnames
1265 while (iter
!= end
) {
1270 } while (iter
!= end
&& !nsContentUtils::IsHTMLWhitespace(*iter
));
1272 classAtom
= NS_AtomizeMainThread(Substring(start
, iter
));
1274 // XXX(Bug 1631371) Check if this should use a fallible operation as it
1275 // pretended earlier.
1276 array
->mArray
.AppendElement(std::move(classAtom
));
1277 array
->mMayContainDuplicates
= true;
1280 while (iter
!= end
&& nsContentUtils::IsHTMLWhitespace(*iter
)) {
1285 SetMiscAtomOrString(&aValue
);
1288 void nsAttrValue::ParseStringOrAtom(const nsAString
& aValue
) {
1289 uint32_t len
= aValue
.Length();
1290 // Don't bother with atoms if it's an empty string since
1291 // we can store those efficiently anyway.
1292 if (len
&& len
<= NS_ATTRVALUE_MAX_STRINGLENGTH_ATOM
) {
1299 void nsAttrValue::ParsePartMapping(const nsAString
& aValue
) {
1301 MiscContainer
* cont
= EnsureEmptyMiscContainer();
1303 cont
->mType
= eShadowParts
;
1304 cont
->mValue
.mShadowParts
= new ShadowParts(ShadowParts::Parse(aValue
));
1306 SetMiscAtomOrString(&aValue
);
1307 MOZ_ASSERT(cont
->mValue
.mRefCount
== 1);
1310 void nsAttrValue::SetIntValueAndType(int32_t aValue
, ValueType aType
,
1311 const nsAString
* aStringValue
) {
1312 if (aStringValue
|| aValue
> NS_ATTRVALUE_INTEGERTYPE_MAXVALUE
||
1313 aValue
< NS_ATTRVALUE_INTEGERTYPE_MINVALUE
) {
1314 MiscContainer
* cont
= EnsureEmptyMiscContainer();
1317 cont
->mValue
.mInteger
= aValue
;
1321 cont
->mDoubleValue
= aValue
;
1325 cont
->mValue
.mEnumValue
= aValue
;
1329 MOZ_ASSERT_UNREACHABLE("unknown integer type");
1333 cont
->mType
= aType
;
1334 SetMiscAtomOrString(aStringValue
);
1336 NS_ASSERTION(!mBits
, "Reset before calling SetIntValueAndType!");
1337 mBits
= (aValue
* NS_ATTRVALUE_INTEGERTYPE_MULTIPLIER
) | aType
;
1341 void nsAttrValue::SetDoubleValueAndType(double aValue
, ValueType aType
,
1342 const nsAString
* aStringValue
) {
1343 MOZ_ASSERT(aType
== eDoubleValue
|| aType
== ePercent
, "Unexpected type");
1344 MiscContainer
* cont
= EnsureEmptyMiscContainer();
1345 cont
->mDoubleValue
= aValue
;
1346 cont
->mType
= aType
;
1347 SetMiscAtomOrString(aStringValue
);
1350 int16_t nsAttrValue::GetEnumTableIndex(const EnumTable
* aTable
) {
1351 int16_t index
= sEnumTableArray
->IndexOf(aTable
);
1353 index
= sEnumTableArray
->Length();
1354 NS_ASSERTION(index
<= NS_ATTRVALUE_ENUMTABLEINDEX_MAXVALUE
,
1355 "too many enum tables");
1356 sEnumTableArray
->AppendElement(aTable
);
1362 int32_t nsAttrValue::EnumTableEntryToValue(const EnumTable
* aEnumTable
,
1363 const EnumTable
* aTableEntry
) {
1364 int16_t index
= GetEnumTableIndex(aEnumTable
);
1366 (aTableEntry
->value
<< NS_ATTRVALUE_ENUMTABLEINDEX_BITS
) + index
;
1370 bool nsAttrValue::ParseEnumValue(const nsAString
& aValue
,
1371 const EnumTable
* aTable
, bool aCaseSensitive
,
1372 const EnumTable
* aDefaultValue
) {
1374 const EnumTable
* tableEntry
= aTable
;
1376 while (tableEntry
->tag
) {
1377 if (aCaseSensitive
? aValue
.EqualsASCII(tableEntry
->tag
)
1378 : aValue
.LowerCaseEqualsASCII(tableEntry
->tag
)) {
1379 int32_t value
= EnumTableEntryToValue(aTable
, tableEntry
);
1381 bool equals
= aCaseSensitive
|| aValue
.EqualsASCII(tableEntry
->tag
);
1384 tag
.AssignASCII(tableEntry
->tag
);
1385 nsContentUtils::ASCIIToUpper(tag
);
1386 if ((equals
= tag
.Equals(aValue
))) {
1387 value
|= NS_ATTRVALUE_ENUMTABLE_VALUE_NEEDS_TO_UPPER
;
1390 SetIntValueAndType(value
, eEnum
, equals
? nullptr : &aValue
);
1391 NS_ASSERTION(GetEnumValue() == tableEntry
->value
,
1392 "failed to store enum properly");
1399 if (aDefaultValue
) {
1400 MOZ_ASSERT(aTable
<= aDefaultValue
&& aDefaultValue
< tableEntry
,
1401 "aDefaultValue not inside aTable?");
1402 SetIntValueAndType(EnumTableEntryToValue(aTable
, aDefaultValue
), eEnum
,
1410 bool nsAttrValue::DoParseHTMLDimension(const nsAString
& aInput
,
1411 bool aEnsureNonzero
) {
1414 // We don't use nsContentUtils::ParseHTMLInteger here because we
1415 // need a bunch of behavioral differences from it. We _could_ try to
1416 // use it, but it would not be a great fit.
1418 // https://html.spec.whatwg.org/multipage/#rules-for-parsing-dimension-values
1421 const char16_t
* position
= aInput
.BeginReading();
1422 const char16_t
* end
= aInput
.EndReading();
1424 // We will need to keep track of whether this was a canonical representation
1425 // or not. It's non-canonical if it has leading whitespace, leading '+',
1426 // leading '0' characters, or trailing garbage.
1427 bool canonical
= true;
1430 while (position
!= end
&& nsContentUtils::IsHTMLWhitespace(*position
)) {
1431 canonical
= false; // Leading whitespace
1436 if (position
== end
|| *position
< char16_t('0') ||
1437 *position
> char16_t('9')) {
1442 CheckedInt32 value
= 0;
1444 // Collect up leading '0' first to avoid extra branching in the main
1445 // loop to set 'canonical' properly.
1446 while (position
!= end
&& *position
== char16_t('0')) {
1447 canonical
= false; // Leading '0'
1451 // Now collect up other digits.
1452 while (position
!= end
&& *position
>= char16_t('0') &&
1453 *position
<= char16_t('9')) {
1454 value
= value
* 10 + (*position
- char16_t('0'));
1455 if (!value
.isValid()) {
1456 // The spec assumes we can deal with arbitrary-size integers here, but we
1457 // really can't. If someone sets something too big, just bail out and
1464 // Step 6 is implemented implicitly via the various "position != end" guards
1465 // from this point on.
1467 Maybe
<double> doubleValue
;
1468 // Step 7. The return in step 7.2 is handled by just falling through to the
1469 // code below this block when we reach end of input or a non-digit, because
1470 // the while loop will terminate at that point.
1471 if (position
!= end
&& *position
== char16_t('.')) {
1472 canonical
= false; // Let's not rely on double serialization reproducing
1473 // the string we started with.
1476 // If we have a '.' _not_ followed by digits, this is not as efficient as it
1477 // could be, because we will store as a double while we could have stored as
1478 // an int. But that seems like a pretty rare case.
1479 doubleValue
.emplace(value
.value());
1481 double divisor
= 1.0f
;
1483 while (position
!= end
&& *position
>= char16_t('0') &&
1484 *position
<= char16_t('9')) {
1486 divisor
= divisor
* 10.0f
;
1488 doubleValue
.ref() += (*position
- char16_t('0')) / divisor
;
1491 // Step 7.4.4 and 7.4.5 are captured in the while loop condition and the
1492 // "position != end" checks below.
1496 if (aEnsureNonzero
&& value
.value() == 0 &&
1497 (!doubleValue
|| *doubleValue
== 0.0f
)) {
1498 // Not valid. Just drop it.
1502 // Step 8 and the spec's early return from step 7.2.
1504 if (position
!= end
&& *position
== char16_t('%')) {
1507 } else if (doubleValue
) {
1508 type
= eDoubleValue
;
1513 if (position
!= end
) {
1518 MOZ_ASSERT(!canonical
, "We set it false above!");
1519 SetDoubleValueAndType(*doubleValue
, type
, &aInput
);
1521 SetIntValueAndType(value
.value(), type
, canonical
? nullptr : &aInput
);
1527 MOZ_ASSERT(str
== aInput
, "We messed up our 'canonical' boolean!");
1533 bool nsAttrValue::ParseIntWithBounds(const nsAString
& aString
, int32_t aMin
,
1535 MOZ_ASSERT(aMin
< aMax
, "bad boundaries");
1539 nsContentUtils::ParseHTMLIntegerResultFlags result
;
1540 int32_t originalVal
= nsContentUtils::ParseHTMLInteger(aString
, &result
);
1541 if (result
& nsContentUtils::eParseHTMLInteger_Error
) {
1545 int32_t val
= std::max(originalVal
, aMin
);
1546 val
= std::min(val
, aMax
);
1548 (val
!= originalVal
) ||
1549 (result
& nsContentUtils::eParseHTMLInteger_NonStandard
) ||
1550 (result
& nsContentUtils::eParseHTMLInteger_DidNotConsumeAllInput
);
1552 SetIntValueAndType(val
, eInteger
, nonStrict
? &aString
: nullptr);
1557 void nsAttrValue::ParseIntWithFallback(const nsAString
& aString
,
1558 int32_t aDefault
, int32_t aMax
) {
1561 nsContentUtils::ParseHTMLIntegerResultFlags result
;
1562 int32_t val
= nsContentUtils::ParseHTMLInteger(aString
, &result
);
1563 bool nonStrict
= false;
1564 if ((result
& nsContentUtils::eParseHTMLInteger_Error
) || val
< 1) {
1574 if ((result
& nsContentUtils::eParseHTMLInteger_NonStandard
) ||
1575 (result
& nsContentUtils::eParseHTMLInteger_DidNotConsumeAllInput
)) {
1579 SetIntValueAndType(val
, eInteger
, nonStrict
? &aString
: nullptr);
1582 void nsAttrValue::ParseClampedNonNegativeInt(const nsAString
& aString
,
1583 int32_t aDefault
, int32_t aMin
,
1587 nsContentUtils::ParseHTMLIntegerResultFlags result
;
1588 int32_t val
= nsContentUtils::ParseHTMLInteger(aString
, &result
);
1590 (result
& nsContentUtils::eParseHTMLInteger_NonStandard
) ||
1591 (result
& nsContentUtils::eParseHTMLInteger_DidNotConsumeAllInput
);
1593 if (result
& nsContentUtils::eParseHTMLInteger_ErrorOverflow
) {
1594 if (result
& nsContentUtils::eParseHTMLInteger_Negative
) {
1600 } else if ((result
& nsContentUtils::eParseHTMLInteger_Error
) || val
< 0) {
1603 } else if (val
< aMin
) {
1606 } else if (val
> aMax
) {
1611 SetIntValueAndType(val
, eInteger
, nonStrict
? &aString
: nullptr);
1614 bool nsAttrValue::ParseNonNegativeIntValue(const nsAString
& aString
) {
1617 nsContentUtils::ParseHTMLIntegerResultFlags result
;
1618 int32_t originalVal
= nsContentUtils::ParseHTMLInteger(aString
, &result
);
1619 if ((result
& nsContentUtils::eParseHTMLInteger_Error
) || originalVal
< 0) {
1624 (result
& nsContentUtils::eParseHTMLInteger_NonStandard
) ||
1625 (result
& nsContentUtils::eParseHTMLInteger_DidNotConsumeAllInput
);
1627 SetIntValueAndType(originalVal
, eInteger
, nonStrict
? &aString
: nullptr);
1632 bool nsAttrValue::ParsePositiveIntValue(const nsAString
& aString
) {
1635 nsContentUtils::ParseHTMLIntegerResultFlags result
;
1636 int32_t originalVal
= nsContentUtils::ParseHTMLInteger(aString
, &result
);
1637 if ((result
& nsContentUtils::eParseHTMLInteger_Error
) || originalVal
<= 0) {
1642 (result
& nsContentUtils::eParseHTMLInteger_NonStandard
) ||
1643 (result
& nsContentUtils::eParseHTMLInteger_DidNotConsumeAllInput
);
1645 SetIntValueAndType(originalVal
, eInteger
, nonStrict
? &aString
: nullptr);
1650 void nsAttrValue::SetColorValue(nscolor aColor
, const nsAString
& aString
) {
1651 nsStringBuffer
* buf
= GetStringBuffer(aString
).take();
1656 MiscContainer
* cont
= EnsureEmptyMiscContainer();
1657 cont
->mValue
.mColor
= aColor
;
1658 cont
->mType
= eColor
;
1660 // Save the literal string we were passed for round-tripping.
1661 cont
->SetStringBitsMainThread(reinterpret_cast<uintptr_t>(buf
) | eStringBase
);
1664 bool nsAttrValue::ParseColor(const nsAString
& aString
) {
1667 // FIXME (partially, at least): HTML5's algorithm says we shouldn't do
1668 // the whitespace compression, trimming, or the test for emptiness.
1669 // (I'm a little skeptical that we shouldn't do the whitespace
1670 // trimming; WebKit also does it.)
1671 nsAutoString
colorStr(aString
);
1672 colorStr
.CompressWhitespace(true, true);
1673 if (colorStr
.IsEmpty()) {
1678 // No color names begin with a '#'; in standards mode, all acceptable
1679 // numeric colors do.
1680 if (colorStr
.First() == '#') {
1681 nsDependentString
withoutHash(colorStr
.get() + 1, colorStr
.Length() - 1);
1682 if (NS_HexToRGBA(withoutHash
, nsHexColorType::NoAlpha
, &color
)) {
1683 SetColorValue(color
, aString
);
1687 if (NS_ColorNameToRGB(colorStr
, &color
)) {
1688 SetColorValue(color
, aString
);
1693 // FIXME (maybe): HTML5 says we should handle system colors. This
1694 // means we probably need another storage type, since we'd need to
1695 // handle dynamic changes. However, I think this is a bad idea:
1696 // http://lists.whatwg.org/pipermail/whatwg-whatwg.org/2010-May/026449.html
1698 // Use NS_LooseHexToRGB as a fallback if nothing above worked.
1699 if (NS_LooseHexToRGB(colorStr
, &color
)) {
1700 SetColorValue(color
, aString
);
1707 bool nsAttrValue::ParseDoubleValue(const nsAString
& aString
) {
1711 double val
= PromiseFlatString(aString
).ToDouble(&ec
);
1712 if (NS_FAILED(ec
)) {
1716 MiscContainer
* cont
= EnsureEmptyMiscContainer();
1717 cont
->mDoubleValue
= val
;
1718 cont
->mType
= eDoubleValue
;
1719 nsAutoString serializedFloat
;
1720 serializedFloat
.AppendFloat(val
);
1721 SetMiscAtomOrString(serializedFloat
.Equals(aString
) ? nullptr : &aString
);
1725 bool nsAttrValue::ParseIntMarginValue(const nsAString
& aString
) {
1728 nsIntMargin margins
;
1729 if (!nsContentUtils::ParseIntMarginValue(aString
, margins
)) return false;
1731 MiscContainer
* cont
= EnsureEmptyMiscContainer();
1732 cont
->mValue
.mIntMargin
= new nsIntMargin(margins
);
1733 cont
->mType
= eIntMarginValue
;
1734 SetMiscAtomOrString(&aString
);
1738 bool nsAttrValue::ParseStyleAttribute(const nsAString
& aString
,
1739 nsIPrincipal
* aMaybeScriptedPrincipal
,
1740 nsStyledElement
* aElement
) {
1741 dom::Document
* ownerDoc
= aElement
->OwnerDoc();
1742 nsHTMLCSSStyleSheet
* sheet
= ownerDoc
->GetInlineStyleSheet();
1743 nsIURI
* baseURI
= aElement
->GetBaseURIForStyleAttr();
1744 nsIURI
* docURI
= ownerDoc
->GetDocumentURI();
1746 NS_ASSERTION(aElement
->NodePrincipal() == ownerDoc
->NodePrincipal(),
1747 "This is unexpected");
1749 nsIPrincipal
* principal
= aMaybeScriptedPrincipal
? aMaybeScriptedPrincipal
1750 : aElement
->NodePrincipal();
1752 // If the (immutable) document URI does not match the element's base URI
1753 // (the common case is that they do match) do not cache the rule. This is
1754 // because the results of the CSS parser are dependent on these URIs, and we
1755 // do not want to have to account for the URIs in the hash lookup.
1756 // Similarly, if the triggering principal does not match the node principal,
1757 // do not cache the rule, since the principal will be encoded in any parsed
1758 // URLs in the rule.
1759 const bool cachingAllowed
=
1760 sheet
&& baseURI
== docURI
&& principal
== aElement
->NodePrincipal();
1761 if (cachingAllowed
) {
1762 MiscContainer
* cont
= sheet
->LookupStyleAttr(aString
);
1764 // Set our MiscContainer to the cached one.
1766 SetPtrValueAndType(cont
, eOtherBase
);
1771 nsCOMPtr
<nsIReferrerInfo
> referrerInfo
=
1772 dom::ReferrerInfo::CreateForInternalCSSResources(ownerDoc
);
1773 auto data
= MakeRefPtr
<URLExtraData
>(baseURI
, referrerInfo
, principal
);
1774 RefPtr
<DeclarationBlock
> decl
= DeclarationBlock::FromCssText(
1775 aString
, data
, ownerDoc
->GetCompatibilityMode(), ownerDoc
->CSSLoader(),
1776 StyleCssRuleType::Style
);
1780 decl
->SetHTMLCSSStyleSheet(sheet
);
1781 SetTo(decl
.forget(), &aString
);
1783 if (cachingAllowed
) {
1784 MiscContainer
* cont
= GetMiscContainer();
1791 void nsAttrValue::SetMiscAtomOrString(const nsAString
* aValue
) {
1792 NS_ASSERTION(GetMiscContainer(), "Must have MiscContainer!");
1793 NS_ASSERTION(!GetMiscContainer()->mStringBits
|| IsInServoTraversal(),
1794 "Trying to re-set atom or string!");
1796 uint32_t len
= aValue
->Length();
1797 // * We're allowing eCSSDeclaration attributes to store empty
1798 // strings as it can be beneficial to store an empty style
1799 // attribute as a parsed rule.
1800 // * We're allowing enumerated values because sometimes the empty
1801 // string corresponds to a particular enumerated value, especially
1802 // for enumerated values that are not limited enumerated.
1803 // Add other types as needed.
1804 NS_ASSERTION(len
|| Type() == eCSSDeclaration
|| Type() == eEnum
,
1806 MiscContainer
* cont
= GetMiscContainer();
1808 if (len
<= NS_ATTRVALUE_MAX_STRINGLENGTH_ATOM
) {
1809 nsAtom
* atom
= MOZ_LIKELY(!IsInServoTraversal())
1810 ? NS_AtomizeMainThread(*aValue
).take()
1811 : NS_Atomize(*aValue
).take();
1812 NS_ENSURE_TRUE_VOID(atom
);
1813 uintptr_t bits
= reinterpret_cast<uintptr_t>(atom
) | eAtomBase
;
1815 // In the common case we're not in the servo traversal, and we can just
1816 // set the bits normally. The parallel case requires more care.
1817 if (MOZ_LIKELY(!IsInServoTraversal())) {
1818 cont
->SetStringBitsMainThread(bits
);
1819 } else if (!cont
->mStringBits
.compareExchange(0, bits
)) {
1820 // We raced with somebody else setting the bits. Release our copy.
1824 nsStringBuffer
* buffer
= GetStringBuffer(*aValue
).take();
1825 NS_ENSURE_TRUE_VOID(buffer
);
1826 uintptr_t bits
= reinterpret_cast<uintptr_t>(buffer
) | eStringBase
;
1828 // In the common case we're not in the servo traversal, and we can just
1829 // set the bits normally. The parallel case requires more care.
1830 if (MOZ_LIKELY(!IsInServoTraversal())) {
1831 cont
->SetStringBitsMainThread(bits
);
1832 } else if (!cont
->mStringBits
.compareExchange(0, bits
)) {
1833 // We raced with somebody else setting the bits. Release our copy.
1840 void nsAttrValue::ResetMiscAtomOrString() {
1841 MiscContainer
* cont
= GetMiscContainer();
1842 void* ptr
= MISC_STR_PTR(cont
);
1844 if (static_cast<ValueBaseType
>(cont
->mStringBits
&
1845 NS_ATTRVALUE_BASETYPE_MASK
) == eStringBase
) {
1846 static_cast<nsStringBuffer
*>(ptr
)->Release();
1848 static_cast<nsAtom
*>(ptr
)->Release();
1850 cont
->SetStringBitsMainThread(0);
1854 void nsAttrValue::SetSVGType(ValueType aType
, const void* aValue
,
1855 const nsAString
* aSerialized
) {
1856 MOZ_ASSERT(IsSVGType(aType
), "Not an SVG type");
1858 MiscContainer
* cont
= EnsureEmptyMiscContainer();
1859 // All SVG types are just pointers to classes so just setting any of them
1860 // will do. We'll lose type-safety but the signature of the calling
1861 // function should ensure we don't get anything unexpected, and once we
1862 // stick aValue in a union we lose type information anyway.
1863 cont
->mValue
.mSVGLength
= static_cast<const SVGAnimatedLength
*>(aValue
);
1864 cont
->mType
= aType
;
1865 SetMiscAtomOrString(aSerialized
);
1868 MiscContainer
* nsAttrValue::ClearMiscContainer() {
1869 MiscContainer
* cont
= nullptr;
1870 if (BaseType() == eOtherBase
) {
1871 cont
= GetMiscContainer();
1872 if (cont
->IsRefCounted() && cont
->mValue
.mRefCount
> 1) {
1873 // This MiscContainer is shared, we need a new one.
1876 cont
= AllocMiscContainer();
1877 SetPtrValueAndType(cont
, eOtherBase
);
1879 switch (cont
->mType
) {
1880 case eCSSDeclaration
: {
1881 MOZ_ASSERT(cont
->mValue
.mRefCount
== 1);
1884 NS_RELEASE(cont
->mValue
.mCSSDeclaration
);
1887 case eShadowParts
: {
1888 MOZ_ASSERT(cont
->mValue
.mRefCount
== 1);
1890 delete cont
->mValue
.mShadowParts
;
1894 NS_RELEASE(cont
->mValue
.mURL
);
1898 delete cont
->mValue
.mAtomArray
;
1901 case eIntMarginValue
: {
1902 delete cont
->mValue
.mIntMargin
;
1910 ResetMiscAtomOrString();
1918 MiscContainer
* nsAttrValue::EnsureEmptyMiscContainer() {
1919 MiscContainer
* cont
= ClearMiscContainer();
1921 MOZ_ASSERT(BaseType() == eOtherBase
);
1922 ResetMiscAtomOrString();
1923 cont
= GetMiscContainer();
1925 cont
= AllocMiscContainer();
1926 SetPtrValueAndType(cont
, eOtherBase
);
1932 bool nsAttrValue::EnsureEmptyAtomArray() {
1933 if (Type() == eAtomArray
) {
1934 ResetMiscAtomOrString();
1935 GetAtomArrayValue()->Clear();
1939 MiscContainer
* cont
= EnsureEmptyMiscContainer();
1940 cont
->mValue
.mAtomArray
= new AttrAtomArray
;
1941 cont
->mType
= eAtomArray
;
1946 already_AddRefed
<nsStringBuffer
> nsAttrValue::GetStringBuffer(
1947 const nsAString
& aValue
) const {
1948 uint32_t len
= aValue
.Length();
1953 RefPtr
<nsStringBuffer
> buf
= nsStringBuffer::FromString(aValue
);
1954 if (buf
&& (buf
->StorageSize() / sizeof(char16_t
) - 1) == len
) {
1955 return buf
.forget();
1958 buf
= nsStringBuffer::Alloc((len
+ 1) * sizeof(char16_t
));
1962 char16_t
* data
= static_cast<char16_t
*>(buf
->Data());
1963 CopyUnicodeTo(aValue
, 0, data
, len
);
1964 data
[len
] = char16_t(0);
1965 return buf
.forget();
1968 size_t nsAttrValue::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf
) const {
1971 switch (BaseType()) {
1973 nsStringBuffer
* str
= static_cast<nsStringBuffer
*>(GetPtr());
1974 n
+= str
? str
->SizeOfIncludingThisIfUnshared(aMallocSizeOf
) : 0;
1978 MiscContainer
* container
= GetMiscContainer();
1982 if (container
->IsRefCounted() && container
->mValue
.mRefCount
> 1) {
1983 // We don't report this MiscContainer at all in order to avoid
1984 // twice-reporting it.
1985 // TODO DMD, bug 1027551 - figure out how to report this ref-counted
1986 // object just once.
1989 n
+= aMallocSizeOf(container
);
1991 void* otherPtr
= MISC_STR_PTR(container
);
1992 // We only count the size of the object pointed by otherPtr if it's a
1993 // string. When it's an atom, it's counted separatly.
1994 if (otherPtr
&& static_cast<ValueBaseType
>(container
->mStringBits
&
1995 NS_ATTRVALUE_BASETYPE_MASK
) ==
1997 nsStringBuffer
* str
= static_cast<nsStringBuffer
*>(otherPtr
);
1998 n
+= str
? str
->SizeOfIncludingThisIfUnshared(aMallocSizeOf
) : 0;
2001 if (Type() == eCSSDeclaration
&& container
->mValue
.mCSSDeclaration
) {
2002 // TODO: mCSSDeclaration might be owned by another object which
2003 // would make us count them twice, bug 677493.
2004 // Bug 1281964: For DeclarationBlock if we do measure we'll
2005 // need a way to call the Servo heap_size_of function.
2006 // n += container->mCSSDeclaration->SizeOfIncludingThis(aMallocSizeOf);
2007 } else if (Type() == eAtomArray
&& container
->mValue
.mAtomArray
) {
2008 // Don't measure each nsAtom, they are measured separatly.
2009 n
+= container
->mValue
.mAtomArray
->mArray
.ShallowSizeOfIncludingThis(
2014 case eAtomBase
: // Atoms are counted separately.
2015 case eIntegerBase
: // The value is in mBits, nothing to do.