/* -*- 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.
*/
/** * Global cache for eAtomArray MiscContainer objects, to speed up the parsing * of class attributes with multiple class names. * This cache doesn't keep anything alive - a MiscContainer removes itself from * the cache once its last reference is dropped.
*/ struct AtomArrayCache { // We don't keep any strong references, neither to the atom nor to the // MiscContainer. The MiscContainer removes itself from the cache when // the last reference to it is dropped, and the atom is kept alive by // the MiscContainer. using MapType = nsTHashMap<nsAtom*, MiscContainer*>;
// This has to be immutable once it goes into the cache.
mValue.mCSSDeclaration->SetImmutable(); break;
} case nsAttrValue::eAtomArray: {
MOZ_ASSERT(IsRefCounted());
MOZ_ASSERT(mValue.mRefCount > 0);
MOZ_ASSERT(!mValue.mCached);
nsAtom* atom = GetStoredAtom(); if (!atom) { 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->mDoubleValue = otherCont->mDoubleValue; break;
} case eColor: {
cont->mValue.mColor = otherCont->mValue.mColor; break;
} case eAtomArray: case eShadowParts: case eCSSDeclaration: {
MOZ_CRASH("These should be refcounted!");
} case eURL: {
NS_ADDREF(cont->mValue.mURL = otherCont->mValue.mURL); break;
} case eDoubleValue: {
cont->mDoubleValue = otherCont->mDoubleValue; 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.mSVGLength = otherCont->mValue.mSVGLength;
} else {
MOZ_ASSERT_UNREACHABLE("unknown type stored in MiscContainer");
} break;
}
}
bool isString; if (void* otherPtr = otherCont->GetStringOrAtomPtr(isString)) { if (isString) { static_cast<mozilla::StringBuffer*>(otherPtr)->AddRef();
} else { static_cast<nsAtom*>(otherPtr)->AddRef();
}
cont->SetStringBitsMainThread(otherCont->mStringBits);
} // Note, set mType after switch-case, otherwise EnsureEmptyAtomArray doesn't // work correctly.
cont->mType = otherCont->mType;
}
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 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 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);
}
// Don't cache the new container. It would stomp over the undeduplicated // value in the cache. But we could have a separate cache for deduplicated // atom arrays, if repeated deduplication shows up in profiles.
}
// This can be reached during parallel selector matching with attribute // selectors on the style attribute. SetMiscAtomOrString handles this // case, and as of this writing this is the only consumer that needs it. const_cast<nsAttrValue*>(this)->SetMiscAtomOrString(&aResult);
break;
} case eDoubleValue: {
aResult.Truncate();
aResult.AppendFloat(GetDoubleValue()); break;
} case eSVGIntegerPair: {
SVGAttrValueWrapper::ToString(
GetMiscContainer()->mValue.mSVGAnimatedIntegerPair, aResult); break;
} case eSVGOrient: {
SVGAttrValueWrapper::ToString(
GetMiscContainer()->mValue.mSVGAnimatedOrient, 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.mSVGAnimatedNumberPair, 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.mSVGAnimatedPreserveAspectRatio, 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.mSVGAnimatedViewBox, aResult); break;
} default: {
aResult.Truncate(); break;
}
}
}
auto CheckDuplicate = [&](size_t i) {
nsAtom* atom = mArray[i]; if (!usingHashTable) { if (!filter.mightContain(atom)) {
filter.add(atom); returnfalse;
} for (size_t j = 0; j < i; ++j) {
hash.Insert(mArray[j]);
}
usingHashTable = true;
} return !hash.EnsureInserted(atom);
};
size_t len = mArray.Length();
UniquePtr<AttrAtomArray> deduplicatedArray; for (size_t i = 0; i < len; ++i) { if (!CheckDuplicate(i)) { if (deduplicatedArray) {
deduplicatedArray->mArray.AppendElement(mArray[i]);
} continue;
} // We've found a duplicate! if (!deduplicatedArray) { // Allocate the deduplicated copy and copy the preceding elements into it.
deduplicatedArray = MakeUnique<AttrAtomArray>();
deduplicatedArray->mMayContainDuplicates = false;
deduplicatedArray->mArray.SetCapacity(len - 1); for (size_t indexToCopy = 0; indexToCopy < i; indexToCopy++) {
deduplicatedArray->mArray.AppendElement(mArray[indexToCopy]);
}
}
}
if (!deduplicatedArray) { // This AttrAtomArray doesn't contain any duplicates, cache this information // for future invocations.
mMayContainDuplicates = false;
} return deduplicatedArray;
}
uint32_t nsAttrValue::GetAtomCount() const {
ValueType type = Type();
if (type == eAtom) { return 1;
}
if (type == eAtomArray) { return GetAtomArrayValue()->mArray.Length();
}
return 0;
}
nsAtom* nsAttrValue::AtomAt(int32_t aIndex) const {
MOZ_ASSERT(aIndex >= 0, "Index must not be negative");
MOZ_ASSERT(GetAtomCount() > uint32_t(aIndex), "aIndex out of range");
if (BaseType() == eAtomBase) { return GetAtomValue();
}
NS_ASSERTION(Type() == eAtomArray, "GetAtomCount must be confused"); return GetAtomArrayValue()->mArray.ElementAt(aIndex);
}
uint32_t nsAttrValue::HashValue() const { switch (BaseType()) { case eStringBase: { if (auto* str = static_cast<mozilla::StringBuffer*>(GetPtr())) {
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;
}
}
switch (cont->mType) { case eInteger: { return cont->mValue.mInteger;
} case eEnum: { return cont->mValue.mEnumValue;
} case ePercent: { return cont->mDoubleValue;
} case eColor: { return cont->mValue.mColor;
} case eCSSDeclaration: { return NS_PTR_TO_INT32(cont->mValue.mCSSDeclaration);
} case eURL: {
nsString str;
ToString(str); return HashString(str);
} case eAtomArray: {
uint32_t hash = 0; for (constauto& atom : cont->mValue.mAtomArray->mArray) {
hash = AddToHash(hash, atom.get());
} return hash;
} case eDoubleValue: { // XXX this is crappy, but oh well return cont->mDoubleValue;
} 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.mSVGLength);
}
MOZ_ASSERT_UNREACHABLE("unknown type stored in MiscContainer"); return 0;
}
}
}
if (thisCont->mType != otherCont->mType) { returnfalse;
}
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->mDoubleValue == otherCont->mDoubleValue) {
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 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)) { returnfalse;
}
needsStringComparison = true; break;
} case eDoubleValue: { return thisCont->mDoubleValue == otherCont->mDoubleValue;
} 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"); returnfalse;
}
MOZ_ASSERT_UNREACHABLE("unknown type stored in MiscContainer"); returnfalse;
}
} if (needsStringComparison) { if (thisCont->mStringBits == otherCont->mStringBits) { returntrue;
} 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<mozilla::StringBuffer*>( static_cast<uintptr_t>(thisCont->mStringBits)))
.Equals(nsCheapString(reinterpret_cast<mozilla::StringBuffer*>( static_cast<uintptr_t>(otherCont->mStringBits))));
}
} returnfalse;
}
// 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);
bool nsAttrValue::Contains(nsAtom* aValue,
nsCaseTreatment aCaseSensitive) const { switch (BaseType()) { case eAtomBase: {
nsAtom* 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(aValue, atom);
} default: { if (Type() == eAtomArray) { const AttrAtomArray* array = GetAtomArrayValue(); if (aCaseSensitive == eCaseMatters) { return array->mArray.Contains(aValue);
}
for (const RefPtr<nsAtom>& cur : array->mArray) { // 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(aValue, cur)) { returntrue;
}
}
}
}
}
RefPtr<nsAtom> atom = NS_Atomize(aValue); if (atom) {
SetPtrValueAndType(atom.forget().take(), eAtomBase);
}
}
void nsAttrValue::ParseAtomArray(nsAtom* aValue) { if (MiscContainer* cont = AtomArrayCache::Lookup(aValue)) { // Set our MiscContainer to the cached one.
NS_ADDREF(cont);
SetPtrValueAndType(cont, eOtherBase); return;
}
const char16_t* iter = aValue->GetUTF16String(); const char16_t* end = iter + aValue->GetLength(); bool hasSpace = false;
// skip initial whitespace while (iter != end && nsContentUtils::IsHTMLWhitespace(*iter)) {
hasSpace = true;
++iter;
}
if (iter == end) { // The value is empty or only contains whitespace. // Set this attribute to the string value. // We don't call the SetTo(nsAtom*) overload because doing so would // leave us with a classList of length 1.
SetTo(nsDependentAtomString(aValue)); return;
}
const char16_t* start = iter;
// get first - and often only - atom do {
++iter;
} while (iter != end && !nsContentUtils::IsHTMLWhitespace(*iter));
RefPtr<nsAtom> classAtom = iter == end && !hasSpace
? RefPtr<nsAtom>(aValue).forget()
: NS_AtomizeMainThread(Substring(start, iter)); if (!classAtom) {
ResetIfSet(); 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();
nsAtom* atom = nullptr;
classAtom.swap(atom);
SetPtrValueAndType(atom, eAtomBase); return;
}
// We have at least one class atom. Create a new AttrAtomArray.
AttrAtomArray* array = new AttrAtomArray;
// XXX(Bug 1631371) Check if this should use a fallible operation as it // pretended earlier.
array->mArray.AppendElement(std::move(classAtom));
// parse the rest of the classnames while (iter != end) {
start = iter;
do {
++iter;
} while (iter != end && !nsContentUtils::IsHTMLWhitespace(*iter));
// XXX(Bug 1631371) Check if this should use a fallible operation as it // pretended earlier.
array->mArray.AppendElement(std::move(classAtom));
array->mMayContainDuplicates = true;
// skip whitespace while (iter != end && nsContentUtils::IsHTMLWhitespace(*iter)) {
++iter;
}
}
// Wrap the AtomArray into a fresh MiscContainer.
MiscContainer* cont = EnsureEmptyMiscContainer();
MOZ_ASSERT(cont->mValue.mRefCount == 0);
cont->mValue.mAtomArray = array;
cont->mType = eAtomArray;
NS_ADDREF(cont);
MOZ_ASSERT(cont->mValue.mRefCount == 1);
// Assign the atom to the container's string bits (like SetMiscAtomOrString // would do).
MOZ_ASSERT(!IsInServoTraversal());
aValue->AddRef();
uintptr_t bits = reinterpret_cast<uintptr_t>(aValue) | eAtomBase;
cont->SetStringBitsMainThread(bits);
// Put the container in the cache.
cont->Cache();
}
void nsAttrValue::ParseAtomArray(const nsAString& aValue) { if (aValue.IsVoid()) {
ResetIfSet();
} else {
RefPtr<nsAtom> atom = NS_AtomizeMainThread(aValue);
ParseAtomArray(atom);
}
}
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 efficiently anyway. if (len && len <= NS_ATTRVALUE_MAX_STRINGLENGTH_ATOM) {
ParseAtom(aValue);
} else {
SetTo(aValue);
}
}
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");
returntrue;
}
tableEntry++;
}
if (aDefaultValue) {
MOZ_ASSERT(aTable <= aDefaultValue && aDefaultValue < tableEntry, "aDefaultValue not inside aTable?");
SetIntValueAndType(EnumTableEntryToValue(aTable, aDefaultValue), eEnum,
&aValue); returntrue;
}
// We don't use nsContentUtils::ParseHTMLInteger here because we // need a bunch of behavioral differences from it. We _could_ try to // use it, but it would not be a great fit.
// Steps 1 and 2. const char16_t* position = aInput.BeginReading(); const char16_t* end = aInput.EndReading();
// We will need to keep track of whether this was a canonical representation // or not. It's non-canonical if it has leading whitespace, leading '+', // leading '0' characters, or trailing garbage. bool canonical = true;
// Step 3. while (position != end && nsContentUtils::IsHTMLWhitespace(*position)) {
canonical = false; // Leading whitespace
++position;
}
// Step 4. if (position == end || *position < char16_t('0') ||
*position > char16_t('9')) { returnfalse;
}
// Step 5.
CheckedInt32 value = 0;
// Collect up leading '0' first to avoid extra branching in the main // loop to set 'canonical' properly. while (position != end && *position == char16_t('0')) {
canonical = false; // Leading '0'
++position;
}
// Now collect up other digits. while (position != end && *position >= char16_t('0') &&
*position <= char16_t('9')) {
value = value * 10 + (*position - char16_t('0')); if (!value.isValid()) { // The spec assumes we can deal with arbitrary-size integers here, but we // really can't. If someone sets something too big, just bail out and // ignore it. returnfalse;
}
++position;
}
// Step 6 is implemented implicitly via the various "position != end" guards // from this point on.
Maybe<double> doubleValue; // Step 7. The return in step 7.2 is handled by just falling through to the // code below this block when we reach end of input or a non-digit, because // the while loop will terminate at that point. if (position != end && *position == char16_t('.')) {
canonical = false; // Let's not rely on double serialization reproducing // the string we started with. // Step 7.1.
++position; // If we have a '.' _not_ followed by digits, this is not as efficient as it // could be, because we will store as a double while we could have stored as // an int. But that seems like a pretty rare case.
doubleValue.emplace(value.value()); // Step 7.3. double divisor = 1.0f; // Step 7.4. while (position != end && *position >= char16_t('0') &&
*position <= char16_t('9')) { // Step 7.4.1.
divisor = divisor * 10.0f; // Step 7.4.2.
doubleValue.ref() += (*position - char16_t('0')) / divisor; // Step 7.4.3.
++position; // Step 7.4.4 and 7.4.5 are captured in the while loop condition and the // "position != end" checks below.
}
}
if (aEnsureNonzero && value.value() == 0 &&
(!doubleValue || *doubleValue == 0.0f)) { // Not valid. Just drop it. returnfalse;
}
// Step 8 and the spec's early return from step 7.2.
ValueType type; if (position != end && *position == char16_t('%')) {
type = ePercent;
++position;
} elseif (doubleValue) {
type = eDoubleValue;
} else {
type = eInteger;
}
if (position != end) {
canonical = false;
}
if (doubleValue) {
MOZ_ASSERT(!canonical, "We set it false above!");
SetDoubleValueAndType(*doubleValue, type, &aInput);
} else {
SetIntValueAndType(value.value(), type, canonical ? nullptr : &aInput);
}
// Save the literal string we were passed for round-tripping.
cont->SetStringBitsMainThread(reinterpret_cast<uintptr_t>(buf) | eStringBase); returntrue;
}
// 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()) { returnfalse;
}
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)) { return SetColorValue(color, aString);
}
} elseif (colorStr.LowerCaseEqualsLiteral("transparent")) { return SetColorValue(NS_RGBA(0, 0, 0, 0), aString);
} else { const NS_ConvertUTF16toUTF8 colorNameU8(colorStr); if (Servo_ColorNameToRgb(&colorNameU8, &color)) { return SetColorValue(color, aString);
}
}
nsIPrincipal* principal = aMaybeScriptedPrincipal ? aMaybeScriptedPrincipal
: aElement->NodePrincipal();
RefPtr<URLExtraData> data = aElement->GetURLDataForStyleAttr(principal);
// 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. // Similarly, if the triggering principal does not match the node principal, // do not cache the rule, since the principal will be encoded in any parsed // URLs in the rule. constbool cachingAllowed = attrStyles &&
doc->GetDocumentURI() == data->BaseURI() &&
principal == aElement->NodePrincipal(); if (cachingAllowed) { if (MiscContainer* cont = attrStyles->LookupStyleAttr(aString)) { // Set our MiscContainer to the cached one.
NS_ADDREF(cont);
SetPtrValueAndType(cont, eOtherBase); returntrue;
}
}
if (cachingAllowed) {
MiscContainer* cont = GetMiscContainer();
cont->Cache();
}
returntrue;
}
void nsAttrValue::SetMiscAtomOrString(const nsAString* aValue) {
NS_ASSERTION(GetMiscContainer(), "Must have MiscContainer!");
NS_ASSERTION(!GetMiscContainer()->mStringBits || IsInServoTraversal(), "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) {
nsAtom* atom = MOZ_LIKELY(!IsInServoTraversal())
? NS_AtomizeMainThread(*aValue).take()
: NS_Atomize(*aValue).take();
NS_ENSURE_TRUE_VOID(atom);
uintptr_t bits = reinterpret_cast<uintptr_t>(atom) | eAtomBase;
// In the common case we're not in the servo traversal, and we can just // set the bits normally. The parallel case requires more care. if (MOZ_LIKELY(!IsInServoTraversal())) {
cont->SetStringBitsMainThread(bits);
} elseif (!cont->mStringBits.compareExchange(0, bits)) { // We raced with somebody else setting the bits. Release our copy.
atom->Release();
}
} else {
mozilla::StringBuffer* buffer = GetStringBuffer(*aValue).take();
NS_ENSURE_TRUE_VOID(buffer);
uintptr_t bits = reinterpret_cast<uintptr_t>(buffer) | eStringBase;
// In the common case we're not in the servo traversal, and we can just // set the bits normally. The parallel case requires more care. if (MOZ_LIKELY(!IsInServoTraversal())) {
cont->SetStringBitsMainThread(bits);
} elseif (!cont->mStringBits.compareExchange(0, bits)) { // We raced with somebody else setting the bits. Release our copy.
buffer->Release();
}
}
}
}
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.mSVGLength = static_cast<const SVGAnimatedLength*>(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);
already_AddRefed<mozilla::StringBuffer> nsAttrValue::GetStringBuffer( const nsAString& aValue) const {
uint32_t len = aValue.Length(); if (!len) { return nullptr;
} if (mozilla::StringBuffer* buf = aValue.GetStringBuffer();
buf && (buf->StorageSize() / sizeof(char16_t) - 1) == len) { // We can only reuse the buffer if it's exactly sized, since we rely on // StorageSize() to get the string length in ToString(). return do_AddRef(buf);
} return mozilla::StringBuffer::Create(aValue.Data(), aValue.Length());
}
size_t nsAttrValue::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const {
size_t n = 0;
switch (BaseType()) { case eStringBase: {
mozilla::StringBuffer* str = static_cast<mozilla::StringBuffer*>(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);
// We only count the size of the object pointed by otherPtr if it's a // string. When it's an atom, it's counted separately. if (mozilla::StringBuffer* buf = container->GetStoredStringBuffer()) {
n += buf->SizeOfIncludingThisIfUnshared(aMallocSizeOf);
}
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 DeclarationBlock if we do measure we'll // need a way to call the Servo heap_size_of function. // n += container->mCSSDeclaration->SizeOfIncludingThis(aMallocSizeOf);
} elseif (Type() == eAtomArray && container->mValue.mAtomArray) { // Don't measure each nsAtom, because they are measured separately.
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;
}
¤ Diese beiden folgenden Angebotsgruppen bietet das Unternehmen0.39Angebot
Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.