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 NodeImpl::Id HTMLBaseFontElementImpl::id() const
47 DOMString HTMLBaseFontElementImpl::color() const
49 return getAttribute(ATTR_COLOR);
52 void HTMLBaseFontElementImpl::setColor(const DOMString &value)
54 setAttribute(ATTR_COLOR, value);
57 DOMString HTMLBaseFontElementImpl::face() const
59 return getAttribute(ATTR_FACE);
62 void HTMLBaseFontElementImpl::setFace(const DOMString &value)
64 setAttribute(ATTR_FACE, value);
67 DOMString HTMLBaseFontElementImpl::size() const
69 return getAttribute(ATTR_SIZE);
72 void HTMLBaseFontElementImpl::setSize(const DOMString &value)
74 setAttribute(ATTR_SIZE, value);
77 // -------------------------------------------------------------------------
79 HTMLCollectionImpl::HTMLCollectionImpl(NodeImpl *_base, int _type)
84 info = _base->isDocumentNode() && _base->getDocument()->isHTMLDocument() ? static_cast<HTMLDocumentImpl*>(_base->getDocument())->collectionInfo(type) : 0;
87 HTMLCollectionImpl::~HTMLCollectionImpl()
91 HTMLCollectionImpl::CollectionInfo::CollectionInfo() :
94 idCache.setAutoDelete(true);
95 nameCache.setAutoDelete(true);
99 void HTMLCollectionImpl::CollectionInfo::reset()
105 elementsArrayPosition = 0;
108 hasNameCache = false;
111 void HTMLCollectionImpl::resetCollectionInfo() const
113 unsigned int docversion = static_cast<HTMLDocumentImpl*>(m_base->getDocument())->domTreeVersion();
116 info = new CollectionInfo;
117 info->version = docversion;
121 if (info->version != docversion) {
123 info->version = docversion;
128 NodeImpl *HTMLCollectionImpl::traverseNextItem(NodeImpl *current) const
132 current = current->traverseNextNode(m_base.get());
135 if(current->nodeType() == Node::ELEMENT_NODE) {
138 HTMLElementImpl *e = static_cast<HTMLElementImpl *>(current);
141 if(e->id() == ID_IMG)
145 if(e->id() == ID_FORM)
148 case DOC_NAMEABLE_ITEMS:
149 if(e->id() == ID_IMG)
151 if(e->id() == ID_FORM)
153 if(e->id() == ID_APPLET)
155 if(e->id() == ID_EMBED)
157 if(e->id() == ID_OBJECT)
161 if(e->id() == ID_TBODY)
163 else if(e->id() == ID_TABLE)
167 if(e->id() == ID_TD || e->id() == ID_TH)
169 else if(e->id() == ID_TABLE)
176 else if(e->id() == ID_TABLE)
180 if(e->id() == ID_OPTION)
184 if(e->id() == ID_AREA)
187 case DOC_APPLETS: // all OBJECT and APPLET elements
188 if(e->id() == ID_OBJECT || e->id() == ID_APPLET)
191 case DOC_EMBEDS: // all EMBED elements
192 if(e->id() == ID_EMBED)
195 case DOC_OBJECTS: // all OBJECT elements
196 if(e->id() == ID_OBJECT)
199 case DOC_LINKS: // all A _and_ AREA elements with a value for href
200 if(e->id() == ID_A || e->id() == ID_AREA)
201 if(!e->getAttribute(ATTR_HREF).isNull())
204 case DOC_ANCHORS: // all A elements with a value for name or an id attribute
206 if(!e->getAttribute(ATTR_NAME).isNull())
217 kdDebug( 6030 ) << "Error in HTMLCollection, wrong tagId!" << endl;
223 current = current->traverseNextNode(m_base.get());
227 current = current->traverseNextSibling(m_base.get());
233 unsigned long HTMLCollectionImpl::calcLength() const
235 unsigned long len = 0;
237 for (NodeImpl *current = traverseNextItem(m_base.get()); current; current = traverseNextItem(current)) {
244 // since the collections are to be "live", we have to do the
245 // calculation every time if anything has changed
246 unsigned long HTMLCollectionImpl::length() const
248 resetCollectionInfo();
249 if (!info->haslength) {
250 info->length = calcLength();
251 info->haslength = true;
256 NodeImpl *HTMLCollectionImpl::item( unsigned long index ) const
258 resetCollectionInfo();
259 if (info->current && info->position == index) {
260 return info->current;
262 if (info->haslength && info->length <= index) {
265 if (!info->current || info->position > index) {
266 info->current = traverseNextItem(m_base.get());
271 NodeImpl *node = info->current;
272 for (unsigned pos = info->position; node && pos < index; pos++) {
273 node = traverseNextItem(node);
275 info->current = node;
276 info->position = index;
277 return info->current;
280 NodeImpl *HTMLCollectionImpl::firstItem() const
285 NodeImpl *HTMLCollectionImpl::nextItem() const
287 resetCollectionInfo();
289 // Look for the 'second' item. The first one is currentItem, already given back.
290 NodeImpl *retval = traverseNextItem(info->current);
291 info->current = retval;
296 bool HTMLCollectionImpl::checkForNameMatch(NodeImpl *node, bool checkName, const DOMString &name, bool caseSensitive) const
298 ElementImpl *e = static_cast<ElementImpl *>(node);
301 // document.all returns only images, forms, applets, objects and embeds
302 // by name (though everything by id)
303 if (type == DOC_ALL &&
304 !(e->id() == ID_IMG || e->id() == ID_FORM ||
305 e->id() == ID_APPLET || e->id() == ID_OBJECT ||
306 e->id() == ID_EMBED))
309 return e->getAttribute(ATTR_NAME) == name && e->getAttribute(ATTR_ID) != name;
311 return e->getAttribute(ATTR_ID) == name;
315 // document.all returns only images, forms, applets, objects and embeds
316 // by name (though everything by id)
317 if (type == DOC_ALL &&
318 !(e->id() == ID_IMG || e->id() == ID_FORM ||
319 e->id() == ID_APPLET || e->id() == ID_OBJECT ||
320 e->id() == ID_EMBED))
323 return e->getAttribute(ATTR_NAME).domString().lower() == name.lower() &&
324 e->getAttribute(ATTR_ID).domString().lower() != name.lower();
326 return e->getAttribute(ATTR_ID).domString().lower() == name.lower();
332 NodeImpl *HTMLCollectionImpl::namedItem( const DOMString &name, bool caseSensitive ) const
334 // http://msdn.microsoft.com/workshop/author/dhtml/reference/methods/nameditem.asp
335 // This method first searches for an object with a matching id
336 // attribute. If a match is not found, the method then searches for an
337 // object with a matching name attribute, but only on those elements
338 // that are allowed a name attribute.
339 resetCollectionInfo();
343 for (n = traverseNextItem(m_base.get()); n; n = traverseNextItem(n)) {
344 if (checkForNameMatch(n, idsDone, name, caseSensitive)) {
351 return info->current;
354 for (n = traverseNextItem(m_base.get()); n; n = traverseNextItem(n)) {
355 if (checkForNameMatch(n, idsDone, name, caseSensitive)) {
361 return info->current;
364 template<class T> static void appendToVector(QPtrVector<T> *vec, T *item)
366 unsigned size = vec->size();
367 unsigned count = vec->count();
369 vec->resize(size == 0 ? 8 : (int)(size * 1.5));
370 vec->insert(count, item);
373 void HTMLCollectionImpl::updateNameCache() const
375 if (info->hasNameCache)
378 for (NodeImpl *n = traverseNextItem(m_base.get()); n; n = traverseNextItem(n)) {
379 ElementImpl *e = static_cast<ElementImpl *>(n);
380 QString idAttr = e->getAttribute(ATTR_ID).string();
381 QString nameAttr = e->getAttribute(ATTR_NAME).string();
382 if (!idAttr.isEmpty()) {
384 QPtrVector<NodeImpl> *idVector = info->idCache.find(idAttr);
386 idVector = new QPtrVector<NodeImpl>;
387 info->idCache.insert(idAttr, idVector);
389 appendToVector(idVector, n);
391 if (!nameAttr.isEmpty() && idAttr != nameAttr
392 && (type != DOC_ALL ||
393 (e->id() == ID_IMG || e->id() == ID_FORM ||
394 e->id() == ID_APPLET || e->id() == ID_OBJECT ||
395 e->id() == ID_EMBED))) {
397 QPtrVector<NodeImpl> *nameVector = info->nameCache.find(nameAttr);
399 nameVector = new QPtrVector<NodeImpl>;
400 info->nameCache.insert(nameAttr, nameVector);
402 appendToVector(nameVector, n);
406 info->hasNameCache = true;
409 QValueList< SharedPtr<NodeImpl> > HTMLCollectionImpl::namedItems(const DOMString &name) const
411 QValueList< SharedPtr<NodeImpl> > result;
416 resetCollectionInfo();
419 QPtrVector<NodeImpl> *idResults = info->idCache.find(name.string());
420 QPtrVector<NodeImpl> *nameResults = info->nameCache.find(name.string());
422 for (unsigned i = 0; idResults && i < idResults->count(); ++i) {
423 result.append(SharedPtr<NodeImpl>(idResults->at(i)));
426 for (unsigned i = 0; nameResults && i < nameResults->count(); ++i) {
427 result.append(SharedPtr<NodeImpl>(nameResults->at(i)));
434 NodeImpl *HTMLCollectionImpl::nextNamedItem( const DOMString &name ) const
436 resetCollectionInfo();
438 for (NodeImpl *n = traverseNextItem(info->current ? info->current : m_base.get()); n; n = traverseNextItem(n)) {
439 if (checkForNameMatch(n, idsDone, name, true)) {
451 for (NodeImpl *n = traverseNextItem(info->current ? info->current : m_base.get()); n; n = traverseNextItem(n)) {
452 if (checkForNameMatch(n, idsDone, name, true)) {
461 // -----------------------------------------------------------------------------
463 HTMLFormCollectionImpl::HTMLFormCollectionImpl(NodeImpl* _base)
464 : HTMLCollectionImpl(_base, 0)
466 HTMLFormElementImpl *formBase = static_cast<HTMLFormElementImpl*>(m_base.get());
467 if (!formBase->collectionInfo) {
468 formBase->collectionInfo = new CollectionInfo();
470 info = formBase->collectionInfo;
473 HTMLFormCollectionImpl::~HTMLFormCollectionImpl()
477 unsigned long HTMLFormCollectionImpl::calcLength() const
479 QPtrVector<HTMLGenericFormElementImpl> &l = static_cast<HTMLFormElementImpl*>(m_base.get())->formElements;
482 for ( unsigned i = 0; i < l.count(); i++ )
483 if ( l.at( i )->isEnumeratable() )
489 NodeImpl *HTMLFormCollectionImpl::item(unsigned long index) const
491 resetCollectionInfo();
493 if (info->current && info->position == index) {
494 return info->current;
496 if (info->haslength && info->length <= index) {
499 if (!info->current || info->position > index) {
502 info->elementsArrayPosition = 0;
505 QPtrVector<HTMLGenericFormElementImpl> &l = static_cast<HTMLFormElementImpl*>(m_base.get())->formElements;
506 unsigned currentIndex = info->position;
508 for (unsigned i = info->elementsArrayPosition; i < l.count(); i++) {
509 if (l[i]->isEnumeratable() ) {
510 if (index == currentIndex) {
511 info->position = index;
512 info->current = l[i];
513 info->elementsArrayPosition = i;
524 NodeImpl* HTMLFormCollectionImpl::getNamedItem(NodeImpl*, int attr_id, const DOMString& name, bool caseSensitive) const
527 return getNamedFormItem( attr_id, name, 0, caseSensitive );
530 NodeImpl* HTMLFormCollectionImpl::getNamedFormItem(int attr_id, const DOMString& name, int duplicateNumber, bool caseSensitive) const
532 if(m_base->nodeType() == Node::ELEMENT_NODE)
534 HTMLElementImpl* baseElement = static_cast<HTMLElementImpl*>(m_base.get());
535 bool foundInputElements = false;
536 if(baseElement->id() == ID_FORM)
538 HTMLFormElementImpl* f = static_cast<HTMLFormElementImpl*>(baseElement);
539 for (unsigned i = 0; i < f->formElements.count(); ++i) {
540 HTMLGenericFormElementImpl* e = f->formElements[i];
541 if (e->isEnumeratable()) {
544 found = e->getAttribute(attr_id) == name;
546 found = e->getAttribute(attr_id).domString().lower() == name.lower();
548 foundInputElements = true;
549 if (!duplicateNumber)
557 if ( !foundInputElements )
559 HTMLFormElementImpl* f = static_cast<HTMLFormElementImpl*>(baseElement);
561 for(unsigned i = 0; i < f->imgElements.count(); ++i)
563 HTMLImageElementImpl* e = f->imgElements[i];
566 found = e->getAttribute(attr_id) == name;
568 found = e->getAttribute(attr_id).domString().lower() == name.lower();
570 if (!duplicateNumber)
580 NodeImpl * HTMLFormCollectionImpl::firstItem() const
585 NodeImpl * HTMLFormCollectionImpl::nextItem() const
587 return item(info->position + 1);
590 NodeImpl * HTMLFormCollectionImpl::nextNamedItemInternal( const DOMString &name ) const
592 NodeImpl *retval = getNamedFormItem( idsDone ? ATTR_NAME : ATTR_ID, name, ++info->position, true );
595 if ( idsDone ) // we're done
597 // After doing all ATTR_ID, do ATTR_NAME
599 return getNamedItem(m_base->firstChild(), ATTR_NAME, name, true);
602 NodeImpl *HTMLFormCollectionImpl::namedItem( const DOMString &name, bool caseSensitive ) const
604 // http://msdn.microsoft.com/workshop/author/dhtml/reference/methods/nameditem.asp
605 // This method first searches for an object with a matching id
606 // attribute. If a match is not found, the method then searches for an
607 // object with a matching name attribute, but only on those elements
608 // that are allowed a name attribute.
609 resetCollectionInfo();
611 info->current = getNamedItem(m_base->firstChild(), ATTR_ID, name, true);
613 return info->current;
615 info->current = getNamedItem(m_base->firstChild(), ATTR_NAME, name, true);
616 return info->current;
620 NodeImpl *HTMLFormCollectionImpl::nextNamedItem( const DOMString &name ) const
622 // nextNamedItemInternal can return an item that has both id=<name> and name=<name>
623 // Here, we have to filter out such cases.
624 NodeImpl *impl = nextNamedItemInternal( name );
625 if (!idsDone) // looking for id=<name> -> no filtering
627 // looking for name=<name> -> filter out if id=<name>
631 if(impl->nodeType() == Node::ELEMENT_NODE)
633 HTMLElementImpl *e = static_cast<HTMLElementImpl *>(impl);
634 ok = (e->getAttribute(ATTR_ID) != name);
636 impl = nextNamedItemInternal( name );
637 } else // can't happen
643 void HTMLFormCollectionImpl::updateNameCache() const
645 if (info->hasNameCache)
648 QDict<char> foundInputElements;
650 if (m_base->id() != ID_FORM) {
651 info->hasNameCache = true;
655 HTMLElementImpl* baseElement = static_cast<HTMLElementImpl*>(m_base.get());
657 HTMLFormElementImpl* f = static_cast<HTMLFormElementImpl*>(baseElement);
658 for (unsigned i = 0; i < f->formElements.count(); ++i) {
659 HTMLGenericFormElementImpl* e = f->formElements[i];
660 if (e->isEnumeratable()) {
661 QString idAttr = e->getAttribute(ATTR_ID).string();
662 QString nameAttr = e->getAttribute(ATTR_NAME).string();
663 if (!idAttr.isEmpty()) {
665 QPtrVector<NodeImpl> *idVector = info->idCache.find(idAttr);
667 idVector = new QPtrVector<NodeImpl>;
668 info->idCache.insert(idAttr, idVector);
670 appendToVector(idVector, static_cast<NodeImpl *>(e));
671 foundInputElements.insert(idAttr, (char *)true);
673 if (!nameAttr.isEmpty() && idAttr != nameAttr) {
675 QPtrVector<NodeImpl> *nameVector = info->nameCache.find(nameAttr);
677 nameVector = new QPtrVector<NodeImpl>;
678 info->nameCache.insert(nameAttr, nameVector);
680 appendToVector(nameVector, static_cast<NodeImpl *>(e));
681 foundInputElements.insert(nameAttr, (char *)true);
686 for (unsigned i = 0; i < f->imgElements.count(); ++i) {
687 HTMLImageElementImpl* e = f->imgElements[i];
688 QString idAttr = e->getAttribute(ATTR_ID).string();
689 QString nameAttr = e->getAttribute(ATTR_NAME).string();
690 if (!idAttr.isEmpty() && !foundInputElements.find(idAttr)) {
692 QPtrVector<NodeImpl> *idVector = info->idCache.find(idAttr);
694 idVector = new QPtrVector<NodeImpl>;
695 info->idCache.insert(idAttr, idVector);
697 appendToVector(idVector, static_cast<NodeImpl *>(e));
699 if (!nameAttr.isEmpty() && idAttr != nameAttr && !foundInputElements.find(nameAttr)) {
701 QPtrVector<NodeImpl> *nameVector = info->nameCache.find(nameAttr);
703 nameVector = new QPtrVector<NodeImpl>;
704 info->nameCache.insert(nameAttr, nameVector);
706 appendToVector(nameVector, static_cast<NodeImpl *>(e));
710 info->hasNameCache = true;