initial import
[vuplus_webkit] / 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 "Comment.h"
31 #include "DocumentFragment.h"
32 #include "DocumentType.h"
33 #include "Element.h"
34 #include "Frame.h"
35 #include "HTMLDocument.h"
36 #include "HTMLElementFactory.h"
37 #include "HTMLFormElement.h"
38 #include "HTMLHtmlElement.h"
39 #include "HTMLNames.h"
40 #include "HTMLScriptElement.h"
41 #include "HTMLToken.h"
42 #include "HTMLTokenizer.h"
43 #include "LocalizedStrings.h"
44 #if ENABLE(MATHML)
45 #include "MathMLNames.h"
46 #endif
47 #include "NotImplemented.h"
48 #if ENABLE(SVG)
49 #include "SVGNames.h"
50 #endif
51 #include "Settings.h"
52 #include "Text.h"
53 #include <wtf/UnusedParam.h>
54
55 namespace WebCore {
56
57 using namespace HTMLNames;
58
59 namespace {
60
61 bool hasImpliedEndTag(ContainerNode* node)
62 {
63     return node->hasTagName(ddTag)
64         || node->hasTagName(dtTag)
65         || node->hasTagName(liTag)
66         || node->hasTagName(optionTag)
67         || node->hasTagName(optgroupTag)
68         || node->hasTagName(pTag)
69         || node->hasTagName(rpTag)
70         || node->hasTagName(rtTag);
71 }
72
73 bool causesFosterParenting(const QualifiedName& tagName)
74 {
75     return tagName == tableTag
76         || tagName == tbodyTag
77         || tagName == tfootTag
78         || tagName == theadTag
79         || tagName == trTag;
80 }
81
82 } // namespace
83
84 template<typename ChildType>
85 PassRefPtr<ChildType> HTMLConstructionSite::attach(ContainerNode* rawParent, PassRefPtr<ChildType> prpChild)
86 {
87     RefPtr<ChildType> child = prpChild;
88     RefPtr<ContainerNode> parent = rawParent;
89
90     // FIXME: It's confusing that HTMLConstructionSite::attach does the magic
91     // redirection to the foster parent but HTMLConstructionSite::attachAtSite
92     // doesn't. It feels like we're missing a concept somehow.
93     if (shouldFosterParent()) {
94         fosterParent(child.get());
95         ASSERT(child->attached() || !child->parentNode() || !child->parentNode()->attached());
96         return child.release();
97     }
98
99     // Add as a sibling of the parent if we have reached the maximum depth allowed.
100     if (m_openElements.stackDepth() > m_maximumDOMTreeDepth)
101         parent = parent->parentNode();
102
103     parent->parserAddChild(child);
104
105     // An event handler (DOM Mutation, beforeload, et al.) could have removed
106     // the child, in which case we shouldn't try attaching it.
107     if (!child->parentNode())
108         return child.release();
109
110     if (parent->attached() && !child->attached())
111         child->attach();
112     return child.release();
113 }
114
115 void HTMLConstructionSite::attachAtSite(const AttachmentSite& site, PassRefPtr<Node> prpChild)
116 {
117     // FIXME: It's unfortunate that we need to hold a reference to child
118     // here to call attach().  We should investigate whether we can rely on
119     // |site.parent| to hold a ref at this point.
120     RefPtr<Node> child = prpChild;
121
122     if (site.nextChild)
123         site.parent->parserInsertBefore(child, site.nextChild);
124     else
125         site.parent->parserAddChild(child);
126
127     // JavaScript run from beforeload (or DOM Mutation or event handlers)
128     // might have removed the child, in which case we should not attach it.
129     if (child->parentNode() && site.parent->attached() && !child->attached())
130         child->attach();
131 }
132
133 HTMLConstructionSite::HTMLConstructionSite(Document* document, unsigned maximumDOMTreeDepth)
134     : m_document(document)
135     , m_attachmentRoot(document)
136     , m_fragmentScriptingPermission(FragmentScriptingAllowed)
137     , m_isParsingFragment(false)
138     , m_redirectAttachToFosterParent(false)
139     , m_maximumDOMTreeDepth(maximumDOMTreeDepth)
140 {
141 }
142
143 HTMLConstructionSite::HTMLConstructionSite(DocumentFragment* fragment, FragmentScriptingPermission scriptingPermission, unsigned maximumDOMTreeDepth)
144     : m_document(fragment->document())
145     , m_attachmentRoot(fragment)
146     , m_fragmentScriptingPermission(scriptingPermission)
147     , m_isParsingFragment(true)
148     , m_redirectAttachToFosterParent(false)
149     , m_maximumDOMTreeDepth(maximumDOMTreeDepth)
150 {
151 }
152
153 HTMLConstructionSite::~HTMLConstructionSite()
154 {
155 }
156
157 void HTMLConstructionSite::detach()
158 {
159     m_document = 0;
160     m_attachmentRoot = 0;
161 }
162
163 void HTMLConstructionSite::setForm(HTMLFormElement* form)
164 {
165     // This method should only be needed for HTMLTreeBuilder in the fragment case.
166     ASSERT(!m_form);
167     m_form = form;
168 }
169
170 PassRefPtr<HTMLFormElement> HTMLConstructionSite::takeForm()
171 {
172     return m_form.release();
173 }
174
175 void HTMLConstructionSite::dispatchDocumentElementAvailableIfNeeded()
176 {
177     ASSERT(m_document);
178     if (m_document->frame() && !m_isParsingFragment)
179         m_document->frame()->loader()->dispatchDocumentElementAvailable();
180 }
181
182 void HTMLConstructionSite::insertHTMLHtmlStartTagBeforeHTML(AtomicHTMLToken& token)
183 {
184     RefPtr<HTMLHtmlElement> element = HTMLHtmlElement::create(m_document);
185     element->setAttributeMap(token.takeAtributes(), m_fragmentScriptingPermission);
186     m_openElements.pushHTMLHtmlElement(attach<Element>(m_attachmentRoot, element.get()));
187 #if ENABLE(OFFLINE_WEB_APPLICATIONS)
188     element->insertedByParser();
189 #endif
190     dispatchDocumentElementAvailableIfNeeded();
191 }
192
193 void HTMLConstructionSite::mergeAttributesFromTokenIntoElement(AtomicHTMLToken& token, Element* element)
194 {
195     if (!token.attributes())
196         return;
197
198     NamedNodeMap* attributes = element->attributes(false);
199     for (unsigned i = 0; i < token.attributes()->length(); ++i) {
200         Attribute* attribute = token.attributes()->attributeItem(i);
201         if (!attributes->getAttributeItem(attribute->name()))
202             element->setAttribute(attribute->name(), attribute->value());
203     }
204 }
205
206 void HTMLConstructionSite::insertHTMLHtmlStartTagInBody(AtomicHTMLToken& token)
207 {
208     // FIXME: parse error
209     
210     // Fragments do not have a root HTML element, so any additional HTML elements
211     // encountered during fragment parsing should be ignored.
212     if (m_isParsingFragment)
213         return;
214
215     mergeAttributesFromTokenIntoElement(token, m_openElements.htmlElement());
216 }
217
218 void HTMLConstructionSite::insertHTMLBodyStartTagInBody(AtomicHTMLToken& token)
219 {
220     // FIXME: parse error
221     mergeAttributesFromTokenIntoElement(token, m_openElements.bodyElement());
222 }
223
224 void HTMLConstructionSite::insertDoctype(AtomicHTMLToken& token)
225 {
226     ASSERT(token.type() == HTMLTokenTypes::DOCTYPE);
227     attach(m_attachmentRoot, DocumentType::create(m_document, token.name(), String::adopt(token.publicIdentifier()), String::adopt(token.systemIdentifier())));
228     
229     // DOCTYPE nodes are only processed when parsing fragments w/o contextElements, which
230     // never occurs.  However, if we ever chose to support such, this code is subtly wrong,
231     // because context-less fragments can determine their own quirks mode, and thus change
232     // parsing rules (like <p> inside <table>).  For now we ASSERT that we never hit this code
233     // in a fragment, as changing the owning document's compatibility mode would be wrong.
234     ASSERT(!m_isParsingFragment);
235     if (m_isParsingFragment)
236         return;
237     
238     if (token.forceQuirks())
239         m_document->setCompatibilityMode(Document::QuirksMode);
240     else
241         m_document->setCompatibilityModeFromDoctype();
242 }
243
244 void HTMLConstructionSite::insertComment(AtomicHTMLToken& token)
245 {
246     ASSERT(token.type() == HTMLTokenTypes::Comment);
247     attach(currentNode(), Comment::create(currentNode()->document(), token.comment()));
248 }
249
250 void HTMLConstructionSite::insertCommentOnDocument(AtomicHTMLToken& token)
251 {
252     ASSERT(token.type() == HTMLTokenTypes::Comment);
253     attach(m_attachmentRoot, Comment::create(m_document, token.comment()));
254 }
255
256 void HTMLConstructionSite::insertCommentOnHTMLHtmlElement(AtomicHTMLToken& token)
257 {
258     ASSERT(token.type() == HTMLTokenTypes::Comment);
259     ContainerNode* parent = m_openElements.rootNode();
260     attach(parent, Comment::create(parent->document(), token.comment()));
261 }
262
263 PassRefPtr<Element> HTMLConstructionSite::attachToCurrent(PassRefPtr<Element> child)
264 {
265     return attach(currentNode(), child);
266 }
267
268 void HTMLConstructionSite::insertHTMLHeadElement(AtomicHTMLToken& token)
269 {
270     ASSERT(!shouldFosterParent());
271     m_head = attachToCurrent(createHTMLElement(token));
272     m_openElements.pushHTMLHeadElement(m_head);
273 }
274
275 void HTMLConstructionSite::insertHTMLBodyElement(AtomicHTMLToken& token)
276 {
277     ASSERT(!shouldFosterParent());
278     m_openElements.pushHTMLBodyElement(attachToCurrent(createHTMLElement(token)));
279 }
280
281 void HTMLConstructionSite::insertHTMLFormElement(AtomicHTMLToken& token, bool isDemoted)
282 {
283     RefPtr<Element> element = createHTMLElement(token);
284     ASSERT(element->hasTagName(formTag));
285     RefPtr<HTMLFormElement> form = static_pointer_cast<HTMLFormElement>(element.release());
286     form->setDemoted(isDemoted);
287     m_openElements.push(attachToCurrent(form.release()));
288     ASSERT(currentElement()->isHTMLElement());
289     ASSERT(currentElement()->hasTagName(formTag));
290     m_form = static_cast<HTMLFormElement*>(currentElement());
291 }
292
293 void HTMLConstructionSite::insertHTMLElement(AtomicHTMLToken& token)
294 {
295     m_openElements.push(attachToCurrent(createHTMLElement(token)));
296 }
297
298 void HTMLConstructionSite::insertSelfClosingHTMLElement(AtomicHTMLToken& token)
299 {
300     ASSERT(token.type() == HTMLTokenTypes::StartTag);
301     RefPtr<Element> element = attachToCurrent(createHTMLElement(token));
302     // Normally HTMLElementStack is responsible for calling finishParsingChildren,
303     // but self-closing elements are never in the element stack so the stack
304     // doesn't get a chance to tell them that we're done parsing their children.
305     element->finishParsingChildren();
306     // FIXME: Do we want to acknowledge the token's self-closing flag?
307     // http://www.whatwg.org/specs/web-apps/current-work/multipage/tokenization.html#acknowledge-self-closing-flag
308 }
309
310 void HTMLConstructionSite::insertFormattingElement(AtomicHTMLToken& token)
311 {
312     // http://www.whatwg.org/specs/web-apps/current-work/multipage/parsing.html#the-stack-of-open-elements
313     // Possible active formatting elements include:
314     // a, b, big, code, em, font, i, nobr, s, small, strike, strong, tt, and u.
315     insertHTMLElement(token);
316     m_activeFormattingElements.append(currentElement());
317 }
318
319 void HTMLConstructionSite::insertScriptElement(AtomicHTMLToken& token)
320 {
321     RefPtr<HTMLScriptElement> element = HTMLScriptElement::create(scriptTag, currentNode()->document(), true);
322     if (m_fragmentScriptingPermission == FragmentScriptingAllowed)
323         element->setAttributeMap(token.takeAtributes(), m_fragmentScriptingPermission);
324     m_openElements.push(attachToCurrent(element.release()));
325 }
326
327 void HTMLConstructionSite::insertForeignElement(AtomicHTMLToken& token, const AtomicString& namespaceURI)
328 {
329     ASSERT(token.type() == HTMLTokenTypes::StartTag);
330     notImplemented(); // parseError when xmlns or xmlns:xlink are wrong.
331
332     RefPtr<Element> element = attachToCurrent(createElement(token, namespaceURI));
333     if (!token.selfClosing())
334         m_openElements.push(element);
335 }
336
337 void HTMLConstructionSite::insertTextNode(const String& characters)
338 {
339     AttachmentSite site;
340     site.parent = currentNode();
341     site.nextChild = 0;
342     if (shouldFosterParent())
343         findFosterSite(site);
344
345     unsigned currentPosition = 0;
346
347     // FIXME: Splitting text nodes into smaller chunks contradicts HTML5 spec, but is currently necessary
348     // for performance, see <https://bugs.webkit.org/show_bug.cgi?id=55898>.
349
350     Node* previousChild = site.nextChild ? site.nextChild->previousSibling() : site.parent->lastChild();
351     if (previousChild && previousChild->isTextNode()) {
352         // FIXME: We're only supposed to append to this text node if it
353         // was the last text node inserted by the parser.
354         CharacterData* textNode = static_cast<CharacterData*>(previousChild);
355         currentPosition = textNode->parserAppendData(characters.characters(), characters.length(), Text::defaultLengthLimit);
356     }
357
358     while (currentPosition < characters.length()) {
359         RefPtr<Text> textNode = Text::createWithLengthLimit(site.parent->document(), characters, currentPosition);
360         // 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.
361         if (!textNode->length())
362             textNode = Text::create(site.parent->document(), characters.substring(currentPosition));
363
364         currentPosition += textNode->length();
365         ASSERT(currentPosition <= characters.length());
366         attachAtSite(site, textNode.release());
367     }
368 }
369
370 PassRefPtr<Element> HTMLConstructionSite::createElement(AtomicHTMLToken& token, const AtomicString& namespaceURI)
371 {
372     QualifiedName tagName(nullAtom, token.name(), namespaceURI);
373     RefPtr<Element> element = currentNode()->document()->createElement(tagName, true);
374     element->setAttributeMap(token.takeAtributes(), m_fragmentScriptingPermission);
375     return element.release();
376 }
377
378 PassRefPtr<Element> HTMLConstructionSite::createHTMLElement(AtomicHTMLToken& token)
379 {
380     QualifiedName tagName(nullAtom, token.name(), xhtmlNamespaceURI);
381     // FIXME: This can't use HTMLConstructionSite::createElement because we
382     // have to pass the current form element.  We should rework form association
383     // to occur after construction to allow better code sharing here.
384     RefPtr<Element> element = HTMLElementFactory::createHTMLElement(tagName, currentNode()->document(), form(), true);
385     element->setAttributeMap(token.takeAtributes(), m_fragmentScriptingPermission);
386     ASSERT(element->isHTMLElement());
387     return element.release();
388 }
389
390 PassRefPtr<Element> HTMLConstructionSite::createHTMLElementFromElementRecord(HTMLElementStack::ElementRecord* record)
391 {
392     return createHTMLElementFromSavedElement(record->element());
393 }
394
395 namespace {
396
397 PassRefPtr<NamedNodeMap> cloneAttributes(Element* element)
398 {
399     NamedNodeMap* attributes = element->attributes(true);
400     if (!attributes)
401         return 0;
402
403     RefPtr<NamedNodeMap> newAttributes = NamedNodeMap::create();
404     for (size_t i = 0; i < attributes->length(); ++i) {
405         Attribute* attribute = attributes->attributeItem(i);
406         RefPtr<Attribute> clone = Attribute::createMapped(attribute->name(), attribute->value());
407         newAttributes->addAttribute(clone);
408     }
409     return newAttributes.release();
410 }
411
412 }
413
414 PassRefPtr<Element> HTMLConstructionSite::createHTMLElementFromSavedElement(Element* element)
415 {
416     // FIXME: This method is wrong.  We should be using the original token.
417     // Using an Element* causes us to fail examples like this:
418     // <b id="1"><p><script>document.getElementById("1").id = "2"</script></p>TEXT</b>
419     // When reconstructTheActiveFormattingElements calls this method to open
420     // a second <b> tag to wrap TEXT, it will have id "2", even though the HTML5
421     // spec implies it should be "1".  Minefield matches the HTML5 spec here.
422
423     ASSERT(element->isHTMLElement()); // otherwise localName() might be wrong.
424     AtomicHTMLToken fakeToken(HTMLTokenTypes::StartTag, element->localName(), cloneAttributes(element));
425     return createHTMLElement(fakeToken);
426 }
427
428 bool HTMLConstructionSite::indexOfFirstUnopenFormattingElement(unsigned& firstUnopenElementIndex) const
429 {
430     if (m_activeFormattingElements.isEmpty())
431         return false;
432     unsigned index = m_activeFormattingElements.size();
433     do {
434         --index;
435         const HTMLFormattingElementList::Entry& entry = m_activeFormattingElements.at(index);
436         if (entry.isMarker() || m_openElements.contains(entry.element())) {
437             firstUnopenElementIndex = index + 1;
438             return firstUnopenElementIndex < m_activeFormattingElements.size();
439         }
440     } while (index);
441     firstUnopenElementIndex = index;
442     return true;
443 }
444
445 void HTMLConstructionSite::reconstructTheActiveFormattingElements()
446 {
447     unsigned firstUnopenElementIndex;
448     if (!indexOfFirstUnopenFormattingElement(firstUnopenElementIndex))
449         return;
450
451     unsigned unopenEntryIndex = firstUnopenElementIndex;
452     ASSERT(unopenEntryIndex < m_activeFormattingElements.size());
453     for (; unopenEntryIndex < m_activeFormattingElements.size(); ++unopenEntryIndex) {
454         HTMLFormattingElementList::Entry& unopenedEntry = m_activeFormattingElements.at(unopenEntryIndex);
455         RefPtr<Element> reconstructed = createHTMLElementFromSavedElement(unopenedEntry.element());
456         m_openElements.push(attachToCurrent(reconstructed.release()));
457         unopenedEntry.replaceElement(currentElement());
458     }
459 }
460
461 void HTMLConstructionSite::generateImpliedEndTagsWithExclusion(const AtomicString& tagName)
462 {
463     while (hasImpliedEndTag(currentNode()) && !currentNode()->hasLocalName(tagName))
464         m_openElements.pop();
465 }
466
467 void HTMLConstructionSite::generateImpliedEndTags()
468 {
469     while (hasImpliedEndTag(currentNode()))
470         m_openElements.pop();
471 }
472
473 void HTMLConstructionSite::findFosterSite(AttachmentSite& site)
474 {
475     HTMLElementStack::ElementRecord* lastTableElementRecord = m_openElements.topmost(tableTag.localName());
476     if (lastTableElementRecord) {
477         Element* lastTableElement = lastTableElementRecord->element();
478         if (ContainerNode* parent = lastTableElement->parentNode()) {
479             site.parent = parent;
480             site.nextChild = lastTableElement;
481             return;
482         }
483         site.parent = lastTableElementRecord->next()->element();
484         site.nextChild = 0;
485         return;
486     }
487     // Fragment case
488     site.parent = m_openElements.rootNode(); // DocumentFragment
489     site.nextChild = 0;
490 }
491
492 bool HTMLConstructionSite::shouldFosterParent() const
493 {
494     return m_redirectAttachToFosterParent
495         && currentNode()->isElementNode()
496         && causesFosterParenting(currentElement()->tagQName());
497 }
498
499 void HTMLConstructionSite::fosterParent(Node* node)
500 {
501     AttachmentSite site;
502     findFosterSite(site);
503     attachAtSite(site, node);
504 }
505
506 }