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