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"
30 #include "AtomicHTMLToken.h"
32 #include "DocumentFragment.h"
33 #include "DocumentType.h"
36 #include "FrameLoader.h"
37 #include "FrameLoaderClient.h"
38 #include "HTMLDocument.h"
39 #include "HTMLElementFactory.h"
40 #include "HTMLFormElement.h"
41 #include "HTMLHtmlElement.h"
42 #include "HTMLNames.h"
43 #include "HTMLParserIdioms.h"
44 #include "HTMLPlugInElement.h"
45 #include "HTMLScriptElement.h"
46 #include "HTMLStackItem.h"
47 #include "HTMLTemplateElement.h"
48 #include "HTMLToken.h"
49 #include "HTMLTokenizer.h"
50 #include "LocalizedStrings.h"
51 #include "NotImplemented.h"
57 using namespace HTMLNames;
59 static inline void setAttributes(Element* element, AtomicHTMLToken* token, ParserContentPolicy parserContentPolicy)
61 if (!scriptingContentIsAllowed(parserContentPolicy))
62 element->stripScriptingAttributes(token->attributes());
63 element->parserSetAttributes(token->attributes());
66 static bool hasImpliedEndTag(const HTMLStackItem* item)
68 return item->hasTagName(ddTag)
69 || item->hasTagName(dtTag)
70 || item->hasTagName(liTag)
71 || item->hasTagName(optionTag)
72 || item->hasTagName(optgroupTag)
73 || item->hasTagName(pTag)
74 || item->hasTagName(rpTag)
75 || item->hasTagName(rtTag);
78 static bool shouldUseLengthLimit(const ContainerNode* node)
80 return !node->hasTagName(scriptTag)
81 && !node->hasTagName(styleTag)
82 && !node->hasTagName(SVGNames::scriptTag);
85 static inline bool isAllWhitespace(const String& string)
87 return string.isAllSpecialCharacters<isHTMLSpace>();
90 static inline void executeTask(HTMLConstructionSiteTask& task)
92 #if ENABLE(TEMPLATE_ELEMENT)
93 if (task.parent->hasTagName(templateTag))
94 task.parent = toHTMLTemplateElement(task.parent.get())->content();
98 task.parent->parserInsertBefore(task.child.get(), task.nextChild.get());
100 task.parent->parserAppendChild(task.child.get());
102 // JavaScript run from beforeload (or DOM Mutation or event handlers)
103 // might have removed the child, in which case we should not attach it.
105 if (task.child->parentNode() && task.parent->attached() && !task.child->attached())
106 task.child->attach();
108 task.child->beginParsingChildren();
110 if (task.selfClosing)
111 task.child->finishParsingChildren();
114 void HTMLConstructionSite::attachLater(ContainerNode* parent, PassRefPtr<Node> prpChild, bool selfClosing)
116 ASSERT(scriptingContentIsAllowed(m_parserContentPolicy) || !prpChild.get()->isElementNode() || !toScriptElementIfPossible(toElement(prpChild.get())));
117 ASSERT(pluginContentIsAllowed(m_parserContentPolicy) || !prpChild->isPluginElement());
119 HTMLConstructionSiteTask task;
120 task.parent = parent;
121 task.child = prpChild;
122 task.selfClosing = selfClosing;
124 if (shouldFosterParent()) {
125 fosterParent(task.child);
129 // Add as a sibling of the parent if we have reached the maximum depth allowed.
130 if (m_openElements.stackDepth() > m_maximumDOMTreeDepth && task.parent->parentNode())
131 task.parent = task.parent->parentNode();
134 m_attachmentQueue.append(task);
137 void HTMLConstructionSite::executeQueuedTasks()
139 const size_t size = m_attachmentQueue.size();
143 // Copy the task queue into a local variable in case executeTask
144 // re-enters the parser.
145 AttachmentQueue queue;
146 queue.swap(m_attachmentQueue);
148 for (size_t i = 0; i < size; ++i)
149 executeTask(queue[i]);
151 // We might be detached now.
154 HTMLConstructionSite::HTMLConstructionSite(Document* document, ParserContentPolicy parserContentPolicy, unsigned maximumDOMTreeDepth)
155 : m_document(document)
156 , m_attachmentRoot(document)
157 , m_parserContentPolicy(parserContentPolicy)
158 , m_isParsingFragment(false)
159 , m_redirectAttachToFosterParent(false)
160 , m_maximumDOMTreeDepth(maximumDOMTreeDepth)
161 , m_inQuirksMode(document->inQuirksMode())
163 ASSERT(m_document->isHTMLDocument() || m_document->isXHTMLDocument());
166 HTMLConstructionSite::HTMLConstructionSite(DocumentFragment* fragment, ParserContentPolicy parserContentPolicy, unsigned maximumDOMTreeDepth)
167 : m_document(fragment->document())
168 , m_attachmentRoot(fragment)
169 , m_parserContentPolicy(parserContentPolicy)
170 , m_isParsingFragment(true)
171 , m_redirectAttachToFosterParent(false)
172 , m_maximumDOMTreeDepth(maximumDOMTreeDepth)
173 , m_inQuirksMode(fragment->document()->inQuirksMode())
175 ASSERT(m_document->isHTMLDocument() || m_document->isXHTMLDocument());
178 HTMLConstructionSite::~HTMLConstructionSite()
182 void HTMLConstructionSite::detach()
185 m_attachmentRoot = 0;
188 void HTMLConstructionSite::setForm(HTMLFormElement* form)
190 // This method should only be needed for HTMLTreeBuilder in the fragment case.
195 PassRefPtr<HTMLFormElement> HTMLConstructionSite::takeForm()
197 return m_form.release();
200 void HTMLConstructionSite::dispatchDocumentElementAvailableIfNeeded()
203 if (m_document->frame() && !m_isParsingFragment)
204 m_document->frame()->loader()->dispatchDocumentElementAvailable();
207 void HTMLConstructionSite::insertHTMLHtmlStartTagBeforeHTML(AtomicHTMLToken* token)
209 RefPtr<HTMLHtmlElement> element = HTMLHtmlElement::create(m_document);
210 setAttributes(element.get(), token, m_parserContentPolicy);
211 attachLater(m_attachmentRoot, element);
212 m_openElements.pushHTMLHtmlElement(HTMLStackItem::create(element, token));
214 executeQueuedTasks();
215 element->insertedByParser();
216 dispatchDocumentElementAvailableIfNeeded();
219 void HTMLConstructionSite::mergeAttributesFromTokenIntoElement(AtomicHTMLToken* token, Element* element)
221 if (token->attributes().isEmpty())
224 for (unsigned i = 0; i < token->attributes().size(); ++i) {
225 const Attribute& tokenAttribute = token->attributes().at(i);
226 if (!element->elementData() || !element->getAttributeItem(tokenAttribute.name()))
227 element->setAttribute(tokenAttribute.name(), tokenAttribute.value());
231 void HTMLConstructionSite::insertHTMLHtmlStartTagInBody(AtomicHTMLToken* token)
233 // Fragments do not have a root HTML element, so any additional HTML elements
234 // encountered during fragment parsing should be ignored.
235 if (m_isParsingFragment)
238 mergeAttributesFromTokenIntoElement(token, m_openElements.htmlElement());
241 void HTMLConstructionSite::insertHTMLBodyStartTagInBody(AtomicHTMLToken* token)
243 mergeAttributesFromTokenIntoElement(token, m_openElements.bodyElement());
246 void HTMLConstructionSite::setDefaultCompatibilityMode()
248 if (m_isParsingFragment)
250 if (m_document->isSrcdocDocument())
252 setCompatibilityMode(Document::QuirksMode);
255 void HTMLConstructionSite::setCompatibilityMode(Document::CompatibilityMode mode)
257 m_inQuirksMode = (mode == Document::QuirksMode);
258 m_document->setCompatibilityMode(mode);
261 void HTMLConstructionSite::setCompatibilityModeFromDoctype(const String& name, const String& publicId, const String& systemId)
263 // There are three possible compatibility modes:
264 // Quirks - quirks mode emulates WinIE and NS4. CSS parsing is also relaxed in this mode, e.g., unit types can
265 // be omitted from numbers.
266 // Limited Quirks - This mode is identical to no-quirks mode except for its treatment of line-height in the inline box model.
267 // No Quirks - no quirks apply. Web pages will obey the specifications to the letter.
269 // Check for Quirks Mode.
271 || publicId.startsWith("+//Silmaril//dtd html Pro v0r11 19970101//", false)
272 || publicId.startsWith("-//AdvaSoft Ltd//DTD HTML 3.0 asWedit + extensions//", false)
273 || publicId.startsWith("-//AS//DTD HTML 3.0 asWedit + extensions//", false)
274 || publicId.startsWith("-//IETF//DTD HTML 2.0 Level 1//", false)
275 || publicId.startsWith("-//IETF//DTD HTML 2.0 Level 2//", false)
276 || publicId.startsWith("-//IETF//DTD HTML 2.0 Strict Level 1//", false)
277 || publicId.startsWith("-//IETF//DTD HTML 2.0 Strict Level 2//", false)
278 || publicId.startsWith("-//IETF//DTD HTML 2.0 Strict//", false)
279 || publicId.startsWith("-//IETF//DTD HTML 2.0//", false)
280 || publicId.startsWith("-//IETF//DTD HTML 2.1E//", false)
281 || publicId.startsWith("-//IETF//DTD HTML 3.0//", false)
282 || publicId.startsWith("-//IETF//DTD HTML 3.2 Final//", false)
283 || publicId.startsWith("-//IETF//DTD HTML 3.2//", false)
284 || publicId.startsWith("-//IETF//DTD HTML 3//", false)
285 || publicId.startsWith("-//IETF//DTD HTML Level 0//", false)
286 || publicId.startsWith("-//IETF//DTD HTML Level 1//", false)
287 || publicId.startsWith("-//IETF//DTD HTML Level 2//", false)
288 || publicId.startsWith("-//IETF//DTD HTML Level 3//", false)
289 || publicId.startsWith("-//IETF//DTD HTML Strict Level 0//", false)
290 || publicId.startsWith("-//IETF//DTD HTML Strict Level 1//", false)
291 || publicId.startsWith("-//IETF//DTD HTML Strict Level 2//", false)
292 || publicId.startsWith("-//IETF//DTD HTML Strict Level 3//", false)
293 || publicId.startsWith("-//IETF//DTD HTML Strict//", false)
294 || publicId.startsWith("-//IETF//DTD HTML//", false)
295 || publicId.startsWith("-//Metrius//DTD Metrius Presentational//", false)
296 || publicId.startsWith("-//Microsoft//DTD Internet Explorer 2.0 HTML Strict//", false)
297 || publicId.startsWith("-//Microsoft//DTD Internet Explorer 2.0 HTML//", false)
298 || publicId.startsWith("-//Microsoft//DTD Internet Explorer 2.0 Tables//", false)
299 || publicId.startsWith("-//Microsoft//DTD Internet Explorer 3.0 HTML Strict//", false)
300 || publicId.startsWith("-//Microsoft//DTD Internet Explorer 3.0 HTML//", false)
301 || publicId.startsWith("-//Microsoft//DTD Internet Explorer 3.0 Tables//", false)
302 || publicId.startsWith("-//Netscape Comm. Corp.//DTD HTML//", false)
303 || publicId.startsWith("-//Netscape Comm. Corp.//DTD Strict HTML//", false)
304 || publicId.startsWith("-//O'Reilly and Associates//DTD HTML 2.0//", false)
305 || publicId.startsWith("-//O'Reilly and Associates//DTD HTML Extended 1.0//", false)
306 || publicId.startsWith("-//O'Reilly and Associates//DTD HTML Extended Relaxed 1.0//", false)
307 || publicId.startsWith("-//SoftQuad Software//DTD HoTMetaL PRO 6.0::19990601::extensions to HTML 4.0//", false)
308 || publicId.startsWith("-//SoftQuad//DTD HoTMetaL PRO 4.0::19971010::extensions to HTML 4.0//", false)
309 || publicId.startsWith("-//Spyglass//DTD HTML 2.0 Extended//", false)
310 || publicId.startsWith("-//SQ//DTD HTML 2.0 HoTMetaL + extensions//", false)
311 || publicId.startsWith("-//Sun Microsystems Corp.//DTD HotJava HTML//", false)
312 || publicId.startsWith("-//Sun Microsystems Corp.//DTD HotJava Strict HTML//", false)
313 || publicId.startsWith("-//W3C//DTD HTML 3 1995-03-24//", false)
314 || publicId.startsWith("-//W3C//DTD HTML 3.2 Draft//", false)
315 || publicId.startsWith("-//W3C//DTD HTML 3.2 Final//", false)
316 || publicId.startsWith("-//W3C//DTD HTML 3.2//", false)
317 || publicId.startsWith("-//W3C//DTD HTML 3.2S Draft//", false)
318 || publicId.startsWith("-//W3C//DTD HTML 4.0 Frameset//", false)
319 || publicId.startsWith("-//W3C//DTD HTML 4.0 Transitional//", false)
320 || publicId.startsWith("-//W3C//DTD HTML Experimental 19960712//", false)
321 || publicId.startsWith("-//W3C//DTD HTML Experimental 970421//", false)
322 || publicId.startsWith("-//W3C//DTD W3 HTML//", false)
323 || publicId.startsWith("-//W3O//DTD W3 HTML 3.0//", false)
324 || equalIgnoringCase(publicId, "-//W3O//DTD W3 HTML Strict 3.0//EN//")
325 || publicId.startsWith("-//WebTechs//DTD Mozilla HTML 2.0//", false)
326 || publicId.startsWith("-//WebTechs//DTD Mozilla HTML//", false)
327 || equalIgnoringCase(publicId, "-/W3C/DTD HTML 4.0 Transitional/EN")
328 || equalIgnoringCase(publicId, "HTML")
329 || equalIgnoringCase(systemId, "http://www.ibm.com/data/dtd/v11/ibmxhtml1-transitional.dtd")
330 || (systemId.isEmpty() && publicId.startsWith("-//W3C//DTD HTML 4.01 Frameset//", false))
331 || (systemId.isEmpty() && publicId.startsWith("-//W3C//DTD HTML 4.01 Transitional//", false))) {
332 setCompatibilityMode(Document::QuirksMode);
336 // Check for Limited Quirks Mode.
337 if (publicId.startsWith("-//W3C//DTD XHTML 1.0 Frameset//", false)
338 || publicId.startsWith("-//W3C//DTD XHTML 1.0 Transitional//", false)
339 || (!systemId.isEmpty() && publicId.startsWith("-//W3C//DTD HTML 4.01 Frameset//", false))
340 || (!systemId.isEmpty() && publicId.startsWith("-//W3C//DTD HTML 4.01 Transitional//", false))) {
341 setCompatibilityMode(Document::LimitedQuirksMode);
345 // Otherwise we are No Quirks Mode.
346 setCompatibilityMode(Document::NoQuirksMode);
349 void HTMLConstructionSite::finishedParsing()
351 m_document->finishedParsing();
354 void HTMLConstructionSite::insertDoctype(AtomicHTMLToken* token)
356 ASSERT(token->type() == HTMLToken::DOCTYPE);
358 const String& publicId = StringImpl::create8BitIfPossible(token->publicIdentifier());
359 const String& systemId = StringImpl::create8BitIfPossible(token->systemIdentifier());
360 RefPtr<DocumentType> doctype = DocumentType::create(m_document, token->name(), publicId, systemId);
361 attachLater(m_attachmentRoot, doctype.release());
363 // DOCTYPE nodes are only processed when parsing fragments w/o contextElements, which
364 // never occurs. However, if we ever chose to support such, this code is subtly wrong,
365 // because context-less fragments can determine their own quirks mode, and thus change
366 // parsing rules (like <p> inside <table>). For now we ASSERT that we never hit this code
367 // in a fragment, as changing the owning document's compatibility mode would be wrong.
368 ASSERT(!m_isParsingFragment);
369 if (m_isParsingFragment)
372 if (token->forceQuirks())
373 setCompatibilityMode(Document::QuirksMode);
375 setCompatibilityModeFromDoctype(token->name(), publicId, systemId);
379 void HTMLConstructionSite::insertComment(AtomicHTMLToken* token)
381 ASSERT(token->type() == HTMLToken::Comment);
382 attachLater(currentNode(), Comment::create(ownerDocumentForCurrentNode(), token->comment()));
385 void HTMLConstructionSite::insertCommentOnDocument(AtomicHTMLToken* token)
387 ASSERT(token->type() == HTMLToken::Comment);
388 attachLater(m_attachmentRoot, Comment::create(m_document, token->comment()));
391 void HTMLConstructionSite::insertCommentOnHTMLHtmlElement(AtomicHTMLToken* token)
393 ASSERT(token->type() == HTMLToken::Comment);
394 ContainerNode* parent = m_openElements.rootNode();
395 attachLater(parent, Comment::create(parent->document(), token->comment()));
398 void HTMLConstructionSite::insertHTMLHeadElement(AtomicHTMLToken* token)
400 ASSERT(!shouldFosterParent());
401 m_head = HTMLStackItem::create(createHTMLElement(token), token);
402 attachLater(currentNode(), m_head->element());
403 m_openElements.pushHTMLHeadElement(m_head);
406 void HTMLConstructionSite::insertHTMLBodyElement(AtomicHTMLToken* token)
408 ASSERT(!shouldFosterParent());
409 RefPtr<Element> body = createHTMLElement(token);
410 attachLater(currentNode(), body);
411 m_openElements.pushHTMLBodyElement(HTMLStackItem::create(body.release(), token));
412 if (Frame* frame = m_document->frame())
413 frame->loader()->client()->dispatchWillInsertBody();
416 void HTMLConstructionSite::insertHTMLFormElement(AtomicHTMLToken* token, bool isDemoted)
418 RefPtr<Element> element = createHTMLElement(token);
419 ASSERT(element->hasTagName(formTag));
420 m_form = static_pointer_cast<HTMLFormElement>(element.release());
421 m_form->setDemoted(isDemoted);
422 attachLater(currentNode(), m_form);
423 m_openElements.push(HTMLStackItem::create(m_form, token));
426 void HTMLConstructionSite::insertHTMLElement(AtomicHTMLToken* token)
428 RefPtr<Element> element = createHTMLElement(token);
429 attachLater(currentNode(), element);
430 m_openElements.push(HTMLStackItem::create(element.release(), token));
433 void HTMLConstructionSite::insertSelfClosingHTMLElement(AtomicHTMLToken* token)
435 ASSERT(token->type() == HTMLToken::StartTag);
436 // Normally HTMLElementStack is responsible for calling finishParsingChildren,
437 // but self-closing elements are never in the element stack so the stack
438 // doesn't get a chance to tell them that we're done parsing their children.
439 attachLater(currentNode(), createHTMLElement(token), true);
440 // FIXME: Do we want to acknowledge the token's self-closing flag?
441 // http://www.whatwg.org/specs/web-apps/current-work/multipage/tokenization.html#acknowledge-self-closing-flag
444 void HTMLConstructionSite::insertFormattingElement(AtomicHTMLToken* token)
446 // http://www.whatwg.org/specs/web-apps/current-work/multipage/parsing.html#the-stack-of-open-elements
447 // Possible active formatting elements include:
448 // a, b, big, code, em, font, i, nobr, s, small, strike, strong, tt, and u.
449 insertHTMLElement(token);
450 m_activeFormattingElements.append(currentElementRecord()->stackItem());
453 void HTMLConstructionSite::insertScriptElement(AtomicHTMLToken* token)
455 // http://www.whatwg.org/specs/web-apps/current-work/multipage/scripting-1.html#already-started
456 // http://html5.org/specs/dom-parsing.html#dom-range-createcontextualfragment
457 // For createContextualFragment, the specifications say to mark it parser-inserted and already-started and later unmark them.
458 // However, we short circuit that logic to avoid the subtree traversal to find script elements since scripts can never see
459 // those flags or effects thereof.
460 const bool parserInserted = m_parserContentPolicy != AllowScriptingContentAndDoNotMarkAlreadyStarted;
461 const bool alreadyStarted = m_isParsingFragment && parserInserted;
462 RefPtr<HTMLScriptElement> element = HTMLScriptElement::create(scriptTag, ownerDocumentForCurrentNode(), parserInserted, alreadyStarted);
463 setAttributes(element.get(), token, m_parserContentPolicy);
464 if (scriptingContentIsAllowed(m_parserContentPolicy))
465 attachLater(currentNode(), element);
466 m_openElements.push(HTMLStackItem::create(element.release(), token));
469 void HTMLConstructionSite::insertForeignElement(AtomicHTMLToken* token, const AtomicString& namespaceURI)
471 ASSERT(token->type() == HTMLToken::StartTag);
472 notImplemented(); // parseError when xmlns or xmlns:xlink are wrong.
474 RefPtr<Element> element = createElement(token, namespaceURI);
475 if (scriptingContentIsAllowed(m_parserContentPolicy) || !toScriptElementIfPossible(element.get()))
476 attachLater(currentNode(), element, token->selfClosing());
477 if (!token->selfClosing())
478 m_openElements.push(HTMLStackItem::create(element.release(), token, namespaceURI));
481 void HTMLConstructionSite::insertTextNode(const String& characters, WhitespaceMode whitespaceMode)
483 HTMLConstructionSiteTask task;
484 task.parent = currentNode();
486 if (shouldFosterParent())
487 findFosterSite(task);
489 #if ENABLE(TEMPLATE_ELEMENT)
490 if (task.parent->hasTagName(templateTag))
491 task.parent = toHTMLTemplateElement(task.parent.get())->content();
494 // Strings composed entirely of whitespace are likely to be repeated.
495 // Turn them into AtomicString so we share a single string for each.
496 bool shouldUseAtomicString = whitespaceMode == AllWhitespace
497 || (whitespaceMode == WhitespaceUnknown && isAllWhitespace(characters));
499 unsigned currentPosition = 0;
500 unsigned lengthLimit = shouldUseLengthLimit(task.parent.get()) ? Text::defaultLengthLimit : std::numeric_limits<unsigned>::max();
502 // FIXME: Splitting text nodes into smaller chunks contradicts HTML5 spec, but is currently necessary
503 // for performance, see <https://bugs.webkit.org/show_bug.cgi?id=55898>.
505 Node* previousChild = task.nextChild ? task.nextChild->previousSibling() : task.parent->lastChild();
506 if (previousChild && previousChild->isTextNode()) {
507 // FIXME: We're only supposed to append to this text node if it
508 // was the last text node inserted by the parser.
509 CharacterData* textNode = static_cast<CharacterData*>(previousChild);
510 currentPosition = textNode->parserAppendData(characters, 0, lengthLimit);
513 while (currentPosition < characters.length()) {
514 RefPtr<Text> textNode = Text::createWithLengthLimit(task.parent->document(), shouldUseAtomicString ? AtomicString(characters).string() : characters, currentPosition, lengthLimit);
515 // 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.
516 if (!textNode->length()) {
517 String substring = characters.substring(currentPosition);
518 textNode = Text::create(task.parent->document(), shouldUseAtomicString ? AtomicString(substring).string() : substring);
521 currentPosition += textNode->length();
522 ASSERT(currentPosition <= characters.length());
523 task.child = textNode.release();
529 PassRefPtr<Element> HTMLConstructionSite::createElement(AtomicHTMLToken* token, const AtomicString& namespaceURI)
531 QualifiedName tagName(nullAtom, token->name(), namespaceURI);
532 RefPtr<Element> element = ownerDocumentForCurrentNode()->createElement(tagName, true);
533 setAttributes(element.get(), token, m_parserContentPolicy);
534 return element.release();
537 inline Document* HTMLConstructionSite::ownerDocumentForCurrentNode()
539 #if ENABLE(TEMPLATE_ELEMENT)
540 if (currentNode()->hasTagName(templateTag))
541 return toHTMLTemplateElement(currentElement())->content()->document();
543 return currentNode()->document();
546 PassRefPtr<Element> HTMLConstructionSite::createHTMLElement(AtomicHTMLToken* token)
548 QualifiedName tagName(nullAtom, token->name(), xhtmlNamespaceURI);
549 // FIXME: This can't use HTMLConstructionSite::createElement because we
550 // have to pass the current form element. We should rework form association
551 // to occur after construction to allow better code sharing here.
552 RefPtr<Element> element = HTMLElementFactory::createHTMLElement(tagName, ownerDocumentForCurrentNode(), form(), true);
553 setAttributes(element.get(), token, m_parserContentPolicy);
554 ASSERT(element->isHTMLElement());
555 return element.release();
558 PassRefPtr<HTMLStackItem> HTMLConstructionSite::createElementFromSavedToken(HTMLStackItem* item)
560 RefPtr<Element> element;
561 // NOTE: Moving from item -> token -> item copies the Attribute vector twice!
562 AtomicHTMLToken fakeToken(HTMLToken::StartTag, item->localName(), item->attributes());
563 if (item->namespaceURI() == HTMLNames::xhtmlNamespaceURI)
564 element = createHTMLElement(&fakeToken);
566 element = createElement(&fakeToken, item->namespaceURI());
567 return HTMLStackItem::create(element.release(), &fakeToken, item->namespaceURI());
570 bool HTMLConstructionSite::indexOfFirstUnopenFormattingElement(unsigned& firstUnopenElementIndex) const
572 if (m_activeFormattingElements.isEmpty())
574 unsigned index = m_activeFormattingElements.size();
577 const HTMLFormattingElementList::Entry& entry = m_activeFormattingElements.at(index);
578 if (entry.isMarker() || m_openElements.contains(entry.element())) {
579 firstUnopenElementIndex = index + 1;
580 return firstUnopenElementIndex < m_activeFormattingElements.size();
583 firstUnopenElementIndex = index;
587 void HTMLConstructionSite::reconstructTheActiveFormattingElements()
589 unsigned firstUnopenElementIndex;
590 if (!indexOfFirstUnopenFormattingElement(firstUnopenElementIndex))
593 unsigned unopenEntryIndex = firstUnopenElementIndex;
594 ASSERT(unopenEntryIndex < m_activeFormattingElements.size());
595 for (; unopenEntryIndex < m_activeFormattingElements.size(); ++unopenEntryIndex) {
596 HTMLFormattingElementList::Entry& unopenedEntry = m_activeFormattingElements.at(unopenEntryIndex);
597 RefPtr<HTMLStackItem> reconstructed = createElementFromSavedToken(unopenedEntry.stackItem().get());
598 attachLater(currentNode(), reconstructed->node());
599 m_openElements.push(reconstructed);
600 unopenedEntry.replaceElement(reconstructed.release());
604 void HTMLConstructionSite::generateImpliedEndTagsWithExclusion(const AtomicString& tagName)
606 while (hasImpliedEndTag(currentStackItem()) && !currentStackItem()->matchesHTMLTag(tagName))
607 m_openElements.pop();
610 void HTMLConstructionSite::generateImpliedEndTags()
612 while (hasImpliedEndTag(currentStackItem()))
613 m_openElements.pop();
616 bool HTMLConstructionSite::inQuirksMode()
618 return m_inQuirksMode;
621 void HTMLConstructionSite::findFosterSite(HTMLConstructionSiteTask& task)
623 #if ENABLE(TEMPLATE_ELEMENT)
624 // 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!)
625 HTMLElementStack::ElementRecord* lastTemplateElement = m_openElements.topmost(templateTag.localName());
626 if (lastTemplateElement && !m_openElements.inTableScope(tableTag)) {
627 task.parent = lastTemplateElement->element();
633 HTMLElementStack::ElementRecord* lastTableElementRecord = m_openElements.topmost(tableTag.localName());
634 if (lastTableElementRecord) {
635 Element* lastTableElement = lastTableElementRecord->element();
636 ContainerNode* parent = lastTableElement->parentNode();
637 // When parsing HTML fragments, we skip step 4.2 ("Let root be a new html element with no attributes") for efficiency,
638 // 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.
639 bool parentCanBeFosterParent = parent && (parent->isElementNode() || (m_isParsingFragment && parent == m_openElements.rootNode()));
640 #if ENABLE(TEMPLATE_ELEMENT)
641 parentCanBeFosterParent = parentCanBeFosterParent || (parent && parent->isDocumentFragment() && static_cast<DocumentFragment*>(parent)->isTemplateContent());
643 if (parentCanBeFosterParent) {
644 task.parent = parent;
645 task.nextChild = lastTableElement;
648 task.parent = lastTableElementRecord->next()->element();
652 task.parent = m_openElements.rootNode(); // DocumentFragment
655 bool HTMLConstructionSite::shouldFosterParent() const
657 return m_redirectAttachToFosterParent
658 && currentStackItem()->isElementNode()
659 && currentStackItem()->causesFosterParenting();
662 void HTMLConstructionSite::fosterParent(PassRefPtr<Node> node)
664 HTMLConstructionSiteTask task;
665 findFosterSite(task);
669 m_attachmentQueue.append(task);