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