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/ShadowParts.h"
24 #include "mozilla/SVGAttrValueWrapper.h"
25 #include "mozilla/DeclarationBlock.h"
26 #include "mozilla/dom/CSSRuleBinding.h"
27 #include "nsContentUtils.h"
28 #include "nsReadableUtils.h"
29 #include "nsHTMLCSSStyleSheet.h"
30 #include "nsStyledElement.h"
32 #include "mozilla/dom/Document.h"
33 #include "ReferrerInfo.h"
36 using namespace mozilla
;
38 #define MISC_STR_PTR(_cont) \
39 reinterpret_cast<void*>((_cont)->mStringBits & NS_ATTRVALUE_POINTERVALUE_MASK)
42 MiscContainer
* nsAttrValue::AllocMiscContainer() {
43 MOZ_ASSERT(NS_IsMainThread());
44 MiscContainer
* cont
= nullptr;
45 std::swap(cont
, sMiscContainerCache
);
48 return new (cont
) MiscContainer
;
51 return new MiscContainer
;
55 void nsAttrValue::DeallocMiscContainer(MiscContainer
* aCont
) {
56 MOZ_ASSERT(NS_IsMainThread());
61 if (!sMiscContainerCache
) {
62 aCont
->~MiscContainer();
63 sMiscContainerCache
= aCont
;
69 bool MiscContainer::GetString(nsAString
& aString
) const {
70 void* ptr
= MISC_STR_PTR(this);
76 if (static_cast<nsAttrValue::ValueBaseType
>(mStringBits
&
77 NS_ATTRVALUE_BASETYPE_MASK
) ==
78 nsAttrValue::eStringBase
) {
79 nsStringBuffer
* buffer
= static_cast<nsStringBuffer
*>(ptr
);
84 buffer
->ToString(buffer
->StorageSize() / sizeof(char16_t
) - 1, aString
);
88 nsAtom
* atom
= static_cast<nsAtom
*>(ptr
);
93 atom
->ToString(aString
);
97 void MiscContainer::Cache() {
98 // Not implemented for anything else yet.
99 if (mType
!= nsAttrValue::eCSSDeclaration
) {
100 MOZ_ASSERT_UNREACHABLE("unexpected cached nsAttrValue type");
104 MOZ_ASSERT(IsRefCounted());
105 MOZ_ASSERT(mValue
.mRefCount
> 0);
106 MOZ_ASSERT(!mValue
.mCached
);
108 nsHTMLCSSStyleSheet
* sheet
= mValue
.mCSSDeclaration
->GetHTMLCSSStyleSheet();
114 bool gotString
= GetString(str
);
119 sheet
->CacheStyleAttr(str
, this);
122 // This has to be immutable once it goes into the cache.
123 mValue
.mCSSDeclaration
->SetImmutable();
126 void MiscContainer::Evict() {
127 // Not implemented for anything else yet.
128 if (mType
!= nsAttrValue::eCSSDeclaration
) {
129 MOZ_ASSERT_UNREACHABLE("unexpected cached nsAttrValue type");
132 MOZ_ASSERT(IsRefCounted());
133 MOZ_ASSERT(mValue
.mRefCount
== 0);
135 if (!mValue
.mCached
) {
139 nsHTMLCSSStyleSheet
* sheet
= mValue
.mCSSDeclaration
->GetHTMLCSSStyleSheet();
143 DebugOnly
<bool> gotString
= GetString(str
);
144 MOZ_ASSERT(gotString
);
146 sheet
->EvictStyleAttr(str
, this);
150 nsTArray
<const nsAttrValue::EnumTable
*>* nsAttrValue::sEnumTableArray
= nullptr;
151 MiscContainer
* nsAttrValue::sMiscContainerCache
= nullptr;
153 nsAttrValue::nsAttrValue() : mBits(0) {}
155 nsAttrValue::nsAttrValue(const nsAttrValue
& aOther
) : mBits(0) {
159 nsAttrValue::nsAttrValue(const nsAString
& aValue
) : mBits(0) { SetTo(aValue
); }
161 nsAttrValue::nsAttrValue(nsAtom
* aValue
) : mBits(0) { SetTo(aValue
); }
163 nsAttrValue::nsAttrValue(already_AddRefed
<DeclarationBlock
> aValue
,
164 const nsAString
* aSerialized
)
166 SetTo(std::move(aValue
), aSerialized
);
169 nsAttrValue::nsAttrValue(const nsIntMargin
& aValue
) : mBits(0) {
173 nsAttrValue::~nsAttrValue() { ResetIfSet(); }
176 void nsAttrValue::Init() {
177 MOZ_ASSERT(!sEnumTableArray
, "nsAttrValue already initialized");
178 sEnumTableArray
= new nsTArray
<const EnumTable
*>;
182 void nsAttrValue::Shutdown() {
183 MOZ_ASSERT(NS_IsMainThread());
184 delete sEnumTableArray
;
185 sEnumTableArray
= nullptr;
186 // The MiscContainer pointed to by sMiscContainerCache has already
187 // be destructed so `delete sMiscContainerCache` is
188 // dangerous. Invoke `operator delete` to free the memory.
189 ::operator delete(sMiscContainerCache
);
190 sMiscContainerCache
= nullptr;
193 void nsAttrValue::Reset() {
194 switch (BaseType()) {
196 nsStringBuffer
* str
= static_cast<nsStringBuffer
*>(GetPtr());
204 MiscContainer
* cont
= GetMiscContainer();
205 if (cont
->IsRefCounted() && cont
->mValue
.mRefCount
> 1) {
210 DeallocMiscContainer(ClearMiscContainer());
215 nsAtom
* atom
= GetAtomValue();
228 void nsAttrValue::SetTo(const nsAttrValue
& aOther
) {
229 if (this == &aOther
) {
233 switch (aOther
.BaseType()) {
236 nsStringBuffer
* str
= static_cast<nsStringBuffer
*>(aOther
.GetPtr());
239 SetPtrValueAndType(str
, eStringBase
);
248 nsAtom
* atom
= aOther
.GetAtomValue();
250 SetPtrValueAndType(atom
, eAtomBase
);
255 mBits
= aOther
.mBits
;
260 MiscContainer
* otherCont
= aOther
.GetMiscContainer();
261 if (otherCont
->IsRefCounted()) {
262 DeallocMiscContainer(ClearMiscContainer());
263 NS_ADDREF(otherCont
);
264 SetPtrValueAndType(otherCont
, eOtherBase
);
268 MiscContainer
* cont
= EnsureEmptyMiscContainer();
269 switch (otherCont
->mType
) {
271 cont
->mValue
.mInteger
= otherCont
->mValue
.mInteger
;
275 cont
->mValue
.mEnumValue
= otherCont
->mValue
.mEnumValue
;
279 cont
->mDoubleValue
= otherCont
->mDoubleValue
;
283 cont
->mValue
.mColor
= otherCont
->mValue
.mColor
;
287 case eCSSDeclaration
: {
288 MOZ_CRASH("These should be refcounted!");
291 NS_ADDREF(cont
->mValue
.mURL
= otherCont
->mValue
.mURL
);
295 if (!EnsureEmptyAtomArray()) {
299 // XXX(Bug 1631371) Check if this should use a fallible operation as it
300 // pretended earlier.
301 GetAtomArrayValue()->AppendElements(*otherCont
->mValue
.mAtomArray
);
305 cont
->mDoubleValue
= otherCont
->mDoubleValue
;
308 case eIntMarginValue
: {
309 if (otherCont
->mValue
.mIntMargin
) {
310 cont
->mValue
.mIntMargin
=
311 new nsIntMargin(*otherCont
->mValue
.mIntMargin
);
316 if (IsSVGType(otherCont
->mType
)) {
317 // All SVG types are just pointers to classes and will therefore have
318 // the same size so it doesn't really matter which one we assign
319 cont
->mValue
.mSVGLength
= otherCont
->mValue
.mSVGLength
;
321 MOZ_ASSERT_UNREACHABLE("unknown type stored in MiscContainer");
327 void* otherPtr
= MISC_STR_PTR(otherCont
);
329 if (static_cast<ValueBaseType
>(otherCont
->mStringBits
&
330 NS_ATTRVALUE_BASETYPE_MASK
) == eStringBase
) {
331 static_cast<nsStringBuffer
*>(otherPtr
)->AddRef();
333 static_cast<nsAtom
*>(otherPtr
)->AddRef();
335 cont
->SetStringBitsMainThread(otherCont
->mStringBits
);
337 // Note, set mType after switch-case, otherwise EnsureEmptyAtomArray doesn't
339 cont
->mType
= otherCont
->mType
;
342 void nsAttrValue::SetTo(const nsAString
& aValue
) {
344 nsStringBuffer
* buf
= GetStringBuffer(aValue
).take();
346 SetPtrValueAndType(buf
, eStringBase
);
350 void nsAttrValue::SetTo(nsAtom
* aValue
) {
354 SetPtrValueAndType(aValue
, eAtomBase
);
358 void nsAttrValue::SetTo(int16_t aInt
) {
360 SetIntValueAndType(aInt
, eInteger
, nullptr);
363 void nsAttrValue::SetTo(int32_t aInt
, const nsAString
* aSerialized
) {
365 SetIntValueAndType(aInt
, eInteger
, aSerialized
);
368 void nsAttrValue::SetTo(double aValue
, const nsAString
* aSerialized
) {
369 MiscContainer
* cont
= EnsureEmptyMiscContainer();
370 cont
->mDoubleValue
= aValue
;
371 cont
->mType
= eDoubleValue
;
372 SetMiscAtomOrString(aSerialized
);
375 void nsAttrValue::SetTo(already_AddRefed
<DeclarationBlock
> aValue
,
376 const nsAString
* aSerialized
) {
377 MiscContainer
* cont
= EnsureEmptyMiscContainer();
378 MOZ_ASSERT(cont
->mValue
.mRefCount
== 0);
379 cont
->mValue
.mCSSDeclaration
= aValue
.take();
380 cont
->mType
= eCSSDeclaration
;
382 SetMiscAtomOrString(aSerialized
);
383 MOZ_ASSERT(cont
->mValue
.mRefCount
== 1);
386 void nsAttrValue::SetTo(nsIURI
* aValue
, const nsAString
* aSerialized
) {
387 MiscContainer
* cont
= EnsureEmptyMiscContainer();
388 NS_ADDREF(cont
->mValue
.mURL
= aValue
);
390 SetMiscAtomOrString(aSerialized
);
393 void nsAttrValue::SetTo(const nsIntMargin
& aValue
) {
394 MiscContainer
* cont
= EnsureEmptyMiscContainer();
395 cont
->mValue
.mIntMargin
= new nsIntMargin(aValue
);
396 cont
->mType
= eIntMarginValue
;
399 void nsAttrValue::SetToSerialized(const nsAttrValue
& aOther
) {
400 if (aOther
.Type() != nsAttrValue::eString
&&
401 aOther
.Type() != nsAttrValue::eAtom
) {
403 aOther
.ToString(val
);
410 void nsAttrValue::SetTo(const SVGAnimatedOrient
& aValue
,
411 const nsAString
* aSerialized
) {
412 SetSVGType(eSVGOrient
, &aValue
, aSerialized
);
415 void nsAttrValue::SetTo(const SVGAnimatedIntegerPair
& aValue
,
416 const nsAString
* aSerialized
) {
417 SetSVGType(eSVGIntegerPair
, &aValue
, aSerialized
);
420 void nsAttrValue::SetTo(const SVGAnimatedLength
& aValue
,
421 const nsAString
* aSerialized
) {
422 SetSVGType(eSVGLength
, &aValue
, aSerialized
);
425 void nsAttrValue::SetTo(const SVGLengthList
& aValue
,
426 const nsAString
* aSerialized
) {
427 // While an empty string will parse as a length list, there's no need to store
428 // it (and SetMiscAtomOrString will assert if we try)
429 if (aSerialized
&& aSerialized
->IsEmpty()) {
430 aSerialized
= nullptr;
432 SetSVGType(eSVGLengthList
, &aValue
, aSerialized
);
435 void nsAttrValue::SetTo(const SVGNumberList
& aValue
,
436 const nsAString
* aSerialized
) {
437 // While an empty string will parse as a number list, there's no need to store
438 // it (and SetMiscAtomOrString will assert if we try)
439 if (aSerialized
&& aSerialized
->IsEmpty()) {
440 aSerialized
= nullptr;
442 SetSVGType(eSVGNumberList
, &aValue
, aSerialized
);
445 void nsAttrValue::SetTo(const SVGAnimatedNumberPair
& aValue
,
446 const nsAString
* aSerialized
) {
447 SetSVGType(eSVGNumberPair
, &aValue
, aSerialized
);
450 void nsAttrValue::SetTo(const SVGPathData
& aValue
,
451 const nsAString
* aSerialized
) {
452 // While an empty string will parse as path data, there's no need to store it
453 // (and SetMiscAtomOrString will assert if we try)
454 if (aSerialized
&& aSerialized
->IsEmpty()) {
455 aSerialized
= nullptr;
457 SetSVGType(eSVGPathData
, &aValue
, aSerialized
);
460 void nsAttrValue::SetTo(const SVGPointList
& aValue
,
461 const nsAString
* aSerialized
) {
462 // While an empty string will parse as a point list, there's no need to store
463 // it (and SetMiscAtomOrString will assert if we try)
464 if (aSerialized
&& aSerialized
->IsEmpty()) {
465 aSerialized
= nullptr;
467 SetSVGType(eSVGPointList
, &aValue
, aSerialized
);
470 void nsAttrValue::SetTo(const SVGAnimatedPreserveAspectRatio
& aValue
,
471 const nsAString
* aSerialized
) {
472 SetSVGType(eSVGPreserveAspectRatio
, &aValue
, aSerialized
);
475 void nsAttrValue::SetTo(const SVGStringList
& aValue
,
476 const nsAString
* aSerialized
) {
477 // While an empty string will parse as a string list, there's no need to store
478 // it (and SetMiscAtomOrString will assert if we try)
479 if (aSerialized
&& aSerialized
->IsEmpty()) {
480 aSerialized
= nullptr;
482 SetSVGType(eSVGStringList
, &aValue
, aSerialized
);
485 void nsAttrValue::SetTo(const SVGTransformList
& aValue
,
486 const nsAString
* aSerialized
) {
487 // While an empty string will parse as a transform list, there's no need to
488 // store it (and SetMiscAtomOrString will assert if we try)
489 if (aSerialized
&& aSerialized
->IsEmpty()) {
490 aSerialized
= nullptr;
492 SetSVGType(eSVGTransformList
, &aValue
, aSerialized
);
495 void nsAttrValue::SetTo(const SVGAnimatedViewBox
& aValue
,
496 const nsAString
* aSerialized
) {
497 SetSVGType(eSVGViewBox
, &aValue
, aSerialized
);
500 void nsAttrValue::SwapValueWith(nsAttrValue
& aOther
) {
501 uintptr_t tmp
= aOther
.mBits
;
502 aOther
.mBits
= mBits
;
506 void nsAttrValue::ToString(nsAString
& aResult
) const {
507 MiscContainer
* cont
= nullptr;
508 if (BaseType() == eOtherBase
) {
509 cont
= GetMiscContainer();
511 if (cont
->GetString(aResult
)) {
518 nsStringBuffer
* str
= static_cast<nsStringBuffer
*>(GetPtr());
520 str
->ToString(str
->StorageSize() / sizeof(char16_t
) - 1, aResult
);
527 nsAtom
* atom
= static_cast<nsAtom
*>(GetPtr());
528 atom
->ToString(aResult
);
534 intStr
.AppendInt(GetIntegerValue());
541 MOZ_ASSERT_UNREACHABLE("color attribute without string data");
547 GetEnumString(aResult
, false);
553 str
.AppendFloat(cont
->mDoubleValue
);
555 str
.AppendInt(GetIntInternal());
557 aResult
= str
+ u
"%"_ns
;
561 case eCSSDeclaration
: {
563 MiscContainer
* container
= GetMiscContainer();
564 if (DeclarationBlock
* decl
= container
->mValue
.mCSSDeclaration
) {
565 nsAutoCString result
;
566 decl
->ToString(result
);
567 CopyUTF8toUTF16(result
, aResult
);
570 // This can be reached during parallel selector matching with attribute
571 // selectors on the style attribute. SetMiscAtomOrString handles this
572 // case, and as of this writing this is the only consumer that needs it.
573 const_cast<nsAttrValue
*>(this)->SetMiscAtomOrString(&aResult
);
579 aResult
.AppendFloat(GetDoubleValue());
582 case eSVGIntegerPair
: {
583 SVGAttrValueWrapper::ToString(
584 GetMiscContainer()->mValue
.mSVGAnimatedIntegerPair
, aResult
);
588 SVGAttrValueWrapper::ToString(
589 GetMiscContainer()->mValue
.mSVGAnimatedOrient
, aResult
);
593 SVGAttrValueWrapper::ToString(GetMiscContainer()->mValue
.mSVGLength
,
597 case eSVGLengthList
: {
598 SVGAttrValueWrapper::ToString(GetMiscContainer()->mValue
.mSVGLengthList
,
602 case eSVGNumberList
: {
603 SVGAttrValueWrapper::ToString(GetMiscContainer()->mValue
.mSVGNumberList
,
607 case eSVGNumberPair
: {
608 SVGAttrValueWrapper::ToString(
609 GetMiscContainer()->mValue
.mSVGAnimatedNumberPair
, aResult
);
613 SVGAttrValueWrapper::ToString(GetMiscContainer()->mValue
.mSVGPathData
,
617 case eSVGPointList
: {
618 SVGAttrValueWrapper::ToString(GetMiscContainer()->mValue
.mSVGPointList
,
622 case eSVGPreserveAspectRatio
: {
623 SVGAttrValueWrapper::ToString(
624 GetMiscContainer()->mValue
.mSVGAnimatedPreserveAspectRatio
, aResult
);
627 case eSVGStringList
: {
628 SVGAttrValueWrapper::ToString(GetMiscContainer()->mValue
.mSVGStringList
,
632 case eSVGTransformList
: {
633 SVGAttrValueWrapper::ToString(
634 GetMiscContainer()->mValue
.mSVGTransformList
, aResult
);
638 SVGAttrValueWrapper::ToString(
639 GetMiscContainer()->mValue
.mSVGAnimatedViewBox
, aResult
);
649 already_AddRefed
<nsAtom
> nsAttrValue::GetAsAtom() const {
652 return NS_AtomizeMainThread(GetStringValue());
655 RefPtr
<nsAtom
> atom
= GetAtomValue();
656 return atom
.forget();
662 return NS_AtomizeMainThread(val
);
667 const nsCheapString
nsAttrValue::GetStringValue() const {
668 MOZ_ASSERT(Type() == eString
, "wrong type");
670 return nsCheapString(static_cast<nsStringBuffer
*>(GetPtr()));
673 bool nsAttrValue::GetColorValue(nscolor
& aColor
) const {
674 if (Type() != eColor
) {
675 // Unparseable value, treat as unset.
676 NS_ASSERTION(Type() == eString
, "unexpected type for color-valued attr");
680 aColor
= GetMiscContainer()->mValue
.mColor
;
684 void nsAttrValue::GetEnumString(nsAString
& aResult
, bool aRealTag
) const {
685 MOZ_ASSERT(Type() == eEnum
, "wrong type");
687 uint32_t allEnumBits
= (BaseType() == eIntegerBase
)
688 ? static_cast<uint32_t>(GetIntInternal())
689 : GetMiscContainer()->mValue
.mEnumValue
;
690 int16_t val
= allEnumBits
>> NS_ATTRVALUE_ENUMTABLEINDEX_BITS
;
691 const EnumTable
* table
= sEnumTableArray
->ElementAt(
692 allEnumBits
& NS_ATTRVALUE_ENUMTABLEINDEX_MASK
);
695 if (table
->value
== val
) {
696 aResult
.AssignASCII(table
->tag
);
698 allEnumBits
& NS_ATTRVALUE_ENUMTABLE_VALUE_NEEDS_TO_UPPER
) {
699 nsContentUtils::ASCIIToUpper(aResult
);
706 MOZ_ASSERT_UNREACHABLE("couldn't find value in EnumTable");
709 uint32_t nsAttrValue::GetAtomCount() const {
710 ValueType type
= Type();
716 if (type
== eAtomArray
) {
717 return GetAtomArrayValue()->Length();
723 nsAtom
* nsAttrValue::AtomAt(int32_t aIndex
) const {
724 MOZ_ASSERT(aIndex
>= 0, "Index must not be negative");
725 MOZ_ASSERT(GetAtomCount() > uint32_t(aIndex
), "aIndex out of range");
727 if (BaseType() == eAtomBase
) {
728 return GetAtomValue();
731 NS_ASSERTION(Type() == eAtomArray
, "GetAtomCount must be confused");
733 return GetAtomArrayValue()->ElementAt(aIndex
);
736 uint32_t nsAttrValue::HashValue() const {
737 switch (BaseType()) {
739 nsStringBuffer
* str
= static_cast<nsStringBuffer
*>(GetPtr());
741 uint32_t len
= str
->StorageSize() / sizeof(char16_t
) - 1;
742 return HashString(static_cast<char16_t
*>(str
->Data()), len
);
752 // mBits and uint32_t might have different size. This should silence
753 // any warnings or compile-errors. This is what the implementation of
754 // NS_PTR_TO_INT32 does to take care of the same problem.
759 MiscContainer
* cont
= GetMiscContainer();
760 if (static_cast<ValueBaseType
>(cont
->mStringBits
&
761 NS_ATTRVALUE_BASETYPE_MASK
) == eAtomBase
) {
762 return cont
->mStringBits
- 0;
765 switch (cont
->mType
) {
767 return cont
->mValue
.mInteger
;
770 return cont
->mValue
.mEnumValue
;
773 return cont
->mDoubleValue
;
776 return cont
->mValue
.mColor
;
778 case eCSSDeclaration
: {
779 return NS_PTR_TO_INT32(cont
->mValue
.mCSSDeclaration
);
784 return HashString(str
);
788 uint32_t count
= cont
->mValue
.mAtomArray
->Length();
789 for (RefPtr
<nsAtom
>*cur
= cont
->mValue
.mAtomArray
->Elements(),
792 hash
= AddToHash(hash
, cur
->get());
797 // XXX this is crappy, but oh well
798 return cont
->mDoubleValue
;
800 case eIntMarginValue
: {
801 return NS_PTR_TO_INT32(cont
->mValue
.mIntMargin
);
804 if (IsSVGType(cont
->mType
)) {
805 // All SVG types are just pointers to classes so we can treat them alike
806 return NS_PTR_TO_INT32(cont
->mValue
.mSVGLength
);
808 MOZ_ASSERT_UNREACHABLE("unknown type stored in MiscContainer");
814 bool nsAttrValue::Equals(const nsAttrValue
& aOther
) const {
815 if (BaseType() != aOther
.BaseType()) {
819 switch (BaseType()) {
821 return GetStringValue().Equals(aOther
.GetStringValue());
828 return mBits
== aOther
.mBits
;
832 MiscContainer
* thisCont
= GetMiscContainer();
833 MiscContainer
* otherCont
= aOther
.GetMiscContainer();
834 if (thisCont
== otherCont
) {
838 if (thisCont
->mType
!= otherCont
->mType
) {
842 bool needsStringComparison
= false;
844 switch (thisCont
->mType
) {
846 if (thisCont
->mValue
.mInteger
== otherCont
->mValue
.mInteger
) {
847 needsStringComparison
= true;
852 if (thisCont
->mValue
.mEnumValue
== otherCont
->mValue
.mEnumValue
) {
853 needsStringComparison
= true;
858 if (thisCont
->mDoubleValue
== otherCont
->mDoubleValue
) {
859 needsStringComparison
= true;
864 if (thisCont
->mValue
.mColor
== otherCont
->mValue
.mColor
) {
865 needsStringComparison
= true;
869 case eCSSDeclaration
: {
870 return thisCont
->mValue
.mCSSDeclaration
==
871 otherCont
->mValue
.mCSSDeclaration
;
874 return thisCont
->mValue
.mURL
== otherCont
->mValue
.mURL
;
877 // For classlists we could be insensitive to order, however
878 // classlists are never mapped attributes so they are never compared.
880 if (!(*thisCont
->mValue
.mAtomArray
== *otherCont
->mValue
.mAtomArray
)) {
884 needsStringComparison
= true;
888 return thisCont
->mDoubleValue
== otherCont
->mDoubleValue
;
890 case eIntMarginValue
: {
891 return thisCont
->mValue
.mIntMargin
== otherCont
->mValue
.mIntMargin
;
894 if (IsSVGType(thisCont
->mType
)) {
895 // Currently this method is never called for nsAttrValue objects that
896 // point to SVG data types.
897 // If that changes then we probably want to add methods to the
898 // corresponding SVG types to compare their base values.
899 // As a shortcut, however, we can begin by comparing the pointers.
900 MOZ_ASSERT(false, "Comparing nsAttrValues that point to SVG data");
903 MOZ_ASSERT_UNREACHABLE("unknown type stored in MiscContainer");
907 if (needsStringComparison
) {
908 if (thisCont
->mStringBits
== otherCont
->mStringBits
) {
911 if ((static_cast<ValueBaseType
>(thisCont
->mStringBits
&
912 NS_ATTRVALUE_BASETYPE_MASK
) ==
914 (static_cast<ValueBaseType
>(otherCont
->mStringBits
&
915 NS_ATTRVALUE_BASETYPE_MASK
) ==
917 return nsCheapString(reinterpret_cast<nsStringBuffer
*>(
918 static_cast<uintptr_t>(thisCont
->mStringBits
)))
919 .Equals(nsCheapString(reinterpret_cast<nsStringBuffer
*>(
920 static_cast<uintptr_t>(otherCont
->mStringBits
))));
926 bool nsAttrValue::Equals(const nsAString
& aValue
,
927 nsCaseTreatment aCaseSensitive
) const {
928 switch (BaseType()) {
930 nsStringBuffer
* str
= static_cast<nsStringBuffer
*>(GetPtr());
932 nsDependentString
dep(static_cast<char16_t
*>(str
->Data()),
933 str
->StorageSize() / sizeof(char16_t
) - 1);
934 return aCaseSensitive
== eCaseMatters
936 : nsContentUtils::EqualsIgnoreASCIICase(aValue
, dep
);
938 return aValue
.IsEmpty();
941 if (aCaseSensitive
== eCaseMatters
) {
942 return static_cast<nsAtom
*>(GetPtr())->Equals(aValue
);
944 return nsContentUtils::EqualsIgnoreASCIICase(
945 nsDependentAtomString(static_cast<nsAtom
*>(GetPtr())), aValue
);
952 return aCaseSensitive
== eCaseMatters
954 : nsContentUtils::EqualsIgnoreASCIICase(val
, aValue
);
957 bool nsAttrValue::Equals(const nsAtom
* aValue
,
958 nsCaseTreatment aCaseSensitive
) const {
959 if (aCaseSensitive
!= eCaseMatters
) {
960 // Need a better way to handle this!
962 aValue
->ToString(value
);
963 return Equals(value
, aCaseSensitive
);
966 switch (BaseType()) {
968 nsStringBuffer
* str
= static_cast<nsStringBuffer
*>(GetPtr());
970 nsDependentString
dep(static_cast<char16_t
*>(str
->Data()),
971 str
->StorageSize() / sizeof(char16_t
) - 1);
972 return aValue
->Equals(dep
);
974 return aValue
== nsGkAtoms::_empty
;
977 return static_cast<nsAtom
*>(GetPtr()) == aValue
;
985 return aValue
->Equals(val
);
989 static bool Check(const char16_t
* aAttrValue
, size_t aAttrLen
,
990 const nsAString
& aSearchValue
,
991 nsCaseTreatment aCaseSensitive
) {
992 if (aCaseSensitive
== eCaseMatters
) {
993 if (aSearchValue
.Length() > aAttrLen
) {
996 return !memcmp(aAttrValue
, aSearchValue
.BeginReading(),
997 aSearchValue
.Length() * sizeof(char16_t
));
999 return StringBeginsWith(nsDependentString(aAttrValue
, aAttrLen
),
1001 nsASCIICaseInsensitiveStringComparator
);
1005 struct HasSuffixFn
{
1006 static bool Check(const char16_t
* aAttrValue
, size_t aAttrLen
,
1007 const nsAString
& aSearchValue
,
1008 nsCaseTreatment aCaseSensitive
) {
1009 if (aCaseSensitive
== eCaseMatters
) {
1010 if (aSearchValue
.Length() > aAttrLen
) {
1013 return !memcmp(aAttrValue
+ aAttrLen
- aSearchValue
.Length(),
1014 aSearchValue
.BeginReading(),
1015 aSearchValue
.Length() * sizeof(char16_t
));
1017 return StringEndsWith(nsDependentString(aAttrValue
, aAttrLen
), aSearchValue
,
1018 nsASCIICaseInsensitiveStringComparator
);
1022 struct HasSubstringFn
{
1023 static bool Check(const char16_t
* aAttrValue
, size_t aAttrLen
,
1024 const nsAString
& aSearchValue
,
1025 nsCaseTreatment aCaseSensitive
) {
1026 if (aCaseSensitive
== eCaseMatters
) {
1027 if (aSearchValue
.IsEmpty()) {
1030 const char16_t
* end
= aAttrValue
+ aAttrLen
;
1031 return std::search(aAttrValue
, end
, aSearchValue
.BeginReading(),
1032 aSearchValue
.EndReading()) != end
;
1034 return FindInReadable(aSearchValue
, nsDependentString(aAttrValue
, aAttrLen
),
1035 nsASCIICaseInsensitiveStringComparator
);
1039 template <typename F
>
1040 bool nsAttrValue::SubstringCheck(const nsAString
& aValue
,
1041 nsCaseTreatment aCaseSensitive
) const {
1042 switch (BaseType()) {
1044 auto str
= static_cast<nsStringBuffer
*>(GetPtr());
1046 return F::Check(static_cast<char16_t
*>(str
->Data()),
1047 str
->StorageSize() / sizeof(char16_t
) - 1, aValue
,
1050 return aValue
.IsEmpty();
1053 auto atom
= static_cast<nsAtom
*>(GetPtr());
1054 return F::Check(atom
->GetUTF16String(), atom
->GetLength(), aValue
,
1063 return F::Check(val
.BeginReading(), val
.Length(), aValue
, aCaseSensitive
);
1066 bool nsAttrValue::HasPrefix(const nsAString
& aValue
,
1067 nsCaseTreatment aCaseSensitive
) const {
1068 return SubstringCheck
<HasPrefixFn
>(aValue
, aCaseSensitive
);
1071 bool nsAttrValue::HasSuffix(const nsAString
& aValue
,
1072 nsCaseTreatment aCaseSensitive
) const {
1073 return SubstringCheck
<HasSuffixFn
>(aValue
, aCaseSensitive
);
1076 bool nsAttrValue::HasSubstring(const nsAString
& aValue
,
1077 nsCaseTreatment aCaseSensitive
) const {
1078 return SubstringCheck
<HasSubstringFn
>(aValue
, aCaseSensitive
);
1081 bool nsAttrValue::EqualsAsStrings(const nsAttrValue
& aOther
) const {
1082 if (Type() == aOther
.Type()) {
1083 return Equals(aOther
);
1086 // We need to serialize at least one nsAttrValue before passing to
1087 // Equals(const nsAString&), but we can avoid unnecessarily serializing both
1088 // by checking if one is already of a string type.
1089 bool thisIsString
= (BaseType() == eStringBase
|| BaseType() == eAtomBase
);
1090 const nsAttrValue
& lhs
= thisIsString
? *this : aOther
;
1091 const nsAttrValue
& rhs
= thisIsString
? aOther
: *this;
1093 switch (rhs
.BaseType()) {
1095 return lhs
.Equals(rhs
.GetAtomValue(), eCaseMatters
);
1098 return lhs
.Equals(rhs
.GetStringValue(), eCaseMatters
);
1103 return lhs
.Equals(val
, eCaseMatters
);
1108 bool nsAttrValue::Contains(nsAtom
* aValue
,
1109 nsCaseTreatment aCaseSensitive
) const {
1110 switch (BaseType()) {
1112 nsAtom
* atom
= GetAtomValue();
1113 if (aCaseSensitive
== eCaseMatters
) {
1114 return aValue
== atom
;
1117 // For performance reasons, don't do a full on unicode case insensitive
1118 // string comparison. This is only used for quirks mode anyway.
1119 return nsContentUtils::EqualsIgnoreASCIICase(aValue
, atom
);
1122 if (Type() == eAtomArray
) {
1123 AtomArray
* array
= GetAtomArrayValue();
1124 if (aCaseSensitive
== eCaseMatters
) {
1125 return array
->Contains(aValue
);
1128 for (RefPtr
<nsAtom
>& cur
: *array
) {
1129 // For performance reasons, don't do a full on unicode case
1130 // insensitive string comparison. This is only used for quirks mode
1132 if (nsContentUtils::EqualsIgnoreASCIICase(aValue
, cur
)) {
1143 struct AtomArrayStringComparator
{
1144 bool Equals(nsAtom
* atom
, const nsAString
& string
) const {
1145 return atom
->Equals(string
);
1149 bool nsAttrValue::Contains(const nsAString
& aValue
) const {
1150 switch (BaseType()) {
1152 nsAtom
* atom
= GetAtomValue();
1153 return atom
->Equals(aValue
);
1156 if (Type() == eAtomArray
) {
1157 AtomArray
* array
= GetAtomArrayValue();
1158 return array
->Contains(aValue
, AtomArrayStringComparator());
1166 void nsAttrValue::ParseAtom(const nsAString
& aValue
) {
1169 RefPtr
<nsAtom
> atom
= NS_Atomize(aValue
);
1171 SetPtrValueAndType(atom
.forget().take(), eAtomBase
);
1175 void nsAttrValue::ParseAtomArray(const nsAString
& aValue
) {
1176 nsAString::const_iterator iter
, end
;
1177 aValue
.BeginReading(iter
);
1178 aValue
.EndReading(end
);
1179 bool hasSpace
= false;
1181 // skip initial whitespace
1182 while (iter
!= end
&& nsContentUtils::IsHTMLWhitespace(*iter
)) {
1192 nsAString::const_iterator
start(iter
);
1194 // get first - and often only - atom
1197 } while (iter
!= end
&& !nsContentUtils::IsHTMLWhitespace(*iter
));
1199 RefPtr
<nsAtom
> classAtom
= NS_AtomizeMainThread(Substring(start
, iter
));
1206 while (iter
!= end
&& nsContentUtils::IsHTMLWhitespace(*iter
)) {
1211 if (iter
== end
&& !hasSpace
) {
1212 // we only found one classname and there was no whitespace so
1213 // don't bother storing a list
1215 nsAtom
* atom
= nullptr;
1216 classAtom
.swap(atom
);
1217 SetPtrValueAndType(atom
, eAtomBase
);
1221 if (!EnsureEmptyAtomArray()) {
1225 AtomArray
* array
= GetAtomArrayValue();
1227 // XXX(Bug 1631371) Check if this should use a fallible operation as it
1228 // pretended earlier.
1229 array
->AppendElement(std::move(classAtom
));
1231 // parse the rest of the classnames
1232 while (iter
!= end
) {
1237 } while (iter
!= end
&& !nsContentUtils::IsHTMLWhitespace(*iter
));
1239 classAtom
= NS_AtomizeMainThread(Substring(start
, iter
));
1241 // XXX(Bug 1631371) Check if this should use a fallible operation as it
1242 // pretended earlier.
1243 array
->AppendElement(std::move(classAtom
));
1246 while (iter
!= end
&& nsContentUtils::IsHTMLWhitespace(*iter
)) {
1251 SetMiscAtomOrString(&aValue
);
1254 void nsAttrValue::ParseStringOrAtom(const nsAString
& aValue
) {
1255 uint32_t len
= aValue
.Length();
1256 // Don't bother with atoms if it's an empty string since
1257 // we can store those efficiently anyway.
1258 if (len
&& len
<= NS_ATTRVALUE_MAX_STRINGLENGTH_ATOM
) {
1265 void nsAttrValue::ParsePartMapping(const nsAString
& aValue
) {
1267 MiscContainer
* cont
= EnsureEmptyMiscContainer();
1269 cont
->mType
= eShadowParts
;
1270 cont
->mValue
.mShadowParts
= new ShadowParts(ShadowParts::Parse(aValue
));
1272 SetMiscAtomOrString(&aValue
);
1273 MOZ_ASSERT(cont
->mValue
.mRefCount
== 1);
1276 void nsAttrValue::SetIntValueAndType(int32_t aValue
, ValueType aType
,
1277 const nsAString
* aStringValue
) {
1278 if (aStringValue
|| aValue
> NS_ATTRVALUE_INTEGERTYPE_MAXVALUE
||
1279 aValue
< NS_ATTRVALUE_INTEGERTYPE_MINVALUE
) {
1280 MiscContainer
* cont
= EnsureEmptyMiscContainer();
1283 cont
->mValue
.mInteger
= aValue
;
1287 cont
->mDoubleValue
= aValue
;
1291 cont
->mValue
.mEnumValue
= aValue
;
1295 MOZ_ASSERT_UNREACHABLE("unknown integer type");
1299 cont
->mType
= aType
;
1300 SetMiscAtomOrString(aStringValue
);
1302 NS_ASSERTION(!mBits
, "Reset before calling SetIntValueAndType!");
1303 mBits
= (aValue
* NS_ATTRVALUE_INTEGERTYPE_MULTIPLIER
) | aType
;
1307 void nsAttrValue::SetDoubleValueAndType(double aValue
, ValueType aType
,
1308 const nsAString
* aStringValue
) {
1309 MOZ_ASSERT(aType
== eDoubleValue
|| aType
== ePercent
, "Unexpected type");
1310 MiscContainer
* cont
= EnsureEmptyMiscContainer();
1311 cont
->mDoubleValue
= aValue
;
1312 cont
->mType
= aType
;
1313 SetMiscAtomOrString(aStringValue
);
1316 int16_t nsAttrValue::GetEnumTableIndex(const EnumTable
* aTable
) {
1317 int16_t index
= sEnumTableArray
->IndexOf(aTable
);
1319 index
= sEnumTableArray
->Length();
1320 NS_ASSERTION(index
<= NS_ATTRVALUE_ENUMTABLEINDEX_MAXVALUE
,
1321 "too many enum tables");
1322 sEnumTableArray
->AppendElement(aTable
);
1328 int32_t nsAttrValue::EnumTableEntryToValue(const EnumTable
* aEnumTable
,
1329 const EnumTable
* aTableEntry
) {
1330 int16_t index
= GetEnumTableIndex(aEnumTable
);
1332 (aTableEntry
->value
<< NS_ATTRVALUE_ENUMTABLEINDEX_BITS
) + index
;
1336 bool nsAttrValue::ParseEnumValue(const nsAString
& aValue
,
1337 const EnumTable
* aTable
, bool aCaseSensitive
,
1338 const EnumTable
* aDefaultValue
) {
1340 const EnumTable
* tableEntry
= aTable
;
1342 while (tableEntry
->tag
) {
1343 if (aCaseSensitive
? aValue
.EqualsASCII(tableEntry
->tag
)
1344 : aValue
.LowerCaseEqualsASCII(tableEntry
->tag
)) {
1345 int32_t value
= EnumTableEntryToValue(aTable
, tableEntry
);
1347 bool equals
= aCaseSensitive
|| aValue
.EqualsASCII(tableEntry
->tag
);
1350 tag
.AssignASCII(tableEntry
->tag
);
1351 nsContentUtils::ASCIIToUpper(tag
);
1352 if ((equals
= tag
.Equals(aValue
))) {
1353 value
|= NS_ATTRVALUE_ENUMTABLE_VALUE_NEEDS_TO_UPPER
;
1356 SetIntValueAndType(value
, eEnum
, equals
? nullptr : &aValue
);
1357 NS_ASSERTION(GetEnumValue() == tableEntry
->value
,
1358 "failed to store enum properly");
1365 if (aDefaultValue
) {
1366 MOZ_ASSERT(aTable
<= aDefaultValue
&& aDefaultValue
< tableEntry
,
1367 "aDefaultValue not inside aTable?");
1368 SetIntValueAndType(EnumTableEntryToValue(aTable
, aDefaultValue
), eEnum
,
1376 bool nsAttrValue::DoParseHTMLDimension(const nsAString
& aInput
,
1377 bool aEnsureNonzero
) {
1380 // We don't use nsContentUtils::ParseHTMLInteger here because we
1381 // need a bunch of behavioral differences from it. We _could_ try to
1382 // use it, but it would not be a great fit.
1384 // https://html.spec.whatwg.org/multipage/#rules-for-parsing-dimension-values
1387 const char16_t
* position
= aInput
.BeginReading();
1388 const char16_t
* end
= aInput
.EndReading();
1390 // We will need to keep track of whether this was a canonical representation
1391 // or not. It's non-canonical if it has leading whitespace, leading '+',
1392 // leading '0' characters, or trailing garbage.
1393 bool canonical
= true;
1396 while (position
!= end
&& nsContentUtils::IsHTMLWhitespace(*position
)) {
1397 canonical
= false; // Leading whitespace
1402 if (position
== end
|| *position
< char16_t('0') ||
1403 *position
> char16_t('9')) {
1408 CheckedInt32 value
= 0;
1410 // Collect up leading '0' first to avoid extra branching in the main
1411 // loop to set 'canonical' properly.
1412 while (position
!= end
&& *position
== char16_t('0')) {
1413 canonical
= false; // Leading '0'
1417 // Now collect up other digits.
1418 while (position
!= end
&& *position
>= char16_t('0') &&
1419 *position
<= char16_t('9')) {
1420 value
= value
* 10 + (*position
- char16_t('0'));
1421 if (!value
.isValid()) {
1422 // The spec assumes we can deal with arbitrary-size integers here, but we
1423 // really can't. If someone sets something too big, just bail out and
1430 // Step 6 is implemented implicitly via the various "position != end" guards
1431 // from this point on.
1433 Maybe
<double> doubleValue
;
1434 // Step 7. The return in step 7.2 is handled by just falling through to the
1435 // code below this block when we reach end of input or a non-digit, because
1436 // the while loop will terminate at that point.
1437 if (position
!= end
&& *position
== char16_t('.')) {
1438 canonical
= false; // Let's not rely on double serialization reproducing
1439 // the string we started with.
1442 // If we have a '.' _not_ followed by digits, this is not as efficient as it
1443 // could be, because we will store as a double while we could have stored as
1444 // an int. But that seems like a pretty rare case.
1445 doubleValue
.emplace(value
.value());
1447 double divisor
= 1.0f
;
1449 while (position
!= end
&& *position
>= char16_t('0') &&
1450 *position
<= char16_t('9')) {
1452 divisor
= divisor
* 10.0f
;
1454 doubleValue
.ref() += (*position
- char16_t('0')) / divisor
;
1457 // Step 7.4.4 and 7.4.5 are captured in the while loop condition and the
1458 // "position != end" checks below.
1462 if (aEnsureNonzero
&& value
.value() == 0 &&
1463 (!doubleValue
|| *doubleValue
== 0.0f
)) {
1464 // Not valid. Just drop it.
1468 // Step 8 and the spec's early return from step 7.2.
1470 if (position
!= end
&& *position
== char16_t('%')) {
1473 } else if (doubleValue
) {
1474 type
= eDoubleValue
;
1479 if (position
!= end
) {
1484 MOZ_ASSERT(!canonical
, "We set it false above!");
1485 SetDoubleValueAndType(*doubleValue
, type
, &aInput
);
1487 SetIntValueAndType(value
.value(), type
, canonical
? nullptr : &aInput
);
1493 MOZ_ASSERT(str
== aInput
, "We messed up our 'canonical' boolean!");
1499 bool nsAttrValue::ParseIntWithBounds(const nsAString
& aString
, int32_t aMin
,
1501 MOZ_ASSERT(aMin
< aMax
, "bad boundaries");
1505 nsContentUtils::ParseHTMLIntegerResultFlags result
;
1506 int32_t originalVal
= nsContentUtils::ParseHTMLInteger(aString
, &result
);
1507 if (result
& nsContentUtils::eParseHTMLInteger_Error
) {
1511 int32_t val
= std::max(originalVal
, aMin
);
1512 val
= std::min(val
, aMax
);
1514 (val
!= originalVal
) ||
1515 (result
& nsContentUtils::eParseHTMLInteger_NonStandard
) ||
1516 (result
& nsContentUtils::eParseHTMLInteger_DidNotConsumeAllInput
);
1518 SetIntValueAndType(val
, eInteger
, nonStrict
? &aString
: nullptr);
1523 void nsAttrValue::ParseIntWithFallback(const nsAString
& aString
,
1524 int32_t aDefault
, int32_t aMax
) {
1527 nsContentUtils::ParseHTMLIntegerResultFlags result
;
1528 int32_t val
= nsContentUtils::ParseHTMLInteger(aString
, &result
);
1529 bool nonStrict
= false;
1530 if ((result
& nsContentUtils::eParseHTMLInteger_Error
) || val
< 1) {
1540 if ((result
& nsContentUtils::eParseHTMLInteger_NonStandard
) ||
1541 (result
& nsContentUtils::eParseHTMLInteger_DidNotConsumeAllInput
)) {
1545 SetIntValueAndType(val
, eInteger
, nonStrict
? &aString
: nullptr);
1548 void nsAttrValue::ParseClampedNonNegativeInt(const nsAString
& aString
,
1549 int32_t aDefault
, int32_t aMin
,
1553 nsContentUtils::ParseHTMLIntegerResultFlags result
;
1554 int32_t val
= nsContentUtils::ParseHTMLInteger(aString
, &result
);
1556 (result
& nsContentUtils::eParseHTMLInteger_NonStandard
) ||
1557 (result
& nsContentUtils::eParseHTMLInteger_DidNotConsumeAllInput
);
1559 if (result
& nsContentUtils::eParseHTMLInteger_ErrorOverflow
) {
1560 if (result
& nsContentUtils::eParseHTMLInteger_Negative
) {
1566 } else if ((result
& nsContentUtils::eParseHTMLInteger_Error
) || val
< 0) {
1569 } else if (val
< aMin
) {
1572 } else if (val
> aMax
) {
1577 SetIntValueAndType(val
, eInteger
, nonStrict
? &aString
: nullptr);
1580 bool nsAttrValue::ParseNonNegativeIntValue(const nsAString
& aString
) {
1583 nsContentUtils::ParseHTMLIntegerResultFlags result
;
1584 int32_t originalVal
= nsContentUtils::ParseHTMLInteger(aString
, &result
);
1585 if ((result
& nsContentUtils::eParseHTMLInteger_Error
) || originalVal
< 0) {
1590 (result
& nsContentUtils::eParseHTMLInteger_NonStandard
) ||
1591 (result
& nsContentUtils::eParseHTMLInteger_DidNotConsumeAllInput
);
1593 SetIntValueAndType(originalVal
, eInteger
, nonStrict
? &aString
: nullptr);
1598 bool nsAttrValue::ParsePositiveIntValue(const nsAString
& aString
) {
1601 nsContentUtils::ParseHTMLIntegerResultFlags result
;
1602 int32_t originalVal
= nsContentUtils::ParseHTMLInteger(aString
, &result
);
1603 if ((result
& nsContentUtils::eParseHTMLInteger_Error
) || originalVal
<= 0) {
1608 (result
& nsContentUtils::eParseHTMLInteger_NonStandard
) ||
1609 (result
& nsContentUtils::eParseHTMLInteger_DidNotConsumeAllInput
);
1611 SetIntValueAndType(originalVal
, eInteger
, nonStrict
? &aString
: nullptr);
1616 void nsAttrValue::SetColorValue(nscolor aColor
, const nsAString
& aString
) {
1617 nsStringBuffer
* buf
= GetStringBuffer(aString
).take();
1622 MiscContainer
* cont
= EnsureEmptyMiscContainer();
1623 cont
->mValue
.mColor
= aColor
;
1624 cont
->mType
= eColor
;
1626 // Save the literal string we were passed for round-tripping.
1627 cont
->SetStringBitsMainThread(reinterpret_cast<uintptr_t>(buf
) | eStringBase
);
1630 bool nsAttrValue::ParseColor(const nsAString
& aString
) {
1633 // FIXME (partially, at least): HTML5's algorithm says we shouldn't do
1634 // the whitespace compression, trimming, or the test for emptiness.
1635 // (I'm a little skeptical that we shouldn't do the whitespace
1636 // trimming; WebKit also does it.)
1637 nsAutoString
colorStr(aString
);
1638 colorStr
.CompressWhitespace(true, true);
1639 if (colorStr
.IsEmpty()) {
1644 // No color names begin with a '#'; in standards mode, all acceptable
1645 // numeric colors do.
1646 if (colorStr
.First() == '#') {
1647 nsDependentString
withoutHash(colorStr
.get() + 1, colorStr
.Length() - 1);
1648 if (NS_HexToRGBA(withoutHash
, nsHexColorType::NoAlpha
, &color
)) {
1649 SetColorValue(color
, aString
);
1653 if (NS_ColorNameToRGB(colorStr
, &color
)) {
1654 SetColorValue(color
, aString
);
1659 // FIXME (maybe): HTML5 says we should handle system colors. This
1660 // means we probably need another storage type, since we'd need to
1661 // handle dynamic changes. However, I think this is a bad idea:
1662 // http://lists.whatwg.org/pipermail/whatwg-whatwg.org/2010-May/026449.html
1664 // Use NS_LooseHexToRGB as a fallback if nothing above worked.
1665 if (NS_LooseHexToRGB(colorStr
, &color
)) {
1666 SetColorValue(color
, aString
);
1673 bool nsAttrValue::ParseDoubleValue(const nsAString
& aString
) {
1677 double val
= PromiseFlatString(aString
).ToDouble(&ec
);
1678 if (NS_FAILED(ec
)) {
1682 MiscContainer
* cont
= EnsureEmptyMiscContainer();
1683 cont
->mDoubleValue
= val
;
1684 cont
->mType
= eDoubleValue
;
1685 nsAutoString serializedFloat
;
1686 serializedFloat
.AppendFloat(val
);
1687 SetMiscAtomOrString(serializedFloat
.Equals(aString
) ? nullptr : &aString
);
1691 bool nsAttrValue::ParseIntMarginValue(const nsAString
& aString
) {
1694 nsIntMargin margins
;
1695 if (!nsContentUtils::ParseIntMarginValue(aString
, margins
)) return false;
1697 MiscContainer
* cont
= EnsureEmptyMiscContainer();
1698 cont
->mValue
.mIntMargin
= new nsIntMargin(margins
);
1699 cont
->mType
= eIntMarginValue
;
1700 SetMiscAtomOrString(&aString
);
1704 bool nsAttrValue::ParseStyleAttribute(const nsAString
& aString
,
1705 nsIPrincipal
* aMaybeScriptedPrincipal
,
1706 nsStyledElement
* aElement
) {
1707 dom::Document
* ownerDoc
= aElement
->OwnerDoc();
1708 nsHTMLCSSStyleSheet
* sheet
= ownerDoc
->GetInlineStyleSheet();
1709 nsIURI
* baseURI
= aElement
->GetBaseURIForStyleAttr();
1710 nsIURI
* docURI
= ownerDoc
->GetDocumentURI();
1712 NS_ASSERTION(aElement
->NodePrincipal() == ownerDoc
->NodePrincipal(),
1713 "This is unexpected");
1715 nsIPrincipal
* principal
= aMaybeScriptedPrincipal
? aMaybeScriptedPrincipal
1716 : aElement
->NodePrincipal();
1718 // If the (immutable) document URI does not match the element's base URI
1719 // (the common case is that they do match) do not cache the rule. This is
1720 // because the results of the CSS parser are dependent on these URIs, and we
1721 // do not want to have to account for the URIs in the hash lookup.
1722 // Similarly, if the triggering principal does not match the node principal,
1723 // do not cache the rule, since the principal will be encoded in any parsed
1724 // URLs in the rule.
1725 const bool cachingAllowed
=
1726 sheet
&& baseURI
== docURI
&& principal
== aElement
->NodePrincipal();
1727 if (cachingAllowed
) {
1728 MiscContainer
* cont
= sheet
->LookupStyleAttr(aString
);
1730 // Set our MiscContainer to the cached one.
1732 SetPtrValueAndType(cont
, eOtherBase
);
1737 nsCOMPtr
<nsIReferrerInfo
> referrerInfo
=
1738 dom::ReferrerInfo::CreateForInternalCSSResources(ownerDoc
);
1739 auto data
= MakeRefPtr
<URLExtraData
>(baseURI
, referrerInfo
, principal
);
1740 RefPtr
<DeclarationBlock
> decl
= DeclarationBlock::FromCssText(
1741 aString
, data
, ownerDoc
->GetCompatibilityMode(), ownerDoc
->CSSLoader(),
1742 dom::CSSRule_Binding::STYLE_RULE
);
1746 decl
->SetHTMLCSSStyleSheet(sheet
);
1747 SetTo(decl
.forget(), &aString
);
1749 if (cachingAllowed
) {
1750 MiscContainer
* cont
= GetMiscContainer();
1757 void nsAttrValue::SetMiscAtomOrString(const nsAString
* aValue
) {
1758 NS_ASSERTION(GetMiscContainer(), "Must have MiscContainer!");
1759 NS_ASSERTION(!GetMiscContainer()->mStringBits
|| IsInServoTraversal(),
1760 "Trying to re-set atom or string!");
1762 uint32_t len
= aValue
->Length();
1763 // * We're allowing eCSSDeclaration attributes to store empty
1764 // strings as it can be beneficial to store an empty style
1765 // attribute as a parsed rule.
1766 // * We're allowing enumerated values because sometimes the empty
1767 // string corresponds to a particular enumerated value, especially
1768 // for enumerated values that are not limited enumerated.
1769 // Add other types as needed.
1770 NS_ASSERTION(len
|| Type() == eCSSDeclaration
|| Type() == eEnum
,
1772 MiscContainer
* cont
= GetMiscContainer();
1774 if (len
<= NS_ATTRVALUE_MAX_STRINGLENGTH_ATOM
) {
1775 nsAtom
* atom
= MOZ_LIKELY(!IsInServoTraversal())
1776 ? NS_AtomizeMainThread(*aValue
).take()
1777 : NS_Atomize(*aValue
).take();
1778 NS_ENSURE_TRUE_VOID(atom
);
1779 uintptr_t bits
= reinterpret_cast<uintptr_t>(atom
) | eAtomBase
;
1781 // In the common case we're not in the servo traversal, and we can just
1782 // set the bits normally. The parallel case requires more care.
1783 if (MOZ_LIKELY(!IsInServoTraversal())) {
1784 cont
->SetStringBitsMainThread(bits
);
1785 } else if (!cont
->mStringBits
.compareExchange(0, bits
)) {
1786 // We raced with somebody else setting the bits. Release our copy.
1790 nsStringBuffer
* buffer
= GetStringBuffer(*aValue
).take();
1791 NS_ENSURE_TRUE_VOID(buffer
);
1792 uintptr_t bits
= reinterpret_cast<uintptr_t>(buffer
) | eStringBase
;
1794 // In the common case we're not in the servo traversal, and we can just
1795 // set the bits normally. The parallel case requires more care.
1796 if (MOZ_LIKELY(!IsInServoTraversal())) {
1797 cont
->SetStringBitsMainThread(bits
);
1798 } else if (!cont
->mStringBits
.compareExchange(0, bits
)) {
1799 // We raced with somebody else setting the bits. Release our copy.
1806 void nsAttrValue::ResetMiscAtomOrString() {
1807 MiscContainer
* cont
= GetMiscContainer();
1808 void* ptr
= MISC_STR_PTR(cont
);
1810 if (static_cast<ValueBaseType
>(cont
->mStringBits
&
1811 NS_ATTRVALUE_BASETYPE_MASK
) == eStringBase
) {
1812 static_cast<nsStringBuffer
*>(ptr
)->Release();
1814 static_cast<nsAtom
*>(ptr
)->Release();
1816 cont
->SetStringBitsMainThread(0);
1820 void nsAttrValue::SetSVGType(ValueType aType
, const void* aValue
,
1821 const nsAString
* aSerialized
) {
1822 MOZ_ASSERT(IsSVGType(aType
), "Not an SVG type");
1824 MiscContainer
* cont
= EnsureEmptyMiscContainer();
1825 // All SVG types are just pointers to classes so just setting any of them
1826 // will do. We'll lose type-safety but the signature of the calling
1827 // function should ensure we don't get anything unexpected, and once we
1828 // stick aValue in a union we lose type information anyway.
1829 cont
->mValue
.mSVGLength
= static_cast<const SVGAnimatedLength
*>(aValue
);
1830 cont
->mType
= aType
;
1831 SetMiscAtomOrString(aSerialized
);
1834 MiscContainer
* nsAttrValue::ClearMiscContainer() {
1835 MiscContainer
* cont
= nullptr;
1836 if (BaseType() == eOtherBase
) {
1837 cont
= GetMiscContainer();
1838 if (cont
->IsRefCounted() && cont
->mValue
.mRefCount
> 1) {
1839 // This MiscContainer is shared, we need a new one.
1842 cont
= AllocMiscContainer();
1843 SetPtrValueAndType(cont
, eOtherBase
);
1845 switch (cont
->mType
) {
1846 case eCSSDeclaration
: {
1847 MOZ_ASSERT(cont
->mValue
.mRefCount
== 1);
1850 NS_RELEASE(cont
->mValue
.mCSSDeclaration
);
1853 case eShadowParts
: {
1854 MOZ_ASSERT(cont
->mValue
.mRefCount
== 1);
1856 delete cont
->mValue
.mShadowParts
;
1860 NS_RELEASE(cont
->mValue
.mURL
);
1864 delete cont
->mValue
.mAtomArray
;
1867 case eIntMarginValue
: {
1868 delete cont
->mValue
.mIntMargin
;
1876 ResetMiscAtomOrString();
1884 MiscContainer
* nsAttrValue::EnsureEmptyMiscContainer() {
1885 MiscContainer
* cont
= ClearMiscContainer();
1887 MOZ_ASSERT(BaseType() == eOtherBase
);
1888 ResetMiscAtomOrString();
1889 cont
= GetMiscContainer();
1891 cont
= AllocMiscContainer();
1892 SetPtrValueAndType(cont
, eOtherBase
);
1898 bool nsAttrValue::EnsureEmptyAtomArray() {
1899 if (Type() == eAtomArray
) {
1900 ResetMiscAtomOrString();
1901 GetAtomArrayValue()->Clear();
1905 MiscContainer
* cont
= EnsureEmptyMiscContainer();
1906 cont
->mValue
.mAtomArray
= new AtomArray
;
1907 cont
->mType
= eAtomArray
;
1912 already_AddRefed
<nsStringBuffer
> nsAttrValue::GetStringBuffer(
1913 const nsAString
& aValue
) const {
1914 uint32_t len
= aValue
.Length();
1919 RefPtr
<nsStringBuffer
> buf
= nsStringBuffer::FromString(aValue
);
1920 if (buf
&& (buf
->StorageSize() / sizeof(char16_t
) - 1) == len
) {
1921 return buf
.forget();
1924 buf
= nsStringBuffer::Alloc((len
+ 1) * sizeof(char16_t
));
1928 char16_t
* data
= static_cast<char16_t
*>(buf
->Data());
1929 CopyUnicodeTo(aValue
, 0, data
, len
);
1930 data
[len
] = char16_t(0);
1931 return buf
.forget();
1934 size_t nsAttrValue::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf
) const {
1937 switch (BaseType()) {
1939 nsStringBuffer
* str
= static_cast<nsStringBuffer
*>(GetPtr());
1940 n
+= str
? str
->SizeOfIncludingThisIfUnshared(aMallocSizeOf
) : 0;
1944 MiscContainer
* container
= GetMiscContainer();
1948 if (container
->IsRefCounted() && container
->mValue
.mRefCount
> 1) {
1949 // We don't report this MiscContainer at all in order to avoid
1950 // twice-reporting it.
1951 // TODO DMD, bug 1027551 - figure out how to report this ref-counted
1952 // object just once.
1955 n
+= aMallocSizeOf(container
);
1957 void* otherPtr
= MISC_STR_PTR(container
);
1958 // We only count the size of the object pointed by otherPtr if it's a
1959 // string. When it's an atom, it's counted separatly.
1960 if (otherPtr
&& static_cast<ValueBaseType
>(container
->mStringBits
&
1961 NS_ATTRVALUE_BASETYPE_MASK
) ==
1963 nsStringBuffer
* str
= static_cast<nsStringBuffer
*>(otherPtr
);
1964 n
+= str
? str
->SizeOfIncludingThisIfUnshared(aMallocSizeOf
) : 0;
1967 if (Type() == eCSSDeclaration
&& container
->mValue
.mCSSDeclaration
) {
1968 // TODO: mCSSDeclaration might be owned by another object which
1969 // would make us count them twice, bug 677493.
1970 // Bug 1281964: For DeclarationBlock if we do measure we'll
1971 // need a way to call the Servo heap_size_of function.
1972 // n += container->mCSSDeclaration->SizeOfIncludingThis(aMallocSizeOf);
1973 } else if (Type() == eAtomArray
&& container
->mValue
.mAtomArray
) {
1974 // Don't measure each nsAtom, they are measured separatly.
1975 n
+= container
->mValue
.mAtomArray
->ShallowSizeOfIncludingThis(
1980 case eAtomBase
: // Atoms are counted separately.
1981 case eIntegerBase
: // The value is in mBits, nothing to do.