2 * Copyright (C) 2010 Google, Inc. All Rights Reserved.
3 * Copyright (C) 2011 Apple Inc. All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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.
28 #include "HTMLTreeBuilder.h"
31 #include "DocumentFragment.h"
32 #include "DocumentType.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"
50 using namespace HTMLNames;
52 static inline void setAttributes(Element* element, AtomicHTMLToken* token, ParserContentPolicy parserContentPolicy)
54 if (!scriptingContentIsAllowed(parserContentPolicy))
55 element->stripScriptingAttributes(token->attributes());
56 element->parserSetAttributes(token->attributes());
59 static bool hasImpliedEndTag(const HTMLStackItem& item)
61 return item.hasTagName(ddTag)
62 || item.hasTagName(dtTag)
63 || item.hasTagName(liTag)
64 || is<HTMLOptionElement>(item.node())
65 || is<HTMLOptGroupElement>(item.node())
66 || item.hasTagName(pTag)
67 || item.hasTagName(rbTag)
68 || item.hasTagName(rpTag)
69 || item.hasTagName(rtTag)
70 || item.hasTagName(rtcTag);
73 static bool shouldUseLengthLimit(const ContainerNode* node)
75 return !node->hasTagName(scriptTag)
76 && !node->hasTagName(styleTag)
77 && !node->hasTagName(SVGNames::scriptTag);
80 static inline bool causesFosterParenting(const HTMLStackItem& item)
82 return item.hasTagName(HTMLNames::tableTag)
83 || item.hasTagName(HTMLNames::tbodyTag)
84 || item.hasTagName(HTMLNames::tfootTag)
85 || item.hasTagName(HTMLNames::theadTag)
86 || item.hasTagName(HTMLNames::trTag);
89 static inline bool isAllWhitespace(const String& string)
91 return string.isAllSpecialCharacters<isHTMLSpace>();
94 static inline void insert(HTMLConstructionSiteTask& task)
96 #if ENABLE(TEMPLATE_ELEMENT)
97 if (is<HTMLTemplateElement>(*task.parent))
98 task.parent = downcast<HTMLTemplateElement>(*task.parent).content();
101 if (ContainerNode* parent = task.child->parentNode())
102 parent->parserRemoveChild(*task.child);
105 task.parent->parserInsertBefore(*task.child, *task.nextChild);
107 task.parent->parserAppendChild(*task.child);
110 static inline void executeInsertTask(HTMLConstructionSiteTask& task)
112 ASSERT(task.operation == HTMLConstructionSiteTask::Insert);
116 task.child->beginParsingChildren();
118 if (task.selfClosing)
119 task.child->finishParsingChildren();
122 static inline void executeReparentTask(HTMLConstructionSiteTask& task)
124 ASSERT(task.operation == HTMLConstructionSiteTask::Reparent);
126 if (ContainerNode* parent = task.child->parentNode())
127 parent->parserRemoveChild(*task.child);
129 task.parent->parserAppendChild(*task.child);
132 static inline void executeInsertAlreadyParsedChildTask(HTMLConstructionSiteTask& task)
134 ASSERT(task.operation == HTMLConstructionSiteTask::InsertAlreadyParsedChild);
139 static inline void executeTakeAllChildrenTask(HTMLConstructionSiteTask& task)
141 ASSERT(task.operation == HTMLConstructionSiteTask::TakeAllChildren);
143 task.parent->takeAllChildrenFrom(task.oldParent());
144 // Notice that we don't need to manually attach the moved children
145 // because takeAllChildrenFrom does that work for us.
148 static inline void executeTask(HTMLConstructionSiteTask& task)
150 switch (task.operation) {
151 case HTMLConstructionSiteTask::Insert:
152 executeInsertTask(task);
154 // All the cases below this point are only used by the adoption agency.
155 case HTMLConstructionSiteTask::InsertAlreadyParsedChild:
156 executeInsertAlreadyParsedChildTask(task);
158 case HTMLConstructionSiteTask::Reparent:
159 executeReparentTask(task);
161 case HTMLConstructionSiteTask::TakeAllChildren:
162 executeTakeAllChildrenTask(task);
165 ASSERT_NOT_REACHED();
168 void HTMLConstructionSite::attachLater(ContainerNode* parent, PassRefPtr<Node> prpChild, bool selfClosing)
170 ASSERT(scriptingContentIsAllowed(m_parserContentPolicy) || !is<Element>(*prpChild) || !toScriptElementIfPossible(downcast<Element>(prpChild.get())));
171 ASSERT(pluginContentIsAllowed(m_parserContentPolicy) || !prpChild->isPluginElement());
173 HTMLConstructionSiteTask task(HTMLConstructionSiteTask::Insert);
174 task.parent = parent;
175 task.child = prpChild;
176 task.selfClosing = selfClosing;
178 if (shouldFosterParent()) {
179 fosterParent(task.child);
183 // Add as a sibling of the parent if we have reached the maximum depth allowed.
184 if (m_openElements.stackDepth() > m_maximumDOMTreeDepth && task.parent->parentNode())
185 task.parent = task.parent->parentNode();
188 m_taskQueue.append(task);
191 void HTMLConstructionSite::executeQueuedTasks()
193 const size_t size = m_taskQueue.size();
197 // Copy the task queue into a local variable in case executeTask
198 // re-enters the parser.
199 TaskQueue queue = WTFMove(m_taskQueue);
201 for (size_t i = 0; i < size; ++i)
202 executeTask(queue[i]);
204 // We might be detached now.
207 HTMLConstructionSite::HTMLConstructionSite(Document& document, ParserContentPolicy parserContentPolicy, unsigned maximumDOMTreeDepth)
208 : m_document(&document)
209 , m_attachmentRoot(&document)
210 , m_parserContentPolicy(parserContentPolicy)
211 , m_isParsingFragment(false)
212 , m_redirectAttachToFosterParent(false)
213 , m_maximumDOMTreeDepth(maximumDOMTreeDepth)
214 , m_inQuirksMode(document.inQuirksMode())
216 ASSERT(m_document->isHTMLDocument() || m_document->isXHTMLDocument());
219 HTMLConstructionSite::HTMLConstructionSite(DocumentFragment& fragment, ParserContentPolicy parserContentPolicy, unsigned maximumDOMTreeDepth)
220 : m_document(&fragment.document())
221 , m_attachmentRoot(&fragment)
222 , m_parserContentPolicy(parserContentPolicy)
223 , m_isParsingFragment(true)
224 , m_redirectAttachToFosterParent(false)
225 , m_maximumDOMTreeDepth(maximumDOMTreeDepth)
226 , m_inQuirksMode(fragment.document().inQuirksMode())
228 ASSERT(m_document->isHTMLDocument() || m_document->isXHTMLDocument());
231 HTMLConstructionSite::~HTMLConstructionSite()
235 void HTMLConstructionSite::detach()
238 m_attachmentRoot = 0;
241 void HTMLConstructionSite::setForm(HTMLFormElement* form)
243 // This method should only be needed for HTMLTreeBuilder in the fragment case.
248 PassRefPtr<HTMLFormElement> HTMLConstructionSite::takeForm()
250 return m_form.release();
253 void HTMLConstructionSite::dispatchDocumentElementAvailableIfNeeded()
256 if (m_document->frame() && !m_isParsingFragment)
257 m_document->frame()->injectUserScripts(InjectAtDocumentStart);
260 void HTMLConstructionSite::insertHTMLHtmlStartTagBeforeHTML(AtomicHTMLToken* token)
262 Ref<HTMLHtmlElement> element = HTMLHtmlElement::create(*m_document);
263 setAttributes(element.ptr(), token, m_parserContentPolicy);
264 attachLater(m_attachmentRoot, element.ptr());
265 m_openElements.pushHTMLHtmlElement(HTMLStackItem::create(element.copyRef(), *token));
267 executeQueuedTasks();
268 element->insertedByParser();
269 dispatchDocumentElementAvailableIfNeeded();
272 void HTMLConstructionSite::mergeAttributesFromTokenIntoElement(AtomicHTMLToken* token, Element* element)
274 if (token->attributes().isEmpty())
277 for (auto& tokenAttribute : token->attributes()) {
278 if (!element->elementData() || !element->findAttributeByName(tokenAttribute.name()))
279 element->setAttribute(tokenAttribute.name(), tokenAttribute.value());
283 void HTMLConstructionSite::insertHTMLHtmlStartTagInBody(AtomicHTMLToken* token)
285 // Fragments do not have a root HTML element, so any additional HTML elements
286 // encountered during fragment parsing should be ignored.
287 if (m_isParsingFragment)
290 mergeAttributesFromTokenIntoElement(token, &m_openElements.htmlElement());
293 void HTMLConstructionSite::insertHTMLBodyStartTagInBody(AtomicHTMLToken* token)
295 mergeAttributesFromTokenIntoElement(token, &m_openElements.bodyElement());
298 void HTMLConstructionSite::setDefaultCompatibilityMode()
300 if (m_isParsingFragment)
302 if (m_document->isSrcdocDocument())
304 setCompatibilityMode(DocumentCompatibilityMode::QuirksMode);
307 void HTMLConstructionSite::setCompatibilityMode(DocumentCompatibilityMode mode)
309 m_inQuirksMode = (mode == DocumentCompatibilityMode::QuirksMode);
310 m_document->setCompatibilityMode(mode);
313 void HTMLConstructionSite::setCompatibilityModeFromDoctype(const String& name, const String& publicId, const String& systemId)
315 // There are three possible compatibility modes:
316 // Quirks - quirks mode emulates WinIE and NS4. CSS parsing is also relaxed in this mode, e.g., unit types can
317 // be omitted from numbers.
318 // Limited Quirks - This mode is identical to no-quirks mode except for its treatment of line-height in the inline box model.
319 // No Quirks - no quirks apply. Web pages will obey the specifications to the letter.
321 // Check for Quirks Mode.
323 || publicId.startsWith("+//Silmaril//dtd html Pro v0r11 19970101//", false)
324 || publicId.startsWith("-//AdvaSoft Ltd//DTD HTML 3.0 asWedit + extensions//", false)
325 || publicId.startsWith("-//AS//DTD HTML 3.0 asWedit + extensions//", false)
326 || publicId.startsWith("-//IETF//DTD HTML 2.0 Level 1//", false)
327 || publicId.startsWith("-//IETF//DTD HTML 2.0 Level 2//", false)
328 || publicId.startsWith("-//IETF//DTD HTML 2.0 Strict Level 1//", false)
329 || publicId.startsWith("-//IETF//DTD HTML 2.0 Strict Level 2//", false)
330 || publicId.startsWith("-//IETF//DTD HTML 2.0 Strict//", false)
331 || publicId.startsWith("-//IETF//DTD HTML 2.0//", false)
332 || publicId.startsWith("-//IETF//DTD HTML 2.1E//", false)
333 || publicId.startsWith("-//IETF//DTD HTML 3.0//", false)
334 || publicId.startsWith("-//IETF//DTD HTML 3.2 Final//", false)
335 || publicId.startsWith("-//IETF//DTD HTML 3.2//", false)
336 || publicId.startsWith("-//IETF//DTD HTML 3//", false)
337 || publicId.startsWith("-//IETF//DTD HTML Level 0//", false)
338 || publicId.startsWith("-//IETF//DTD HTML Level 1//", false)
339 || publicId.startsWith("-//IETF//DTD HTML Level 2//", false)
340 || publicId.startsWith("-//IETF//DTD HTML Level 3//", false)
341 || publicId.startsWith("-//IETF//DTD HTML Strict Level 0//", false)
342 || publicId.startsWith("-//IETF//DTD HTML Strict Level 1//", false)
343 || publicId.startsWith("-//IETF//DTD HTML Strict Level 2//", false)
344 || publicId.startsWith("-//IETF//DTD HTML Strict Level 3//", false)
345 || publicId.startsWith("-//IETF//DTD HTML Strict//", false)
346 || publicId.startsWith("-//IETF//DTD HTML//", false)
347 || publicId.startsWith("-//Metrius//DTD Metrius Presentational//", false)
348 || publicId.startsWith("-//Microsoft//DTD Internet Explorer 2.0 HTML Strict//", false)
349 || publicId.startsWith("-//Microsoft//DTD Internet Explorer 2.0 HTML//", false)
350 || publicId.startsWith("-//Microsoft//DTD Internet Explorer 2.0 Tables//", false)
351 || publicId.startsWith("-//Microsoft//DTD Internet Explorer 3.0 HTML Strict//", false)
352 || publicId.startsWith("-//Microsoft//DTD Internet Explorer 3.0 HTML//", false)
353 || publicId.startsWith("-//Microsoft//DTD Internet Explorer 3.0 Tables//", false)
354 || publicId.startsWith("-//Netscape Comm. Corp.//DTD HTML//", false)
355 || publicId.startsWith("-//Netscape Comm. Corp.//DTD Strict HTML//", false)
356 || publicId.startsWith("-//O'Reilly and Associates//DTD HTML 2.0//", false)
357 || publicId.startsWith("-//O'Reilly and Associates//DTD HTML Extended 1.0//", false)
358 || publicId.startsWith("-//O'Reilly and Associates//DTD HTML Extended Relaxed 1.0//", false)
359 || publicId.startsWith("-//SoftQuad Software//DTD HoTMetaL PRO 6.0::19990601::extensions to HTML 4.0//", false)
360 || publicId.startsWith("-//SoftQuad//DTD HoTMetaL PRO 4.0::19971010::extensions to HTML 4.0//", false)
361 || publicId.startsWith("-//Spyglass//DTD HTML 2.0 Extended//", false)
362 || publicId.startsWith("-//SQ//DTD HTML 2.0 HoTMetaL + extensions//", false)
363 || publicId.startsWith("-//Sun Microsystems Corp.//DTD HotJava HTML//", false)
364 || publicId.startsWith("-//Sun Microsystems Corp.//DTD HotJava Strict HTML//", false)
365 || publicId.startsWith("-//W3C//DTD HTML 3 1995-03-24//", false)
366 || publicId.startsWith("-//W3C//DTD HTML 3.2 Draft//", false)
367 || publicId.startsWith("-//W3C//DTD HTML 3.2 Final//", false)
368 || publicId.startsWith("-//W3C//DTD HTML 3.2//", false)
369 || publicId.startsWith("-//W3C//DTD HTML 3.2S Draft//", false)
370 || publicId.startsWith("-//W3C//DTD HTML 4.0 Frameset//", false)
371 || publicId.startsWith("-//W3C//DTD HTML 4.0 Transitional//", false)
372 || publicId.startsWith("-//W3C//DTD HTML Experimental 19960712//", false)
373 || publicId.startsWith("-//W3C//DTD HTML Experimental 970421//", false)
374 || publicId.startsWith("-//W3C//DTD W3 HTML//", false)
375 || publicId.startsWith("-//W3O//DTD W3 HTML 3.0//", false)
376 || equalIgnoringCase(publicId, "-//W3O//DTD W3 HTML Strict 3.0//EN//")
377 || publicId.startsWith("-//WebTechs//DTD Mozilla HTML 2.0//", false)
378 || publicId.startsWith("-//WebTechs//DTD Mozilla HTML//", false)
379 || equalIgnoringCase(publicId, "-/W3C/DTD HTML 4.0 Transitional/EN")
380 || equalIgnoringCase(publicId, "HTML")
381 || equalIgnoringCase(systemId, "http://www.ibm.com/data/dtd/v11/ibmxhtml1-transitional.dtd")
382 || (systemId.isEmpty() && publicId.startsWith("-//W3C//DTD HTML 4.01 Frameset//", false))
383 || (systemId.isEmpty() && publicId.startsWith("-//W3C//DTD HTML 4.01 Transitional//", false))) {
384 setCompatibilityMode(DocumentCompatibilityMode::QuirksMode);
388 // Check for Limited Quirks Mode.
389 if (publicId.startsWith("-//W3C//DTD XHTML 1.0 Frameset//", false)
390 || publicId.startsWith("-//W3C//DTD XHTML 1.0 Transitional//", false)
391 || (!systemId.isEmpty() && publicId.startsWith("-//W3C//DTD HTML 4.01 Frameset//", false))
392 || (!systemId.isEmpty() && publicId.startsWith("-//W3C//DTD HTML 4.01 Transitional//", false))) {
393 setCompatibilityMode(DocumentCompatibilityMode::LimitedQuirksMode);
397 // Otherwise we are No Quirks Mode.
398 setCompatibilityMode(DocumentCompatibilityMode::NoQuirksMode);
401 void HTMLConstructionSite::finishedParsing()
403 m_document->finishedParsing();
406 void HTMLConstructionSite::insertDoctype(AtomicHTMLToken* token)
408 ASSERT(token->type() == HTMLToken::DOCTYPE);
410 String publicId = token->publicIdentifier();
411 String systemId = token->systemIdentifier();
413 RefPtr<DocumentType> doctype = DocumentType::create(*m_document, token->name(), publicId, systemId);
414 attachLater(m_attachmentRoot, doctype.release());
416 // DOCTYPE nodes are only processed when parsing fragments w/o contextElements, which
417 // never occurs. However, if we ever chose to support such, this code is subtly wrong,
418 // because context-less fragments can determine their own quirks mode, and thus change
419 // parsing rules (like <p> inside <table>). For now we ASSERT that we never hit this code
420 // in a fragment, as changing the owning document's compatibility mode would be wrong.
421 ASSERT(!m_isParsingFragment);
422 if (m_isParsingFragment)
425 if (token->forceQuirks())
426 setCompatibilityMode(DocumentCompatibilityMode::QuirksMode);
428 setCompatibilityModeFromDoctype(token->name(), publicId, systemId);
431 void HTMLConstructionSite::insertComment(AtomicHTMLToken* token)
433 ASSERT(token->type() == HTMLToken::Comment);
434 attachLater(¤tNode(), Comment::create(ownerDocumentForCurrentNode(), token->comment()));
437 void HTMLConstructionSite::insertCommentOnDocument(AtomicHTMLToken* token)
439 ASSERT(token->type() == HTMLToken::Comment);
440 attachLater(m_attachmentRoot, Comment::create(*m_document, token->comment()));
443 void HTMLConstructionSite::insertCommentOnHTMLHtmlElement(AtomicHTMLToken* token)
445 ASSERT(token->type() == HTMLToken::Comment);
446 ContainerNode& parent = m_openElements.rootNode();
447 attachLater(&parent, Comment::create(parent.document(), token->comment()));
450 void HTMLConstructionSite::insertHTMLHeadElement(AtomicHTMLToken* token)
452 ASSERT(!shouldFosterParent());
453 m_head = HTMLStackItem::create(*createHTMLElement(token), *token);
454 attachLater(¤tNode(), &m_head->element());
455 m_openElements.pushHTMLHeadElement(m_head);
458 void HTMLConstructionSite::insertHTMLBodyElement(AtomicHTMLToken* token)
460 ASSERT(!shouldFosterParent());
461 RefPtr<Element> body = createHTMLElement(token);
462 attachLater(¤tNode(), body.get());
463 m_openElements.pushHTMLBodyElement(HTMLStackItem::create(body.releaseNonNull(), *token));
466 void HTMLConstructionSite::insertHTMLFormElement(AtomicHTMLToken* token, bool isDemoted)
468 RefPtr<Element> element = createHTMLElement(token);
469 ASSERT(is<HTMLFormElement>(*element));
470 m_form = static_pointer_cast<HTMLFormElement>(element.release());
471 m_form->setDemoted(isDemoted);
472 attachLater(¤tNode(), m_form);
473 m_openElements.push(HTMLStackItem::create(*m_form, *token));
476 void HTMLConstructionSite::insertHTMLElement(AtomicHTMLToken* token)
478 RefPtr<Element> element = createHTMLElement(token);
479 attachLater(¤tNode(), element);
480 m_openElements.push(HTMLStackItem::create(element.releaseNonNull(), *token));
483 void HTMLConstructionSite::insertSelfClosingHTMLElement(AtomicHTMLToken* token)
485 ASSERT(token->type() == HTMLToken::StartTag);
486 // Normally HTMLElementStack is responsible for calling finishParsingChildren,
487 // but self-closing elements are never in the element stack so the stack
488 // doesn't get a chance to tell them that we're done parsing their children.
489 attachLater(¤tNode(), createHTMLElement(token), true);
490 // FIXME: Do we want to acknowledge the token's self-closing flag?
491 // http://www.whatwg.org/specs/web-apps/current-work/multipage/tokenization.html#acknowledge-self-closing-flag
494 void HTMLConstructionSite::insertFormattingElement(AtomicHTMLToken* token)
496 // http://www.whatwg.org/specs/web-apps/current-work/multipage/parsing.html#the-stack-of-open-elements
497 // Possible active formatting elements include:
498 // a, b, big, code, em, font, i, nobr, s, small, strike, strong, tt, and u.
499 insertHTMLElement(token);
500 m_activeFormattingElements.append(¤tStackItem());
503 void HTMLConstructionSite::insertScriptElement(AtomicHTMLToken* token)
505 // http://www.whatwg.org/specs/web-apps/current-work/multipage/scripting-1.html#already-started
506 // http://html5.org/specs/dom-parsing.html#dom-range-createcontextualfragment
507 // For createContextualFragment, the specifications say to mark it parser-inserted and already-started and later unmark them.
508 // However, we short circuit that logic to avoid the subtree traversal to find script elements since scripts can never see
509 // those flags or effects thereof.
510 const bool parserInserted = m_parserContentPolicy != AllowScriptingContentAndDoNotMarkAlreadyStarted;
511 const bool alreadyStarted = m_isParsingFragment && parserInserted;
512 RefPtr<HTMLScriptElement> element = HTMLScriptElement::create(scriptTag, ownerDocumentForCurrentNode(), parserInserted, alreadyStarted);
513 setAttributes(element.get(), token, m_parserContentPolicy);
514 if (scriptingContentIsAllowed(m_parserContentPolicy))
515 attachLater(¤tNode(), element);
516 m_openElements.push(HTMLStackItem::create(element.releaseNonNull(), *token));
519 void HTMLConstructionSite::insertForeignElement(AtomicHTMLToken* token, const AtomicString& namespaceURI)
521 ASSERT(token->type() == HTMLToken::StartTag);
522 notImplemented(); // parseError when xmlns or xmlns:xlink are wrong.
524 RefPtr<Element> element = createElement(token, namespaceURI);
525 if (scriptingContentIsAllowed(m_parserContentPolicy) || !toScriptElementIfPossible(element.get()))
526 attachLater(¤tNode(), element, token->selfClosing());
527 if (!token->selfClosing())
528 m_openElements.push(HTMLStackItem::create(element.releaseNonNull(), *token, namespaceURI));
531 void HTMLConstructionSite::insertTextNode(const String& characters, WhitespaceMode whitespaceMode)
533 HTMLConstructionSiteTask task(HTMLConstructionSiteTask::Insert);
534 task.parent = ¤tNode();
536 if (shouldFosterParent())
537 findFosterSite(task);
539 #if ENABLE(TEMPLATE_ELEMENT)
540 if (is<HTMLTemplateElement>(*task.parent))
541 task.parent = downcast<HTMLTemplateElement>(*task.parent).content();
544 // Strings composed entirely of whitespace are likely to be repeated.
545 // Turn them into AtomicString so we share a single string for each.
546 bool shouldUseAtomicString = whitespaceMode == AllWhitespace
547 || (whitespaceMode == WhitespaceUnknown && isAllWhitespace(characters));
549 unsigned currentPosition = 0;
550 unsigned lengthLimit = shouldUseLengthLimit(task.parent.get()) ? Text::defaultLengthLimit : std::numeric_limits<unsigned>::max();
552 // FIXME: Splitting text nodes into smaller chunks contradicts HTML5 spec, but is currently necessary
553 // for performance, see <https://bugs.webkit.org/show_bug.cgi?id=55898>.
555 Node* previousChild = task.nextChild ? task.nextChild->previousSibling() : task.parent->lastChild();
556 if (is<Text>(previousChild)) {
557 // FIXME: We're only supposed to append to this text node if it
558 // was the last text node inserted by the parser.
559 Text& textNode = downcast<Text>(*previousChild);
560 currentPosition = textNode.parserAppendData(characters, 0, lengthLimit);
563 while (currentPosition < characters.length()) {
564 Ref<Text> textNode = Text::createWithLengthLimit(task.parent->document(), shouldUseAtomicString ? AtomicString(characters).string() : characters, currentPosition, lengthLimit);
565 // 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.
566 if (!textNode->length()) {
567 String substring = characters.substring(currentPosition);
568 textNode = Text::create(task.parent->document(), shouldUseAtomicString ? AtomicString(substring).string() : substring);
571 currentPosition += textNode->length();
572 ASSERT(currentPosition <= characters.length());
573 task.child = WTFMove(textNode);
579 void HTMLConstructionSite::reparent(HTMLElementStack::ElementRecord& newParent, HTMLElementStack::ElementRecord& child)
581 HTMLConstructionSiteTask task(HTMLConstructionSiteTask::Reparent);
582 task.parent = &newParent.node();
583 task.child = &child.element();
584 m_taskQueue.append(task);
587 void HTMLConstructionSite::reparent(HTMLElementStack::ElementRecord& newParent, HTMLStackItem& child)
589 HTMLConstructionSiteTask task(HTMLConstructionSiteTask::Reparent);
590 task.parent = &newParent.node();
591 task.child = &child.element();
592 m_taskQueue.append(task);
595 void HTMLConstructionSite::insertAlreadyParsedChild(HTMLStackItem& newParent, HTMLElementStack::ElementRecord& child)
597 if (causesFosterParenting(newParent)) {
598 fosterParent(&child.element());
602 HTMLConstructionSiteTask task(HTMLConstructionSiteTask::InsertAlreadyParsedChild);
603 task.parent = &newParent.node();
604 task.child = &child.element();
605 m_taskQueue.append(task);
608 void HTMLConstructionSite::takeAllChildren(HTMLStackItem& newParent, HTMLElementStack::ElementRecord& oldParent)
610 HTMLConstructionSiteTask task(HTMLConstructionSiteTask::TakeAllChildren);
611 task.parent = &newParent.node();
612 task.child = &oldParent.node();
613 m_taskQueue.append(task);
616 PassRefPtr<Element> HTMLConstructionSite::createElement(AtomicHTMLToken* token, const AtomicString& namespaceURI)
618 QualifiedName tagName(nullAtom, token->name(), namespaceURI);
619 RefPtr<Element> element = ownerDocumentForCurrentNode().createElement(tagName, true);
620 setAttributes(element.get(), token, m_parserContentPolicy);
621 return element.release();
624 inline Document& HTMLConstructionSite::ownerDocumentForCurrentNode()
626 #if ENABLE(TEMPLATE_ELEMENT)
627 if (is<HTMLTemplateElement>(currentNode()))
628 return downcast<HTMLTemplateElement>(currentNode()).content()->document();
630 return currentNode().document();
633 PassRefPtr<Element> HTMLConstructionSite::createHTMLElement(AtomicHTMLToken* token)
635 QualifiedName tagName(nullAtom, token->name(), xhtmlNamespaceURI);
636 // FIXME: This can't use HTMLConstructionSite::createElement because we
637 // have to pass the current form element. We should rework form association
638 // to occur after construction to allow better code sharing here.
639 // http://www.whatwg.org/specs/web-apps/current-work/multipage/tree-construction.html#create-an-element-for-the-token
640 Document& ownerDocument = ownerDocumentForCurrentNode();
641 bool insideTemplateElement = !ownerDocument.frame();
642 RefPtr<Element> element = HTMLElementFactory::createElement(tagName, ownerDocument, insideTemplateElement ? nullptr : form(), true);
643 setAttributes(element.get(), token, m_parserContentPolicy);
644 ASSERT(element->isHTMLElement());
645 return element.release();
648 PassRefPtr<HTMLStackItem> HTMLConstructionSite::createElementFromSavedToken(HTMLStackItem* item)
650 RefPtr<Element> element;
651 // NOTE: Moving from item -> token -> item copies the Attribute vector twice!
652 AtomicHTMLToken fakeToken(HTMLToken::StartTag, item->localName(), Vector<Attribute>(item->attributes()));
653 if (item->namespaceURI() == HTMLNames::xhtmlNamespaceURI)
654 element = createHTMLElement(&fakeToken);
656 element = createElement(&fakeToken, item->namespaceURI());
657 return HTMLStackItem::create(element.releaseNonNull(), fakeToken, item->namespaceURI());
660 bool HTMLConstructionSite::indexOfFirstUnopenFormattingElement(unsigned& firstUnopenElementIndex) const
662 if (m_activeFormattingElements.isEmpty())
664 unsigned index = m_activeFormattingElements.size();
667 const HTMLFormattingElementList::Entry& entry = m_activeFormattingElements.at(index);
668 if (entry.isMarker() || m_openElements.contains(&entry.element())) {
669 firstUnopenElementIndex = index + 1;
670 return firstUnopenElementIndex < m_activeFormattingElements.size();
673 firstUnopenElementIndex = index;
677 void HTMLConstructionSite::reconstructTheActiveFormattingElements()
679 unsigned firstUnopenElementIndex;
680 if (!indexOfFirstUnopenFormattingElement(firstUnopenElementIndex))
683 unsigned unopenEntryIndex = firstUnopenElementIndex;
684 ASSERT(unopenEntryIndex < m_activeFormattingElements.size());
685 for (; unopenEntryIndex < m_activeFormattingElements.size(); ++unopenEntryIndex) {
686 HTMLFormattingElementList::Entry& unopenedEntry = m_activeFormattingElements.at(unopenEntryIndex);
687 RefPtr<HTMLStackItem> reconstructed = createElementFromSavedToken(unopenedEntry.stackItem());
688 attachLater(¤tNode(), &reconstructed->node());
689 m_openElements.push(reconstructed);
690 unopenedEntry.replaceElement(reconstructed.release());
694 void HTMLConstructionSite::generateImpliedEndTagsWithExclusion(const AtomicString& tagName)
696 while (hasImpliedEndTag(currentStackItem()) && !currentStackItem().matchesHTMLTag(tagName))
697 m_openElements.pop();
700 void HTMLConstructionSite::generateImpliedEndTags()
702 while (hasImpliedEndTag(currentStackItem()))
703 m_openElements.pop();
706 bool HTMLConstructionSite::inQuirksMode()
708 return m_inQuirksMode;
711 void HTMLConstructionSite::findFosterSite(HTMLConstructionSiteTask& task)
713 #if ENABLE(TEMPLATE_ELEMENT)
714 // 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!)
715 HTMLElementStack::ElementRecord* lastTemplateElement = m_openElements.topmost(templateTag.localName());
716 if (lastTemplateElement && !m_openElements.inTableScope(tableTag)) {
717 task.parent = &lastTemplateElement->element();
723 HTMLElementStack::ElementRecord* lastTableElementRecord = m_openElements.topmost(tableTag.localName());
724 if (lastTableElementRecord) {
725 Element& lastTableElement = lastTableElementRecord->element();
726 ContainerNode* parent = lastTableElement.parentNode();
727 // When parsing HTML fragments, we skip step 4.2 ("Let root be a new html element with no attributes") for efficiency,
728 // 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.
729 bool parentCanBeFosterParent = parent && (parent->isElementNode() || (m_isParsingFragment && parent == &m_openElements.rootNode()));
730 #if ENABLE(TEMPLATE_ELEMENT)
731 parentCanBeFosterParent = parentCanBeFosterParent || (is<DocumentFragment>(parent) && downcast<DocumentFragment>(parent)->isTemplateContent());
733 if (parentCanBeFosterParent) {
734 task.parent = parent;
735 task.nextChild = &lastTableElement;
738 task.parent = &lastTableElementRecord->next()->element();
742 task.parent = &m_openElements.rootNode(); // DocumentFragment
745 bool HTMLConstructionSite::shouldFosterParent() const
747 return m_redirectAttachToFosterParent
748 && causesFosterParenting(currentStackItem());
751 void HTMLConstructionSite::fosterParent(PassRefPtr<Node> node)
753 HTMLConstructionSiteTask task(HTMLConstructionSiteTask::Insert);
754 findFosterSite(task);
758 m_taskQueue.append(task);