Reviewed by Dave.
[WebKit-https.git] / WebCore / khtml / html / html_miscimpl.cpp
index a94900f..a628d38 100644 (file)
@@ -24,6 +24,8 @@
 // -------------------------------------------------------------------------
 #include "html/html_miscimpl.h"
 #include "html/html_formimpl.h"
+#include "html/html_imageimpl.h"
+#include "html/html_documentimpl.h"
 
 #include "misc/htmlhashes.h"
 #include "dom/dom_node.h"
@@ -53,320 +55,200 @@ HTMLCollectionImpl::HTMLCollectionImpl(NodeImpl *_base, int _type)
     base = _base;
     base->ref();
     type = _type;
-    currentItem = 0L;
     idsDone = false;
+    info = base->isDocumentNode() ? static_cast<HTMLDocumentImpl*>(base->getDocument())->collectionInfo(type) : new CollectionInfo;
 }
 
 HTMLCollectionImpl::~HTMLCollectionImpl()
 {
+    if (!base->isDocumentNode())
+        delete info;
     base->deref();
 }
 
-unsigned long HTMLCollectionImpl::calcLength(NodeImpl *current) const
+void HTMLCollectionImpl::resetCollectionInfo() const
 {
-    unsigned long len = 0;
-    while(current)
-    {
-        if(current->nodeType() == Node::ELEMENT_NODE)
-        {
-            bool deep = true;
-            HTMLElementImpl *e = static_cast<HTMLElementImpl *>(current);
-            switch(type)
-            {
-            case DOC_IMAGES:
-                if(e->id() == ID_IMG)
-                    len++;
-                break;
-            case DOC_FORMS:
-                if(e->id() == ID_FORM)
-                    len++;
-                break;
-            case TABLE_TBODIES:
-                if(e->id() == ID_TBODY)
-                    len++;
-                else if(e->id() == ID_TABLE)
-                    deep = false;
-                break;
-            case TR_CELLS:
-                if(e->id() == ID_TD || e->id() == ID_TH)
-                    len++;
-                else if(e->id() == ID_TABLE)
-                    deep = false;
-                break;
-            case TABLE_ROWS:
-            case TSECTION_ROWS:
-                if(e->id() == ID_TR)
-                    len++;
-                else if(e->id() == ID_TABLE)
-                    deep = false;
-                break;
-            case SELECT_OPTIONS:
-                if(e->id() == ID_OPTION)
-                    len++;
-                break;
-            case MAP_AREAS:
-                if(e->id() == ID_AREA)
-                    len++;
-                break;
-            case DOC_APPLETS:   // all OBJECT and APPLET elements
-                if(e->id() == ID_OBJECT || e->id() == ID_APPLET)
-                    len++;
-                break;
-            case DOC_LINKS:     // all A _and_ AREA elements with a value for href
-                if(e->id() == ID_A || e->id() == ID_AREA)
-                    if(!e->getAttribute(ATTR_HREF).isNull())
-                        len++;
-                break;
-            case DOC_ANCHORS:      // all A elements with a value for name and all elements with an id attribute
-                if(e->id() == ID_A) {
-                    if(!e->getAttribute(ATTR_NAME).isNull())
-                        len++;
-                }
-                break;
-            case DOC_ALL:      // "all" elements
-                len++;
-                break;
-            case NODE_CHILDREN: // first-level children
-                len++;
-                deep = false;
-                break;
-            default:
-                kdDebug( 6030 ) << "Error in HTMLCollection, wrong tagId!" << endl;
-            }
-            if(deep && current->firstChild())
-                len += calcLength(current->firstChild());
-        }
-        current = current->nextSibling();
+    unsigned int docversion = static_cast<HTMLDocumentImpl*>(base->getDocument())->domTreeVersion();
+    if (info->version != docversion) {
+        info->current = 0;
+        info->position = 0;
+        info->length = 0;
+        info->haslength = false;
+        info->version = docversion;
     }
-    return len;
 }
 
-// since the collections are to be "live", we have to do the
-// calculation every time...
-unsigned long HTMLCollectionImpl::length() const
-{
-    return calcLength(base->firstChild());
-}
 
-NodeImpl *HTMLCollectionImpl::getItem(NodeImpl *current, int index, int &len) const
+NodeImpl *HTMLCollectionImpl::traverseNextItem(NodeImpl *current) const
 {
-    while(current)
-    {
-        if(current->nodeType() == Node::ELEMENT_NODE)
-        {
+    current = current->traverseNextNode();
+
+    while (current) {
+        if(current->nodeType() == Node::ELEMENT_NODE) {
+            bool found = false;
             bool deep = true;
             HTMLElementImpl *e = static_cast<HTMLElementImpl *>(current);
-            switch(type)
-            {
+            switch(type) {
             case DOC_IMAGES:
                 if(e->id() == ID_IMG)
-                    len++;
+                    found = true;
                 break;
             case DOC_FORMS:
                 if(e->id() == ID_FORM)
-                    len++;
+                    found = true;
                 break;
             case TABLE_TBODIES:
                 if(e->id() == ID_TBODY)
-                    len++;
+                    found = true;
                 else if(e->id() == ID_TABLE)
                     deep = false;
                 break;
             case TR_CELLS:
                 if(e->id() == ID_TD || e->id() == ID_TH)
-                    len++;
+                    found = true;
                 else if(e->id() == ID_TABLE)
                     deep = false;
                 break;
             case TABLE_ROWS:
             case TSECTION_ROWS:
                 if(e->id() == ID_TR)
-                    len++;
+                    found = true;
                 else if(e->id() == ID_TABLE)
                     deep = false;
                 break;
             case SELECT_OPTIONS:
                 if(e->id() == ID_OPTION)
-                    len++;
+                    found = true;
                 break;
             case MAP_AREAS:
                 if(e->id() == ID_AREA)
-                    len++;
+                    found = true;
                 break;
             case DOC_APPLETS:   // all OBJECT and APPLET elements
                 if(e->id() == ID_OBJECT || e->id() == ID_APPLET)
-                    len++;
+                    found = true;
+                break;
+            case DOC_EMBEDS:   // all EMBED elements
+                if(e->id() == ID_EMBED)
+                    found = true;
                 break;
             case DOC_LINKS:     // all A _and_ AREA elements with a value for href
                 if(e->id() == ID_A || e->id() == ID_AREA)
                     if(!e->getAttribute(ATTR_HREF).isNull())
-                        len++;
+                        found = true;
                 break;
             case DOC_ANCHORS:      // all A elements with a value for name or an id attribute
                 if(e->id() == ID_A)
                     if(!e->getAttribute(ATTR_NAME).isNull())
-                        len++;
+                        found = true;
                 break;
             case DOC_ALL:
-                len++;
+                found = true;
                 break;
             case NODE_CHILDREN:
-                len++;
+                found = true;
                 deep = false;
                 break;
             default:
                 kdDebug( 6030 ) << "Error in HTMLCollection, wrong tagId!" << endl;
             }
-            if(len == (index + 1)) return current;
-            NodeImpl *retval=0;
-            if(deep && current->firstChild())
-                retval = getItem(current->firstChild(), index, len);
-            if(retval) return retval;
+            if (found) {
+                return current;
+            }
+            if (deep) {
+                current = current->traverseNextNode(base);
+                continue;
+            } 
         }
-        current = current->nextSibling();
+        current = current->traverseNextSibling(base);
     }
     return 0;
 }
 
+
+unsigned long HTMLCollectionImpl::calcLength() const
+{
+    unsigned long len = 0;
+
+    for (NodeImpl *current = traverseNextItem(base); current; current = traverseNextItem(current)) {
+        len++;
+    }
+
+    return len;
+}
+
+// since the collections are to be "live", we have to do the
+// calculation every time if anything has changed
+unsigned long HTMLCollectionImpl::length() const
+{
+    resetCollectionInfo();
+    if (!info->haslength) {
+        info->length = calcLength();
+        info->haslength = true;
+    }
+    return info->length;
+}
+
 NodeImpl *HTMLCollectionImpl::item( unsigned long index ) const
 {
-    int pos = 0;
-    return getItem(base->firstChild(), index, pos);
+     resetCollectionInfo();
+     if (info->current && info->position == index) {
+         return info->current;
+     }
+     if (info->haslength && info->length <= index) {
+         return 0;
+     }
+     if (!info->current || info->position > index) {
+         info->current = traverseNextItem(base);
+         info->position = 0;
+         if (!info->current)
+             return 0;
+     }
+     NodeImpl *node = info->current;
+     for (unsigned pos = info->position; pos < index; pos++) {
+         node = traverseNextItem(node);
+     }     
+     info->current = node;
+     info->position = index;
+     return info->current;
 }
 
 NodeImpl *HTMLCollectionImpl::firstItem() const
 {
-    int pos = 0;
-    currentItem = getItem(base->firstChild(), 0, pos);
-    return currentItem;
+     return item(0);
 }
 
 NodeImpl *HTMLCollectionImpl::nextItem() const
 {
-    int pos = 0;
-    // Look for the 'second' item. The first one is currentItem, already given back.
-    NodeImpl *retval = getItem(currentItem, 1, pos);
-    if (retval)
-    {
-        currentItem = retval;
-        return retval;
-    }
-    // retval was 0, means we have to go up
-    while( !retval && currentItem && currentItem->parentNode()
-           && currentItem->parentNode() != base )
-    {
-        currentItem = currentItem->parentNode();
-        if (currentItem->nextSibling())
-        {
-            // ... and to take the first one from there
-            pos = 0;
-            retval = getItem(currentItem->nextSibling(), 0, pos);
-        }
-    }
-    currentItem = retval;
-    return currentItem;
+     resetCollectionInfo();
+     // Look for the 'second' item. The first one is currentItem, already given back.
+     NodeImpl *retval = traverseNextItem(info->current);
+     info->current = retval;
+     info->position++;
+     return retval;
 }
 
-NodeImpl *HTMLCollectionImpl::getNamedItem( NodeImpl *current, int attr_id,
-                                            const DOMString &name, bool caseSensitive ) const
+bool HTMLCollectionImpl::checkForNameMatch(NodeImpl *node, bool checkName, const DOMString &name, bool caseSensitive) const
 {
-    if(name.isEmpty())
-        return 0;
-
-    while(current)
-    {
-        if(current->nodeType() == Node::ELEMENT_NODE)
-        {
-            bool deep = true;
-            bool check = false;
-            HTMLElementImpl *e = static_cast<HTMLElementImpl *>(current);
-            switch(type)
-            {
-            case DOC_IMAGES:
-                if(e->id() == ID_IMG)
-                    check = true;
-                break;
-            case DOC_FORMS:
-                if(e->id() == ID_FORM)
-                    check = true;
-                break;
-            case TABLE_TBODIES:
-                if(e->id() == ID_TBODY)
-                    check = true;
-                else if(e->id() == ID_TABLE)
-                    deep = false;
-                break;
-            case TR_CELLS:
-                if(e->id() == ID_TD || e->id() == ID_TH)
-                    check = true;
-                else if(e->id() == ID_TABLE)
-                    deep = false;
-                break;
-            case TABLE_ROWS:
-            case TSECTION_ROWS:
-                if(e->id() == ID_TR)
-                    check = true;
-                else if(e->id() == ID_TABLE)
-                    deep = false;
-                break;
-            case SELECT_OPTIONS:
-                if(e->id() == ID_OPTION)
-                    check = true;
-                break;
-            case MAP_AREAS:
-                if(e->id() == ID_AREA)
-                    check = true;
-                break;
-            case DOC_APPLETS:   // all OBJECT and APPLET elements
-                if(e->id() == ID_OBJECT || e->id() == ID_APPLET)
-                    check = true;
-                break;
-            case DOC_LINKS:     // all A _and_ AREA elements with a value for href
-                if(e->id() == ID_A || e->id() == ID_AREA)
-                    if(!e->getAttribute(ATTR_HREF).isNull())
-                        check = true;
-                break;
-            case DOC_ANCHORS:      // all A elements with a value for name
-                if(e->id() == ID_A)
-                    if(!e->getAttribute(ATTR_NAME).isNull())
-                        check = true;
-                break;
-            case DOC_ALL:
-                check = true;
-                break;
-            case NODE_CHILDREN:
-                check = true;
-                deep = false;
-                break;
-            default:
-                kdDebug( 6030 ) << "Error in HTMLCollection, wrong tagId!" << endl;
-                break;
-            }
-            if (check) {
-                bool found;
-                if (caseSensitive)
-                    found = e->getAttribute(attr_id) == name;
-                else
-                    found = e->getAttribute(attr_id).domString().lower() == name.lower();
-                if (found) {
-                    //kdDebug( 6030 ) << "found node: " << e << " " << current << " " << e->id() << " " << e->tagName().string() << endl;
-                    return current;
-                }
-            }
-            NodeImpl *retval = 0;
-            if(deep && current->firstChild())
-                retval = getNamedItem(current->firstChild(), attr_id, name, caseSensitive);
-            if(retval)
-            {
-                //kdDebug( 6030 ) << "got a return value " << retval << endl;
-                return retval;
-            }
+    ElementImpl *e = static_cast<ElementImpl *>(node);
+    if (caseSensitive) {
+        if (checkName) {
+            return e->getAttribute(ATTR_NAME) == name && e->getAttribute(ATTR_ID) != name;
+        } else {
+            return e->getAttribute(ATTR_ID) == name;
+        }
+    } else {
+        if (checkName) {
+            return e->getAttribute(ATTR_NAME).domString().lower() == name.lower() &&
+                e->getAttribute(ATTR_ID).domString().lower() != name.lower();
+        } else {
+            return e->getAttribute(ATTR_ID).domString().lower() == name.lower();
         }
-        current = current->nextSibling();
     }
-    return 0;
 }
 
+
 NodeImpl *HTMLCollectionImpl::namedItem( const DOMString &name, bool caseSensitive ) const
 {
     // http://msdn.microsoft.com/workshop/author/dhtml/reference/methods/nameditem.asp
@@ -374,90 +256,109 @@ NodeImpl *HTMLCollectionImpl::namedItem( const DOMString &name, bool caseSensiti
     // attribute. If a match is not found, the method then searches for an
     // object with a matching name attribute, but only on those elements
     // that are allowed a name attribute.
+    resetCollectionInfo();
     idsDone = false;
-    currentItem = getNamedItem(base->firstChild(), ATTR_ID, name, caseSensitive);
-    if(currentItem)
-        return currentItem;
+
+    NodeImpl *n;
+    for (n = traverseNextItem(base); n; n = traverseNextItem(n)) {
+        if (checkForNameMatch(n, idsDone, name, caseSensitive)) {
+            break;
+        }
+    }
+        
+    info->current = n;
+    if(info->current)
+        return info->current;
     idsDone = true;
-    currentItem = getNamedItem(base->firstChild(), ATTR_NAME, name, caseSensitive);
-    return currentItem;
+
+    for (n = traverseNextItem(base); n; n = traverseNextItem(n)) {
+        if (checkForNameMatch(n, idsDone, name, caseSensitive)) {
+            break;
+        }
+    }
+
+    info->current = n;
+    return info->current;
 }
 
-NodeImpl *HTMLCollectionImpl::nextNamedItem( const DOMString &name ) const
+QValueList<Node> HTMLCollectionImpl::namedItems(const DOMString &name) const
 {
-    // nextNamedItemInternal can return an item that has both id=<name> and name=<name>
-    // Here, we have to filter out such cases.
-    NodeImpl *impl = nextNamedItemInternal( name );
-    if (!idsDone) // looking for id=<name> -> no filtering
-        return impl;
-    // looking for name=<name> -> filter out if id=<name>
-    bool ok = false;
-    while (impl && !ok)
-    {
-        if(impl->nodeType() == Node::ELEMENT_NODE)
-        {
-            HTMLElementImpl *e = static_cast<HTMLElementImpl *>(impl);
-            ok = (e->getAttribute(ATTR_ID) != name);
-            if (!ok)
-                impl = nextNamedItemInternal( name );
-        } else // can't happen
-            ok = true;
+    resetCollectionInfo();
+    
+    QValueList<Node> idResults;
+    QValueList<Node> nameResults;
+
+    for (NodeImpl *n = traverseNextItem(base); n; n = traverseNextItem(n)) {
+        if (checkForNameMatch(n, false, name, true)) {
+            idResults.append(Node(n));
+        } 
+        if (checkForNameMatch(n, true, name, true)) {
+            nameResults.append(Node(n));
+        }
     }
-    return impl;
+
+    if (idResults.isEmpty())
+        return nameResults;
+
+    for (QValueListIterator<Node> iter = nameResults.begin(); iter != nameResults.end(); ++iter) {
+        idResults.append(*iter);
+    }
+
+    return idResults;
 }
 
-NodeImpl *HTMLCollectionImpl::nextNamedItemInternal( const DOMString &name ) const
-{
-    //kdDebug() << "\nHTMLCollectionImpl::nextNamedItem starting at " << currentItem << endl;
-    // Go to next item first (to avoid returning the same)
-    currentItem = nextItem();
-    //kdDebug() << "*HTMLCollectionImpl::nextNamedItem next item is " << currentItem << endl;
 
-    if ( currentItem )
-    {
-        // Then look for next matching named item
-        NodeImpl *retval = getNamedItem(currentItem, idsDone ? ATTR_NAME : ATTR_ID, name);
-        if ( retval )
-        {
-            //kdDebug() << "*HTMLCollectionImpl::nextNamedItem found " << retval << endl;
-            currentItem = retval;
-            return retval;
-        }
+NodeImpl *HTMLCollectionImpl::nextNamedItem( const DOMString &name ) const
+{
+    resetCollectionInfo();
 
-        // retval was 0, means we have to go up
-        while( !retval && currentItem->parentNode()
-               && currentItem->parentNode() != base )
-        {
-            currentItem = currentItem->parentNode();
-            if (currentItem->nextSibling())
-            {
-                // ... and to take the first one from there
-                retval = getNamedItem(currentItem->nextSibling(), idsDone ? ATTR_NAME : ATTR_ID, name);
-            }
-        }
-        if ( retval )
-        {
-            //kdDebug() << "*HTMLCollectionImpl::nextNamedItem found after going up " << retval << endl;
-            currentItem = retval;
-            return currentItem;
+    for (NodeImpl *n = traverseNextItem(info->current ? info->current : base); n; n = traverseNextItem(n)) {
+        if (checkForNameMatch(n, idsDone, name, true)) {
+            info->current = n;
+            return n;
         }
     }
-
-    if ( idsDone )
+    
+    if (idsDone) {
+        info->current = 0; 
         return 0;
-    // After doing all ATTR_ID, do ATTR_NAME
-    //kdDebug() << "*HTMLCollectionImpl::nextNamedItem going to ATTR_NAME now" << endl;
+    }
     idsDone = true;
-    currentItem = getNamedItem(base->firstChild(), ATTR_NAME, name);
-    return currentItem;
 
+    for (NodeImpl *n = traverseNextItem(info->current ? info->current : base); n; n = traverseNextItem(n)) {
+        if (checkForNameMatch(n, idsDone, name, true)) {
+            info->current = n;
+            return n;
+        }
+    }
+
+    return 0;
 }
 
 // -----------------------------------------------------------------------------
 
-unsigned long HTMLFormCollectionImpl::calcLength(NodeImpl*) const
+HTMLFormCollectionImpl::FormCollectionInfo::FormCollectionInfo()
+{
+    reset();
+}
+
+void::HTMLFormCollectionImpl::FormCollectionInfo::reset()
+{
+    elementsArrayPosition = 0;
+}
+
+void HTMLFormCollectionImpl::resetCollectionInfo() const
 {
-    QPtrList<HTMLGenericFormElementImpl> l = static_cast<HTMLFormElementImpl*>( base )->formElements;
+    unsigned int docversion = static_cast<HTMLDocumentImpl*>(base->getDocument())->domTreeVersion();
+    if (info->version != docversion) {
+        formInfo.reset();
+    }
+    HTMLCollectionImpl::resetCollectionInfo();
+}
+
+unsigned long HTMLFormCollectionImpl::calcLength() const
+{
+    QPtrVector<HTMLGenericFormElementImpl> &l = static_cast<HTMLFormElementImpl*>( base )->formElements;
 
     int len = 0;
     for ( unsigned i = 0; i < l.count(); i++ )
@@ -467,17 +368,35 @@ unsigned long HTMLFormCollectionImpl::calcLength(NodeImpl*) const
     return len;
 }
 
-NodeImpl* HTMLFormCollectionImpl::getItem(NodeImpl *, int index, int&) const
+NodeImpl *HTMLFormCollectionImpl::item(unsigned long index) const
 {
-    QPtrList<HTMLGenericFormElementImpl> l = static_cast<HTMLFormElementImpl*>( base )->formElements;
+    resetCollectionInfo();
 
-    for ( unsigned i = 0; i < l.count(); i++ ) {
+    if (info->current && info->position == index) {
+        return info->current;
+    }
+    if (info->haslength && info->length <= index) {
+        return 0;
+    }
+    if (!info->current || info->position > index) {
+        info->current = 0;
+        info->position = 0;
+        formInfo.elementsArrayPosition = 0;
+    }
 
-        if( l.at( i )->isEnumeratable() ) {
-            if ( !index )
-                return l.at( i );
+    QPtrVector<HTMLGenericFormElementImpl> &l = static_cast<HTMLFormElementImpl*>( base )->formElements;
+    unsigned currentIndex = info->position;
+    
+    for (unsigned i = formInfo.elementsArrayPosition; i < l.count(); i++) {
+        if (l[i]->isEnumeratable() ) {
+            if (index == currentIndex) {
+                info->position = index;
+                info->current = l[i];
+                formInfo.elementsArrayPosition = i;
+                return l[i];
+            }
 
-            --index;
+            currentIndex++;
         }
     }
 
@@ -486,7 +405,7 @@ NodeImpl* HTMLFormCollectionImpl::getItem(NodeImpl *, int index, int&) const
 
 NodeImpl* HTMLFormCollectionImpl::getNamedItem(NodeImpl*, int attr_id, const DOMString& name, bool caseSensitive) const
 {
-    currentPos = 0;
+    info->position = 0;
     return getNamedFormItem( attr_id, name, 0, caseSensitive );
 }
 
@@ -494,87 +413,65 @@ NodeImpl* HTMLFormCollectionImpl::getNamedFormItem(int attr_id, const DOMString&
 {
     if(base->nodeType() == Node::ELEMENT_NODE)
     {
-        HTMLElementImpl* e = static_cast<HTMLElementImpl*>(base);
-        if(e->id() == ID_FORM)
+        HTMLElementImpl* baseElement = static_cast<HTMLElementImpl*>(base);
+        bool foundInputElements = false;
+        if(baseElement->id() == ID_FORM)
         {
-            HTMLFormElementImpl* f = static_cast<HTMLFormElementImpl*>(e);
-
-            for(HTMLGenericFormElementImpl* e = f->formElements.first(); e; e = f->formElements.next())
-                if(e->isEnumeratable()) {
+            HTMLFormElementImpl* f = static_cast<HTMLFormElementImpl*>(baseElement);
+            for (unsigned i = 0; i < f->formElements.count(); ++i) {
+                HTMLGenericFormElementImpl* e = f->formElements[i];
+                if (e->isEnumeratable()) {
                     bool found;
                     if (caseSensitive)
                         found = e->getAttribute(attr_id) == name;
                     else
                         found = e->getAttribute(attr_id).domString().lower() == name.lower();
                     if (found) {
+                        foundInputElements = true;
                         if (!duplicateNumber)
                             return e;
                         --duplicateNumber;
                     }
                 }
+            }
         }
-        NodeImpl* retval = getNamedImgItem( base->firstChild(), attr_id, name, duplicateNumber, caseSensitive );
-        if ( retval )
-            return retval;
-    }
-    return 0;
-}
 
-NodeImpl* HTMLFormCollectionImpl::getNamedImgItem(NodeImpl* current, int attr_id, const DOMString& name, int& duplicateNumber, bool caseSensitive) const
-{
-    // strange case. IE and NS allow to get hold of <img> tags,
-    // but they don't include them in the elements() collection.
-    while ( current )
-    {
-        if(current->nodeType() == Node::ELEMENT_NODE)
+        if ( !foundInputElements )
         {
-            HTMLElementImpl *e = static_cast<HTMLElementImpl *>(current);
-            if(e->id() == ID_IMG) {
+            HTMLFormElementImpl* f = static_cast<HTMLFormElementImpl*>(baseElement);
+
+            for(unsigned i = 0; i < f->imgElements.count(); ++i)
+            {
+                HTMLImageElementImpl* e = f->imgElements[i];
                 bool found;
                 if (caseSensitive)
                     found = e->getAttribute(attr_id) == name;
                 else
                     found = e->getAttribute(attr_id).domString().lower() == name.lower();
-                if (found)
-                {
+                if (found) {
                     if (!duplicateNumber)
-                        return current;
+                        return e;
                     --duplicateNumber;
                 }
             }
-            if(current->firstChild())
-            {
-                // The recursion here is the reason why this is a separate method
-                NodeImpl *retval = getNamedImgItem(current->firstChild(), attr_id, name, duplicateNumber, caseSensitive);
-                if(retval)
-                {
-                    //kdDebug( 6030 ) << "got a return value " << retval << endl;
-                    return retval;
-                }
-            }
         }
-        current = current->nextSibling();
-    } // while
+    }
     return 0;
 }
 
 NodeImpl * HTMLFormCollectionImpl::firstItem() const
 {
-    currentPos = 0;
-    int dummy = 0;
-    return getItem(0 /*base->firstChild() unused*/, currentPos, dummy);
+    return item(0);
 }
 
 NodeImpl * HTMLFormCollectionImpl::nextItem() const
 {
-    // This implementation loses the whole benefit of firstItem/nextItem :(
-    int dummy = 0;
-    return getItem(0 /*base->firstChild() unused*/, ++currentPos, dummy);
+    return item(info->position + 1);
 }
 
 NodeImpl * HTMLFormCollectionImpl::nextNamedItemInternal( const DOMString &name ) const
 {
-    NodeImpl *retval = getNamedFormItem( idsDone ? ATTR_NAME : ATTR_ID, name, ++currentPos, true );
+    NodeImpl *retval = getNamedFormItem( idsDone ? ATTR_NAME : ATTR_ID, name, ++info->position, true );
     if ( retval )
         return retval;
     if ( idsDone ) // we're done
@@ -583,3 +480,102 @@ NodeImpl * HTMLFormCollectionImpl::nextNamedItemInternal( const DOMString &name
     idsDone = true;
     return getNamedItem(base->firstChild(), ATTR_NAME, name, true);
 }
+
+NodeImpl *HTMLFormCollectionImpl::namedItem( const DOMString &name, bool caseSensitive ) const
+{
+    // 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
+    // object with a matching name attribute, but only on those elements
+    // that are allowed a name attribute.
+    resetCollectionInfo();
+    idsDone = false;
+    info->current = getNamedItem(base->firstChild(), ATTR_ID, name, true);
+    if(info->current)
+        return info->current;
+    idsDone = true;
+    info->current = getNamedItem(base->firstChild(), ATTR_NAME, name, true);
+    return info->current;
+}
+
+
+NodeImpl *HTMLFormCollectionImpl::nextNamedItem( const DOMString &name ) const
+{
+    // nextNamedItemInternal can return an item that has both id=<name> and name=<name>
+    // Here, we have to filter out such cases.
+    NodeImpl *impl = nextNamedItemInternal( name );
+    if (!idsDone) // looking for id=<name> -> no filtering
+        return impl;
+    // looking for name=<name> -> filter out if id=<name>
+    bool ok = false;
+    while (impl && !ok)
+    {
+        if(impl->nodeType() == Node::ELEMENT_NODE)
+        {
+            HTMLElementImpl *e = static_cast<HTMLElementImpl *>(impl);
+            ok = (e->getAttribute(ATTR_ID) != name);
+            if (!ok)
+                impl = nextNamedItemInternal( name );
+        } else // can't happen
+            ok = true;
+    }
+    return impl;
+}
+
+QValueList<Node> HTMLFormCollectionImpl::namedItems(const DOMString &name) const
+{
+    if (name.length()) {
+        return QValueList<Node>();
+    }
+
+    resetCollectionInfo();
+
+    QValueList<Node> idResults;
+    QValueList<Node> nameResults;
+
+    if (base->nodeType() != Node::ELEMENT_NODE ||static_cast<HTMLElementImpl*>(base)->id() != ID_FORM)
+        return idResults;
+
+    HTMLElementImpl* baseElement = static_cast<HTMLElementImpl*>(base);
+
+    bool foundInputElements = false;
+    HTMLFormElementImpl* f = static_cast<HTMLFormElementImpl*>(baseElement);
+    for (unsigned i = 0; i < f->formElements.count(); ++i) {
+        HTMLGenericFormElementImpl* e = f->formElements[i];
+        if (e->isEnumeratable()) {
+            if (checkForNameMatch(e, false, name, true)) {
+                idResults.append(Node(e));
+                foundInputElements = true;
+            } 
+            if (checkForNameMatch(e, true, name, true)) {
+                nameResults.append(Node(e));
+                foundInputElements = true;
+            }
+        }
+    }
+
+    if (!foundInputElements) {
+        HTMLFormElementImpl* f = static_cast<HTMLFormElementImpl*>(baseElement);
+
+        for (unsigned i = 0; i < f->imgElements.count(); ++i) {
+            HTMLImageElementImpl* e = f->imgElements[i];
+
+            if (checkForNameMatch(e, false, name, true)) {
+                idResults.append(Node(e));
+            } 
+            if (checkForNameMatch(e, true, name, true)) {
+                nameResults.append(Node(e));
+            }
+        }
+    }
+
+    if (idResults.isEmpty())
+        return nameResults;
+
+    for (QValueListIterator<Node> iter = nameResults.begin(); iter != nameResults.end(); ++iter) {
+        idResults.append(*iter);
+    }
+
+    return idResults;
+}
+