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 "HTMLTreeBuilder.h" 29 30#include "Comment.h" 31#include "DocumentFragment.h" 32#include "DocumentType.h" 33#include "Frame.h" 34#include "FrameLoader.h" 35#include "FrameLoaderClient.h" 36#include "HTMLElementFactory.h" 37#include "HTMLFormElement.h" 38#include "HTMLHtmlElement.h" 39#include "HTMLOptGroupElement.h" 40#include "HTMLOptionElement.h" 41#include "HTMLParserIdioms.h" 42#include "HTMLScriptElement.h" 43#include "HTMLTemplateElement.h" 44#include "NotImplemented.h" 45#include "SVGElement.h" 46#include "Text.h" 47 48namespace WebCore { 49 50using namespace HTMLNames; 51 52static inline void setAttributes(Element* element, AtomicHTMLToken* token, ParserContentPolicy parserContentPolicy) 53{ 54 if (!scriptingContentIsAllowed(parserContentPolicy)) 55 element->stripScriptingAttributes(token->attributes()); 56 element->parserSetAttributes(token->attributes()); 57} 58 59static bool hasImpliedEndTag(const HTMLStackItem* item) 60{ 61 return item->hasTagName(ddTag) 62 || item->hasTagName(dtTag) 63 || item->hasTagName(liTag) 64 || isHTMLOptionElement(item->node()) 65 || isHTMLOptGroupElement(item->node()) 66 || item->hasTagName(pTag) 67 || item->hasTagName(rbTag) 68 || item->hasTagName(rpTag) 69 || item->hasTagName(rtTag) 70 || item->hasTagName(rtcTag); 71} 72 73static bool shouldUseLengthLimit(const ContainerNode* node) 74{ 75 return !node->hasTagName(scriptTag) 76 && !node->hasTagName(styleTag) 77 && !node->hasTagName(SVGNames::scriptTag); 78} 79 80static inline bool isAllWhitespace(const String& string) 81{ 82 return string.isAllSpecialCharacters<isHTMLSpace>(); 83} 84 85static inline void insert(HTMLConstructionSiteTask& task) 86{ 87#if ENABLE(TEMPLATE_ELEMENT) 88 if (task.parent->hasTagName(templateTag)) 89 task.parent = toHTMLTemplateElement(task.parent.get())->content(); 90#endif 91 92 if (ContainerNode* parent = task.child->parentNode()) 93 parent->parserRemoveChild(*task.child); 94 95 if (task.nextChild) 96 task.parent->parserInsertBefore(task.child.get(), task.nextChild.get()); 97 else 98 task.parent->parserAppendChild(task.child.get()); 99} 100 101static inline void executeInsertTask(HTMLConstructionSiteTask& task) 102{ 103 ASSERT(task.operation == HTMLConstructionSiteTask::Insert); 104 105 insert(task); 106 107 task.child->beginParsingChildren(); 108 109 if (task.selfClosing) 110 task.child->finishParsingChildren(); 111} 112 113static inline void executeReparentTask(HTMLConstructionSiteTask& task) 114{ 115 ASSERT(task.operation == HTMLConstructionSiteTask::Reparent); 116 117 if (ContainerNode* parent = task.child->parentNode()) 118 parent->parserRemoveChild(*task.child); 119 120 task.parent->parserAppendChild(task.child); 121} 122 123static inline void executeInsertAlreadyParsedChildTask(HTMLConstructionSiteTask& task) 124{ 125 ASSERT(task.operation == HTMLConstructionSiteTask::InsertAlreadyParsedChild); 126 127 insert(task); 128} 129 130static inline void executeTakeAllChildrenTask(HTMLConstructionSiteTask& task) 131{ 132 ASSERT(task.operation == HTMLConstructionSiteTask::TakeAllChildren); 133 134 task.parent->takeAllChildrenFrom(task.oldParent()); 135 // Notice that we don't need to manually attach the moved children 136 // because takeAllChildrenFrom does that work for us. 137} 138 139static inline void executeTask(HTMLConstructionSiteTask& task) 140{ 141 switch (task.operation) { 142 case HTMLConstructionSiteTask::Insert: 143 executeInsertTask(task); 144 return; 145 // All the cases below this point are only used by the adoption agency. 146 case HTMLConstructionSiteTask::InsertAlreadyParsedChild: 147 executeInsertAlreadyParsedChildTask(task); 148 return; 149 case HTMLConstructionSiteTask::Reparent: 150 executeReparentTask(task); 151 return; 152 case HTMLConstructionSiteTask::TakeAllChildren: 153 executeTakeAllChildrenTask(task); 154 return; 155 } 156 ASSERT_NOT_REACHED(); 157} 158 159void HTMLConstructionSite::attachLater(ContainerNode* parent, PassRefPtr<Node> prpChild, bool selfClosing) 160{ 161 ASSERT(scriptingContentIsAllowed(m_parserContentPolicy) || !prpChild.get()->isElementNode() || !toScriptElementIfPossible(toElement(prpChild.get()))); 162 ASSERT(pluginContentIsAllowed(m_parserContentPolicy) || !prpChild->isPluginElement()); 163 164 HTMLConstructionSiteTask task(HTMLConstructionSiteTask::Insert); 165 task.parent = parent; 166 task.child = prpChild; 167 task.selfClosing = selfClosing; 168 169 if (shouldFosterParent()) { 170 fosterParent(task.child); 171 return; 172 } 173 174 // Add as a sibling of the parent if we have reached the maximum depth allowed. 175 if (m_openElements.stackDepth() > m_maximumDOMTreeDepth && task.parent->parentNode()) 176 task.parent = task.parent->parentNode(); 177 178 ASSERT(task.parent); 179 m_taskQueue.append(task); 180} 181 182void HTMLConstructionSite::executeQueuedTasks() 183{ 184 const size_t size = m_taskQueue.size(); 185 if (!size) 186 return; 187 188 // Copy the task queue into a local variable in case executeTask 189 // re-enters the parser. 190 TaskQueue queue = WTF::move(m_taskQueue); 191 192 for (size_t i = 0; i < size; ++i) 193 executeTask(queue[i]); 194 195 // We might be detached now. 196} 197 198HTMLConstructionSite::HTMLConstructionSite(Document& document, ParserContentPolicy parserContentPolicy, unsigned maximumDOMTreeDepth) 199 : m_document(&document) 200 , m_attachmentRoot(&document) 201 , m_parserContentPolicy(parserContentPolicy) 202 , m_isParsingFragment(false) 203 , m_redirectAttachToFosterParent(false) 204 , m_maximumDOMTreeDepth(maximumDOMTreeDepth) 205 , m_inQuirksMode(document.inQuirksMode()) 206{ 207 ASSERT(m_document->isHTMLDocument() || m_document->isXHTMLDocument()); 208} 209 210HTMLConstructionSite::HTMLConstructionSite(DocumentFragment& fragment, ParserContentPolicy parserContentPolicy, unsigned maximumDOMTreeDepth) 211 : m_document(&fragment.document()) 212 , m_attachmentRoot(&fragment) 213 , m_parserContentPolicy(parserContentPolicy) 214 , m_isParsingFragment(true) 215 , m_redirectAttachToFosterParent(false) 216 , m_maximumDOMTreeDepth(maximumDOMTreeDepth) 217 , m_inQuirksMode(fragment.document().inQuirksMode()) 218{ 219 ASSERT(m_document->isHTMLDocument() || m_document->isXHTMLDocument()); 220} 221 222HTMLConstructionSite::~HTMLConstructionSite() 223{ 224} 225 226void HTMLConstructionSite::detach() 227{ 228 m_document = 0; 229 m_attachmentRoot = 0; 230} 231 232void HTMLConstructionSite::setForm(HTMLFormElement* form) 233{ 234 // This method should only be needed for HTMLTreeBuilder in the fragment case. 235 ASSERT(!m_form); 236 m_form = form; 237} 238 239PassRefPtr<HTMLFormElement> HTMLConstructionSite::takeForm() 240{ 241 return m_form.release(); 242} 243 244void HTMLConstructionSite::dispatchDocumentElementAvailableIfNeeded() 245{ 246 ASSERT(m_document); 247 if (m_document->frame() && !m_isParsingFragment) 248 m_document->frame()->injectUserScripts(InjectAtDocumentStart); 249} 250 251void HTMLConstructionSite::insertHTMLHtmlStartTagBeforeHTML(AtomicHTMLToken* token) 252{ 253 RefPtr<HTMLHtmlElement> element = HTMLHtmlElement::create(*m_document); 254 setAttributes(element.get(), token, m_parserContentPolicy); 255 attachLater(m_attachmentRoot, element); 256 m_openElements.pushHTMLHtmlElement(HTMLStackItem::create(element, token)); 257 258 executeQueuedTasks(); 259 element->insertedByParser(); 260 dispatchDocumentElementAvailableIfNeeded(); 261} 262 263void HTMLConstructionSite::mergeAttributesFromTokenIntoElement(AtomicHTMLToken* token, Element* element) 264{ 265 if (token->attributes().isEmpty()) 266 return; 267 268 for (unsigned i = 0; i < token->attributes().size(); ++i) { 269 const Attribute& tokenAttribute = token->attributes().at(i); 270 if (!element->elementData() || !element->findAttributeByName(tokenAttribute.name())) 271 element->setAttribute(tokenAttribute.name(), tokenAttribute.value()); 272 } 273} 274 275void HTMLConstructionSite::insertHTMLHtmlStartTagInBody(AtomicHTMLToken* token) 276{ 277 // Fragments do not have a root HTML element, so any additional HTML elements 278 // encountered during fragment parsing should be ignored. 279 if (m_isParsingFragment) 280 return; 281 282 mergeAttributesFromTokenIntoElement(token, m_openElements.htmlElement()); 283} 284 285void HTMLConstructionSite::insertHTMLBodyStartTagInBody(AtomicHTMLToken* token) 286{ 287 mergeAttributesFromTokenIntoElement(token, m_openElements.bodyElement()); 288} 289 290void HTMLConstructionSite::setDefaultCompatibilityMode() 291{ 292 if (m_isParsingFragment) 293 return; 294 if (m_document->isSrcdocDocument()) 295 return; 296 setCompatibilityMode(DocumentCompatibilityMode::QuirksMode); 297} 298 299void HTMLConstructionSite::setCompatibilityMode(DocumentCompatibilityMode mode) 300{ 301 m_inQuirksMode = (mode == DocumentCompatibilityMode::QuirksMode); 302 m_document->setCompatibilityMode(mode); 303} 304 305void HTMLConstructionSite::setCompatibilityModeFromDoctype(const String& name, const String& publicId, const String& systemId) 306{ 307 // There are three possible compatibility modes: 308 // Quirks - quirks mode emulates WinIE and NS4. CSS parsing is also relaxed in this mode, e.g., unit types can 309 // be omitted from numbers. 310 // Limited Quirks - This mode is identical to no-quirks mode except for its treatment of line-height in the inline box model. 311 // No Quirks - no quirks apply. Web pages will obey the specifications to the letter. 312 313 // Check for Quirks Mode. 314 if (name != "html" 315 || publicId.startsWith("+//Silmaril//dtd html Pro v0r11 19970101//", false) 316 || publicId.startsWith("-//AdvaSoft Ltd//DTD HTML 3.0 asWedit + extensions//", false) 317 || publicId.startsWith("-//AS//DTD HTML 3.0 asWedit + extensions//", false) 318 || publicId.startsWith("-//IETF//DTD HTML 2.0 Level 1//", false) 319 || publicId.startsWith("-//IETF//DTD HTML 2.0 Level 2//", false) 320 || publicId.startsWith("-//IETF//DTD HTML 2.0 Strict Level 1//", false) 321 || publicId.startsWith("-//IETF//DTD HTML 2.0 Strict Level 2//", false) 322 || publicId.startsWith("-//IETF//DTD HTML 2.0 Strict//", false) 323 || publicId.startsWith("-//IETF//DTD HTML 2.0//", false) 324 || publicId.startsWith("-//IETF//DTD HTML 2.1E//", false) 325 || publicId.startsWith("-//IETF//DTD HTML 3.0//", false) 326 || publicId.startsWith("-//IETF//DTD HTML 3.2 Final//", false) 327 || publicId.startsWith("-//IETF//DTD HTML 3.2//", false) 328 || publicId.startsWith("-//IETF//DTD HTML 3//", false) 329 || publicId.startsWith("-//IETF//DTD HTML Level 0//", false) 330 || publicId.startsWith("-//IETF//DTD HTML Level 1//", false) 331 || publicId.startsWith("-//IETF//DTD HTML Level 2//", false) 332 || publicId.startsWith("-//IETF//DTD HTML Level 3//", false) 333 || publicId.startsWith("-//IETF//DTD HTML Strict Level 0//", false) 334 || publicId.startsWith("-//IETF//DTD HTML Strict Level 1//", false) 335 || publicId.startsWith("-//IETF//DTD HTML Strict Level 2//", false) 336 || publicId.startsWith("-//IETF//DTD HTML Strict Level 3//", false) 337 || publicId.startsWith("-//IETF//DTD HTML Strict//", false) 338 || publicId.startsWith("-//IETF//DTD HTML//", false) 339 || publicId.startsWith("-//Metrius//DTD Metrius Presentational//", false) 340 || publicId.startsWith("-//Microsoft//DTD Internet Explorer 2.0 HTML Strict//", false) 341 || publicId.startsWith("-//Microsoft//DTD Internet Explorer 2.0 HTML//", false) 342 || publicId.startsWith("-//Microsoft//DTD Internet Explorer 2.0 Tables//", false) 343 || publicId.startsWith("-//Microsoft//DTD Internet Explorer 3.0 HTML Strict//", false) 344 || publicId.startsWith("-//Microsoft//DTD Internet Explorer 3.0 HTML//", false) 345 || publicId.startsWith("-//Microsoft//DTD Internet Explorer 3.0 Tables//", false) 346 || publicId.startsWith("-//Netscape Comm. Corp.//DTD HTML//", false) 347 || publicId.startsWith("-//Netscape Comm. Corp.//DTD Strict HTML//", false) 348 || publicId.startsWith("-//O'Reilly and Associates//DTD HTML 2.0//", false) 349 || publicId.startsWith("-//O'Reilly and Associates//DTD HTML Extended 1.0//", false) 350 || publicId.startsWith("-//O'Reilly and Associates//DTD HTML Extended Relaxed 1.0//", false) 351 || publicId.startsWith("-//SoftQuad Software//DTD HoTMetaL PRO 6.0::19990601::extensions to HTML 4.0//", false) 352 || publicId.startsWith("-//SoftQuad//DTD HoTMetaL PRO 4.0::19971010::extensions to HTML 4.0//", false) 353 || publicId.startsWith("-//Spyglass//DTD HTML 2.0 Extended//", false) 354 || publicId.startsWith("-//SQ//DTD HTML 2.0 HoTMetaL + extensions//", false) 355 || publicId.startsWith("-//Sun Microsystems Corp.//DTD HotJava HTML//", false) 356 || publicId.startsWith("-//Sun Microsystems Corp.//DTD HotJava Strict HTML//", false) 357 || publicId.startsWith("-//W3C//DTD HTML 3 1995-03-24//", false) 358 || publicId.startsWith("-//W3C//DTD HTML 3.2 Draft//", false) 359 || publicId.startsWith("-//W3C//DTD HTML 3.2 Final//", false) 360 || publicId.startsWith("-//W3C//DTD HTML 3.2//", false) 361 || publicId.startsWith("-//W3C//DTD HTML 3.2S Draft//", false) 362 || publicId.startsWith("-//W3C//DTD HTML 4.0 Frameset//", false) 363 || publicId.startsWith("-//W3C//DTD HTML 4.0 Transitional//", false) 364 || publicId.startsWith("-//W3C//DTD HTML Experimental 19960712//", false) 365 || publicId.startsWith("-//W3C//DTD HTML Experimental 970421//", false) 366 || publicId.startsWith("-//W3C//DTD W3 HTML//", false) 367 || publicId.startsWith("-//W3O//DTD W3 HTML 3.0//", false) 368 || equalIgnoringCase(publicId, "-//W3O//DTD W3 HTML Strict 3.0//EN//") 369 || publicId.startsWith("-//WebTechs//DTD Mozilla HTML 2.0//", false) 370 || publicId.startsWith("-//WebTechs//DTD Mozilla HTML//", false) 371 || equalIgnoringCase(publicId, "-/W3C/DTD HTML 4.0 Transitional/EN") 372 || equalIgnoringCase(publicId, "HTML") 373 || equalIgnoringCase(systemId, "http://www.ibm.com/data/dtd/v11/ibmxhtml1-transitional.dtd") 374 || (systemId.isEmpty() && publicId.startsWith("-//W3C//DTD HTML 4.01 Frameset//", false)) 375 || (systemId.isEmpty() && publicId.startsWith("-//W3C//DTD HTML 4.01 Transitional//", false))) { 376 setCompatibilityMode(DocumentCompatibilityMode::QuirksMode); 377 return; 378 } 379 380 // Check for Limited Quirks Mode. 381 if (publicId.startsWith("-//W3C//DTD XHTML 1.0 Frameset//", false) 382 || publicId.startsWith("-//W3C//DTD XHTML 1.0 Transitional//", false) 383 || (!systemId.isEmpty() && publicId.startsWith("-//W3C//DTD HTML 4.01 Frameset//", false)) 384 || (!systemId.isEmpty() && publicId.startsWith("-//W3C//DTD HTML 4.01 Transitional//", false))) { 385 setCompatibilityMode(DocumentCompatibilityMode::LimitedQuirksMode); 386 return; 387 } 388 389 // Otherwise we are No Quirks Mode. 390 setCompatibilityMode(DocumentCompatibilityMode::NoQuirksMode); 391} 392 393void HTMLConstructionSite::finishedParsing() 394{ 395 m_document->finishedParsing(); 396} 397 398void HTMLConstructionSite::insertDoctype(AtomicHTMLToken* token) 399{ 400 ASSERT(token->type() == HTMLToken::DOCTYPE); 401 402 const String& publicId = StringImpl::create8BitIfPossible(token->publicIdentifier()); 403 const String& systemId = StringImpl::create8BitIfPossible(token->systemIdentifier()); 404 RefPtr<DocumentType> doctype = DocumentType::create(*m_document, token->name(), publicId, systemId); 405 attachLater(m_attachmentRoot, doctype.release()); 406 407 // DOCTYPE nodes are only processed when parsing fragments w/o contextElements, which 408 // never occurs. However, if we ever chose to support such, this code is subtly wrong, 409 // because context-less fragments can determine their own quirks mode, and thus change 410 // parsing rules (like <p> inside <table>). For now we ASSERT that we never hit this code 411 // in a fragment, as changing the owning document's compatibility mode would be wrong. 412 ASSERT(!m_isParsingFragment); 413 if (m_isParsingFragment) 414 return; 415 416 if (token->forceQuirks()) 417 setCompatibilityMode(DocumentCompatibilityMode::QuirksMode); 418 else { 419 setCompatibilityModeFromDoctype(token->name(), publicId, systemId); 420 } 421} 422 423void HTMLConstructionSite::insertComment(AtomicHTMLToken* token) 424{ 425 ASSERT(token->type() == HTMLToken::Comment); 426 attachLater(currentNode(), Comment::create(ownerDocumentForCurrentNode(), token->comment())); 427} 428 429void HTMLConstructionSite::insertCommentOnDocument(AtomicHTMLToken* token) 430{ 431 ASSERT(token->type() == HTMLToken::Comment); 432 attachLater(m_attachmentRoot, Comment::create(*m_document, token->comment())); 433} 434 435void HTMLConstructionSite::insertCommentOnHTMLHtmlElement(AtomicHTMLToken* token) 436{ 437 ASSERT(token->type() == HTMLToken::Comment); 438 ContainerNode* parent = m_openElements.rootNode(); 439 attachLater(parent, Comment::create(parent->document(), token->comment())); 440} 441 442void HTMLConstructionSite::insertHTMLHeadElement(AtomicHTMLToken* token) 443{ 444 ASSERT(!shouldFosterParent()); 445 m_head = HTMLStackItem::create(createHTMLElement(token), token); 446 attachLater(currentNode(), m_head->element()); 447 m_openElements.pushHTMLHeadElement(m_head); 448} 449 450void HTMLConstructionSite::insertHTMLBodyElement(AtomicHTMLToken* token) 451{ 452 ASSERT(!shouldFosterParent()); 453 RefPtr<Element> body = createHTMLElement(token); 454 attachLater(currentNode(), body); 455 m_openElements.pushHTMLBodyElement(HTMLStackItem::create(body.release(), token)); 456} 457 458void HTMLConstructionSite::insertHTMLFormElement(AtomicHTMLToken* token, bool isDemoted) 459{ 460 RefPtr<Element> element = createHTMLElement(token); 461 ASSERT(isHTMLFormElement(element.get())); 462 RefPtr<HTMLFormElement> form = static_pointer_cast<HTMLFormElement>(element.release()); 463 if (!insideTemplateElement()) 464 m_form = form; 465 form->setDemoted(isDemoted); 466 attachLater(currentNode(), form); 467 m_openElements.push(HTMLStackItem::create(form.release(), token)); 468} 469 470void HTMLConstructionSite::insertHTMLElement(AtomicHTMLToken* token) 471{ 472 RefPtr<Element> element = createHTMLElement(token); 473 attachLater(currentNode(), element); 474 m_openElements.push(HTMLStackItem::create(element.release(), token)); 475} 476 477void HTMLConstructionSite::insertSelfClosingHTMLElement(AtomicHTMLToken* token) 478{ 479 ASSERT(token->type() == HTMLToken::StartTag); 480 // Normally HTMLElementStack is responsible for calling finishParsingChildren, 481 // but self-closing elements are never in the element stack so the stack 482 // doesn't get a chance to tell them that we're done parsing their children. 483 attachLater(currentNode(), createHTMLElement(token), true); 484 // FIXME: Do we want to acknowledge the token's self-closing flag? 485 // http://www.whatwg.org/specs/web-apps/current-work/multipage/tokenization.html#acknowledge-self-closing-flag 486} 487 488void HTMLConstructionSite::insertFormattingElement(AtomicHTMLToken* token) 489{ 490 // http://www.whatwg.org/specs/web-apps/current-work/multipage/parsing.html#the-stack-of-open-elements 491 // Possible active formatting elements include: 492 // a, b, big, code, em, font, i, nobr, s, small, strike, strong, tt, and u. 493 insertHTMLElement(token); 494 m_activeFormattingElements.append(currentElementRecord()->stackItem()); 495} 496 497void HTMLConstructionSite::insertScriptElement(AtomicHTMLToken* token) 498{ 499 // http://www.whatwg.org/specs/web-apps/current-work/multipage/scripting-1.html#already-started 500 // http://html5.org/specs/dom-parsing.html#dom-range-createcontextualfragment 501 // For createContextualFragment, the specifications say to mark it parser-inserted and already-started and later unmark them. 502 // However, we short circuit that logic to avoid the subtree traversal to find script elements since scripts can never see 503 // those flags or effects thereof. 504 const bool parserInserted = m_parserContentPolicy != AllowScriptingContentAndDoNotMarkAlreadyStarted; 505 const bool alreadyStarted = m_isParsingFragment && parserInserted; 506 RefPtr<HTMLScriptElement> element = HTMLScriptElement::create(scriptTag, ownerDocumentForCurrentNode(), parserInserted, alreadyStarted); 507 setAttributes(element.get(), token, m_parserContentPolicy); 508 if (scriptingContentIsAllowed(m_parserContentPolicy)) 509 attachLater(currentNode(), element); 510 m_openElements.push(HTMLStackItem::create(element.release(), token)); 511} 512 513void HTMLConstructionSite::insertForeignElement(AtomicHTMLToken* token, const AtomicString& namespaceURI) 514{ 515 ASSERT(token->type() == HTMLToken::StartTag); 516 notImplemented(); // parseError when xmlns or xmlns:xlink are wrong. 517 518 RefPtr<Element> element = createElement(token, namespaceURI); 519 if (scriptingContentIsAllowed(m_parserContentPolicy) || !toScriptElementIfPossible(element.get())) 520 attachLater(currentNode(), element, token->selfClosing()); 521 if (!token->selfClosing()) 522 m_openElements.push(HTMLStackItem::create(element.release(), token, namespaceURI)); 523} 524 525void HTMLConstructionSite::insertTextNode(const String& characters, WhitespaceMode whitespaceMode) 526{ 527 HTMLConstructionSiteTask task(HTMLConstructionSiteTask::Insert); 528 task.parent = currentNode(); 529 530 if (shouldFosterParent()) 531 findFosterSite(task); 532 533#if ENABLE(TEMPLATE_ELEMENT) 534 if (task.parent->hasTagName(templateTag)) 535 task.parent = toHTMLTemplateElement(task.parent.get())->content(); 536#endif 537 538 // Strings composed entirely of whitespace are likely to be repeated. 539 // Turn them into AtomicString so we share a single string for each. 540 bool shouldUseAtomicString = whitespaceMode == AllWhitespace 541 || (whitespaceMode == WhitespaceUnknown && isAllWhitespace(characters)); 542 543 unsigned currentPosition = 0; 544 unsigned lengthLimit = shouldUseLengthLimit(task.parent.get()) ? Text::defaultLengthLimit : std::numeric_limits<unsigned>::max(); 545 546 // FIXME: Splitting text nodes into smaller chunks contradicts HTML5 spec, but is currently necessary 547 // for performance, see <https://bugs.webkit.org/show_bug.cgi?id=55898>. 548 549 Node* previousChild = task.nextChild ? task.nextChild->previousSibling() : task.parent->lastChild(); 550 if (previousChild && previousChild->isTextNode()) { 551 // FIXME: We're only supposed to append to this text node if it 552 // was the last text node inserted by the parser. 553 Text* textNode = toText(previousChild); 554 currentPosition = textNode->parserAppendData(characters, 0, lengthLimit); 555 } 556 557 while (currentPosition < characters.length()) { 558 RefPtr<Text> textNode = Text::createWithLengthLimit(task.parent->document(), shouldUseAtomicString ? AtomicString(characters).string() : characters, currentPosition, lengthLimit); 559 // If we have a whole string of unbreakable characters the above could lead to an infinite loop. Exceeding the length limit is the lesser evil. 560 if (!textNode->length()) { 561 String substring = characters.substring(currentPosition); 562 textNode = Text::create(task.parent->document(), shouldUseAtomicString ? AtomicString(substring).string() : substring); 563 } 564 565 currentPosition += textNode->length(); 566 ASSERT(currentPosition <= characters.length()); 567 task.child = textNode.release(); 568 569 executeTask(task); 570 } 571} 572 573void HTMLConstructionSite::reparent(HTMLElementStack::ElementRecord& newParent, HTMLElementStack::ElementRecord& child) 574{ 575 HTMLConstructionSiteTask task(HTMLConstructionSiteTask::Reparent); 576 task.parent = newParent.node(); 577 task.child = child.element(); 578 m_taskQueue.append(task); 579} 580 581void HTMLConstructionSite::reparent(HTMLElementStack::ElementRecord& newParent, HTMLStackItem& child) 582{ 583 HTMLConstructionSiteTask task(HTMLConstructionSiteTask::Reparent); 584 task.parent = newParent.node(); 585 task.child = child.element(); 586 m_taskQueue.append(task); 587} 588 589void HTMLConstructionSite::insertAlreadyParsedChild(HTMLStackItem& newParent, HTMLElementStack::ElementRecord& child) 590{ 591 if (newParent.causesFosterParenting()) { 592 fosterParent(child.element()); 593 return; 594 } 595 596 HTMLConstructionSiteTask task(HTMLConstructionSiteTask::InsertAlreadyParsedChild); 597 task.parent = newParent.node(); 598 task.child = child.element(); 599 m_taskQueue.append(task); 600} 601 602void HTMLConstructionSite::takeAllChildren(HTMLStackItem& newParent, HTMLElementStack::ElementRecord& oldParent) 603{ 604 HTMLConstructionSiteTask task(HTMLConstructionSiteTask::TakeAllChildren); 605 task.parent = newParent.node(); 606 task.child = oldParent.node(); 607 m_taskQueue.append(task); 608} 609 610PassRefPtr<Element> HTMLConstructionSite::createElement(AtomicHTMLToken* token, const AtomicString& namespaceURI) 611{ 612 QualifiedName tagName(nullAtom, token->name(), namespaceURI); 613 RefPtr<Element> element = ownerDocumentForCurrentNode().createElement(tagName, true); 614 setAttributes(element.get(), token, m_parserContentPolicy); 615 return element.release(); 616} 617 618inline Document& HTMLConstructionSite::ownerDocumentForCurrentNode() 619{ 620#if ENABLE(TEMPLATE_ELEMENT) 621 if (currentNode()->hasTagName(templateTag)) 622 return toHTMLTemplateElement(currentElement())->content()->document(); 623#endif 624 return currentNode()->document(); 625} 626 627inline bool HTMLConstructionSite::insideTemplateElement() 628{ 629 return !ownerDocumentForCurrentNode().frame(); 630} 631 632PassRefPtr<Element> HTMLConstructionSite::createHTMLElement(AtomicHTMLToken* token) 633{ 634 QualifiedName tagName(nullAtom, token->name(), xhtmlNamespaceURI); 635 // FIXME: This can't use HTMLConstructionSite::createElement because we 636 // have to pass the current form element. We should rework form association 637 // to occur after construction to allow better code sharing here. 638 // http://www.whatwg.org/specs/web-apps/current-work/multipage/tree-construction.html#create-an-element-for-the-token 639 Document& ownerDocument = ownerDocumentForCurrentNode(); 640 bool insideTemplateElement = !ownerDocument.frame(); 641 RefPtr<Element> element = HTMLElementFactory::createElement(tagName, ownerDocument, insideTemplateElement ? nullptr : form(), true); 642 setAttributes(element.get(), token, m_parserContentPolicy); 643 ASSERT(element->isHTMLElement()); 644 return element.release(); 645} 646 647PassRefPtr<HTMLStackItem> HTMLConstructionSite::createElementFromSavedToken(HTMLStackItem* item) 648{ 649 RefPtr<Element> element; 650 // NOTE: Moving from item -> token -> item copies the Attribute vector twice! 651 AtomicHTMLToken fakeToken(HTMLToken::StartTag, item->localName(), item->attributes()); 652 if (item->namespaceURI() == HTMLNames::xhtmlNamespaceURI) 653 element = createHTMLElement(&fakeToken); 654 else 655 element = createElement(&fakeToken, item->namespaceURI()); 656 return HTMLStackItem::create(element.release(), &fakeToken, item->namespaceURI()); 657} 658 659bool HTMLConstructionSite::indexOfFirstUnopenFormattingElement(unsigned& firstUnopenElementIndex) const 660{ 661 if (m_activeFormattingElements.isEmpty()) 662 return false; 663 unsigned index = m_activeFormattingElements.size(); 664 do { 665 --index; 666 const HTMLFormattingElementList::Entry& entry = m_activeFormattingElements.at(index); 667 if (entry.isMarker() || m_openElements.contains(entry.element())) { 668 firstUnopenElementIndex = index + 1; 669 return firstUnopenElementIndex < m_activeFormattingElements.size(); 670 } 671 } while (index); 672 firstUnopenElementIndex = index; 673 return true; 674} 675 676void HTMLConstructionSite::reconstructTheActiveFormattingElements() 677{ 678 unsigned firstUnopenElementIndex; 679 if (!indexOfFirstUnopenFormattingElement(firstUnopenElementIndex)) 680 return; 681 682 unsigned unopenEntryIndex = firstUnopenElementIndex; 683 ASSERT(unopenEntryIndex < m_activeFormattingElements.size()); 684 for (; unopenEntryIndex < m_activeFormattingElements.size(); ++unopenEntryIndex) { 685 HTMLFormattingElementList::Entry& unopenedEntry = m_activeFormattingElements.at(unopenEntryIndex); 686 RefPtr<HTMLStackItem> reconstructed = createElementFromSavedToken(unopenedEntry.stackItem().get()); 687 attachLater(currentNode(), reconstructed->node()); 688 m_openElements.push(reconstructed); 689 unopenedEntry.replaceElement(reconstructed.release()); 690 } 691} 692 693void HTMLConstructionSite::generateImpliedEndTagsWithExclusion(const AtomicString& tagName) 694{ 695 while (hasImpliedEndTag(currentStackItem()) && !currentStackItem()->matchesHTMLTag(tagName)) 696 m_openElements.pop(); 697} 698 699void HTMLConstructionSite::generateImpliedEndTags() 700{ 701 while (hasImpliedEndTag(currentStackItem())) 702 m_openElements.pop(); 703} 704 705bool HTMLConstructionSite::inQuirksMode() 706{ 707 return m_inQuirksMode; 708} 709 710void HTMLConstructionSite::findFosterSite(HTMLConstructionSiteTask& task) 711{ 712#if ENABLE(TEMPLATE_ELEMENT) 713 // When a node is to be foster parented, the last template element with no table element is below it in the stack of open elements is the foster parent element (NOT the template's parent!) 714 HTMLElementStack::ElementRecord* lastTemplateElement = m_openElements.topmost(templateTag.localName()); 715 if (lastTemplateElement && !m_openElements.inTableScope(tableTag)) { 716 task.parent = lastTemplateElement->element(); 717 return; 718 } 719 720#endif 721 722 HTMLElementStack::ElementRecord* lastTableElementRecord = m_openElements.topmost(tableTag.localName()); 723 if (lastTableElementRecord) { 724 Element* lastTableElement = lastTableElementRecord->element(); 725 ContainerNode* parent = lastTableElement->parentNode(); 726 // When parsing HTML fragments, we skip step 4.2 ("Let root be a new html element with no attributes") for efficiency, 727 // and instead use the DocumentFragment as a root node. So we must treat the root node (DocumentFragment) as if it is a html element here. 728 bool parentCanBeFosterParent = parent && (parent->isElementNode() || (m_isParsingFragment && parent == m_openElements.rootNode())); 729#if ENABLE(TEMPLATE_ELEMENT) 730 parentCanBeFosterParent = parentCanBeFosterParent || (parent && parent->isDocumentFragment() && toDocumentFragment(parent)->isTemplateContent()); 731#endif 732 if (parentCanBeFosterParent) { 733 task.parent = parent; 734 task.nextChild = lastTableElement; 735 return; 736 } 737 task.parent = lastTableElementRecord->next()->element(); 738 return; 739 } 740 // Fragment case 741 task.parent = m_openElements.rootNode(); // DocumentFragment 742} 743 744bool HTMLConstructionSite::shouldFosterParent() const 745{ 746 return m_redirectAttachToFosterParent 747 && currentStackItem()->isElementNode() 748 && currentStackItem()->causesFosterParenting(); 749} 750 751void HTMLConstructionSite::fosterParent(PassRefPtr<Node> node) 752{ 753 HTMLConstructionSiteTask task(HTMLConstructionSiteTask::Insert); 754 findFosterSite(task); 755 task.child = node; 756 ASSERT(task.parent); 757 758 m_taskQueue.append(task); 759} 760 761} 762