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