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->nodeType() == Node::SHADOW_ROOT_NODE
57 || node->hasTagName(htmlTag);
60 inline bool isScopeMarker(ContainerNode* node)
62 return node->hasTagName(appletTag)
63 || node->hasTagName(captionTag)
64 || node->hasTagName(marqueeTag)
65 || node->hasTagName(objectTag)
66 || node->hasTagName(tableTag)
67 || node->hasTagName(tdTag)
68 || node->hasTagName(thTag)
69 || node->hasTagName(MathMLNames::miTag)
70 || node->hasTagName(MathMLNames::moTag)
71 || node->hasTagName(MathMLNames::mnTag)
72 || node->hasTagName(MathMLNames::msTag)
73 || node->hasTagName(MathMLNames::mtextTag)
74 || node->hasTagName(MathMLNames::annotation_xmlTag)
75 || node->hasTagName(SVGNames::foreignObjectTag)
76 || node->hasTagName(SVGNames::descTag)
77 || node->hasTagName(SVGNames::titleTag)
81 inline bool isListItemScopeMarker(ContainerNode* node)
83 return isScopeMarker(node)
84 || node->hasTagName(olTag)
85 || node->hasTagName(ulTag);
88 inline bool isTableScopeMarker(ContainerNode* node)
90 return node->hasTagName(tableTag)
94 inline bool isTableBodyScopeMarker(ContainerNode* node)
96 return node->hasTagName(tbodyTag)
97 || node->hasTagName(tfootTag)
98 || node->hasTagName(theadTag)
102 inline bool isTableRowScopeMarker(ContainerNode* node)
104 return node->hasTagName(trTag)
108 inline bool isForeignContentScopeMarker(ContainerNode* node)
110 return node->hasTagName(MathMLNames::miTag)
111 || node->hasTagName(MathMLNames::moTag)
112 || node->hasTagName(MathMLNames::mnTag)
113 || node->hasTagName(MathMLNames::msTag)
114 || node->hasTagName(MathMLNames::mtextTag)
115 || node->hasTagName(SVGNames::foreignObjectTag)
116 || node->hasTagName(SVGNames::descTag)
117 || node->hasTagName(SVGNames::titleTag)
118 || isInHTMLNamespace(node);
121 inline bool isButtonScopeMarker(ContainerNode* node)
123 return isScopeMarker(node)
124 || node->hasTagName(buttonTag);
127 inline bool isSelectScopeMarker(ContainerNode* node)
129 return !node->hasTagName(optgroupTag)
130 && !node->hasTagName(optionTag);
135 HTMLElementStack::ElementRecord::ElementRecord(PassRefPtr<ContainerNode> node, PassOwnPtr<ElementRecord> next)
142 HTMLElementStack::ElementRecord::~ElementRecord()
146 void HTMLElementStack::ElementRecord::replaceElement(PassRefPtr<Element> element)
149 ASSERT(!m_node || m_node->isElementNode());
150 // FIXME: Should this call finishParsingChildren?
154 bool HTMLElementStack::ElementRecord::isAbove(ElementRecord* other) const
156 for (ElementRecord* below = next(); below; below = below->next()) {
163 HTMLElementStack::HTMLElementStack()
171 HTMLElementStack::~HTMLElementStack()
175 bool HTMLElementStack::hasOnlyOneElement() const
177 return !topRecord()->next();
180 bool HTMLElementStack::secondElementIsHTMLBodyElement() const
182 // This is used the fragment case of <body> and <frameset> in the "in body"
184 // http://www.whatwg.org/specs/web-apps/current-work/multipage/tokenization.html#parsing-main-inbody
186 // If we have a body element, it must always be the second element on the
187 // stack, as we always start with an html element, and any other element
188 // would cause the implicit creation of a body element.
189 return !!m_bodyElement;
192 void HTMLElementStack::popHTMLHeadElement()
194 ASSERT(top() == m_headElement);
199 void HTMLElementStack::popHTMLBodyElement()
201 ASSERT(top() == m_bodyElement);
206 void HTMLElementStack::popAll()
213 topNode()->finishParsingChildren();
214 m_top = m_top->releaseNext();
218 void HTMLElementStack::pop()
220 ASSERT(!top()->hasTagName(HTMLNames::headTag));
224 void HTMLElementStack::popUntil(const AtomicString& tagName)
226 while (!top()->hasLocalName(tagName)) {
227 // pop() will ASSERT at <body> if callers fail to check that there is an
228 // element with localName |tagName| on the stack of open elements.
233 void HTMLElementStack::popUntilPopped(const AtomicString& tagName)
239 void HTMLElementStack::popUntilNumberedHeaderElementPopped()
241 while (!isNumberedHeaderElement(topNode()))
246 void HTMLElementStack::popUntil(Element* element)
248 while (top() != element)
252 void HTMLElementStack::popUntilPopped(Element* element)
258 void HTMLElementStack::popUntilTableScopeMarker()
260 // http://www.whatwg.org/specs/web-apps/current-work/multipage/tokenization.html#clear-the-stack-back-to-a-table-context
261 while (!isTableScopeMarker(topNode()))
265 void HTMLElementStack::popUntilTableBodyScopeMarker()
267 // http://www.whatwg.org/specs/web-apps/current-work/multipage/tokenization.html#clear-the-stack-back-to-a-table-body-context
268 while (!isTableBodyScopeMarker(topNode()))
272 void HTMLElementStack::popUntilTableRowScopeMarker()
274 // http://www.whatwg.org/specs/web-apps/current-work/multipage/tokenization.html#clear-the-stack-back-to-a-table-row-context
275 while (!isTableRowScopeMarker(topNode()))
279 void HTMLElementStack::popUntilForeignContentScopeMarker()
281 while (!isForeignContentScopeMarker(topNode()))
285 void HTMLElementStack::pushRootNode(PassRefPtr<ContainerNode> rootNode)
287 ASSERT(rootNode->nodeType() == Node::DOCUMENT_FRAGMENT_NODE || rootNode->nodeType() == Node::SHADOW_ROOT_NODE);
288 pushRootNodeCommon(rootNode);
291 void HTMLElementStack::pushHTMLHtmlElement(PassRefPtr<Element> element)
293 ASSERT(element->hasTagName(HTMLNames::htmlTag));
294 pushRootNodeCommon(element);
297 void HTMLElementStack::pushRootNodeCommon(PassRefPtr<ContainerNode> rootNode)
301 m_rootNode = rootNode.get();
302 pushCommon(rootNode);
305 void HTMLElementStack::pushHTMLHeadElement(PassRefPtr<Element> element)
307 ASSERT(element->hasTagName(HTMLNames::headTag));
308 ASSERT(!m_headElement);
309 m_headElement = element.get();
313 void HTMLElementStack::pushHTMLBodyElement(PassRefPtr<Element> element)
315 ASSERT(element->hasTagName(HTMLNames::bodyTag));
316 ASSERT(!m_bodyElement);
317 m_bodyElement = element.get();
321 void HTMLElementStack::push(PassRefPtr<Element> element)
323 ASSERT(!element->hasTagName(HTMLNames::htmlTag));
324 ASSERT(!element->hasTagName(HTMLNames::headTag));
325 ASSERT(!element->hasTagName(HTMLNames::bodyTag));
330 void HTMLElementStack::insertAbove(PassRefPtr<Element> element, ElementRecord* recordBelow)
335 ASSERT(!element->hasTagName(HTMLNames::htmlTag));
336 ASSERT(!element->hasTagName(HTMLNames::headTag));
337 ASSERT(!element->hasTagName(HTMLNames::bodyTag));
339 if (recordBelow == m_top) {
344 for (ElementRecord* recordAbove = m_top.get(); recordAbove; recordAbove = recordAbove->next()) {
345 if (recordAbove->next() != recordBelow)
349 recordAbove->setNext(adoptPtr(new ElementRecord(element, recordAbove->releaseNext())));
350 recordAbove->next()->element()->beginParsingChildren();
353 ASSERT_NOT_REACHED();
356 HTMLElementStack::ElementRecord* HTMLElementStack::topRecord() const
362 Element* HTMLElementStack::oneBelowTop() const
364 // We should never be calling this if it could be 0.
366 ASSERT(m_top->next());
367 return m_top->next()->element();
370 Element* HTMLElementStack::bottom() const
372 return htmlElement();
375 void HTMLElementStack::removeHTMLHeadElement(Element* element)
377 ASSERT(m_headElement == element);
378 if (m_top->element() == element) {
379 popHTMLHeadElement();
383 removeNonTopCommon(element);
386 void HTMLElementStack::remove(Element* element)
388 ASSERT(!element->hasTagName(HTMLNames::headTag));
389 if (m_top->element() == element) {
393 removeNonTopCommon(element);
396 HTMLElementStack::ElementRecord* HTMLElementStack::find(Element* element) const
398 for (ElementRecord* pos = m_top.get(); pos; pos = pos->next()) {
399 if (pos->node() == element)
405 HTMLElementStack::ElementRecord* HTMLElementStack::topmost(const AtomicString& tagName) const
407 for (ElementRecord* pos = m_top.get(); pos; pos = pos->next()) {
408 if (pos->node()->hasLocalName(tagName))
414 bool HTMLElementStack::contains(Element* element) const
416 return !!find(element);
419 bool HTMLElementStack::contains(const AtomicString& tagName) const
421 return !!topmost(tagName);
424 template <bool isMarker(ContainerNode*)>
425 bool inScopeCommon(HTMLElementStack::ElementRecord* top, const AtomicString& targetTag)
427 for (HTMLElementStack::ElementRecord* pos = top; pos; pos = pos->next()) {
428 ContainerNode* node = pos->node();
429 if (node->hasLocalName(targetTag))
434 ASSERT_NOT_REACHED(); // <html> is always on the stack and is a scope marker.
438 bool HTMLElementStack::hasOnlyHTMLElementsInScope() const
440 for (ElementRecord* record = m_top.get(); record; record = record->next()) {
441 ContainerNode* node = record->node();
442 if (!isInHTMLNamespace(node))
444 if (isScopeMarker(node))
447 ASSERT_NOT_REACHED(); // <html> is always on the stack and is a scope marker.
451 bool HTMLElementStack::hasNumberedHeaderElementInScope() const
453 for (ElementRecord* record = m_top.get(); record; record = record->next()) {
454 ContainerNode* node = record->node();
455 if (isNumberedHeaderElement(node))
457 if (isScopeMarker(node))
460 ASSERT_NOT_REACHED(); // <html> is always on the stack and is a scope marker.
464 bool HTMLElementStack::inScope(Element* targetElement) const
466 for (ElementRecord* pos = m_top.get(); pos; pos = pos->next()) {
467 ContainerNode* node = pos->node();
468 if (node == targetElement)
470 if (isScopeMarker(node))
473 ASSERT_NOT_REACHED(); // <html> is always on the stack and is a scope marker.
477 bool HTMLElementStack::inScope(const AtomicString& targetTag) const
479 return inScopeCommon<isScopeMarker>(m_top.get(), targetTag);
482 bool HTMLElementStack::inScope(const QualifiedName& tagName) const
484 // FIXME: Is localName() right for non-html elements?
485 return inScope(tagName.localName());
488 bool HTMLElementStack::inListItemScope(const AtomicString& targetTag) const
490 return inScopeCommon<isListItemScopeMarker>(m_top.get(), targetTag);
493 bool HTMLElementStack::inListItemScope(const QualifiedName& tagName) const
495 // FIXME: Is localName() right for non-html elements?
496 return inListItemScope(tagName.localName());
499 bool HTMLElementStack::inTableScope(const AtomicString& targetTag) const
501 return inScopeCommon<isTableScopeMarker>(m_top.get(), targetTag);
504 bool HTMLElementStack::inTableScope(const QualifiedName& tagName) const
506 // FIXME: Is localName() right for non-html elements?
507 return inTableScope(tagName.localName());
510 bool HTMLElementStack::inButtonScope(const AtomicString& targetTag) const
512 return inScopeCommon<isButtonScopeMarker>(m_top.get(), targetTag);
515 bool HTMLElementStack::inButtonScope(const QualifiedName& tagName) const
517 // FIXME: Is localName() right for non-html elements?
518 return inButtonScope(tagName.localName());
521 bool HTMLElementStack::inSelectScope(const AtomicString& targetTag) const
523 return inScopeCommon<isSelectScopeMarker>(m_top.get(), targetTag);
526 bool HTMLElementStack::inSelectScope(const QualifiedName& tagName) const
528 // FIXME: Is localName() right for non-html elements?
529 return inSelectScope(tagName.localName());
532 Element* HTMLElementStack::htmlElement() const
535 return toElement(m_rootNode);
538 Element* HTMLElementStack::headElement() const
540 ASSERT(m_headElement);
541 return m_headElement;
544 Element* HTMLElementStack::bodyElement() const
546 ASSERT(m_bodyElement);
547 return m_bodyElement;
550 ContainerNode* HTMLElementStack::rootNode() const
556 void HTMLElementStack::pushCommon(PassRefPtr<ContainerNode> node)
561 m_top = adoptPtr(new ElementRecord(node, m_top.release()));
562 topNode()->beginParsingChildren();
565 void HTMLElementStack::popCommon()
567 ASSERT(!top()->hasTagName(HTMLNames::htmlTag));
568 ASSERT(!top()->hasTagName(HTMLNames::headTag) || !m_headElement);
569 ASSERT(!top()->hasTagName(HTMLNames::bodyTag) || !m_bodyElement);
570 top()->finishParsingChildren();
571 m_top = m_top->releaseNext();
576 void HTMLElementStack::removeNonTopCommon(Element* element)
578 ASSERT(!element->hasTagName(HTMLNames::htmlTag));
579 ASSERT(!element->hasTagName(HTMLNames::bodyTag));
580 ASSERT(top() != element);
581 for (ElementRecord* pos = m_top.get(); pos; pos = pos->next()) {
582 if (pos->next()->element() == element) {
583 // FIXME: Is it OK to call finishParsingChildren()
584 // when the children aren't actually finished?
585 element->finishParsingChildren();
586 pos->setNext(pos->next()->releaseNext());
591 ASSERT_NOT_REACHED();
596 void HTMLElementStack::show()
598 for (ElementRecord* record = m_top.get(); record; record = record->next())
599 record->element()->showNode();