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