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 "HTMLImageElement.h"
40 #include "HTMLOptGroupElement.h"
41 #include "HTMLOptionElement.h"
42 #include "HTMLParserIdioms.h"
43 #include "HTMLPictureElement.h"
44 #include "HTMLScriptElement.h"
45 #include "HTMLTemplateElement.h"
46 #include "NotImplemented.h"
47 #include "SVGElement.h"
52 using namespace HTMLNames;
54 static inline void setAttributes(Element& element, AtomicHTMLToken* token, ParserContentPolicy parserContentPolicy)
56 if (!scriptingContentIsAllowed(parserContentPolicy))
57 element.stripScriptingAttributes(token->attributes());
58 element.parserSetAttributes(token->attributes());
61 static bool hasImpliedEndTag(const HTMLStackItem& item)
63 return item.hasTagName(ddTag)
64 || item.hasTagName(dtTag)
65 || item.hasTagName(liTag)
66 || is<HTMLOptionElement>(item.node())
67 || is<HTMLOptGroupElement>(item.node())
68 || item.hasTagName(pTag)
69 || item.hasTagName(rbTag)
70 || item.hasTagName(rpTag)
71 || item.hasTagName(rtTag)
72 || item.hasTagName(rtcTag);
75 static bool shouldUseLengthLimit(const ContainerNode* node)
77 return !node->hasTagName(scriptTag)
78 && !node->hasTagName(styleTag)
79 && !node->hasTagName(SVGNames::scriptTag);
82 static inline bool causesFosterParenting(const HTMLStackItem& item)
84 return item.hasTagName(HTMLNames::tableTag)
85 || item.hasTagName(HTMLNames::tbodyTag)
86 || item.hasTagName(HTMLNames::tfootTag)
87 || item.hasTagName(HTMLNames::theadTag)
88 || item.hasTagName(HTMLNames::trTag);
91 static inline bool isAllWhitespace(const String& string)
93 return string.isAllSpecialCharacters<isHTMLSpace>();
96 static inline void insert(HTMLConstructionSiteTask& task)
98 #if ENABLE(TEMPLATE_ELEMENT)
99 if (is<HTMLTemplateElement>(*task.parent))
100 task.parent = downcast<HTMLTemplateElement>(*task.parent).content();
103 if (ContainerNode* parent = task.child->parentNode())
104 parent->parserRemoveChild(*task.child);
107 task.parent->parserInsertBefore(*task.child, *task.nextChild);
109 task.parent->parserAppendChild(*task.child);
112 static inline void executeInsertTask(HTMLConstructionSiteTask& task)
114 ASSERT(task.operation == HTMLConstructionSiteTask::Insert);
118 task.child->beginParsingChildren();
120 if (task.selfClosing)
121 task.child->finishParsingChildren();
124 static inline void executeReparentTask(HTMLConstructionSiteTask& task)
126 ASSERT(task.operation == HTMLConstructionSiteTask::Reparent);
128 if (ContainerNode* parent = task.child->parentNode())
129 parent->parserRemoveChild(*task.child);
131 task.parent->parserAppendChild(*task.child);
134 static inline void executeInsertAlreadyParsedChildTask(HTMLConstructionSiteTask& task)
136 ASSERT(task.operation == HTMLConstructionSiteTask::InsertAlreadyParsedChild);
141 static inline void executeTakeAllChildrenTask(HTMLConstructionSiteTask& task)
143 ASSERT(task.operation == HTMLConstructionSiteTask::TakeAllChildren);
145 task.parent->takeAllChildrenFrom(task.oldParent());
146 // Notice that we don't need to manually attach the moved children
147 // because takeAllChildrenFrom does that work for us.
150 static inline void executeTask(HTMLConstructionSiteTask& task)
152 switch (task.operation) {
153 case HTMLConstructionSiteTask::Insert:
154 executeInsertTask(task);
156 // All the cases below this point are only used by the adoption agency.
157 case HTMLConstructionSiteTask::InsertAlreadyParsedChild:
158 executeInsertAlreadyParsedChildTask(task);
160 case HTMLConstructionSiteTask::Reparent:
161 executeReparentTask(task);
163 case HTMLConstructionSiteTask::TakeAllChildren:
164 executeTakeAllChildrenTask(task);
167 ASSERT_NOT_REACHED();
170 void HTMLConstructionSite::attachLater(ContainerNode* parent, PassRefPtr<Node> prpChild, bool selfClosing)
172 ASSERT(scriptingContentIsAllowed(m_parserContentPolicy) || !is<Element>(*prpChild) || !toScriptElementIfPossible(downcast<Element>(prpChild.get())));
173 ASSERT(pluginContentIsAllowed(m_parserContentPolicy) || !prpChild->isPluginElement());
175 HTMLConstructionSiteTask task(HTMLConstructionSiteTask::Insert);
176 task.parent = parent;
177 task.child = prpChild;
178 task.selfClosing = selfClosing;
180 if (shouldFosterParent()) {
181 fosterParent(task.child);
185 // Add as a sibling of the parent if we have reached the maximum depth allowed.
186 if (m_openElements.stackDepth() > m_maximumDOMTreeDepth && task.parent->parentNode())
187 task.parent = task.parent->parentNode();
190 m_taskQueue.append(task);
193 void HTMLConstructionSite::executeQueuedTasks()
195 const size_t size = m_taskQueue.size();
199 // Copy the task queue into a local variable in case executeTask
200 // re-enters the parser.
201 TaskQueue queue = WTFMove(m_taskQueue);
203 for (size_t i = 0; i < size; ++i)
204 executeTask(queue[i]);
206 // We might be detached now.
209 HTMLConstructionSite::HTMLConstructionSite(Document& document, ParserContentPolicy parserContentPolicy, unsigned maximumDOMTreeDepth)
210 : m_document(&document)
211 , m_attachmentRoot(&document)
212 , m_parserContentPolicy(parserContentPolicy)
213 , m_isParsingFragment(false)
214 , m_redirectAttachToFosterParent(false)
215 , m_maximumDOMTreeDepth(maximumDOMTreeDepth)
216 , m_inQuirksMode(document.inQuirksMode())
218 ASSERT(m_document->isHTMLDocument() || m_document->isXHTMLDocument());
221 HTMLConstructionSite::HTMLConstructionSite(DocumentFragment& fragment, ParserContentPolicy parserContentPolicy, unsigned maximumDOMTreeDepth)
222 : m_document(&fragment.document())
223 , m_attachmentRoot(&fragment)
224 , m_parserContentPolicy(parserContentPolicy)
225 , m_isParsingFragment(true)
226 , m_redirectAttachToFosterParent(false)
227 , m_maximumDOMTreeDepth(maximumDOMTreeDepth)
228 , m_inQuirksMode(fragment.document().inQuirksMode())
230 ASSERT(m_document->isHTMLDocument() || m_document->isXHTMLDocument());
233 HTMLConstructionSite::~HTMLConstructionSite()
237 void HTMLConstructionSite::detach()
240 m_attachmentRoot = 0;
243 void HTMLConstructionSite::setForm(HTMLFormElement* form)
245 // This method should only be needed for HTMLTreeBuilder in the fragment case.
250 PassRefPtr<HTMLFormElement> HTMLConstructionSite::takeForm()
252 return m_form.release();
255 void HTMLConstructionSite::dispatchDocumentElementAvailableIfNeeded()
258 if (m_document->frame() && !m_isParsingFragment)
259 m_document->frame()->injectUserScripts(InjectAtDocumentStart);
262 void HTMLConstructionSite::insertHTMLHtmlStartTagBeforeHTML(AtomicHTMLToken* token)
264 Ref<HTMLHtmlElement> element = HTMLHtmlElement::create(*m_document);
265 setAttributes(element.get(), token, m_parserContentPolicy);
266 attachLater(m_attachmentRoot, element.ptr());
267 m_openElements.pushHTMLHtmlElement(HTMLStackItem::create(element.copyRef(), *token));
269 executeQueuedTasks();
270 element->insertedByParser();
271 dispatchDocumentElementAvailableIfNeeded();
274 void HTMLConstructionSite::mergeAttributesFromTokenIntoElement(AtomicHTMLToken* token, Element* element)
276 if (token->attributes().isEmpty())
279 for (auto& tokenAttribute : token->attributes()) {
280 if (!element->elementData() || !element->findAttributeByName(tokenAttribute.name()))
281 element->setAttribute(tokenAttribute.name(), tokenAttribute.value());
285 void HTMLConstructionSite::insertHTMLHtmlStartTagInBody(AtomicHTMLToken* token)
287 // Fragments do not have a root HTML element, so any additional HTML elements
288 // encountered during fragment parsing should be ignored.
289 if (m_isParsingFragment)
292 mergeAttributesFromTokenIntoElement(token, &m_openElements.htmlElement());
295 void HTMLConstructionSite::insertHTMLBodyStartTagInBody(AtomicHTMLToken* token)
297 mergeAttributesFromTokenIntoElement(token, &m_openElements.bodyElement());
300 void HTMLConstructionSite::setDefaultCompatibilityMode()
302 if (m_isParsingFragment)
304 if (m_document->isSrcdocDocument())
306 setCompatibilityMode(DocumentCompatibilityMode::QuirksMode);
309 void HTMLConstructionSite::setCompatibilityMode(DocumentCompatibilityMode mode)
311 m_inQuirksMode = (mode == DocumentCompatibilityMode::QuirksMode);
312 m_document->setCompatibilityMode(mode);
315 void HTMLConstructionSite::setCompatibilityModeFromDoctype(const String& name, const String& publicId, const String& systemId)
317 // There are three possible compatibility modes:
318 // Quirks - quirks mode emulates WinIE and NS4. CSS parsing is also relaxed in this mode, e.g., unit types can
319 // be omitted from numbers.
320 // Limited Quirks - This mode is identical to no-quirks mode except for its treatment of line-height in the inline box model.
321 // No Quirks - no quirks apply. Web pages will obey the specifications to the letter.
323 // Check for Quirks Mode.
325 || publicId.startsWith("+//Silmaril//dtd html Pro v0r11 19970101//", false)
326 || publicId.startsWith("-//AdvaSoft Ltd//DTD HTML 3.0 asWedit + extensions//", false)
327 || publicId.startsWith("-//AS//DTD HTML 3.0 asWedit + extensions//", false)
328 || publicId.startsWith("-//IETF//DTD HTML 2.0 Level 1//", false)
329 || publicId.startsWith("-//IETF//DTD HTML 2.0 Level 2//", false)
330 || publicId.startsWith("-//IETF//DTD HTML 2.0 Strict Level 1//", false)
331 || publicId.startsWith("-//IETF//DTD HTML 2.0 Strict Level 2//", false)
332 || publicId.startsWith("-//IETF//DTD HTML 2.0 Strict//", false)
333 || publicId.startsWith("-//IETF//DTD HTML 2.0//", false)
334 || publicId.startsWith("-//IETF//DTD HTML 2.1E//", false)
335 || publicId.startsWith("-//IETF//DTD HTML 3.0//", false)
336 || publicId.startsWith("-//IETF//DTD HTML 3.2 Final//", false)
337 || publicId.startsWith("-//IETF//DTD HTML 3.2//", false)
338 || publicId.startsWith("-//IETF//DTD HTML 3//", false)
339 || publicId.startsWith("-//IETF//DTD HTML Level 0//", false)
340 || publicId.startsWith("-//IETF//DTD HTML Level 1//", false)
341 || publicId.startsWith("-//IETF//DTD HTML Level 2//", false)
342 || publicId.startsWith("-//IETF//DTD HTML Level 3//", false)
343 || publicId.startsWith("-//IETF//DTD HTML Strict Level 0//", false)
344 || publicId.startsWith("-//IETF//DTD HTML Strict Level 1//", false)
345 || publicId.startsWith("-//IETF//DTD HTML Strict Level 2//", false)
346 || publicId.startsWith("-//IETF//DTD HTML Strict Level 3//", false)
347 || publicId.startsWith("-//IETF//DTD HTML Strict//", false)
348 || publicId.startsWith("-//IETF//DTD HTML//", false)
349 || publicId.startsWith("-//Metrius//DTD Metrius Presentational//", false)
350 || publicId.startsWith("-//Microsoft//DTD Internet Explorer 2.0 HTML Strict//", false)
351 || publicId.startsWith("-//Microsoft//DTD Internet Explorer 2.0 HTML//", false)
352 || publicId.startsWith("-//Microsoft//DTD Internet Explorer 2.0 Tables//", false)
353 || publicId.startsWith("-//Microsoft//DTD Internet Explorer 3.0 HTML Strict//", false)
354 || publicId.startsWith("-//Microsoft//DTD Internet Explorer 3.0 HTML//", false)
355 || publicId.startsWith("-//Microsoft//DTD Internet Explorer 3.0 Tables//", false)
356 || publicId.startsWith("-//Netscape Comm. Corp.//DTD HTML//", false)
357 || publicId.startsWith("-//Netscape Comm. Corp.//DTD Strict HTML//", false)
358 || publicId.startsWith("-//O'Reilly and Associates//DTD HTML 2.0//", false)
359 || publicId.startsWith("-//O'Reilly and Associates//DTD HTML Extended 1.0//", false)
360 || publicId.startsWith("-//O'Reilly and Associates//DTD HTML Extended Relaxed 1.0//", false)
361 || publicId.startsWith("-//SoftQuad Software//DTD HoTMetaL PRO 6.0::19990601::extensions to HTML 4.0//", false)
362 || publicId.startsWith("-//SoftQuad//DTD HoTMetaL PRO 4.0::19971010::extensions to HTML 4.0//", false)
363 || publicId.startsWith("-//Spyglass//DTD HTML 2.0 Extended//", false)
364 || publicId.startsWith("-//SQ//DTD HTML 2.0 HoTMetaL + extensions//", false)
365 || publicId.startsWith("-//Sun Microsystems Corp.//DTD HotJava HTML//", false)
366 || publicId.startsWith("-//Sun Microsystems Corp.//DTD HotJava Strict HTML//", false)
367 || publicId.startsWith("-//W3C//DTD HTML 3 1995-03-24//", false)
368 || publicId.startsWith("-//W3C//DTD HTML 3.2 Draft//", false)
369 || publicId.startsWith("-//W3C//DTD HTML 3.2 Final//", false)
370 || publicId.startsWith("-//W3C//DTD HTML 3.2//", false)
371 || publicId.startsWith("-//W3C//DTD HTML 3.2S Draft//", false)
372 || publicId.startsWith("-//W3C//DTD HTML 4.0 Frameset//", false)
373 || publicId.startsWith("-//W3C//DTD HTML 4.0 Transitional//", false)
374 || publicId.startsWith("-//W3C//DTD HTML Experimental 19960712//", false)
375 || publicId.startsWith("-//W3C//DTD HTML Experimental 970421//", false)
376 || publicId.startsWith("-//W3C//DTD W3 HTML//", false)
377 || publicId.startsWith("-//W3O//DTD W3 HTML 3.0//", false)
378 || equalIgnoringCase(publicId, "-//W3O//DTD W3 HTML Strict 3.0//EN//")
379 || publicId.startsWith("-//WebTechs//DTD Mozilla HTML 2.0//", false)
380 || publicId.startsWith("-//WebTechs//DTD Mozilla HTML//", false)
381 || equalIgnoringCase(publicId, "-/W3C/DTD HTML 4.0 Transitional/EN")
382 || equalIgnoringCase(publicId, "HTML")
383 || equalIgnoringCase(systemId, "http://www.ibm.com/data/dtd/v11/ibmxhtml1-transitional.dtd")
384 || (systemId.isEmpty() && publicId.startsWith("-//W3C//DTD HTML 4.01 Frameset//", false))
385 || (systemId.isEmpty() && publicId.startsWith("-//W3C//DTD HTML 4.01 Transitional//", false))) {
386 setCompatibilityMode(DocumentCompatibilityMode::QuirksMode);
390 // Check for Limited Quirks Mode.
391 if (publicId.startsWith("-//W3C//DTD XHTML 1.0 Frameset//", false)
392 || publicId.startsWith("-//W3C//DTD XHTML 1.0 Transitional//", false)
393 || (!systemId.isEmpty() && publicId.startsWith("-//W3C//DTD HTML 4.01 Frameset//", false))
394 || (!systemId.isEmpty() && publicId.startsWith("-//W3C//DTD HTML 4.01 Transitional//", false))) {
395 setCompatibilityMode(DocumentCompatibilityMode::LimitedQuirksMode);
399 // Otherwise we are No Quirks Mode.
400 setCompatibilityMode(DocumentCompatibilityMode::NoQuirksMode);
403 void HTMLConstructionSite::finishedParsing()
405 m_document->finishedParsing();
408 void HTMLConstructionSite::insertDoctype(AtomicHTMLToken* token)
410 ASSERT(token->type() == HTMLToken::DOCTYPE);
412 String publicId = token->publicIdentifier();
413 String systemId = token->systemIdentifier();
415 RefPtr<DocumentType> doctype = DocumentType::create(*m_document, token->name(), publicId, systemId);
416 attachLater(m_attachmentRoot, doctype.release());
418 // DOCTYPE nodes are only processed when parsing fragments w/o contextElements, which
419 // never occurs. However, if we ever chose to support such, this code is subtly wrong,
420 // because context-less fragments can determine their own quirks mode, and thus change
421 // parsing rules (like <p> inside <table>). For now we ASSERT that we never hit this code
422 // in a fragment, as changing the owning document's compatibility mode would be wrong.
423 ASSERT(!m_isParsingFragment);
424 if (m_isParsingFragment)
427 if (token->forceQuirks())
428 setCompatibilityMode(DocumentCompatibilityMode::QuirksMode);
430 setCompatibilityModeFromDoctype(token->name(), publicId, systemId);
433 void HTMLConstructionSite::insertComment(AtomicHTMLToken* token)
435 ASSERT(token->type() == HTMLToken::Comment);
436 attachLater(¤tNode(), Comment::create(ownerDocumentForCurrentNode(), token->comment()));
439 void HTMLConstructionSite::insertCommentOnDocument(AtomicHTMLToken* token)
441 ASSERT(token->type() == HTMLToken::Comment);
442 attachLater(m_attachmentRoot, Comment::create(*m_document, token->comment()));
445 void HTMLConstructionSite::insertCommentOnHTMLHtmlElement(AtomicHTMLToken* token)
447 ASSERT(token->type() == HTMLToken::Comment);
448 ContainerNode& parent = m_openElements.rootNode();
449 attachLater(&parent, Comment::create(parent.document(), token->comment()));
452 void HTMLConstructionSite::insertHTMLHeadElement(AtomicHTMLToken* token)
454 ASSERT(!shouldFosterParent());
455 m_head = HTMLStackItem::create(createHTMLElement(token), *token);
456 attachLater(¤tNode(), &m_head->element());
457 m_openElements.pushHTMLHeadElement(m_head);
460 void HTMLConstructionSite::insertHTMLBodyElement(AtomicHTMLToken* token)
462 ASSERT(!shouldFosterParent());
463 RefPtr<Element> body = createHTMLElement(token);
464 attachLater(¤tNode(), body.get());
465 m_openElements.pushHTMLBodyElement(HTMLStackItem::create(body.releaseNonNull(), *token));
468 void HTMLConstructionSite::insertHTMLFormElement(AtomicHTMLToken* token, bool isDemoted)
470 RefPtr<Element> element = createHTMLElement(token);
471 ASSERT(is<HTMLFormElement>(*element));
472 m_form = static_pointer_cast<HTMLFormElement>(element.release());
473 m_form->setDemoted(isDemoted);
474 attachLater(¤tNode(), m_form);
475 m_openElements.push(HTMLStackItem::create(*m_form, *token));
478 void HTMLConstructionSite::insertHTMLElement(AtomicHTMLToken* token)
480 RefPtr<Element> element = createHTMLElement(token);
481 attachLater(¤tNode(), element);
482 m_openElements.push(HTMLStackItem::create(element.releaseNonNull(), *token));
485 void HTMLConstructionSite::insertSelfClosingHTMLElement(AtomicHTMLToken* token)
487 ASSERT(token->type() == HTMLToken::StartTag);
488 // Normally HTMLElementStack is responsible for calling finishParsingChildren,
489 // but self-closing elements are never in the element stack so the stack
490 // doesn't get a chance to tell them that we're done parsing their children.
491 attachLater(¤tNode(), createHTMLElement(token), true);
492 // FIXME: Do we want to acknowledge the token's self-closing flag?
493 // http://www.whatwg.org/specs/web-apps/current-work/multipage/tokenization.html#acknowledge-self-closing-flag
496 void HTMLConstructionSite::insertFormattingElement(AtomicHTMLToken* token)
498 // http://www.whatwg.org/specs/web-apps/current-work/multipage/parsing.html#the-stack-of-open-elements
499 // Possible active formatting elements include:
500 // a, b, big, code, em, font, i, nobr, s, small, strike, strong, tt, and u.
501 ASSERT(isFormattingTag(token->name()));
502 insertHTMLElement(token);
503 m_activeFormattingElements.append(¤tStackItem());
506 void HTMLConstructionSite::insertScriptElement(AtomicHTMLToken* token)
508 // http://www.whatwg.org/specs/web-apps/current-work/multipage/scripting-1.html#already-started
509 // http://html5.org/specs/dom-parsing.html#dom-range-createcontextualfragment
510 // For createContextualFragment, the specifications say to mark it parser-inserted and already-started and later unmark them.
511 // However, we short circuit that logic to avoid the subtree traversal to find script elements since scripts can never see
512 // those flags or effects thereof.
513 const bool parserInserted = m_parserContentPolicy != AllowScriptingContentAndDoNotMarkAlreadyStarted;
514 const bool alreadyStarted = m_isParsingFragment && parserInserted;
515 Ref<HTMLScriptElement> element = HTMLScriptElement::create(scriptTag, ownerDocumentForCurrentNode(), parserInserted, alreadyStarted);
516 setAttributes(element.get(), token, m_parserContentPolicy);
517 if (scriptingContentIsAllowed(m_parserContentPolicy))
518 attachLater(¤tNode(), element.ptr());
519 m_openElements.push(HTMLStackItem::create(WTFMove(element), *token));
522 void HTMLConstructionSite::insertForeignElement(AtomicHTMLToken* token, const AtomicString& namespaceURI)
524 ASSERT(token->type() == HTMLToken::StartTag);
525 notImplemented(); // parseError when xmlns or xmlns:xlink are wrong.
527 RefPtr<Element> element = createElement(token, namespaceURI);
528 if (scriptingContentIsAllowed(m_parserContentPolicy) || !toScriptElementIfPossible(element.get()))
529 attachLater(¤tNode(), element, token->selfClosing());
530 if (!token->selfClosing())
531 m_openElements.push(HTMLStackItem::create(element.releaseNonNull(), *token, namespaceURI));
534 void HTMLConstructionSite::insertTextNode(const String& characters, WhitespaceMode whitespaceMode)
536 HTMLConstructionSiteTask task(HTMLConstructionSiteTask::Insert);
537 task.parent = ¤tNode();
539 if (shouldFosterParent())
540 findFosterSite(task);
542 #if ENABLE(TEMPLATE_ELEMENT)
543 if (is<HTMLTemplateElement>(*task.parent))
544 task.parent = downcast<HTMLTemplateElement>(*task.parent).content();
547 // Strings composed entirely of whitespace are likely to be repeated.
548 // Turn them into AtomicString so we share a single string for each.
549 bool shouldUseAtomicString = whitespaceMode == AllWhitespace
550 || (whitespaceMode == WhitespaceUnknown && isAllWhitespace(characters));
552 unsigned currentPosition = 0;
553 unsigned lengthLimit = shouldUseLengthLimit(task.parent.get()) ? Text::defaultLengthLimit : std::numeric_limits<unsigned>::max();
555 // FIXME: Splitting text nodes into smaller chunks contradicts HTML5 spec, but is currently necessary
556 // for performance, see <https://bugs.webkit.org/show_bug.cgi?id=55898>.
558 Node* previousChild = task.nextChild ? task.nextChild->previousSibling() : task.parent->lastChild();
559 if (is<Text>(previousChild)) {
560 // FIXME: We're only supposed to append to this text node if it
561 // was the last text node inserted by the parser.
562 Text& textNode = downcast<Text>(*previousChild);
563 currentPosition = textNode.parserAppendData(characters, 0, lengthLimit);
566 while (currentPosition < characters.length()) {
567 Ref<Text> textNode = Text::createWithLengthLimit(task.parent->document(), shouldUseAtomicString ? AtomicString(characters).string() : characters, currentPosition, lengthLimit);
568 // 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.
569 if (!textNode->length()) {
570 String substring = characters.substring(currentPosition);
571 textNode = Text::create(task.parent->document(), shouldUseAtomicString ? AtomicString(substring).string() : substring);
574 currentPosition += textNode->length();
575 ASSERT(currentPosition <= characters.length());
576 task.child = WTFMove(textNode);
582 void HTMLConstructionSite::reparent(HTMLElementStack::ElementRecord& newParent, HTMLElementStack::ElementRecord& child)
584 HTMLConstructionSiteTask task(HTMLConstructionSiteTask::Reparent);
585 task.parent = &newParent.node();
586 task.child = &child.element();
587 m_taskQueue.append(task);
590 void HTMLConstructionSite::reparent(HTMLElementStack::ElementRecord& newParent, HTMLStackItem& child)
592 HTMLConstructionSiteTask task(HTMLConstructionSiteTask::Reparent);
593 task.parent = &newParent.node();
594 task.child = &child.element();
595 m_taskQueue.append(task);
598 void HTMLConstructionSite::insertAlreadyParsedChild(HTMLStackItem& newParent, HTMLElementStack::ElementRecord& child)
600 if (causesFosterParenting(newParent)) {
601 fosterParent(&child.element());
605 HTMLConstructionSiteTask task(HTMLConstructionSiteTask::InsertAlreadyParsedChild);
606 task.parent = &newParent.node();
607 task.child = &child.element();
608 m_taskQueue.append(task);
611 void HTMLConstructionSite::takeAllChildren(HTMLStackItem& newParent, HTMLElementStack::ElementRecord& oldParent)
613 HTMLConstructionSiteTask task(HTMLConstructionSiteTask::TakeAllChildren);
614 task.parent = &newParent.node();
615 task.child = &oldParent.node();
616 m_taskQueue.append(task);
619 Ref<Element> HTMLConstructionSite::createElement(AtomicHTMLToken* token, const AtomicString& namespaceURI)
621 QualifiedName tagName(nullAtom, token->name(), namespaceURI);
622 Ref<Element> element = ownerDocumentForCurrentNode().createElement(tagName, true);
623 setAttributes(element.get(), token, m_parserContentPolicy);
627 inline Document& HTMLConstructionSite::ownerDocumentForCurrentNode()
629 #if ENABLE(TEMPLATE_ELEMENT)
630 if (is<HTMLTemplateElement>(currentNode()))
631 return downcast<HTMLTemplateElement>(currentNode()).content()->document();
633 return currentNode().document();
636 Ref<Element> HTMLConstructionSite::createHTMLElement(AtomicHTMLToken* token)
638 QualifiedName tagName(nullAtom, token->name(), xhtmlNamespaceURI);
639 // FIXME: This can't use HTMLConstructionSite::createElement because we
640 // have to pass the current form element. We should rework form association
641 // to occur after construction to allow better code sharing here.
642 // http://www.whatwg.org/specs/web-apps/current-work/multipage/tree-construction.html#create-an-element-for-the-token
643 Document& ownerDocument = ownerDocumentForCurrentNode();
644 bool insideTemplateElement = !ownerDocument.frame();
645 Ref<Element> element = HTMLElementFactory::createElement(tagName, ownerDocument, insideTemplateElement ? nullptr : form(), true);
647 // FIXME: This is a hack to connect images to pictures before the image has
648 // been inserted into the document. It can be removed once asynchronous image
649 // loading is working.
650 if (is<HTMLPictureElement>(currentNode()) && is<HTMLImageElement>(element))
651 downcast<HTMLImageElement>(element.get()).setPictureElement(&downcast<HTMLPictureElement>(currentNode()));
653 setAttributes(element.get(), token, m_parserContentPolicy);
654 ASSERT(element->isHTMLElement());
658 Ref<HTMLStackItem> HTMLConstructionSite::createElementFromSavedToken(HTMLStackItem* item)
660 // NOTE: Moving from item -> token -> item copies the Attribute vector twice!
661 AtomicHTMLToken fakeToken(HTMLToken::StartTag, item->localName(), Vector<Attribute>(item->attributes()));
662 ASSERT(item->namespaceURI() == HTMLNames::xhtmlNamespaceURI);
663 ASSERT(isFormattingTag(item->localName()));
664 return HTMLStackItem::create(createHTMLElement(&fakeToken), fakeToken, item->namespaceURI());
667 bool HTMLConstructionSite::indexOfFirstUnopenFormattingElement(unsigned& firstUnopenElementIndex) const
669 if (m_activeFormattingElements.isEmpty())
671 unsigned index = m_activeFormattingElements.size();
674 const HTMLFormattingElementList::Entry& entry = m_activeFormattingElements.at(index);
675 if (entry.isMarker() || m_openElements.contains(&entry.element())) {
676 firstUnopenElementIndex = index + 1;
677 return firstUnopenElementIndex < m_activeFormattingElements.size();
680 firstUnopenElementIndex = index;
684 void HTMLConstructionSite::reconstructTheActiveFormattingElements()
686 unsigned firstUnopenElementIndex;
687 if (!indexOfFirstUnopenFormattingElement(firstUnopenElementIndex))
690 unsigned unopenEntryIndex = firstUnopenElementIndex;
691 ASSERT(unopenEntryIndex < m_activeFormattingElements.size());
692 for (; unopenEntryIndex < m_activeFormattingElements.size(); ++unopenEntryIndex) {
693 HTMLFormattingElementList::Entry& unopenedEntry = m_activeFormattingElements.at(unopenEntryIndex);
694 RefPtr<HTMLStackItem> reconstructed = createElementFromSavedToken(unopenedEntry.stackItem());
695 attachLater(¤tNode(), &reconstructed->node());
696 m_openElements.push(reconstructed);
697 unopenedEntry.replaceElement(reconstructed.release());
701 void HTMLConstructionSite::generateImpliedEndTagsWithExclusion(const AtomicString& tagName)
703 while (hasImpliedEndTag(currentStackItem()) && !currentStackItem().matchesHTMLTag(tagName))
704 m_openElements.pop();
707 void HTMLConstructionSite::generateImpliedEndTags()
709 while (hasImpliedEndTag(currentStackItem()))
710 m_openElements.pop();
713 bool HTMLConstructionSite::inQuirksMode()
715 return m_inQuirksMode;
718 void HTMLConstructionSite::findFosterSite(HTMLConstructionSiteTask& task)
720 #if ENABLE(TEMPLATE_ELEMENT)
721 // 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!)
722 HTMLElementStack::ElementRecord* lastTemplateElement = m_openElements.topmost(templateTag.localName());
723 if (lastTemplateElement && !m_openElements.inTableScope(tableTag)) {
724 task.parent = &lastTemplateElement->element();
730 HTMLElementStack::ElementRecord* lastTableElementRecord = m_openElements.topmost(tableTag.localName());
731 if (lastTableElementRecord) {
732 Element& lastTableElement = lastTableElementRecord->element();
733 ContainerNode* parent = lastTableElement.parentNode();
734 // When parsing HTML fragments, we skip step 4.2 ("Let root be a new html element with no attributes") for efficiency,
735 // 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.
736 bool parentCanBeFosterParent = parent && (parent->isElementNode() || (m_isParsingFragment && parent == &m_openElements.rootNode()));
737 #if ENABLE(TEMPLATE_ELEMENT)
738 parentCanBeFosterParent = parentCanBeFosterParent || (is<DocumentFragment>(parent) && downcast<DocumentFragment>(parent)->isTemplateContent());
740 if (parentCanBeFosterParent) {
741 task.parent = parent;
742 task.nextChild = &lastTableElement;
745 task.parent = &lastTableElementRecord->next()->element();
749 task.parent = &m_openElements.rootNode(); // DocumentFragment
752 bool HTMLConstructionSite::shouldFosterParent() const
754 return m_redirectAttachToFosterParent
755 && causesFosterParenting(currentStackItem());
758 void HTMLConstructionSite::fosterParent(PassRefPtr<Node> node)
760 HTMLConstructionSiteTask task(HTMLConstructionSiteTask::Insert);
761 findFosterSite(task);
765 m_taskQueue.append(task);