bd8c28d88c044b28d8cb1802cfd5b7be7643df16
[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         default:
537             break;
538         }
539
540         // switch on the currently active element
541         switch(current->id())
542         {
543         case ID_HTML:
544             switch(id)
545             {
546             case ID_SCRIPT:
547             case ID_STYLE:
548             case ID_META:
549             case ID_LINK:
550             case ID_OBJECT:
551             case ID_EMBED:
552             case ID_TITLE:
553             case ID_ISINDEX:
554             case ID_BASE:
555                 if(!head) {
556                     head = new HTMLHeadElementImpl(document);
557                     e = head;
558                     insertNode(e);
559                     handled = true;
560                 }
561                 break;
562             case ID_TEXT: {
563                 TextImpl *t = static_cast<TextImpl *>(n);
564                 if (t->containsOnlyWhitespace())
565                     return false;
566                 /* Fall through to default */
567             }
568             default:
569                 if ( haveFrameSet ) break;
570                 e = new HTMLBodyElementImpl(document);
571                 startBody();
572                 insertNode(e);
573                 handled = true;
574                 break;
575             }
576             break;
577         case ID_HEAD:
578             // we can get here only if the element is not allowed in head.
579             if (id == ID_HTML)
580                 return false;
581             else {
582                 // This means the body starts here...
583                 if ( haveFrameSet ) break;
584                 popBlock(ID_HEAD);
585                 e = new HTMLBodyElementImpl(document);
586                 startBody();
587                 insertNode(e);
588                 handled = true;
589             }
590             break;
591         case ID_BODY:
592             break;
593         case ID_CAPTION:
594             // Illegal content in a caption. Close the caption and try again.
595             popBlock(ID_CAPTION);
596             switch( id ) {
597             case ID_THEAD:
598             case ID_TFOOT:
599             case ID_TBODY:
600             case ID_TR:
601             case ID_TD:
602             case ID_TH:
603                 return insertNode(n, flat);
604             }
605             break;
606         case ID_TABLE:
607         case ID_THEAD:
608         case ID_TFOOT:
609         case ID_TBODY:
610         case ID_TR:
611             switch(id)
612             {
613             case ID_TABLE:
614                 popBlock(ID_TABLE); // end the table
615                 handled = true;      // ...and start a new one
616                 break;
617             case ID_TEXT:
618             {
619                 TextImpl *t = static_cast<TextImpl *>(n);
620                 if (t->containsOnlyWhitespace())
621                     return false;
622                 DOMStringImpl *i = t->string();
623                 unsigned int pos = 0;
624                 while(pos < i->l && ( *(i->s+pos) == QChar(' ') ||
625                                       *(i->s+pos) == QChar(0xa0))) pos++;
626                 if(pos == i->l)
627                     break;
628             }
629             default:
630             {
631                 NodeImpl *node = current;
632                 NodeImpl *parent = node->parentNode();
633
634                 NodeImpl *parentparent = parent->parentNode();
635
636                 if (n->isTextNode() ||
637                     ( node->id() == ID_TR &&
638                      ( parent->id() == ID_THEAD ||
639                       parent->id() == ID_TBODY ||
640                       parent->id() == ID_TFOOT ) && parentparent->id() == ID_TABLE ) ||
641                     ( !checkChild( ID_TR, id ) && ( node->id() == ID_THEAD || node->id() == ID_TBODY || node->id() == ID_TFOOT ) &&
642                      parent->id() == ID_TABLE ))
643                 {
644                     node = (node->id() == ID_TABLE) ? node :
645                             ((node->id() == ID_TR) ? parentparent : parent);
646                     NodeImpl *parent = node->parentNode();
647                     int exceptioncode = 0;
648                     parent->insertBefore( n, node, exceptioncode );
649                     if ( exceptioncode ) {
650 #ifdef PARSER_DEBUG
651                         kdDebug( 6035 ) << "adding content before table failed!" << endl;
652 #endif
653                         break;
654                     }
655                     if (n->isElementNode() && tagPriority[id] != 0 && 
656                         !flat && endTag[id] != DOM::FORBIDDEN)
657                     {
658                         pushBlock(id, tagPriority[id]);
659                         setCurrent(n);
660                         inStrayTableContent++;
661                         blockStack->strayTableContent = true;
662                     }
663                     return true;
664                 }
665
666                 if ( current->id() == ID_TR )
667                     e = new HTMLTableCellElementImpl(document, ID_TD);
668                 else if ( current->id() == ID_TABLE )
669                     e = new HTMLTableSectionElementImpl( document, ID_TBODY, true /* implicit */ );
670                 else
671                     e = new HTMLTableRowElementImpl( document );
672                 
673                 insertNode(e);
674                 handled = true;
675                 break;
676             } // end default
677             } // end switch
678             break;
679         case ID_OBJECT:
680             discard_until = id + ID_CLOSE_TAG;
681             return false;
682         case ID_UL:
683         case ID_OL:
684         case ID_DIR:
685         case ID_MENU:
686             e = new HTMLDivElementImpl(document);
687             insertNode(e);
688             handled = true;
689             break;
690             case ID_DL:
691                 popBlock(ID_DL);
692                 handled = true;
693                 break;
694             case ID_DT:
695                 popBlock(ID_DT);
696                 handled = true;
697                 break;
698         case ID_SELECT:
699             if( n->isInline() )
700                 return false;
701             break;
702         case ID_P:
703         case ID_H1:
704         case ID_H2:
705         case ID_H3:
706         case ID_H4:
707         case ID_H5:
708         case ID_H6:
709             if(!n->isInline())
710             {
711                 popBlock(current->id());
712                 handled = true;
713             }
714             break;
715         case ID_OPTION:
716         case ID_OPTGROUP:
717             if (id == ID_OPTGROUP)
718             {
719                 popBlock(current->id());
720                 handled = true;
721             }
722             else if(id == ID_SELECT)
723             {
724                 // IE treats a nested select as </select>. Let's do the same
725                 popBlock( ID_SELECT );
726                 break;
727             }
728             break;
729             // head elements in the body should be ignored.
730         case ID_ADDRESS:
731             popBlock(ID_ADDRESS);
732             handled = true;
733             break;
734         case ID_COLGROUP:
735             if (id != ID_TEXT) {
736                 popBlock(ID_COLGROUP);
737                 handled = true;
738             }
739             break;
740         case ID_FONT:
741             popBlock(ID_FONT);
742             handled = true;
743             break;
744         default:
745             if(current->isDocumentNode())
746             {
747                 if(current->firstChild() == 0) {
748                     e = new HTMLHtmlElementImpl(document);
749                     insertNode(e);
750                     handled = true;
751                 }
752             }
753             else if(current->isInline())
754             {
755                 popInlineBlocks();
756                 handled = true;
757             }
758         }
759
760         // if we couldn't handle the error, just rethrow the exception...
761         if(!handled)
762         {
763             //kdDebug( 6035 ) << "Exception handler failed in HTMLPArser::insertNode()" << endl;
764             return false;
765         }
766
767         return insertNode(n);
768     }
769 }
770
771
772 NodeImpl *KHTMLParser::getElement(Token* t)
773 {
774     NodeImpl *n = 0;
775
776     switch(t->id)
777     {
778     case ID_HTML:
779         n = new HTMLHtmlElementImpl(document);
780         break;
781     case ID_HEAD:
782         if(!head && current->id() == ID_HTML) {
783             head = new HTMLHeadElementImpl(document);
784             n = head;
785         }
786         break;
787     case ID_BODY:
788         // body no longer allowed if we have a frameset
789         if(haveFrameSet) break;
790         popBlock(ID_HEAD);
791         n = new HTMLBodyElementImpl(document);
792         startBody();
793         break;
794
795 // head elements
796     case ID_BASE:
797         n = new HTMLBaseElementImpl(document);
798         break;
799     case ID_LINK:
800         n = new HTMLLinkElementImpl(document);
801         break;
802     case ID_META:
803         n = new HTMLMetaElementImpl(document);
804         break;
805     case ID_STYLE:
806         n = new HTMLStyleElementImpl(document);
807         break;
808     case ID_TITLE:
809         n = new HTMLTitleElementImpl(document);
810         break;
811
812 // frames
813     case ID_FRAME:
814         n = new HTMLFrameElementImpl(document);
815         break;
816     case ID_FRAMESET:
817         popBlock(ID_HEAD);
818         if ( inBody && !haveFrameSet && !haveContent) {
819             popBlock( ID_BODY );
820             // ### actually for IE document.body returns the now hidden "body" element
821             // we can't implement that behaviour now because it could cause too many
822             // regressions and the headaches are not worth the work as long as there is
823             // no site actually relying on that detail (Dirk)
824             if (static_cast<HTMLDocumentImpl*>(document->document())->body())
825                 static_cast<HTMLDocumentImpl*>(document->document())->body()
826                     ->setAttribute(ATTR_STYLE, "display:none");
827             inBody = false;
828         }
829         if ( (haveContent || haveFrameSet) && current->id() == ID_HTML)
830             break;
831         n = new HTMLFrameSetElementImpl(document);
832         haveFrameSet = true;
833         startBody();
834         break;
835         // a bit a special case, since the frame is inlined...
836     case ID_IFRAME:
837         n = new HTMLIFrameElementImpl(document);
838         discard_until = ID_IFRAME+ID_CLOSE_TAG;
839         break;
840
841 // form elements
842     case ID_FORM:
843         if (!form) {
844             // Only create a new form if we're not already inside one.
845             // This is consistent with other browsers' behavior.
846             form = new HTMLFormElementImpl(document);
847             n = form;
848         }
849         break;
850     case ID_BUTTON:
851         n = new HTMLButtonElementImpl(document, form);
852         break;
853     case ID_FIELDSET:
854         n = new HTMLFieldSetElementImpl(document, form);
855         break;
856     case ID_INPUT:
857         n = new HTMLInputElementImpl(document, form);
858         break;
859     case ID_ISINDEX:
860         n = handleIsindex(t);
861         if( !inBody ) {
862             isindex = n;
863             n = 0;
864         } else
865             t->flat = true;
866         break;
867     case ID_KEYGEN:
868         n = new HTMLKeygenElementImpl(document, form);
869         break;
870     case ID_LABEL:
871         n = new HTMLLabelElementImpl(document);
872         break;
873     case ID_LEGEND:
874         n = new HTMLLegendElementImpl(document, form);
875         break;
876     case ID_OPTGROUP:
877         n = new HTMLOptGroupElementImpl(document, form);
878         break;
879     case ID_OPTION:
880         n = new HTMLOptionElementImpl(document, form);
881         break;
882     case ID_SELECT:
883         inSelect = true;
884         n = new HTMLSelectElementImpl(document, form);
885         break;
886     case ID_TEXTAREA:
887         n = new HTMLTextAreaElementImpl(document, form);
888         break;
889
890 // lists
891     case ID_DL:
892         n = new HTMLDListElementImpl(document);
893         break;
894     case ID_DD:
895         n = new HTMLGenericElementImpl(document, t->id);
896         popBlock(ID_DT);
897         popBlock(ID_DD);
898         break;
899     case ID_DT:
900         n = new HTMLGenericElementImpl(document, t->id);
901         popBlock(ID_DD);
902         popBlock(ID_DT);
903         break;
904     case ID_UL:
905     {
906         n = new HTMLUListElementImpl(document);
907         break;
908     }
909     case ID_OL:
910     {
911         n = new HTMLOListElementImpl(document);
912         break;
913     }
914     case ID_DIR:
915         n = new HTMLDirectoryElementImpl(document);
916         break;
917     case ID_MENU:
918         n = new HTMLMenuElementImpl(document);
919         break;
920     case ID_LI:
921     {
922         popBlock(ID_LI);
923         n = new HTMLLIElementImpl(document);
924         break;
925     }
926 // formatting elements (block)
927     case ID_BLOCKQUOTE:
928         n = new HTMLBlockquoteElementImpl(document);
929         break;
930     case ID_DIV:
931         n = new HTMLDivElementImpl(document);
932         break;
933     case ID_LAYER:
934         n = new HTMLLayerElementImpl(document);
935         break;
936     case ID_H1:
937     case ID_H2:
938     case ID_H3:
939     case ID_H4:
940     case ID_H5:
941     case ID_H6:
942         n = new HTMLHeadingElementImpl(document, t->id);
943         break;
944     case ID_HR:
945         n = new HTMLHRElementImpl(document);
946         break;
947     case ID_P:
948         n = new HTMLParagraphElementImpl(document);
949         break;
950     case ID_XMP:
951     case ID_PRE:
952     case ID_PLAINTEXT:
953         n = new HTMLPreElementImpl(document, t->id);
954         break;
955
956 // font stuff
957     case ID_BASEFONT:
958         n = new HTMLBaseFontElementImpl(document);
959         break;
960     case ID_FONT:
961         n = new HTMLFontElementImpl(document);
962         break;
963
964 // ins/del
965     case ID_DEL:
966     case ID_INS:
967         n = new HTMLGenericElementImpl(document, t->id);
968         break;
969
970 // anchor
971     case ID_A:
972         // Never allow nested <a>s.
973         popBlock(ID_A);
974
975         n = new HTMLAnchorElementImpl(document);
976         break;
977
978 // canvas
979     case ID_CANVAS:
980         n = new HTMLCanvasElementImpl(document);
981         break;
982
983 // images
984     case ID_IMG:
985         n = new HTMLImageElementImpl(document);
986         break;
987     case ID_MAP:
988         map = new HTMLMapElementImpl(document);
989         n = map;
990         break;
991     case ID_AREA:
992         n = new HTMLAreaElementImpl(document);
993         break;
994
995 // objects, applets and scripts
996     case ID_APPLET:
997         n = new HTMLAppletElementImpl(document);
998         break;
999     case ID_EMBED:
1000         n = new HTMLEmbedElementImpl(document);
1001         break;
1002     case ID_OBJECT:
1003         n = new HTMLObjectElementImpl(document);
1004         break;
1005     case ID_PARAM:
1006         n = new HTMLParamElementImpl(document);
1007         break;
1008     case ID_SCRIPT:
1009         n = new HTMLScriptElementImpl(document);
1010         break;
1011
1012 // tables
1013     case ID_TABLE:
1014         n = new HTMLTableElementImpl(document);
1015         break;
1016     case ID_CAPTION:
1017         n = new HTMLTableCaptionElementImpl(document);
1018         break;
1019     case ID_COLGROUP:
1020     case ID_COL:
1021         n = new HTMLTableColElementImpl(document, t->id);
1022         break;
1023     case ID_TR:
1024         popBlock(ID_TR);
1025         n = new HTMLTableRowElementImpl(document);
1026         break;
1027     case ID_TD:
1028     case ID_TH:
1029         popBlock(ID_TH);
1030         popBlock(ID_TD);
1031         n = new HTMLTableCellElementImpl(document, t->id);
1032         break;
1033     case ID_TBODY:
1034     case ID_THEAD:
1035     case ID_TFOOT:
1036         popBlock( ID_THEAD );
1037         popBlock( ID_TBODY );
1038         popBlock( ID_TFOOT );
1039         n = new HTMLTableSectionElementImpl(document, t->id, false);
1040         break;
1041
1042 // inline elements
1043     case ID_BR:
1044         n = new HTMLBRElementImpl(document);
1045         break;
1046     case ID_Q:
1047         n = new HTMLGenericElementImpl(document, t->id);
1048         break;
1049
1050 // elements with no special representation in the DOM
1051
1052 // block:
1053     case ID_ADDRESS:
1054     case ID_CENTER:
1055         n = new HTMLGenericElementImpl(document, t->id);
1056         break;
1057 // inline
1058         // %fontstyle
1059     case ID_TT:
1060     case ID_U:
1061     case ID_B:
1062     case ID_I:
1063     case ID_S:
1064     case ID_STRIKE:
1065     case ID_BIG:
1066     case ID_SMALL:
1067         if (!allowNestedRedundantTag(t->id))
1068             return 0;
1069         // Fall through and get handled with the rest of the tags
1070         // %phrase
1071     case ID_EM:
1072     case ID_STRONG:
1073     case ID_DFN:
1074     case ID_CODE:
1075     case ID_SAMP:
1076     case ID_KBD:
1077     case ID_VAR:
1078     case ID_CITE:
1079     case ID_ABBR:
1080     case ID_ACRONYM:
1081
1082         // %special
1083     case ID_SUB:
1084     case ID_SUP:
1085     case ID_SPAN:
1086     case ID_NOBR:
1087     case ID_WBR:
1088         if (t->id == ID_NOBR || t->id == ID_WBR)
1089             popBlock(t->id); // Don't allow nested <nobr> or <wbr>
1090         n = new HTMLGenericElementImpl(document, t->id);
1091         break;
1092
1093     case ID_BDO:
1094         break;
1095
1096         // these are special, and normally not rendered
1097     case ID_NOEMBED:
1098         discard_until = ID_NOEMBED + ID_CLOSE_TAG;
1099         return 0;
1100     case ID_NOFRAMES:
1101         discard_until = ID_NOFRAMES + ID_CLOSE_TAG;
1102         return 0;
1103     case ID_NOSCRIPT:
1104         if(HTMLWidget && HTMLWidget->part()->jScriptEnabled())
1105             discard_until = ID_NOSCRIPT + ID_CLOSE_TAG;
1106         return 0;
1107     case ID_NOLAYER:
1108 //        discard_until = ID_NOLAYER + ID_CLOSE_TAG;
1109         return 0;
1110         break;
1111     case ID_MARQUEE:
1112         n = new HTMLMarqueeElementImpl(document);
1113         break;
1114 // text
1115     case ID_TEXT:
1116         n = new TextImpl(document, t->text);
1117         break;
1118     case ID_COMMENT:
1119 #ifdef COMMENTS_IN_DOM
1120         n = new CommentImpl(document, t->text);
1121 #endif
1122         break;
1123     default:
1124         kdDebug( 6035 ) << "Unknown tag " << t->id << "!" << endl;
1125     }
1126     return n;
1127 }
1128
1129 #define MAX_REDUNDANT 20
1130
1131 bool KHTMLParser::allowNestedRedundantTag(int _id)
1132 {
1133     // www.liceo.edu.mx is an example of a site that achieves a level of nesting of
1134     // about 1500 tags, all from a bunch of <b>s.  We will only allow at most 20
1135     // nested tags of the same type before just ignoring them all together.
1136     int i = 0;
1137     for (HTMLStackElem* curr = blockStack;
1138          i < MAX_REDUNDANT && curr && curr->id == _id;
1139          curr = curr->next, i++);
1140     return i != MAX_REDUNDANT;
1141 }
1142
1143 void KHTMLParser::processCloseTag(Token *t)
1144 {
1145     // support for really broken html. Can't believe I'm supporting such crap (lars)
1146     switch(t->id)
1147     {
1148     case ID_HTML+ID_CLOSE_TAG:
1149     case ID_BODY+ID_CLOSE_TAG:
1150         // we never close the body tag, since some stupid web pages close it before the actual end of the doc.
1151         // let's rely on the end() call to close things.
1152         return;
1153     case ID_FORM+ID_CLOSE_TAG:
1154         form = 0;
1155         // this one is to get the right style on the body element
1156         break;
1157     case ID_MAP+ID_CLOSE_TAG:
1158         map = 0;
1159         break;
1160     case ID_SELECT+ID_CLOSE_TAG:
1161         inSelect = false;
1162         break;
1163     default:
1164         break;
1165     }
1166
1167 #ifdef PARSER_DEBUG
1168     kdDebug( 6035 ) << "added the following childs to " << current->nodeName().string() << endl;
1169     NodeImpl *child = current->firstChild();
1170     while(child != 0)
1171     {
1172         kdDebug( 6035 ) << "    " << child->nodeName().string() << endl;
1173         child = child->nextSibling();
1174     }
1175 #endif
1176     HTMLStackElem* oldElem = blockStack;
1177     popBlock(t->id-ID_CLOSE_TAG);
1178     if (oldElem == blockStack && t->id == ID_P+ID_CLOSE_TAG) {
1179         // We encountered a stray </p>.  Amazingly Gecko, WinIE, and MacIE all treat
1180         // this as a valid break, i.e., <p></p>.  So go ahead and make the empty
1181         // paragraph.
1182         t->id-=ID_CLOSE_TAG;
1183         parseToken(t);
1184         popBlock(ID_P);
1185     }
1186 #ifdef PARSER_DEBUG
1187     kdDebug( 6035 ) << "closeTag --> current = " << current->nodeName().string() << endl;
1188 #endif
1189 }
1190
1191 bool KHTMLParser::isResidualStyleTag(int _id)
1192 {
1193     switch (_id) {
1194         case ID_A:
1195         case ID_FONT:
1196         case ID_TT:
1197         case ID_U:
1198         case ID_B:
1199         case ID_I:
1200         case ID_S:
1201         case ID_STRIKE:
1202         case ID_BIG:
1203         case ID_SMALL:
1204         case ID_EM:
1205         case ID_STRONG:
1206         case ID_DFN:
1207         case ID_CODE:
1208         case ID_SAMP:
1209         case ID_KBD:
1210         case ID_VAR:
1211             return true;
1212         default:
1213             return false;
1214     }
1215 }
1216
1217 bool KHTMLParser::isAffectedByResidualStyle(int _id)
1218 {
1219     if (isResidualStyleTag(_id))
1220         return true;
1221     
1222     switch (_id) {
1223         case ID_P:
1224         case ID_DIV:
1225         case ID_BLOCKQUOTE:
1226         case ID_ADDRESS:
1227         case ID_H1:
1228         case ID_H2:
1229         case ID_H3:
1230         case ID_H4:
1231         case ID_H5:
1232         case ID_H6:
1233         case ID_CENTER:
1234         case ID_UL:
1235         case ID_OL:
1236         case ID_LI:
1237         case ID_DL:
1238         case ID_DT:
1239         case ID_DD:
1240         case ID_PRE:
1241             return true;
1242         default:
1243             return false;
1244     }
1245 }
1246
1247 void KHTMLParser::handleResidualStyleCloseTagAcrossBlocks(HTMLStackElem* elem)
1248 {
1249     // Find the element that crosses over to a higher level.   For now, if there is more than
1250     // one, we will just give up and not attempt any sort of correction.  It's highly unlikely that
1251     // there will be more than one, since <p> tags aren't allowed to be nested.
1252     int exceptionCode = 0;
1253     HTMLStackElem* curr = blockStack;
1254     HTMLStackElem* maxElem = 0;
1255     HTMLStackElem* prev = 0;
1256     HTMLStackElem* prevMaxElem = 0;
1257     while (curr && curr != elem) {
1258         if (curr->level > elem->level) {
1259             if (maxElem)
1260                 return;
1261             maxElem = curr;
1262             prevMaxElem = prev;
1263         }
1264
1265         prev = curr;
1266         curr = curr->next;
1267     }
1268
1269     if (!curr || !maxElem || !isAffectedByResidualStyle(maxElem->id)) return;
1270
1271     NodeImpl* residualElem = prev->node;
1272     NodeImpl* blockElem = prevMaxElem ? prevMaxElem->node : current;
1273     NodeImpl* parentElem = elem->node;
1274
1275     // Check to see if the reparenting that is going to occur is allowed according to the DOM.
1276     // FIXME: We should either always allow it or perform an additional fixup instead of
1277     // just bailing here.
1278     // Example: <p><font><center>blah</font></center></p> isn't doing a fixup right now.
1279     if (!parentElem->childAllowed(blockElem))
1280         return;
1281     
1282     if (maxElem->node->parentNode() != elem->node) {
1283         // Walk the stack and remove any elements that aren't residual style tags.  These
1284         // are basically just being closed up.  Example:
1285         // <font><span>Moo<p>Goo</font></p>.
1286         // In the above example, the <span> doesn't need to be reopened.  It can just close.
1287         HTMLStackElem* currElem = maxElem->next;
1288         HTMLStackElem* prevElem = maxElem;
1289         while (currElem != elem) {
1290             HTMLStackElem* nextElem = currElem->next;
1291             if (!isResidualStyleTag(currElem->id)) {
1292                 prevElem->next = nextElem;
1293                 prevElem->node = currElem->node;
1294                 delete currElem;
1295             }
1296             else
1297                 prevElem = currElem;
1298             currElem = nextElem;
1299         }
1300         
1301         // We have to reopen residual tags in between maxElem and elem.  An example of this case is:
1302         // <font><i>Moo<p>Foo</font>.
1303         // In this case, we need to transform the part before the <p> into:
1304         // <font><i>Moo</i></font><i>
1305         // so that the <i> will remain open.  This involves the modification of elements
1306         // in the block stack.
1307         // This will also affect how we ultimately reparent the block, since we want it to end up
1308         // under the reopened residual tags (e.g., the <i> in the above example.)
1309         NodeImpl* prevNode = 0;
1310         NodeImpl* currNode = 0;
1311         currElem = maxElem;
1312         while (currElem->node != residualElem) {
1313             if (isResidualStyleTag(currElem->node->id())) {
1314                 // Create a clone of this element.
1315                 currNode = currElem->node->cloneNode(false);
1316
1317                 // Change the stack element's node to point to the clone.
1318                 currElem->node = currNode;
1319                 
1320                 // Attach the previous node as a child of this new node.
1321                 if (prevNode)
1322                     currNode->appendChild(prevNode, exceptionCode);
1323                 else // The new parent for the block element is going to be the innermost clone.
1324                     parentElem = currNode;
1325                                 
1326                 prevNode = currNode;
1327             }
1328             
1329             currElem = currElem->next;
1330         }
1331
1332         // Now append the chain of new residual style elements if one exists.
1333         if (prevNode)
1334             elem->node->appendChild(prevNode, exceptionCode);
1335     }
1336          
1337     // We need to make a clone of |residualElem| and place it just inside |blockElem|.
1338     // All content of |blockElem| is reparented to be under this clone.  We then
1339     // reparent |blockElem| using real DOM calls so that attachment/detachment will
1340     // be performed to fix up the rendering tree.
1341     // So for this example: <b>...<p>Foo</b>Goo</p>
1342     // The end result will be: <b>...</b><p><b>Foo</b>Goo</p>
1343     //
1344     // Step 1: Remove |blockElem| from its parent, doing a batch detach of all the kids.
1345     blockElem->parentNode()->removeChild(blockElem, exceptionCode);
1346         
1347     // Step 2: Clone |residualElem|.
1348     NodeImpl* newNode = residualElem->cloneNode(false); // Shallow clone. We don't pick up the same kids.
1349
1350     // Step 3: Place |blockElem|'s children under |newNode|.  Remove all of the children of |blockElem|
1351     // before we've put |newElem| into the document.  That way we'll only do one attachment of all
1352     // the new content (instead of a bunch of individual attachments).
1353     NodeImpl* currNode = blockElem->firstChild();
1354     while (currNode) {
1355         NodeImpl* nextNode = currNode->nextSibling();
1356         blockElem->removeChild(currNode, exceptionCode);
1357         newNode->appendChild(currNode, exceptionCode);
1358         currNode = nextNode;
1359     }
1360
1361     // Step 4: Place |newNode| under |blockElem|.  |blockElem| is still out of the document, so no
1362     // attachment can occur yet.
1363     blockElem->appendChild(newNode, exceptionCode);
1364     
1365     // Step 5: Reparent |blockElem|.  Now the full attachment of the fixed up tree takes place.
1366     parentElem->appendChild(blockElem, exceptionCode);
1367         
1368     // Step 6: Elide |elem|, since it is effectively no longer open.  Also update
1369     // the node associated with the previous stack element so that when it gets popped,
1370     // it doesn't make the residual element the next current node.
1371     HTMLStackElem* currElem = maxElem;
1372     HTMLStackElem* prevElem = 0;
1373     while (currElem != elem) {
1374         prevElem = currElem;
1375         currElem = currElem->next;
1376     }
1377     prevElem->next = elem->next;
1378     prevElem->node = elem->node;
1379     delete elem;
1380     
1381     // Step 7: Reopen intermediate inlines, e.g., <b><p><i>Foo</b>Goo</p>.
1382     // In the above example, Goo should stay italic.
1383     curr = blockStack;
1384     HTMLStackElem* residualStyleStack = 0;
1385     while (curr && curr != maxElem) {
1386         // We will actually schedule this tag for reopening
1387         // after we complete the close of this entire block.
1388         NodeImpl* currNode = current;
1389         if (isResidualStyleTag(curr->id)) {
1390             // We've overloaded the use of stack elements and are just reusing the
1391             // struct with a slightly different meaning to the variables.  Instead of chaining
1392             // from innermost to outermost, we build up a list of all the tags we need to reopen
1393             // from the outermost to the innermost, i.e., residualStyleStack will end up pointing
1394             // to the outermost tag we need to reopen.
1395             // We also set curr->node to be the actual element that corresponds to the ID stored in
1396             // curr->id rather than the node that you should pop to when the element gets pulled off
1397             // the stack.
1398             popOneBlock(false);
1399             curr->node = currNode;
1400             curr->next = residualStyleStack;
1401             residualStyleStack = curr;
1402         }
1403         else
1404             popOneBlock();
1405
1406         curr = blockStack;
1407     }
1408
1409     reopenResidualStyleTags(residualStyleStack, 0); // FIXME: Deal with stray table content some day
1410                                                     // if it becomes necessary to do so.
1411 }
1412
1413 void KHTMLParser::reopenResidualStyleTags(HTMLStackElem* elem, DOM::NodeImpl* malformedTableParent)
1414 {
1415     // Loop for each tag that needs to be reopened.
1416     while (elem) {
1417         // Create a shallow clone of the DOM node for this element.
1418         NodeImpl* newNode = elem->node->cloneNode(false); 
1419
1420         // Append the new node. In the malformed table case, we need to insert before the table,
1421         // which will be the last child.
1422         int exceptionCode = 0;
1423         if (malformedTableParent)
1424             malformedTableParent->insertBefore(newNode, malformedTableParent->lastChild(), exceptionCode);
1425         else
1426             current->appendChild(newNode, exceptionCode);
1427         // FIXME: Is it really OK to ignore the exceptions here?
1428
1429         // Now push a new stack element for this node we just created.
1430         pushBlock(elem->id, elem->level);
1431
1432         // Set our strayTableContent boolean if needed, so that the reopened tag also knows
1433         // that it is inside a malformed table.
1434         blockStack->strayTableContent = malformedTableParent != 0;
1435         if (blockStack->strayTableContent)
1436             inStrayTableContent++;
1437
1438         // Clear our malformed table parent variable.
1439         malformedTableParent = 0;
1440
1441         // Update |current| manually to point to the new node.
1442         setCurrent(newNode);
1443         
1444         // Advance to the next tag that needs to be reopened.
1445         HTMLStackElem* next = elem->next;
1446         delete elem;
1447         elem = next;
1448     }
1449 }
1450
1451 void KHTMLParser::pushBlock(int _id, int _level)
1452 {
1453     HTMLStackElem *Elem = new HTMLStackElem(_id, _level, current, blockStack);
1454
1455     blockStack = Elem;
1456     addForbidden(_id, forbiddenTag);
1457 }
1458
1459 void KHTMLParser::popBlock( int _id )
1460 {
1461     HTMLStackElem *Elem = blockStack;
1462     
1463     int maxLevel = 0;
1464
1465 #ifdef PARSER_DEBUG
1466     kdDebug( 6035 ) << "popBlock(" << getTagName(_id).string() << ")" << endl;
1467     while(Elem) {
1468         kdDebug( 6035) << "   > " << getTagName(Elem->id).string() << endl;
1469         Elem = Elem->next;
1470     }
1471     Elem = blockStack;
1472 #endif
1473
1474     while( Elem && (Elem->id != _id))
1475     {
1476         if (maxLevel < Elem->level)
1477         {
1478             maxLevel = Elem->level;
1479         }
1480         Elem = Elem->next;
1481     }
1482
1483     if (!Elem)
1484         return;
1485
1486     if (maxLevel > Elem->level) {
1487         // We didn't match because the tag is in a different scope, e.g.,
1488         // <b><p>Foo</b>.  Try to correct the problem.
1489         if (!isResidualStyleTag(_id))
1490             return;
1491         return handleResidualStyleCloseTagAcrossBlocks(Elem);
1492     }
1493
1494     bool isAffectedByStyle = isAffectedByResidualStyle(Elem->id);
1495     HTMLStackElem* residualStyleStack = 0;
1496     NodeImpl* malformedTableParent = 0;
1497     
1498     Elem = blockStack;
1499     while (Elem)
1500     {
1501         if (Elem->id == _id)
1502         {
1503             int strayTable = inStrayTableContent;
1504             popOneBlock();
1505             Elem = 0;
1506
1507             // This element was the root of some malformed content just inside an implicit or
1508             // explicit <tbody> or <tr>.
1509             // If we end up needing to reopen residual style tags, the root of the reopened chain
1510             // must also know that it is the root of malformed content inside a <tbody>/<tr>.
1511             if (strayTable && (inStrayTableContent < strayTable) && residualStyleStack) {
1512                 NodeImpl* curr = current;
1513                 while (curr && curr->id() != ID_TABLE)
1514                     curr = curr->parentNode();
1515                 malformedTableParent = curr ? curr->parentNode() : 0;
1516             }
1517         }
1518         else
1519         {
1520             if (Elem->id == ID_FORM && form)
1521                 // A <form> is being closed prematurely (and this is
1522                 // malformed HTML).  Set an attribute on the form to clear out its
1523                 // bottom margin.
1524                 form->setMalformed(true);
1525
1526             // Schedule this tag for reopening
1527             // after we complete the close of this entire block.
1528             NodeImpl* currNode = current;
1529             if (isAffectedByStyle && isResidualStyleTag(Elem->id)) {
1530                 // We've overloaded the use of stack elements and are just reusing the
1531                 // struct with a slightly different meaning to the variables.  Instead of chaining
1532                 // from innermost to outermost, we build up a list of all the tags we need to reopen
1533                 // from the outermost to the innermost, i.e., residualStyleStack will end up pointing
1534                 // to the outermost tag we need to reopen.
1535                 // We also set Elem->node to be the actual element that corresponds to the ID stored in
1536                 // Elem->id rather than the node that you should pop to when the element gets pulled off
1537                 // the stack.
1538                 popOneBlock(false);
1539                 Elem->next = residualStyleStack;
1540                 Elem->node = currNode;
1541                 residualStyleStack = Elem;
1542             }
1543             else
1544                 popOneBlock();
1545             Elem = blockStack;
1546         }
1547     }
1548
1549     reopenResidualStyleTags(residualStyleStack, malformedTableParent);
1550 }
1551
1552 void KHTMLParser::popOneBlock(bool delBlock)
1553 {
1554     HTMLStackElem *Elem = blockStack;
1555
1556     // we should never get here, but some bad html might cause it.
1557 #ifndef PARSER_DEBUG
1558     if(!Elem) return;
1559 #else
1560     kdDebug( 6035 ) << "popping block: " << getTagName(Elem->id).string() << "(" << Elem->id << ")" << endl;
1561 #endif
1562
1563 #if SPEED_DEBUG < 1
1564     if((Elem->node != current)) {
1565         if (current->maintainsState() && document->document()){
1566             document->document()->registerMaintainsState(current);
1567             QStringList &states = document->document()->restoreState();
1568             if (!states.isEmpty())
1569                 current->restoreState(states);
1570         }
1571     }
1572 #endif
1573
1574     removeForbidden(Elem->id, forbiddenTag);
1575
1576     blockStack = Elem->next;
1577     setCurrent(Elem->node);
1578
1579     if (Elem->strayTableContent)
1580         inStrayTableContent--;
1581
1582     if (delBlock)
1583         delete Elem;
1584 }
1585
1586 void KHTMLParser::popInlineBlocks()
1587 {
1588     while(current->isInline())
1589         popOneBlock();
1590 }
1591
1592 void KHTMLParser::freeBlock()
1593 {
1594     while (blockStack)
1595         popOneBlock();
1596 }
1597
1598 void KHTMLParser::createHead()
1599 {
1600     if(head || !doc()->firstChild())
1601         return;
1602
1603     head = new HTMLHeadElementImpl(document);
1604     HTMLElementImpl *body = doc()->body();
1605     int exceptioncode = 0;
1606     doc()->firstChild()->insertBefore(head, body, exceptioncode);
1607     if ( exceptioncode ) {
1608 #ifdef PARSER_DEBUG
1609         kdDebug( 6035 ) << "creation of head failed!!!!" << endl;
1610 #endif
1611         delete head;
1612         head = 0;
1613     }
1614 }
1615
1616 NodeImpl *KHTMLParser::handleIsindex( Token *t )
1617 {
1618     NodeImpl *n;
1619     HTMLFormElementImpl *myform = form;
1620     if ( !myform ) {
1621         myform = new HTMLFormElementImpl(document);
1622         n = myform;
1623     } else
1624         n = new HTMLDivElementImpl( document );
1625     NodeImpl *child = new HTMLHRElementImpl( document );
1626     n->addChild( child );
1627     AttributeImpl* a = t->attrs ? t->attrs->getAttributeItem(ATTR_PROMPT) : 0;
1628 #if APPLE_CHANGES
1629     DOMString text = searchableIndexIntroduction();
1630 #else
1631     DOMString text = i18n("This is a searchable index. Enter search keywords: ");
1632 #endif
1633     if (a)
1634         text = DOMString(a->value()) + " ";
1635     child = new TextImpl(document, text);
1636     n->addChild( child );
1637     child = new HTMLIsIndexElementImpl(document, myform);
1638     static_cast<ElementImpl *>(child)->setAttribute(ATTR_TYPE, "khtml_isindex");
1639     n->addChild( child );
1640     child = new HTMLHRElementImpl( document );
1641     n->addChild( child );
1642
1643     return n;
1644 }
1645
1646 void KHTMLParser::startBody()
1647 {
1648     if(inBody) return;
1649
1650     inBody = true;
1651
1652     if( isindex ) {
1653         insertNode( isindex, true /* don't decend into this node */ );
1654         isindex = 0;
1655     }
1656 }
1657
1658 void KHTMLParser::finished()
1659 {
1660     // This ensures that "current" is not left pointing to a node when the document is destroyed.
1661     freeBlock();
1662     setCurrent(0);
1663 }