896af996b1c401ae7d0a2fb965d14c3154530427
[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
948     return document->document()->createHTMLElement(t->id);
949 }
950
951 #define MAX_REDUNDANT 20
952
953 bool KHTMLParser::allowNestedRedundantTag(int _id)
954 {
955     // www.liceo.edu.mx is an example of a site that achieves a level of nesting of
956     // about 1500 tags, all from a bunch of <b>s.  We will only allow at most 20
957     // nested tags of the same type before just ignoring them all together.
958     int i = 0;
959     for (HTMLStackElem* curr = blockStack;
960          i < MAX_REDUNDANT && curr && curr->id == _id;
961          curr = curr->next, i++);
962     return i != MAX_REDUNDANT;
963 }
964
965 void KHTMLParser::processCloseTag(Token *t)
966 {
967     // support for really broken html. Can't believe I'm supporting such crap (lars)
968     switch(t->id)
969     {
970     case ID_HTML+ID_CLOSE_TAG:
971     case ID_BODY+ID_CLOSE_TAG:
972         // we never close the body tag, since some stupid web pages close it before the actual end of the doc.
973         // let's rely on the end() call to close things.
974         return;
975     case ID_FORM+ID_CLOSE_TAG:
976         form = 0;
977         // this one is to get the right style on the body element
978         break;
979     case ID_MAP+ID_CLOSE_TAG:
980         map = 0;
981         break;
982     case ID_SELECT+ID_CLOSE_TAG:
983         inSelect = false;
984         break;
985     default:
986         break;
987     }
988
989 #ifdef PARSER_DEBUG
990     kdDebug( 6035 ) << "added the following childs to " << current->nodeName().string() << endl;
991     NodeImpl *child = current->firstChild();
992     while(child != 0)
993     {
994         kdDebug( 6035 ) << "    " << child->nodeName().string() << endl;
995         child = child->nextSibling();
996     }
997 #endif
998     HTMLStackElem* oldElem = blockStack;
999     popBlock(t->id-ID_CLOSE_TAG);
1000     if (oldElem == blockStack && t->id == ID_P+ID_CLOSE_TAG) {
1001         // We encountered a stray </p>.  Amazingly Gecko, WinIE, and MacIE all treat
1002         // this as a valid break, i.e., <p></p>.  So go ahead and make the empty
1003         // paragraph.
1004         t->id-=ID_CLOSE_TAG;
1005         parseToken(t);
1006         popBlock(ID_P);
1007     }
1008 #ifdef PARSER_DEBUG
1009     kdDebug( 6035 ) << "closeTag --> current = " << current->nodeName().string() << endl;
1010 #endif
1011 }
1012
1013 bool KHTMLParser::isHeaderTag(int _id)
1014 {
1015     switch (_id) {
1016         case ID_H1:
1017         case ID_H2:
1018         case ID_H3:
1019         case ID_H4:
1020         case ID_H5:
1021         case ID_H6:
1022             return true;
1023         default:
1024             return false;
1025     }
1026 }
1027
1028 void KHTMLParser::popNestedHeaderTag()
1029 {
1030     // This function only cares about checking for nested headers that have only inlines in between them.
1031     NodeImpl* currNode = current;
1032     for (HTMLStackElem* curr = blockStack; curr; curr = curr->next) {
1033         if (isHeaderTag(curr->id)) {
1034             popBlock(curr->id);
1035             return;
1036         }
1037         if (currNode && !currNode->isInline())
1038             return;
1039         currNode = curr->node;
1040     }
1041 }
1042
1043 bool KHTMLParser::isResidualStyleTag(int _id)
1044 {
1045     switch (_id) {
1046         case ID_A:
1047         case ID_FONT:
1048         case ID_TT:
1049         case ID_U:
1050         case ID_B:
1051         case ID_I:
1052         case ID_S:
1053         case ID_STRIKE:
1054         case ID_BIG:
1055         case ID_SMALL:
1056         case ID_EM:
1057         case ID_STRONG:
1058         case ID_DFN:
1059         case ID_CODE:
1060         case ID_SAMP:
1061         case ID_KBD:
1062         case ID_VAR:
1063             return true;
1064         default:
1065             return false;
1066     }
1067 }
1068
1069 bool KHTMLParser::isAffectedByResidualStyle(int _id)
1070 {
1071     if (isResidualStyleTag(_id))
1072         return true;
1073     
1074     switch (_id) {
1075         case ID_P:
1076         case ID_DIV:
1077         case ID_BLOCKQUOTE:
1078         case ID_ADDRESS:
1079         case ID_H1:
1080         case ID_H2:
1081         case ID_H3:
1082         case ID_H4:
1083         case ID_H5:
1084         case ID_H6:
1085         case ID_CENTER:
1086         case ID_UL:
1087         case ID_OL:
1088         case ID_LI:
1089         case ID_DL:
1090         case ID_DT:
1091         case ID_DD:
1092         case ID_PRE:
1093             return true;
1094         default:
1095             return false;
1096     }
1097 }
1098
1099 void KHTMLParser::handleResidualStyleCloseTagAcrossBlocks(HTMLStackElem* elem)
1100 {
1101     // Find the element that crosses over to a higher level.   For now, if there is more than
1102     // one, we will just give up and not attempt any sort of correction.  It's highly unlikely that
1103     // there will be more than one, since <p> tags aren't allowed to be nested.
1104     int exceptionCode = 0;
1105     HTMLStackElem* curr = blockStack;
1106     HTMLStackElem* maxElem = 0;
1107     HTMLStackElem* prev = 0;
1108     HTMLStackElem* prevMaxElem = 0;
1109     while (curr && curr != elem) {
1110         if (curr->level > elem->level) {
1111             if (maxElem)
1112                 return;
1113             maxElem = curr;
1114             prevMaxElem = prev;
1115         }
1116
1117         prev = curr;
1118         curr = curr->next;
1119     }
1120
1121     if (!curr || !maxElem || !isAffectedByResidualStyle(maxElem->id)) return;
1122
1123     NodeImpl* residualElem = prev->node;
1124     NodeImpl* blockElem = prevMaxElem ? prevMaxElem->node : current;
1125     NodeImpl* parentElem = elem->node;
1126
1127     // Check to see if the reparenting that is going to occur is allowed according to the DOM.
1128     // FIXME: We should either always allow it or perform an additional fixup instead of
1129     // just bailing here.
1130     // Example: <p><font><center>blah</font></center></p> isn't doing a fixup right now.
1131     if (!parentElem->childAllowed(blockElem))
1132         return;
1133     
1134     if (maxElem->node->parentNode() != elem->node) {
1135         // Walk the stack and remove any elements that aren't residual style tags.  These
1136         // are basically just being closed up.  Example:
1137         // <font><span>Moo<p>Goo</font></p>.
1138         // In the above example, the <span> doesn't need to be reopened.  It can just close.
1139         HTMLStackElem* currElem = maxElem->next;
1140         HTMLStackElem* prevElem = maxElem;
1141         while (currElem != elem) {
1142             HTMLStackElem* nextElem = currElem->next;
1143             if (!isResidualStyleTag(currElem->id)) {
1144                 prevElem->next = nextElem;
1145                 prevElem->node = currElem->node;
1146                 delete currElem;
1147             }
1148             else
1149                 prevElem = currElem;
1150             currElem = nextElem;
1151         }
1152         
1153         // We have to reopen residual tags in between maxElem and elem.  An example of this case is:
1154         // <font><i>Moo<p>Foo</font>.
1155         // In this case, we need to transform the part before the <p> into:
1156         // <font><i>Moo</i></font><i>
1157         // so that the <i> will remain open.  This involves the modification of elements
1158         // in the block stack.
1159         // This will also affect how we ultimately reparent the block, since we want it to end up
1160         // under the reopened residual tags (e.g., the <i> in the above example.)
1161         NodeImpl* prevNode = 0;
1162         NodeImpl* currNode = 0;
1163         currElem = maxElem;
1164         while (currElem->node != residualElem) {
1165             if (isResidualStyleTag(currElem->node->id())) {
1166                 // Create a clone of this element.
1167                 currNode = currElem->node->cloneNode(false);
1168
1169                 // Change the stack element's node to point to the clone.
1170                 currElem->node = currNode;
1171                 
1172                 // Attach the previous node as a child of this new node.
1173                 if (prevNode)
1174                     currNode->appendChild(prevNode, exceptionCode);
1175                 else // The new parent for the block element is going to be the innermost clone.
1176                     parentElem = currNode;
1177                                 
1178                 prevNode = currNode;
1179             }
1180             
1181             currElem = currElem->next;
1182         }
1183
1184         // Now append the chain of new residual style elements if one exists.
1185         if (prevNode)
1186             elem->node->appendChild(prevNode, exceptionCode);
1187     }
1188          
1189     // We need to make a clone of |residualElem| and place it just inside |blockElem|.
1190     // All content of |blockElem| is reparented to be under this clone.  We then
1191     // reparent |blockElem| using real DOM calls so that attachment/detachment will
1192     // be performed to fix up the rendering tree.
1193     // So for this example: <b>...<p>Foo</b>Goo</p>
1194     // The end result will be: <b>...</b><p><b>Foo</b>Goo</p>
1195     //
1196     // Step 1: Remove |blockElem| from its parent, doing a batch detach of all the kids.
1197     blockElem->parentNode()->removeChild(blockElem, exceptionCode);
1198         
1199     // Step 2: Clone |residualElem|.
1200     NodeImpl* newNode = residualElem->cloneNode(false); // Shallow clone. We don't pick up the same kids.
1201
1202     // Step 3: Place |blockElem|'s children under |newNode|.  Remove all of the children of |blockElem|
1203     // before we've put |newElem| into the document.  That way we'll only do one attachment of all
1204     // the new content (instead of a bunch of individual attachments).
1205     NodeImpl* currNode = blockElem->firstChild();
1206     while (currNode) {
1207         NodeImpl* nextNode = currNode->nextSibling();
1208         blockElem->removeChild(currNode, exceptionCode);
1209         newNode->appendChild(currNode, exceptionCode);
1210         currNode = nextNode;
1211     }
1212
1213     // Step 4: Place |newNode| under |blockElem|.  |blockElem| is still out of the document, so no
1214     // attachment can occur yet.
1215     blockElem->appendChild(newNode, exceptionCode);
1216     
1217     // Step 5: Reparent |blockElem|.  Now the full attachment of the fixed up tree takes place.
1218     parentElem->appendChild(blockElem, exceptionCode);
1219         
1220     // Step 6: Elide |elem|, since it is effectively no longer open.  Also update
1221     // the node associated with the previous stack element so that when it gets popped,
1222     // it doesn't make the residual element the next current node.
1223     HTMLStackElem* currElem = maxElem;
1224     HTMLStackElem* prevElem = 0;
1225     while (currElem != elem) {
1226         prevElem = currElem;
1227         currElem = currElem->next;
1228     }
1229     prevElem->next = elem->next;
1230     prevElem->node = elem->node;
1231     delete elem;
1232     
1233     // Step 7: Reopen intermediate inlines, e.g., <b><p><i>Foo</b>Goo</p>.
1234     // In the above example, Goo should stay italic.
1235     curr = blockStack;
1236     HTMLStackElem* residualStyleStack = 0;
1237     while (curr && curr != maxElem) {
1238         // We will actually schedule this tag for reopening
1239         // after we complete the close of this entire block.
1240         NodeImpl* currNode = current;
1241         if (isResidualStyleTag(curr->id)) {
1242             // We've overloaded the use of stack elements and are just reusing the
1243             // struct with a slightly different meaning to the variables.  Instead of chaining
1244             // from innermost to outermost, we build up a list of all the tags we need to reopen
1245             // from the outermost to the innermost, i.e., residualStyleStack will end up pointing
1246             // to the outermost tag we need to reopen.
1247             // We also set curr->node to be the actual element that corresponds to the ID stored in
1248             // curr->id rather than the node that you should pop to when the element gets pulled off
1249             // the stack.
1250             popOneBlock(false);
1251             curr->node = currNode;
1252             curr->next = residualStyleStack;
1253             residualStyleStack = curr;
1254         }
1255         else
1256             popOneBlock();
1257
1258         curr = blockStack;
1259     }
1260
1261     reopenResidualStyleTags(residualStyleStack, 0); // FIXME: Deal with stray table content some day
1262                                                     // if it becomes necessary to do so.
1263 }
1264
1265 void KHTMLParser::reopenResidualStyleTags(HTMLStackElem* elem, DOM::NodeImpl* malformedTableParent)
1266 {
1267     // Loop for each tag that needs to be reopened.
1268     while (elem) {
1269         // Create a shallow clone of the DOM node for this element.
1270         NodeImpl* newNode = elem->node->cloneNode(false); 
1271
1272         // Append the new node. In the malformed table case, we need to insert before the table,
1273         // which will be the last child.
1274         int exceptionCode = 0;
1275         if (malformedTableParent)
1276             malformedTableParent->insertBefore(newNode, malformedTableParent->lastChild(), exceptionCode);
1277         else
1278             current->appendChild(newNode, exceptionCode);
1279         // FIXME: Is it really OK to ignore the exceptions here?
1280
1281         // Now push a new stack element for this node we just created.
1282         pushBlock(elem->id, elem->level);
1283
1284         // Set our strayTableContent boolean if needed, so that the reopened tag also knows
1285         // that it is inside a malformed table.
1286         blockStack->strayTableContent = malformedTableParent != 0;
1287         if (blockStack->strayTableContent)
1288             inStrayTableContent++;
1289
1290         // Clear our malformed table parent variable.
1291         malformedTableParent = 0;
1292
1293         // Update |current| manually to point to the new node.
1294         setCurrent(newNode);
1295         
1296         // Advance to the next tag that needs to be reopened.
1297         HTMLStackElem* next = elem->next;
1298         delete elem;
1299         elem = next;
1300     }
1301 }
1302
1303 void KHTMLParser::pushBlock(int _id, int _level)
1304 {
1305     HTMLStackElem *Elem = new HTMLStackElem(_id, _level, current, blockStack);
1306
1307     blockStack = Elem;
1308     addForbidden(_id, forbiddenTag);
1309 }
1310
1311 void KHTMLParser::popBlock( int _id )
1312 {
1313     HTMLStackElem *Elem = blockStack;
1314     
1315     int maxLevel = 0;
1316
1317 #ifdef PARSER_DEBUG
1318     kdDebug( 6035 ) << "popBlock(" << getTagName(_id).string() << ")" << endl;
1319     while(Elem) {
1320         kdDebug( 6035) << "   > " << getTagName(Elem->id).string() << endl;
1321         Elem = Elem->next;
1322     }
1323     Elem = blockStack;
1324 #endif
1325
1326     while( Elem && (Elem->id != _id))
1327     {
1328         if (maxLevel < Elem->level)
1329         {
1330             maxLevel = Elem->level;
1331         }
1332         Elem = Elem->next;
1333     }
1334
1335     if (!Elem)
1336         return;
1337
1338     if (maxLevel > Elem->level) {
1339         // We didn't match because the tag is in a different scope, e.g.,
1340         // <b><p>Foo</b>.  Try to correct the problem.
1341         if (!isResidualStyleTag(_id))
1342             return;
1343         return handleResidualStyleCloseTagAcrossBlocks(Elem);
1344     }
1345
1346     bool isAffectedByStyle = isAffectedByResidualStyle(Elem->id);
1347     HTMLStackElem* residualStyleStack = 0;
1348     NodeImpl* malformedTableParent = 0;
1349     
1350     Elem = blockStack;
1351     while (Elem)
1352     {
1353         if (Elem->id == _id)
1354         {
1355             int strayTable = inStrayTableContent;
1356             popOneBlock();
1357             Elem = 0;
1358
1359             // This element was the root of some malformed content just inside an implicit or
1360             // explicit <tbody> or <tr>.
1361             // If we end up needing to reopen residual style tags, the root of the reopened chain
1362             // must also know that it is the root of malformed content inside a <tbody>/<tr>.
1363             if (strayTable && (inStrayTableContent < strayTable) && residualStyleStack) {
1364                 NodeImpl* curr = current;
1365                 while (curr && curr->id() != ID_TABLE)
1366                     curr = curr->parentNode();
1367                 malformedTableParent = curr ? curr->parentNode() : 0;
1368             }
1369         }
1370         else
1371         {
1372             if (Elem->id == ID_FORM && form)
1373                 // A <form> is being closed prematurely (and this is
1374                 // malformed HTML).  Set an attribute on the form to clear out its
1375                 // bottom margin.
1376                 form->setMalformed(true);
1377
1378             // Schedule this tag for reopening
1379             // after we complete the close of this entire block.
1380             NodeImpl* currNode = current;
1381             if (isAffectedByStyle && isResidualStyleTag(Elem->id)) {
1382                 // We've overloaded the use of stack elements and are just reusing the
1383                 // struct with a slightly different meaning to the variables.  Instead of chaining
1384                 // from innermost to outermost, we build up a list of all the tags we need to reopen
1385                 // from the outermost to the innermost, i.e., residualStyleStack will end up pointing
1386                 // to the outermost tag we need to reopen.
1387                 // We also set Elem->node to be the actual element that corresponds to the ID stored in
1388                 // Elem->id rather than the node that you should pop to when the element gets pulled off
1389                 // the stack.
1390                 popOneBlock(false);
1391                 Elem->next = residualStyleStack;
1392                 Elem->node = currNode;
1393                 residualStyleStack = Elem;
1394             }
1395             else
1396                 popOneBlock();
1397             Elem = blockStack;
1398         }
1399     }
1400
1401     reopenResidualStyleTags(residualStyleStack, malformedTableParent);
1402 }
1403
1404 void KHTMLParser::popOneBlock(bool delBlock)
1405 {
1406     HTMLStackElem *Elem = blockStack;
1407
1408     // we should never get here, but some bad html might cause it.
1409 #ifndef PARSER_DEBUG
1410     if(!Elem) return;
1411 #else
1412     kdDebug( 6035 ) << "popping block: " << getTagName(Elem->id).string() << "(" << Elem->id << ")" << endl;
1413 #endif
1414
1415 #if SPEED_DEBUG < 1
1416     if((Elem->node != current)) {
1417         if (current->maintainsState() && doc()){
1418             doc()->registerMaintainsState(current);
1419             QStringList &states = doc()->restoreState();
1420             if (!states.isEmpty())
1421                 current->restoreState(states);
1422         }
1423         
1424         // A few elements (<applet>, <object>) need to know when all child elements (<param>s) are available:
1425         current->closeRenderer();
1426     }
1427 #endif
1428
1429     removeForbidden(Elem->id, forbiddenTag);
1430
1431     blockStack = Elem->next;
1432     setCurrent(Elem->node);
1433
1434     if (Elem->strayTableContent)
1435         inStrayTableContent--;
1436     
1437     if (delBlock)
1438         delete Elem;
1439 }
1440
1441 void KHTMLParser::popInlineBlocks()
1442 {
1443     while (blockStack && current->isInline())
1444         popOneBlock();
1445 }
1446
1447 void KHTMLParser::freeBlock()
1448 {
1449     while (blockStack)
1450         popOneBlock();
1451 }
1452
1453 void KHTMLParser::createHead()
1454 {
1455     if(head || !doc()->firstChild())
1456         return;
1457
1458     head = new HTMLHeadElementImpl(document);
1459     HTMLElementImpl *body = doc()->body();
1460     int exceptioncode = 0;
1461     doc()->firstChild()->insertBefore(head, body, exceptioncode);
1462     if ( exceptioncode ) {
1463 #ifdef PARSER_DEBUG
1464         kdDebug( 6035 ) << "creation of head failed!!!!" << endl;
1465 #endif
1466         head = 0;
1467     }
1468 }
1469
1470 NodeImpl *KHTMLParser::handleIsindex( Token *t )
1471 {
1472     NodeImpl *n;
1473     HTMLFormElementImpl *myform = form;
1474     if ( !myform ) {
1475         myform = new HTMLFormElementImpl(document);
1476         n = myform;
1477     } else
1478         n = new HTMLDivElementImpl( document );
1479     NodeImpl *child = new HTMLHRElementImpl( document );
1480     n->addChild( child );
1481     AttributeImpl* a = t->attrs ? t->attrs->getAttributeItem(ATTR_PROMPT) : 0;
1482 #if APPLE_CHANGES
1483     DOMString text = searchableIndexIntroduction();
1484 #else
1485     DOMString text = i18n("This is a searchable index. Enter search keywords: ");
1486 #endif
1487     if (a)
1488         text = DOMString(a->value()) + " ";
1489     child = new TextImpl(document, text);
1490     n->addChild( child );
1491     child = new HTMLIsIndexElementImpl(document, myform);
1492     static_cast<ElementImpl *>(child)->setAttribute(ATTR_TYPE, "khtml_isindex");
1493     n->addChild( child );
1494     child = new HTMLHRElementImpl( document );
1495     n->addChild( child );
1496
1497     return n;
1498 }
1499
1500 void KHTMLParser::startBody()
1501 {
1502     if(inBody) return;
1503
1504     inBody = true;
1505
1506     if( isindex ) {
1507         insertNode( isindex, true /* don't decend into this node */ );
1508         isindex = 0;
1509     }
1510 }
1511
1512 void KHTMLParser::finished()
1513 {
1514     // In the case of a completely empty document, here's the place to create the HTML element.
1515     if (current->isDocumentNode() && current->firstChild() == 0) {
1516         insertNode(new HTMLHtmlElementImpl(document));
1517     }
1518
1519     // This ensures that "current" is not left pointing to a node when the document is destroyed.
1520     freeBlock();
1521     setCurrent(0);
1522 }