Home | History | Annotate | Download | only in parser
      1 /*
      2  * Copyright (C) 2010 Google, Inc. All Rights Reserved.
      3  * Copyright (C) 2011 Apple Inc. All rights reserved.
      4  *
      5  * Redistribution and use in source and binary forms, with or without
      6  * modification, are permitted provided that the following conditions
      7  * are met:
      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.
     13  *
     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.
     25  */
     26 
     27 #include "config.h"
     28 #include "HTMLElementStack.h"
     29 
     30 #include "DocumentFragment.h"
     31 #include "Element.h"
     32 #include "HTMLNames.h"
     33 #include "MathMLNames.h"
     34 #include "SVGNames.h"
     35 #include <wtf/PassOwnPtr.h>
     36 
     37 namespace WebCore {
     38 
     39 using namespace HTMLNames;
     40 
     41 namespace {
     42 
     43 inline bool isNumberedHeaderElement(ContainerNode* node)
     44 {
     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);
     51 }
     52 
     53 inline bool isRootNode(ContainerNode* node)
     54 {
     55     return node->nodeType() == Node::DOCUMENT_FRAGMENT_NODE
     56         || node->hasTagName(htmlTag);
     57 }
     58 
     59 inline bool isScopeMarker(ContainerNode* node)
     60 {
     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)
     77         || isRootNode(node);
     78 }
     79 
     80 inline bool isListItemScopeMarker(ContainerNode* node)
     81 {
     82     return isScopeMarker(node)
     83         || node->hasTagName(olTag)
     84         || node->hasTagName(ulTag);
     85 }
     86 
     87 inline bool isTableScopeMarker(ContainerNode* node)
     88 {
     89     return node->hasTagName(tableTag)
     90         || isRootNode(node);
     91 }
     92 
     93 inline bool isTableBodyScopeMarker(ContainerNode* node)
     94 {
     95     return node->hasTagName(tbodyTag)
     96         || node->hasTagName(tfootTag)
     97         || node->hasTagName(theadTag)
     98         || isRootNode(node);
     99 }
    100 
    101 inline bool isTableRowScopeMarker(ContainerNode* node)
    102 {
    103     return node->hasTagName(trTag)
    104         || isRootNode(node);
    105 }
    106 
    107 inline bool isForeignContentScopeMarker(ContainerNode* node)
    108 {
    109     return node->hasTagName(MathMLNames::miTag)
    110         || node->hasTagName(MathMLNames::moTag)
    111         || node->hasTagName(MathMLNames::mnTag)
    112         || node->hasTagName(MathMLNames::msTag)
    113         || node->hasTagName(MathMLNames::mtextTag)
    114         || node->hasTagName(SVGNames::foreignObjectTag)
    115         || node->hasTagName(SVGNames::descTag)
    116         || node->hasTagName(SVGNames::titleTag)
    117         || isInHTMLNamespace(node);
    118 }
    119 
    120 inline bool isButtonScopeMarker(ContainerNode* node)
    121 {
    122     return isScopeMarker(node)
    123         || node->hasTagName(buttonTag);
    124 }
    125 
    126 inline bool isSelectScopeMarker(ContainerNode* node)
    127 {
    128     return !node->hasTagName(optgroupTag)
    129         && !node->hasTagName(optionTag);
    130 }
    131 
    132 }
    133 
    134 HTMLElementStack::ElementRecord::ElementRecord(PassRefPtr<ContainerNode> node, PassOwnPtr<ElementRecord> next)
    135     : m_node(node)
    136     , m_next(next)
    137 {
    138     ASSERT(m_node);
    139 }
    140 
    141 HTMLElementStack::ElementRecord::~ElementRecord()
    142 {
    143 }
    144 
    145 void HTMLElementStack::ElementRecord::replaceElement(PassRefPtr<Element> element)
    146 {
    147     ASSERT(element);
    148     ASSERT(!m_node || m_node->isElementNode());
    149     // FIXME: Should this call finishParsingChildren?
    150     m_node = element;
    151 }
    152 
    153 bool HTMLElementStack::ElementRecord::isAbove(ElementRecord* other) const
    154 {
    155     for (ElementRecord* below = next(); below; below = below->next()) {
    156         if (below == other)
    157             return true;
    158     }
    159     return false;
    160 }
    161 
    162 HTMLElementStack::HTMLElementStack()
    163     : m_rootNode(0)
    164     , m_headElement(0)
    165     , m_bodyElement(0)
    166 {
    167 }
    168 
    169 HTMLElementStack::~HTMLElementStack()
    170 {
    171 }
    172 
    173 bool HTMLElementStack::hasOnlyOneElement() const
    174 {
    175     return !topRecord()->next();
    176 }
    177 
    178 bool HTMLElementStack::secondElementIsHTMLBodyElement() const
    179 {
    180     // This is used the fragment case of <body> and <frameset> in the "in body"
    181     // insertion mode.
    182     // http://www.whatwg.org/specs/web-apps/current-work/multipage/tokenization.html#parsing-main-inbody
    183     ASSERT(m_rootNode);
    184     // If we have a body element, it must always be the second element on the
    185     // stack, as we always start with an html element, and any other element
    186     // would cause the implicit creation of a body element.
    187     return !!m_bodyElement;
    188 }
    189 
    190 void HTMLElementStack::popHTMLHeadElement()
    191 {
    192     ASSERT(top() == m_headElement);
    193     m_headElement = 0;
    194     popCommon();
    195 }
    196 
    197 void HTMLElementStack::popHTMLBodyElement()
    198 {
    199     ASSERT(top() == m_bodyElement);
    200     m_bodyElement = 0;
    201     popCommon();
    202 }
    203 
    204 void HTMLElementStack::popAll()
    205 {
    206     m_rootNode = 0;
    207     m_headElement = 0;
    208     m_bodyElement = 0;
    209     while (m_top) {
    210         topNode()->finishParsingChildren();
    211         m_top = m_top->releaseNext();
    212     }
    213 }
    214 
    215 void HTMLElementStack::pop()
    216 {
    217     ASSERT(!top()->hasTagName(HTMLNames::headTag));
    218     popCommon();
    219 }
    220 
    221 void HTMLElementStack::popUntil(const AtomicString& tagName)
    222 {
    223     while (!top()->hasLocalName(tagName)) {
    224         // pop() will ASSERT at <body> if callers fail to check that there is an
    225         // element with localName |tagName| on the stack of open elements.
    226         pop();
    227     }
    228 }
    229 
    230 void HTMLElementStack::popUntilPopped(const AtomicString& tagName)
    231 {
    232     popUntil(tagName);
    233     pop();
    234 }
    235 
    236 void HTMLElementStack::popUntilNumberedHeaderElementPopped()
    237 {
    238     while (!isNumberedHeaderElement(topNode()))
    239         pop();
    240     pop();
    241 }
    242 
    243 void HTMLElementStack::popUntil(Element* element)
    244 {
    245     while (top() != element)
    246         pop();
    247 }
    248 
    249 void HTMLElementStack::popUntilPopped(Element* element)
    250 {
    251     popUntil(element);
    252     pop();
    253 }
    254 
    255 void HTMLElementStack::popUntilTableScopeMarker()
    256 {
    257     // http://www.whatwg.org/specs/web-apps/current-work/multipage/tokenization.html#clear-the-stack-back-to-a-table-context
    258     while (!isTableScopeMarker(topNode()))
    259         pop();
    260 }
    261 
    262 void HTMLElementStack::popUntilTableBodyScopeMarker()
    263 {
    264     // http://www.whatwg.org/specs/web-apps/current-work/multipage/tokenization.html#clear-the-stack-back-to-a-table-body-context
    265     while (!isTableBodyScopeMarker(topNode()))
    266         pop();
    267 }
    268 
    269 void HTMLElementStack::popUntilTableRowScopeMarker()
    270 {
    271     // http://www.whatwg.org/specs/web-apps/current-work/multipage/tokenization.html#clear-the-stack-back-to-a-table-row-context
    272     while (!isTableRowScopeMarker(topNode()))
    273         pop();
    274 }
    275 
    276 void HTMLElementStack::popUntilForeignContentScopeMarker()
    277 {
    278     while (!isForeignContentScopeMarker(topNode()))
    279         pop();
    280 }
    281 
    282 void HTMLElementStack::pushRootNode(PassRefPtr<ContainerNode> rootNode)
    283 {
    284     ASSERT(rootNode->nodeType() == Node::DOCUMENT_FRAGMENT_NODE);
    285     pushRootNodeCommon(rootNode);
    286 }
    287 
    288 void HTMLElementStack::pushHTMLHtmlElement(PassRefPtr<Element> element)
    289 {
    290     ASSERT(element->hasTagName(HTMLNames::htmlTag));
    291     pushRootNodeCommon(element);
    292 }
    293 
    294 void HTMLElementStack::pushRootNodeCommon(PassRefPtr<ContainerNode> rootNode)
    295 {
    296     ASSERT(!m_top);
    297     ASSERT(!m_rootNode);
    298     m_rootNode = rootNode.get();
    299     pushCommon(rootNode);
    300 }
    301 
    302 void HTMLElementStack::pushHTMLHeadElement(PassRefPtr<Element> element)
    303 {
    304     ASSERT(element->hasTagName(HTMLNames::headTag));
    305     ASSERT(!m_headElement);
    306     m_headElement = element.get();
    307     pushCommon(element);
    308 }
    309 
    310 void HTMLElementStack::pushHTMLBodyElement(PassRefPtr<Element> element)
    311 {
    312     ASSERT(element->hasTagName(HTMLNames::bodyTag));
    313     ASSERT(!m_bodyElement);
    314     m_bodyElement = element.get();
    315     pushCommon(element);
    316 }
    317 
    318 void HTMLElementStack::push(PassRefPtr<Element> element)
    319 {
    320     ASSERT(!element->hasTagName(HTMLNames::htmlTag));
    321     ASSERT(!element->hasTagName(HTMLNames::headTag));
    322     ASSERT(!element->hasTagName(HTMLNames::bodyTag));
    323     ASSERT(m_rootNode);
    324     pushCommon(element);
    325 }
    326 
    327 void HTMLElementStack::insertAbove(PassRefPtr<Element> element, ElementRecord* recordBelow)
    328 {
    329     ASSERT(element);
    330     ASSERT(recordBelow);
    331     ASSERT(m_top);
    332     ASSERT(!element->hasTagName(HTMLNames::htmlTag));
    333     ASSERT(!element->hasTagName(HTMLNames::headTag));
    334     ASSERT(!element->hasTagName(HTMLNames::bodyTag));
    335     ASSERT(m_rootNode);
    336     if (recordBelow == m_top) {
    337         push(element);
    338         return;
    339     }
    340 
    341     for (ElementRecord* recordAbove = m_top.get(); recordAbove; recordAbove = recordAbove->next()) {
    342         if (recordAbove->next() != recordBelow)
    343             continue;
    344 
    345         recordAbove->setNext(adoptPtr(new ElementRecord(element, recordAbove->releaseNext())));
    346         recordAbove->next()->element()->beginParsingChildren();
    347         return;
    348     }
    349     ASSERT_NOT_REACHED();
    350 }
    351 
    352 HTMLElementStack::ElementRecord* HTMLElementStack::topRecord() const
    353 {
    354     ASSERT(m_top);
    355     return m_top.get();
    356 }
    357 
    358 Element* HTMLElementStack::oneBelowTop() const
    359 {
    360     // We should never be calling this if it could be 0.
    361     ASSERT(m_top);
    362     ASSERT(m_top->next());
    363     return m_top->next()->element();
    364 }
    365 
    366 Element* HTMLElementStack::bottom() const
    367 {
    368     return htmlElement();
    369 }
    370 
    371 void HTMLElementStack::removeHTMLHeadElement(Element* element)
    372 {
    373     ASSERT(m_headElement == element);
    374     if (m_top->element() == element) {
    375         popHTMLHeadElement();
    376         return;
    377     }
    378     m_headElement = 0;
    379     removeNonTopCommon(element);
    380 }
    381 
    382 void HTMLElementStack::remove(Element* element)
    383 {
    384     ASSERT(!element->hasTagName(HTMLNames::headTag));
    385     if (m_top->element() == element) {
    386         pop();
    387         return;
    388     }
    389     removeNonTopCommon(element);
    390 }
    391 
    392 HTMLElementStack::ElementRecord* HTMLElementStack::find(Element* element) const
    393 {
    394     for (ElementRecord* pos = m_top.get(); pos; pos = pos->next()) {
    395         if (pos->node() == element)
    396             return pos;
    397     }
    398     return 0;
    399 }
    400 
    401 HTMLElementStack::ElementRecord* HTMLElementStack::topmost(const AtomicString& tagName) const
    402 {
    403     for (ElementRecord* pos = m_top.get(); pos; pos = pos->next()) {
    404         if (pos->node()->hasLocalName(tagName))
    405             return pos;
    406     }
    407     return 0;
    408 }
    409 
    410 bool HTMLElementStack::contains(Element* element) const
    411 {
    412     return !!find(element);
    413 }
    414 
    415 bool HTMLElementStack::contains(const AtomicString& tagName) const
    416 {
    417     return !!topmost(tagName);
    418 }
    419 
    420 template <bool isMarker(ContainerNode*)>
    421 bool inScopeCommon(HTMLElementStack::ElementRecord* top, const AtomicString& targetTag)
    422 {
    423     for (HTMLElementStack::ElementRecord* pos = top; pos; pos = pos->next()) {
    424         ContainerNode* node = pos->node();
    425         if (node->hasLocalName(targetTag))
    426             return true;
    427         if (isMarker(node))
    428             return false;
    429     }
    430     ASSERT_NOT_REACHED(); // <html> is always on the stack and is a scope marker.
    431     return false;
    432 }
    433 
    434 bool HTMLElementStack::hasOnlyHTMLElementsInScope() const
    435 {
    436     for (ElementRecord* record = m_top.get(); record; record = record->next()) {
    437         ContainerNode* node = record->node();
    438         if (!isInHTMLNamespace(node))
    439             return false;
    440         if (isScopeMarker(node))
    441             return true;
    442     }
    443     ASSERT_NOT_REACHED(); // <html> is always on the stack and is a scope marker.
    444     return true;
    445 }
    446 
    447 bool HTMLElementStack::hasNumberedHeaderElementInScope() const
    448 {
    449     for (ElementRecord* record = m_top.get(); record; record = record->next()) {
    450         ContainerNode* node = record->node();
    451         if (isNumberedHeaderElement(node))
    452             return true;
    453         if (isScopeMarker(node))
    454             return false;
    455     }
    456     ASSERT_NOT_REACHED(); // <html> is always on the stack and is a scope marker.
    457     return false;
    458 }
    459 
    460 bool HTMLElementStack::inScope(Element* targetElement) const
    461 {
    462     for (ElementRecord* pos = m_top.get(); pos; pos = pos->next()) {
    463         ContainerNode* node = pos->node();
    464         if (node == targetElement)
    465             return true;
    466         if (isScopeMarker(node))
    467             return false;
    468     }
    469     ASSERT_NOT_REACHED(); // <html> is always on the stack and is a scope marker.
    470     return false;
    471 }
    472 
    473 bool HTMLElementStack::inScope(const AtomicString& targetTag) const
    474 {
    475     return inScopeCommon<isScopeMarker>(m_top.get(), targetTag);
    476 }
    477 
    478 bool HTMLElementStack::inScope(const QualifiedName& tagName) const
    479 {
    480     // FIXME: Is localName() right for non-html elements?
    481     return inScope(tagName.localName());
    482 }
    483 
    484 bool HTMLElementStack::inListItemScope(const AtomicString& targetTag) const
    485 {
    486     return inScopeCommon<isListItemScopeMarker>(m_top.get(), targetTag);
    487 }
    488 
    489 bool HTMLElementStack::inListItemScope(const QualifiedName& tagName) const
    490 {
    491     // FIXME: Is localName() right for non-html elements?
    492     return inListItemScope(tagName.localName());
    493 }
    494 
    495 bool HTMLElementStack::inTableScope(const AtomicString& targetTag) const
    496 {
    497     return inScopeCommon<isTableScopeMarker>(m_top.get(), targetTag);
    498 }
    499 
    500 bool HTMLElementStack::inTableScope(const QualifiedName& tagName) const
    501 {
    502     // FIXME: Is localName() right for non-html elements?
    503     return inTableScope(tagName.localName());
    504 }
    505 
    506 bool HTMLElementStack::inButtonScope(const AtomicString& targetTag) const
    507 {
    508     return inScopeCommon<isButtonScopeMarker>(m_top.get(), targetTag);
    509 }
    510 
    511 bool HTMLElementStack::inButtonScope(const QualifiedName& tagName) const
    512 {
    513     // FIXME: Is localName() right for non-html elements?
    514     return inButtonScope(tagName.localName());
    515 }
    516 
    517 bool HTMLElementStack::inSelectScope(const AtomicString& targetTag) const
    518 {
    519     return inScopeCommon<isSelectScopeMarker>(m_top.get(), targetTag);
    520 }
    521 
    522 bool HTMLElementStack::inSelectScope(const QualifiedName& tagName) const
    523 {
    524     // FIXME: Is localName() right for non-html elements?
    525     return inSelectScope(tagName.localName());
    526 }
    527 
    528 Element* HTMLElementStack::htmlElement() const
    529 {
    530     ASSERT(m_rootNode);
    531     return toElement(m_rootNode);
    532 }
    533 
    534 Element* HTMLElementStack::headElement() const
    535 {
    536     ASSERT(m_headElement);
    537     return m_headElement;
    538 }
    539 
    540 Element* HTMLElementStack::bodyElement() const
    541 {
    542     ASSERT(m_bodyElement);
    543     return m_bodyElement;
    544 }
    545 
    546 ContainerNode* HTMLElementStack::rootNode() const
    547 {
    548     ASSERT(m_rootNode);
    549     return m_rootNode;
    550 }
    551 
    552 void HTMLElementStack::pushCommon(PassRefPtr<ContainerNode> node)
    553 {
    554     ASSERT(m_rootNode);
    555     m_top = adoptPtr(new ElementRecord(node, m_top.release()));
    556     topNode()->beginParsingChildren();
    557 }
    558 
    559 void HTMLElementStack::popCommon()
    560 {
    561     ASSERT(!top()->hasTagName(HTMLNames::htmlTag));
    562     ASSERT(!top()->hasTagName(HTMLNames::headTag) || !m_headElement);
    563     ASSERT(!top()->hasTagName(HTMLNames::bodyTag) || !m_bodyElement);
    564     top()->finishParsingChildren();
    565     m_top = m_top->releaseNext();
    566 }
    567 
    568 void HTMLElementStack::removeNonTopCommon(Element* element)
    569 {
    570     ASSERT(!element->hasTagName(HTMLNames::htmlTag));
    571     ASSERT(!element->hasTagName(HTMLNames::bodyTag));
    572     ASSERT(top() != element);
    573     for (ElementRecord* pos = m_top.get(); pos; pos = pos->next()) {
    574         if (pos->next()->element() == element) {
    575             // FIXME: Is it OK to call finishParsingChildren()
    576             // when the children aren't actually finished?
    577             element->finishParsingChildren();
    578             pos->setNext(pos->next()->releaseNext());
    579             return;
    580         }
    581     }
    582     ASSERT_NOT_REACHED();
    583 }
    584 
    585 #ifndef NDEBUG
    586 
    587 void HTMLElementStack::show()
    588 {
    589     for (ElementRecord* record = m_top.get(); record; record = record->next())
    590         record->element()->showNode();
    591 }
    592 
    593 #endif
    594 
    595 }
    596