Reviewed by Dave.
authormjs <mjs@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 23 Nov 2004 07:45:14 +0000 (07:45 +0000)
committermjs <mjs@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 23 Nov 2004 07:45:14 +0000 (07:45 +0000)
<rdar://problem/3492044> performing JavaScript operations on form elements is slower than WinIE (HTMLFormCollection)
<rdar://problem/3489679> selecting an item on the Apache bugzilla query page is very slow (HTMLFormCollection)
<rdar://problem/3477810> checking 80 check boxes with JavaScript is 10x slower than in IE (HTMLFormCollection)
<rdar://problem/3760962> JavaScript that toggles checkboxes is slow (HTMLCollection,HTMLFormCollection)

* khtml/ecma/kjs_html.cpp:
        (KJS::HTMLDocument::tryGet):
        * khtml/html/html_formimpl.cpp:
        (DOM::HTMLFormElementImpl::HTMLFormElementImpl):
        (DOM::HTMLFormElementImpl::~HTMLFormElementImpl):
        * khtml/html/html_formimpl.h:
        * khtml/html/html_miscimpl.cpp:
        (HTMLCollectionImpl::HTMLCollectionImpl):
        (HTMLCollectionImpl::~HTMLCollectionImpl):
        (HTMLCollectionImpl::CollectionInfo::CollectionInfo):
        (HTMLCollectionImpl::CollectionInfo::reset):
        (HTMLCollectionImpl::resetCollectionInfo):
        (HTMLCollectionImpl::checkForNameMatch):
        (appendToVector):
        (HTMLCollectionImpl::updateNameCache):
        (HTMLCollectionImpl::namedItems):
        (HTMLFormCollectionImpl::HTMLFormCollectionImpl):
        (HTMLFormCollectionImpl::~HTMLFormCollectionImpl):
        (HTMLFormCollectionImpl::item):
        (HTMLFormCollectionImpl::updateNameCache):
        * khtml/html/html_miscimpl.h:

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@8074 268f45cc-cd09-0410-ab3c-d52691b4dbfc

WebCore/ChangeLog-2005-08-23
WebCore/khtml/ecma/kjs_html.cpp
WebCore/khtml/html/html_formimpl.cpp
WebCore/khtml/html/html_formimpl.h
WebCore/khtml/html/html_miscimpl.cpp
WebCore/khtml/html/html_miscimpl.h

index 4867c05..3caef00 100644 (file)
@@ -1,3 +1,34 @@
+2004-11-22  Maciej Stachowiak  <mjs@apple.com>
+
+        Reviewed by Dave.
+
+       <rdar://problem/3492044> performing JavaScript operations on form elements is slower than WinIE (HTMLFormCollection)
+       <rdar://problem/3489679> selecting an item on the Apache bugzilla query page is very slow (HTMLFormCollection)
+       <rdar://problem/3477810> checking 80 check boxes with JavaScript is 10x slower than in IE (HTMLFormCollection)
+       <rdar://problem/3760962> JavaScript that toggles checkboxes is slow (HTMLCollection,HTMLFormCollection)
+        
+       * khtml/ecma/kjs_html.cpp:
+        (KJS::HTMLDocument::tryGet):
+        * khtml/html/html_formimpl.cpp:
+        (DOM::HTMLFormElementImpl::HTMLFormElementImpl):
+        (DOM::HTMLFormElementImpl::~HTMLFormElementImpl):
+        * khtml/html/html_formimpl.h:
+        * khtml/html/html_miscimpl.cpp:
+        (HTMLCollectionImpl::HTMLCollectionImpl):
+        (HTMLCollectionImpl::~HTMLCollectionImpl):
+        (HTMLCollectionImpl::CollectionInfo::CollectionInfo):
+        (HTMLCollectionImpl::CollectionInfo::reset):
+        (HTMLCollectionImpl::resetCollectionInfo):
+        (HTMLCollectionImpl::checkForNameMatch):
+        (appendToVector):
+        (HTMLCollectionImpl::updateNameCache):
+        (HTMLCollectionImpl::namedItems):
+        (HTMLFormCollectionImpl::HTMLFormCollectionImpl):
+        (HTMLFormCollectionImpl::~HTMLFormCollectionImpl):
+        (HTMLFormCollectionImpl::item):
+        (HTMLFormCollectionImpl::updateNameCache):
+        * khtml/html/html_miscimpl.h:
+
 2004-11-22  David Hyatt  <hyatt@apple.com>
 
        Improve the WebCore cache so that the maximum cacheable object size is scaled based off the total cache
index 503ba54..3bc4179 100644 (file)
@@ -332,30 +332,9 @@ Value KJS::HTMLDocument::tryGet(ExecState *exec, const Identifier &propertyName)
     return Undefined();
   }
 
-  DOM::HTMLCollection coll = doc.images();
-  DOM::HTMLCollection coll2 = doc.forms();
-  DOM::HTMLElement element = coll.namedItem(propertyName.string());
-  DOM::HTMLElement element2;
-  if (element.isNull()) {
-    element = coll2.namedItem(propertyName.string());
-    element2 = coll2.nextNamedItem(propertyName.string());
-  }
-  else {
-    element2 = coll.nextNamedItem(propertyName.string());
-    if (element2.isNull())
-        element2 = coll2.namedItem(propertyName.string());
-  }
-  
-  if (!element.isNull() && (element.elementId() == ID_IMG || element.elementId() == ID_FORM))
-  {
-    if (element2.isNull())
-        return getDOMNode(exec, element);
-    else {
-        DOM::HTMLCollection collAll = doc.all();
-        KJS::HTMLCollection htmlcoll(exec,collAll);
-        return htmlcoll.getNamedItems(exec, propertyName); // Get all the items with the same name
-    }
-  }
+  DOM::HTMLCollection collAll = doc.all();
+  KJS::HTMLCollection htmlcoll(exec,collAll);
+  return htmlcoll.getNamedItems(exec, propertyName); // Get all the items with the same name
 
   return Undefined();
 }
index e0227d0..61c0202 100644 (file)
@@ -95,6 +95,7 @@ private:
 HTMLFormElementImpl::HTMLFormElementImpl(DocumentPtr *doc)
     : HTMLElementImpl(doc)
 {
+    collectionInfo = 0;
     m_post = false;
     m_multipart = false;
     m_autocomplete = true;
@@ -109,6 +110,8 @@ HTMLFormElementImpl::HTMLFormElementImpl(DocumentPtr *doc)
 
 HTMLFormElementImpl::~HTMLFormElementImpl()
 {
+    delete collectionInfo;
+    
     for (unsigned i = 0; i < formElements.count(); ++i)
         formElements[i]->m_form = 0;
     for (unsigned i = 0; i < dormantFormElements.count(); ++i)
index 5a727c5..b189c8d 100644 (file)
@@ -27,6 +27,7 @@
 
 #include "html/html_elementimpl.h"
 #include "dom/html_element.h"
+#include "html/html_miscimpl.h"
 
 #include <qptrvector.h>
 #include <qmemarray.h>
@@ -111,6 +112,8 @@ public:
     friend class HTMLFormElement;
     friend class HTMLFormCollectionImpl;
 
+    HTMLCollectionImpl::CollectionInfo *collectionInfo;
+
     QPtrVector<HTMLGenericFormElementImpl> formElements;
     QPtrVector<HTMLGenericFormElementImpl> dormantFormElements;
     QPtrVector<HTMLImageElementImpl> imgElements;
index a628d38..917910e 100644 (file)
@@ -56,24 +56,46 @@ HTMLCollectionImpl::HTMLCollectionImpl(NodeImpl *_base, int _type)
     base->ref();
     type = _type;
     idsDone = false;
-    info = base->isDocumentNode() ? static_cast<HTMLDocumentImpl*>(base->getDocument())->collectionInfo(type) : new CollectionInfo;
+    info = base->isDocumentNode() ? static_cast<HTMLDocumentImpl*>(base->getDocument())->collectionInfo(type) : 0;
 }
 
 HTMLCollectionImpl::~HTMLCollectionImpl()
 {
-    if (!base->isDocumentNode())
-        delete info;
     base->deref();
 }
 
+HTMLCollectionImpl::CollectionInfo::CollectionInfo() :
+    version(0)
+{
+    idCache.setAutoDelete(true);
+    nameCache.setAutoDelete(true);
+    reset();
+}
+
+void HTMLCollectionImpl::CollectionInfo::reset()
+{
+    current = 0;
+    position = 0;
+    length = 0;
+    haslength = false;
+    elementsArrayPosition = 0;
+    idCache.clear();
+    nameCache.clear();
+    hasNameCache = false;
+}
+
 void HTMLCollectionImpl::resetCollectionInfo() const
 {
     unsigned int docversion = static_cast<HTMLDocumentImpl*>(base->getDocument())->domTreeVersion();
+
+    if (!info) {
+        info = new CollectionInfo;
+        info->version = docversion;
+        return;
+    }
+
     if (info->version != docversion) {
-        info->current = 0;
-        info->position = 0;
-        info->length = 0;
-        info->haslength = false;
+        info->reset();
         info->version = docversion;
     }
 }
@@ -234,12 +256,28 @@ bool HTMLCollectionImpl::checkForNameMatch(NodeImpl *node, bool checkName, const
     ElementImpl *e = static_cast<ElementImpl *>(node);
     if (caseSensitive) {
         if (checkName) {
+            // document.all returns only images, forms, applets, objects and embeds
+            // by name (though everything by id)
+            if (type == DOC_ALL && 
+                !(e->id() == ID_IMG || e->id() == ID_FORM ||
+                  e->id() == ID_APPLET || e->id() == ID_OBJECT ||
+                  e->id() == ID_EMBED))
+                return false;
+
             return e->getAttribute(ATTR_NAME) == name && e->getAttribute(ATTR_ID) != name;
         } else {
             return e->getAttribute(ATTR_ID) == name;
         }
     } else {
         if (checkName) {
+            // document.all returns only images, forms, applets, objects and embeds
+            // by name (though everything by id)
+            if (type == DOC_ALL && 
+                !(e->id() == ID_IMG || e->id() == ID_FORM ||
+                  e->id() == ID_APPLET || e->id() == ID_OBJECT ||
+                  e->id() == ID_EMBED))
+                return false;
+
             return e->getAttribute(ATTR_NAME).domString().lower() == name.lower() &&
                 e->getAttribute(ATTR_ID).domString().lower() != name.lower();
         } else {
@@ -281,30 +319,73 @@ NodeImpl *HTMLCollectionImpl::namedItem( const DOMString &name, bool caseSensiti
     return info->current;
 }
 
-QValueList<Node> HTMLCollectionImpl::namedItems(const DOMString &name) const
+template<class T> static void appendToVector(QPtrVector<T> *vec, T *item)
 {
-    resetCollectionInfo();
-    
-    QValueList<Node> idResults;
-    QValueList<Node> nameResults;
+    unsigned size = vec->size();
+    unsigned count = vec->count();
+    if (size == count)
+        vec->resize(size == 0 ? 8 : (int)(size * 1.5));
+    vec->insert(count, item);
+}
 
+void HTMLCollectionImpl::updateNameCache() const
+{
+    if (info->hasNameCache)
+        return;
+    
     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));
+        ElementImpl *e = static_cast<ElementImpl *>(n);
+        QString idAttr = e->getAttribute(ATTR_ID).string();
+        QString nameAttr = e->getAttribute(ATTR_NAME).string();
+        if (!idAttr.isEmpty()) {
+            // add to id cache
+            QPtrVector<NodeImpl> *idVector = info->idCache.find(idAttr);
+            if (!idVector) {
+                idVector = new QPtrVector<NodeImpl>;
+                info->idCache.insert(idAttr, idVector);
+            }
+            appendToVector(idVector, n);
+        }
+        if (!nameAttr.isEmpty() && idAttr != nameAttr
+            && (type != DOC_ALL || 
+                (e->id() == ID_IMG || e->id() == ID_FORM ||
+                 e->id() == ID_APPLET || e->id() == ID_OBJECT ||
+                 e->id() == ID_EMBED))) {
+            // add to name cache
+            QPtrVector<NodeImpl> *nameVector = info->idCache.find(nameAttr);
+            if (!nameVector) {
+                nameVector = new QPtrVector<NodeImpl>;
+                info->nameCache.insert(nameAttr, nameVector);
+            }
+            appendToVector(nameVector, n);
         }
     }
 
-    if (idResults.isEmpty())
-        return nameResults;
+    info->hasNameCache = true;
+}
 
-    for (QValueListIterator<Node> iter = nameResults.begin(); iter != nameResults.end(); ++iter) {
-        idResults.append(*iter);
+QValueList<Node> HTMLCollectionImpl::namedItems(const DOMString &name) const
+{
+    QValueList<Node> result;
+
+    if (name.isEmpty())
+        return result;
+
+    resetCollectionInfo();
+    updateNameCache();
+    
+    QPtrVector<NodeImpl> *idResults = info->idCache.find(name.string());
+    QPtrVector<NodeImpl> *nameResults = info->nameCache.find(name.string());
+    
+    for (unsigned i = 0; idResults && i < idResults->count(); ++i) {
+        result.append(idResults->at(i));
+    }
+
+    for (unsigned i = 0; nameResults && i < nameResults->count(); ++i) {
+        result.append(nameResults->at(i));
     }
 
-    return idResults;
+    return result;
 }
 
 
@@ -337,23 +418,18 @@ NodeImpl *HTMLCollectionImpl::nextNamedItem( const DOMString &name ) const
 
 // -----------------------------------------------------------------------------
 
-HTMLFormCollectionImpl::FormCollectionInfo::FormCollectionInfo()
-{
-    reset();
-}
-
-void::HTMLFormCollectionImpl::FormCollectionInfo::reset()
+HTMLFormCollectionImpl::HTMLFormCollectionImpl(NodeImpl* _base)
+    : HTMLCollectionImpl(_base, 0)
 {
-    elementsArrayPosition = 0;
+    HTMLFormElementImpl *formBase = static_cast<HTMLFormElementImpl*>(base);
+    if (!formBase->collectionInfo) {
+        formBase->collectionInfo = new CollectionInfo();
+    }
+    info = formBase->collectionInfo;
 }
 
-void HTMLFormCollectionImpl::resetCollectionInfo() const
+HTMLFormCollectionImpl::~HTMLFormCollectionImpl()
 {
-    unsigned int docversion = static_cast<HTMLDocumentImpl*>(base->getDocument())->domTreeVersion();
-    if (info->version != docversion) {
-        formInfo.reset();
-    }
-    HTMLCollectionImpl::resetCollectionInfo();
 }
 
 unsigned long HTMLFormCollectionImpl::calcLength() const
@@ -381,18 +457,18 @@ NodeImpl *HTMLFormCollectionImpl::item(unsigned long index) const
     if (!info->current || info->position > index) {
         info->current = 0;
         info->position = 0;
-        formInfo.elementsArrayPosition = 0;
+        info->elementsArrayPosition = 0;
     }
 
     QPtrVector<HTMLGenericFormElementImpl> &l = static_cast<HTMLFormElementImpl*>( base )->formElements;
     unsigned currentIndex = info->position;
     
-    for (unsigned i = formInfo.elementsArrayPosition; i < l.count(); i++) {
+    for (unsigned i = info->elementsArrayPosition; i < l.count(); i++) {
         if (l[i]->isEnumeratable() ) {
             if (index == currentIndex) {
                 info->position = index;
                 info->current = l[i];
-                formInfo.elementsArrayPosition = i;
+                info->elementsArrayPosition = i;
                 return l[i];
             }
 
@@ -522,60 +598,72 @@ NodeImpl *HTMLFormCollectionImpl::nextNamedItem( const DOMString &name ) const
     return impl;
 }
 
-QValueList<Node> HTMLFormCollectionImpl::namedItems(const DOMString &name) const
+void HTMLFormCollectionImpl::updateNameCache() const
 {
-    if (name.length()) {
-        return QValueList<Node>();
-    }
-
-    resetCollectionInfo();
+    if (info->hasNameCache)
+        return;
 
-    QValueList<Node> idResults;
-    QValueList<Node> nameResults;
+    QDict<char> foundInputElements;
 
-    if (base->nodeType() != Node::ELEMENT_NODE ||static_cast<HTMLElementImpl*>(base)->id() != ID_FORM)
-        return idResults;
+    if (base->nodeType() != Node::ELEMENT_NODE ||static_cast<HTMLElementImpl*>(base)->id() != ID_FORM) {
+        info->hasNameCache = true;
+        return;
+    }
 
     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;
+            QString idAttr = e->getAttribute(ATTR_ID).string();
+            QString nameAttr = e->getAttribute(ATTR_NAME).string();
+            if (!idAttr.isEmpty()) {
+                // add to id cache
+                QPtrVector<NodeImpl> *idVector = info->idCache.find(idAttr);
+                if (!idVector) {
+                    idVector = new QPtrVector<NodeImpl>;
+                    info->idCache.insert(idAttr, idVector);
+                }
+                appendToVector(idVector, static_cast<NodeImpl *>(e));
+                foundInputElements.insert(idAttr, (char *)true);
+            }
+            if (!nameAttr.isEmpty() && idAttr != nameAttr) {
+                // add to name cache
+                QPtrVector<NodeImpl> *nameVector = info->idCache.find(nameAttr);
+                if (!nameVector) {
+                    nameVector = new QPtrVector<NodeImpl>;
+                    info->nameCache.insert(nameAttr, nameVector);
+                }
+                appendToVector(nameVector, static_cast<NodeImpl *>(e));
+                foundInputElements.insert(nameAttr, (char *)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));
+    for (unsigned i = 0; i < f->imgElements.count(); ++i) {
+        HTMLImageElementImpl* e = f->imgElements[i];
+        QString idAttr = e->getAttribute(ATTR_ID).string();
+        QString nameAttr = e->getAttribute(ATTR_NAME).string();
+        if (!idAttr.isEmpty() && !foundInputElements.find(idAttr)) {
+            // add to id cache
+            QPtrVector<NodeImpl> *idVector = info->idCache.find(idAttr);
+            if (!idVector) {
+                idVector = new QPtrVector<NodeImpl>;
+                info->idCache.insert(idAttr, idVector);
             }
+            appendToVector(idVector, static_cast<NodeImpl *>(e));
+        }
+        if (!nameAttr.isEmpty() && idAttr != nameAttr && !foundInputElements.find(nameAttr)) {
+            // add to name cache
+            QPtrVector<NodeImpl> *nameVector = info->idCache.find(nameAttr);
+            if (!nameVector) {
+                nameVector = new QPtrVector<NodeImpl>;
+                info->nameCache.insert(nameAttr, nameVector);
+            }
+            appendToVector(nameVector, static_cast<NodeImpl *>(e));
         }
     }
 
-    if (idResults.isEmpty())
-        return nameResults;
-
-    for (QValueListIterator<Node> iter = nameResults.begin(); iter != nameResults.end(); ++iter) {
-        idResults.append(*iter);
-    }
-
-    return idResults;
+    info->hasNameCache = true;
 }
-
index 5cec109..5d58598 100644 (file)
@@ -25,6 +25,8 @@
 
 #include "html_elementimpl.h"
 #include "misc/shared.h"
+#include <qdict.h>
+#include <qptrvector.h>
 
 namespace DOM {
 
@@ -83,18 +85,25 @@ public:
     // In case of multiple items named the same way
     NodeImpl *nextNamedItem( const DOMString &name ) const;
 
-    virtual QValueList<Node> namedItems( const DOMString &name ) const;
+    QValueList<Node> namedItems( const DOMString &name ) const;
 
     struct CollectionInfo {
-        CollectionInfo() : version(0), current(0), position(0), length(0), haslength(false) {}
+        CollectionInfo();
+        void reset();
         unsigned int version;
         NodeImpl *current;
         unsigned int position;
         unsigned int length;
         bool haslength;
+        int elementsArrayPosition;
+        QDict<QPtrVector<NodeImpl> > idCache;
+        QDict<QPtrVector<NodeImpl> > nameCache;
+        bool hasNameCache;
      };
 
 protected:
+    virtual void updateNameCache() const;
+
     virtual NodeImpl *traverseNextItem(NodeImpl *start) const;
     bool checkForNameMatch(NodeImpl *node, bool checkName, const DOMString &name, bool caseSensitive) const;
     virtual unsigned long calcLength() const;
@@ -116,16 +125,8 @@ class HTMLFormCollectionImpl : public HTMLCollectionImpl
 {
 public:
     // base must inherit HTMLGenericFormElementImpl or this won't work
-    HTMLFormCollectionImpl(NodeImpl* _base)
-        : HTMLCollectionImpl(_base, 0)
-    {};
-    ~HTMLFormCollectionImpl() {}
-
-    struct FormCollectionInfo {
-        FormCollectionInfo(); 
-        void reset();
-        int elementsArrayPosition;
-     };
+    HTMLFormCollectionImpl(NodeImpl* _base);
+    ~HTMLFormCollectionImpl();
 
     virtual NodeImpl *item ( unsigned long index ) const;
     virtual NodeImpl *firstItem() const;
@@ -134,15 +135,13 @@ public:
     NodeImpl *namedItem ( const DOMString &name, bool caseSensitive = true ) const;
     NodeImpl *nextNamedItem( const DOMString &name ) const;
 
-    virtual QValueList<Node> namedItems( const DOMString &name ) const;
 protected:
+    virtual void updateNameCache() const;
     virtual unsigned long calcLength() const;
     virtual NodeImpl *getNamedItem(NodeImpl* current, int attr_id, const DOMString& name, bool caseSensitive) const;
     virtual NodeImpl *nextNamedItemInternal( const DOMString &name ) const;
 private:
-    virtual void resetCollectionInfo() const;
     NodeImpl* getNamedFormItem(int attr_id, const DOMString& name, int duplicateNumber, bool caseSensitive) const;
-    mutable FormCollectionInfo formInfo;
     mutable int currentPos;
 };