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