summaryrefslogtreecommitdiffstats
path: root/dom/base/Element.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'dom/base/Element.cpp')
-rw-r--r--dom/base/Element.cpp78
1 files changed, 54 insertions, 24 deletions
diff --git a/dom/base/Element.cpp b/dom/base/Element.cpp
index 7c5029e2e..11ad042a0 100644
--- a/dom/base/Element.cpp
+++ b/dom/base/Element.cpp
@@ -2382,7 +2382,8 @@ Element::MaybeCheckSameAttrVal(int32_t aNamespaceID,
bool aNotify,
nsAttrValue& aOldValue,
uint8_t* aModType,
- bool* aHasListeners)
+ bool* aHasListeners,
+ bool* aOldValueSet)
{
bool modification = false;
*aHasListeners = aNotify &&
@@ -2390,6 +2391,8 @@ Element::MaybeCheckSameAttrVal(int32_t aNamespaceID,
NS_EVENT_BITS_MUTATION_ATTRMODIFIED,
this);
+ *aOldValueSet = false;
+
// If we have no listeners and aNotify is false, we are almost certainly
// coming from the content sink and will almost certainly have no previous
// value. Even if we do, setting the value is cheap when we have no
@@ -2413,6 +2416,7 @@ Element::MaybeCheckSameAttrVal(int32_t aNamespaceID,
// We have to serialize the value anyway in order to create the
// mutation event so there's no cost in doing it now.
aOldValue.SetToSerialized(*info.mValue);
+ *aOldValueSet = true;
}
bool valueMatches = aValue.EqualsAsStrings(*info.mValue);
if (valueMatches && aPrefix == info.mName->GetPrefix()) {
@@ -2432,10 +2436,12 @@ Element::OnlyNotifySameValueSet(int32_t aNamespaceID, nsIAtom* aName,
nsIAtom* aPrefix,
const nsAttrValueOrString& aValue,
bool aNotify, nsAttrValue& aOldValue,
- uint8_t* aModType, bool* aHasListeners)
+ uint8_t* aModType, bool* aHasListeners,
+ bool* aOldValueSet)
{
if (!MaybeCheckSameAttrVal(aNamespaceID, aName, aPrefix, aValue, aNotify,
- aOldValue, aModType, aHasListeners)) {
+ aOldValue, aModType, aHasListeners,
+ aOldValueSet)) {
return false;
}
@@ -2466,9 +2472,10 @@ Element::SetAttr(int32_t aNamespaceID, nsIAtom* aName,
// OnlyNotifySameValueSet call.
nsAttrValueOrString value(aValue);
nsAttrValue oldValue;
+ bool oldValueSet;
if (OnlyNotifySameValueSet(aNamespaceID, aName, aPrefix, value, aNotify,
- oldValue, &modType, &hasListeners)) {
+ oldValue, &modType, &hasListeners, &oldValueSet)) {
return NS_OK;
}
@@ -2501,7 +2508,8 @@ Element::SetAttr(int32_t aNamespaceID, nsIAtom* aName,
attrValue.SetTo(aValue);
}
- return SetAttrAndNotify(aNamespaceID, aName, aPrefix, oldValue,
+ return SetAttrAndNotify(aNamespaceID, aName, aPrefix,
+ oldValueSet ? &oldValue : nullptr,
attrValue, modType, hasListeners, aNotify,
kCallAfterSetAttr, document, updateBatch);
}
@@ -2526,9 +2534,10 @@ Element::SetParsedAttr(int32_t aNamespaceID, nsIAtom* aName,
bool hasListeners;
nsAttrValueOrString value(aParsedValue);
nsAttrValue oldValue;
+ bool oldValueSet;
if (OnlyNotifySameValueSet(aNamespaceID, aName, aPrefix, value, aNotify,
- oldValue, &modType, &hasListeners)) {
+ oldValue, &modType, &hasListeners, &oldValueSet)) {
return NS_OK;
}
@@ -2542,7 +2551,8 @@ Element::SetParsedAttr(int32_t aNamespaceID, nsIAtom* aName,
nsIDocument* document = GetComposedDoc();
mozAutoDocUpdate updateBatch(document, UPDATE_CONTENT_MODEL, aNotify);
- return SetAttrAndNotify(aNamespaceID, aName, aPrefix, oldValue,
+ return SetAttrAndNotify(aNamespaceID, aName, aPrefix,
+ oldValueSet ? &oldValue : nullptr,
aParsedValue, modType, hasListeners, aNotify,
kCallAfterSetAttr, document, updateBatch);
}
@@ -2551,7 +2561,7 @@ nsresult
Element::SetAttrAndNotify(int32_t aNamespaceID,
nsIAtom* aName,
nsIAtom* aPrefix,
- const nsAttrValue& aOldValue,
+ const nsAttrValue* aOldValue,
nsAttrValue& aParsedValue,
uint8_t aModType,
bool aFireMutation,
@@ -2573,6 +2583,7 @@ Element::SetAttrAndNotify(int32_t aNamespaceID,
bool hadValidDir = false;
bool hadDirAuto = false;
+ bool oldValueSet;
if (aNamespaceID == kNameSpaceID_None) {
if (aName == nsGkAtoms::dir) {
@@ -2583,8 +2594,8 @@ Element::SetAttrAndNotify(int32_t aNamespaceID,
// XXXbz Perhaps we should push up the attribute mapping function
// stuff to Element?
if (!IsAttributeMapped(aName) ||
- !SetMappedAttribute(aComposedDocument, aName, aParsedValue, &rv)) {
- rv = mAttrsAndChildren.SetAndSwapAttr(aName, aParsedValue);
+ !SetAndSwapMappedAttribute(aComposedDocument, aName, aParsedValue, &oldValueSet, &rv)) {
+ rv = mAttrsAndChildren.SetAndSwapAttr(aName, aParsedValue, &oldValueSet);
}
}
else {
@@ -2593,14 +2604,24 @@ Element::SetAttrAndNotify(int32_t aNamespaceID,
aNamespaceID,
nsIDOMNode::ATTRIBUTE_NODE);
- rv = mAttrsAndChildren.SetAndSwapAttr(ni, aParsedValue);
+ rv = mAttrsAndChildren.SetAndSwapAttr(ni, aParsedValue, &oldValueSet);
}
+ NS_ENSURE_SUCCESS(rv, rv);
// If the old value owns its own data, we know it is OK to keep using it.
- const nsAttrValue* oldValue =
- aParsedValue.StoresOwnData() ? &aParsedValue : &aOldValue;
-
- NS_ENSURE_SUCCESS(rv, rv);
+ // oldValue will be null if there was no previously set value
+ const nsAttrValue* oldValue;
+ if (aParsedValue.StoresOwnData()) {
+ if (oldValueSet) {
+ oldValue = &aParsedValue;
+ } else {
+ oldValue = nullptr;
+ }
+ } else {
+ // No need to conditionally assign null here. If there was no previously
+ // set value for the attribute, aOldValue will already be null.
+ oldValue = aOldValue;
+ }
if (aComposedDocument || HasFlag(NODE_FORCE_XBL_BINDINGS)) {
RefPtr<nsXBLBinding> binding = GetXBLBinding();
@@ -2618,7 +2639,14 @@ Element::SetAttrAndNotify(int32_t aNamespaceID,
MOZ_ASSERT(data->mState == CustomElementData::State::eCustom,
"AttributeChanged callback should fire only if "
"custom element state is custom");
- nsCOMPtr<nsIAtom> oldValueAtom = oldValue->GetAsAtom();
+ nsCOMPtr<nsIAtom> oldValueAtom;
+ if (oldValue) {
+ oldValueAtom = oldValue->GetAsAtom();
+ } else {
+ // If there is no old value, get the value of the uninitialized attribute
+ // that was swapped with aParsedValue.
+ oldValueAtom = aParsedValue.GetAsAtom();
+ }
nsCOMPtr<nsIAtom> newValueAtom = valueForAfterSetAttr.GetAsAtom();
nsAutoString ns;
nsContentUtils::NameSpaceManager()->GetNameSpaceURI(aNamespaceID, ns);
@@ -2638,7 +2666,8 @@ Element::SetAttrAndNotify(int32_t aNamespaceID,
}
if (aCallAfterSetAttr) {
- rv = AfterSetAttr(aNamespaceID, aName, &valueForAfterSetAttr, aNotify);
+ rv = AfterSetAttr(aNamespaceID, aName, &valueForAfterSetAttr, oldValue,
+ aNotify);
NS_ENSURE_SUCCESS(rv, rv);
if (aNamespaceID == kNameSpaceID_None && aName == nsGkAtoms::dir) {
@@ -2654,7 +2683,7 @@ Element::SetAttrAndNotify(int32_t aNamespaceID,
// Callers only compute aOldValue under certain conditions which may not
// be triggered by all nsIMutationObservers.
nsNodeUtils::AttributeChanged(this, aNamespaceID, aName, aModType,
- oldValue == &aParsedValue ? &aParsedValue : nullptr);
+ aParsedValue.StoresOwnData() ? &aParsedValue : nullptr);
}
if (aFireMutation) {
@@ -2672,7 +2701,7 @@ Element::SetAttrAndNotify(int32_t aNamespaceID,
if (!newValue.IsEmpty()) {
mutation.mNewAttrValue = NS_Atomize(newValue);
}
- if (!oldValue->IsEmptyString()) {
+ if (oldValue && !oldValue->IsEmptyString()) {
mutation.mPrevAttrValue = oldValue->GetAsAtom();
}
mutation.mAttrChange = aModType;
@@ -2715,10 +2744,11 @@ Element::ParseAttribute(int32_t aNamespaceID,
}
bool
-Element::SetMappedAttribute(nsIDocument* aDocument,
- nsIAtom* aName,
- nsAttrValue& aValue,
- nsresult* aRetval)
+Element::SetAndSwapMappedAttribute(nsIDocument* aDocument,
+ nsIAtom* aName,
+ nsAttrValue& aValue,
+ bool* aValueWasSet,
+ nsresult* aRetval)
{
*aRetval = NS_OK;
return false;
@@ -2886,7 +2916,7 @@ Element::UnsetAttr(int32_t aNameSpaceID, nsIAtom* aName,
}
}
- rv = AfterSetAttr(aNameSpaceID, aName, nullptr, aNotify);
+ rv = AfterSetAttr(aNameSpaceID, aName, nullptr, &oldValue, aNotify);
NS_ENSURE_SUCCESS(rv, rv);
UpdateState(aNotify);