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