Reviewed by Ken.
[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(endTag[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 (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 #endif
343         }
344
345         return true;
346     } else {
347 #ifdef PARSER_DEBUG
348         kdDebug( 6035 ) << "ADDING NODE FAILED!!!! current = " << current->nodeName().string() << ", new = " << n->nodeName().string() << endl;
349 #endif
350         // error handling...
351         HTMLElementImpl *e;
352         bool handled = false;
353
354         // switch according to the element to insert
355         switch(id)
356         {
357         case ID_TR:
358         case ID_TH:
359         case ID_TD:
360             if (inStrayTableContent && !isTableRelatedTag(current->id())) {
361                 // pop out to the nearest enclosing table-related tag.
362                 while (!isTableRelatedTag(current->id()))
363                     popOneBlock();
364                 return insertNode(n);
365             }
366             break;
367         case ID_COMMENT:
368             break;
369         case ID_HEAD:
370             // ### alllow not having <HTML> in at all, as per HTML spec
371             if (!current->isDocumentNode() && current->id() != ID_HTML )
372                 return false;
373             break;
374             // We can deal with a base, meta and link element in the body, by just adding the element to head.
375         case ID_META:
376         case ID_LINK:
377         case ID_BASE:
378             if( !head )
379                 createHead();
380             if( head ) {
381                 if ( head->addChild(n) ) {
382 #if SPEED_DEBUG < 2
383                     if(!n->attached() && HTMLWidget)
384                         n->attach();
385 #endif
386                     return true;
387                 } else {
388                     return false;
389                 }
390             }
391             break;
392         case ID_HTML:
393             if (!current->isDocumentNode() ) {
394                 if ( doc()->firstChild()->id() == ID_HTML) {
395                     // we have another <HTML> element.... apply attributes to existing one
396                     // make sure we don't overwrite already existing attributes
397                     NamedAttrMapImpl *map = static_cast<ElementImpl*>(n)->attributes(true);
398                     NamedAttrMapImpl *bmap = static_cast<ElementImpl*>(doc()->firstChild())->attributes(false);
399                     bool changed = false;
400                     for (unsigned long l = 0; map && l < map->length(); ++l) {
401                         AttributeImpl* it = map->attributeItem(l);
402                         changed = !bmap->getAttributeItem(it->id());
403                         bmap->insertAttribute(it->clone(false));
404                     }
405                     if ( changed )
406                         doc()->recalcStyle( NodeImpl::Inherit );
407                 }
408                 return false;
409             }
410             break;
411         case ID_TITLE:
412         case ID_STYLE:
413             if ( !head )
414                 createHead();
415             if ( head ) {
416                 DOM::NodeImpl *newNode = head->addChild(n);
417                 if ( newNode ) {
418                     pushBlock(id, tagPriority[id]);
419                     setCurrent(newNode);
420 #if SPEED_DEBUG < 2
421                     if(!n->attached() && HTMLWidget)
422                         n->attach();
423 #endif
424                 } else {
425 #ifdef PARSER_DEBUG
426                     kdDebug( 6035 ) << "adding style before to body failed!!!!" << endl;
427 #endif
428                     discard_until = ID_STYLE + ID_CLOSE_TAG;
429                     return false;
430                 }
431                 return true;
432             } else if(inBody) {
433                 discard_until = ID_STYLE + ID_CLOSE_TAG;
434                 return false;
435             }
436             break;
437             // SCRIPT and OBJECT are allowed in the body.
438         case ID_BODY:
439             if(inBody && doc()->body()) {
440                 // we have another <BODY> element.... apply attributes to existing one
441                 // make sure we don't overwrite already existing attributes
442                 // some sites use <body bgcolor=rightcolor>...<body bgcolor=wrongcolor>
443                 NamedAttrMapImpl *map = static_cast<ElementImpl*>(n)->attributes(true);
444                 NamedAttrMapImpl *bmap = doc()->body()->attributes(false);
445                 bool changed = false;
446                 for (unsigned long l = 0; map && l < map->length(); ++l) {
447                     AttributeImpl* it = map->attributeItem(l);
448                     changed = !bmap->getAttributeItem(it->id());
449                     bmap->insertAttribute(it->clone(false));
450                 }
451                 if ( changed )
452                     doc()->recalcStyle( NodeImpl::Inherit );
453             } else if ( current->isDocumentNode() )
454                 break;
455             return false;
456             break;
457
458             // the following is a hack to move non rendered elements
459             // outside of tables.
460             // needed for broken constructs like <table><form ...><tr>....
461         case ID_INPUT:
462         {
463             ElementImpl *e = static_cast<ElementImpl *>(n);
464             DOMString type = e->getAttribute(ATTR_TYPE);
465
466             if ( strcasecmp( type, "hidden" ) == 0 && form) {
467                 form->addChild(n);
468 #if SPEED_DEBUG < 2
469                 if(!n->attached() && HTMLWidget)
470                     n->attach();
471 #endif
472                 return true;
473             }
474             break;
475         }
476         case ID_TEXT:
477             // ignore text inside the following elements.
478             switch(current->id())
479             {
480             case ID_SELECT:
481                 return false;
482             default:
483                 ;
484                 // fall through!!
485             };
486             break;
487         case ID_DD:
488         case ID_DT:
489             e = new HTMLDListElementImpl(document);
490             if ( insertNode(e) ) {
491                 insertNode(n);
492                 return true;
493             }
494             break;
495         case ID_AREA:
496         {
497             if(map)
498             {
499                 map->addChild(n);
500 #if SPEED_DEBUG < 2
501                 if(!n->attached() && HTMLWidget)
502                     n->attach();
503 #endif
504                 handled = true;
505             }
506             else
507                 return false;
508             return true;
509         }
510         case ID_CAPTION: {
511             switch (current->id()) {
512                 case ID_THEAD:
513                 case ID_TBODY:
514                 case ID_TFOOT:
515                 case ID_TR:
516                 case ID_TH:
517                 case ID_TD: {
518                     NodeImpl* tsection = current;
519                     if (current->id() == ID_TR)
520                         tsection = current->parent();
521                     else if (current->id() == ID_TD || current->id() == ID_TH)
522                         tsection = current->parent()->parent();
523                     NodeImpl* table = tsection->parent();
524                     int exceptioncode = 0;
525                     table->insertBefore(n, tsection, exceptioncode);
526                     pushBlock(id, tagPriority[id]);
527                     setCurrent(n);
528                     inStrayTableContent++;
529                     blockStack->strayTableContent = true;
530                     return true;
531                 }
532                 default:
533                     break;
534             }
535             break;
536         }
537         case ID_THEAD:
538         case ID_TBODY:
539         case ID_TFOOT:
540         case ID_COLGROUP: {
541             if (isTableRelatedTag(current->id())) {
542                 while (current->id() != ID_TABLE && isTableRelatedTag(current->id()))
543                     popOneBlock();
544                 return insertNode(n);
545             }
546         }
547         default:
548             break;
549         }
550
551         // switch on the currently active element
552         switch(current->id())
553         {
554         case ID_HTML:
555             switch(id)
556             {
557             case ID_SCRIPT:
558             case ID_STYLE:
559             case ID_META:
560             case ID_LINK:
561             case ID_OBJECT:
562             case ID_EMBED:
563             case ID_TITLE:
564             case ID_ISINDEX:
565             case ID_BASE:
566                 if(!head) {
567                     head = new HTMLHeadElementImpl(document);
568                     e = head;
569                     insertNode(e);
570                     handled = true;
571                 }
572                 break;
573             case ID_TEXT: {
574                 TextImpl *t = static_cast<TextImpl *>(n);
575                 if (t->containsOnlyWhitespace())
576                     return false;
577                 /* Fall through to default */
578             }
579             default:
580                 if ( haveFrameSet ) break;
581                 e = new HTMLBodyElementImpl(document);
582                 startBody();
583                 insertNode(e);
584                 handled = true;
585                 break;
586             }
587             break;
588         case ID_HEAD:
589             // we can get here only if the element is not allowed in head.
590             if (id == ID_HTML)
591                 return false;
592             else {
593                 // This means the body starts here...
594                 if ( haveFrameSet ) break;
595                 popBlock(ID_HEAD);
596                 e = new HTMLBodyElementImpl(document);
597                 startBody();
598                 insertNode(e);
599                 handled = true;
600             }
601             break;
602         case ID_BODY:
603             break;
604         case ID_CAPTION:
605             // Illegal content in a caption. Close the caption and try again.
606             popBlock(ID_CAPTION);
607             switch( id ) {
608             case ID_THEAD:
609             case ID_TFOOT:
610             case ID_TBODY:
611             case ID_TR:
612             case ID_TD:
613             case ID_TH:
614                 return insertNode(n, flat);
615             }
616             break;
617         case ID_TABLE:
618         case ID_THEAD:
619         case ID_TFOOT:
620         case ID_TBODY:
621         case ID_TR:
622             switch(id)
623             {
624             case ID_TABLE:
625                 popBlock(ID_TABLE); // end the table
626                 handled = true;      // ...and start a new one
627                 break;
628             case ID_TEXT:
629             {
630                 TextImpl *t = static_cast<TextImpl *>(n);
631                 if (t->containsOnlyWhitespace())
632                     return false;
633                 DOMStringImpl *i = t->string();
634                 unsigned int pos = 0;
635                 while(pos < i->l && ( *(i->s+pos) == QChar(' ') ||
636                                       *(i->s+pos) == QChar(0xa0))) pos++;
637                 if(pos == i->l)
638                     break;
639             }
640             default:
641             {
642                 NodeImpl *node = current;
643                 NodeImpl *parent = node->parentNode();
644
645                 NodeImpl *parentparent = parent->parentNode();
646
647                 if (n->isTextNode() ||
648                     ( node->id() == ID_TR &&
649                      ( parent->id() == ID_THEAD ||
650                       parent->id() == ID_TBODY ||
651                       parent->id() == ID_TFOOT ) && parentparent->id() == ID_TABLE ) ||
652                     ( !checkChild( ID_TR, id ) && ( node->id() == ID_THEAD || node->id() == ID_TBODY || node->id() == ID_TFOOT ) &&
653                      parent->id() == ID_TABLE ))
654                 {
655                     node = (node->id() == ID_TABLE) ? node :
656                             ((node->id() == ID_TR) ? parentparent : parent);
657                     NodeImpl *parent = node->parentNode();
658                     int exceptioncode = 0;
659                     parent->insertBefore( n, node, exceptioncode );
660                     if ( exceptioncode ) {
661 #ifdef PARSER_DEBUG
662                         kdDebug( 6035 ) << "adding content before table failed!" << endl;
663 #endif
664                         break;
665                     }
666                     if (n->isElementNode() && tagPriority[id] != 0 && 
667                         !flat && endTag[id] != DOM::FORBIDDEN)
668                     {
669                         pushBlock(id, tagPriority[id]);
670                         setCurrent(n);
671                         inStrayTableContent++;
672                         blockStack->strayTableContent = true;
673                     }
674                     return true;
675                 }
676
677                 if ( current->id() == ID_TR )
678                     e = new HTMLTableCellElementImpl(document, ID_TD);
679                 else if ( current->id() == ID_TABLE )
680                     e = new HTMLTableSectionElementImpl( document, ID_TBODY, true /* implicit */ );
681                 else
682                     e = new HTMLTableRowElementImpl( document );
683                 
684                 insertNode(e);
685                 handled = true;
686                 break;
687             } // end default
688             } // end switch
689             break;
690         case ID_OBJECT:
691             discard_until = ID_OBJECT + ID_CLOSE_TAG;
692             return false;
693         case ID_UL:
694         case ID_OL:
695         case ID_DIR:
696         case ID_MENU:
697             e = new HTMLDivElementImpl(document);
698             insertNode(e);
699             handled = true;
700             break;
701             case ID_DL:
702                 popBlock(ID_DL);
703                 handled = true;
704                 break;
705             case ID_DT:
706                 popBlock(ID_DT);
707                 handled = true;
708                 break;
709         case ID_SELECT:
710             if( n->isInline() )
711                 return false;
712             break;
713         case ID_P:
714         case ID_H1:
715         case ID_H2:
716         case ID_H3:
717         case ID_H4:
718         case ID_H5:
719         case ID_H6:
720             if(!n->isInline())
721             {
722                 popBlock(current->id());
723                 handled = true;
724             }
725             break;
726         case ID_OPTION:
727         case ID_OPTGROUP:
728             if (id == ID_OPTGROUP)
729             {
730                 popBlock(current->id());
731                 handled = true;
732             }
733             else if(id == ID_SELECT)
734             {
735                 // IE treats a nested select as </select>. Let's do the same
736                 popBlock( ID_SELECT );
737                 break;
738             }
739             break;
740             // head elements in the body should be ignored.
741         case ID_ADDRESS:
742             popBlock(ID_ADDRESS);
743             handled = true;
744             break;
745         case ID_COLGROUP:
746             if (id != ID_TEXT) {
747                 popBlock(ID_COLGROUP);
748                 handled = true;
749             }
750             break;
751         case ID_FONT:
752             popBlock(ID_FONT);
753             handled = true;
754             break;
755         default:
756             if(current->isDocumentNode())
757             {
758                 if(current->firstChild() == 0) {
759                     e = new HTMLHtmlElementImpl(document);
760                     insertNode(e);
761                     handled = true;
762                 }
763             }
764             else if(current->isInline())
765             {
766                 popInlineBlocks();
767                 handled = true;
768             }
769         }
770
771         // if we couldn't handle the error, just rethrow the exception...
772         if(!handled)
773         {
774             //kdDebug( 6035 ) << "Exception handler failed in HTMLPArser::insertNode()" << endl;
775             return false;
776         }
777
778         return insertNode(n);
779     }
780 }
781
782 NodeImpl *KHTMLParser::getElement(Token* t)
783 {
784     switch (t->id)
785     {
786     case ID_HEAD:
787         if (!head && current->id() == ID_HTML) {
788             head = new HTMLHeadElementImpl(document);
789             return head;
790         }
791         return 0;
792     case ID_BODY:
793         // body no longer allowed if we have a frameset
794         if (haveFrameSet)
795             return 0;
796         popBlock(ID_HEAD);
797         startBody();
798         return new HTMLBodyElementImpl(document);
799
800 // frames
801     case ID_FRAMESET:
802         popBlock(ID_HEAD);
803         if (inBody && !haveFrameSet && !haveContent) {
804             popBlock(ID_BODY);
805             // ### actually for IE document.body returns the now hidden "body" element
806             // we can't implement that behaviour now because it could cause too many
807             // regressions and the headaches are not worth the work as long as there is
808             // no site actually relying on that detail (Dirk)
809             if (doc()->body())
810                 doc()->body()->setAttribute(ATTR_STYLE, "display:none");
811             inBody = false;
812         }
813         if ((haveContent || haveFrameSet) && current->id() == ID_HTML)
814             return 0;
815         haveFrameSet = true;
816         startBody();
817         return new HTMLFrameSetElementImpl(document);
818
819     // a bit of a special case, since the frame is inlined
820     case ID_IFRAME:
821         discard_until = ID_IFRAME + ID_CLOSE_TAG;
822         break;
823
824 // form elements
825     case ID_FORM:
826         // Only create a new form if we're not already inside one.
827         // This is consistent with other browsers' behavior.
828         if (form)
829             return 0;
830         form = new HTMLFormElementImpl(document);
831         return form;
832     case ID_BUTTON:
833         return new HTMLButtonElementImpl(document, form);
834     case ID_FIELDSET:
835         return new HTMLFieldSetElementImpl(document, form);
836     case ID_INPUT:
837         return new HTMLInputElementImpl(document, form);
838     case ID_ISINDEX: {
839         NodeImpl *n = handleIsindex(t);
840         if (!inBody) {
841             if (isindex)
842                 isindex->deref();
843             isindex = n;
844             isindex->ref();
845             return 0;
846         }
847         t->flat = true;
848         return n;
849     }
850     case ID_KEYGEN:
851         return new HTMLKeygenElementImpl(document, form);
852     case ID_LEGEND:
853         return new HTMLLegendElementImpl(document, form);
854     case ID_OPTGROUP:
855         return new HTMLOptGroupElementImpl(document, form);
856     case ID_OPTION:
857         return new HTMLOptionElementImpl(document, form);
858     case ID_SELECT:
859         inSelect = true;
860         return new HTMLSelectElementImpl(document, form);
861     case ID_TEXTAREA:
862         return new HTMLTextAreaElementImpl(document, form);
863
864 // lists
865     case ID_DD:
866         popBlock(ID_DT);
867         popBlock(ID_DD);
868         break;
869     case ID_DT:
870         popBlock(ID_DD);
871         popBlock(ID_DT);
872         break;
873     case ID_LI:
874         popBlock(ID_LI);
875         break;
876
877 // anchor
878     case ID_A:
879         // Never allow nested <a>s.
880         popBlock(ID_A);
881         break;
882
883 // images
884     case ID_IMG:
885         return new HTMLImageElementImpl(document, form);
886     case ID_MAP:
887         map = new HTMLMapElementImpl(document);
888         return map;
889
890 // tables
891     case ID_TR:
892         popBlock(ID_TR);
893         break;
894     case ID_TD:
895     case ID_TH:
896         popBlock(ID_TH);
897         popBlock(ID_TD);
898         break;
899     case ID_TBODY:
900     case ID_THEAD:
901     case ID_TFOOT:
902         popBlock(ID_THEAD);
903         popBlock(ID_TBODY);
904         popBlock(ID_TFOOT);
905         break;
906
907 // elements with no special representation in the DOM
908     case ID_TT:
909     case ID_U:
910     case ID_B:
911     case ID_I:
912     case ID_S:
913     case ID_STRIKE:
914     case ID_BIG:
915     case ID_SMALL:
916         if (!allowNestedRedundantTag(t->id))
917             return 0;
918         break;
919
920     case ID_NOBR:
921     case ID_WBR:
922         popBlock(t->id); // Don't allow nested <nobr> or <wbr>
923         break;
924
925 // these are special, and normally not rendered
926     case ID_NOEMBED:
927         discard_until = ID_NOEMBED + ID_CLOSE_TAG;
928         return 0;
929     case ID_NOFRAMES:
930         discard_until = ID_NOFRAMES + ID_CLOSE_TAG;
931         return 0;
932     case ID_NOSCRIPT:
933         if (HTMLWidget && HTMLWidget->part()->jScriptEnabled())
934             discard_until = ID_NOSCRIPT + ID_CLOSE_TAG;
935         return 0;
936     case ID_NOLAYER:
937         //discard_until = ID_NOLAYER + ID_CLOSE_TAG;
938         return 0;
939     case ID_TEXT:
940         return new TextImpl(document, t->text);
941     case ID_COMMENT:
942         if (!includesCommentsInDOM)
943             return 0;
944         break;
945     }
946
947     return document->document()->createHTMLElement(t->id);
948 }
949
950 #define MAX_REDUNDANT 20
951
952 bool KHTMLParser::allowNestedRedundantTag(int _id)
953 {
954     // www.liceo.edu.mx is an example of a site that achieves a level of nesting of
955     // about 1500 tags, all from a bunch of <b>s.  We will only allow at most 20
956     // nested tags of the same type before just ignoring them all together.
957     int i = 0;
958     for (HTMLStackElem* curr = blockStack;
959          i < MAX_REDUNDANT && curr && curr->id == _id;
960          curr = curr->next, i++);
961     return i != MAX_REDUNDANT;
962 }
963
964 void KHTMLParser::processCloseTag(Token *t)
965 {
966     // support for really broken html. Can't believe I'm supporting such crap (lars)
967     switch(t->id)
968     {
969     case ID_HTML+ID_CLOSE_TAG:
970     case ID_BODY+ID_CLOSE_TAG:
971         // we never close the body tag, since some stupid web pages close it before the actual end of the doc.
972         // let's rely on the end() call to close things.
973         return;
974     case ID_FORM+ID_CLOSE_TAG:
975         form = 0;
976         // this one is to get the right style on the body element
977         break;
978     case ID_MAP+ID_CLOSE_TAG:
979         map = 0;
980         break;
981     case ID_SELECT+ID_CLOSE_TAG:
982         inSelect = false;
983         break;
984     default:
985         break;
986     }
987
988 #ifdef PARSER_DEBUG
989     kdDebug( 6035 ) << "added the following childs to " << current->nodeName().string() << endl;
990     NodeImpl *child = current->firstChild();
991     while(child != 0)
992     {
993         kdDebug( 6035 ) << "    " << child->nodeName().string() << endl;
994         child = child->nextSibling();
995     }
996 #endif
997     HTMLStackElem* oldElem = blockStack;
998     popBlock(t->id-ID_CLOSE_TAG);
999     if (oldElem == blockStack && t->id == ID_P+ID_CLOSE_TAG) {
1000         // We encountered a stray </p>.  Amazingly Gecko, WinIE, and MacIE all treat
1001         // this as a valid break, i.e., <p></p>.  So go ahead and make the empty
1002         // paragraph.
1003         t->id-=ID_CLOSE_TAG;
1004         parseToken(t);
1005         popBlock(ID_P);
1006     }
1007 #ifdef PARSER_DEBUG
1008     kdDebug( 6035 ) << "closeTag --> current = " << current->nodeName().string() << endl;
1009 #endif
1010 }
1011
1012 bool KHTMLParser::isHeaderTag(int _id)
1013 {
1014     switch (_id) {
1015         case ID_H1:
1016         case ID_H2:
1017         case ID_H3:
1018         case ID_H4:
1019         case ID_H5:
1020         case ID_H6:
1021             return true;
1022         default:
1023             return false;
1024     }
1025 }
1026
1027 void KHTMLParser::popNestedHeaderTag()
1028 {
1029     // This function only cares about checking for nested headers that have only inlines in between them.
1030     NodeImpl* currNode = current;
1031     for (HTMLStackElem* curr = blockStack; curr; curr = curr->next) {
1032         if (isHeaderTag(curr->id)) {
1033             popBlock(curr->id);
1034             return;
1035         }
1036         if (currNode && !currNode->isInline())
1037             return;
1038         currNode = curr->node;
1039     }
1040 }
1041
1042 bool KHTMLParser::isResidualStyleTag(int _id)
1043 {
1044     switch (_id) {
1045         case ID_A:
1046         case ID_FONT:
1047         case ID_TT:
1048         case ID_U:
1049         case ID_B:
1050         case ID_I:
1051         case ID_S:
1052         case ID_STRIKE:
1053         case ID_BIG:
1054         case ID_SMALL:
1055         case ID_EM:
1056         case ID_STRONG:
1057         case ID_DFN:
1058         case ID_CODE:
1059         case ID_SAMP:
1060         case ID_KBD:
1061         case ID_VAR:
1062             return true;
1063         default:
1064             return false;
1065     }
1066 }
1067
1068 bool KHTMLParser::isAffectedByResidualStyle(int _id)
1069 {
1070     if (isResidualStyleTag(_id))
1071         return true;
1072     
1073     switch (_id) {
1074         case ID_P:
1075         case ID_DIV:
1076         case ID_BLOCKQUOTE:
1077         case ID_ADDRESS:
1078         case ID_H1:
1079         case ID_H2:
1080         case ID_H3:
1081         case ID_H4:
1082         case ID_H5:
1083         case ID_H6:
1084         case ID_CENTER:
1085         case ID_UL:
1086         case ID_OL:
1087         case ID_LI:
1088         case ID_DL:
1089         case ID_DT:
1090         case ID_DD:
1091         case ID_PRE:
1092             return true;
1093         default:
1094             return false;
1095     }
1096 }
1097
1098 void KHTMLParser::handleResidualStyleCloseTagAcrossBlocks(HTMLStackElem* elem)
1099 {
1100     // Find the element that crosses over to a higher level.   For now, if there is more than
1101     // one, we will just give up and not attempt any sort of correction.  It's highly unlikely that
1102     // there will be more than one, since <p> tags aren't allowed to be nested.
1103     int exceptionCode = 0;
1104     HTMLStackElem* curr = blockStack;
1105     HTMLStackElem* maxElem = 0;
1106     HTMLStackElem* prev = 0;
1107     HTMLStackElem* prevMaxElem = 0;
1108     while (curr && curr != elem) {
1109         if (curr->level > elem->level) {
1110             if (maxElem)
1111                 return;
1112             maxElem = curr;
1113             prevMaxElem = prev;
1114         }
1115
1116         prev = curr;
1117         curr = curr->next;
1118     }
1119
1120     if (!curr || !maxElem || !isAffectedByResidualStyle(maxElem->id)) return;
1121
1122     NodeImpl* residualElem = prev->node;
1123     NodeImpl* blockElem = prevMaxElem ? prevMaxElem->node : current;
1124     NodeImpl* parentElem = elem->node;
1125
1126     // Check to see if the reparenting that is going to occur is allowed according to the DOM.
1127     // FIXME: We should either always allow it or perform an additional fixup instead of
1128     // just bailing here.
1129     // Example: <p><font><center>blah</font></center></p> isn't doing a fixup right now.
1130     if (!parentElem->childAllowed(blockElem))
1131         return;
1132     
1133     if (maxElem->node->parentNode() != elem->node) {
1134         // Walk the stack and remove any elements that aren't residual style tags.  These
1135         // are basically just being closed up.  Example:
1136         // <font><span>Moo<p>Goo</font></p>.
1137         // In the above example, the <span> doesn't need to be reopened.  It can just close.
1138         HTMLStackElem* currElem = maxElem->next;
1139         HTMLStackElem* prevElem = maxElem;
1140         while (currElem != elem) {
1141             HTMLStackElem* nextElem = currElem->next;
1142             if (!isResidualStyleTag(currElem->id)) {
1143                 prevElem->next = nextElem;
1144                 prevElem->node = currElem->node;
1145                 delete currElem;
1146             }
1147             else
1148                 prevElem = currElem;
1149             currElem = nextElem;
1150         }
1151         
1152         // We have to reopen residual tags in between maxElem and elem.  An example of this case is:
1153         // <font><i>Moo<p>Foo</font>.
1154         // In this case, we need to transform the part before the <p> into:
1155         // <font><i>Moo</i></font><i>
1156         // so that the <i> will remain open.  This involves the modification of elements
1157         // in the block stack.
1158         // This will also affect how we ultimately reparent the block, since we want it to end up
1159         // under the reopened residual tags (e.g., the <i> in the above example.)
1160         NodeImpl* prevNode = 0;
1161         NodeImpl* currNode = 0;
1162         currElem = maxElem;
1163         while (currElem->node != residualElem) {
1164             if (isResidualStyleTag(currElem->node->id())) {
1165                 // Create a clone of this element.
1166                 currNode = currElem->node->cloneNode(false);
1167
1168                 // Change the stack element's node to point to the clone.
1169                 currElem->node = currNode;
1170                 
1171                 // Attach the previous node as a child of this new node.
1172                 if (prevNode)
1173                     currNode->appendChild(prevNode, exceptionCode);
1174                 else // The new parent for the block element is going to be the innermost clone.
1175                     parentElem = currNode;
1176                                 
1177                 prevNode = currNode;
1178             }
1179             
1180             currElem = currElem->next;
1181         }
1182
1183         // Now append the chain of new residual style elements if one exists.
1184         if (prevNode)
1185             elem->node->appendChild(prevNode, exceptionCode);
1186     }
1187          
1188     // We need to make a clone of |residualElem| and place it just inside |blockElem|.
1189     // All content of |blockElem| is reparented to be under this clone.  We then
1190     // reparent |blockElem| using real DOM calls so that attachment/detachment will
1191     // be performed to fix up the rendering tree.
1192     // So for this example: <b>...<p>Foo</b>Goo</p>
1193     // The end result will be: <b>...</b><p><b>Foo</b>Goo</p>
1194     //
1195     // Step 1: Remove |blockElem| from its parent, doing a batch detach of all the kids.
1196     blockElem->parentNode()->removeChild(blockElem, exceptionCode);
1197         
1198     // Step 2: Clone |residualElem|.
1199     NodeImpl* newNode = residualElem->cloneNode(false); // Shallow clone. We don't pick up the same kids.
1200
1201     // Step 3: Place |blockElem|'s children under |newNode|.  Remove all of the children of |blockElem|
1202     // before we've put |newElem| into the document.  That way we'll only do one attachment of all
1203     // the new content (instead of a bunch of individual attachments).
1204     NodeImpl* currNode = blockElem->firstChild();
1205     while (currNode) {
1206         NodeImpl* nextNode = currNode->nextSibling();
1207         blockElem->removeChild(currNode, exceptionCode);
1208         newNode->appendChild(currNode, exceptionCode);
1209         currNode = nextNode;
1210     }
1211
1212     // Step 4: Place |newNode| under |blockElem|.  |blockElem| is still out of the document, so no
1213     // attachment can occur yet.
1214     blockElem->appendChild(newNode, exceptionCode);
1215     
1216     // Step 5: Reparent |blockElem|.  Now the full attachment of the fixed up tree takes place.
1217     parentElem->appendChild(blockElem, exceptionCode);
1218         
1219     // Step 6: Elide |elem|, since it is effectively no longer open.  Also update
1220     // the node associated with the previous stack element so that when it gets popped,
1221     // it doesn't make the residual element the next current node.
1222     HTMLStackElem* currElem = maxElem;
1223     HTMLStackElem* prevElem = 0;
1224     while (currElem != elem) {
1225         prevElem = currElem;
1226         currElem = currElem->next;
1227     }
1228     prevElem->next = elem->next;
1229     prevElem->node = elem->node;
1230     delete elem;
1231     
1232     // Step 7: Reopen intermediate inlines, e.g., <b><p><i>Foo</b>Goo</p>.
1233     // In the above example, Goo should stay italic.
1234     curr = blockStack;
1235     HTMLStackElem* residualStyleStack = 0;
1236     while (curr && curr != maxElem) {
1237         // We will actually schedule this tag for reopening
1238         // after we complete the close of this entire block.
1239         NodeImpl* currNode = current;
1240         if (isResidualStyleTag(curr->id)) {
1241             // We've overloaded the use of stack elements and are just reusing the
1242             // struct with a slightly different meaning to the variables.  Instead of chaining
1243             // from innermost to outermost, we build up a list of all the tags we need to reopen
1244             // from the outermost to the innermost, i.e., residualStyleStack will end up pointing
1245             // to the outermost tag we need to reopen.
1246             // We also set curr->node to be the actual element that corresponds to the ID stored in
1247             // curr->id rather than the node that you should pop to when the element gets pulled off
1248             // the stack.
1249             popOneBlock(false);
1250             curr->node = currNode;
1251             curr->next = residualStyleStack;
1252             residualStyleStack = curr;
1253         }
1254         else
1255             popOneBlock();
1256
1257         curr = blockStack;
1258     }
1259
1260     reopenResidualStyleTags(residualStyleStack, 0); // FIXME: Deal with stray table content some day
1261                                                     // if it becomes necessary to do so.
1262 }
1263
1264 void KHTMLParser::reopenResidualStyleTags(HTMLStackElem* elem, DOM::NodeImpl* malformedTableParent)
1265 {
1266     // Loop for each tag that needs to be reopened.
1267     while (elem) {
1268         // Create a shallow clone of the DOM node for this element.
1269         NodeImpl* newNode = elem->node->cloneNode(false); 
1270
1271         // Append the new node. In the malformed table case, we need to insert before the table,
1272         // which will be the last child.
1273         int exceptionCode = 0;
1274         if (malformedTableParent)
1275             malformedTableParent->insertBefore(newNode, malformedTableParent->lastChild(), exceptionCode);
1276         else
1277             current->appendChild(newNode, exceptionCode);
1278         // FIXME: Is it really OK to ignore the exceptions here?
1279
1280         // Now push a new stack element for this node we just created.
1281         pushBlock(elem->id, elem->level);
1282
1283         // Set our strayTableContent boolean if needed, so that the reopened tag also knows
1284         // that it is inside a malformed table.
1285         blockStack->strayTableContent = malformedTableParent != 0;
1286         if (blockStack->strayTableContent)
1287             inStrayTableContent++;
1288
1289         // Clear our malformed table parent variable.
1290         malformedTableParent = 0;
1291
1292         // Update |current| manually to point to the new node.
1293         setCurrent(newNode);
1294         
1295         // Advance to the next tag that needs to be reopened.
1296         HTMLStackElem* next = elem->next;
1297         delete elem;
1298         elem = next;
1299     }
1300 }
1301
1302 void KHTMLParser::pushBlock(int _id, int _level)
1303 {
1304     HTMLStackElem *Elem = new HTMLStackElem(_id, _level, current, blockStack);
1305
1306     blockStack = Elem;
1307     addForbidden(_id, forbiddenTag);
1308 }
1309
1310 void KHTMLParser::popBlock( int _id )
1311 {
1312     HTMLStackElem *Elem = blockStack;
1313     
1314     int maxLevel = 0;
1315
1316 #ifdef PARSER_DEBUG
1317     kdDebug( 6035 ) << "popBlock(" << getTagName(_id).string() << ")" << endl;
1318     while(Elem) {
1319         kdDebug( 6035) << "   > " << getTagName(Elem->id).string() << endl;
1320         Elem = Elem->next;
1321     }
1322     Elem = blockStack;
1323 #endif
1324
1325     while( Elem && (Elem->id != _id))
1326     {
1327         if (maxLevel < Elem->level)
1328         {
1329             maxLevel = Elem->level;
1330         }
1331         Elem = Elem->next;
1332     }
1333
1334     if (!Elem)
1335         return;
1336
1337     if (maxLevel > Elem->level) {
1338         // We didn't match because the tag is in a different scope, e.g.,
1339         // <b><p>Foo</b>.  Try to correct the problem.
1340         if (!isResidualStyleTag(_id))
1341             return;
1342         return handleResidualStyleCloseTagAcrossBlocks(Elem);
1343     }
1344
1345     bool isAffectedByStyle = isAffectedByResidualStyle(Elem->id);
1346     HTMLStackElem* residualStyleStack = 0;
1347     NodeImpl* malformedTableParent = 0;
1348     
1349     Elem = blockStack;
1350     while (Elem)
1351     {
1352         if (Elem->id == _id)
1353         {
1354             int strayTable = inStrayTableContent;
1355             popOneBlock();
1356             Elem = 0;
1357
1358             // This element was the root of some malformed content just inside an implicit or
1359             // explicit <tbody> or <tr>.
1360             // If we end up needing to reopen residual style tags, the root of the reopened chain
1361             // must also know that it is the root of malformed content inside a <tbody>/<tr>.
1362             if (strayTable && (inStrayTableContent < strayTable) && residualStyleStack) {
1363                 NodeImpl* curr = current;
1364                 while (curr && curr->id() != ID_TABLE)
1365                     curr = curr->parentNode();
1366                 malformedTableParent = curr ? curr->parentNode() : 0;
1367             }
1368         }
1369         else
1370         {
1371             if (Elem->id == ID_FORM && form)
1372                 // A <form> is being closed prematurely (and this is
1373                 // malformed HTML).  Set an attribute on the form to clear out its
1374                 // bottom margin.
1375                 form->setMalformed(true);
1376
1377             // Schedule this tag for reopening
1378             // after we complete the close of this entire block.
1379             NodeImpl* currNode = current;
1380             if (isAffectedByStyle && isResidualStyleTag(Elem->id)) {
1381                 // We've overloaded the use of stack elements and are just reusing the
1382                 // struct with a slightly different meaning to the variables.  Instead of chaining
1383                 // from innermost to outermost, we build up a list of all the tags we need to reopen
1384                 // from the outermost to the innermost, i.e., residualStyleStack will end up pointing
1385                 // to the outermost tag we need to reopen.
1386                 // We also set Elem->node to be the actual element that corresponds to the ID stored in
1387                 // Elem->id rather than the node that you should pop to when the element gets pulled off
1388                 // the stack.
1389                 popOneBlock(false);
1390                 Elem->next = residualStyleStack;
1391                 Elem->node = currNode;
1392                 residualStyleStack = Elem;
1393             }
1394             else
1395                 popOneBlock();
1396             Elem = blockStack;
1397         }
1398     }
1399
1400     reopenResidualStyleTags(residualStyleStack, malformedTableParent);
1401 }
1402
1403 void KHTMLParser::popOneBlock(bool delBlock)
1404 {
1405     HTMLStackElem *Elem = blockStack;
1406
1407     // we should never get here, but some bad html might cause it.
1408 #ifndef PARSER_DEBUG
1409     if(!Elem) return;
1410 #else
1411     kdDebug( 6035 ) << "popping block: " << getTagName(Elem->id).string() << "(" << Elem->id << ")" << endl;
1412 #endif
1413
1414 #if SPEED_DEBUG < 1
1415     if((Elem->node != current)) {
1416         if (current->maintainsState() && doc()){
1417             doc()->registerMaintainsState(current);
1418             QStringList &states = doc()->restoreState();
1419             if (!states.isEmpty())
1420                 current->restoreState(states);
1421         }
1422     }
1423 #endif
1424
1425     removeForbidden(Elem->id, forbiddenTag);
1426
1427     blockStack = Elem->next;
1428     setCurrent(Elem->node);
1429
1430     if (Elem->strayTableContent)
1431         inStrayTableContent--;
1432
1433     if (delBlock)
1434         delete Elem;
1435 }
1436
1437 void KHTMLParser::popInlineBlocks()
1438 {
1439     while(current->isInline())
1440         popOneBlock();
1441 }
1442
1443 void KHTMLParser::freeBlock()
1444 {
1445     while (blockStack)
1446         popOneBlock();
1447 }
1448
1449 void KHTMLParser::createHead()
1450 {
1451     if(head || !doc()->firstChild())
1452         return;
1453
1454     head = new HTMLHeadElementImpl(document);
1455     HTMLElementImpl *body = doc()->body();
1456     int exceptioncode = 0;
1457     doc()->firstChild()->insertBefore(head, body, exceptioncode);
1458     if ( exceptioncode ) {
1459 #ifdef PARSER_DEBUG
1460         kdDebug( 6035 ) << "creation of head failed!!!!" << endl;
1461 #endif
1462         head = 0;
1463     }
1464 }
1465
1466 NodeImpl *KHTMLParser::handleIsindex( Token *t )
1467 {
1468     NodeImpl *n;
1469     HTMLFormElementImpl *myform = form;
1470     if ( !myform ) {
1471         myform = new HTMLFormElementImpl(document);
1472         n = myform;
1473     } else
1474         n = new HTMLDivElementImpl( document );
1475     NodeImpl *child = new HTMLHRElementImpl( document );
1476     n->addChild( child );
1477     AttributeImpl* a = t->attrs ? t->attrs->getAttributeItem(ATTR_PROMPT) : 0;
1478 #if APPLE_CHANGES
1479     DOMString text = searchableIndexIntroduction();
1480 #else
1481     DOMString text = i18n("This is a searchable index. Enter search keywords: ");
1482 #endif
1483     if (a)
1484         text = DOMString(a->value()) + " ";
1485     child = new TextImpl(document, text);
1486     n->addChild( child );
1487     child = new HTMLIsIndexElementImpl(document, myform);
1488     static_cast<ElementImpl *>(child)->setAttribute(ATTR_TYPE, "khtml_isindex");
1489     n->addChild( child );
1490     child = new HTMLHRElementImpl( document );
1491     n->addChild( child );
1492
1493     return n;
1494 }
1495
1496 void KHTMLParser::startBody()
1497 {
1498     if(inBody) return;
1499
1500     inBody = true;
1501
1502     if( isindex ) {
1503         insertNode( isindex, true /* don't decend into this node */ );
1504         isindex = 0;
1505     }
1506 }
1507
1508 void KHTMLParser::finished()
1509 {
1510     // This ensures that "current" is not left pointing to a node when the document is destroyed.
1511     freeBlock();
1512     setCurrent(0);
1513 }