Merged KDE 3.0 final code in and:
[WebKit-https.git] / WebCore / khtml / html / html_miscimpl.cpp
1 /**
2  * This file is part of the DOM implementation for KDE.
3  *
4  * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
5  *           (C) 1999 Antti Koivisto (koivisto@kde.org)
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Library General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Library General Public License
18  * along with this library; see the file COPYING.LIB.  If not, write to
19  * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20  * Boston, MA 02111-1307, USA.
21  *
22  */
23 // -------------------------------------------------------------------------
24 #include "html/html_miscimpl.h"
25 #include "html/html_formimpl.h"
26
27 #include "misc/htmlhashes.h"
28 #include "dom/dom_node.h"
29
30 using namespace DOM;
31
32 #include <kdebug.h>
33
34 HTMLBaseFontElementImpl::HTMLBaseFontElementImpl(DocumentPtr *doc)
35     : HTMLElementImpl(doc)
36 {
37 }
38
39 HTMLBaseFontElementImpl::~HTMLBaseFontElementImpl()
40 {
41 }
42
43 NodeImpl::Id HTMLBaseFontElementImpl::id() const
44 {
45     return ID_BASEFONT;
46 }
47
48 // -------------------------------------------------------------------------
49
50 HTMLCollectionImpl::HTMLCollectionImpl(NodeImpl *_base, int _type)
51 {
52     base = _base;
53     base->ref();
54     type = _type;
55     currentItem = 0L;
56     idsDone = false;
57 }
58
59 HTMLCollectionImpl::~HTMLCollectionImpl()
60 {
61     base->deref();
62 }
63
64 unsigned long HTMLCollectionImpl::calcLength(NodeImpl *current) const
65 {
66     unsigned long len = 0;
67     while(current)
68     {
69         if(current->nodeType() == Node::ELEMENT_NODE)
70         {
71             bool deep = true;
72             HTMLElementImpl *e = static_cast<HTMLElementImpl *>(current);
73             switch(type)
74             {
75             case DOC_IMAGES:
76                 if(e->id() == ID_IMG)
77                     len++;
78                 break;
79             case DOC_FORMS:
80                 if(e->id() == ID_FORM)
81                     len++;
82                 break;
83             case TABLE_TBODIES:
84                 if(e->id() == ID_TBODY)
85                     len++;
86                 else if(e->id() == ID_TABLE)
87                     deep = false;
88                 break;
89             case TR_CELLS:
90                 if(e->id() == ID_TD)
91                     len++;
92                 else if(e->id() == ID_TABLE)
93                     deep = false;
94                 break;
95             case TABLE_ROWS:
96             case TSECTION_ROWS:
97                 if(e->id() == ID_TR || e->id() == ID_TH)
98                     len++;
99                 else if(e->id() == ID_TABLE)
100                     deep = false;
101                 break;
102             case SELECT_OPTIONS:
103                 if(e->id() == ID_OPTION)
104                     len++;
105                 break;
106             case MAP_AREAS:
107                 if(e->id() == ID_AREA)
108                     len++;
109                 break;
110             case DOC_APPLETS:   // all OBJECT and APPLET elements
111                 if(e->id() == ID_OBJECT || e->id() == ID_APPLET)
112                     len++;
113                 break;
114             case DOC_LINKS:     // all A _and_ AREA elements with a value for href
115                 if(e->id() == ID_A || e->id() == ID_AREA)
116                     if(!e->getAttribute(ATTR_HREF).isNull())
117                         len++;
118                 break;
119             case DOC_ANCHORS:      // all A elements with a value for name and all elements with an id attribute
120                 if ( e->hasID() )
121                     len++;
122                 else if(e->id() == ID_A) {
123                     if(!e->getAttribute(ATTR_NAME).isNull())
124                         len++;
125                 }
126                 break;
127             case DOC_ALL:      // "all" elements
128                 len++;
129                 break;
130             case NODE_CHILDREN: // first-level children
131                 len++;
132                 deep = false;
133                 break;
134             default:
135                 kdDebug( 6030 ) << "Error in HTMLCollection, wrong tagId!" << endl;
136             }
137             if(deep && current->firstChild())
138                 len += calcLength(current->firstChild());
139         }
140         current = current->nextSibling();
141     }
142     return len;
143 }
144
145 // since the collections are to be "live", we have to do the
146 // calculation every time...
147 unsigned long HTMLCollectionImpl::length() const
148 {
149     return calcLength(base->firstChild());
150 }
151
152 NodeImpl *HTMLCollectionImpl::getItem(NodeImpl *current, int index, int &len) const
153 {
154     while(current)
155     {
156         if(current->nodeType() == Node::ELEMENT_NODE)
157         {
158             bool deep = true;
159             HTMLElementImpl *e = static_cast<HTMLElementImpl *>(current);
160             switch(type)
161             {
162             case DOC_IMAGES:
163                 if(e->id() == ID_IMG)
164                     len++;
165                 break;
166             case DOC_FORMS:
167                 if(e->id() == ID_FORM)
168                     len++;
169                 break;
170             case TABLE_TBODIES:
171                 if(e->id() == ID_TBODY)
172                     len++;
173                 else if(e->id() == ID_TABLE)
174                     deep = false;
175                 break;
176             case TR_CELLS:
177                 if(e->id() == ID_TD)
178                     len++;
179                 else if(e->id() == ID_TABLE)
180                     deep = false;
181                 break;
182             case TABLE_ROWS:
183             case TSECTION_ROWS:
184                 if(e->id() == ID_TR || e->id() == ID_TH)
185                     len++;
186                 else if(e->id() == ID_TABLE)
187                     deep = false;
188                 break;
189             case SELECT_OPTIONS:
190                 if(e->id() == ID_OPTION)
191                     len++;
192                 break;
193             case MAP_AREAS:
194                 if(e->id() == ID_AREA)
195                     len++;
196                 break;
197             case DOC_APPLETS:   // all OBJECT and APPLET elements
198                 if(e->id() == ID_OBJECT || e->id() == ID_APPLET)
199                     len++;
200                 break;
201             case DOC_LINKS:     // all A _and_ AREA elements with a value for href
202                 if(e->id() == ID_A || e->id() == ID_AREA)
203                     if(!e->getAttribute(ATTR_HREF).isNull())
204                         len++;
205                 break;
206             case DOC_ANCHORS:      // all A elements with a value for name or an id attribute
207                 if( e->hasID() )
208                     len++;
209                 else if(e->id() == ID_A)
210                     if(!e->getAttribute(ATTR_NAME).isNull())
211                         len++;
212                 break;
213             case DOC_ALL:
214                 len++;
215                 break;
216             case NODE_CHILDREN:
217                 len++;
218                 deep = false;
219                 break;
220             default:
221                 kdDebug( 6030 ) << "Error in HTMLCollection, wrong tagId!" << endl;
222             }
223             if(len == (index + 1)) return current;
224             NodeImpl *retval=0;
225             if(deep && current->firstChild())
226                 retval = getItem(current->firstChild(), index, len);
227             if(retval) return retval;
228         }
229         current = current->nextSibling();
230     }
231     return 0;
232 }
233
234 NodeImpl *HTMLCollectionImpl::item( unsigned long index ) const
235 {
236     int pos = 0;
237     return getItem(base->firstChild(), index, pos);
238 }
239
240 NodeImpl *HTMLCollectionImpl::firstItem() const
241 {
242     int pos = 0;
243     currentItem = getItem(base->firstChild(), 0, pos);
244     return currentItem;
245 }
246
247 NodeImpl *HTMLCollectionImpl::nextItem() const
248 {
249     int pos = 0;
250     // Look for the 'second' item. The first one is currentItem, already given back.
251     NodeImpl *retval = getItem(currentItem, 1, pos);
252     if (retval)
253     {
254         currentItem = retval;
255         return retval;
256     }
257     // retval was 0, means we have to go up
258     while( !retval && currentItem->parentNode()
259            && currentItem->parentNode() != base )
260     {
261         currentItem = currentItem->parentNode();
262         if (currentItem->nextSibling())
263         {
264             // ... and to take the first one from there
265             pos = 0;
266             retval = getItem(currentItem->nextSibling(), 0, pos);
267         }
268     }
269     currentItem = retval;
270     return currentItem;
271 }
272
273 NodeImpl *HTMLCollectionImpl::getNamedItem( NodeImpl *current, int attr_id,
274                                             const DOMString &name ) const
275 {
276     if(name.isEmpty())
277         return 0;
278
279     while(current)
280     {
281         if(current->nodeType() == Node::ELEMENT_NODE)
282         {
283             bool deep = true;
284             bool check = false;
285             HTMLElementImpl *e = static_cast<HTMLElementImpl *>(current);
286             switch(type)
287             {
288             case DOC_IMAGES:
289                 if(e->id() == ID_IMG)
290                     check = true;
291                 break;
292             case DOC_FORMS:
293                 if(e->id() == ID_FORM)
294                     check = true;
295                 break;
296             case TABLE_TBODIES:
297                 if(e->id() == ID_TBODY)
298                     check = true;
299                 else if(e->id() == ID_TABLE)
300                     deep = false;
301                 break;
302             case TR_CELLS:
303                 if(e->id() == ID_TD)
304                     check = true;
305                 else if(e->id() == ID_TABLE)
306                     deep = false;
307                 break;
308             case TABLE_ROWS:
309             case TSECTION_ROWS:
310                 if(e->id() == ID_TR || e->id() == ID_TH)
311                     check = true;
312                 else if(e->id() == ID_TABLE)
313                     deep = false;
314                 break;
315             case SELECT_OPTIONS:
316                 if(e->id() == ID_OPTION)
317                     check = true;
318                 break;
319             case MAP_AREAS:
320                 if(e->id() == ID_AREA)
321                     check = true;
322                 break;
323             case DOC_APPLETS:   // all OBJECT and APPLET elements
324                 if(e->id() == ID_OBJECT || e->id() == ID_APPLET)
325                     check = true;
326                 break;
327             case DOC_LINKS:     // all A _and_ AREA elements with a value for href
328                 if(e->id() == ID_A || e->id() == ID_AREA)
329                     if(!e->getAttribute(ATTR_HREF).isNull())
330                         check = true;
331                 break;
332             case DOC_ANCHORS:      // all A elements with a value for name
333                 if( e->hasID() )
334                     check = true;
335                 else if(e->id() == ID_A)
336                     if(!e->getAttribute(ATTR_NAME).isNull())
337                         check = true;
338                 break;
339             case DOC_ALL:
340                 check = true;
341                 break;
342             case NODE_CHILDREN:
343                 check = true;
344                 deep = false;
345                 break;
346             default:
347                 kdDebug( 6030 ) << "Error in HTMLCollection, wrong tagId!" << endl;
348                 break;
349             }
350             if(check && e->getAttribute(attr_id) == name)
351             {
352                 //kdDebug( 6030 ) << "found node: " << e << " " << current << " " << e->id() << " " << e->tagName().string() << endl;
353                 return current;
354             }
355             NodeImpl *retval = 0;
356             if(deep && current->firstChild())
357                 retval = getNamedItem(current->firstChild(), attr_id, name);
358             if(retval)
359             {
360                 //kdDebug( 6030 ) << "got a return value " << retval << endl;
361                 return retval;
362             }
363         }
364         current = current->nextSibling();
365     }
366     return 0;
367 }
368
369 NodeImpl *HTMLCollectionImpl::namedItem( const DOMString &name ) const
370 {
371     // http://msdn.microsoft.com/workshop/author/dhtml/reference/methods/nameditem.asp
372     // This method first searches for an object with a matching id
373     // attribute. If a match is not found, the method then searches for an
374     // object with a matching name attribute, but only on those elements
375     // that are allowed a name attribute.
376     idsDone = false;
377     currentItem = getNamedItem(base->firstChild(), ATTR_ID, name);
378     if(currentItem)
379         return currentItem;
380     idsDone = true;
381     currentItem = getNamedItem(base->firstChild(), ATTR_NAME, name);
382     return currentItem;
383 }
384
385 NodeImpl *HTMLCollectionImpl::nextNamedItem( const DOMString &name ) const
386 {
387     // nextNamedItemInternal can return an item that has both id=<name> and name=<name>
388     // Here, we have to filter out such cases.
389     NodeImpl *impl = nextNamedItemInternal( name );
390     if (!idsDone) // looking for id=<name> -> no filtering
391         return impl;
392     // looking for name=<name> -> filter out if id=<name>
393     bool ok = false;
394     while (impl && !ok)
395     {
396         if(impl->nodeType() == Node::ELEMENT_NODE)
397         {
398             HTMLElementImpl *e = static_cast<HTMLElementImpl *>(impl);
399             ok = (e->getAttribute(ATTR_ID) != name);
400             if (!ok)
401                 impl = nextNamedItemInternal( name );
402         } else // can't happen
403             ok = true;
404     }
405     return impl;
406 }
407
408 NodeImpl *HTMLCollectionImpl::nextNamedItemInternal( const DOMString &name ) const
409 {
410     //kdDebug() << "\nHTMLCollectionImpl::nextNamedItem starting at " << currentItem << endl;
411     // Go to next item first (to avoid returning the same)
412     currentItem = nextItem();
413     //kdDebug() << "*HTMLCollectionImpl::nextNamedItem next item is " << currentItem << endl;
414
415     if ( currentItem )
416     {
417         // Then look for next matching named item
418         NodeImpl *retval = getNamedItem(currentItem, idsDone ? ATTR_NAME : ATTR_ID, name);
419         if ( retval )
420         {
421             //kdDebug() << "*HTMLCollectionImpl::nextNamedItem found " << retval << endl;
422             currentItem = retval;
423             return retval;
424         }
425
426         // retval was 0, means we have to go up
427         while( !retval && currentItem->parentNode()
428                && currentItem->parentNode() != base )
429         {
430             currentItem = currentItem->parentNode();
431             if (currentItem->nextSibling())
432             {
433                 // ... and to take the first one from there
434                 retval = getNamedItem(currentItem->nextSibling(), idsDone ? ATTR_NAME : ATTR_ID, name);
435             }
436         }
437         if ( retval )
438         {
439             //kdDebug() << "*HTMLCollectionImpl::nextNamedItem found after going up " << retval << endl;
440             currentItem = retval;
441             return currentItem;
442         }
443     }
444
445     if ( idsDone )
446         return 0;
447     // After doing all ATTR_ID, do ATTR_NAME
448     //kdDebug() << "*HTMLCollectionImpl::nextNamedItem going to ATTR_NAME now" << endl;
449     idsDone = true;
450     currentItem = getNamedItem(base->firstChild(), ATTR_NAME, name);
451     return currentItem;
452
453 }
454
455 // -----------------------------------------------------------------------------
456
457 unsigned long HTMLFormCollectionImpl::calcLength(NodeImpl*) const
458 {
459     QPtrList<HTMLGenericFormElementImpl> l = static_cast<HTMLFormElementImpl*>( base )->formElements;
460
461     int len = 0;
462     for ( unsigned i = 0; i < l.count(); i++ )
463         if ( l.at( i )->isEnumeratable() )
464             ++len;
465
466     return len;
467 }
468
469 NodeImpl* HTMLFormCollectionImpl::getItem(NodeImpl *, int index, int&) const
470 {
471     QPtrList<HTMLGenericFormElementImpl> l = static_cast<HTMLFormElementImpl*>( base )->formElements;
472
473     for ( unsigned i = 0; i < l.count(); i++ ) {
474
475         if( l.at( i )->isEnumeratable() ) {
476             if ( !index )
477                 return l.at( i );
478
479             --index;
480         }
481     }
482
483     return 0;
484 }
485
486 NodeImpl* HTMLFormCollectionImpl::getNamedItem(NodeImpl*, int attr_id, const DOMString& name) const
487 {
488     currentPos = 0;
489     return getNamedFormItem( attr_id, name, 0 );
490 }
491
492 NodeImpl* HTMLFormCollectionImpl::getNamedFormItem(int attr_id, const DOMString& name, int duplicateNumber) const
493 {
494     if(base->nodeType() == Node::ELEMENT_NODE)
495     {
496         HTMLElementImpl* e = static_cast<HTMLElementImpl*>(base);
497         if(e->id() == ID_FORM)
498         {
499             HTMLGenericFormElementImpl* element;
500             HTMLFormElementImpl* f = static_cast<HTMLFormElementImpl*>(e);
501
502             for(element = f->formElements.first(); element; element = f->formElements.next())
503                 if(element->isEnumeratable() && element->getAttribute(attr_id) == name)
504                 {
505                     if (!duplicateNumber)
506                         return element;
507                     --duplicateNumber;
508                 }
509         }
510         NodeImpl* retval = getNamedImgItem( base->firstChild(), attr_id, name, duplicateNumber );
511         if ( retval )
512             return retval;
513     }
514     return 0;
515 }
516
517 NodeImpl* HTMLFormCollectionImpl::getNamedImgItem(NodeImpl* current, int attr_id, const DOMString& name, int& duplicateNumber) const
518 {
519     // strange case. IE and NS allow to get hold of <img> tags,
520     // but they don't include them in the elements() collection.
521     while ( current )
522     {
523         if(current->nodeType() == Node::ELEMENT_NODE)
524         {
525             HTMLElementImpl *currelem = static_cast<HTMLElementImpl *>(current);
526             if(currelem->id() == ID_IMG && currelem->getAttribute(attr_id) == name)
527             {
528                 if (!duplicateNumber)
529                     return current;
530                 --duplicateNumber;
531             }
532             if(current->firstChild())
533             {
534                 // The recursion here is the reason why this is a separate method
535                 NodeImpl *retval = getNamedImgItem(current->firstChild(), attr_id, name, duplicateNumber);
536                 if(retval)
537                 {
538                     //kdDebug( 6030 ) << "got a return value " << retval << endl;
539                     return retval;
540                 }
541             }
542         }
543         current = current->nextSibling();
544     } // while
545     return 0;
546 }
547
548 NodeImpl * HTMLFormCollectionImpl::firstItem() const
549 {
550     currentPos = 0;
551     int dummy = 0;
552     return getItem(0 /*base->firstChild() unused*/, currentPos, dummy);
553 }
554
555 NodeImpl * HTMLFormCollectionImpl::nextItem() const
556 {
557     // This implementation loses the whole benefit of firstItem/nextItem :(
558     int dummy = 0;
559     return getItem(0 /*base->firstChild() unused*/, ++currentPos, dummy);
560 }
561
562 NodeImpl * HTMLFormCollectionImpl::nextNamedItemInternal( const DOMString &name ) const
563 {
564     NodeImpl *retval = getNamedFormItem( idsDone ? ATTR_NAME : ATTR_ID, name, ++currentPos );
565     if ( retval )
566         return retval;
567     if ( idsDone ) // we're done
568         return 0;
569     // After doing all ATTR_ID, do ATTR_NAME
570     idsDone = true;
571     return getNamedItem(base->firstChild(), ATTR_NAME, name);
572 }