Bug 2047481 - Skip redundant scans and atomize the lookup name in setAttribute. r=smaug,emilio

When Element::SetAttribute determines that the attribute is new,
propagate that as an IsKnownNewAttr hint through SetAttr,
SetAttrInternal, SetAttrAndNotify and SetAndSwapAttr so the
secondary scans in MaybeCheckSameAttrVal and
AttrArray::SetAndSwapAttr can be skipped.

AttrArray::GetExistingAttrNameFromQName atomizes the lookup name
once up front and compares attribute names by atom pointer,
falling back to QualifiedNameEquals only for NodeInfo-typed
entries.

Differential Revision: https://phabricator.services.mozilla.com/D306935
This commit is contained in:
Jan-Niklas Jaeschke
2026-06-17 01:48:57 +00:00
committed by jjaschke@mozilla.com
parent 828f5369c5
commit 01ee16866b
9 changed files with 188 additions and 97 deletions
+2 -1
View File
@@ -177,7 +177,8 @@ void Attr::SetValueInternal(const nsAString& aValue, ErrorResult& aRv) {
RefPtr<nsAtom> nameAtom = mNodeInfo->NameAtom();
aRv = element->SetAttr(mNodeInfo->NamespaceID(), nameAtom,
mNodeInfo->GetPrefixAtom(), aValue, nullptr, true);
mNodeInfo->GetPrefixAtom(), aValue, nullptr, true,
IsKnownNewAttr::No);
}
bool Attr::Specified() const { return true; }
+44 -16
View File
@@ -132,36 +132,48 @@ const nsAttrValue* AttrArray::AddNewAttributeAssumeAvailableSlot(
}
nsresult AttrArray::SetAndSwapAttr(nsAtom* aLocalName, nsAttrValue& aValue,
bool* aHadValue) {
bool* aHadValue,
mozilla::dom::IsKnownNewAttr aIsKnownNew) {
*aHadValue = false;
for (InternalAttr& attr : Attrs()) {
if (attr.mName.Equals(aLocalName)) {
attr.mValue.SwapValueWith(aValue);
*aHadValue = true;
return NS_OK;
if (aIsKnownNew == mozilla::dom::IsKnownNewAttr::No) {
for (InternalAttr& attr : Attrs()) {
if (attr.mName.Equals(aLocalName)) {
attr.mValue.SwapValueWith(aValue);
*aHadValue = true;
return NS_OK;
}
}
} else {
MOZ_ASSERT(IndexOfAttr(aLocalName) == -1,
"Caller asserted attribute is new but it already exists");
}
return AddNewAttribute(aLocalName, aValue);
}
nsresult AttrArray::SetAndSwapAttr(mozilla::dom::NodeInfo* aName,
nsAttrValue& aValue, bool* aHadValue) {
nsAttrValue& aValue, bool* aHadValue,
mozilla::dom::IsKnownNewAttr aIsKnownNew) {
int32_t namespaceID = aName->NamespaceID();
nsAtom* localName = aName->NameAtom();
if (namespaceID == kNameSpaceID_None) {
return SetAndSwapAttr(localName, aValue, aHadValue);
return SetAndSwapAttr(localName, aValue, aHadValue, aIsKnownNew);
}
*aHadValue = false;
for (InternalAttr& attr : Attrs()) {
if (attr.mName.Equals(localName, namespaceID)) {
attr.mName.SetTo(aName);
attr.mValue.SwapValueWith(aValue);
*aHadValue = true;
return NS_OK;
if (aIsKnownNew == mozilla::dom::IsKnownNewAttr::No) {
for (InternalAttr& attr : Attrs()) {
if (attr.mName.Equals(localName, namespaceID)) {
attr.mName.SetTo(aName);
attr.mValue.SwapValueWith(aValue);
*aHadValue = true;
return NS_OK;
}
}
} else {
MOZ_ASSERT(IndexOfAttr(localName, namespaceID) == -1,
"Caller asserted attribute is new but it already exists");
}
return AddNewAttribute(aName, aValue);
@@ -213,11 +225,27 @@ const nsAttrName* AttrArray::GetSafeAttrNameAt(uint32_t aPos) const {
}
const nsAttrName* AttrArray::GetExistingAttrNameFromQName(
const nsAString& aName) const {
const nsAString& aName, RefPtr<nsAtom>* aOutAtom) const {
if (aOutAtom) {
*aOutAtom = nullptr;
}
if (!HasAttrs()) {
return nullptr;
}
RefPtr<nsAtom> nameAtom = NS_AtomizeMainThread(aName);
for (const InternalAttr& attr : Attrs()) {
if (attr.mName.QualifiedNameEquals(aName)) {
// Equals(nsAtom*) compares mBits; for NodeInfo-typed entries mBits has the
// tag bit set and can never equal a clean atom pointer, so this is safe
// for both kinds without branching on IsAtom().
if (attr.mName.Equals(nameAtom.get())) {
return &attr.mName;
}
if (!attr.mName.IsAtom() && attr.mName.QualifiedNameEquals(aName)) {
return &attr.mName;
}
}
if (aOutAtom) {
*aOutAtom = nameAtom.forget();
}
return nullptr;
}
+16 -3
View File
@@ -25,6 +25,10 @@ struct StyleLockedDeclarationBlock;
namespace dom {
class Element;
class ElementInternals;
// Caller-supplied assertion that an attribute does not already exist, used
// to skip redundant scans on the addition path.
enum class IsKnownNewAttr : bool { No, Yes };
} // namespace dom
} // namespace mozilla
@@ -98,7 +102,14 @@ class AttrArray {
// Otherwise, crash.
const nsAttrName* GetSafeAttrNameAt(uint32_t aPos) const;
const nsAttrName* GetExistingAttrNameFromQName(const nsAString& aName) const;
// Find an existing attribute by qualified name.
//
// When aOutAtom is non-null and no matching attribute is found, *aOutAtom
// is set to the atomized lookup name so callers can reuse it without
// re-atomizing. In all other cases (match found, or the element has no
// attributes), *aOutAtom is set to nullptr.
const nsAttrName* GetExistingAttrNameFromQName(
const nsAString& aName, RefPtr<nsAtom>* aOutAtom = nullptr) const;
int32_t IndexOfAttr(const nsAtom* aLocalName) const;
int32_t IndexOfAttr(const nsAtom* aLocalName, int32_t aNamespaceID) const;
@@ -369,9 +380,11 @@ class AttrArray {
// aValue and aHadValue will be set to false. Otherwise, aHadValue will be set
// to true.
nsresult SetAndSwapAttr(nsAtom* aLocalName, nsAttrValue& aValue,
bool* aHadValue);
bool* aHadValue,
mozilla::dom::IsKnownNewAttr aIsKnownNew);
nsresult SetAndSwapAttr(mozilla::dom::NodeInfo* aName, nsAttrValue& aValue,
bool* aHadValue);
bool* aHadValue,
mozilla::dom::IsKnownNewAttr aIsKnownNew);
// mImpl serves dual purposes using pointer tagging:
//
+90 -57
View File
@@ -1794,20 +1794,20 @@ void Element::SetAttribute(const nsAString& aName, const nsAString& aValue,
}
nsAutoString nameToUse;
const nsAttrName* name = InternalGetAttrNameFromQName(aName, &nameToUse);
RefPtr<nsAtom> nameAtom;
const nsAttrName* name =
InternalGetAttrNameFromQName(aName, &nameToUse, &nameAtom);
if (!name) {
RefPtr<nsAtom> nameAtom = NS_AtomizeMainThread(nameToUse);
if (!nameAtom) {
aError.Throw(NS_ERROR_OUT_OF_MEMORY);
return;
nameAtom = NS_AtomizeMainThread(nameToUse);
}
aError = SetAttr(kNameSpaceID_None, nameAtom, aValue, aTriggeringPrincipal,
true);
aError = SetAttr(kNameSpaceID_None, nameAtom, nullptr, aValue,
aTriggeringPrincipal, true, IsKnownNewAttr::Yes);
return;
}
aError = SetAttr(name->NamespaceID(), name->LocalName(), name->GetPrefix(),
aValue, aTriggeringPrincipal, true);
aValue, aTriggeringPrincipal, true, IsKnownNewAttr::No);
}
void Element::RemoveAttribute(const nsAString& aName, ErrorResult& aError) {
@@ -1884,7 +1884,7 @@ void Element::SetAttributeNS(const nsAString& aNamespaceURI,
}
aError = SetAttr(ni->NamespaceID(), ni->NameAtom(), ni->GetPrefixAtom(),
aValue, aTriggeringPrincipal, true);
aValue, aTriggeringPrincipal, true, IsKnownNewAttr::No);
}
already_AddRefed<nsIPrincipal> Element::CreateDevtoolsPrincipal() {
@@ -1920,10 +1920,15 @@ void Element::SetAttribute(
}
nsAutoString nameToUse;
const nsAttrName* name = InternalGetAttrNameFromQName(aName, &nameToUse);
RefPtr<nsAtom> nameAtom;
const nsAttrName* name =
InternalGetAttrNameFromQName(aName, &nameToUse, &nameAtom);
if (!name) {
RefPtr<nsAtom> nameAtom = NS_AtomizeMainThread(nameToUse);
if (!nameAtom) {
nameAtom = NS_AtomizeMainThread(nameToUse);
}
Maybe<nsAutoString> compliantStringHolder;
nsMutationGuard guard;
const nsAString* compliantString =
TrustedTypeUtils::GetTrustedTypesCompliantAttributeValue(
*this, nameAtom, kNameSpaceID_None, aValue, aTriggeringPrincipal,
@@ -1931,8 +1936,12 @@ void Element::SetAttribute(
if (aError.Failed()) {
return;
}
aError = SetAttr(kNameSpaceID_None, nameAtom, *compliantString,
aTriggeringPrincipal, true);
// The fast path is only safe if nothing script-runnable above mutated
// mAttrs out from under us.
const IsKnownNewAttr isKnownNew =
guard.Mutated(0) ? IsKnownNewAttr::No : IsKnownNewAttr::Yes;
aError = SetAttr(kNameSpaceID_None, nameAtom, nullptr, *compliantString,
aTriggeringPrincipal, true, isKnownNew);
return;
}
@@ -1948,7 +1957,8 @@ void Element::SetAttribute(
}
if (!guard.Mutated(0)) {
aError = SetAttr(name->NamespaceID(), name->LocalName(), name->GetPrefix(),
*compliantString, aTriggeringPrincipal, true);
*compliantString, aTriggeringPrincipal, true,
IsKnownNewAttr::No);
return;
}
@@ -1980,8 +1990,9 @@ void Element::SetAttributeNS(
if (aError.Failed()) {
return;
}
aError = SetAttr(ni->NamespaceID(), ni->NameAtom(), ni->GetPrefixAtom(),
*compliantString, aTriggeringPrincipal, true);
aError =
SetAttr(ni->NamespaceID(), ni->NameAtom(), ni->GetPrefixAtom(),
*compliantString, aTriggeringPrincipal, true, IsKnownNewAttr::No);
}
void Element::SetAttributeDevtools(const nsAString& aName,
@@ -3489,19 +3500,20 @@ void Element::SetEventHandler(nsAtom* aEventName, const nsAString& aValue,
//----------------------------------------------------------------------
const nsAttrName* Element::InternalGetAttrNameFromQName(
const nsAString& aStr, nsAutoString* aNameToUse) const {
const nsAString& aStr, nsAutoString* aNameToUse,
RefPtr<nsAtom>* aOutAtom) const {
MOZ_ASSERT(!aNameToUse || aNameToUse->IsEmpty());
const nsAttrName* val = nullptr;
if (IsHTMLElement() && IsInHTMLDocument()) {
nsAutoString lower;
nsAutoString& outStr = aNameToUse ? *aNameToUse : lower;
nsContentUtils::ASCIIToLower(aStr, outStr);
val = mAttrs.GetExistingAttrNameFromQName(outStr);
val = mAttrs.GetExistingAttrNameFromQName(outStr, aOutAtom);
if (val) {
outStr.Truncate();
}
} else {
val = mAttrs.GetExistingAttrNameFromQName(aStr);
val = mAttrs.GetExistingAttrNameFromQName(aStr, aOutAtom);
if (!val && aNameToUse) {
*aNameToUse = aStr;
}
@@ -3586,28 +3598,32 @@ nsresult Element::SetClassAttrFromParser(nsAtom* aValue) {
nullptr, // old value
value, nullptr, AttrModType::Addition,
false, // notify
kCallAfterSetAttr, document, updateBatch);
kCallAfterSetAttr, document, updateBatch,
IsKnownNewAttr::Yes);
}
nsresult Element::SetAttr(int32_t aNamespaceID, nsAtom* aName, nsAtom* aPrefix,
const nsAString& aValue,
nsIPrincipal* aSubjectPrincipal, bool aNotify) {
nsIPrincipal* aSubjectPrincipal, bool aNotify,
IsKnownNewAttr aIsKnownNew) {
// Keep this in sync with SetParsedAttr below and SetSingleClassFromParser
// above.
const nsAttrValueOrString valueForComparison(aValue);
return SetAttrInternal(aNamespaceID, aName, aPrefix, valueForComparison,
aSubjectPrincipal, aNotify,
[&](nsAttrValue& attrValue) {
if (!ParseAttribute(aNamespaceID, aName, aValue,
aSubjectPrincipal, attrValue)) {
attrValue.SetTo(aValue);
}
});
return SetAttrInternal(
aNamespaceID, aName, aPrefix, valueForComparison, aSubjectPrincipal,
aNotify,
[&](nsAttrValue& attrValue) {
if (!ParseAttribute(aNamespaceID, aName, aValue, aSubjectPrincipal,
attrValue)) {
attrValue.SetTo(aValue);
}
},
aIsKnownNew);
}
nsresult Element::SetAndSwapAttr(nsAtom* aLocalName, nsAttrValue& aValue,
bool* aHadValue) {
MOZ_TRY(mAttrs.SetAndSwapAttr(aLocalName, aValue, aHadValue));
bool* aHadValue, IsKnownNewAttr aIsKnownNew) {
MOZ_TRY(mAttrs.SetAndSwapAttr(aLocalName, aValue, aHadValue, aIsKnownNew));
if (aLocalName == nsGkAtoms::_class) {
UpdateSubtreeBloomFilterForClass(GetClasses());
@@ -3619,8 +3635,9 @@ nsresult Element::SetAndSwapAttr(nsAtom* aLocalName, nsAttrValue& aValue,
}
nsresult Element::SetAndSwapAttr(mozilla::dom::NodeInfo* aName,
nsAttrValue& aValue, bool* aHadValue) {
MOZ_TRY(mAttrs.SetAndSwapAttr(aName, aValue, aHadValue));
nsAttrValue& aValue, bool* aHadValue,
IsKnownNewAttr aIsKnownNew) {
MOZ_TRY(mAttrs.SetAndSwapAttr(aName, aValue, aHadValue, aIsKnownNew));
// Only update bloom filter for null-namespace attributes, since the
// querySelector bloom filter optimization only applies to those.
@@ -3643,14 +3660,16 @@ nsresult Element::SetAttr(int32_t aNamespaceID, nsAtom* aName, nsAtom* aPrefix,
// above.
const nsDependentAtomString valueString(aValue);
const nsAttrValueOrString valueForComparison(valueString);
return SetAttrInternal(aNamespaceID, aName, aPrefix, valueForComparison,
aSubjectPrincipal, aNotify,
[&](nsAttrValue& attrValue) {
if (!ParseAttribute(aNamespaceID, aName, valueString,
aSubjectPrincipal, attrValue)) {
attrValue.SetTo(aValue);
}
});
return SetAttrInternal(
aNamespaceID, aName, aPrefix, valueForComparison, aSubjectPrincipal,
aNotify,
[&](nsAttrValue& attrValue) {
if (!ParseAttribute(aNamespaceID, aName, valueString, aSubjectPrincipal,
attrValue)) {
attrValue.SetTo(aValue);
}
},
IsKnownNewAttr::No);
}
template <typename ParseFunc>
@@ -3658,17 +3677,23 @@ nsresult Element::SetAttrInternal(int32_t aNamespaceID, nsAtom* aName,
nsAtom* aPrefix,
const nsAttrValueOrString& aValue,
nsIPrincipal* aSubjectPrincipal, bool aNotify,
ParseFunc&& aParseFn) {
ParseFunc&& aParseFn,
IsKnownNewAttr aIsKnownNew) {
NS_ENSURE_ARG_POINTER(aName);
NS_ASSERTION(aNamespaceID != kNameSpaceID_Unknown,
"Don't call SetAttr with unknown namespace");
AttrModType modType{0}; // NOTE: Initialized with invalid value.
nsAttrValue oldValue;
bool oldValueSet;
bool oldValueSet = false;
if (OnlyNotifySameValueSet(aNamespaceID, aName, aPrefix, aValue, aNotify,
oldValue, &modType, &oldValueSet)) {
if (aIsKnownNew == IsKnownNewAttr::Yes) {
MOZ_ASSERT(mAttrs.IndexOfAttr(aName, aNamespaceID) == -1,
"Caller asserted attribute is new but it already exists");
modType = AttrModType::Addition;
} else if (OnlyNotifySameValueSet(aNamespaceID, aName, aPrefix, aValue,
aNotify, oldValue, &modType,
&oldValueSet)) {
return NS_OK;
}
@@ -3689,15 +3714,15 @@ nsresult Element::SetAttrInternal(int32_t aNamespaceID, nsAtom* aName,
PreIdMaybeChange(aNamespaceID, aName, &attrValue);
return SetAttrAndNotify(aNamespaceID, aName, aPrefix,
oldValueSet ? &oldValue : nullptr, attrValue,
aSubjectPrincipal, modType, aNotify,
kCallAfterSetAttr, document, updateBatch);
return SetAttrAndNotify(
aNamespaceID, aName, aPrefix, oldValueSet ? &oldValue : nullptr,
attrValue, aSubjectPrincipal, modType, aNotify, kCallAfterSetAttr,
document, updateBatch, aIsKnownNew);
}
nsresult Element::SetParsedAttr(int32_t aNamespaceID, nsAtom* aName,
nsAtom* aPrefix, nsAttrValue& aParsedValue,
bool aNotify) {
bool aNotify, IsKnownNewAttr aIsKnownNew) {
// Keep this in sync with SetAttr and SetSingleClassFromParser above
NS_ENSURE_ARG_POINTER(aName);
@@ -3706,9 +3731,13 @@ nsresult Element::SetParsedAttr(int32_t aNamespaceID, nsAtom* aName,
AttrModType modType{0}; // NOTE: Initialized with invalid value.
nsAttrValue oldValue;
bool oldValueSet;
bool oldValueSet = false;
{
if (aIsKnownNew == IsKnownNewAttr::Yes) {
MOZ_ASSERT(mAttrs.IndexOfAttr(aName, aNamespaceID) == -1,
"Caller asserted attribute is new but it already exists");
modType = AttrModType::Addition;
} else {
const nsAttrValueOrString value(aParsedValue);
if (OnlyNotifySameValueSet(aNamespaceID, aName, aPrefix, value, aNotify,
oldValue, &modType, &oldValueSet)) {
@@ -3731,7 +3760,7 @@ nsresult Element::SetParsedAttr(int32_t aNamespaceID, nsAtom* aName,
return SetAttrAndNotify(aNamespaceID, aName, aPrefix,
oldValueSet ? &oldValue : nullptr, aParsedValue,
nullptr, modType, aNotify, kCallAfterSetAttr,
document, updateBatch);
document, updateBatch, aIsKnownNew);
}
static MOZ_ALWAYS_INLINE void SetLifecycleCallbackNamespaceURI(
@@ -3930,7 +3959,7 @@ nsresult Element::SetAttrAndNotify(
const nsAttrValue* aOldValue, nsAttrValue& aParsedValue,
nsIPrincipal* aSubjectPrincipal, AttrModType aModType, bool aNotify,
bool aCallAfterSetAttr, Document* aComposedDocument,
const mozAutoDocUpdate& aGuard) {
const mozAutoDocUpdate& aGuard, IsKnownNewAttr aIsKnownNew) {
// NOTE: Please keep changes to this method in sync with
// `SetNoNameSpaceAttrOnNewlyCreatedElement`!
nsMutationGuard::DidMutate();
@@ -3952,7 +3981,7 @@ nsresult Element::SetAttrAndNotify(
hadDirAuto = HasDirAuto(); // already takes bdi into account
}
MOZ_TRY(SetAndSwapAttr(aName, aParsedValue, &oldValueSet));
MOZ_TRY(SetAndSwapAttr(aName, aParsedValue, &oldValueSet, aIsKnownNew));
if (IsAttributeMapped(aName) && !IsPendingMappedAttributeEvaluation()) {
mAttrs.InfallibleMarkAsPendingPresAttributeEvaluation();
if (Document* doc = GetComposedDoc()) {
@@ -3962,7 +3991,7 @@ nsresult Element::SetAttrAndNotify(
} else {
RefPtr<mozilla::dom::NodeInfo> ni = NodeInfoManager()->GetNodeInfo(
aName, aPrefix, aNamespaceID, ATTRIBUTE_NODE);
MOZ_TRY(SetAndSwapAttr(ni, aParsedValue, &oldValueSet));
MOZ_TRY(SetAndSwapAttr(ni, aParsedValue, &oldValueSet, aIsKnownNew));
}
PostIdMaybeChange(aNamespaceID, aName, &valueForAfterSetAttr);
@@ -4770,14 +4799,18 @@ nsresult Element::CopyInnerTo(Element* aDst) {
} else if (isSVG) {
nsAutoString valStr;
value->ToString(valStr);
// aDst started empty via EnsureCapacityToClone, so every attribute here
// is a fresh addition and the fast path is safe.
MOZ_TRY(aDst->SetAttr(name->NamespaceID(), name->LocalName(),
name->GetPrefix(), valStr, false));
name->GetPrefix(), valStr, nullptr, false,
IsKnownNewAttr::Yes));
continue;
}
MOZ_ASSERT(value->StoresOwnData());
nsAttrValue valueCopy(*info.mValue);
MOZ_TRY(aDst->SetParsedAttr(name->NamespaceID(), name->LocalName(),
name->GetPrefix(), valueCopy, false));
name->GetPrefix(), valueCopy, false,
IsKnownNewAttr::Yes));
}
// https://dom.spec.whatwg.org/#clone-a-single-node
+21 -10
View File
@@ -920,7 +920,8 @@ class Element : public FragmentOrElement {
// aParsedValue receives the old value of the attribute. That's useful if
// either the input or output value of aParsedValue is StoresOwnData.
nsresult SetParsedAttr(int32_t aNameSpaceID, nsAtom* aName, nsAtom* aPrefix,
nsAttrValue& aParsedValue, bool aNotify);
nsAttrValue& aParsedValue, bool aNotify,
IsKnownNewAttr aIsKnownNew);
/**
* This is meant to be called only by the HTML parser and, at this time,
@@ -1064,16 +1065,18 @@ class Element : public FragmentOrElement {
*/
nsresult SetAttr(int32_t aNameSpaceID, nsAtom* aName, const nsAString& aValue,
bool aNotify) {
return SetAttr(aNameSpaceID, aName, nullptr, aValue, aNotify);
return SetAttr(aNameSpaceID, aName, nullptr, aValue, nullptr, aNotify,
IsKnownNewAttr::No);
}
nsresult SetAttr(int32_t aNameSpaceID, nsAtom* aName, nsAtom* aPrefix,
const nsAString& aValue, bool aNotify) {
return SetAttr(aNameSpaceID, aName, aPrefix, aValue, nullptr, aNotify);
return SetAttr(aNameSpaceID, aName, aPrefix, aValue, nullptr, aNotify,
IsKnownNewAttr::No);
}
nsresult SetAttr(int32_t aNameSpaceID, nsAtom* aName, const nsAString& aValue,
nsIPrincipal* aTriggeringPrincipal, bool aNotify) {
return SetAttr(aNameSpaceID, aName, nullptr, aValue, aTriggeringPrincipal,
aNotify);
aNotify, IsKnownNewAttr::No);
}
/**
@@ -1098,7 +1101,8 @@ class Element : public FragmentOrElement {
*/
nsresult SetAttr(int32_t aNameSpaceID, nsAtom* aName, nsAtom* aPrefix,
const nsAString& aValue,
nsIPrincipal* aMaybeScriptedPrincipal, bool aNotify);
nsIPrincipal* aMaybeScriptedPrincipal, bool aNotify,
IsKnownNewAttr aIsKnownNew);
nsresult SetAttr(int32_t aNameSpaceID, nsAtom* aName, nsAtom* aPrefix,
nsAtom* aValue, nsIPrincipal* aMaybeScriptedPrincipal,
@@ -1125,7 +1129,7 @@ class Element : public FragmentOrElement {
* @param aHadValue set to true if attribute existed, false otherwise
*/
nsresult SetAndSwapAttr(nsAtom* aLocalName, nsAttrValue& aValue,
bool* aHadValue);
bool* aHadValue, IsKnownNewAttr aIsKnownNew);
/**
* Swap an attribute value. This is a public wrapper that ensures bloom
@@ -1138,7 +1142,7 @@ class Element : public FragmentOrElement {
* @param aHadValue set to true if attribute existed, false otherwise
*/
nsresult SetAndSwapAttr(mozilla::dom::NodeInfo* aName, nsAttrValue& aValue,
bool* aHadValue);
bool* aHadValue, IsKnownNewAttr aIsKnownNew);
/**
* Get the namespace / name / prefix of a given attribute.
@@ -2282,7 +2286,7 @@ class Element : public FragmentOrElement {
nsresult SetAttrInternal(int32_t aNamespaceID, nsAtom* aName, nsAtom* aPrefix,
const nsAttrValueOrString& aValueForComparison,
nsIPrincipal* aSubjectPrincipal, bool aNotify,
ParseFunc&& aParseFn);
ParseFunc&& aParseFn, IsKnownNewAttr aIsKnownNew);
/**
* Set attribute and (if needed) notify documentobservers. This will send the
@@ -2330,7 +2334,8 @@ class Element : public FragmentOrElement {
nsIPrincipal* aSubjectPrincipal,
AttrModType aModType, bool aNotify,
bool aCallAfterSetAttr, Document* aComposedDocument,
const mozAutoDocUpdate& aGuard);
const mozAutoDocUpdate& aGuard,
IsKnownNewAttr aIsKnownNew);
/**
* Convert an attribute string value to attribute type based on the type of
@@ -2467,9 +2472,15 @@ class Element : public FragmentOrElement {
* Internal hook for converting an attribute name-string to nsAttrName in
* case there is such existing attribute. aNameToUse can be passed to get
* name which was used for looking for the attribute (lowercase in HTML).
*
* When aOutAtom is non-null and no matching attribute is found, *aOutAtom
* is set to the atomized lookup name so callers can reuse it without
* re-atomizing. In all other cases (match found, or the element has no
* attributes), *aOutAtom is set to nullptr.
*/
const nsAttrName* InternalGetAttrNameFromQName(
const nsAString& aStr, nsAutoString* aNameToUse = nullptr) const;
const nsAString& aStr, nsAutoString* aNameToUse = nullptr,
RefPtr<nsAtom>* aOutAtom = nullptr) const;
virtual Element* GetNameSpaceElement() override { return this; }
+3 -2
View File
@@ -106,7 +106,7 @@ nsresult nsStyledElement::SetInlineStyleDeclaration(
return SetAttrAndNotify(kNameSpaceID_None, nsGkAtoms::style, nullptr,
aData.mOldValue.ptrOr(nullptr), attrValue, nullptr,
aData.mModType, true, kDontCallAfterSetAttr, document,
updateBatch);
updateBatch, mozilla::dom::IsKnownNewAttr::No);
}
// ---------------------------------------------------------------
@@ -152,7 +152,8 @@ nsresult nsStyledElement::ReparseStyleAttribute(bool aForceInDataDoc) {
// Don't bother going through SetInlineStyleDeclaration; we don't
// want to fire off mutation events or document notifications anyway
bool oldValueSet;
nsresult rv = SetAndSwapAttr(nsGkAtoms::style, attrValue, &oldValueSet);
nsresult rv = SetAndSwapAttr(nsGkAtoms::style, attrValue, &oldValueSet,
mozilla::dom::IsKnownNewAttr::No);
NS_ENSURE_SUCCESS(rv, rv);
}
+4 -2
View File
@@ -473,7 +473,8 @@ nsresult ElementInternals::SetAttr(nsAtom* aName, const nsAString& aValue) {
}
} else {
bool attrHadValue = false;
rs = mAttrs.SetAndSwapAttr(aName, attrValue, &attrHadValue);
rs = mAttrs.SetAndSwapAttr(aName, attrValue, &attrHadValue,
mozilla::dom::IsKnownNewAttr::No);
}
nsMutationGuard::DidMutate();
@@ -489,7 +490,8 @@ nsresult ElementInternals::SetAttrInternal(nsAtom* aName,
const nsAString& aValue) {
bool attrHadValue;
nsAttrValue attrValue(aValue);
return mAttrs.SetAndSwapAttr(aName, attrValue, &attrHadValue);
return mAttrs.SetAndSwapAttr(aName, attrValue, &attrHadValue,
mozilla::dom::IsKnownNewAttr::No);
}
nsresult ElementInternals::UnsetAttrInternal(nsAtom* aName) {
+6 -5
View File
@@ -1469,7 +1469,8 @@ void SVGElement::DidChangeValue(nsAtom* aName, nsAttrValue& aNewValue,
const nsAttrValue emptyValue;
SetAttrAndNotify(kNameSpaceID_None, aName, nullptr, &emptyValue, aNewValue,
nullptr, modType, kNotifyDocumentObservers,
kCallAfterSetAttr, GetComposedDoc(), aProofOfUpdate);
kCallAfterSetAttr, GetComposedDoc(), aProofOfUpdate,
mozilla::dom::IsKnownNewAttr::No);
}
nsAtom* SVGElement::GetEventNameForAttr(nsAtom* aAttr) {
@@ -1763,7 +1764,7 @@ void SVGElement::DidChangeNumber(uint8_t aAttrEnum) {
attrValue.SetTo(info.mValues[aAttrEnum].GetBaseValue(), nullptr);
SetParsedAttr(kNameSpaceID_None, info.mInfos[aAttrEnum].mName, nullptr,
attrValue, true);
attrValue, true, mozilla::dom::IsKnownNewAttr::No);
}
void SVGElement::GetAnimatedNumberValues(float* aFirst, ...) {
@@ -1822,7 +1823,7 @@ void SVGElement::DidChangeInteger(uint8_t aAttrEnum) {
attrValue.SetTo(info.mValues[aAttrEnum].GetBaseValue(), nullptr);
SetParsedAttr(kNameSpaceID_None, info.mInfos[aAttrEnum].mName, nullptr,
attrValue, true);
attrValue, true, mozilla::dom::IsKnownNewAttr::No);
}
void SVGElement::GetAnimatedIntegerValues(int32_t* aFirst, ...) {
@@ -1880,7 +1881,7 @@ void SVGElement::DidChangeBoolean(uint8_t aAttrEnum) {
nsAttrValue attrValue(info.mValues[aAttrEnum].GetBaseValueAtom());
SetParsedAttr(kNameSpaceID_None, info.mInfos[aAttrEnum].mName, nullptr,
attrValue, true);
attrValue, true, mozilla::dom::IsKnownNewAttr::No);
}
SVGElement::EnumAttributesInfo SVGElement::GetEnumInfo() {
@@ -1896,7 +1897,7 @@ void SVGElement::DidChangeEnum(uint8_t aAttrEnum) {
nsAttrValue attrValue(info.mValues[aAttrEnum].GetBaseValueAtom(this));
SetParsedAttr(kNameSpaceID_None, info.mInfos[aAttrEnum].mName, nullptr,
attrValue, true);
attrValue, true, mozilla::dom::IsKnownNewAttr::No);
}
SVGAnimatedOrient* SVGElement::GetAnimatedOrient() { return nullptr; }
+2 -1
View File
@@ -997,7 +997,8 @@ nsresult nsXULElement::MakeHeavyweight(nsXULPrototypeElement* aPrototype) {
nsAttrValue value(protoattr.mValue);
MOZ_TRY(SetParsedAttr(
protoattr.mName.NamespaceID(), protoattr.mName.LocalName(),
protoattr.mName.GetPrefix(), value, /* aNotify = */ false));
protoattr.mName.GetPrefix(), value,
/* aNotify = */ false, mozilla::dom::IsKnownNewAttr::No));
}
return NS_OK;
}