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