4f3a774a7d080a61c7b29810189171952ebe6128
[WebKit-https.git] / Source / WebCore / html / parser / HTMLConstructionSite.cpp
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 "AtomicHTMLToken.h"
31 #include "Comment.h"
32 #include "DocumentFragment.h"
33 #include "DocumentType.h"
34 #include "Element.h"
35 #include "Frame.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"
52 #include "Settings.h"
53 #include "Text.h"
54
55 namespace WebCore {
56
57 using namespace HTMLNames;
58
59 static inline void setAttributes(Element* element, AtomicHTMLToken* token, ParserContentPolicy parserContentPolicy)
60 {
61     if (!scriptingContentIsAllowed(parserContentPolicy))
62         element->stripScriptingAttributes(token->attributes());
63     element->parserSetAttributes(token->attributes());
64 }
65
66 static bool hasImpliedEndTag(const HTMLStackItem* item)
67 {
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);
76 }
77
78 static bool shouldUseLengthLimit(const ContainerNode* node)
79 {
80     return !node->hasTagName(scriptTag)
81         && !node->hasTagName(styleTag)
82         && !node->hasTagName(SVGNames::scriptTag);
83 }
84
85 static inline bool isAllWhitespace(const String& string)
86 {
87     return string.isAllSpecialCharacters<isHTMLSpace>();
88 }
89
90 static inline void executeTask(HTMLConstructionSiteTask& task)
91 {
92 #if ENABLE(TEMPLATE_ELEMENT)
93     if (task.parent->hasTagName(templateTag))
94         task.parent = toHTMLTemplateElement(task.parent.get())->content();
95 #endif
96
97     if (task.nextChild)
98         task.parent->parserInsertBefore(task.child.get(), task.nextChild.get());
99     else
100         task.parent->parserAppendChild(task.child.get());
101
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.
104
105     if (task.child->parentNode() && task.parent->attached() && !task.child->attached())
106         task.child->attach();
107
108     task.child->beginParsingChildren();
109
110     if (task.selfClosing)
111         task.child->finishParsingChildren();
112 }
113
114 void HTMLConstructionSite::attachLater(ContainerNode* parent, PassRefPtr<Node> prpChild, bool selfClosing)
115 {
116     ASSERT(scriptingContentIsAllowed(m_parserContentPolicy) || !prpChild.get()->isElementNode() || !toScriptElementIfPossible(toElement(prpChild.get())));
117     ASSERT(pluginContentIsAllowed(m_parserContentPolicy) || !prpChild->isPluginElement());
118
119     HTMLConstructionSiteTask task;
120     task.parent = parent;
121     task.child = prpChild;
122     task.selfClosing = selfClosing;
123
124     if (shouldFosterParent()) {
125         fosterParent(task.child);
126         return;
127     }
128
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();
132
133     ASSERT(task.parent);
134     m_attachmentQueue.append(task);
135 }
136
137 void HTMLConstructionSite::executeQueuedTasks()
138 {
139     const size_t size = m_attachmentQueue.size();
140     if (!size)
141         return;
142
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);
147
148     for (size_t i = 0; i < size; ++i)
149         executeTask(queue[i]);
150
151     // We might be detached now.
152 }
153
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())
162 {
163     ASSERT(m_document->isHTMLDocument() || m_document->isXHTMLDocument());
164 }
165
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())
174 {
175     ASSERT(m_document->isHTMLDocument() || m_document->isXHTMLDocument());
176 }
177
178 HTMLConstructionSite::~HTMLConstructionSite()
179 {
180 }
181
182 void HTMLConstructionSite::detach()
183 {
184     m_document = 0;
185     m_attachmentRoot = 0;
186 }
187
188 void HTMLConstructionSite::setForm(HTMLFormElement* form)
189 {
190     // This method should only be needed for HTMLTreeBuilder in the fragment case.
191     ASSERT(!m_form);
192     m_form = form;
193 }
194
195 PassRefPtr<HTMLFormElement> HTMLConstructionSite::takeForm()
196 {
197     return m_form.release();
198 }
199
200 void HTMLConstructionSite::dispatchDocumentElementAvailableIfNeeded()
201 {
202     ASSERT(m_document);
203     if (m_document->frame() && !m_isParsingFragment)
204         m_document->frame()->loader()->dispatchDocumentElementAvailable();
205 }
206
207 void HTMLConstructionSite::insertHTMLHtmlStartTagBeforeHTML(AtomicHTMLToken* token)
208 {
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));
213
214     executeQueuedTasks();
215     element->insertedByParser();
216     dispatchDocumentElementAvailableIfNeeded();
217 }
218
219 void HTMLConstructionSite::mergeAttributesFromTokenIntoElement(AtomicHTMLToken* token, Element* element)
220 {
221     if (token->attributes().isEmpty())
222         return;
223
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());
228     }
229 }
230
231 void HTMLConstructionSite::insertHTMLHtmlStartTagInBody(AtomicHTMLToken* token)
232 {
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)
236         return;
237
238     mergeAttributesFromTokenIntoElement(token, m_openElements.htmlElement());
239 }
240
241 void HTMLConstructionSite::insertHTMLBodyStartTagInBody(AtomicHTMLToken* token)
242 {
243     mergeAttributesFromTokenIntoElement(token, m_openElements.bodyElement());
244 }
245
246 void HTMLConstructionSite::setDefaultCompatibilityMode()
247 {
248     if (m_isParsingFragment)
249         return;
250     if (m_document->isSrcdocDocument())
251         return;
252     setCompatibilityMode(Document::QuirksMode);
253 }
254
255 void HTMLConstructionSite::setCompatibilityMode(Document::CompatibilityMode mode)
256 {
257     m_inQuirksMode = (mode == Document::QuirksMode);
258     m_document->setCompatibilityMode(mode);
259 }
260
261 void HTMLConstructionSite::setCompatibilityModeFromDoctype(const String& name, const String& publicId, const String& systemId)
262 {
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.
268
269     // Check for Quirks Mode.
270     if (name != "html"
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);
333         return;
334     }
335
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);
342         return;
343     }
344
345     // Otherwise we are No Quirks Mode.
346     setCompatibilityMode(Document::NoQuirksMode);
347 }
348
349 void HTMLConstructionSite::finishedParsing()
350 {
351     m_document->finishedParsing();
352 }
353
354 void HTMLConstructionSite::insertDoctype(AtomicHTMLToken* token)
355 {
356     ASSERT(token->type() == HTMLToken::DOCTYPE);
357
358     const String& publicId = String::adopt(token->publicIdentifier());
359     const String& systemId = String::adopt(token->systemIdentifier());
360     RefPtr<DocumentType> doctype = DocumentType::create(m_document, token->name(), publicId, systemId);
361     attachLater(m_attachmentRoot, doctype.release());
362
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)
370         return;
371
372     if (token->forceQuirks())
373         setCompatibilityMode(Document::QuirksMode);
374     else {
375         setCompatibilityModeFromDoctype(token->name(), publicId, systemId);
376     }
377 }
378
379 void HTMLConstructionSite::insertComment(AtomicHTMLToken* token)
380 {
381     ASSERT(token->type() == HTMLToken::Comment);
382     attachLater(currentNode(), Comment::create(ownerDocumentForCurrentNode(), token->comment()));
383 }
384
385 void HTMLConstructionSite::insertCommentOnDocument(AtomicHTMLToken* token)
386 {
387     ASSERT(token->type() == HTMLToken::Comment);
388     attachLater(m_attachmentRoot, Comment::create(m_document, token->comment()));
389 }
390
391 void HTMLConstructionSite::insertCommentOnHTMLHtmlElement(AtomicHTMLToken* token)
392 {
393     ASSERT(token->type() == HTMLToken::Comment);
394     ContainerNode* parent = m_openElements.rootNode();
395     attachLater(parent, Comment::create(parent->document(), token->comment()));
396 }
397
398 void HTMLConstructionSite::insertHTMLHeadElement(AtomicHTMLToken* token)
399 {
400     ASSERT(!shouldFosterParent());
401     m_head = HTMLStackItem::create(createHTMLElement(token), token);
402     attachLater(currentNode(), m_head->element());
403     m_openElements.pushHTMLHeadElement(m_head);
404 }
405
406 void HTMLConstructionSite::insertHTMLBodyElement(AtomicHTMLToken* token)
407 {
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();
414 }
415
416 void HTMLConstructionSite::insertHTMLFormElement(AtomicHTMLToken* token, bool isDemoted)
417 {
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));
424 }
425
426 void HTMLConstructionSite::insertHTMLElement(AtomicHTMLToken* token)
427 {
428     RefPtr<Element> element = createHTMLElement(token);
429     attachLater(currentNode(), element);
430     m_openElements.push(HTMLStackItem::create(element.release(), token));
431 }
432
433 void HTMLConstructionSite::insertSelfClosingHTMLElement(AtomicHTMLToken* token)
434 {
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
442 }
443
444 void HTMLConstructionSite::insertFormattingElement(AtomicHTMLToken* token)
445 {
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());
451 }
452
453 void HTMLConstructionSite::insertScriptElement(AtomicHTMLToken* token)
454 {
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));
467 }
468
469 void HTMLConstructionSite::insertForeignElement(AtomicHTMLToken* token, const AtomicString& namespaceURI)
470 {
471     ASSERT(token->type() == HTMLToken::StartTag);
472     notImplemented(); // parseError when xmlns or xmlns:xlink are wrong.
473
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));
479 }
480
481 void HTMLConstructionSite::insertTextNode(const String& characters, WhitespaceMode whitespaceMode)
482 {
483     HTMLConstructionSiteTask task;
484     task.parent = currentNode();
485
486     if (shouldFosterParent())
487         findFosterSite(task);
488
489 #if ENABLE(TEMPLATE_ELEMENT)
490     if (task.parent->hasTagName(templateTag))
491         task.parent = toHTMLTemplateElement(task.parent.get())->content();
492 #endif
493
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));
498
499     unsigned currentPosition = 0;
500     unsigned lengthLimit = shouldUseLengthLimit(task.parent.get()) ? Text::defaultLengthLimit : std::numeric_limits<unsigned>::max();
501
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>.
504
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);
511     }
512
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);
519         }
520
521         currentPosition += textNode->length();
522         ASSERT(currentPosition <= characters.length());
523         task.child = textNode.release();
524
525         executeTask(task);
526     }
527 }
528
529 PassRefPtr<Element> HTMLConstructionSite::createElement(AtomicHTMLToken* token, const AtomicString& namespaceURI)
530 {
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();
535 }
536
537 inline Document* HTMLConstructionSite::ownerDocumentForCurrentNode()
538 {
539 #if ENABLE(TEMPLATE_ELEMENT)
540     if (currentNode()->hasTagName(templateTag))
541         return toHTMLTemplateElement(currentElement())->content()->document();
542 #endif
543     return currentNode()->document();
544 }
545
546 PassRefPtr<Element> HTMLConstructionSite::createHTMLElement(AtomicHTMLToken* token)
547 {
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();
556 }
557
558 PassRefPtr<HTMLStackItem> HTMLConstructionSite::createElementFromSavedToken(HTMLStackItem* item)
559 {
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);
565     else
566         element = createElement(&fakeToken, item->namespaceURI());
567     return HTMLStackItem::create(element.release(), &fakeToken, item->namespaceURI());
568 }
569
570 bool HTMLConstructionSite::indexOfFirstUnopenFormattingElement(unsigned& firstUnopenElementIndex) const
571 {
572     if (m_activeFormattingElements.isEmpty())
573         return false;
574     unsigned index = m_activeFormattingElements.size();
575     do {
576         --index;
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();
581         }
582     } while (index);
583     firstUnopenElementIndex = index;
584     return true;
585 }
586
587 void HTMLConstructionSite::reconstructTheActiveFormattingElements()
588 {
589     unsigned firstUnopenElementIndex;
590     if (!indexOfFirstUnopenFormattingElement(firstUnopenElementIndex))
591         return;
592
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());
601     }
602 }
603
604 void HTMLConstructionSite::generateImpliedEndTagsWithExclusion(const AtomicString& tagName)
605 {
606     while (hasImpliedEndTag(currentStackItem()) && !currentStackItem()->matchesHTMLTag(tagName))
607         m_openElements.pop();
608 }
609
610 void HTMLConstructionSite::generateImpliedEndTags()
611 {
612     while (hasImpliedEndTag(currentStackItem()))
613         m_openElements.pop();
614 }
615
616 bool HTMLConstructionSite::inQuirksMode()
617 {
618     return m_inQuirksMode;
619 }
620
621 void HTMLConstructionSite::findFosterSite(HTMLConstructionSiteTask& task)
622 {
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();
628         return;
629     }
630
631 #endif
632
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());
642 #endif
643         if (parentCanBeFosterParent) {
644             task.parent = parent;
645             task.nextChild = lastTableElement;
646             return;
647         }
648         task.parent = lastTableElementRecord->next()->element();
649         return;
650     }
651     // Fragment case
652     task.parent = m_openElements.rootNode(); // DocumentFragment
653 }
654
655 bool HTMLConstructionSite::shouldFosterParent() const
656 {
657     return m_redirectAttachToFosterParent
658         && currentStackItem()->isElementNode()
659         && currentStackItem()->causesFosterParenting();
660 }
661
662 void HTMLConstructionSite::fosterParent(PassRefPtr<Node> node)
663 {
664     HTMLConstructionSiteTask task;
665     findFosterSite(task);
666     task.child = node;
667     ASSERT(task.parent);
668
669     m_attachmentQueue.append(task);
670 }
671
672 }