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);
}