2 * This file is part of the DOM implementation for KDE.
4 * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
5 * (C) 1999 Antti Koivisto (koivisto@kde.org)
6 * Copyright (C) 2003 Apple Computer, Inc.
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Library General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Library General Public License for more details.
18 * You should have received a copy of the GNU Library General Public License
19 * along with this library; see the file COPYING.LIB. If not, write to
20 * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21 * Boston, MA 02111-1307, USA.
24 // -------------------------------------------------------------------------
25 #include "html/html_miscimpl.h"
26 #include "html/html_formimpl.h"
27 #include "html/html_imageimpl.h"
28 #include "html/html_documentimpl.h"
30 #include "misc/htmlhashes.h"
31 #include "dom/dom_node.h"
37 HTMLBaseFontElementImpl::HTMLBaseFontElementImpl(DocumentPtr *doc)
38 : HTMLElementImpl(doc)
42 HTMLBaseFontElementImpl::~HTMLBaseFontElementImpl()
46 NodeImpl::Id HTMLBaseFontElementImpl::id() const
51 // -------------------------------------------------------------------------
53 HTMLCollectionImpl::HTMLCollectionImpl(NodeImpl *_base, int _type)
59 info = base->isDocumentNode() ? static_cast<HTMLDocumentImpl*>(base->getDocument())->collectionInfo(type) : 0;
62 HTMLCollectionImpl::~HTMLCollectionImpl()
67 HTMLCollectionImpl::CollectionInfo::CollectionInfo() :
70 idCache.setAutoDelete(true);
71 nameCache.setAutoDelete(true);
75 void HTMLCollectionImpl::CollectionInfo::reset()
81 elementsArrayPosition = 0;
87 void HTMLCollectionImpl::resetCollectionInfo() const
89 unsigned int docversion = static_cast<HTMLDocumentImpl*>(base->getDocument())->domTreeVersion();
92 info = new CollectionInfo;
93 info->version = docversion;
97 if (info->version != docversion) {
99 info->version = docversion;
104 NodeImpl *HTMLCollectionImpl::traverseNextItem(NodeImpl *current) const
106 current = current->traverseNextNode();
109 if(current->nodeType() == Node::ELEMENT_NODE) {
112 HTMLElementImpl *e = static_cast<HTMLElementImpl *>(current);
115 if(e->id() == ID_IMG)
119 if(e->id() == ID_FORM)
122 case DOC_NAMEABLE_ITEMS:
123 if(e->id() == ID_IMG)
125 if(e->id() == ID_FORM)
127 if(e->id() == ID_APPLET)
129 if(e->id() == ID_EMBED)
131 if(e->id() == ID_OBJECT)
135 if(e->id() == ID_TBODY)
137 else if(e->id() == ID_TABLE)
141 if(e->id() == ID_TD || e->id() == ID_TH)
143 else if(e->id() == ID_TABLE)
150 else if(e->id() == ID_TABLE)
154 if(e->id() == ID_OPTION)
158 if(e->id() == ID_AREA)
161 case DOC_APPLETS: // all OBJECT and APPLET elements
162 if(e->id() == ID_OBJECT || e->id() == ID_APPLET)
165 case DOC_EMBEDS: // all EMBED elements
166 if(e->id() == ID_EMBED)
169 case DOC_LINKS: // all A _and_ AREA elements with a value for href
170 if(e->id() == ID_A || e->id() == ID_AREA)
171 if(!e->getAttribute(ATTR_HREF).isNull())
174 case DOC_ANCHORS: // all A elements with a value for name or an id attribute
176 if(!e->getAttribute(ATTR_NAME).isNull())
187 kdDebug( 6030 ) << "Error in HTMLCollection, wrong tagId!" << endl;
193 current = current->traverseNextNode(base);
197 current = current->traverseNextSibling(base);
203 unsigned long HTMLCollectionImpl::calcLength() const
205 unsigned long len = 0;
207 for (NodeImpl *current = traverseNextItem(base); current; current = traverseNextItem(current)) {
214 // since the collections are to be "live", we have to do the
215 // calculation every time if anything has changed
216 unsigned long HTMLCollectionImpl::length() const
218 resetCollectionInfo();
219 if (!info->haslength) {
220 info->length = calcLength();
221 info->haslength = true;
226 NodeImpl *HTMLCollectionImpl::item( unsigned long index ) const
228 resetCollectionInfo();
229 if (info->current && info->position == index) {
230 return info->current;
232 if (info->haslength && info->length <= index) {
235 if (!info->current || info->position > index) {
236 info->current = traverseNextItem(base);
241 NodeImpl *node = info->current;
242 for (unsigned pos = info->position; pos < index; pos++) {
243 node = traverseNextItem(node);
245 info->current = node;
246 info->position = index;
247 return info->current;
250 NodeImpl *HTMLCollectionImpl::firstItem() const
255 NodeImpl *HTMLCollectionImpl::nextItem() const
257 resetCollectionInfo();
259 // Look for the 'second' item. The first one is currentItem, already given back.
260 NodeImpl *retval = traverseNextItem(info->current);
261 info->current = retval;
266 bool HTMLCollectionImpl::checkForNameMatch(NodeImpl *node, bool checkName, const DOMString &name, bool caseSensitive) const
268 ElementImpl *e = static_cast<ElementImpl *>(node);
271 // document.all returns only images, forms, applets, objects and embeds
272 // by name (though everything by id)
273 if (type == DOC_ALL &&
274 !(e->id() == ID_IMG || e->id() == ID_FORM ||
275 e->id() == ID_APPLET || e->id() == ID_OBJECT ||
276 e->id() == ID_EMBED))
279 return e->getAttribute(ATTR_NAME) == name && e->getAttribute(ATTR_ID) != name;
281 return e->getAttribute(ATTR_ID) == name;
285 // document.all returns only images, forms, applets, objects and embeds
286 // by name (though everything by id)
287 if (type == DOC_ALL &&
288 !(e->id() == ID_IMG || e->id() == ID_FORM ||
289 e->id() == ID_APPLET || e->id() == ID_OBJECT ||
290 e->id() == ID_EMBED))
293 return e->getAttribute(ATTR_NAME).domString().lower() == name.lower() &&
294 e->getAttribute(ATTR_ID).domString().lower() != name.lower();
296 return e->getAttribute(ATTR_ID).domString().lower() == name.lower();
302 NodeImpl *HTMLCollectionImpl::namedItem( const DOMString &name, bool caseSensitive ) const
304 // http://msdn.microsoft.com/workshop/author/dhtml/reference/methods/nameditem.asp
305 // This method first searches for an object with a matching id
306 // attribute. If a match is not found, the method then searches for an
307 // object with a matching name attribute, but only on those elements
308 // that are allowed a name attribute.
309 resetCollectionInfo();
313 for (n = traverseNextItem(base); n; n = traverseNextItem(n)) {
314 if (checkForNameMatch(n, idsDone, name, caseSensitive)) {
321 return info->current;
324 for (n = traverseNextItem(base); n; n = traverseNextItem(n)) {
325 if (checkForNameMatch(n, idsDone, name, caseSensitive)) {
331 return info->current;
334 template<class T> static void appendToVector(QPtrVector<T> *vec, T *item)
336 unsigned size = vec->size();
337 unsigned count = vec->count();
339 vec->resize(size == 0 ? 8 : (int)(size * 1.5));
340 vec->insert(count, item);
343 void HTMLCollectionImpl::updateNameCache() const
345 if (info->hasNameCache)
348 for (NodeImpl *n = traverseNextItem(base); n; n = traverseNextItem(n)) {
349 ElementImpl *e = static_cast<ElementImpl *>(n);
350 QString idAttr = e->getAttribute(ATTR_ID).string();
351 QString nameAttr = e->getAttribute(ATTR_NAME).string();
352 if (!idAttr.isEmpty()) {
354 QPtrVector<NodeImpl> *idVector = info->idCache.find(idAttr);
356 idVector = new QPtrVector<NodeImpl>;
357 info->idCache.insert(idAttr, idVector);
359 appendToVector(idVector, n);
361 if (!nameAttr.isEmpty() && idAttr != nameAttr
362 && (type != DOC_ALL ||
363 (e->id() == ID_IMG || e->id() == ID_FORM ||
364 e->id() == ID_APPLET || e->id() == ID_OBJECT ||
365 e->id() == ID_EMBED))) {
367 QPtrVector<NodeImpl> *nameVector = info->nameCache.find(nameAttr);
369 nameVector = new QPtrVector<NodeImpl>;
370 info->nameCache.insert(nameAttr, nameVector);
372 appendToVector(nameVector, n);
376 info->hasNameCache = true;
379 QValueList<Node> HTMLCollectionImpl::namedItems(const DOMString &name) const
381 QValueList<Node> result;
386 resetCollectionInfo();
389 QPtrVector<NodeImpl> *idResults = info->idCache.find(name.string());
390 QPtrVector<NodeImpl> *nameResults = info->nameCache.find(name.string());
392 for (unsigned i = 0; idResults && i < idResults->count(); ++i) {
393 result.append(idResults->at(i));
396 for (unsigned i = 0; nameResults && i < nameResults->count(); ++i) {
397 result.append(nameResults->at(i));
404 NodeImpl *HTMLCollectionImpl::nextNamedItem( const DOMString &name ) const
406 resetCollectionInfo();
408 for (NodeImpl *n = traverseNextItem(info->current ? info->current : base); n; n = traverseNextItem(n)) {
409 if (checkForNameMatch(n, idsDone, name, true)) {
421 for (NodeImpl *n = traverseNextItem(info->current ? info->current : base); n; n = traverseNextItem(n)) {
422 if (checkForNameMatch(n, idsDone, name, true)) {
431 // -----------------------------------------------------------------------------
433 HTMLFormCollectionImpl::HTMLFormCollectionImpl(NodeImpl* _base)
434 : HTMLCollectionImpl(_base, 0)
436 HTMLFormElementImpl *formBase = static_cast<HTMLFormElementImpl*>(base);
437 if (!formBase->collectionInfo) {
438 formBase->collectionInfo = new CollectionInfo();
440 info = formBase->collectionInfo;
443 HTMLFormCollectionImpl::~HTMLFormCollectionImpl()
447 unsigned long HTMLFormCollectionImpl::calcLength() const
449 QPtrVector<HTMLGenericFormElementImpl> &l = static_cast<HTMLFormElementImpl*>( base )->formElements;
452 for ( unsigned i = 0; i < l.count(); i++ )
453 if ( l.at( i )->isEnumeratable() )
459 NodeImpl *HTMLFormCollectionImpl::item(unsigned long index) const
461 resetCollectionInfo();
463 if (info->current && info->position == index) {
464 return info->current;
466 if (info->haslength && info->length <= index) {
469 if (!info->current || info->position > index) {
472 info->elementsArrayPosition = 0;
475 QPtrVector<HTMLGenericFormElementImpl> &l = static_cast<HTMLFormElementImpl*>( base )->formElements;
476 unsigned currentIndex = info->position;
478 for (unsigned i = info->elementsArrayPosition; i < l.count(); i++) {
479 if (l[i]->isEnumeratable() ) {
480 if (index == currentIndex) {
481 info->position = index;
482 info->current = l[i];
483 info->elementsArrayPosition = i;
494 NodeImpl* HTMLFormCollectionImpl::getNamedItem(NodeImpl*, int attr_id, const DOMString& name, bool caseSensitive) const
497 return getNamedFormItem( attr_id, name, 0, caseSensitive );
500 NodeImpl* HTMLFormCollectionImpl::getNamedFormItem(int attr_id, const DOMString& name, int duplicateNumber, bool caseSensitive) const
502 if(base->nodeType() == Node::ELEMENT_NODE)
504 HTMLElementImpl* baseElement = static_cast<HTMLElementImpl*>(base);
505 bool foundInputElements = false;
506 if(baseElement->id() == ID_FORM)
508 HTMLFormElementImpl* f = static_cast<HTMLFormElementImpl*>(baseElement);
509 for (unsigned i = 0; i < f->formElements.count(); ++i) {
510 HTMLGenericFormElementImpl* e = f->formElements[i];
511 if (e->isEnumeratable()) {
514 found = e->getAttribute(attr_id) == name;
516 found = e->getAttribute(attr_id).domString().lower() == name.lower();
518 foundInputElements = true;
519 if (!duplicateNumber)
527 if ( !foundInputElements )
529 HTMLFormElementImpl* f = static_cast<HTMLFormElementImpl*>(baseElement);
531 for(unsigned i = 0; i < f->imgElements.count(); ++i)
533 HTMLImageElementImpl* e = f->imgElements[i];
536 found = e->getAttribute(attr_id) == name;
538 found = e->getAttribute(attr_id).domString().lower() == name.lower();
540 if (!duplicateNumber)
550 NodeImpl * HTMLFormCollectionImpl::firstItem() const
555 NodeImpl * HTMLFormCollectionImpl::nextItem() const
557 return item(info->position + 1);
560 NodeImpl * HTMLFormCollectionImpl::nextNamedItemInternal( const DOMString &name ) const
562 NodeImpl *retval = getNamedFormItem( idsDone ? ATTR_NAME : ATTR_ID, name, ++info->position, true );
565 if ( idsDone ) // we're done
567 // After doing all ATTR_ID, do ATTR_NAME
569 return getNamedItem(base->firstChild(), ATTR_NAME, name, true);
572 NodeImpl *HTMLFormCollectionImpl::namedItem( const DOMString &name, bool caseSensitive ) const
574 // http://msdn.microsoft.com/workshop/author/dhtml/reference/methods/nameditem.asp
575 // This method first searches for an object with a matching id
576 // attribute. If a match is not found, the method then searches for an
577 // object with a matching name attribute, but only on those elements
578 // that are allowed a name attribute.
579 resetCollectionInfo();
581 info->current = getNamedItem(base->firstChild(), ATTR_ID, name, true);
583 return info->current;
585 info->current = getNamedItem(base->firstChild(), ATTR_NAME, name, true);
586 return info->current;
590 NodeImpl *HTMLFormCollectionImpl::nextNamedItem( const DOMString &name ) const
592 // nextNamedItemInternal can return an item that has both id=<name> and name=<name>
593 // Here, we have to filter out such cases.
594 NodeImpl *impl = nextNamedItemInternal( name );
595 if (!idsDone) // looking for id=<name> -> no filtering
597 // looking for name=<name> -> filter out if id=<name>
601 if(impl->nodeType() == Node::ELEMENT_NODE)
603 HTMLElementImpl *e = static_cast<HTMLElementImpl *>(impl);
604 ok = (e->getAttribute(ATTR_ID) != name);
606 impl = nextNamedItemInternal( name );
607 } else // can't happen
613 void HTMLFormCollectionImpl::updateNameCache() const
615 if (info->hasNameCache)
618 QDict<char> foundInputElements;
620 if (base->nodeType() != Node::ELEMENT_NODE ||static_cast<HTMLElementImpl*>(base)->id() != ID_FORM) {
621 info->hasNameCache = true;
625 HTMLElementImpl* baseElement = static_cast<HTMLElementImpl*>(base);
627 HTMLFormElementImpl* f = static_cast<HTMLFormElementImpl*>(baseElement);
628 for (unsigned i = 0; i < f->formElements.count(); ++i) {
629 HTMLGenericFormElementImpl* e = f->formElements[i];
630 if (e->isEnumeratable()) {
631 QString idAttr = e->getAttribute(ATTR_ID).string();
632 QString nameAttr = e->getAttribute(ATTR_NAME).string();
633 if (!idAttr.isEmpty()) {
635 QPtrVector<NodeImpl> *idVector = info->idCache.find(idAttr);
637 idVector = new QPtrVector<NodeImpl>;
638 info->idCache.insert(idAttr, idVector);
640 appendToVector(idVector, static_cast<NodeImpl *>(e));
641 foundInputElements.insert(idAttr, (char *)true);
643 if (!nameAttr.isEmpty() && idAttr != nameAttr) {
645 QPtrVector<NodeImpl> *nameVector = info->nameCache.find(nameAttr);
647 nameVector = new QPtrVector<NodeImpl>;
648 info->nameCache.insert(nameAttr, nameVector);
650 appendToVector(nameVector, static_cast<NodeImpl *>(e));
651 foundInputElements.insert(nameAttr, (char *)true);
656 for (unsigned i = 0; i < f->imgElements.count(); ++i) {
657 HTMLImageElementImpl* e = f->imgElements[i];
658 QString idAttr = e->getAttribute(ATTR_ID).string();
659 QString nameAttr = e->getAttribute(ATTR_NAME).string();
660 if (!idAttr.isEmpty() && !foundInputElements.find(idAttr)) {
662 QPtrVector<NodeImpl> *idVector = info->idCache.find(idAttr);
664 idVector = new QPtrVector<NodeImpl>;
665 info->idCache.insert(idAttr, idVector);
667 appendToVector(idVector, static_cast<NodeImpl *>(e));
669 if (!nameAttr.isEmpty() && idAttr != nameAttr && !foundInputElements.find(nameAttr)) {
671 QPtrVector<NodeImpl> *nameVector = info->nameCache.find(nameAttr);
673 nameVector = new QPtrVector<NodeImpl>;
674 info->nameCache.insert(nameAttr, nameVector);
676 appendToVector(nameVector, static_cast<NodeImpl *>(e));
680 info->hasNameCache = true;