Reviewed by darin.
[WebKit-https.git] / WebCore / khtml / html / htmlparser.cpp
1 /*
2     This file is part of the KDE libraries
3
4     Copyright (C) 1997 Martin Jones (mjones@kde.org)
5               (C) 1997 Torben Weis (weis@kde.org)
6               (C) 1999,2001 Lars Knoll (knoll@kde.org)
7               (C) 2000,2001 Dirk Mueller (mueller@kde.org)
8     Copyright (C) 2004 Apple Computer, Inc.
9
10     This library is free software; you can redistribute it and/or
11     modify it under the terms of the GNU Library General Public
12     License as published by the Free Software Foundation; either
13     version 2 of the License, or (at your option) any later version.
14
15     This library is distributed in the hope that it will be useful,
16     but WITHOUT ANY WARRANTY; without even the implied warranty of
17     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18     Library General Public License for more details.
19
20     You should have received a copy of the GNU Library General Public License
21     along with this library; see the file COPYING.LIB.  If not, write to
22     the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
23     Boston, MA 02111-1307, USA.
24 */
25 //----------------------------------------------------------------------------
26 //
27 // KDE HTML Widget -- HTML Parser
28 //#define PARSER_DEBUG
29
30 #include "html/htmlparser.h"
31
32 #include "dom/dom_exception.h"
33
34 #include "html/html_baseimpl.h"
35 #include "html/html_blockimpl.h"
36 #include "html/html_canvasimpl.h"
37 #include "html/html_documentimpl.h"
38 #include "html/html_elementimpl.h"
39 #include "html/html_formimpl.h"
40 #include "html/html_headimpl.h"
41 #include "html/html_imageimpl.h"
42 #include "html/html_inlineimpl.h"
43 #include "html/html_listimpl.h"
44 #include "html/html_miscimpl.h"
45 #include "html/html_tableimpl.h"
46 #include "html/html_objectimpl.h"
47 #include "xml/dom_textimpl.h"
48 #include "xml/dom_nodeimpl.h"
49 #include "misc/htmlhashes.h"
50 #include "html/htmltokenizer.h"
51 #include "khtmlview.h"
52 #include "khtml_part.h"
53 #include "css/cssproperties.h"
54 #include "css/cssvalues.h"
55
56 #include "rendering/render_object.h"
57
58 #include <kdebug.h>
59 #include <klocale.h>
60
61 using namespace DOM;
62 using namespace khtml;
63
64 //----------------------------------------------------------------------------
65
66 /**
67  * @internal
68  */
69 class HTMLStackElem
70 {
71 public:
72     HTMLStackElem( int _id,
73                    int _level,
74                    DOM::NodeImpl *_node,
75                    HTMLStackElem * _next
76         )
77         :
78         id(_id),
79         level(_level),
80         strayTableContent(false),
81         node(_node),
82         next(_next)
83         { }
84
85     int       id;
86     int       level;
87     bool      strayTableContent;
88     NodeImpl *node;
89     HTMLStackElem *next;
90 };
91
92 /**
93  * @internal
94  *
95  * The parser parses tokenized input into the document, building up the
96  * document tree. If the document is wellformed, parsing it is
97  * straightforward.
98  * Unfortunately, people can't write wellformed HTML documents, so the parser
99  * has to be tolerant about errors.
100  *
101  * We have to take care of the following error conditions:
102  * 1. The element being added is explicitly forbidden inside some outer tag.
103  *    In this case we should close all tags up to the one, which forbids
104  *    the element, and add it afterwards.
105  * 2. We are not allowed to add the element directly. It could be, that
106  *    the person writing the document forgot some tag inbetween (or that the
107  *    tag inbetween is optional...) This could be the case with the following
108  *    tags: HTML HEAD BODY TBODY TR TD LI (did I forget any?)
109  * 3. We wan't to add a block element inside to an inline element. Close all
110  *    inline elements up to the next higher block element.
111  * 4. If this doesn't help close elements, until we are allowed to add the
112  *    element or ignore the tag.
113  *
114  */
115 KHTMLParser::KHTMLParser(KHTMLView *_parent, DocumentPtr *doc, bool includesComments) 
116     : current(0), currentIsReferenced(false), includesCommentsInDOM(includesComments)
117 {
118     //kdDebug( 6035 ) << "parser constructor" << endl;
119 #if SPEED_DEBUG > 0
120     qt.start();
121 #endif
122
123     HTMLWidget    = _parent;
124     document      = doc;
125     document->ref();
126
127     blockStack = 0;
128
129     reset();
130 }
131
132 KHTMLParser::KHTMLParser(DOM::DocumentFragmentImpl *i, DocumentPtr *doc, bool includesComments)
133     : current(0), currentIsReferenced(false), includesCommentsInDOM(includesComments)
134 {
135     HTMLWidget = 0;
136     document = doc;
137     document->ref();
138
139     blockStack = 0;
140
141     reset();
142     setCurrent(i);
143     inBody = true;
144 }
145
146 KHTMLParser::~KHTMLParser()
147 {
148 #if SPEED_DEBUG > 0
149     kdDebug( ) << "TIME: parsing time was = " << qt.elapsed() << endl;
150 #endif
151
152     freeBlock();
153
154     setCurrent(0);
155
156     document->deref();
157
158     if (isindex)
159         isindex->deref();
160 }
161
162 void KHTMLParser::reset()
163 {
164     setCurrent(doc());
165
166     freeBlock();
167
168     // before parsing, no tags are forbidden
169     memset(forbiddenTag, 0, sizeof(forbiddenTag));
170
171     inBody = false;
172     haveFrameSet = false;
173     haveContent = false;
174     inSelect = false;
175     inStrayTableContent = 0;
176     
177     form = 0;
178     map = 0;
179     head = 0;
180     end = false;
181     isindex = 0;
182     
183     discard_until = 0;
184 }
185
186 void KHTMLParser::setCurrent(DOM::NodeImpl *newCurrent) 
187 {
188     bool newCurrentIsReferenced = newCurrent && newCurrent != doc();
189     if (newCurrentIsReferenced) 
190         newCurrent->ref(); 
191     if (currentIsReferenced) 
192         current->deref(); 
193     current = newCurrent;
194     currentIsReferenced = newCurrentIsReferenced;
195 }
196
197 void KHTMLParser::parseToken(Token *t)
198 {
199     if(discard_until) {
200         if(t->id == discard_until)
201             discard_until = 0;
202
203         // do not skip </iframe>
204         if ( discard_until || current->id() + ID_CLOSE_TAG != t->id )
205             return;
206     }
207
208 #ifdef PARSER_DEBUG
209     kdDebug( 6035 ) << "\n\n==> parser: processing token " << getTagName(t->id).string() << "(" << t->id << ")"
210                     << " current = " << getTagName(current->id()).string() << "(" << current->id() << ")" << endl;
211     kdDebug(6035) << " inBody=" << inBody << " haveFrameSet=" << haveFrameSet << endl;
212 #endif
213
214     // holy shit. apparently some sites use </br> instead of <br>
215     // be compatible with IE and NS
216     if (t->id == ID_BR + ID_CLOSE_TAG && doc()->inCompatMode())
217         t->id = ID_BR;
218
219     if (t->id > ID_CLOSE_TAG)
220     {
221         processCloseTag(t);
222         return;
223     }
224
225     // ignore spaces, if we're not inside a paragraph or other inline code
226     if( t->id == ID_TEXT && t->text ) {
227         if(inBody && !skipMode() && current->id() != ID_STYLE 
228             && current->id() != ID_TITLE && current->id() != ID_SCRIPT &&
229             !t->text->containsOnlyWhitespace()) 
230             haveContent = true;
231 #ifdef PARSER_DEBUG
232         kdDebug(6035) << "length="<< t->text->l << " text='" << QConstString(t->text->s, t->text->l).string() << "'" << endl;
233 #endif
234     }
235
236     NodeImpl *n = getElement(t);
237     // just to be sure, and to catch currently unimplemented stuff
238     if(!n)
239         return;
240
241     Node protectNode(n);
242
243     // set attributes
244     if(n->isElementNode())
245     {
246         ElementImpl *e = static_cast<ElementImpl *>(n);
247         e->setAttributeMap(t->attrs);
248
249         // take care of optional close tags
250         if(endTagRequirement(e->id()) == DOM::OPTIONAL)
251             popBlock(t->id);
252             
253         if (isHeaderTag(t->id))
254             // Do not allow two header tags to be nested if the intervening tags are inlines.
255             popNestedHeaderTag();
256     }
257
258     // if this tag is forbidden inside the current context, pop
259     // blocks until we are allowed to add it...
260     while (blockStack && t->id <= ID_LAST_TAG && forbiddenTag[t->id]) {
261 #ifdef PARSER_DEBUG
262         kdDebug( 6035 ) << "t->id: " << t->id << " is forbidden :-( " << endl;
263 #endif
264         popOneBlock();
265     }
266
267     if (!insertNode(n, t->flat)) 
268     {
269         // we couldn't insert the node...
270         
271         if(n->isElementNode())
272         {
273             ElementImpl *e = static_cast<ElementImpl *>(n);
274             e->setAttributeMap(0);
275         }
276             
277 #ifdef PARSER_DEBUG
278         kdDebug( 6035 ) << "insertNode failed current=" << current->id() << ", new=" << n->id() << "!" << endl;
279 #endif
280         if (map == n)
281         {
282 #ifdef PARSER_DEBUG
283             kdDebug( 6035 ) << "  --> resetting map!" << endl;
284 #endif
285             map = 0;
286         }
287         if (form == n)
288         {
289 #ifdef PARSER_DEBUG
290             kdDebug( 6035 ) << "   --> resetting form!" << endl;
291 #endif
292             form = 0;
293         }
294     }
295 }
296
297 static bool isTableRelatedTag(int id)
298 {
299     return (id == ID_TR || id == ID_TD || id == ID_TABLE || id == ID_TBODY || id == ID_TFOOT || id == ID_THEAD ||
300             id == ID_TH);
301 }
302
303 bool KHTMLParser::insertNode(NodeImpl *n, bool flat)
304 {
305     Node protectNode(n);
306
307     int id = n->id();
308
309     // let's be stupid and just try to insert it.
310     // this should work if the document is wellformed
311 #ifdef PARSER_DEBUG
312     NodeImpl *tmp = current;
313 #endif
314     NodeImpl *newNode = current->addChild(n);
315     if ( newNode ) {
316 #ifdef PARSER_DEBUG
317         kdDebug( 6035 ) << "added " << n->nodeName().string() << " to " << tmp->nodeName().string() << ", new current=" << newNode->nodeName().string() << endl;
318 #endif
319         // don't push elements without end tag on the stack
320         if(tagPriority(id) != 0 && !flat)
321         {
322             pushBlock(id, tagPriority(id));
323             if (newNode == current)
324                 popBlock(id);
325             else
326                 setCurrent(newNode);
327 #if SPEED_DEBUG < 2
328             if(!n->attached() && HTMLWidget)
329                 n->attach();
330 #endif
331         }
332         else {
333 #if SPEED_DEBUG < 2
334             if(!n->attached() && HTMLWidget)
335                 n->attach();
336             if (n->maintainsState()) {
337                 doc()->registerMaintainsState(n);
338                 QStringList &states = doc()->restoreState();
339                 if (!states.isEmpty())
340                     n->restoreState(states);
341             }
342             n->closeRenderer();
343 #endif
344         }
345
346         return true;
347     } else {
348 #ifdef PARSER_DEBUG
349         kdDebug( 6035 ) << "ADDING NODE FAILED!!!! current = " << current->nodeName().string() << ", new = " << n->nodeName().string() << endl;
350 #endif
351         // error handling...
352         HTMLElementImpl *e;
353         bool handled = false;
354
355         // switch according to the element to insert
356         switch(id)
357         {
358         case ID_TR:
359         case ID_TH:
360         case ID_TD:
361             if (inStrayTableContent && !isTableRelatedTag(current->id())) {
362                 // pop out to the nearest enclosing table-related tag.
363                 while (blockStack && !isTableRelatedTag(current->id()))
364                     popOneBlock();
365                 return insertNode(n);
366             }
367             break;
368         case ID_COMMENT:
369             break;
370         case ID_HEAD:
371             // ### alllow not having <HTML> in at all, as per HTML spec
372             if (!current->isDocumentNode() && current->id() != ID_HTML )
373                 return false;
374             break;
375             // We can deal with a base, meta and link element in the body, by just adding the element to head.
376         case ID_META:
377         case ID_LINK:
378         case ID_BASE:
379             if( !head )
380                 createHead();
381             if( head ) {
382                 if ( head->addChild(n) ) {
383 #if SPEED_DEBUG < 2
384                     if(!n->attached() && HTMLWidget)
385                         n->attach();
386 #endif
387                     return true;
388                 } else {
389                     return false;
390                 }
391             }
392             break;
393         case ID_HTML:
394             if (!current->isDocumentNode() ) {
395                 if ( doc()->firstChild()->id() == ID_HTML) {
396                     // we have another <HTML> element.... apply attributes to existing one
397                     // make sure we don't overwrite already existing attributes
398                     NamedAttrMapImpl *map = static_cast<ElementImpl*>(n)->attributes(true);
399                     NamedAttrMapImpl *bmap = static_cast<ElementImpl*>(doc()->firstChild())->attributes(false);
400                     bool changed = false;
401                     for (unsigned long l = 0; map && l < map->length(); ++l) {
402                         AttributeImpl* it = map->attributeItem(l);
403                         changed = !bmap->getAttributeItem(it->id());
404                         bmap->insertAttribute(it->clone(false));
405                     }
406                     if ( changed )
407                         doc()->recalcStyle( NodeImpl::Inherit );
408                 }
409                 return false;
410             }
411             break;
412         case ID_TITLE:
413         case ID_STYLE:
414             if ( !head )
415                 createHead();
416             if ( head ) {
417                 DOM::NodeImpl *newNode = head->addChild(n);
418                 if ( newNode ) {
419                     pushBlock(id, tagPriority(id));
420                     setCurrent(newNode);
421 #if SPEED_DEBUG < 2
422                     if(!n->attached() && HTMLWidget)
423                         n->attach();
424 #endif
425                 } else {
426 #ifdef PARSER_DEBUG
427                     kdDebug( 6035 ) << "adding style before to body failed!!!!" << endl;
428 #endif
429                     discard_until = ID_STYLE + ID_CLOSE_TAG;
430                     return false;
431                 }
432                 return true;
433             } else if(inBody) {
434                 discard_until = ID_STYLE + ID_CLOSE_TAG;
435                 return false;
436             }
437             break;
438             // SCRIPT and OBJECT are allowed in the body.
439         case ID_BODY:
440             if(inBody && doc()->body()) {
441                 // we have another <BODY> element.... apply attributes to existing one
442                 // make sure we don't overwrite already existing attributes
443                 // some sites use <body bgcolor=rightcolor>...<body bgcolor=wrongcolor>
444                 NamedAttrMapImpl *map = static_cast<ElementImpl*>(n)->attributes(true);
445                 NamedAttrMapImpl *bmap = doc()->body()->attributes(false);
446                 bool changed = false;
447                 for (unsigned long l = 0; map && l < map->length(); ++l) {
448                     AttributeImpl* it = map->attributeItem(l);
449                     changed = !bmap->getAttributeItem(it->id());
450                     bmap->insertAttribute(it->clone(false));
451                 }
452                 if ( changed )
453                     doc()->recalcStyle( NodeImpl::Inherit );
454             } else if ( current->isDocumentNode() )
455                 break;
456             return false;
457             break;
458
459             // the following is a hack to move non rendered elements
460             // outside of tables.
461             // needed for broken constructs like <table><form ...><tr>....
462         case ID_INPUT:
463         {
464             ElementImpl *e = static_cast<ElementImpl *>(n);
465             DOMString type = e->getAttribute(ATTR_TYPE);
466
467             if ( strcasecmp( type, "hidden" ) == 0 && form) {
468                 form->addChild(n);
469 #if SPEED_DEBUG < 2
470                 if(!n->attached() && HTMLWidget)
471                     n->attach();
472 #endif
473                 return true;
474             }
475             break;
476         }
477         case ID_TEXT:
478             // ignore text inside the following elements.
479             switch(current->id())
480             {
481             case ID_SELECT:
482                 return false;
483             default:
484                 ;
485                 // fall through!!
486             };
487             break;
488         case ID_DD:
489         case ID_DT:
490             e = new HTMLDListElementImpl(document);
491             if ( insertNode(e) ) {
492                 insertNode(n);
493                 return true;
494             }
495             break;
496         case ID_AREA:
497         {
498             if(map)
499             {
500                 map->addChild(n);
501 #if SPEED_DEBUG < 2
502                 if(!n->attached() && HTMLWidget)
503                     n->attach();
504 #endif
505                 handled = true;
506             }
507             else
508                 return false;
509             return true;
510         }
511         case ID_CAPTION: {
512             switch (current->id()) {
513                 case ID_THEAD:
514                 case ID_TBODY:
515                 case ID_TFOOT:
516                 case ID_TR:
517                 case ID_TH:
518                 case ID_TD: {
519                     NodeImpl* tsection = current;
520                     if (current->id() == ID_TR)
521                         tsection = current->parent();
522                     else if (current->id() == ID_TD || current->id() == ID_TH)
523                         tsection = current->parent()->parent();
524                     NodeImpl* table = tsection->parent();
525                     int exceptioncode = 0;
526                     table->insertBefore(n, tsection, exceptioncode);
527                     pushBlock(id, tagPriority(id));
528                     setCurrent(n);
529                     inStrayTableContent++;
530                     blockStack->strayTableContent = true;
531                     return true;
532                 }
533                 default:
534                     break;
535             }
536             break;
537         }
538         case ID_THEAD:
539         case ID_TBODY:
540         case ID_TFOOT:
541         case ID_COLGROUP: {
542             if (isTableRelatedTag(current->id())) {
543                 while (blockStack && current->id() != ID_TABLE && isTableRelatedTag(current->id()))
544                     popOneBlock();
545                 return insertNode(n);
546             }
547         }
548         default:
549             break;
550         }
551
552         // switch on the currently active element
553         switch(current->id())
554         {
555         case ID_HTML:
556             switch(id)
557             {
558             case ID_SCRIPT:
559             case ID_STYLE:
560             case ID_META:
561             case ID_LINK:
562             case ID_OBJECT:
563             case ID_EMBED:
564             case ID_TITLE:
565             case ID_ISINDEX:
566             case ID_BASE:
567                 if(!head) {
568                     head = new HTMLHeadElementImpl(document);
569                     e = head;
570                     insertNode(e);
571                     handled = true;
572                 }
573                 break;
574             case ID_TEXT: {
575                 TextImpl *t = static_cast<TextImpl *>(n);
576                 if (t->containsOnlyWhitespace())
577                     return false;
578                 /* Fall through to default */
579             }
580             default:
581                 if ( haveFrameSet ) break;
582                 e = new HTMLBodyElementImpl(document);
583                 startBody();
584                 insertNode(e);
585                 handled = true;
586                 break;
587             }
588             break;
589         case ID_HEAD:
590             // we can get here only if the element is not allowed in head.
591             if (id == ID_HTML)
592                 return false;
593             else {
594                 // This means the body starts here...
595                 if ( haveFrameSet ) break;
596                 popBlock(ID_HEAD);
597                 e = new HTMLBodyElementImpl(document);
598                 startBody();
599                 insertNode(e);
600                 handled = true;
601             }
602             break;
603         case ID_BODY:
604             break;
605         case ID_CAPTION:
606             // Illegal content in a caption. Close the caption and try again.
607             popBlock(ID_CAPTION);
608             switch( id ) {
609             case ID_THEAD:
610             case ID_TFOOT:
611             case ID_TBODY:
612             case ID_TR:
613             case ID_TD:
614             case ID_TH:
615                 return insertNode(n, flat);
616             }
617             break;
618         case ID_TABLE:
619         case ID_THEAD:
620         case ID_TFOOT:
621         case ID_TBODY:
622         case ID_TR:
623             switch(id)
624             {
625             case ID_TABLE:
626                 popBlock(ID_TABLE); // end the table
627                 handled = true;      // ...and start a new one
628                 break;
629             case ID_TEXT:
630             {
631                 TextImpl *t = static_cast<TextImpl *>(n);
632                 if (t->containsOnlyWhitespace())
633                     return false;
634                 DOMStringImpl *i = t->string();
635                 unsigned int pos = 0;
636                 while(pos < i->l && ( *(i->s+pos) == QChar(' ') ||
637                                       *(i->s+pos) == QChar(0xa0))) pos++;
638                 if(pos == i->l)
639                     break;
640             }
641             default:
642             {
643                 NodeImpl *node = current;
644                 NodeImpl *parent = node->parentNode();
645
646                 NodeImpl *parentparent = parent->parentNode();
647
648                 if (n->isTextNode() ||
649                     ( node->id() == ID_TR &&
650                      ( parent->id() == ID_THEAD ||
651                       parent->id() == ID_TBODY ||
652                       parent->id() == ID_TFOOT ) && parentparent->id() == ID_TABLE ) ||
653                     ( !checkChild(ID_TR, id, !doc()->inCompatMode()) && ( node->id() == ID_THEAD || node->id() == ID_TBODY || node->id() == ID_TFOOT ) &&
654                      parent->id() == ID_TABLE ))
655                 {
656                     node = (node->id() == ID_TABLE) ? node :
657                             ((node->id() == ID_TR) ? parentparent : parent);
658                     NodeImpl *parent = node->parentNode();
659                     int exceptioncode = 0;
660                     parent->insertBefore( n, node, exceptioncode );
661                     if ( exceptioncode ) {
662 #ifdef PARSER_DEBUG
663                         kdDebug( 6035 ) << "adding content before table failed!" << endl;
664 #endif
665                         break;
666                     }
667                     if (n->isElementNode() && tagPriority(id) != 0 && 
668                         !flat && endTagRequirement(id) != DOM::FORBIDDEN)
669                     {
670                         pushBlock(id, tagPriority(id));
671                         setCurrent(n);
672                         inStrayTableContent++;
673                         blockStack->strayTableContent = true;
674                     }
675                     return true;
676                 }
677
678                 if ( current->id() == ID_TR )
679                     e = new HTMLTableCellElementImpl(document, ID_TD);
680                 else if ( current->id() == ID_TABLE )
681                     e = new HTMLTableSectionElementImpl( document, ID_TBODY, true /* implicit */ );
682                 else
683                     e = new HTMLTableRowElementImpl( document );
684                 
685                 insertNode(e);
686                 handled = true;
687                 break;
688             } // end default
689             } // end switch
690             break;
691         case ID_OBJECT:
692             discard_until = ID_OBJECT + ID_CLOSE_TAG;
693             return false;
694         case ID_UL:
695         case ID_OL:
696         case ID_DIR:
697         case ID_MENU:
698             e = new HTMLDivElementImpl(document);
699             insertNode(e);
700             handled = true;
701             break;
702             case ID_DL:
703                 popBlock(ID_DL);
704                 handled = true;
705                 break;
706             case ID_DT:
707                 popBlock(ID_DT);
708                 handled = true;
709                 break;
710         case ID_SELECT:
711             if( n->isInline() )
712                 return false;
713             break;
714         case ID_P:
715         case ID_H1:
716         case ID_H2:
717         case ID_H3:
718         case ID_H4:
719         case ID_H5:
720         case ID_H6:
721             if(!n->isInline())
722             {
723                 popBlock(current->id());
724                 handled = true;
725             }
726             break;
727         case ID_OPTION:
728         case ID_OPTGROUP:
729             if (id == ID_OPTGROUP)
730             {
731                 popBlock(current->id());
732                 handled = true;
733             }
734             else if(id == ID_SELECT)
735             {
736                 // IE treats a nested select as </select>. Let's do the same
737                 popBlock( ID_SELECT );
738                 break;
739             }
740             break;
741             // head elements in the body should be ignored.
742         case ID_ADDRESS:
743             popBlock(ID_ADDRESS);
744             handled = true;
745             break;
746         case ID_COLGROUP:
747             if (id != ID_TEXT) {
748                 popBlock(ID_COLGROUP);
749                 handled = true;
750             }
751             break;
752         case ID_FONT:
753             popBlock(ID_FONT);
754             handled = true;
755             break;
756         default:
757             if(current->isDocumentNode())
758             {
759                 if(current->firstChild() == 0) {
760                     e = new HTMLHtmlElementImpl(document);
761                     insertNode(e);
762                     handled = true;
763                 }
764             }
765             else if(current->isInline())
766             {
767                 popInlineBlocks();
768                 handled = true;
769             }
770         }
771
772         // if we couldn't handle the error, just rethrow the exception...
773         if(!handled)
774         {
775             //kdDebug( 6035 ) << "Exception handler failed in HTMLPArser::insertNode()" << endl;
776             return false;
777         }
778
779         return insertNode(n);
780     }
781 }
782
783 NodeImpl *KHTMLParser::getElement(Token* t)
784 {
785     switch (t->id)
786     {
787     case ID_HEAD:
788         if (!head && current->id() == ID_HTML) {
789             head = new HTMLHeadElementImpl(document);
790             return head;
791         }
792         return 0;
793     case ID_BODY:
794         // body no longer allowed if we have a frameset
795         if (haveFrameSet)
796             return 0;
797         popBlock(ID_HEAD);
798         startBody();
799         return new HTMLBodyElementImpl(document);
800
801 // frames
802     case ID_FRAMESET:
803         popBlock(ID_HEAD);
804         if (inBody && !haveFrameSet && !haveContent) {
805             popBlock(ID_BODY);
806             // ### actually for IE document.body returns the now hidden "body" element
807             // we can't implement that behaviour now because it could cause too many
808             // regressions and the headaches are not worth the work as long as there is
809             // no site actually relying on that detail (Dirk)
810             if (doc()->body())
811                 doc()->body()->setAttribute(ATTR_STYLE, "display:none");
812             inBody = false;
813         }
814         if ((haveContent || haveFrameSet) && current->id() == ID_HTML)
815             return 0;
816         haveFrameSet = true;
817         startBody();
818         return new HTMLFrameSetElementImpl(document);
819
820     // a bit of a special case, since the frame is inlined
821     case ID_IFRAME:
822         discard_until = ID_IFRAME + ID_CLOSE_TAG;
823         break;
824
825 // form elements
826     case ID_FORM:
827         // Only create a new form if we're not already inside one.
828         // This is consistent with other browsers' behavior.
829         if (form)
830             return 0;
831         form = new HTMLFormElementImpl(document);
832         return form;
833     case ID_BUTTON:
834         return new HTMLButtonElementImpl(document, form);
835     case ID_FIELDSET:
836         return new HTMLFieldSetElementImpl(document, form);
837     case ID_INPUT:
838         return new HTMLInputElementImpl(document, form);
839     case ID_ISINDEX: {
840         NodeImpl *n = handleIsindex(t);
841         if (!inBody) {
842             if (isindex)
843                 isindex->deref();
844             isindex = n;
845             isindex->ref();
846             return 0;
847         }
848         t->flat = true;
849         return n;
850     }
851     case ID_KEYGEN:
852         return new HTMLKeygenElementImpl(document, form);
853     case ID_LEGEND:
854         return new HTMLLegendElementImpl(document, form);
855     case ID_OPTGROUP:
856         return new HTMLOptGroupElementImpl(document, form);
857     case ID_OPTION:
858         return new HTMLOptionElementImpl(document, form);
859     case ID_SELECT:
860         inSelect = true;
861         return new HTMLSelectElementImpl(document, form);
862     case ID_TEXTAREA:
863         return new HTMLTextAreaElementImpl(document, form);
864
865 // lists
866     case ID_DD:
867         popBlock(ID_DT);
868         popBlock(ID_DD);
869         break;
870     case ID_DT:
871         popBlock(ID_DD);
872         popBlock(ID_DT);
873         break;
874     case ID_LI:
875         popBlock(ID_LI);
876         break;
877
878 // anchor
879     case ID_A:
880         // Never allow nested <a>s.
881         popBlock(ID_A);
882         break;
883
884 // images
885     case ID_IMG:
886         return new HTMLImageElementImpl(document, form);
887     case ID_MAP:
888         map = new HTMLMapElementImpl(document);
889         return map;
890
891 // tables
892     case ID_TR:
893         popBlock(ID_TR);
894         break;
895     case ID_TD:
896     case ID_TH:
897         popBlock(ID_TH);
898         popBlock(ID_TD);
899         break;
900     case ID_TBODY:
901     case ID_THEAD:
902     case ID_TFOOT:
903         popBlock(ID_THEAD);
904         popBlock(ID_TBODY);
905         popBlock(ID_TFOOT);
906         break;
907
908 // elements with no special representation in the DOM
909     case ID_TT:
910     case ID_U:
911     case ID_B:
912     case ID_I:
913     case ID_S:
914     case ID_STRIKE:
915     case ID_BIG:
916     case ID_SMALL:
917         if (!allowNestedRedundantTag(t->id))
918             return 0;
919         break;
920
921     case ID_NOBR:
922     case ID_WBR:
923         popBlock(t->id); // Don't allow nested <nobr> or <wbr>
924         break;
925
926 // these are special, and normally not rendered
927     case ID_NOEMBED:
928         discard_until = ID_NOEMBED + ID_CLOSE_TAG;
929         return 0;
930     case ID_NOFRAMES:
931         discard_until = ID_NOFRAMES + ID_CLOSE_TAG;
932         return 0;
933     case ID_NOSCRIPT:
934         if (HTMLWidget && HTMLWidget->part()->jScriptEnabled())
935             discard_until = ID_NOSCRIPT + ID_CLOSE_TAG;
936         return 0;
937     case ID_NOLAYER:
938         //discard_until = ID_NOLAYER + ID_CLOSE_TAG;
939         return 0;
940     case ID_TEXT:
941         return new TextImpl(document, t->text);
942     case ID_COMMENT:
943         if (!includesCommentsInDOM)
944             return 0;
945         break;
946
947     case ID_SCRIPT:
948         {
949             HTMLScriptElementImpl *scriptElement = new HTMLScriptElementImpl(document);
950             scriptElement->setCreatedByParser(true);
951             return scriptElement;
952         }
953     }
954
955     return document->document()->createHTMLElement(t->id);
956 }
957
958 #define MAX_REDUNDANT 20
959
960 bool KHTMLParser::allowNestedRedundantTag(int _id)
961 {
962     // www.liceo.edu.mx is an example of a site that achieves a level of nesting of
963     // about 1500 tags, all from a bunch of <b>s.  We will only allow at most 20
964     // nested tags of the same type before just ignoring them all together.
965     int i = 0;
966     for (HTMLStackElem* curr = blockStack;
967          i < MAX_REDUNDANT && curr && curr->id == _id;
968          curr = curr->next, i++);
969     return i != MAX_REDUNDANT;
970 }
971
972 void KHTMLParser::processCloseTag(Token *t)
973 {
974     // support for really broken html. Can't believe I'm supporting such crap (lars)
975     switch(t->id)
976     {
977     case ID_HTML+ID_CLOSE_TAG:
978     case ID_BODY+ID_CLOSE_TAG:
979         // we never close the body tag, since some stupid web pages close it before the actual end of the doc.
980         // let's rely on the end() call to close things.
981         return;
982     case ID_FORM+ID_CLOSE_TAG:
983         form = 0;
984         // this one is to get the right style on the body element
985         break;
986     case ID_MAP+ID_CLOSE_TAG:
987         map = 0;
988         break;
989     case ID_SELECT+ID_CLOSE_TAG:
990         inSelect = false;
991         break;
992     default:
993         break;
994     }
995
996 #ifdef PARSER_DEBUG
997     kdDebug( 6035 ) << "added the following childs to " << current->nodeName().string() << endl;
998     NodeImpl *child = current->firstChild();
999     while(child != 0)
1000     {
1001         kdDebug( 6035 ) << "    " << child->nodeName().string() << endl;
1002         child = child->nextSibling();
1003     }
1004 #endif
1005     HTMLStackElem* oldElem = blockStack;
1006     popBlock(t->id-ID_CLOSE_TAG);
1007     if (oldElem == blockStack && t->id == ID_P+ID_CLOSE_TAG) {
1008         // We encountered a stray </p>.  Amazingly Gecko, WinIE, and MacIE all treat
1009         // this as a valid break, i.e., <p></p>.  So go ahead and make the empty
1010         // paragraph.
1011         t->id-=ID_CLOSE_TAG;
1012         parseToken(t);
1013         popBlock(ID_P);
1014     }
1015 #ifdef PARSER_DEBUG
1016     kdDebug( 6035 ) << "closeTag --> current = " << current->nodeName().string() << endl;
1017 #endif
1018 }
1019
1020 bool KHTMLParser::isHeaderTag(int _id)
1021 {
1022     switch (_id) {
1023         case ID_H1:
1024         case ID_H2:
1025         case ID_H3:
1026         case ID_H4:
1027         case ID_H5:
1028         case ID_H6:
1029             return true;
1030         default:
1031             return false;
1032     }
1033 }
1034
1035 void KHTMLParser::popNestedHeaderTag()
1036 {
1037     // This function only cares about checking for nested headers that have only inlines in between them.
1038     NodeImpl* currNode = current;
1039     for (HTMLStackElem* curr = blockStack; curr; curr = curr->next) {
1040         if (isHeaderTag(curr->id)) {
1041             popBlock(curr->id);
1042             return;
1043         }
1044         if (currNode && !currNode->isInline())
1045             return;
1046         currNode = curr->node;
1047     }
1048 }
1049
1050 bool KHTMLParser::isResidualStyleTag(int _id)
1051 {
1052     switch (_id) {
1053         case ID_A:
1054         case ID_FONT:
1055         case ID_TT:
1056         case ID_U:
1057         case ID_B:
1058         case ID_I:
1059         case ID_S:
1060         case ID_STRIKE:
1061         case ID_BIG:
1062         case ID_SMALL:
1063         case ID_EM:
1064         case ID_STRONG:
1065         case ID_DFN:
1066         case ID_CODE:
1067         case ID_SAMP:
1068         case ID_KBD:
1069         case ID_VAR:
1070             return true;
1071         default:
1072             return false;
1073     }
1074 }
1075
1076 bool KHTMLParser::isAffectedByResidualStyle(int _id)
1077 {
1078     if (isResidualStyleTag(_id))
1079         return true;
1080     
1081     switch (_id) {
1082         case ID_P:
1083         case ID_DIV:
1084         case ID_BLOCKQUOTE:
1085         case ID_ADDRESS:
1086         case ID_H1:
1087         case ID_H2:
1088         case ID_H3:
1089         case ID_H4:
1090         case ID_H5:
1091         case ID_H6:
1092         case ID_CENTER:
1093         case ID_UL:
1094         case ID_OL:
1095         case ID_LI:
1096         case ID_DL:
1097         case ID_DT:
1098         case ID_DD:
1099         case ID_PRE:
1100             return true;
1101         default:
1102             return false;
1103     }
1104 }
1105
1106 void KHTMLParser::handleResidualStyleCloseTagAcrossBlocks(HTMLStackElem* elem)
1107 {
1108     // Find the element that crosses over to a higher level.   For now, if there is more than
1109     // one, we will just give up and not attempt any sort of correction.  It's highly unlikely that
1110     // there will be more than one, since <p> tags aren't allowed to be nested.
1111     int exceptionCode = 0;
1112     HTMLStackElem* curr = blockStack;
1113     HTMLStackElem* maxElem = 0;
1114     HTMLStackElem* prev = 0;
1115     HTMLStackElem* prevMaxElem = 0;
1116     while (curr && curr != elem) {
1117         if (curr->level > elem->level) {
1118             if (maxElem)
1119                 return;
1120             maxElem = curr;
1121             prevMaxElem = prev;
1122         }
1123
1124         prev = curr;
1125         curr = curr->next;
1126     }
1127
1128     if (!curr || !maxElem || !isAffectedByResidualStyle(maxElem->id)) return;
1129
1130     NodeImpl* residualElem = prev->node;
1131     NodeImpl* blockElem = prevMaxElem ? prevMaxElem->node : current;
1132     NodeImpl* parentElem = elem->node;
1133
1134     // Check to see if the reparenting that is going to occur is allowed according to the DOM.
1135     // FIXME: We should either always allow it or perform an additional fixup instead of
1136     // just bailing here.
1137     // Example: <p><font><center>blah</font></center></p> isn't doing a fixup right now.
1138     if (!parentElem->childAllowed(blockElem))
1139         return;
1140     
1141     if (maxElem->node->parentNode() != elem->node) {
1142         // Walk the stack and remove any elements that aren't residual style tags.  These
1143         // are basically just being closed up.  Example:
1144         // <font><span>Moo<p>Goo</font></p>.
1145         // In the above example, the <span> doesn't need to be reopened.  It can just close.
1146         HTMLStackElem* currElem = maxElem->next;
1147         HTMLStackElem* prevElem = maxElem;
1148         while (currElem != elem) {
1149             HTMLStackElem* nextElem = currElem->next;
1150             if (!isResidualStyleTag(currElem->id)) {
1151                 prevElem->next = nextElem;
1152                 prevElem->node = currElem->node;
1153                 delete currElem;
1154             }
1155             else
1156                 prevElem = currElem;
1157             currElem = nextElem;
1158         }
1159         
1160         // We have to reopen residual tags in between maxElem and elem.  An example of this case is:
1161         // <font><i>Moo<p>Foo</font>.
1162         // In this case, we need to transform the part before the <p> into:
1163         // <font><i>Moo</i></font><i>
1164         // so that the <i> will remain open.  This involves the modification of elements
1165         // in the block stack.
1166         // This will also affect how we ultimately reparent the block, since we want it to end up
1167         // under the reopened residual tags (e.g., the <i> in the above example.)
1168         NodeImpl* prevNode = 0;
1169         NodeImpl* currNode = 0;
1170         currElem = maxElem;
1171         while (currElem->node != residualElem) {
1172             if (isResidualStyleTag(currElem->node->id())) {
1173                 // Create a clone of this element.
1174                 currNode = currElem->node->cloneNode(false);
1175
1176                 // Change the stack element's node to point to the clone.
1177                 currElem->node = currNode;
1178                 
1179                 // Attach the previous node as a child of this new node.
1180                 if (prevNode)
1181                     currNode->appendChild(prevNode, exceptionCode);
1182                 else // The new parent for the block element is going to be the innermost clone.
1183                     parentElem = currNode;
1184                                 
1185                 prevNode = currNode;
1186             }
1187             
1188             currElem = currElem->next;
1189         }
1190
1191         // Now append the chain of new residual style elements if one exists.
1192         if (prevNode)
1193             elem->node->appendChild(prevNode, exceptionCode);
1194     }
1195          
1196     // We need to make a clone of |residualElem| and place it just inside |blockElem|.
1197     // All content of |blockElem| is reparented to be under this clone.  We then
1198     // reparent |blockElem| using real DOM calls so that attachment/detachment will
1199     // be performed to fix up the rendering tree.
1200     // So for this example: <b>...<p>Foo</b>Goo</p>
1201     // The end result will be: <b>...</b><p><b>Foo</b>Goo</p>
1202     //
1203     // Step 1: Remove |blockElem| from its parent, doing a batch detach of all the kids.
1204     blockElem->parentNode()->removeChild(blockElem, exceptionCode);
1205         
1206     // Step 2: Clone |residualElem|.
1207     NodeImpl* newNode = residualElem->cloneNode(false); // Shallow clone. We don't pick up the same kids.
1208
1209     // Step 3: Place |blockElem|'s children under |newNode|.  Remove all of the children of |blockElem|
1210     // before we've put |newElem| into the document.  That way we'll only do one attachment of all
1211     // the new content (instead of a bunch of individual attachments).
1212     NodeImpl* currNode = blockElem->firstChild();
1213     while (currNode) {
1214         NodeImpl* nextNode = currNode->nextSibling();
1215         blockElem->removeChild(currNode, exceptionCode);
1216         newNode->appendChild(currNode, exceptionCode);
1217         currNode = nextNode;
1218     }
1219
1220     // Step 4: Place |newNode| under |blockElem|.  |blockElem| is still out of the document, so no
1221     // attachment can occur yet.
1222     blockElem->appendChild(newNode, exceptionCode);
1223     
1224     // Step 5: Reparent |blockElem|.  Now the full attachment of the fixed up tree takes place.
1225     parentElem->appendChild(blockElem, exceptionCode);
1226         
1227     // Step 6: Elide |elem|, since it is effectively no longer open.  Also update
1228     // the node associated with the previous stack element so that when it gets popped,
1229     // it doesn't make the residual element the next current node.
1230     HTMLStackElem* currElem = maxElem;
1231     HTMLStackElem* prevElem = 0;
1232     while (currElem != elem) {
1233         prevElem = currElem;
1234         currElem = currElem->next;
1235     }
1236     prevElem->next = elem->next;
1237     prevElem->node = elem->node;
1238     delete elem;
1239     
1240     // Step 7: Reopen intermediate inlines, e.g., <b><p><i>Foo</b>Goo</p>.
1241     // In the above example, Goo should stay italic.
1242     curr = blockStack;
1243     HTMLStackElem* residualStyleStack = 0;
1244     while (curr && curr != maxElem) {
1245         // We will actually schedule this tag for reopening
1246         // after we complete the close of this entire block.
1247         NodeImpl* currNode = current;
1248         if (isResidualStyleTag(curr->id)) {
1249             // We've overloaded the use of stack elements and are just reusing the
1250             // struct with a slightly different meaning to the variables.  Instead of chaining
1251             // from innermost to outermost, we build up a list of all the tags we need to reopen
1252             // from the outermost to the innermost, i.e., residualStyleStack will end up pointing
1253             // to the outermost tag we need to reopen.
1254             // We also set curr->node to be the actual element that corresponds to the ID stored in
1255             // curr->id rather than the node that you should pop to when the element gets pulled off
1256             // the stack.
1257             popOneBlock(false);
1258             curr->node = currNode;
1259             curr->next = residualStyleStack;
1260             residualStyleStack = curr;
1261         }
1262         else
1263             popOneBlock();
1264
1265         curr = blockStack;
1266     }
1267
1268     reopenResidualStyleTags(residualStyleStack, 0); // FIXME: Deal with stray table content some day
1269                                                     // if it becomes necessary to do so.
1270 }
1271
1272 void KHTMLParser::reopenResidualStyleTags(HTMLStackElem* elem, DOM::NodeImpl* malformedTableParent)
1273 {
1274     // Loop for each tag that needs to be reopened.
1275     while (elem) {
1276         // Create a shallow clone of the DOM node for this element.
1277         NodeImpl* newNode = elem->node->cloneNode(false); 
1278
1279         // Append the new node. In the malformed table case, we need to insert before the table,
1280         // which will be the last child.
1281         int exceptionCode = 0;
1282         if (malformedTableParent)
1283             malformedTableParent->insertBefore(newNode, malformedTableParent->lastChild(), exceptionCode);
1284         else
1285             current->appendChild(newNode, exceptionCode);
1286         // FIXME: Is it really OK to ignore the exceptions here?
1287
1288         // Now push a new stack element for this node we just created.
1289         pushBlock(elem->id, elem->level);
1290
1291         // Set our strayTableContent boolean if needed, so that the reopened tag also knows
1292         // that it is inside a malformed table.
1293         blockStack->strayTableContent = malformedTableParent != 0;
1294         if (blockStack->strayTableContent)
1295             inStrayTableContent++;
1296
1297         // Clear our malformed table parent variable.
1298         malformedTableParent = 0;
1299
1300         // Update |current| manually to point to the new node.
1301         setCurrent(newNode);
1302         
1303         // Advance to the next tag that needs to be reopened.
1304         HTMLStackElem* next = elem->next;
1305         delete elem;
1306         elem = next;
1307     }
1308 }
1309
1310 void KHTMLParser::pushBlock(int _id, int _level)
1311 {
1312     HTMLStackElem *Elem = new HTMLStackElem(_id, _level, current, blockStack);
1313
1314     blockStack = Elem;
1315     addForbidden(_id, forbiddenTag);
1316 }
1317
1318 void KHTMLParser::popBlock( int _id )
1319 {
1320     HTMLStackElem *Elem = blockStack;
1321     
1322     int maxLevel = 0;
1323
1324 #ifdef PARSER_DEBUG
1325     kdDebug( 6035 ) << "popBlock(" << getTagName(_id).string() << ")" << endl;
1326     while(Elem) {
1327         kdDebug( 6035) << "   > " << getTagName(Elem->id).string() << endl;
1328         Elem = Elem->next;
1329     }
1330     Elem = blockStack;
1331 #endif
1332
1333     while( Elem && (Elem->id != _id))
1334     {
1335         if (maxLevel < Elem->level)
1336         {
1337             maxLevel = Elem->level;
1338         }
1339         Elem = Elem->next;
1340     }
1341
1342     if (!Elem)
1343         return;
1344
1345     if (maxLevel > Elem->level) {
1346         // We didn't match because the tag is in a different scope, e.g.,
1347         // <b><p>Foo</b>.  Try to correct the problem.
1348         if (!isResidualStyleTag(_id))
1349             return;
1350         return handleResidualStyleCloseTagAcrossBlocks(Elem);
1351     }
1352
1353     bool isAffectedByStyle = isAffectedByResidualStyle(Elem->id);
1354     HTMLStackElem* residualStyleStack = 0;
1355     NodeImpl* malformedTableParent = 0;
1356     
1357     Elem = blockStack;
1358     while (Elem)
1359     {
1360         if (Elem->id == _id)
1361         {
1362             int strayTable = inStrayTableContent;
1363             popOneBlock();
1364             Elem = 0;
1365
1366             // This element was the root of some malformed content just inside an implicit or
1367             // explicit <tbody> or <tr>.
1368             // If we end up needing to reopen residual style tags, the root of the reopened chain
1369             // must also know that it is the root of malformed content inside a <tbody>/<tr>.
1370             if (strayTable && (inStrayTableContent < strayTable) && residualStyleStack) {
1371                 NodeImpl* curr = current;
1372                 while (curr && curr->id() != ID_TABLE)
1373                     curr = curr->parentNode();
1374                 malformedTableParent = curr ? curr->parentNode() : 0;
1375             }
1376         }
1377         else
1378         {
1379             if (Elem->id == ID_FORM && form)
1380                 // A <form> is being closed prematurely (and this is
1381                 // malformed HTML).  Set an attribute on the form to clear out its
1382                 // bottom margin.
1383                 form->setMalformed(true);
1384
1385             // Schedule this tag for reopening
1386             // after we complete the close of this entire block.
1387             NodeImpl* currNode = current;
1388             if (isAffectedByStyle && isResidualStyleTag(Elem->id)) {
1389                 // We've overloaded the use of stack elements and are just reusing the
1390                 // struct with a slightly different meaning to the variables.  Instead of chaining
1391                 // from innermost to outermost, we build up a list of all the tags we need to reopen
1392                 // from the outermost to the innermost, i.e., residualStyleStack will end up pointing
1393                 // to the outermost tag we need to reopen.
1394                 // We also set Elem->node to be the actual element that corresponds to the ID stored in
1395                 // Elem->id rather than the node that you should pop to when the element gets pulled off
1396                 // the stack.
1397                 popOneBlock(false);
1398                 Elem->next = residualStyleStack;
1399                 Elem->node = currNode;
1400                 residualStyleStack = Elem;
1401             }
1402             else
1403                 popOneBlock();
1404             Elem = blockStack;
1405         }
1406     }
1407
1408     reopenResidualStyleTags(residualStyleStack, malformedTableParent);
1409 }
1410
1411 void KHTMLParser::popOneBlock(bool delBlock)
1412 {
1413     HTMLStackElem *Elem = blockStack;
1414
1415     // we should never get here, but some bad html might cause it.
1416 #ifndef PARSER_DEBUG
1417     if(!Elem) return;
1418 #else
1419     kdDebug( 6035 ) << "popping block: " << getTagName(Elem->id).string() << "(" << Elem->id << ")" << endl;
1420 #endif
1421
1422 #if SPEED_DEBUG < 1
1423     if((Elem->node != current)) {
1424         if (current->maintainsState() && doc()){
1425             doc()->registerMaintainsState(current);
1426             QStringList &states = doc()->restoreState();
1427             if (!states.isEmpty())
1428                 current->restoreState(states);
1429         }
1430         
1431         // A few elements (<applet>, <object>) need to know when all child elements (<param>s) are available:
1432         current->closeRenderer();
1433     }
1434 #endif
1435
1436     removeForbidden(Elem->id, forbiddenTag);
1437
1438     blockStack = Elem->next;
1439     setCurrent(Elem->node);
1440
1441     if (Elem->strayTableContent)
1442         inStrayTableContent--;
1443     
1444     if (delBlock)
1445         delete Elem;
1446 }
1447
1448 void KHTMLParser::popInlineBlocks()
1449 {
1450     while (blockStack && current->isInline())
1451         popOneBlock();
1452 }
1453
1454 void KHTMLParser::freeBlock()
1455 {
1456     while (blockStack)
1457         popOneBlock();
1458 }
1459
1460 void KHTMLParser::createHead()
1461 {
1462     if(head || !doc()->firstChild())
1463         return;
1464
1465     head = new HTMLHeadElementImpl(document);
1466     HTMLElementImpl *body = doc()->body();
1467     int exceptioncode = 0;
1468     doc()->firstChild()->insertBefore(head, body, exceptioncode);
1469     if ( exceptioncode ) {
1470 #ifdef PARSER_DEBUG
1471         kdDebug( 6035 ) << "creation of head failed!!!!" << endl;
1472 #endif
1473         head = 0;
1474     }
1475 }
1476
1477 NodeImpl *KHTMLParser::handleIsindex( Token *t )
1478 {
1479     NodeImpl *n;
1480     HTMLFormElementImpl *myform = form;
1481     if ( !myform ) {
1482         myform = new HTMLFormElementImpl(document);
1483         n = myform;
1484     } else
1485         n = new HTMLDivElementImpl( document );
1486     NodeImpl *child = new HTMLHRElementImpl( document );
1487     n->addChild( child );
1488     AttributeImpl* a = t->attrs ? t->attrs->getAttributeItem(ATTR_PROMPT) : 0;
1489 #if APPLE_CHANGES
1490     DOMString text = searchableIndexIntroduction();
1491 #else
1492     DOMString text = i18n("This is a searchable index. Enter search keywords: ");
1493 #endif
1494     if (a)
1495         text = DOMString(a->value()) + " ";
1496     child = new TextImpl(document, text);
1497     n->addChild( child );
1498     child = new HTMLIsIndexElementImpl(document, myform);
1499     static_cast<ElementImpl *>(child)->setAttribute(ATTR_TYPE, "khtml_isindex");
1500     n->addChild( child );
1501     child = new HTMLHRElementImpl( document );
1502     n->addChild( child );
1503
1504     return n;
1505 }
1506
1507 void KHTMLParser::startBody()
1508 {
1509     if(inBody) return;
1510
1511     inBody = true;
1512
1513     if( isindex ) {
1514         insertNode( isindex, true /* don't decend into this node */ );
1515         isindex = 0;
1516     }
1517 }
1518
1519 void KHTMLParser::finished()
1520 {
1521     // In the case of a completely empty document, here's the place to create the HTML element.
1522     if (current->isDocumentNode() && current->firstChild() == 0) {
1523         insertNode(new HTMLHtmlElementImpl(document));
1524     }
1525
1526     // This ensures that "current" is not left pointing to a node when the document is destroyed.
1527     freeBlock();
1528     setCurrent(0);
1529 }