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.