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