diff options
author | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
---|---|---|
committer | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
commit | 5f8de423f190bbb79a62f804151bc24824fa32d8 (patch) | |
tree | 10027f336435511475e392454359edea8e25895d /dom/base/nsAttrValue.cpp | |
parent | 49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff) | |
download | UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip |
Add m-esr52 at 52.6.0
Diffstat (limited to 'dom/base/nsAttrValue.cpp')
-rw-r--r-- | dom/base/nsAttrValue.cpp | 1974 |
1 files changed, 1974 insertions, 0 deletions
diff --git a/dom/base/nsAttrValue.cpp b/dom/base/nsAttrValue.cpp new file mode 100644 index 000000000..8eb1aaf97 --- /dev/null +++ b/dom/base/nsAttrValue.cpp @@ -0,0 +1,1974 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * A struct that represents the value (type and actual data) of an + * attribute. + */ + +#include "mozilla/DebugOnly.h" +#include "mozilla/HashFunctions.h" + +#include "nsAttrValue.h" +#include "nsAttrValueInlines.h" +#include "nsIAtom.h" +#include "nsUnicharUtils.h" +#include "mozilla/MemoryReporting.h" +#include "mozilla/ServoBindingTypes.h" +#include "mozilla/DeclarationBlockInlines.h" +#include "nsContentUtils.h" +#include "nsReadableUtils.h" +#include "prprf.h" +#include "nsHTMLCSSStyleSheet.h" +#include "nsCSSParser.h" +#include "nsStyledElement.h" +#include "nsIURI.h" +#include "nsIDocument.h" +#include <algorithm> + +#ifdef LoadImage +// Undefine LoadImage to prevent naming conflict with Windows. +#undef LoadImage +#endif + +using namespace mozilla; + +#define MISC_STR_PTR(_cont) \ + reinterpret_cast<void*>((_cont)->mStringBits & NS_ATTRVALUE_POINTERVALUE_MASK) + +bool +MiscContainer::GetString(nsAString& aString) const +{ + void* ptr = MISC_STR_PTR(this); + + if (!ptr) { + return false; + } + + if (static_cast<nsAttrValue::ValueBaseType>(mStringBits & + NS_ATTRVALUE_BASETYPE_MASK) == + nsAttrValue::eStringBase) { + nsStringBuffer* buffer = static_cast<nsStringBuffer*>(ptr); + if (!buffer) { + return false; + } + + buffer->ToString(buffer->StorageSize() / sizeof(char16_t) - 1, aString); + return true; + } + + nsIAtom* atom = static_cast<nsIAtom*>(ptr); + if (!atom) { + return false; + } + + atom->ToString(aString); + return true; +} + +void +MiscContainer::Cache() +{ + // Not implemented for anything else yet. + if (mType != nsAttrValue::eCSSDeclaration) { + MOZ_ASSERT_UNREACHABLE("unexpected cached nsAttrValue type"); + return; + } + + MOZ_ASSERT(IsRefCounted()); + MOZ_ASSERT(mValue.mRefCount > 0); + MOZ_ASSERT(!mValue.mCached); + + nsHTMLCSSStyleSheet* sheet = mValue.mCSSDeclaration->GetHTMLCSSStyleSheet(); + if (!sheet) { + return; + } + + nsString str; + bool gotString = GetString(str); + if (!gotString) { + return; + } + + sheet->CacheStyleAttr(str, this); + mValue.mCached = 1; + + // This has to be immutable once it goes into the cache. + mValue.mCSSDeclaration->SetImmutable(); +} + +void +MiscContainer::Evict() +{ + // Not implemented for anything else yet. + if (mType != nsAttrValue::eCSSDeclaration) { + MOZ_ASSERT_UNREACHABLE("unexpected cached nsAttrValue type"); + return; + } + MOZ_ASSERT(IsRefCounted()); + MOZ_ASSERT(mValue.mRefCount == 0); + + if (!mValue.mCached) { + return; + } + + nsHTMLCSSStyleSheet* sheet = mValue.mCSSDeclaration->GetHTMLCSSStyleSheet(); + MOZ_ASSERT(sheet); + + nsString str; + DebugOnly<bool> gotString = GetString(str); + MOZ_ASSERT(gotString); + + sheet->EvictStyleAttr(str, this); + mValue.mCached = 0; +} + +nsTArray<const nsAttrValue::EnumTable*>* nsAttrValue::sEnumTableArray = nullptr; + +nsAttrValue::nsAttrValue() + : mBits(0) +{ +} + +nsAttrValue::nsAttrValue(const nsAttrValue& aOther) + : mBits(0) +{ + SetTo(aOther); +} + +nsAttrValue::nsAttrValue(const nsAString& aValue) + : mBits(0) +{ + SetTo(aValue); +} + +nsAttrValue::nsAttrValue(nsIAtom* aValue) + : mBits(0) +{ + SetTo(aValue); +} + +nsAttrValue::nsAttrValue(already_AddRefed<DeclarationBlock> aValue, + const nsAString* aSerialized) + : mBits(0) +{ + SetTo(Move(aValue), aSerialized); +} + +nsAttrValue::nsAttrValue(const nsIntMargin& aValue) + : mBits(0) +{ + SetTo(aValue); +} + +nsAttrValue::~nsAttrValue() +{ + ResetIfSet(); +} + +/* static */ +nsresult +nsAttrValue::Init() +{ + NS_ASSERTION(!sEnumTableArray, "nsAttrValue already initialized"); + sEnumTableArray = new nsTArray<const EnumTable*>; + return NS_OK; +} + +/* static */ +void +nsAttrValue::Shutdown() +{ + delete sEnumTableArray; + sEnumTableArray = nullptr; +} + +nsAttrValue::ValueType +nsAttrValue::Type() const +{ + switch (BaseType()) { + case eIntegerBase: + { + return static_cast<ValueType>(mBits & NS_ATTRVALUE_INTEGERTYPE_MASK); + } + case eOtherBase: + { + return GetMiscContainer()->mType; + } + default: + { + return static_cast<ValueType>(static_cast<uint16_t>(BaseType())); + } + } +} + +void +nsAttrValue::Reset() +{ + switch(BaseType()) { + case eStringBase: + { + nsStringBuffer* str = static_cast<nsStringBuffer*>(GetPtr()); + if (str) { + str->Release(); + } + + break; + } + case eOtherBase: + { + MiscContainer* cont = GetMiscContainer(); + if (cont->IsRefCounted() && cont->mValue.mRefCount > 1) { + NS_RELEASE(cont); + break; + } + + delete ClearMiscContainer(); + + break; + } + case eAtomBase: + { + nsIAtom* atom = GetAtomValue(); + NS_RELEASE(atom); + + break; + } + case eIntegerBase: + { + break; + } + } + + mBits = 0; +} + +void +nsAttrValue::SetTo(const nsAttrValue& aOther) +{ + if (this == &aOther) { + return; + } + + switch (aOther.BaseType()) { + case eStringBase: + { + ResetIfSet(); + nsStringBuffer* str = static_cast<nsStringBuffer*>(aOther.GetPtr()); + if (str) { + str->AddRef(); + SetPtrValueAndType(str, eStringBase); + } + return; + } + case eOtherBase: + { + break; + } + case eAtomBase: + { + ResetIfSet(); + nsIAtom* atom = aOther.GetAtomValue(); + NS_ADDREF(atom); + SetPtrValueAndType(atom, eAtomBase); + return; + } + case eIntegerBase: + { + ResetIfSet(); + mBits = aOther.mBits; + return; + } + } + + MiscContainer* otherCont = aOther.GetMiscContainer(); + if (otherCont->IsRefCounted()) { + delete ClearMiscContainer(); + NS_ADDREF(otherCont); + SetPtrValueAndType(otherCont, eOtherBase); + return; + } + + MiscContainer* cont = EnsureEmptyMiscContainer(); + switch (otherCont->mType) { + case eInteger: + { + cont->mValue.mInteger = otherCont->mValue.mInteger; + break; + } + case eEnum: + { + cont->mValue.mEnumValue = otherCont->mValue.mEnumValue; + break; + } + case ePercent: + { + cont->mValue.mPercent = otherCont->mValue.mPercent; + break; + } + case eColor: + { + cont->mValue.mColor = otherCont->mValue.mColor; + break; + } + case eCSSDeclaration: + { + MOZ_CRASH("These should be refcounted!"); + } + case eURL: + { + NS_ADDREF(cont->mValue.mURL = otherCont->mValue.mURL); + break; + } + case eImage: + { + NS_ADDREF(cont->mValue.mImage = otherCont->mValue.mImage); + break; + } + case eAtomArray: + { + if (!EnsureEmptyAtomArray() || + !GetAtomArrayValue()->AppendElements(*otherCont->mValue.mAtomArray)) { + Reset(); + return; + } + break; + } + case eDoubleValue: + { + cont->mDoubleValue = otherCont->mDoubleValue; + break; + } + case eIntMarginValue: + { + if (otherCont->mValue.mIntMargin) + cont->mValue.mIntMargin = + new nsIntMargin(*otherCont->mValue.mIntMargin); + break; + } + default: + { + if (IsSVGType(otherCont->mType)) { + // All SVG types are just pointers to classes and will therefore have + // the same size so it doesn't really matter which one we assign + cont->mValue.mSVGAngle = otherCont->mValue.mSVGAngle; + } else { + NS_NOTREACHED("unknown type stored in MiscContainer"); + } + break; + } + } + + void* otherPtr = MISC_STR_PTR(otherCont); + if (otherPtr) { + if (static_cast<ValueBaseType>(otherCont->mStringBits & NS_ATTRVALUE_BASETYPE_MASK) == + eStringBase) { + static_cast<nsStringBuffer*>(otherPtr)->AddRef(); + } else { + static_cast<nsIAtom*>(otherPtr)->AddRef(); + } + cont->mStringBits = otherCont->mStringBits; + } + // Note, set mType after switch-case, otherwise EnsureEmptyAtomArray doesn't + // work correctly. + cont->mType = otherCont->mType; +} + +void +nsAttrValue::SetTo(const nsAString& aValue) +{ + ResetIfSet(); + nsStringBuffer* buf = GetStringBuffer(aValue).take(); + if (buf) { + SetPtrValueAndType(buf, eStringBase); + } +} + +void +nsAttrValue::SetTo(nsIAtom* aValue) +{ + ResetIfSet(); + if (aValue) { + NS_ADDREF(aValue); + SetPtrValueAndType(aValue, eAtomBase); + } +} + +void +nsAttrValue::SetTo(int16_t aInt) +{ + ResetIfSet(); + SetIntValueAndType(aInt, eInteger, nullptr); +} + +void +nsAttrValue::SetTo(int32_t aInt, const nsAString* aSerialized) +{ + ResetIfSet(); + SetIntValueAndType(aInt, eInteger, aSerialized); +} + +void +nsAttrValue::SetTo(double aValue, const nsAString* aSerialized) +{ + MiscContainer* cont = EnsureEmptyMiscContainer(); + cont->mDoubleValue = aValue; + cont->mType = eDoubleValue; + SetMiscAtomOrString(aSerialized); +} + +void +nsAttrValue::SetTo(already_AddRefed<DeclarationBlock> aValue, + const nsAString* aSerialized) +{ + MiscContainer* cont = EnsureEmptyMiscContainer(); + MOZ_ASSERT(cont->mValue.mRefCount == 0); + cont->mValue.mCSSDeclaration = aValue.take(); + cont->mType = eCSSDeclaration; + NS_ADDREF(cont); + SetMiscAtomOrString(aSerialized); + MOZ_ASSERT(cont->mValue.mRefCount == 1); +} + +void +nsAttrValue::SetTo(css::URLValue* aValue, const nsAString* aSerialized) +{ + MiscContainer* cont = EnsureEmptyMiscContainer(); + NS_ADDREF(cont->mValue.mURL = aValue); + cont->mType = eURL; + SetMiscAtomOrString(aSerialized); +} + +void +nsAttrValue::SetTo(const nsIntMargin& aValue) +{ + MiscContainer* cont = EnsureEmptyMiscContainer(); + cont->mValue.mIntMargin = new nsIntMargin(aValue); + cont->mType = eIntMarginValue; +} + +void +nsAttrValue::SetToSerialized(const nsAttrValue& aOther) +{ + if (aOther.Type() != nsAttrValue::eString && + aOther.Type() != nsAttrValue::eAtom) { + nsAutoString val; + aOther.ToString(val); + SetTo(val); + } else { + SetTo(aOther); + } +} + +void +nsAttrValue::SetTo(const nsSVGAngle& aValue, const nsAString* aSerialized) +{ + SetSVGType(eSVGAngle, &aValue, aSerialized); +} + +void +nsAttrValue::SetTo(const nsSVGIntegerPair& aValue, const nsAString* aSerialized) +{ + SetSVGType(eSVGIntegerPair, &aValue, aSerialized); +} + +void +nsAttrValue::SetTo(const nsSVGLength2& aValue, const nsAString* aSerialized) +{ + SetSVGType(eSVGLength, &aValue, aSerialized); +} + +void +nsAttrValue::SetTo(const SVGLengthList& aValue, + const nsAString* aSerialized) +{ + // While an empty string will parse as a length list, there's no need to store + // it (and SetMiscAtomOrString will assert if we try) + if (aSerialized && aSerialized->IsEmpty()) { + aSerialized = nullptr; + } + SetSVGType(eSVGLengthList, &aValue, aSerialized); +} + +void +nsAttrValue::SetTo(const SVGNumberList& aValue, + const nsAString* aSerialized) +{ + // While an empty string will parse as a number list, there's no need to store + // it (and SetMiscAtomOrString will assert if we try) + if (aSerialized && aSerialized->IsEmpty()) { + aSerialized = nullptr; + } + SetSVGType(eSVGNumberList, &aValue, aSerialized); +} + +void +nsAttrValue::SetTo(const nsSVGNumberPair& aValue, const nsAString* aSerialized) +{ + SetSVGType(eSVGNumberPair, &aValue, aSerialized); +} + +void +nsAttrValue::SetTo(const SVGPathData& aValue, + const nsAString* aSerialized) +{ + // While an empty string will parse as path data, there's no need to store it + // (and SetMiscAtomOrString will assert if we try) + if (aSerialized && aSerialized->IsEmpty()) { + aSerialized = nullptr; + } + SetSVGType(eSVGPathData, &aValue, aSerialized); +} + +void +nsAttrValue::SetTo(const SVGPointList& aValue, + const nsAString* aSerialized) +{ + // While an empty string will parse as a point list, there's no need to store + // it (and SetMiscAtomOrString will assert if we try) + if (aSerialized && aSerialized->IsEmpty()) { + aSerialized = nullptr; + } + SetSVGType(eSVGPointList, &aValue, aSerialized); +} + +void +nsAttrValue::SetTo(const SVGAnimatedPreserveAspectRatio& aValue, + const nsAString* aSerialized) +{ + SetSVGType(eSVGPreserveAspectRatio, &aValue, aSerialized); +} + +void +nsAttrValue::SetTo(const SVGStringList& aValue, + const nsAString* aSerialized) +{ + // While an empty string will parse as a string list, there's no need to store + // it (and SetMiscAtomOrString will assert if we try) + if (aSerialized && aSerialized->IsEmpty()) { + aSerialized = nullptr; + } + SetSVGType(eSVGStringList, &aValue, aSerialized); +} + +void +nsAttrValue::SetTo(const SVGTransformList& aValue, + const nsAString* aSerialized) +{ + // While an empty string will parse as a transform list, there's no need to + // store it (and SetMiscAtomOrString will assert if we try) + if (aSerialized && aSerialized->IsEmpty()) { + aSerialized = nullptr; + } + SetSVGType(eSVGTransformList, &aValue, aSerialized); +} + +void +nsAttrValue::SetTo(const nsSVGViewBox& aValue, const nsAString* aSerialized) +{ + SetSVGType(eSVGViewBox, &aValue, aSerialized); +} + +void +nsAttrValue::SwapValueWith(nsAttrValue& aOther) +{ + uintptr_t tmp = aOther.mBits; + aOther.mBits = mBits; + mBits = tmp; +} + +void +nsAttrValue::ToString(nsAString& aResult) const +{ + MiscContainer* cont = nullptr; + if (BaseType() == eOtherBase) { + cont = GetMiscContainer(); + + if (cont->GetString(aResult)) { + return; + } + } + + switch(Type()) { + case eString: + { + nsStringBuffer* str = static_cast<nsStringBuffer*>(GetPtr()); + if (str) { + str->ToString(str->StorageSize()/sizeof(char16_t) - 1, aResult); + } + else { + aResult.Truncate(); + } + break; + } + case eAtom: + { + nsIAtom *atom = static_cast<nsIAtom*>(GetPtr()); + atom->ToString(aResult); + + break; + } + case eInteger: + { + nsAutoString intStr; + intStr.AppendInt(GetIntegerValue()); + aResult = intStr; + + break; + } +#ifdef DEBUG + case eColor: + { + NS_NOTREACHED("color attribute without string data"); + aResult.Truncate(); + break; + } +#endif + case eEnum: + { + GetEnumString(aResult, false); + break; + } + case ePercent: + { + nsAutoString intStr; + intStr.AppendInt(cont ? cont->mValue.mPercent : GetIntInternal()); + aResult = intStr + NS_LITERAL_STRING("%"); + + break; + } + case eCSSDeclaration: + { + aResult.Truncate(); + MiscContainer *container = GetMiscContainer(); + if (DeclarationBlock* decl = container->mValue.mCSSDeclaration) { + decl->ToString(aResult); + } + const_cast<nsAttrValue*>(this)->SetMiscAtomOrString(&aResult); + + break; + } + case eDoubleValue: + { + aResult.Truncate(); + aResult.AppendFloat(GetDoubleValue()); + break; + } + case eSVGAngle: + { + SVGAttrValueWrapper::ToString(GetMiscContainer()->mValue.mSVGAngle, + aResult); + break; + } + case eSVGIntegerPair: + { + SVGAttrValueWrapper::ToString(GetMiscContainer()->mValue.mSVGIntegerPair, + aResult); + break; + } + case eSVGLength: + { + SVGAttrValueWrapper::ToString(GetMiscContainer()->mValue.mSVGLength, + aResult); + break; + } + case eSVGLengthList: + { + SVGAttrValueWrapper::ToString(GetMiscContainer()->mValue.mSVGLengthList, + aResult); + break; + } + case eSVGNumberList: + { + SVGAttrValueWrapper::ToString(GetMiscContainer()->mValue.mSVGNumberList, + aResult); + break; + } + case eSVGNumberPair: + { + SVGAttrValueWrapper::ToString(GetMiscContainer()->mValue.mSVGNumberPair, + aResult); + break; + } + case eSVGPathData: + { + SVGAttrValueWrapper::ToString(GetMiscContainer()->mValue.mSVGPathData, + aResult); + break; + } + case eSVGPointList: + { + SVGAttrValueWrapper::ToString(GetMiscContainer()->mValue.mSVGPointList, + aResult); + break; + } + case eSVGPreserveAspectRatio: + { + SVGAttrValueWrapper::ToString(GetMiscContainer()->mValue.mSVGPreserveAspectRatio, + aResult); + break; + } + case eSVGStringList: + { + SVGAttrValueWrapper::ToString(GetMiscContainer()->mValue.mSVGStringList, + aResult); + break; + } + case eSVGTransformList: + { + SVGAttrValueWrapper::ToString(GetMiscContainer()->mValue.mSVGTransformList, + aResult); + break; + } + case eSVGViewBox: + { + SVGAttrValueWrapper::ToString(GetMiscContainer()->mValue.mSVGViewBox, + aResult); + break; + } + default: + { + aResult.Truncate(); + break; + } + } +} + +already_AddRefed<nsIAtom> +nsAttrValue::GetAsAtom() const +{ + switch (Type()) { + case eString: + return NS_Atomize(GetStringValue()); + + case eAtom: + { + nsCOMPtr<nsIAtom> atom = GetAtomValue(); + return atom.forget(); + } + + default: + { + nsAutoString val; + ToString(val); + return NS_Atomize(val); + } + } +} + +const nsCheapString +nsAttrValue::GetStringValue() const +{ + NS_PRECONDITION(Type() == eString, "wrong type"); + + return nsCheapString(static_cast<nsStringBuffer*>(GetPtr())); +} + +bool +nsAttrValue::GetColorValue(nscolor& aColor) const +{ + if (Type() != eColor) { + // Unparseable value, treat as unset. + NS_ASSERTION(Type() == eString, "unexpected type for color-valued attr"); + return false; + } + + aColor = GetMiscContainer()->mValue.mColor; + return true; +} + +void +nsAttrValue::GetEnumString(nsAString& aResult, bool aRealTag) const +{ + NS_PRECONDITION(Type() == eEnum, "wrong type"); + + uint32_t allEnumBits = + (BaseType() == eIntegerBase) ? static_cast<uint32_t>(GetIntInternal()) + : GetMiscContainer()->mValue.mEnumValue; + int16_t val = allEnumBits >> NS_ATTRVALUE_ENUMTABLEINDEX_BITS; + const EnumTable* table = sEnumTableArray-> + ElementAt(allEnumBits & NS_ATTRVALUE_ENUMTABLEINDEX_MASK); + + while (table->tag) { + if (table->value == val) { + aResult.AssignASCII(table->tag); + if (!aRealTag && allEnumBits & NS_ATTRVALUE_ENUMTABLE_VALUE_NEEDS_TO_UPPER) { + nsContentUtils::ASCIIToUpper(aResult); + } + return; + } + table++; + } + + NS_NOTREACHED("couldn't find value in EnumTable"); +} + +uint32_t +nsAttrValue::GetAtomCount() const +{ + ValueType type = Type(); + + if (type == eAtom) { + return 1; + } + + if (type == eAtomArray) { + return GetAtomArrayValue()->Length(); + } + + return 0; +} + +nsIAtom* +nsAttrValue::AtomAt(int32_t aIndex) const +{ + NS_PRECONDITION(aIndex >= 0, "Index must not be negative"); + NS_PRECONDITION(GetAtomCount() > uint32_t(aIndex), "aIndex out of range"); + + if (BaseType() == eAtomBase) { + return GetAtomValue(); + } + + NS_ASSERTION(Type() == eAtomArray, "GetAtomCount must be confused"); + + return GetAtomArrayValue()->ElementAt(aIndex); +} + +uint32_t +nsAttrValue::HashValue() const +{ + switch(BaseType()) { + case eStringBase: + { + nsStringBuffer* str = static_cast<nsStringBuffer*>(GetPtr()); + if (str) { + uint32_t len = str->StorageSize()/sizeof(char16_t) - 1; + return HashString(static_cast<char16_t*>(str->Data()), len); + } + + return 0; + } + case eOtherBase: + { + break; + } + case eAtomBase: + case eIntegerBase: + { + // mBits and uint32_t might have different size. This should silence + // any warnings or compile-errors. This is what the implementation of + // NS_PTR_TO_INT32 does to take care of the same problem. + return mBits - 0; + } + } + + MiscContainer* cont = GetMiscContainer(); + if (static_cast<ValueBaseType>(cont->mStringBits & NS_ATTRVALUE_BASETYPE_MASK) + == eAtomBase) { + return cont->mStringBits - 0; + } + + switch (cont->mType) { + case eInteger: + { + return cont->mValue.mInteger; + } + case eEnum: + { + return cont->mValue.mEnumValue; + } + case ePercent: + { + return cont->mValue.mPercent; + } + case eColor: + { + return cont->mValue.mColor; + } + case eCSSDeclaration: + { + return NS_PTR_TO_INT32(cont->mValue.mCSSDeclaration); + } + // Intentionally identical, so that loading the image does not change the + // hash code. + case eURL: + case eImage: + { + nsString str; + ToString(str); + return HashString(str); + } + case eAtomArray: + { + uint32_t hash = 0; + uint32_t count = cont->mValue.mAtomArray->Length(); + for (nsCOMPtr<nsIAtom> *cur = cont->mValue.mAtomArray->Elements(), + *end = cur + count; + cur != end; ++cur) { + hash = AddToHash(hash, cur->get()); + } + return hash; + } + case eDoubleValue: + { + // XXX this is crappy, but oh well + return cont->mDoubleValue; + } + case eIntMarginValue: + { + return NS_PTR_TO_INT32(cont->mValue.mIntMargin); + } + default: + { + if (IsSVGType(cont->mType)) { + // All SVG types are just pointers to classes so we can treat them alike + return NS_PTR_TO_INT32(cont->mValue.mSVGAngle); + } + NS_NOTREACHED("unknown type stored in MiscContainer"); + return 0; + } + } +} + +bool +nsAttrValue::Equals(const nsAttrValue& aOther) const +{ + if (BaseType() != aOther.BaseType()) { + return false; + } + + switch(BaseType()) { + case eStringBase: + { + return GetStringValue().Equals(aOther.GetStringValue()); + } + case eOtherBase: + { + break; + } + case eAtomBase: + case eIntegerBase: + { + return mBits == aOther.mBits; + } + } + + MiscContainer* thisCont = GetMiscContainer(); + MiscContainer* otherCont = aOther.GetMiscContainer(); + if (thisCont == otherCont) { + return true; + } + + if (thisCont->mType != otherCont->mType) { + return false; + } + + bool needsStringComparison = false; + + switch (thisCont->mType) { + case eInteger: + { + if (thisCont->mValue.mInteger == otherCont->mValue.mInteger) { + needsStringComparison = true; + } + break; + } + case eEnum: + { + if (thisCont->mValue.mEnumValue == otherCont->mValue.mEnumValue) { + needsStringComparison = true; + } + break; + } + case ePercent: + { + if (thisCont->mValue.mPercent == otherCont->mValue.mPercent) { + needsStringComparison = true; + } + break; + } + case eColor: + { + if (thisCont->mValue.mColor == otherCont->mValue.mColor) { + needsStringComparison = true; + } + break; + } + case eCSSDeclaration: + { + return thisCont->mValue.mCSSDeclaration == + otherCont->mValue.mCSSDeclaration; + } + case eURL: + { + return thisCont->mValue.mURL == otherCont->mValue.mURL; + } + case eImage: + { + return thisCont->mValue.mImage == otherCont->mValue.mImage; + } + case eAtomArray: + { + // For classlists we could be insensitive to order, however + // classlists are never mapped attributes so they are never compared. + + if (!(*thisCont->mValue.mAtomArray == *otherCont->mValue.mAtomArray)) { + return false; + } + + needsStringComparison = true; + break; + } + case eDoubleValue: + { + return thisCont->mDoubleValue == otherCont->mDoubleValue; + } + case eIntMarginValue: + { + return thisCont->mValue.mIntMargin == otherCont->mValue.mIntMargin; + } + default: + { + if (IsSVGType(thisCont->mType)) { + // Currently this method is never called for nsAttrValue objects that + // point to SVG data types. + // If that changes then we probably want to add methods to the + // corresponding SVG types to compare their base values. + // As a shortcut, however, we can begin by comparing the pointers. + MOZ_ASSERT(false, "Comparing nsAttrValues that point to SVG data"); + return false; + } + NS_NOTREACHED("unknown type stored in MiscContainer"); + return false; + } + } + if (needsStringComparison) { + if (thisCont->mStringBits == otherCont->mStringBits) { + return true; + } + if ((static_cast<ValueBaseType>(thisCont->mStringBits & NS_ATTRVALUE_BASETYPE_MASK) == + eStringBase) && + (static_cast<ValueBaseType>(otherCont->mStringBits & NS_ATTRVALUE_BASETYPE_MASK) == + eStringBase)) { + return nsCheapString(reinterpret_cast<nsStringBuffer*>(thisCont->mStringBits)).Equals( + nsCheapString(reinterpret_cast<nsStringBuffer*>(otherCont->mStringBits))); + } + } + return false; +} + +bool +nsAttrValue::Equals(const nsAString& aValue, + nsCaseTreatment aCaseSensitive) const +{ + switch (BaseType()) { + case eStringBase: + { + nsStringBuffer* str = static_cast<nsStringBuffer*>(GetPtr()); + if (str) { + nsDependentString dep(static_cast<char16_t*>(str->Data()), + str->StorageSize()/sizeof(char16_t) - 1); + return aCaseSensitive == eCaseMatters ? aValue.Equals(dep) : + nsContentUtils::EqualsIgnoreASCIICase(aValue, dep); + } + return aValue.IsEmpty(); + } + case eAtomBase: + if (aCaseSensitive == eCaseMatters) { + return static_cast<nsIAtom*>(GetPtr())->Equals(aValue); + } + return nsContentUtils::EqualsIgnoreASCIICase( + nsDependentAtomString(static_cast<nsIAtom*>(GetPtr())), + aValue); + default: + break; + } + + nsAutoString val; + ToString(val); + return aCaseSensitive == eCaseMatters ? val.Equals(aValue) : + nsContentUtils::EqualsIgnoreASCIICase(val, aValue); +} + +bool +nsAttrValue::Equals(nsIAtom* aValue, nsCaseTreatment aCaseSensitive) const +{ + if (aCaseSensitive != eCaseMatters) { + // Need a better way to handle this! + nsAutoString value; + aValue->ToString(value); + return Equals(value, aCaseSensitive); + } + + switch (BaseType()) { + case eStringBase: + { + nsStringBuffer* str = static_cast<nsStringBuffer*>(GetPtr()); + if (str) { + nsDependentString dep(static_cast<char16_t*>(str->Data()), + str->StorageSize()/sizeof(char16_t) - 1); + return aValue->Equals(dep); + } + return aValue == nsGkAtoms::_empty; + } + case eAtomBase: + { + return static_cast<nsIAtom*>(GetPtr()) == aValue; + } + default: + break; + } + + nsAutoString val; + ToString(val); + return aValue->Equals(val); +} + +bool +nsAttrValue::EqualsAsStrings(const nsAttrValue& aOther) const +{ + if (Type() == aOther.Type()) { + return Equals(aOther); + } + + // We need to serialize at least one nsAttrValue before passing to + // Equals(const nsAString&), but we can avoid unnecessarily serializing both + // by checking if one is already of a string type. + bool thisIsString = (BaseType() == eStringBase || BaseType() == eAtomBase); + const nsAttrValue& lhs = thisIsString ? *this : aOther; + const nsAttrValue& rhs = thisIsString ? aOther : *this; + + switch (rhs.BaseType()) { + case eAtomBase: + return lhs.Equals(rhs.GetAtomValue(), eCaseMatters); + + case eStringBase: + return lhs.Equals(rhs.GetStringValue(), eCaseMatters); + + default: + { + nsAutoString val; + rhs.ToString(val); + return lhs.Equals(val, eCaseMatters); + } + } +} + +bool +nsAttrValue::Contains(nsIAtom* aValue, nsCaseTreatment aCaseSensitive) const +{ + switch (BaseType()) { + case eAtomBase: + { + nsIAtom* atom = GetAtomValue(); + + if (aCaseSensitive == eCaseMatters) { + return aValue == atom; + } + + // For performance reasons, don't do a full on unicode case insensitive + // string comparison. This is only used for quirks mode anyway. + return + nsContentUtils::EqualsIgnoreASCIICase(nsDependentAtomString(aValue), + nsDependentAtomString(atom)); + } + default: + { + if (Type() == eAtomArray) { + AtomArray* array = GetAtomArrayValue(); + if (aCaseSensitive == eCaseMatters) { + return array->Contains(aValue); + } + + nsDependentAtomString val1(aValue); + + for (nsCOMPtr<nsIAtom> *cur = array->Elements(), + *end = cur + array->Length(); + cur != end; ++cur) { + // For performance reasons, don't do a full on unicode case + // insensitive string comparison. This is only used for quirks mode + // anyway. + if (nsContentUtils::EqualsIgnoreASCIICase(val1, + nsDependentAtomString(*cur))) { + return true; + } + } + } + } + } + + return false; +} + +struct AtomArrayStringComparator { + bool Equals(nsIAtom* atom, const nsAString& string) const { + return atom->Equals(string); + } +}; + +bool +nsAttrValue::Contains(const nsAString& aValue) const +{ + switch (BaseType()) { + case eAtomBase: + { + nsIAtom* atom = GetAtomValue(); + return atom->Equals(aValue); + } + default: + { + if (Type() == eAtomArray) { + AtomArray* array = GetAtomArrayValue(); + return array->Contains(aValue, AtomArrayStringComparator()); + } + } + } + + return false; +} + +void +nsAttrValue::ParseAtom(const nsAString& aValue) +{ + ResetIfSet(); + + nsCOMPtr<nsIAtom> atom = NS_Atomize(aValue); + if (atom) { + SetPtrValueAndType(atom.forget().take(), eAtomBase); + } +} + +void +nsAttrValue::ParseAtomArray(const nsAString& aValue) +{ + nsAString::const_iterator iter, end; + aValue.BeginReading(iter); + aValue.EndReading(end); + bool hasSpace = false; + + // skip initial whitespace + while (iter != end && nsContentUtils::IsHTMLWhitespace(*iter)) { + hasSpace = true; + ++iter; + } + + if (iter == end) { + SetTo(aValue); + return; + } + + nsAString::const_iterator start(iter); + + // get first - and often only - atom + do { + ++iter; + } while (iter != end && !nsContentUtils::IsHTMLWhitespace(*iter)); + + nsCOMPtr<nsIAtom> classAtom = NS_Atomize(Substring(start, iter)); + if (!classAtom) { + Reset(); + return; + } + + // skip whitespace + while (iter != end && nsContentUtils::IsHTMLWhitespace(*iter)) { + hasSpace = true; + ++iter; + } + + if (iter == end && !hasSpace) { + // we only found one classname and there was no whitespace so + // don't bother storing a list + ResetIfSet(); + nsIAtom* atom = nullptr; + classAtom.swap(atom); + SetPtrValueAndType(atom, eAtomBase); + return; + } + + if (!EnsureEmptyAtomArray()) { + return; + } + + AtomArray* array = GetAtomArrayValue(); + + if (!array->AppendElement(classAtom)) { + Reset(); + return; + } + + // parse the rest of the classnames + while (iter != end) { + start = iter; + + do { + ++iter; + } while (iter != end && !nsContentUtils::IsHTMLWhitespace(*iter)); + + classAtom = NS_Atomize(Substring(start, iter)); + + if (!array->AppendElement(classAtom)) { + Reset(); + return; + } + + // skip whitespace + while (iter != end && nsContentUtils::IsHTMLWhitespace(*iter)) { + ++iter; + } + } + + SetMiscAtomOrString(&aValue); + return; +} + +void +nsAttrValue::ParseStringOrAtom(const nsAString& aValue) +{ + uint32_t len = aValue.Length(); + // Don't bother with atoms if it's an empty string since + // we can store those efficently anyway. + if (len && len <= NS_ATTRVALUE_MAX_STRINGLENGTH_ATOM) { + ParseAtom(aValue); + } + else { + SetTo(aValue); + } +} + +void +nsAttrValue::SetIntValueAndType(int32_t aValue, ValueType aType, + const nsAString* aStringValue) +{ + if (aStringValue || aValue > NS_ATTRVALUE_INTEGERTYPE_MAXVALUE || + aValue < NS_ATTRVALUE_INTEGERTYPE_MINVALUE) { + MiscContainer* cont = EnsureEmptyMiscContainer(); + switch (aType) { + case eInteger: + { + cont->mValue.mInteger = aValue; + break; + } + case ePercent: + { + cont->mValue.mPercent = aValue; + break; + } + case eEnum: + { + cont->mValue.mEnumValue = aValue; + break; + } + default: + { + NS_NOTREACHED("unknown integer type"); + break; + } + } + cont->mType = aType; + SetMiscAtomOrString(aStringValue); + } else { + NS_ASSERTION(!mBits, "Reset before calling SetIntValueAndType!"); + mBits = (aValue * NS_ATTRVALUE_INTEGERTYPE_MULTIPLIER) | aType; + } +} + +int16_t +nsAttrValue::GetEnumTableIndex(const EnumTable* aTable) +{ + int16_t index = sEnumTableArray->IndexOf(aTable); + if (index < 0) { + index = sEnumTableArray->Length(); + NS_ASSERTION(index <= NS_ATTRVALUE_ENUMTABLEINDEX_MAXVALUE, + "too many enum tables"); + sEnumTableArray->AppendElement(aTable); + } + + return index; +} + +int32_t +nsAttrValue::EnumTableEntryToValue(const EnumTable* aEnumTable, + const EnumTable* aTableEntry) +{ + int16_t index = GetEnumTableIndex(aEnumTable); + int32_t value = (aTableEntry->value << NS_ATTRVALUE_ENUMTABLEINDEX_BITS) + + index; + return value; +} + +bool +nsAttrValue::ParseEnumValue(const nsAString& aValue, + const EnumTable* aTable, + bool aCaseSensitive, + const EnumTable* aDefaultValue) +{ + ResetIfSet(); + const EnumTable* tableEntry = aTable; + + while (tableEntry->tag) { + if (aCaseSensitive ? aValue.EqualsASCII(tableEntry->tag) : + aValue.LowerCaseEqualsASCII(tableEntry->tag)) { + int32_t value = EnumTableEntryToValue(aTable, tableEntry); + + bool equals = aCaseSensitive || aValue.EqualsASCII(tableEntry->tag); + if (!equals) { + nsAutoString tag; + tag.AssignASCII(tableEntry->tag); + nsContentUtils::ASCIIToUpper(tag); + if ((equals = tag.Equals(aValue))) { + value |= NS_ATTRVALUE_ENUMTABLE_VALUE_NEEDS_TO_UPPER; + } + } + SetIntValueAndType(value, eEnum, equals ? nullptr : &aValue); + NS_ASSERTION(GetEnumValue() == tableEntry->value, + "failed to store enum properly"); + + return true; + } + tableEntry++; + } + + if (aDefaultValue) { + NS_PRECONDITION(aTable <= aDefaultValue && aDefaultValue < tableEntry, + "aDefaultValue not inside aTable?"); + SetIntValueAndType(EnumTableEntryToValue(aTable, aDefaultValue), + eEnum, &aValue); + return true; + } + + return false; +} + +bool +nsAttrValue::ParseSpecialIntValue(const nsAString& aString) +{ + ResetIfSet(); + + nsAutoString tmp(aString); + nsContentUtils::ParseHTMLIntegerResultFlags result; + int32_t originalVal = nsContentUtils::ParseHTMLInteger(aString, &result); + + if (result & nsContentUtils::eParseHTMLInteger_Error) { + return false; + } + + bool isPercent = result & nsContentUtils::eParseHTMLInteger_IsPercent; + int32_t val = std::max(originalVal, 0); + bool nonStrict = val != originalVal || + (result & nsContentUtils::eParseHTMLInteger_NonStandard) || + (result & nsContentUtils::eParseHTMLInteger_DidNotConsumeAllInput); + + // % (percent) + if (isPercent || tmp.RFindChar('%') >= 0) { + isPercent = true; + } + + SetIntValueAndType(val, isPercent ? ePercent : eInteger, + nonStrict ? &aString : nullptr); + return true; +} + +bool +nsAttrValue::ParseIntWithBounds(const nsAString& aString, + int32_t aMin, int32_t aMax) +{ + NS_PRECONDITION(aMin < aMax, "bad boundaries"); + + ResetIfSet(); + + nsContentUtils::ParseHTMLIntegerResultFlags result; + int32_t originalVal = nsContentUtils::ParseHTMLInteger(aString, &result); + if (result & nsContentUtils::eParseHTMLInteger_Error) { + return false; + } + + int32_t val = std::max(originalVal, aMin); + val = std::min(val, aMax); + bool nonStrict = (val != originalVal) || + (result & nsContentUtils::eParseHTMLInteger_IsPercent) || + (result & nsContentUtils::eParseHTMLInteger_NonStandard) || + (result & nsContentUtils::eParseHTMLInteger_DidNotConsumeAllInput); + + SetIntValueAndType(val, eInteger, nonStrict ? &aString : nullptr); + + return true; +} + +void +nsAttrValue::ParseIntWithFallback(const nsAString& aString, int32_t aDefault, + int32_t aMax) +{ + ResetIfSet(); + + nsContentUtils::ParseHTMLIntegerResultFlags result; + int32_t val = nsContentUtils::ParseHTMLInteger(aString, &result); + bool nonStrict = false; + if ((result & nsContentUtils::eParseHTMLInteger_Error) || val < 1) { + val = aDefault; + nonStrict = true; + } + + if (val > aMax) { + val = aMax; + nonStrict = true; + } + + if ((result & nsContentUtils::eParseHTMLInteger_IsPercent) || + (result & nsContentUtils::eParseHTMLInteger_NonStandard) || + (result & nsContentUtils::eParseHTMLInteger_DidNotConsumeAllInput)) { + nonStrict = true; + } + + SetIntValueAndType(val, eInteger, nonStrict ? &aString : nullptr); +} + +bool +nsAttrValue::ParseNonNegativeIntValue(const nsAString& aString) +{ + ResetIfSet(); + + nsContentUtils::ParseHTMLIntegerResultFlags result; + int32_t originalVal = nsContentUtils::ParseHTMLInteger(aString, &result); + if ((result & nsContentUtils::eParseHTMLInteger_Error) || originalVal < 0) { + return false; + } + + bool nonStrict = (result & nsContentUtils::eParseHTMLInteger_IsPercent) || + (result & nsContentUtils::eParseHTMLInteger_NonStandard) || + (result & nsContentUtils::eParseHTMLInteger_DidNotConsumeAllInput); + + SetIntValueAndType(originalVal, eInteger, nonStrict ? &aString : nullptr); + + return true; +} + +bool +nsAttrValue::ParsePositiveIntValue(const nsAString& aString) +{ + ResetIfSet(); + + nsContentUtils::ParseHTMLIntegerResultFlags result; + int32_t originalVal = nsContentUtils::ParseHTMLInteger(aString, &result); + if ((result & nsContentUtils::eParseHTMLInteger_Error) || originalVal <= 0) { + return false; + } + + bool nonStrict = (result & nsContentUtils::eParseHTMLInteger_IsPercent) || + (result & nsContentUtils::eParseHTMLInteger_NonStandard) || + (result & nsContentUtils::eParseHTMLInteger_DidNotConsumeAllInput); + + SetIntValueAndType(originalVal, eInteger, nonStrict ? &aString : nullptr); + + return true; +} + +void +nsAttrValue::SetColorValue(nscolor aColor, const nsAString& aString) +{ + nsStringBuffer* buf = GetStringBuffer(aString).take(); + if (!buf) { + return; + } + + MiscContainer* cont = EnsureEmptyMiscContainer(); + cont->mValue.mColor = aColor; + cont->mType = eColor; + + // Save the literal string we were passed for round-tripping. + cont->mStringBits = reinterpret_cast<uintptr_t>(buf) | eStringBase; +} + +bool +nsAttrValue::ParseColor(const nsAString& aString) +{ + ResetIfSet(); + + // FIXME (partially, at least): HTML5's algorithm says we shouldn't do + // the whitespace compression, trimming, or the test for emptiness. + // (I'm a little skeptical that we shouldn't do the whitespace + // trimming; WebKit also does it.) + nsAutoString colorStr(aString); + colorStr.CompressWhitespace(true, true); + if (colorStr.IsEmpty()) { + return false; + } + + nscolor color; + // No color names begin with a '#'; in standards mode, all acceptable + // numeric colors do. + if (colorStr.First() == '#') { + nsDependentString withoutHash(colorStr.get() + 1, colorStr.Length() - 1); + if (NS_HexToRGBA(withoutHash, nsHexColorType::NoAlpha, &color)) { + SetColorValue(color, aString); + return true; + } + } else { + if (NS_ColorNameToRGB(colorStr, &color)) { + SetColorValue(color, aString); + return true; + } + } + + // FIXME (maybe): HTML5 says we should handle system colors. This + // means we probably need another storage type, since we'd need to + // handle dynamic changes. However, I think this is a bad idea: + // http://lists.whatwg.org/pipermail/whatwg-whatwg.org/2010-May/026449.html + + // Use NS_LooseHexToRGB as a fallback if nothing above worked. + if (NS_LooseHexToRGB(colorStr, &color)) { + SetColorValue(color, aString); + return true; + } + + return false; +} + +bool nsAttrValue::ParseDoubleValue(const nsAString& aString) +{ + ResetIfSet(); + + nsresult ec; + double val = PromiseFlatString(aString).ToDouble(&ec); + if (NS_FAILED(ec)) { + return false; + } + + MiscContainer* cont = EnsureEmptyMiscContainer(); + cont->mDoubleValue = val; + cont->mType = eDoubleValue; + nsAutoString serializedFloat; + serializedFloat.AppendFloat(val); + SetMiscAtomOrString(serializedFloat.Equals(aString) ? nullptr : &aString); + return true; +} + +bool +nsAttrValue::ParseIntMarginValue(const nsAString& aString) +{ + ResetIfSet(); + + nsIntMargin margins; + if (!nsContentUtils::ParseIntMarginValue(aString, margins)) + return false; + + MiscContainer* cont = EnsureEmptyMiscContainer(); + cont->mValue.mIntMargin = new nsIntMargin(margins); + cont->mType = eIntMarginValue; + SetMiscAtomOrString(&aString); + return true; +} + +void +nsAttrValue::LoadImage(nsIDocument* aDocument) +{ + NS_ASSERTION(Type() == eURL, "wrong type"); + +#ifdef DEBUG + { + nsString val; + ToString(val); + NS_ASSERTION(!val.IsEmpty(), + "How did we end up with an empty string for eURL"); + } +#endif + + MiscContainer* cont = GetMiscContainer(); + mozilla::css::URLValue* url = cont->mValue.mURL; + mozilla::css::ImageValue* image = + new css::ImageValue(url->GetURI(), url->mString, url->mBaseURI, + url->mReferrer, url->mOriginPrincipal, aDocument); + + NS_ADDREF(image); + cont->mValue.mImage = image; + NS_RELEASE(url); + cont->mType = eImage; +} + +bool +nsAttrValue::ParseStyleAttribute(const nsAString& aString, + nsStyledElement* aElement) +{ + nsIDocument* ownerDoc = aElement->OwnerDoc(); + nsHTMLCSSStyleSheet* sheet = ownerDoc->GetInlineStyleSheet(); + nsCOMPtr<nsIURI> baseURI = aElement->GetBaseURI(); + nsIURI* docURI = ownerDoc->GetDocumentURI(); + + NS_ASSERTION(aElement->NodePrincipal() == ownerDoc->NodePrincipal(), + "This is unexpected"); + + // If the (immutable) document URI does not match the element's base URI + // (the common case is that they do match) do not cache the rule. This is + // because the results of the CSS parser are dependent on these URIs, and we + // do not want to have to account for the URIs in the hash lookup. + bool cachingAllowed = sheet && baseURI == docURI; + if (cachingAllowed) { + MiscContainer* cont = sheet->LookupStyleAttr(aString); + if (cont) { + // Set our MiscContainer to the cached one. + NS_ADDREF(cont); + SetPtrValueAndType(cont, eOtherBase); + return true; + } + } + + RefPtr<DeclarationBlock> decl; + if (ownerDoc->GetStyleBackendType() == StyleBackendType::Servo) { + decl = ServoDeclarationBlock::FromCssText(aString); + } else { + css::Loader* cssLoader = ownerDoc->CSSLoader(); + nsCSSParser cssParser(cssLoader); + decl = cssParser.ParseStyleAttribute(aString, docURI, baseURI, + aElement->NodePrincipal()); + } + if (!decl) { + return false; + } + decl->SetHTMLCSSStyleSheet(sheet); + SetTo(decl.forget(), &aString); + + if (cachingAllowed) { + MiscContainer* cont = GetMiscContainer(); + cont->Cache(); + } + + return true; +} + +void +nsAttrValue::SetMiscAtomOrString(const nsAString* aValue) +{ + NS_ASSERTION(GetMiscContainer(), "Must have MiscContainer!"); + NS_ASSERTION(!GetMiscContainer()->mStringBits, + "Trying to re-set atom or string!"); + if (aValue) { + uint32_t len = aValue->Length(); + // * We're allowing eCSSDeclaration attributes to store empty + // strings as it can be beneficial to store an empty style + // attribute as a parsed rule. + // * We're allowing enumerated values because sometimes the empty + // string corresponds to a particular enumerated value, especially + // for enumerated values that are not limited enumerated. + // Add other types as needed. + NS_ASSERTION(len || Type() == eCSSDeclaration || Type() == eEnum, + "Empty string?"); + MiscContainer* cont = GetMiscContainer(); + if (len <= NS_ATTRVALUE_MAX_STRINGLENGTH_ATOM) { + nsCOMPtr<nsIAtom> atom = NS_Atomize(*aValue); + if (atom) { + cont->mStringBits = + reinterpret_cast<uintptr_t>(atom.forget().take()) | eAtomBase; + } + } else { + nsStringBuffer* buf = GetStringBuffer(*aValue).take(); + if (buf) { + cont->mStringBits = reinterpret_cast<uintptr_t>(buf) | eStringBase; + } + } + } +} + +void +nsAttrValue::ResetMiscAtomOrString() +{ + MiscContainer* cont = GetMiscContainer(); + void* ptr = MISC_STR_PTR(cont); + if (ptr) { + if (static_cast<ValueBaseType>(cont->mStringBits & NS_ATTRVALUE_BASETYPE_MASK) == + eStringBase) { + static_cast<nsStringBuffer*>(ptr)->Release(); + } else { + static_cast<nsIAtom*>(ptr)->Release(); + } + cont->mStringBits = 0; + } +} + +void +nsAttrValue::SetSVGType(ValueType aType, const void* aValue, + const nsAString* aSerialized) { + MOZ_ASSERT(IsSVGType(aType), "Not an SVG type"); + + MiscContainer* cont = EnsureEmptyMiscContainer(); + // All SVG types are just pointers to classes so just setting any of them + // will do. We'll lose type-safety but the signature of the calling + // function should ensure we don't get anything unexpected, and once we + // stick aValue in a union we lose type information anyway. + cont->mValue.mSVGAngle = static_cast<const nsSVGAngle*>(aValue); + cont->mType = aType; + SetMiscAtomOrString(aSerialized); +} + +MiscContainer* +nsAttrValue::ClearMiscContainer() +{ + MiscContainer* cont = nullptr; + if (BaseType() == eOtherBase) { + cont = GetMiscContainer(); + if (cont->IsRefCounted() && cont->mValue.mRefCount > 1) { + // This MiscContainer is shared, we need a new one. + NS_RELEASE(cont); + + cont = new MiscContainer; + SetPtrValueAndType(cont, eOtherBase); + } + else { + switch (cont->mType) { + case eCSSDeclaration: + { + MOZ_ASSERT(cont->mValue.mRefCount == 1); + cont->Release(); + cont->Evict(); + NS_RELEASE(cont->mValue.mCSSDeclaration); + break; + } + case eURL: + { + NS_RELEASE(cont->mValue.mURL); + break; + } + case eImage: + { + NS_RELEASE(cont->mValue.mImage); + break; + } + case eAtomArray: + { + delete cont->mValue.mAtomArray; + break; + } + case eIntMarginValue: + { + delete cont->mValue.mIntMargin; + break; + } + default: + { + break; + } + } + } + ResetMiscAtomOrString(); + } + else { + ResetIfSet(); + } + + return cont; +} + +MiscContainer* +nsAttrValue::EnsureEmptyMiscContainer() +{ + MiscContainer* cont = ClearMiscContainer(); + if (cont) { + MOZ_ASSERT(BaseType() == eOtherBase); + ResetMiscAtomOrString(); + cont = GetMiscContainer(); + } + else { + cont = new MiscContainer; + SetPtrValueAndType(cont, eOtherBase); + } + + return cont; +} + +bool +nsAttrValue::EnsureEmptyAtomArray() +{ + if (Type() == eAtomArray) { + ResetMiscAtomOrString(); + GetAtomArrayValue()->Clear(); + return true; + } + + MiscContainer* cont = EnsureEmptyMiscContainer(); + cont->mValue.mAtomArray = new AtomArray; + cont->mType = eAtomArray; + + return true; +} + +already_AddRefed<nsStringBuffer> +nsAttrValue::GetStringBuffer(const nsAString& aValue) const +{ + uint32_t len = aValue.Length(); + if (!len) { + return nullptr; + } + + RefPtr<nsStringBuffer> buf = nsStringBuffer::FromString(aValue); + if (buf && (buf->StorageSize()/sizeof(char16_t) - 1) == len) { + return buf.forget(); + } + + buf = nsStringBuffer::Alloc((len + 1) * sizeof(char16_t)); + if (!buf) { + return nullptr; + } + char16_t *data = static_cast<char16_t*>(buf->Data()); + CopyUnicodeTo(aValue, 0, data, len); + data[len] = char16_t(0); + return buf.forget(); +} + +size_t +nsAttrValue::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const +{ + size_t n = 0; + + switch (BaseType()) { + case eStringBase: + { + nsStringBuffer* str = static_cast<nsStringBuffer*>(GetPtr()); + n += str ? str->SizeOfIncludingThisIfUnshared(aMallocSizeOf) : 0; + break; + } + case eOtherBase: + { + MiscContainer* container = GetMiscContainer(); + if (!container) { + break; + } + if (container->IsRefCounted() && container->mValue.mRefCount > 1) { + // We don't report this MiscContainer at all in order to avoid + // twice-reporting it. + // TODO DMD, bug 1027551 - figure out how to report this ref-counted + // object just once. + break; + } + n += aMallocSizeOf(container); + + void* otherPtr = MISC_STR_PTR(container); + // We only count the size of the object pointed by otherPtr if it's a + // string. When it's an atom, it's counted separatly. + if (otherPtr && + static_cast<ValueBaseType>(container->mStringBits & NS_ATTRVALUE_BASETYPE_MASK) == eStringBase) { + nsStringBuffer* str = static_cast<nsStringBuffer*>(otherPtr); + n += str ? str->SizeOfIncludingThisIfUnshared(aMallocSizeOf) : 0; + } + + if (Type() == eCSSDeclaration && container->mValue.mCSSDeclaration) { + // TODO: mCSSDeclaration might be owned by another object which + // would make us count them twice, bug 677493. + // Bug 1281964: For ServoDeclarationBlock if we do measure we'll + // need a way to call the Servo heap_size_of function. + //n += container->mCSSDeclaration->SizeOfIncludingThis(aMallocSizeOf); + } else if (Type() == eAtomArray && container->mValue.mAtomArray) { + // Don't measure each nsIAtom, they are measured separatly. + n += container->mValue.mAtomArray->ShallowSizeOfIncludingThis(aMallocSizeOf); + } + break; + } + case eAtomBase: // Atoms are counted separately. + case eIntegerBase: // The value is in mBits, nothing to do. + break; + } + + return n; +} + |