http://bugs.webkit.org/show_bug.cgi?id=16982
[WebKit-https.git] / WebCore / html / HTMLParser.cpp
1 /*
2     Copyright (C) 1997 Martin Jones (mjones@kde.org)
3               (C) 1997 Torben Weis (weis@kde.org)
4               (C) 1999,2001 Lars Knoll (knoll@kde.org)
5               (C) 2000,2001 Dirk Mueller (mueller@kde.org)
6     Copyright (C) 2004, 2005, 2006, 2007 Apple Inc. All rights reserved.
7
8     This library is free software; you can redistribute it and/or
9     modify it under the terms of the GNU Library General Public
10     License as published by the Free Software Foundation; either
11     version 2 of the License, or (at your option) any later version.
12
13     This library is distributed in the hope that it will be useful,
14     but WITHOUT ANY WARRANTY; without even the implied warranty of
15     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16     Library General Public License for more details.
17
18     You should have received a copy of the GNU Library General Public License
19     along with this library; see the file COPYING.LIB.  If not, write to
20     the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21     Boston, MA 02110-1301, USA.
22 */
23
24 #include "config.h"
25 #include "HTMLParser.h"
26
27 #include "CharacterNames.h"
28 #include "CSSPropertyNames.h"
29 #include "CSSValueKeywords.h"
30 #include "Comment.h"
31 #include "DocumentFragment.h"
32 #include "Frame.h"
33 #include "HTMLBodyElement.h"
34 #include "HTMLDocument.h"
35 #include "HTMLDivElement.h"
36 #include "HTMLDListElement.h"
37 #include "HTMLElementFactory.h"
38 #include "HTMLFormElement.h"
39 #include "HTMLHeadElement.h"
40 #include "HTMLHRElement.h"
41 #include "HTMLHtmlElement.h"
42 #include "HTMLIsIndexElement.h"
43 #include "HTMLMapElement.h"
44 #include "HTMLNames.h"
45 #include "HTMLTableCellElement.h"
46 #include "HTMLTableRowElement.h"
47 #include "HTMLTableSectionElement.h"
48 #include "HTMLTokenizer.h"
49 #include "LocalizedStrings.h"
50 #include "Page.h"
51 #include "Settings.h"
52 #include "Text.h"
53
54 namespace WebCore {
55
56 using namespace HTMLNames;
57
58 static const unsigned cMaxRedundantTagDepth = 20;
59 static const unsigned cResidualStyleMaxDepth = 200;
60
61 struct HTMLStackElem : Noncopyable {
62     HTMLStackElem(const AtomicString& t, int lvl, Node* n, bool r, HTMLStackElem* nx)
63         : tagName(t)
64         , level(lvl)
65         , strayTableContent(false)
66         , node(n)
67         , didRefNode(r)
68         , next(nx)
69     {
70     }
71
72     void derefNode()
73     {
74         if (didRefNode)
75             node->deref();
76     }
77
78     AtomicString tagName;
79     int level;
80     bool strayTableContent;
81     Node* node;
82     bool didRefNode;
83     HTMLStackElem* next;
84 };
85
86 /**
87  * The parser parses tokenized input into the document, building up the
88  * document tree. If the document is well-formed, parsing it is straightforward.
89  *
90  * Unfortunately, we have to handle many HTML documents that are not well-formed,
91  * so the parser has to be tolerant about errors.
92  *
93  * We have to take care of at least the following error conditions:
94  *
95  * 1. The element being added is explicitly forbidden inside some outer tag.
96  *    In this case we should close all tags up to the one, which forbids
97  *    the element, and add it afterwards.
98  *
99  * 2. We are not allowed to add the element directly. It could be that
100  *    the person writing the document forgot some tag in between (or that the
101  *    tag in between is optional). This could be the case with the following
102  *    tags: HTML HEAD BODY TBODY TR TD LI (did I forget any?).
103  *
104  * 3. We want to add a block element inside to an inline element. Close all
105  *    inline elements up to the next higher block element.
106  *
107  * 4. If this doesn't help, close elements until we are allowed to add the
108  *    element or ignore the tag.
109  *
110  */
111
112 HTMLParser::HTMLParser(HTMLDocument* doc, bool reportErrors)
113     : document(doc)
114     , current(doc)
115     , didRefCurrent(false)
116     , blockStack(0)
117     , head(0)
118     , inBody(false)
119     , haveContent(false)
120     , haveFrameSet(false)
121     , m_isParsingFragment(false)
122     , m_reportErrors(reportErrors)
123     , m_handlingResidualStyleAcrossBlocks(false)
124     , inStrayTableContent(0)
125 {
126 }
127
128 HTMLParser::HTMLParser(DocumentFragment* frag)
129     : document(frag->document())
130     , current(frag)
131     , didRefCurrent(true)
132     , blockStack(0)
133     , head(0)
134     , inBody(true)
135     , haveContent(false)
136     , haveFrameSet(false)
137     , m_isParsingFragment(true)
138     , m_reportErrors(false)
139     , m_handlingResidualStyleAcrossBlocks(false)
140     , inStrayTableContent(0)
141 {
142     if (frag)
143         frag->ref();
144 }
145
146 HTMLParser::~HTMLParser()
147 {
148     freeBlock();
149     if (didRefCurrent) 
150         current->deref(); 
151 }
152
153 void HTMLParser::reset()
154 {
155     ASSERT(!m_isParsingFragment);
156
157     setCurrent(document);
158
159     freeBlock();
160
161     inBody = false;
162     haveFrameSet = false;
163     haveContent = false;
164     inStrayTableContent = 0;
165
166     m_currentFormElement = 0;
167     m_currentMapElement = 0;
168     head = 0;
169     m_isindexElement = 0;
170
171     m_skipModeTag = nullAtom;
172 }
173
174 void HTMLParser::setCurrent(Node* newCurrent) 
175 {
176     bool didRefNewCurrent = newCurrent && newCurrent != document;
177     if (didRefNewCurrent) 
178         newCurrent->ref(); 
179     if (didRefCurrent) 
180         current->deref(); 
181     current = newCurrent;
182     didRefCurrent = didRefNewCurrent;
183 }
184
185 PassRefPtr<Node> HTMLParser::parseToken(Token* t)
186 {
187     if (!m_skipModeTag.isNull()) {
188         if (!t->beginTag && t->tagName == m_skipModeTag)
189             // Found the end tag for the current skip mode, so we're done skipping.
190             m_skipModeTag = nullAtom;
191         else if (current->localName() == t->tagName)
192             // Do not skip </iframe>.
193             // FIXME: What does that comment mean? How can it be right to parse a token without clearing m_skipModeTag?
194             ;
195         else
196             return 0;
197     }
198
199     // Apparently some sites use </br> instead of <br>. Be compatible with IE and Firefox and treat this like <br>.
200     if (t->isCloseTag(brTag) && document->inCompatMode()) {
201         reportError(MalformedBRError);
202         t->beginTag = true;
203     }
204
205     if (!t->beginTag) {
206         processCloseTag(t);
207         return 0;
208     }
209
210     // ignore spaces, if we're not inside a paragraph or other inline code
211     if (t->tagName == textAtom && t->text) {
212         if (inBody && !skipMode() && current->localName() != styleTag && current->localName() != titleTag && 
213             current->localName() != scriptTag && !t->text->containsOnlyWhitespace()) 
214             haveContent = true;
215         
216         RefPtr<Node> n;
217         String text = t->text.get();
218         unsigned charsLeft = text.length();
219         while (charsLeft) {
220             // split large blocks of text to nodes of manageable size
221             n = Text::createWithLengthLimit(document, text, charsLeft);
222             if (!insertNode(n.get(), t->flat))
223                 return 0;
224         }
225         return n;
226     }
227
228     RefPtr<Node> n = getNode(t);
229     // just to be sure, and to catch currently unimplemented stuff
230     if (!n)
231         return 0;
232
233     // set attributes
234     if (n->isHTMLElement()) {
235         HTMLElement* e = static_cast<HTMLElement*>(n.get());
236         e->setAttributeMap(t->attrs.get());
237
238         // take care of optional close tags
239         if (e->endTagRequirement() == TagStatusOptional)
240             popBlock(t->tagName);
241             
242         // If the node does not have a forbidden end tag requirement, and if the broken XML self-closing
243         // syntax was used, report an error.
244         if (t->brokenXMLStyle && e->endTagRequirement() != TagStatusForbidden) {
245             if (t->tagName == scriptTag)
246                 reportError(IncorrectXMLCloseScriptWarning);
247             else
248                 reportError(IncorrectXMLSelfCloseError, &t->tagName);
249         }
250     }
251
252     if (!insertNode(n.get(), t->flat)) {
253         // we couldn't insert the node
254
255         if (n->isElementNode()) {
256             Element* e = static_cast<Element*>(n.get());
257             e->setAttributeMap(0);
258         }
259
260         if (m_currentMapElement == n)
261             m_currentMapElement = 0;
262
263         if (m_currentFormElement == n)
264             m_currentFormElement = 0;
265
266         if (head == n)
267             head = 0;
268
269         return 0;
270     }
271     return n;
272 }
273
274 static bool isTableSection(Node* n)
275 {
276     return n->hasTagName(tbodyTag) || n->hasTagName(tfootTag) || n->hasTagName(theadTag);
277 }
278
279 static bool isTablePart(Node* n)
280 {
281     return n->hasTagName(trTag) || n->hasTagName(tdTag) || n->hasTagName(thTag) ||
282            isTableSection(n);
283 }
284
285 static bool isTableRelated(Node* n)
286 {
287     return n->hasTagName(tableTag) || isTablePart(n);
288 }
289
290 bool HTMLParser::insertNode(Node* n, bool flat)
291 {
292     RefPtr<Node> protectNode(n);
293
294     const AtomicString& localName = n->localName();
295     int tagPriority = n->isHTMLElement() ? static_cast<HTMLElement*>(n)->tagPriority() : 0;
296     
297     // <table> is never allowed inside stray table content.  Always pop out of the stray table content
298     // and close up the first table, and then start the second table as a sibling.
299     if (inStrayTableContent && localName == tableTag)
300         popBlock(tableTag);
301     
302     // let's be stupid and just try to insert it.
303     // this should work if the document is well-formed
304     Node* newNode = current->addChild(n);
305     if (!newNode)
306         return handleError(n, flat, localName, tagPriority); // Try to handle the error.
307
308     // don't push elements without end tags (e.g., <img>) on the stack
309     bool parentAttached = current->attached();
310     if (tagPriority > 0 && !flat) {
311         if (newNode == current) {
312             // This case should only be hit when a demoted <form> is placed inside a table.
313             ASSERT(localName == formTag);
314             reportError(FormInsideTablePartError, &current->localName());
315         } else {
316             // The pushBlock function transfers ownership of current to the block stack
317             // so we're guaranteed that didRefCurrent is false. The code below is an
318             // optimized version of setCurrent that takes advantage of that fact and also
319             // assumes that newNode is neither 0 nor a pointer to the document.
320             pushBlock(localName, tagPriority);
321             ASSERT(!didRefCurrent);
322             newNode->ref(); 
323             current = newNode;
324             didRefCurrent = true;
325         }
326         if (parentAttached && !n->attached() && !m_isParsingFragment)
327             n->attach();
328     } else {
329         if (parentAttached && !n->attached() && !m_isParsingFragment)
330             n->attach();
331         n->finishedParsing();
332     }
333
334     return true;
335 }
336
337 bool HTMLParser::handleError(Node* n, bool flat, const AtomicString& localName, int tagPriority)
338 {
339     // Error handling code.  This is just ad hoc handling of specific parent/child combinations.
340     HTMLElement* e;
341     bool handled = false;
342
343     // 1. Check out the element's tag name to decide how to deal with errors.
344     if (n->isHTMLElement()) {
345         HTMLElement* h = static_cast<HTMLElement*>(n);
346         if (h->hasLocalName(trTag) || h->hasLocalName(thTag) || h->hasLocalName(tdTag)) {
347             if (inStrayTableContent && !isTableRelated(current)) {
348                 reportError(MisplacedTablePartError, &localName, &current->localName());
349                 // pop out to the nearest enclosing table-related tag.
350                 while (blockStack && !isTableRelated(current))
351                     popOneBlock();
352                 return insertNode(n);
353             }
354         } else if (h->hasLocalName(headTag)) {
355             if (!current->isDocumentNode() && !current->hasTagName(htmlTag)) {
356                 reportError(MisplacedHeadError);
357                 return false;
358             }
359         } else if (h->hasLocalName(metaTag) || h->hasLocalName(linkTag) || h->hasLocalName(baseTag)) {
360             bool createdHead = false;
361             if (!head) {
362                 createHead();
363                 createdHead = true;
364             }
365             if (head) {
366                 if (!createdHead)
367                     reportError(MisplacedHeadContentError, &localName, &current->localName());
368                 if (head->addChild(n)) {
369                     if (!n->attached() && !m_isParsingFragment)
370                         n->attach();
371                     return true;
372                 } else
373                     return false;
374             }
375         } else if (h->hasLocalName(htmlTag)) {
376             if (!current->isDocumentNode() ) {
377                 if (document->documentElement()->hasTagName(htmlTag)) {
378                     reportError(RedundantHTMLBodyError, &localName);
379                     // we have another <HTML> element.... apply attributes to existing one
380                     // make sure we don't overwrite already existing attributes
381                     NamedAttrMap* map = static_cast<Element*>(n)->attributes(true);
382                     Element* existingHTML = static_cast<Element*>(document->documentElement());
383                     NamedAttrMap* bmap = existingHTML->attributes(false);
384                     for (unsigned l = 0; map && l < map->length(); ++l) {
385                         Attribute* it = map->attributeItem(l);
386                         if (!bmap->getAttributeItem(it->name()))
387                             existingHTML->setAttribute(it->name(), it->value());
388                     }
389                 }
390                 return false;
391             }
392         } else if (h->hasLocalName(titleTag) || h->hasLocalName(styleTag)) {
393             bool createdHead = false;
394             if (!head) {
395                 createHead();
396                 createdHead = true;
397             }
398             if (head) {
399                 Node* newNode = head->addChild(n);
400                 if (!newNode) {
401                     setSkipMode(h->tagQName());
402                     return false;
403                 }
404                 
405                 if (!createdHead)
406                     reportError(MisplacedHeadContentError, &localName, &current->localName());
407                 
408                 pushBlock(localName, tagPriority);
409                 setCurrent(newNode);
410                 if (!n->attached() && !m_isParsingFragment)
411                     n->attach();
412                 return true;
413             }
414             if (inBody) {
415                 setSkipMode(h->tagQName());
416                 return false;
417             }
418         } else if (h->hasLocalName(bodyTag)) {
419             if (inBody && document->body()) {
420                 // we have another <BODY> element.... apply attributes to existing one
421                 // make sure we don't overwrite already existing attributes
422                 // some sites use <body bgcolor=rightcolor>...<body bgcolor=wrongcolor>
423                 reportError(RedundantHTMLBodyError, &localName);
424                 NamedAttrMap* map = static_cast<Element*>(n)->attributes(true);
425                 Element* existingBody = document->body();
426                 NamedAttrMap* bmap = existingBody->attributes(false);
427                 for (unsigned l = 0; map && l < map->length(); ++l) {
428                     Attribute* it = map->attributeItem(l);
429                     if (!bmap->getAttributeItem(it->name()))
430                         existingBody->setAttribute(it->name(), it->value());
431                 }
432                 return false;
433             }
434             else if (!current->isDocumentNode())
435                 return false;
436         } else if (h->hasLocalName(areaTag)) {
437             if (m_currentMapElement) {
438                 reportError(MisplacedAreaError, &current->localName());
439                 m_currentMapElement->addChild(n);
440                 if (!n->attached() && !m_isParsingFragment)
441                     n->attach();
442                 handled = true;
443                 return true;
444             }
445             return false;
446         } else if (h->hasLocalName(colgroupTag) || h->hasLocalName(captionTag)) {
447             if (isTableRelated(current)) {
448                 while (blockStack && isTablePart(current))
449                     popOneBlock();
450                 return insertNode(n);
451             }
452         }
453     } else if (n->isCommentNode() && !head)
454         return false;
455
456     // 2. Next we examine our currently active element to do some further error handling.
457     if (current->isHTMLElement()) {
458         HTMLElement* h = static_cast<HTMLElement*>(current);
459         const AtomicString& currentTagName = current->localName();
460         if (h->hasLocalName(htmlTag)) {
461             HTMLElement* elt = n->isHTMLElement() ? static_cast<HTMLElement*>(n) : 0;
462             if (elt && (elt->hasLocalName(scriptTag) || elt->hasLocalName(styleTag) ||
463                 elt->hasLocalName(metaTag) || elt->hasLocalName(linkTag) ||
464                 elt->hasLocalName(objectTag) || elt->hasLocalName(embedTag) ||
465                 elt->hasLocalName(titleTag) || elt->hasLocalName(isindexTag) ||
466                 elt->hasLocalName(baseTag))) {
467                 if (!head) {
468                     head = new HTMLHeadElement(document);
469                     e = head;
470                     insertNode(e);
471                     handled = true;
472                 }
473             } else {
474                 if (n->isTextNode()) {
475                     Text* t = static_cast<Text*>(n);
476                     if (t->containsOnlyWhitespace())
477                         return false;
478                 }
479                 if (!haveFrameSet) {
480                     e = new HTMLBodyElement(document);
481                     startBody();
482                     insertNode(e);
483                     handled = true;
484                 } else
485                     reportError(MisplacedFramesetContentError, &localName);
486             }
487         } else if (h->hasLocalName(headTag)) {
488             if (n->hasTagName(htmlTag))
489                 return false;
490             else {
491                 // This means the body starts here...
492                 if (!haveFrameSet) {
493                     popBlock(currentTagName);
494                     e = new HTMLBodyElement(document);
495                     startBody();
496                     insertNode(e);
497                     handled = true;
498                 } else
499                     reportError(MisplacedFramesetContentError, &localName);
500             }
501         } else if (h->hasLocalName(addressTag) || h->hasLocalName(dlTag) || h->hasLocalName(dtTag)
502                    || h->hasLocalName(fontTag) || h->hasLocalName(styleTag) || h->hasLocalName(titleTag)) {
503             reportError(MisplacedContentRetryError, &localName, &currentTagName);
504             popBlock(currentTagName);
505             handled = true;
506         } else if (h->hasLocalName(captionTag)) {
507             // Illegal content in a caption. Close the caption and try again.
508             reportError(MisplacedCaptionContentError, &localName);
509             popBlock(currentTagName);
510             if (isTablePart(n))
511                 return insertNode(n, flat);
512         } else if (h->hasLocalName(tableTag) || h->hasLocalName(trTag) || isTableSection(h)) {
513             if (n->hasTagName(tableTag)) {
514                 reportError(MisplacedTableError, &currentTagName);
515                 if (m_isParsingFragment && !h->hasLocalName(tableTag))
516                     // fragment may contain table parts without <table> ancestor, pop them one by one
517                     popBlock(h->localName());
518                 popBlock(localName); // end the table
519                 handled = true;      // ...and start a new one
520             } else {
521                 ExceptionCode ec = 0;
522                 Node* node = current;
523                 Node* parent = node->parentNode();
524                 // A script may have removed the current node's parent from the DOM
525                 // http://bugs.webkit.org/show_bug.cgi?id=7137
526                 // FIXME: we should do real recovery here and re-parent with the correct node.
527                 if (!parent)
528                     return false;
529                 Node* grandparent = parent->parentNode();
530
531                 if (n->isTextNode() ||
532                     (h->hasLocalName(trTag) &&
533                      isTableSection(parent) && grandparent && grandparent->hasTagName(tableTag)) ||
534                      ((!n->hasTagName(tdTag) && !n->hasTagName(thTag) &&
535                        !n->hasTagName(formTag) && !n->hasTagName(scriptTag)) && isTableSection(node) &&
536                      parent->hasTagName(tableTag))) {
537                     node = (node->hasTagName(tableTag)) ? node :
538                             ((node->hasTagName(trTag)) ? grandparent : parent);
539                     // This can happen with fragments
540                     if (!node)
541                         return false;
542                     Node* parent = node->parentNode();
543                     if (!parent)
544                         return false;
545                     parent->insertBefore(n, node, ec);
546                     if (!ec) {
547                         reportError(StrayTableContentError, &localName, &currentTagName);
548                         if (n->isHTMLElement() && tagPriority > 0 && 
549                             !flat && static_cast<HTMLElement*>(n)->endTagRequirement() != TagStatusForbidden)
550                         {
551                             pushBlock(localName, tagPriority);
552                             setCurrent(n);
553                             inStrayTableContent++;
554                             blockStack->strayTableContent = true;
555                         }
556                         return true;
557                     }
558                 }
559
560                 if (!ec) {
561                     if (current->hasTagName(trTag)) {
562                         reportError(TablePartRequiredError, &localName, &tdTag.localName());
563                         e = new HTMLTableCellElement(tdTag, document);
564                     } else if (current->hasTagName(tableTag)) {
565                         // Don't report an error in this case, since making a <tbody> happens all the time when you have <table><tr>,
566                         // and it isn't really a parse error per se.
567                         e = new HTMLTableSectionElement(tbodyTag, document); 
568                     } else {
569                         reportError(TablePartRequiredError, &localName, &trTag.localName());
570                         e = new HTMLTableRowElement(document);
571                     }
572
573                     insertNode(e);
574                     handled = true;
575                 }
576             }
577         } else if (h->hasLocalName(objectTag)) {
578             reportError(MisplacedContentRetryError, &localName, &currentTagName);
579             popBlock(objectTag);
580             handled = true;
581         } else if (h->hasLocalName(pTag) || isHeaderTag(currentTagName)) {
582             if (!isInline(n)) {
583                 popBlock(currentTagName);
584                 handled = true;
585             }
586         } else if (h->hasLocalName(optionTag) || h->hasLocalName(optgroupTag)) {
587             if (localName == optgroupTag) {
588                 popBlock(currentTagName);
589                 handled = true;
590             } else if (localName == selectTag) {
591                 // IE treats a nested select as </select>. Let's do the same
592                 popBlock(localName);
593             }
594         } else if (h->hasLocalName(colgroupTag)) {
595             popBlock(currentTagName);
596             handled = true;
597         } else if (!h->hasLocalName(bodyTag)) {
598             if (isInline(current)) {
599                 popInlineBlocks();
600                 handled = true;
601             }
602         }
603     } else if (current->isDocumentNode()) {
604         if (n->isTextNode()) {
605             Text* t = static_cast<Text*>(n);
606             if (t->containsOnlyWhitespace())
607                 return false;
608         }
609
610         if (!document->documentElement()) {
611             e = new HTMLHtmlElement(document);
612             insertNode(e);
613             handled = true;
614         }
615     }
616
617     // 3. If we couldn't handle the error, just return false and attempt to error-correct again.
618     if (!handled) {
619         reportError(IgnoredContentError, &localName, &current->localName());
620         return false;
621     }
622     return insertNode(n);
623 }
624
625 typedef bool (HTMLParser::*CreateErrorCheckFunc)(Token* t, RefPtr<Node>&);
626 typedef HashMap<AtomicStringImpl*, CreateErrorCheckFunc> FunctionMap;
627
628 bool HTMLParser::textCreateErrorCheck(Token* t, RefPtr<Node>& result)
629 {
630     result = new Text(document, t->text.get());
631     return false;
632 }
633
634 bool HTMLParser::commentCreateErrorCheck(Token* t, RefPtr<Node>& result)
635 {
636     result = new Comment(document, t->text.get());
637     return false;
638 }
639
640 bool HTMLParser::headCreateErrorCheck(Token* t, RefPtr<Node>& result)
641 {
642     if (!head || current->localName() == htmlTag) {
643         head = new HTMLHeadElement(document);
644         result = head;
645     } else
646         reportError(MisplacedHeadError);
647     return false;
648 }
649
650 bool HTMLParser::bodyCreateErrorCheck(Token* t, RefPtr<Node>& result)
651 {
652     // body no longer allowed if we have a frameset
653     if (haveFrameSet)
654         return false;
655     popBlock(headTag);
656     startBody();
657     return true;
658 }
659
660 bool HTMLParser::framesetCreateErrorCheck(Token* t, RefPtr<Node>& result)
661 {
662     popBlock(headTag);
663     if (inBody && !haveFrameSet && !haveContent) {
664         popBlock(bodyTag);
665         // ### actually for IE document.body returns the now hidden "body" element
666         // we can't implement that behaviour now because it could cause too many
667         // regressions and the headaches are not worth the work as long as there is
668         // no site actually relying on that detail (Dirk)
669         if (document->body())
670             document->body()->setAttribute(styleAttr, "display:none");
671         inBody = false;
672     }
673     if ((haveContent || haveFrameSet) && current->localName() == htmlTag)
674         return false;
675     haveFrameSet = true;
676     startBody();
677     return true;
678 }
679
680 bool HTMLParser::iframeCreateErrorCheck(Token* t, RefPtr<Node>& result)
681 {
682     // a bit of a special case, since the frame is inlined
683     setSkipMode(iframeTag);
684     return true;
685 }
686
687 bool HTMLParser::formCreateErrorCheck(Token* t, RefPtr<Node>& result)
688 {
689     // Only create a new form if we're not already inside one.
690     // This is consistent with other browsers' behavior.
691     if (!m_currentFormElement) {
692         m_currentFormElement = new HTMLFormElement(document);
693         result = m_currentFormElement;
694     }
695     return false;
696 }
697
698 bool HTMLParser::isindexCreateErrorCheck(Token* t, RefPtr<Node>& result)
699 {
700     RefPtr<Node> n = handleIsindex(t);
701     if (!inBody) {
702         m_isindexElement = n.release();
703     } else {
704         t->flat = true;
705         result = n.release();
706     }
707     return false;
708 }
709
710 bool HTMLParser::selectCreateErrorCheck(Token* t, RefPtr<Node>& result)
711 {
712     return true;
713 }
714
715 bool HTMLParser::ddCreateErrorCheck(Token* t, RefPtr<Node>& result)
716 {
717     popBlock(dtTag);
718     popBlock(ddTag);
719     return true;
720 }
721
722 bool HTMLParser::dtCreateErrorCheck(Token* t, RefPtr<Node>& result)
723 {
724     popBlock(ddTag);
725     popBlock(dtTag);
726     return true;
727 }
728
729 bool HTMLParser::nestedCreateErrorCheck(Token* t, RefPtr<Node>& result)
730 {
731     popBlock(t->tagName);
732     return true;
733 }
734
735 bool HTMLParser::nestedStyleCreateErrorCheck(Token* t, RefPtr<Node>& result)
736 {
737     return allowNestedRedundantTag(t->tagName);
738 }
739
740 bool HTMLParser::tableCellCreateErrorCheck(Token* t, RefPtr<Node>& result)
741 {
742     popBlock(tdTag);
743     popBlock(thTag);
744     return true;
745 }
746
747 bool HTMLParser::tableSectionCreateErrorCheck(Token* t, RefPtr<Node>& result)
748 {
749     popBlock(theadTag);
750     popBlock(tbodyTag);
751     popBlock(tfootTag);
752     return true;
753 }
754
755 bool HTMLParser::noembedCreateErrorCheck(Token* t, RefPtr<Node>& result)
756 {
757     setSkipMode(noembedTag);
758     return true;
759 }
760
761 bool HTMLParser::noframesCreateErrorCheck(Token* t, RefPtr<Node>& result)
762 {
763     setSkipMode(noframesTag);
764     return true;
765 }
766
767 bool HTMLParser::noscriptCreateErrorCheck(Token* t, RefPtr<Node>& result)
768 {
769     if (!m_isParsingFragment) {
770         Settings* settings = document->settings();
771         if (settings && settings->isJavaScriptEnabled())
772             setSkipMode(noscriptTag);
773     }
774     return true;
775 }
776
777 bool HTMLParser::mapCreateErrorCheck(Token* t, RefPtr<Node>& result)
778 {
779     m_currentMapElement = new HTMLMapElement(document);
780     result = m_currentMapElement;
781     return false;
782 }
783
784 PassRefPtr<Node> HTMLParser::getNode(Token* t)
785 {
786     // Init our error handling table.
787     static FunctionMap gFunctionMap;
788     if (gFunctionMap.isEmpty()) {
789         gFunctionMap.set(aTag.localName().impl(), &HTMLParser::nestedCreateErrorCheck);
790         gFunctionMap.set(bTag.localName().impl(), &HTMLParser::nestedStyleCreateErrorCheck);
791         gFunctionMap.set(bigTag.localName().impl(), &HTMLParser::nestedStyleCreateErrorCheck);
792         gFunctionMap.set(bodyTag.localName().impl(), &HTMLParser::bodyCreateErrorCheck);
793         gFunctionMap.set(buttonTag.localName().impl(), &HTMLParser::nestedCreateErrorCheck);
794         gFunctionMap.set(commentAtom.impl(), &HTMLParser::commentCreateErrorCheck);
795         gFunctionMap.set(ddTag.localName().impl(), &HTMLParser::ddCreateErrorCheck);
796         gFunctionMap.set(dtTag.localName().impl(), &HTMLParser::dtCreateErrorCheck);
797         gFunctionMap.set(formTag.localName().impl(), &HTMLParser::formCreateErrorCheck);
798         gFunctionMap.set(framesetTag.localName().impl(), &HTMLParser::framesetCreateErrorCheck);
799         gFunctionMap.set(headTag.localName().impl(), &HTMLParser::headCreateErrorCheck);
800         gFunctionMap.set(iTag.localName().impl(), &HTMLParser::nestedStyleCreateErrorCheck);
801         gFunctionMap.set(iframeTag.localName().impl(), &HTMLParser::iframeCreateErrorCheck);
802         gFunctionMap.set(isindexTag.localName().impl(), &HTMLParser::isindexCreateErrorCheck);
803         gFunctionMap.set(liTag.localName().impl(), &HTMLParser::nestedCreateErrorCheck);
804         gFunctionMap.set(mapTag.localName().impl(), &HTMLParser::mapCreateErrorCheck);
805         gFunctionMap.set(nobrTag.localName().impl(), &HTMLParser::nestedCreateErrorCheck);
806         gFunctionMap.set(noembedTag.localName().impl(), &HTMLParser::noembedCreateErrorCheck);
807         gFunctionMap.set(noframesTag.localName().impl(), &HTMLParser::noframesCreateErrorCheck);
808         gFunctionMap.set(noscriptTag.localName().impl(), &HTMLParser::noscriptCreateErrorCheck);
809         gFunctionMap.set(sTag.localName().impl(), &HTMLParser::nestedStyleCreateErrorCheck);
810         gFunctionMap.set(selectTag.localName().impl(), &HTMLParser::selectCreateErrorCheck);
811         gFunctionMap.set(smallTag.localName().impl(), &HTMLParser::nestedStyleCreateErrorCheck);
812         gFunctionMap.set(strikeTag.localName().impl(), &HTMLParser::nestedStyleCreateErrorCheck);
813         gFunctionMap.set(tbodyTag.localName().impl(), &HTMLParser::tableSectionCreateErrorCheck);
814         gFunctionMap.set(tdTag.localName().impl(), &HTMLParser::tableCellCreateErrorCheck);
815         gFunctionMap.set(textAtom.impl(), &HTMLParser::textCreateErrorCheck);
816         gFunctionMap.set(tfootTag.localName().impl(), &HTMLParser::tableSectionCreateErrorCheck);
817         gFunctionMap.set(thTag.localName().impl(), &HTMLParser::tableCellCreateErrorCheck);
818         gFunctionMap.set(theadTag.localName().impl(), &HTMLParser::tableSectionCreateErrorCheck);
819         gFunctionMap.set(trTag.localName().impl(), &HTMLParser::nestedCreateErrorCheck);
820         gFunctionMap.set(ttTag.localName().impl(), &HTMLParser::nestedStyleCreateErrorCheck);
821         gFunctionMap.set(uTag.localName().impl(), &HTMLParser::nestedStyleCreateErrorCheck);
822     }
823
824     bool proceed = true;
825     RefPtr<Node> result;
826     if (CreateErrorCheckFunc errorCheckFunc = gFunctionMap.get(t->tagName.impl()))
827         proceed = (this->*errorCheckFunc)(t, result);
828     if (proceed)
829         result = HTMLElementFactory::createHTMLElement(t->tagName, document, m_currentFormElement.get());
830     return result.release();
831 }
832
833 bool HTMLParser::allowNestedRedundantTag(const AtomicString& tagName)
834 {
835     // www.liceo.edu.mx is an example of a site that achieves a level of nesting of
836     // about 1500 tags, all from a bunch of <b>s.  We will only allow at most 20
837     // nested tags of the same type before just ignoring them all together.
838     unsigned i = 0;
839     for (HTMLStackElem* curr = blockStack;
840          i < cMaxRedundantTagDepth && curr && curr->tagName == tagName;
841          curr = curr->next, i++) { }
842     return i != cMaxRedundantTagDepth;
843 }
844
845 void HTMLParser::processCloseTag(Token* t)
846 {
847     // Support for really broken html.
848     // we never close the body tag, since some stupid web pages close it before the actual end of the doc.
849     // let's rely on the end() call to close things.
850     if (t->tagName == htmlTag || t->tagName == bodyTag || t->tagName == commentAtom)
851         return;
852     
853     bool checkForCloseTagErrors = true;
854     if (t->tagName == formTag && m_currentFormElement) {
855         m_currentFormElement = 0;
856         checkForCloseTagErrors = false;
857     } else if (t->tagName == mapTag)
858         m_currentMapElement = 0;
859     else if (t->tagName == pTag)
860         checkForCloseTagErrors = false;
861         
862     HTMLStackElem* oldElem = blockStack;
863     popBlock(t->tagName, checkForCloseTagErrors);
864     if (oldElem == blockStack && t->tagName == pTag) {
865         // We encountered a stray </p>.  Amazingly Gecko, WinIE, and MacIE all treat
866         // this as a valid break, i.e., <p></p>.  So go ahead and make the empty
867         // paragraph.
868         t->beginTag = true;
869         parseToken(t);
870         popBlock(t->tagName);
871         reportError(StrayParagraphCloseError);
872     }
873 }
874
875 bool HTMLParser::isHeaderTag(const AtomicString& tagName)
876 {
877     static HashSet<AtomicStringImpl*> headerTags;
878     if (headerTags.isEmpty()) {
879         headerTags.add(h1Tag.localName().impl());
880         headerTags.add(h2Tag.localName().impl());
881         headerTags.add(h3Tag.localName().impl());
882         headerTags.add(h4Tag.localName().impl());
883         headerTags.add(h5Tag.localName().impl());
884         headerTags.add(h6Tag.localName().impl());
885     }
886     
887     return headerTags.contains(tagName.impl());
888 }
889
890 bool HTMLParser::isInline(Node* node) const
891 {
892     if (node->isTextNode())
893         return true;
894
895     if (node->isHTMLElement()) {
896         HTMLElement* e = static_cast<HTMLElement*>(node);
897         if (e->hasLocalName(aTag) || e->hasLocalName(fontTag) || e->hasLocalName(ttTag) ||
898             e->hasLocalName(uTag) || e->hasLocalName(bTag) || e->hasLocalName(iTag) ||
899             e->hasLocalName(sTag) || e->hasLocalName(strikeTag) || e->hasLocalName(bigTag) ||
900             e->hasLocalName(smallTag) || e->hasLocalName(emTag) || e->hasLocalName(strongTag) ||
901             e->hasLocalName(dfnTag) || e->hasLocalName(codeTag) || e->hasLocalName(sampTag) ||
902             e->hasLocalName(kbdTag) || e->hasLocalName(varTag) || e->hasLocalName(citeTag) ||
903             e->hasLocalName(abbrTag) || e->hasLocalName(acronymTag) || e->hasLocalName(subTag) ||
904             e->hasLocalName(supTag) || e->hasLocalName(spanTag) || e->hasLocalName(nobrTag) ||
905             e->hasLocalName(noframesTag) || e->hasLocalName(nolayerTag) ||
906             e->hasLocalName(noembedTag))
907             return true;
908         if (e->hasLocalName(noscriptTag) && !m_isParsingFragment) {
909             Settings* settings = document->settings();
910             if (settings && settings->isJavaScriptEnabled())
911                 return true;
912         }
913     }
914     
915     return false;
916 }
917
918 bool HTMLParser::isResidualStyleTag(const AtomicString& tagName)
919 {
920     static HashSet<AtomicStringImpl*> residualStyleTags;
921     if (residualStyleTags.isEmpty()) {
922         residualStyleTags.add(aTag.localName().impl());
923         residualStyleTags.add(fontTag.localName().impl());
924         residualStyleTags.add(ttTag.localName().impl());
925         residualStyleTags.add(uTag.localName().impl());
926         residualStyleTags.add(bTag.localName().impl());
927         residualStyleTags.add(iTag.localName().impl());
928         residualStyleTags.add(sTag.localName().impl());
929         residualStyleTags.add(strikeTag.localName().impl());
930         residualStyleTags.add(bigTag.localName().impl());
931         residualStyleTags.add(smallTag.localName().impl());
932         residualStyleTags.add(emTag.localName().impl());
933         residualStyleTags.add(strongTag.localName().impl());
934         residualStyleTags.add(dfnTag.localName().impl());
935         residualStyleTags.add(codeTag.localName().impl());
936         residualStyleTags.add(sampTag.localName().impl());
937         residualStyleTags.add(kbdTag.localName().impl());
938         residualStyleTags.add(varTag.localName().impl());
939         residualStyleTags.add(nobrTag.localName().impl());
940     }
941     
942     return residualStyleTags.contains(tagName.impl());
943 }
944
945 bool HTMLParser::isAffectedByResidualStyle(const AtomicString& tagName)
946 {
947     static HashSet<AtomicStringImpl*> unaffectedTags;
948     if (unaffectedTags.isEmpty()) {
949         unaffectedTags.add(bodyTag.localName().impl());
950         unaffectedTags.add(tableTag.localName().impl());
951         unaffectedTags.add(theadTag.localName().impl());
952         unaffectedTags.add(tbodyTag.localName().impl());
953         unaffectedTags.add(tfootTag.localName().impl());
954         unaffectedTags.add(trTag.localName().impl());
955         unaffectedTags.add(thTag.localName().impl());
956         unaffectedTags.add(tdTag.localName().impl());
957         unaffectedTags.add(captionTag.localName().impl());
958         unaffectedTags.add(colgroupTag.localName().impl());
959         unaffectedTags.add(colTag.localName().impl());
960         unaffectedTags.add(optionTag.localName().impl());
961         unaffectedTags.add(optgroupTag.localName().impl());
962         unaffectedTags.add(selectTag.localName().impl());
963         unaffectedTags.add(objectTag.localName().impl());
964     }
965     
966     return !unaffectedTags.contains(tagName.impl());
967 }
968
969 void HTMLParser::handleResidualStyleCloseTagAcrossBlocks(HTMLStackElem* elem)
970 {
971     HTMLStackElem* maxElem = 0;
972     bool finished = false;
973     bool strayTableContent = elem->strayTableContent;
974
975     m_handlingResidualStyleAcrossBlocks = true;
976     while (!finished) {
977         // Find the outermost element that crosses over to a higher level. If there exists another higher-level
978         // element, we will do another pass, until we have corrected the innermost one.
979         ExceptionCode ec = 0;
980         HTMLStackElem* curr = blockStack;
981         HTMLStackElem* prev = 0;
982         HTMLStackElem* prevMaxElem = 0;
983         maxElem = 0;
984         finished = true;
985         while (curr && curr != elem) {
986             if (curr->level > elem->level) {
987                 if (!isAffectedByResidualStyle(curr->tagName))
988                     return;
989                 if (maxElem)
990                     // We will need another pass.
991                     finished = false;
992                 maxElem = curr;
993                 prevMaxElem = prev;
994             }
995
996             prev = curr;
997             curr = curr->next;
998         }
999
1000         if (!curr || !maxElem)
1001             return;
1002
1003         Node* residualElem = prev->node;
1004         Node* blockElem = prevMaxElem ? prevMaxElem->node : current;
1005         Node* parentElem = elem->node;
1006
1007         // Check to see if the reparenting that is going to occur is allowed according to the DOM.
1008         // FIXME: We should either always allow it or perform an additional fixup instead of
1009         // just bailing here.
1010         // Example: <p><font><center>blah</font></center></p> isn't doing a fixup right now.
1011         if (!parentElem->childAllowed(blockElem))
1012             return;
1013
1014         if (maxElem->node->parentNode() != elem->node) {
1015             // Walk the stack and remove any elements that aren't residual style tags.  These
1016             // are basically just being closed up.  Example:
1017             // <font><span>Moo<p>Goo</font></p>.
1018             // In the above example, the <span> doesn't need to be reopened.  It can just close.
1019             HTMLStackElem* currElem = maxElem->next;
1020             HTMLStackElem* prevElem = maxElem;
1021             while (currElem != elem) {
1022                 HTMLStackElem* nextElem = currElem->next;
1023                 if (!isResidualStyleTag(currElem->tagName)) {
1024                     prevElem->next = nextElem;
1025                     prevElem->derefNode();
1026                     prevElem->node = currElem->node;
1027                     prevElem->didRefNode = currElem->didRefNode;
1028                     delete currElem;
1029                 }
1030                 else
1031                     prevElem = currElem;
1032                 currElem = nextElem;
1033             }
1034
1035             // We have to reopen residual tags in between maxElem and elem.  An example of this case is:
1036             // <font><i>Moo<p>Foo</font>.
1037             // In this case, we need to transform the part before the <p> into:
1038             // <font><i>Moo</i></font><i>
1039             // so that the <i> will remain open.  This involves the modification of elements
1040             // in the block stack.
1041             // This will also affect how we ultimately reparent the block, since we want it to end up
1042             // under the reopened residual tags (e.g., the <i> in the above example.)
1043             RefPtr<Node> prevNode = 0;
1044             currElem = maxElem;
1045             while (currElem->node != residualElem) {
1046                 if (isResidualStyleTag(currElem->node->localName())) {
1047                     // Create a clone of this element.
1048                     // We call releaseRef to get a raw pointer since we plan to hand over ownership to currElem.
1049                     Node* currNode = currElem->node->cloneNode(false).releaseRef();
1050                     reportError(ResidualStyleError, &currNode->localName());
1051     
1052                     // Change the stack element's node to point to the clone.
1053                     // The stack element adopts the reference we obtained above by calling release().
1054                     currElem->derefNode();
1055                     currElem->node = currNode;
1056                     currElem->didRefNode = true;
1057
1058                     // Attach the previous node as a child of this new node.
1059                     if (prevNode)
1060                         currNode->appendChild(prevNode, ec);
1061                     else // The new parent for the block element is going to be the innermost clone.
1062                         parentElem = currNode;  // FIXME: We shifted parentElem to be a residual inline.  We never checked to see if blockElem could be legally placed inside the inline though.
1063
1064                     prevNode = currNode;
1065                 }
1066
1067                 currElem = currElem->next;
1068             }
1069
1070             // Now append the chain of new residual style elements if one exists.
1071             if (prevNode)
1072                 elem->node->appendChild(prevNode, ec);  // FIXME: This append can result in weird stuff happening, like an inline chain being put into a table section.
1073         }
1074
1075         // Check if the block is still in the tree. If it isn't, then we don't
1076         // want to remove it from its parent (that would crash) or insert it into
1077         // a new parent later. See http://bugs.webkit.org/show_bug.cgi?id=6778
1078         bool isBlockStillInTree = blockElem->parentNode();
1079
1080         // We need to make a clone of |residualElem| and place it just inside |blockElem|.
1081         // All content of |blockElem| is reparented to be under this clone.  We then
1082         // reparent |blockElem| using real DOM calls so that attachment/detachment will
1083         // be performed to fix up the rendering tree.
1084         // So for this example: <b>...<p>Foo</b>Goo</p>
1085         // The end result will be: <b>...</b><p><b>Foo</b>Goo</p>
1086         //
1087         // Step 1: Remove |blockElem| from its parent, doing a batch detach of all the kids.
1088         if (isBlockStillInTree)
1089             blockElem->parentNode()->removeChild(blockElem, ec);
1090
1091         Node* newNodePtr = 0;
1092         ASSERT(finished || blockElem->firstChild());
1093         if (blockElem->firstChild()) {
1094             // Step 2: Clone |residualElem|.
1095             RefPtr<Node> newNode = residualElem->cloneNode(false); // Shallow clone. We don't pick up the same kids.
1096             newNodePtr = newNode.get();
1097             reportError(ResidualStyleError, &newNode->localName());
1098
1099             // Step 3: Place |blockElem|'s children under |newNode|.  Remove all of the children of |blockElem|
1100             // before we've put |newElem| into the document.  That way we'll only do one attachment of all
1101             // the new content (instead of a bunch of individual attachments).
1102             Node* currNode = blockElem->firstChild();
1103             while (currNode) {
1104                 Node* nextNode = currNode->nextSibling();
1105                 newNode->appendChild(currNode, ec);
1106                 currNode = nextNode;
1107             }
1108
1109             // Step 4: Place |newNode| under |blockElem|.  |blockElem| is still out of the document, so no
1110             // attachment can occur yet.
1111             blockElem->appendChild(newNode.release(), ec);
1112         }
1113
1114         // Step 5: Reparent |blockElem|.  Now the full attachment of the fixed up tree takes place.
1115         if (isBlockStillInTree)
1116             parentElem->appendChild(blockElem, ec);
1117
1118         // Step 6: Pull |elem| out of the stack, since it is no longer enclosing us.  Also update
1119         // the node associated with the previous stack element so that when it gets popped,
1120         // it doesn't make the residual element the next current node.
1121         HTMLStackElem* currElem = maxElem;
1122         HTMLStackElem* prevElem = 0;
1123         while (currElem != elem) {
1124             prevElem = currElem;
1125             currElem = currElem->next;
1126         }
1127         prevElem->next = elem->next;
1128         prevElem->derefNode();
1129         prevElem->node = elem->node;
1130         prevElem->didRefNode = elem->didRefNode;
1131         if (!finished) {
1132             // Repurpose |elem| to represent |newNode| and insert it at the appropriate position
1133             // in the stack. We do not do this for the innermost block, because in that case the new
1134             // node is effectively no longer open.
1135             elem->next = maxElem;
1136             elem->node = prevMaxElem->node;
1137             elem->didRefNode = prevMaxElem->didRefNode;
1138             elem->strayTableContent = false;
1139             prevMaxElem->next = elem;
1140             ASSERT(newNodePtr);
1141             prevMaxElem->node = newNodePtr;
1142             prevMaxElem->didRefNode = false;
1143         } else
1144             delete elem;
1145     }
1146
1147     // FIXME: If we ever make a case like this work:
1148     // <table><b><i><form></b></form></i></table>
1149     // Then this check will be too simplistic.  Right now the <i><form> chain will end up inside the <tbody>, which is pretty crazy.
1150     if (strayTableContent)
1151         inStrayTableContent--;
1152
1153     // Step 7: Reopen intermediate inlines, e.g., <b><p><i>Foo</b>Goo</p>.
1154     // In the above example, Goo should stay italic.
1155     // We cap the number of tags we're willing to reopen based off cResidualStyleMaxDepth.
1156     
1157     HTMLStackElem* curr = blockStack;
1158     HTMLStackElem* residualStyleStack = 0;
1159     unsigned stackDepth = 1;
1160     while (curr && curr != maxElem) {
1161         // We will actually schedule this tag for reopening
1162         // after we complete the close of this entire block.
1163         if (isResidualStyleTag(curr->tagName) && stackDepth++ < cResidualStyleMaxDepth)
1164             // We've overloaded the use of stack elements and are just reusing the
1165             // struct with a slightly different meaning to the variables.  Instead of chaining
1166             // from innermost to outermost, we build up a list of all the tags we need to reopen
1167             // from the outermost to the innermost, i.e., residualStyleStack will end up pointing
1168             // to the outermost tag we need to reopen.
1169             // We also set curr->node to be the actual element that corresponds to the ID stored in
1170             // curr->id rather than the node that you should pop to when the element gets pulled off
1171             // the stack.
1172             moveOneBlockToStack(residualStyleStack);
1173         else
1174             popOneBlock();
1175
1176         curr = blockStack;
1177     }
1178
1179     reopenResidualStyleTags(residualStyleStack, 0); // Stray table content can't be an issue here, since some element above will always become the root of new stray table content.
1180
1181     m_handlingResidualStyleAcrossBlocks = false;
1182 }
1183
1184 void HTMLParser::reopenResidualStyleTags(HTMLStackElem* elem, Node* malformedTableParent)
1185 {
1186     // Loop for each tag that needs to be reopened.
1187     while (elem) {
1188         // Create a shallow clone of the DOM node for this element.
1189         RefPtr<Node> newNode = elem->node->cloneNode(false); 
1190         reportError(ResidualStyleError, &newNode->localName());
1191
1192         // Append the new node. In the malformed table case, we need to insert before the table,
1193         // which will be the last child.
1194         ExceptionCode ec = 0;
1195         if (malformedTableParent)
1196             malformedTableParent->insertBefore(newNode, malformedTableParent->lastChild(), ec);
1197         else
1198             current->appendChild(newNode, ec);
1199         // FIXME: Is it really OK to ignore the exceptions here?
1200
1201         // Now push a new stack element for this node we just created.
1202         pushBlock(elem->tagName, elem->level);
1203
1204         // Set our strayTableContent boolean if needed, so that the reopened tag also knows
1205         // that it is inside a malformed table.
1206         blockStack->strayTableContent = malformedTableParent != 0;
1207         if (blockStack->strayTableContent)
1208             inStrayTableContent++;
1209
1210         // Clear our malformed table parent variable.
1211         malformedTableParent = 0;
1212
1213         // Update |current| manually to point to the new node.
1214         setCurrent(newNode.get());
1215         
1216         // Advance to the next tag that needs to be reopened.
1217         HTMLStackElem* next = elem->next;
1218         elem->derefNode();
1219         delete elem;
1220         elem = next;
1221     }
1222 }
1223
1224 void HTMLParser::pushBlock(const AtomicString& tagName, int level)
1225 {
1226     blockStack = new HTMLStackElem(tagName, level, current, didRefCurrent, blockStack);
1227     didRefCurrent = false;
1228 }
1229
1230 void HTMLParser::popBlock(const AtomicString& tagName, bool reportErrors)
1231 {
1232     HTMLStackElem* elem = blockStack;
1233     
1234     int maxLevel = 0;
1235
1236     while (elem && (elem->tagName != tagName)) {
1237         if (maxLevel < elem->level)
1238             maxLevel = elem->level;
1239         elem = elem->next;
1240     }
1241
1242     if (!elem) {
1243         if (reportErrors)
1244             reportError(StrayCloseTagError, &tagName, 0, true);
1245         return;
1246     }
1247
1248     if (maxLevel > elem->level) {
1249         // We didn't match because the tag is in a different scope, e.g.,
1250         // <b><p>Foo</b>.  Try to correct the problem.
1251         if (!isResidualStyleTag(tagName))
1252             return;
1253         return handleResidualStyleCloseTagAcrossBlocks(elem);
1254     }
1255
1256     bool isAffectedByStyle = isAffectedByResidualStyle(elem->tagName);
1257     HTMLStackElem* residualStyleStack = 0;
1258     Node* malformedTableParent = 0;
1259     
1260     elem = blockStack;
1261     unsigned stackDepth = 1;
1262     while (elem) {
1263         if (elem->tagName == tagName) {
1264             int strayTable = inStrayTableContent;
1265             popOneBlock();
1266             elem = 0;
1267
1268             // This element was the root of some malformed content just inside an implicit or
1269             // explicit <tbody> or <tr>.
1270             // If we end up needing to reopen residual style tags, the root of the reopened chain
1271             // must also know that it is the root of malformed content inside a <tbody>/<tr>.
1272             if (strayTable && (inStrayTableContent < strayTable) && residualStyleStack) {
1273                 Node* curr = current;
1274                 while (curr && !curr->hasTagName(tableTag))
1275                     curr = curr->parentNode();
1276                 malformedTableParent = curr ? curr->parentNode() : 0;
1277             }
1278         }
1279         else {
1280             if (m_currentFormElement && elem->tagName == formTag)
1281                 // A <form> is being closed prematurely (and this is
1282                 // malformed HTML).  Set an attribute on the form to clear out its
1283                 // bottom margin.
1284                 m_currentFormElement->setMalformed(true);
1285
1286             // Schedule this tag for reopening
1287             // after we complete the close of this entire block.
1288             if (isAffectedByStyle && isResidualStyleTag(elem->tagName) && stackDepth++ < cResidualStyleMaxDepth)
1289                 // We've overloaded the use of stack elements and are just reusing the
1290                 // struct with a slightly different meaning to the variables.  Instead of chaining
1291                 // from innermost to outermost, we build up a list of all the tags we need to reopen
1292                 // from the outermost to the innermost, i.e., residualStyleStack will end up pointing
1293                 // to the outermost tag we need to reopen.
1294                 // We also set elem->node to be the actual element that corresponds to the ID stored in
1295                 // elem->id rather than the node that you should pop to when the element gets pulled off
1296                 // the stack.
1297                 moveOneBlockToStack(residualStyleStack);
1298             else
1299                 popOneBlock();
1300             elem = blockStack;
1301         }
1302     }
1303
1304     reopenResidualStyleTags(residualStyleStack, malformedTableParent);
1305 }
1306
1307 inline HTMLStackElem* HTMLParser::popOneBlockCommon()
1308 {
1309     HTMLStackElem* elem = blockStack;
1310
1311     // Form elements restore their state during the parsing process.
1312     // Also, a few elements (<applet>, <object>) need to know when all child elements (<param>s) are available.
1313     if (current && elem->node != current)
1314         current->finishedParsing();
1315
1316     blockStack = elem->next;
1317     current = elem->node;
1318     didRefCurrent = elem->didRefNode;
1319
1320     if (elem->strayTableContent)
1321         inStrayTableContent--;
1322
1323     return elem;
1324 }
1325
1326 void HTMLParser::popOneBlock()
1327 {
1328     // Store the current node before popOneBlockCommon overwrites it.
1329     Node* lastCurrent = current;
1330     bool didRefLastCurrent = didRefCurrent;
1331
1332     delete popOneBlockCommon();
1333
1334     if (didRefLastCurrent)
1335         lastCurrent->deref();
1336 }
1337
1338 void HTMLParser::moveOneBlockToStack(HTMLStackElem*& head)
1339 {
1340     // We'll be using the stack element we're popping, but for the current node.
1341     // See the two callers for details.
1342
1343     // Store the current node before popOneBlockCommon overwrites it.
1344     Node* lastCurrent = current;
1345     bool didRefLastCurrent = didRefCurrent;
1346
1347     // Pop the block, but don't deref the current node as popOneBlock does because
1348     // we'll be using the pointer in the new stack element.
1349     HTMLStackElem* elem = popOneBlockCommon();
1350
1351     // Transfer the current node into the stack element.
1352     // No need to deref the old elem->node because popOneBlockCommon transferred
1353     // it into the current/didRefCurrent fields.
1354     elem->node = lastCurrent;
1355     elem->didRefNode = didRefLastCurrent;
1356     elem->next = head;
1357     head = elem;
1358 }
1359
1360 void HTMLParser::popInlineBlocks()
1361 {
1362     while (blockStack && isInline(current))
1363         popOneBlock();
1364 }
1365
1366 void HTMLParser::freeBlock()
1367 {
1368     while (blockStack)
1369         popOneBlock();
1370 }
1371
1372 void HTMLParser::createHead()
1373 {
1374     if (head || !document->documentElement())
1375         return;
1376
1377     head = new HTMLHeadElement(document);
1378     HTMLElement* body = document->body();
1379     ExceptionCode ec = 0;
1380     document->documentElement()->insertBefore(head, body, ec);
1381     if (ec)
1382         head = 0;
1383         
1384     // If the body does not exist yet, then the <head> should be pushed as the current block.
1385     if (head && !body) {
1386         pushBlock(head->localName(), head->tagPriority());
1387         setCurrent(head);
1388     }
1389 }
1390
1391 PassRefPtr<Node> HTMLParser::handleIsindex(Token* t)
1392 {
1393     RefPtr<Node> n = new HTMLDivElement(document);
1394
1395     NamedMappedAttrMap* attrs = t->attrs.get();
1396
1397     RefPtr<HTMLIsIndexElement> isIndex = new HTMLIsIndexElement(document, m_currentFormElement.get());
1398     isIndex->setAttributeMap(attrs);
1399     isIndex->setAttribute(typeAttr, "khtml_isindex");
1400
1401     String text = searchableIndexIntroduction();
1402     if (attrs) {
1403         if (Attribute* a = attrs->getAttributeItem(promptAttr))
1404             text = a->value().domString() + " ";
1405         t->attrs = 0;
1406     }
1407
1408     n->addChild(new HTMLHRElement(document));
1409     n->addChild(new Text(document, text));
1410     n->addChild(isIndex.release());
1411     n->addChild(new HTMLHRElement(document));
1412
1413     return n.release();
1414 }
1415
1416 void HTMLParser::startBody()
1417 {
1418     if (inBody)
1419         return;
1420
1421     inBody = true;
1422
1423     if (m_isindexElement) {
1424         insertNode(m_isindexElement.get(), true /* don't descend into this node */);
1425         m_isindexElement = 0;
1426     }
1427 }
1428
1429 void HTMLParser::finished()
1430 {
1431     // In the case of a completely empty document, here's the place to create the HTML element.
1432     if (current && current->isDocumentNode() && !document->documentElement())
1433         insertNode(new HTMLHtmlElement(document));
1434
1435     // This ensures that "current" is not left pointing to a node when the document is destroyed.
1436     freeBlock();
1437     setCurrent(0);
1438
1439     // Warning, this may delete the tokenizer and parser, so don't try to do anything else after this.
1440     if (!m_isParsingFragment)
1441         document->finishedParsing();
1442 }
1443
1444 void HTMLParser::reportErrorToConsole(HTMLParserErrorCode errorCode, const AtomicString* tagName1, const AtomicString* tagName2, bool closeTags)
1445 {    
1446     Frame* frame = document->frame();
1447     if (!frame)
1448         return;
1449     
1450     Page* page = frame->page();
1451     if (!page)
1452         return;
1453
1454     HTMLTokenizer* htmlTokenizer = static_cast<HTMLTokenizer*>(document->tokenizer());
1455     int lineNumber = htmlTokenizer->lineNumber() + 1;
1456
1457     AtomicString tag1;
1458     AtomicString tag2;
1459     if (tagName1) {
1460         if (*tagName1 == "#text")
1461             tag1 = "Text";
1462         else if (*tagName1 == "#comment")
1463             tag1 = "<!-- comment -->";
1464         else
1465             tag1 = (closeTags ? "</" : "<") + *tagName1 + ">";
1466     }
1467     if (tagName2) {
1468         if (*tagName2 == "#text")
1469             tag2 = "Text";
1470         else if (*tagName2 == "#comment")
1471             tag2 = "<!-- comment -->";
1472         else
1473             tag2 = (closeTags ? "</" : "<") + *tagName2 + ">";
1474     }
1475         
1476     const char* errorMsg = htmlParserErrorMessageTemplate(errorCode);
1477     if (!errorMsg)
1478         return;
1479         
1480     String message;
1481     if (htmlTokenizer->processingContentWrittenByScript())
1482         message += htmlParserDocumentWriteMessage();
1483     message += errorMsg;
1484     message.replace("%tag1", tag1);
1485     message.replace("%tag2", tag2);
1486
1487     page->chrome()->addMessageToConsole(HTMLMessageSource, isWarning(errorCode) ? WarningMessageLevel: ErrorMessageLevel, message, lineNumber, document->url());
1488 }
1489
1490 }