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