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