Correctly handle updates over attached Attrs involving null values.
Element attributes having a null value are not stored by the ElementData's
attribute collection. When updating an Element-attached Attr with
a new value, which previously was null, correctly handle updates over
null values (before and after.)
R=
BUG=472840
Review URL: https://codereview.chromium.org/1073823002
git-svn-id: svn://svn.chromium.org/blink/trunk@193484 bbb929c8-8fbe-4397-9dbb-9b2b20218538
diff --git a/LayoutTests/fast/dom/Attr/update-attribute-node-null-value-no-crash-expected.txt b/LayoutTests/fast/dom/Attr/update-attribute-node-null-value-no-crash-expected.txt
new file mode 100644
index 0000000..9743f61
--- /dev/null
+++ b/LayoutTests/fast/dom/Attr/update-attribute-node-null-value-no-crash-expected.txt
@@ -0,0 +1,17 @@
+Updating value of an attached Attr node having a null value
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS attr.value is ""
+PASS attr.value = null; attr.value is null
+PASS element.setAttributeNode(attr); is null
+PASS element.getAttribute('nullable'); is null
+PASS attr.value = 'noCrash'; attr.value is "noCrash"
+PASS element.getAttribute('nullable') is "noCrash"
+PASS attr.value = null; attr.value is null
+PASS element.getAttribute('nullable') is null
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/fast/dom/Attr/update-attribute-node-null-value-no-crash.html b/LayoutTests/fast/dom/Attr/update-attribute-node-null-value-no-crash.html
new file mode 100644
index 0000000..af3a5a5
--- /dev/null
+++ b/LayoutTests/fast/dom/Attr/update-attribute-node-null-value-no-crash.html
@@ -0,0 +1,22 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<script src="../../../resources/js-test.js"></script>
+</head>
+<body>
+<script>
+description("Updating value of an attached Attr node having a null value");
+
+var element = document.createElement("div");
+var attr = document.createAttribute("nullable");
+shouldBeEmptyString("attr.value");
+shouldBeNull("attr.value = null; attr.value");
+shouldBeNull("element.setAttributeNode(attr);");
+shouldBeNull("element.getAttribute('nullable');");
+shouldBeEqualToString("attr.value = 'noCrash'; attr.value", "noCrash");
+shouldBeEqualToString("element.getAttribute('nullable')", "noCrash");
+shouldBeNull("attr.value = null; attr.value");
+shouldBeNull("element.getAttribute('nullable')");
+</script>
+</body>
+</html>
diff --git a/Source/core/dom/Attr.cpp b/Source/core/dom/Attr.cpp
index a2641d2..3c09bc2 100644
--- a/Source/core/dom/Attr.cpp
+++ b/Source/core/dom/Attr.cpp
@@ -111,7 +111,7 @@
// attributes as the JS callback could alter the attributes and leave us in a bad state.
removeChildren(OmitSubtreeModifiedEvent);
if (m_element)
- elementAttribute().setValue(value);
+ updateElementAttribute(value);
else
m_standaloneValueOrAttachedLocalName = value;
createTextChild();
@@ -187,7 +187,7 @@
m_element->willModifyAttribute(qualifiedName(), value(), newValue);
if (m_element)
- elementAttribute().setValue(newValue);
+ updateElementAttribute(newValue);
else
m_standaloneValueOrAttachedLocalName = newValue;
@@ -202,11 +202,19 @@
return m_standaloneValueOrAttachedLocalName;
}
-Attribute& Attr::elementAttribute()
+void Attr::updateElementAttribute(const AtomicString& value)
{
ASSERT(m_element);
ASSERT(m_element->elementData());
- return *m_element->ensureUniqueElementData().attributes().find(qualifiedName());
+ MutableAttributeCollection attributes = m_element->ensureUniqueElementData().attributes();
+ size_t index = attributes.findIndex(qualifiedName());
+ if (index == kNotFound) {
+ // Element attributes with null values are not stored.
+ if (!value.isNull())
+ attributes.append(qualifiedName(), value);
+ return;
+ }
+ return attributes[index].setValue(value);
}
void Attr::detachFromElementWithValue(const AtomicString& value)
diff --git a/Source/core/dom/Attr.h b/Source/core/dom/Attr.h
index 97225bf..b0794f8 100644
--- a/Source/core/dom/Attr.h
+++ b/Source/core/dom/Attr.h
@@ -86,7 +86,7 @@
virtual void childrenChanged(const ChildrenChange&) override;
- Attribute& elementAttribute();
+ void updateElementAttribute(const AtomicString&);
// Attr wraps either an element/name, or a name/value pair (when it's a standalone Node.)
// Note that m_name is always set, but m_element/m_standaloneValue may be null.