Make HTMLCollections play nice after their base node is gone.
<http://webkit.org/b/75410>

Reviewed by Anders Carlsson.

Source/WebCore: 

Added HTMLCollection::detachFromNode() and call that from destructors of nodes
with cached collections.

Sprinkled checks/assertions where applicable to make sure HTMLCollections are
empty after their associated node has been destroyed.

This is a slight change in behavior, as collections would previously keep
their nodes alive indefinitely. Added a test to document this.

Test: fast/dom/htmlcollection-zombies.html

* dom/Document.cpp:
(WebCore::Document::~Document):
* html/HTMLAllCollection.cpp:
(WebCore::HTMLAllCollection::namedItemWithIndex):
* html/HTMLCollection.cpp:
(WebCore::HTMLCollection::detachFromNode):
(WebCore::HTMLCollection::resetCollectionInfo):
(WebCore::HTMLCollection::itemAfter):
(WebCore::HTMLCollection::calcLength):
(WebCore::HTMLCollection::length):
(WebCore::HTMLCollection::item):
(WebCore::HTMLCollection::nextItem):
(WebCore::HTMLCollection::namedItem):
(WebCore::HTMLCollection::updateNameCache):
(WebCore::HTMLCollection::hasNamedItem):
(WebCore::HTMLCollection::namedItems):
(WebCore::HTMLCollection::tags):
* html/HTMLCollection.h:
* html/HTMLFormCollection.cpp:
(WebCore::HTMLFormCollection::calcLength):
(WebCore::HTMLFormCollection::item):
(WebCore::HTMLFormCollection::getNamedItem):
(WebCore::HTMLFormCollection::getNamedFormItem):
(WebCore::HTMLFormCollection::namedItem):
(WebCore::HTMLFormCollection::updateNameCache):
* html/HTMLFormElement.cpp:
(WebCore::HTMLFormElement::~HTMLFormElement):
* html/HTMLNameCollection.cpp:
(WebCore::HTMLNameCollection::itemAfter):
* html/HTMLOptionsCollection.cpp:
(WebCore::HTMLOptionsCollection::add):
(WebCore::HTMLOptionsCollection::remove):
(WebCore::HTMLOptionsCollection::selectedIndex):
(WebCore::HTMLOptionsCollection::setSelectedIndex):
(WebCore::HTMLOptionsCollection::setLength):
* html/HTMLPropertiesCollection.cpp:
(WebCore::HTMLPropertiesCollection::length):
(WebCore::HTMLPropertiesCollection::item):
(WebCore::HTMLPropertiesCollection::names):
* html/HTMLSelectElement.cpp:
(WebCore::HTMLSelectElement::~HTMLSelectElement):
* html/HTMLSelectElement.h:
* html/HTMLTableElement.cpp:
(WebCore::HTMLTableElement::~HTMLTableElement):
* html/HTMLTableElement.h:
* html/HTMLTableRowsCollection.cpp:
(WebCore::HTMLTableRowsCollection::itemAfter):

LayoutTests: 

* fast/dom/htmlcollection-zombies-expected.txt: Added.
* fast/dom/htmlcollection-zombies.html: Added.


git-svn-id: http://svn.webkit.org/repository/webkit/trunk@103873 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/Source/WebCore/html/HTMLCollection.cpp b/Source/WebCore/html/HTMLCollection.cpp
index 7b9f725e..f7aed50 100644
--- a/Source/WebCore/html/HTMLCollection.cpp
+++ b/Source/WebCore/html/HTMLCollection.cpp
@@ -109,8 +109,16 @@
         m_base->deref();
 }
 
+void HTMLCollection::detachFromNode()
+{
+    m_base = 0;
+    m_baseIsRetained = false;
+}
+
 void HTMLCollection::resetCollectionInfo() const
 {
+    ASSERT(m_base);
+
     uint64_t docversion = static_cast<HTMLDocument*>(m_base->document())->domTreeVersion();
 
     if (!m_info) {
@@ -184,6 +192,8 @@
 
 Element* HTMLCollection::itemAfter(Element* previous) const
 {
+    ASSERT(m_base);
+
     Node* current;
     if (!previous)
         current = m_base->firstChild();
@@ -203,6 +213,8 @@
 
 unsigned HTMLCollection::calcLength() const
 {
+    ASSERT(m_base);
+
     unsigned len = 0;
     for (Element* current = itemAfter(0); current; current = itemAfter(current))
         ++len;
@@ -213,6 +225,9 @@
 // calculation every time if anything has changed
 unsigned HTMLCollection::length() const
 {
+    if (!m_base)
+        return 0;
+
     resetCollectionInfo();
     if (!m_info->hasLength) {
         m_info->length = calcLength();
@@ -223,6 +238,9 @@
 
 Node* HTMLCollection::item(unsigned index) const
 {
+    if (!m_base)
+        return 0;
+
      resetCollectionInfo();
      if (m_info->current && m_info->position == index)
          return m_info->current;
@@ -249,8 +267,9 @@
 
 Node* HTMLCollection::nextItem() const
 {
+     ASSERT(m_base);
      resetCollectionInfo();
- 
+
      // Look for the 'second' item. The first one is currentItem, already given back.
      Element* retval = itemAfter(m_info->current);
      m_info->current = retval;
@@ -288,6 +307,9 @@
 
 Node* HTMLCollection::namedItem(const AtomicString& name) const
 {
+    if (!m_base)
+        return 0;
+
     // http://msdn.microsoft.com/workshop/author/dhtml/reference/methods/nameditem.asp
     // This method first searches for an object with a matching id
     // attribute. If a match is not found, the method then searches for an
@@ -315,9 +337,11 @@
 
 void HTMLCollection::updateNameCache() const
 {
+    ASSERT(m_base);
+
     if (m_info->hasNameCache)
         return;
-    
+
     for (Element* element = itemAfter(0); element; element = itemAfter(element)) {
         if (!element->isHTMLElement())
             continue;
@@ -335,6 +359,9 @@
 
 bool HTMLCollection::hasNamedItem(const AtomicString& name) const
 {
+    if (!m_base)
+        return false;
+
     if (name.isEmpty())
         return false;
 
@@ -357,8 +384,10 @@
 
 void HTMLCollection::namedItems(const AtomicString& name, Vector<RefPtr<Node> >& result) const
 {
+    if (!m_base)
+        return;
+
     ASSERT(result.isEmpty());
-    
     if (name.isEmpty())
         return;
 
@@ -378,6 +407,9 @@
 
 PassRefPtr<NodeList> HTMLCollection::tags(const String& name)
 {
+    if (!m_base)
+        return 0;
+
     return m_base->getElementsByTagName(name);
 }