2 * Copyright (C) 2010 Google, Inc. All Rights Reserved.
3 * Copyright (C) 2011 Apple Inc. All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
14 * THIS SOFTWARE IS PROVIDED BY GOOGLE INC. ``AS IS'' AND ANY
15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GOOGLE INC. OR
18 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 #include "HTMLElementStack.h"
30 #include "DocumentFragment.h"
32 #include "HTMLNames.h"
33 #include "MathMLNames.h"
35 #include <wtf/PassOwnPtr.h>
39 using namespace HTMLNames;
43 inline bool isNumberedHeaderElement(ContainerNode* node)
45 return node->hasTagName(h1Tag)
46 || node->hasTagName(h2Tag)
47 || node->hasTagName(h3Tag)
48 || node->hasTagName(h4Tag)
49 || node->hasTagName(h5Tag)
50 || node->hasTagName(h6Tag);
53 inline bool isRootNode(ContainerNode* node)
55 return node->nodeType() == Node::DOCUMENT_FRAGMENT_NODE
56 || node->hasTagName(htmlTag);
59 inline bool isScopeMarker(ContainerNode* node)
61 return node->hasTagName(appletTag)
62 || node->hasTagName(captionTag)
63 || node->hasTagName(marqueeTag)
64 || node->hasTagName(objectTag)
65 || node->hasTagName(tableTag)
66 || node->hasTagName(tdTag)
67 || node->hasTagName(thTag)
68 || node->hasTagName(MathMLNames::miTag)
69 || node->hasTagName(MathMLNames::moTag)
70 || node->hasTagName(MathMLNames::mnTag)
71 || node->hasTagName(MathMLNames::msTag)
72 || node->hasTagName(MathMLNames::mtextTag)
73 || node->hasTagName(MathMLNames::annotation_xmlTag)
74 || node->hasTagName(SVGNames::foreignObjectTag)
75 || node->hasTagName(SVGNames::descTag)
76 || node->hasTagName(SVGNames::titleTag)
80 inline bool isListItemScopeMarker(ContainerNode* node)
82 return isScopeMarker(node)
83 || node->hasTagName(olTag)
84 || node->hasTagName(ulTag);
87 inline bool isTableScopeMarker(ContainerNode* node)
89 return node->hasTagName(tableTag)
93 inline bool isTableBodyScopeMarker(ContainerNode* node)
95 return node->hasTagName(tbodyTag)
96 || node->hasTagName(tfootTag)
97 || node->hasTagName(theadTag)
101 inline bool isTableRowScopeMarker(ContainerNode* node)
103 return node->hasTagName(trTag)
107 inline bool isForeignContentScopeMarker(ContainerNode* node)
109 return HTMLElementStack::isMathMLTextIntegrationPoint(node)
110 || HTMLElementStack::isHTMLIntegrationPoint(node)
111 || isInHTMLNamespace(node);
114 inline bool isButtonScopeMarker(ContainerNode* node)
116 return isScopeMarker(node)
117 || node->hasTagName(buttonTag);
120 inline bool isSelectScopeMarker(ContainerNode* node)
122 return !node->hasTagName(optgroupTag)
123 && !node->hasTagName(optionTag);
128 HTMLElementStack::ElementRecord::ElementRecord(PassRefPtr<HTMLStackItem> item, PassOwnPtr<ElementRecord> next)
135 HTMLElementStack::ElementRecord::~ElementRecord()
139 void HTMLElementStack::ElementRecord::replaceElement(PassRefPtr<HTMLStackItem> item)
142 ASSERT(!m_item || m_item->node()->isElementNode());
143 // FIXME: Should this call finishParsingChildren?
147 bool HTMLElementStack::ElementRecord::isAbove(ElementRecord* other) const
149 for (ElementRecord* below = next(); below; below = below->next()) {
156 HTMLElementStack::HTMLElementStack()
164 HTMLElementStack::~HTMLElementStack()
168 bool HTMLElementStack::hasOnlyOneElement() const
170 return !topRecord()->next();
173 bool HTMLElementStack::secondElementIsHTMLBodyElement() const
175 // This is used the fragment case of <body> and <frameset> in the "in body"
177 // http://www.whatwg.org/specs/web-apps/current-work/multipage/tokenization.html#parsing-main-inbody
179 // If we have a body element, it must always be the second element on the
180 // stack, as we always start with an html element, and any other element
181 // would cause the implicit creation of a body element.
182 return !!m_bodyElement;
185 void HTMLElementStack::popHTMLHeadElement()
187 ASSERT(top() == m_headElement);
192 void HTMLElementStack::popHTMLBodyElement()
194 ASSERT(top() == m_bodyElement);
199 void HTMLElementStack::popAll()
206 topNode()->finishParsingChildren();
207 m_top = m_top->releaseNext();
211 void HTMLElementStack::pop()
213 ASSERT(!top()->hasTagName(HTMLNames::headTag));
217 void HTMLElementStack::popUntil(const AtomicString& tagName)
219 while (!top()->hasLocalName(tagName)) {
220 // pop() will ASSERT at <body> if callers fail to check that there is an
221 // element with localName |tagName| on the stack of open elements.
226 void HTMLElementStack::popUntilPopped(const AtomicString& tagName)
232 void HTMLElementStack::popUntilNumberedHeaderElementPopped()
234 while (!isNumberedHeaderElement(topNode()))
239 void HTMLElementStack::popUntil(Element* element)
241 while (top() != element)
245 void HTMLElementStack::popUntilPopped(Element* element)
251 void HTMLElementStack::popUntilTableScopeMarker()
253 // http://www.whatwg.org/specs/web-apps/current-work/multipage/tokenization.html#clear-the-stack-back-to-a-table-context
254 while (!isTableScopeMarker(topNode()))
258 void HTMLElementStack::popUntilTableBodyScopeMarker()
260 // http://www.whatwg.org/specs/web-apps/current-work/multipage/tokenization.html#clear-the-stack-back-to-a-table-body-context
261 while (!isTableBodyScopeMarker(topNode()))
265 void HTMLElementStack::popUntilTableRowScopeMarker()
267 // http://www.whatwg.org/specs/web-apps/current-work/multipage/tokenization.html#clear-the-stack-back-to-a-table-row-context
268 while (!isTableRowScopeMarker(topNode()))
272 // http://www.whatwg.org/specs/web-apps/current-work/multipage/tree-construction.html#mathml-text-integration-point
273 bool HTMLElementStack::isMathMLTextIntegrationPoint(ContainerNode* node)
275 if (!node->isElementNode())
277 Element* element = static_cast<Element*>(node);
278 return element->hasTagName(MathMLNames::miTag)
279 || element->hasTagName(MathMLNames::moTag)
280 || element->hasTagName(MathMLNames::mnTag)
281 || element->hasTagName(MathMLNames::msTag)
282 || element->hasTagName(MathMLNames::mtextTag);
285 // http://www.whatwg.org/specs/web-apps/current-work/multipage/tree-construction.html#html-integration-point
286 bool HTMLElementStack::isHTMLIntegrationPoint(ContainerNode* node)
288 if (!node->isElementNode())
290 Element* element = static_cast<Element*>(node);
291 if (element->hasTagName(MathMLNames::annotation_xmlTag)) {
292 // FIXME: Technically we shouldn't read back from the DOM here.
293 // Instead, we're supposed to track this information in the element
294 // stack, which lets the parser run on its own thread.
295 String encoding = element->fastGetAttribute(MathMLNames::encodingAttr);
296 return equalIgnoringCase(encoding, "text/html")
297 || equalIgnoringCase(encoding, "application/xhtml+xml");
299 return element->hasTagName(SVGNames::foreignObjectTag)
300 || element->hasTagName(SVGNames::descTag)
301 || element->hasTagName(SVGNames::titleTag);
304 void HTMLElementStack::popUntilForeignContentScopeMarker()
306 while (!isForeignContentScopeMarker(topNode()))
310 void HTMLElementStack::pushRootNode(PassRefPtr<HTMLStackItem> rootItem)
312 ASSERT(rootItem->node()->nodeType() == Node::DOCUMENT_FRAGMENT_NODE);
313 pushRootNodeCommon(rootItem);
316 void HTMLElementStack::pushHTMLHtmlElement(PassRefPtr<HTMLStackItem> item)
318 ASSERT(item->element()->hasTagName(HTMLNames::htmlTag));
319 pushRootNodeCommon(item);
322 void HTMLElementStack::pushRootNodeCommon(PassRefPtr<HTMLStackItem> rootItem)
326 m_rootNode = rootItem->node();
327 pushCommon(rootItem);
330 void HTMLElementStack::pushHTMLHeadElement(PassRefPtr<HTMLStackItem> item)
332 ASSERT(item->element()->hasTagName(HTMLNames::headTag));
333 ASSERT(!m_headElement);
334 m_headElement = item->element();
338 void HTMLElementStack::pushHTMLBodyElement(PassRefPtr<HTMLStackItem> item)
340 ASSERT(item->element()->hasTagName(HTMLNames::bodyTag));
341 ASSERT(!m_bodyElement);
342 m_bodyElement = item->element();
346 void HTMLElementStack::push(PassRefPtr<HTMLStackItem> item)
348 ASSERT(!item->element()->hasTagName(HTMLNames::htmlTag));
349 ASSERT(!item->element()->hasTagName(HTMLNames::headTag));
350 ASSERT(!item->element()->hasTagName(HTMLNames::bodyTag));
355 void HTMLElementStack::insertAbove(PassRefPtr<HTMLStackItem> item, ElementRecord* recordBelow)
360 ASSERT(!item->element()->hasTagName(HTMLNames::htmlTag));
361 ASSERT(!item->element()->hasTagName(HTMLNames::headTag));
362 ASSERT(!item->element()->hasTagName(HTMLNames::bodyTag));
364 if (recordBelow == m_top) {
369 for (ElementRecord* recordAbove = m_top.get(); recordAbove; recordAbove = recordAbove->next()) {
370 if (recordAbove->next() != recordBelow)
374 recordAbove->setNext(adoptPtr(new ElementRecord(item, recordAbove->releaseNext())));
375 recordAbove->next()->element()->beginParsingChildren();
378 ASSERT_NOT_REACHED();
381 HTMLElementStack::ElementRecord* HTMLElementStack::topRecord() const
387 Element* HTMLElementStack::oneBelowTop() const
389 // We should never call this if there are fewer than 2 elements on the stack.
391 ASSERT(m_top->next());
392 if (m_top->next()->node()->isElementNode())
393 return m_top->next()->element();
397 Element* HTMLElementStack::bottom() const
399 return htmlElement();
402 void HTMLElementStack::removeHTMLHeadElement(Element* element)
404 ASSERT(m_headElement == element);
405 if (m_top->element() == element) {
406 popHTMLHeadElement();
410 removeNonTopCommon(element);
413 void HTMLElementStack::remove(Element* element)
415 ASSERT(!element->hasTagName(HTMLNames::headTag));
416 if (m_top->element() == element) {
420 removeNonTopCommon(element);
423 HTMLElementStack::ElementRecord* HTMLElementStack::find(Element* element) const
425 for (ElementRecord* pos = m_top.get(); pos; pos = pos->next()) {
426 if (pos->node() == element)
432 HTMLElementStack::ElementRecord* HTMLElementStack::topmost(const AtomicString& tagName) const
434 for (ElementRecord* pos = m_top.get(); pos; pos = pos->next()) {
435 if (pos->node()->hasLocalName(tagName))
441 bool HTMLElementStack::contains(Element* element) const
443 return !!find(element);
446 bool HTMLElementStack::contains(const AtomicString& tagName) const
448 return !!topmost(tagName);
451 template <bool isMarker(ContainerNode*)>
452 bool inScopeCommon(HTMLElementStack::ElementRecord* top, const AtomicString& targetTag)
454 for (HTMLElementStack::ElementRecord* pos = top; pos; pos = pos->next()) {
455 ContainerNode* node = pos->node();
456 if (node->hasLocalName(targetTag))
461 ASSERT_NOT_REACHED(); // <html> is always on the stack and is a scope marker.
465 bool HTMLElementStack::hasNumberedHeaderElementInScope() const
467 for (ElementRecord* record = m_top.get(); record; record = record->next()) {
468 ContainerNode* node = record->node();
469 if (isNumberedHeaderElement(node))
471 if (isScopeMarker(node))
474 ASSERT_NOT_REACHED(); // <html> is always on the stack and is a scope marker.
478 bool HTMLElementStack::inScope(Element* targetElement) const
480 for (ElementRecord* pos = m_top.get(); pos; pos = pos->next()) {
481 ContainerNode* node = pos->node();
482 if (node == targetElement)
484 if (isScopeMarker(node))
487 ASSERT_NOT_REACHED(); // <html> is always on the stack and is a scope marker.
491 bool HTMLElementStack::inScope(const AtomicString& targetTag) const
493 return inScopeCommon<isScopeMarker>(m_top.get(), targetTag);
496 bool HTMLElementStack::inScope(const QualifiedName& tagName) const
498 // FIXME: Is localName() right for non-html elements?
499 return inScope(tagName.localName());
502 bool HTMLElementStack::inListItemScope(const AtomicString& targetTag) const
504 return inScopeCommon<isListItemScopeMarker>(m_top.get(), targetTag);
507 bool HTMLElementStack::inListItemScope(const QualifiedName& tagName) const
509 // FIXME: Is localName() right for non-html elements?
510 return inListItemScope(tagName.localName());
513 bool HTMLElementStack::inTableScope(const AtomicString& targetTag) const
515 return inScopeCommon<isTableScopeMarker>(m_top.get(), targetTag);
518 bool HTMLElementStack::inTableScope(const QualifiedName& tagName) const
520 // FIXME: Is localName() right for non-html elements?
521 return inTableScope(tagName.localName());
524 bool HTMLElementStack::inButtonScope(const AtomicString& targetTag) const
526 return inScopeCommon<isButtonScopeMarker>(m_top.get(), targetTag);
529 bool HTMLElementStack::inButtonScope(const QualifiedName& tagName) const
531 // FIXME: Is localName() right for non-html elements?
532 return inButtonScope(tagName.localName());
535 bool HTMLElementStack::inSelectScope(const AtomicString& targetTag) const
537 return inScopeCommon<isSelectScopeMarker>(m_top.get(), targetTag);
540 bool HTMLElementStack::inSelectScope(const QualifiedName& tagName) const
542 // FIXME: Is localName() right for non-html elements?
543 return inSelectScope(tagName.localName());
546 Element* HTMLElementStack::htmlElement() const
549 return toElement(m_rootNode);
552 Element* HTMLElementStack::headElement() const
554 ASSERT(m_headElement);
555 return m_headElement;
558 Element* HTMLElementStack::bodyElement() const
560 ASSERT(m_bodyElement);
561 return m_bodyElement;
564 ContainerNode* HTMLElementStack::rootNode() const
570 void HTMLElementStack::pushCommon(PassRefPtr<HTMLStackItem> item)
575 m_top = adoptPtr(new ElementRecord(item, m_top.release()));
578 void HTMLElementStack::popCommon()
580 ASSERT(!top()->hasTagName(HTMLNames::htmlTag));
581 ASSERT(!top()->hasTagName(HTMLNames::headTag) || !m_headElement);
582 ASSERT(!top()->hasTagName(HTMLNames::bodyTag) || !m_bodyElement);
583 top()->finishParsingChildren();
584 m_top = m_top->releaseNext();
589 void HTMLElementStack::removeNonTopCommon(Element* element)
591 ASSERT(!element->hasTagName(HTMLNames::htmlTag));
592 ASSERT(!element->hasTagName(HTMLNames::bodyTag));
593 ASSERT(top() != element);
594 for (ElementRecord* pos = m_top.get(); pos; pos = pos->next()) {
595 if (pos->next()->element() == element) {
596 // FIXME: Is it OK to call finishParsingChildren()
597 // when the children aren't actually finished?
598 element->finishParsingChildren();
599 pos->setNext(pos->next()->releaseNext());
604 ASSERT_NOT_REACHED();
609 void HTMLElementStack::show()
611 for (ElementRecord* record = m_top.get(); record; record = record->next())
612 record->element()->showNode();