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:
committed by
jjaschke@mozilla.com
parent
828f5369c5
commit
01ee16866b
+2
-1
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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; }
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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; }
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user