2011-03-02 Sheriff Bot <webkit.review.bot@gmail.com>
[WebKit-https.git] / Source / WebCore / html / parser / HTMLConstructionSite.cpp
1 /*
2  * Copyright (C) 2010 Google, Inc. All Rights Reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY GOOGLE INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL GOOGLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "HTMLTreeBuilder.h"
28
29 #include "Comment.h"
30 #include "DocumentFragment.h"
31 #include "DocumentType.h"
32 #include "Element.h"
33 #include "Frame.h"
34 #include "HTMLDocument.h"
35 #include "HTMLElementFactory.h"
36 #include "HTMLFormElement.h"
37 #include "HTMLHtmlElement.h"
38 #include "HTMLNames.h"
39 #include "HTMLScriptElement.h"
40 #include "HTMLToken.h"
41 #include "HTMLTokenizer.h"
42 #include "LocalizedStrings.h"
43 #if ENABLE(MATHML)
44 #include "MathMLNames.h"
45 #endif
46 #include "NotImplemented.h"
47 #if ENABLE(SVG)
48 #include "SVGNames.h"
49 #endif
50 #include "ScriptController.h"
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(Element* element)
62 {
63     return element->hasTagName(ddTag)
64         || element->hasTagName(dtTag)
65         || element->hasTagName(liTag)
66         || element->hasTagName(optionTag)
67         || element->hasTagName(optgroupTag)
68         || element->hasTagName(pTag)
69         || element->hasTagName(rpTag)
70         || element->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* parent, PassRefPtr<ChildType> prpChild)
86 {
87     RefPtr<ChildType> child = prpChild;
88
89     // FIXME: It's confusing that HTMLConstructionSite::attach does the magic
90     // redirection to the foster parent but HTMLConstructionSite::attachAtSite
91     // doesn't.  It feels like we're missing a concept somehow.
92     if (shouldFosterParent()) {
93         fosterParent(child.get());
94         ASSERT(child->attached() || !child->parentNode() || !child->parentNode()->attached());
95         return child.release();
96     }
97
98     parent->parserAddChild(child);
99
100     // An event handler (DOM Mutation, beforeload, et al.) could have removed
101     // the child, in which case we shouldn't try attaching it.
102     if (!child->parentNode())
103         return child.release();
104
105     // It's slightly unfortunate that we need to hold a reference to child
106     // here to call attach().  We should investigate whether we can rely on
107     // |parent| to hold a ref at this point.  In the common case (at least
108     // for elements), however, we'll get to use this ref in the stack of
109     // open elements.
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)
134     : m_document(document)
135     , m_attachmentRoot(document)
136     , m_fragmentScriptingPermission(FragmentScriptingAllowed)
137     , m_isParsingFragment(false)
138     , m_redirectAttachToFosterParent(false)
139 {
140 }
141
142 HTMLConstructionSite::HTMLConstructionSite(DocumentFragment* fragment, FragmentScriptingPermission scriptingPermission)
143     : m_document(fragment->document())
144     , m_attachmentRoot(fragment)
145     , m_fragmentScriptingPermission(scriptingPermission)
146     , m_isParsingFragment(true)
147     , m_redirectAttachToFosterParent(false)
148 {
149 }
150
151 HTMLConstructionSite::~HTMLConstructionSite()
152 {
153 }
154
155 void HTMLConstructionSite::detach()
156 {
157     m_document = 0;
158     m_attachmentRoot = 0;
159 }
160
161 void HTMLConstructionSite::setForm(HTMLFormElement* form)
162 {
163     // This method should only be needed for HTMLTreeBuilder in the fragment case.
164     ASSERT(!m_form);
165     m_form = form;
166 }
167
168 PassRefPtr<HTMLFormElement> HTMLConstructionSite::takeForm()
169 {
170     return m_form.release();
171 }
172
173 void HTMLConstructionSite::dispatchDocumentElementAvailableIfNeeded()
174 {
175     ASSERT(m_document);
176     if (m_document->frame() && !m_isParsingFragment)
177         m_document->frame()->loader()->dispatchDocumentElementAvailable();
178 }
179
180 void HTMLConstructionSite::insertHTMLHtmlStartTagBeforeHTML(AtomicHTMLToken& token)
181 {
182     RefPtr<HTMLHtmlElement> element = HTMLHtmlElement::create(m_document);
183     element->setAttributeMap(token.takeAtributes(), m_fragmentScriptingPermission);
184     m_openElements.pushHTMLHtmlElement(attach<Element>(m_attachmentRoot, element.get()));
185 #if ENABLE(OFFLINE_WEB_APPLICATIONS)
186     element->insertedByParser();
187 #endif
188     dispatchDocumentElementAvailableIfNeeded();
189 }
190
191 void HTMLConstructionSite::mergeAttributesFromTokenIntoElement(AtomicHTMLToken& token, Element* element)
192 {
193     if (!token.attributes())
194         return;
195
196     NamedNodeMap* attributes = element->attributes(false);
197     for (unsigned i = 0; i < token.attributes()->length(); ++i) {
198         Attribute* attribute = token.attributes()->attributeItem(i);
199         if (!attributes->getAttributeItem(attribute->name()))
200             element->setAttribute(attribute->name(), attribute->value());
201     }
202 }
203
204 void HTMLConstructionSite::insertHTMLHtmlStartTagInBody(AtomicHTMLToken& token)
205 {
206     // FIXME: parse error
207     mergeAttributesFromTokenIntoElement(token, m_openElements.htmlElement());
208 }
209
210 void HTMLConstructionSite::insertHTMLBodyStartTagInBody(AtomicHTMLToken& token)
211 {
212     // FIXME: parse error
213     mergeAttributesFromTokenIntoElement(token, m_openElements.bodyElement());
214 }
215
216 void HTMLConstructionSite::insertDoctype(AtomicHTMLToken& token)
217 {
218     ASSERT(token.type() == HTMLToken::DOCTYPE);
219     attach(m_attachmentRoot, DocumentType::create(m_document, token.name(), String::adopt(token.publicIdentifier()), String::adopt(token.systemIdentifier())));
220     
221     // DOCTYPE nodes are only processed when parsing fragments w/o contextElements, which
222     // never occurs.  However, if we ever chose to support such, this code is subtly wrong,
223     // because context-less fragments can determine their own quirks mode, and thus change
224     // parsing rules (like <p> inside <table>).  For now we ASSERT that we never hit this code
225     // in a fragment, as changing the owning document's compatibility mode would be wrong.
226     ASSERT(!m_isParsingFragment);
227     if (m_isParsingFragment)
228         return;
229     
230     if (token.forceQuirks())
231         m_document->setCompatibilityMode(Document::QuirksMode);
232     else
233         m_document->setCompatibilityModeFromDoctype();
234 }
235
236 void HTMLConstructionSite::insertComment(AtomicHTMLToken& token)
237 {
238     ASSERT(token.type() == HTMLToken::Comment);
239     attach(currentElement(), Comment::create(currentElement()->document(), token.comment()));
240 }
241
242 void HTMLConstructionSite::insertCommentOnDocument(AtomicHTMLToken& token)
243 {
244     ASSERT(token.type() == HTMLToken::Comment);
245     attach(m_attachmentRoot, Comment::create(m_document, token.comment()));
246 }
247
248 void HTMLConstructionSite::insertCommentOnHTMLHtmlElement(AtomicHTMLToken& token)
249 {
250     ASSERT(token.type() == HTMLToken::Comment);
251     Element* parent = m_openElements.htmlElement();
252     attach(parent, Comment::create(parent->document(), token.comment()));
253 }
254
255 PassRefPtr<Element> HTMLConstructionSite::attachToCurrent(PassRefPtr<Element> child)
256 {
257     return attach(currentElement(), child);
258 }
259
260 void HTMLConstructionSite::insertHTMLHeadElement(AtomicHTMLToken& token)
261 {
262     ASSERT(!shouldFosterParent());
263     m_head = attachToCurrent(createHTMLElement(token));
264     m_openElements.pushHTMLHeadElement(m_head);
265 }
266
267 void HTMLConstructionSite::insertHTMLBodyElement(AtomicHTMLToken& token)
268 {
269     ASSERT(!shouldFosterParent());
270     m_openElements.pushHTMLBodyElement(attachToCurrent(createHTMLElement(token)));
271 }
272
273 void HTMLConstructionSite::insertHTMLFormElement(AtomicHTMLToken& token, bool isDemoted)
274 {
275     RefPtr<Element> element = createHTMLElement(token);
276     ASSERT(element->hasTagName(formTag));
277     RefPtr<HTMLFormElement> form = static_pointer_cast<HTMLFormElement>(element.release());
278     form->setDemoted(isDemoted);
279     m_openElements.push(attachToCurrent(form.release()));
280     ASSERT(currentElement()->isHTMLElement());
281     ASSERT(currentElement()->hasTagName(formTag));
282     m_form = static_cast<HTMLFormElement*>(currentElement());
283 }
284
285 void HTMLConstructionSite::insertHTMLElement(AtomicHTMLToken& token)
286 {
287     m_openElements.push(attachToCurrent(createHTMLElement(token)));
288 }
289
290 void HTMLConstructionSite::insertSelfClosingHTMLElement(AtomicHTMLToken& token)
291 {
292     ASSERT(token.type() == HTMLToken::StartTag);
293     RefPtr<Element> element = attachToCurrent(createHTMLElement(token));
294     // Normally HTMLElementStack is responsible for calling finishParsingChildren,
295     // but self-closing elements are never in the element stack so the stack
296     // doesn't get a chance to tell them that we're done parsing their children.
297     element->finishParsingChildren();
298     // FIXME: Do we want to acknowledge the token's self-closing flag?
299     // http://www.whatwg.org/specs/web-apps/current-work/multipage/tokenization.html#acknowledge-self-closing-flag
300 }
301
302 void HTMLConstructionSite::insertFormattingElement(AtomicHTMLToken& token)
303 {
304     // http://www.whatwg.org/specs/web-apps/current-work/multipage/parsing.html#the-stack-of-open-elements
305     // Possible active formatting elements include:
306     // a, b, big, code, em, font, i, nobr, s, small, strike, strong, tt, and u.
307     insertHTMLElement(token);
308     m_activeFormattingElements.append(currentElement());
309 }
310
311 void HTMLConstructionSite::insertScriptElement(AtomicHTMLToken& token)
312 {
313     RefPtr<HTMLScriptElement> element = HTMLScriptElement::create(scriptTag, currentElement()->document(), true);
314     if (m_fragmentScriptingPermission == FragmentScriptingAllowed)
315         element->setAttributeMap(token.takeAtributes(), m_fragmentScriptingPermission);
316     m_openElements.push(attachToCurrent(element.release()));
317 }
318
319 void HTMLConstructionSite::insertForeignElement(AtomicHTMLToken& token, const AtomicString& namespaceURI)
320 {
321     ASSERT(token.type() == HTMLToken::StartTag);
322     notImplemented(); // parseError when xmlns or xmlns:xlink are wrong.
323
324     RefPtr<Element> element = attachToCurrent(createElement(token, namespaceURI));
325     if (!token.selfClosing())
326         m_openElements.push(element);
327 }
328
329 void HTMLConstructionSite::insertTextNode(const String& characters)
330 {
331     AttachmentSite site;
332     site.parent = currentElement();
333     site.nextChild = 0;
334     if (shouldFosterParent())
335         findFosterSite(site);
336
337     Node* previousChild = site.nextChild ? site.nextChild->previousSibling() : site.parent->lastChild();
338     if (previousChild && previousChild->isTextNode()) {
339         // FIXME: We're only supposed to append to this text node if it
340         // was the last text node inserted by the parser.
341         CharacterData* textNode = static_cast<CharacterData*>(previousChild);
342         textNode->parserAppendData(characters);
343         return;
344     }
345
346     attachAtSite(site, Text::create(site.parent->document(), characters));
347 }
348
349 PassRefPtr<Element> HTMLConstructionSite::createElement(AtomicHTMLToken& token, const AtomicString& namespaceURI)
350 {
351     QualifiedName tagName(nullAtom, token.name(), namespaceURI);
352     RefPtr<Element> element = currentElement()->document()->createElement(tagName, true);
353     element->setAttributeMap(token.takeAtributes(), m_fragmentScriptingPermission);
354     return element.release();
355 }
356
357 PassRefPtr<Element> HTMLConstructionSite::createHTMLElement(AtomicHTMLToken& token)
358 {
359     QualifiedName tagName(nullAtom, token.name(), xhtmlNamespaceURI);
360     // FIXME: This can't use HTMLConstructionSite::createElement because we
361     // have to pass the current form element.  We should rework form association
362     // to occur after construction to allow better code sharing here.
363     RefPtr<Element> element = HTMLElementFactory::createHTMLElement(tagName, currentElement()->document(), form(), true);
364     element->setAttributeMap(token.takeAtributes(), m_fragmentScriptingPermission);
365     ASSERT(element->isHTMLElement());
366     return element.release();
367 }
368
369 PassRefPtr<Element> HTMLConstructionSite::createHTMLElementFromElementRecord(HTMLElementStack::ElementRecord* record)
370 {
371     return createHTMLElementFromSavedElement(record->element());
372 }
373
374 namespace {
375
376 PassRefPtr<NamedNodeMap> cloneAttributes(Element* element)
377 {
378     NamedNodeMap* attributes = element->attributes(true);
379     if (!attributes)
380         return 0;
381
382     RefPtr<NamedNodeMap> newAttributes = NamedNodeMap::create();
383     for (size_t i = 0; i < attributes->length(); ++i) {
384         Attribute* attribute = attributes->attributeItem(i);
385         RefPtr<Attribute> clone = Attribute::createMapped(attribute->name(), attribute->value());
386         newAttributes->addAttribute(clone);
387     }
388     return newAttributes.release();
389 }
390
391 }
392
393 PassRefPtr<Element> HTMLConstructionSite::createHTMLElementFromSavedElement(Element* element)
394 {
395     // FIXME: This method is wrong.  We should be using the original token.
396     // Using an Element* causes us to fail examples like this:
397     // <b id="1"><p><script>document.getElementById("1").id = "2"</script></p>TEXT</b>
398     // When reconstructTheActiveFormattingElements calls this method to open
399     // a second <b> tag to wrap TEXT, it will have id "2", even though the HTML5
400     // spec implies it should be "1".  Minefield matches the HTML5 spec here.
401
402     ASSERT(element->isHTMLElement()); // otherwise localName() might be wrong.
403     AtomicHTMLToken fakeToken(HTMLToken::StartTag, element->localName(), cloneAttributes(element));
404     return createHTMLElement(fakeToken);
405 }
406
407 bool HTMLConstructionSite::indexOfFirstUnopenFormattingElement(unsigned& firstUnopenElementIndex) const
408 {
409     if (m_activeFormattingElements.isEmpty())
410         return false;
411     unsigned index = m_activeFormattingElements.size();
412     do {
413         --index;
414         const HTMLFormattingElementList::Entry& entry = m_activeFormattingElements.at(index);
415         if (entry.isMarker() || m_openElements.contains(entry.element())) {
416             firstUnopenElementIndex = index + 1;
417             return firstUnopenElementIndex < m_activeFormattingElements.size();
418         }
419     } while (index);
420     firstUnopenElementIndex = index;
421     return true;
422 }
423
424 void HTMLConstructionSite::reconstructTheActiveFormattingElements()
425 {
426     unsigned firstUnopenElementIndex;
427     if (!indexOfFirstUnopenFormattingElement(firstUnopenElementIndex))
428         return;
429
430     unsigned unopenEntryIndex = firstUnopenElementIndex;
431     ASSERT(unopenEntryIndex < m_activeFormattingElements.size());
432     for (; unopenEntryIndex < m_activeFormattingElements.size(); ++unopenEntryIndex) {
433         HTMLFormattingElementList::Entry& unopenedEntry = m_activeFormattingElements.at(unopenEntryIndex);
434         RefPtr<Element> reconstructed = createHTMLElementFromSavedElement(unopenedEntry.element());
435         m_openElements.push(attachToCurrent(reconstructed.release()));
436         unopenedEntry.replaceElement(currentElement());
437     }
438 }
439
440 void HTMLConstructionSite::generateImpliedEndTagsWithExclusion(const AtomicString& tagName)
441 {
442     while (hasImpliedEndTag(currentElement()) && !currentElement()->hasLocalName(tagName))
443         m_openElements.pop();
444 }
445
446 void HTMLConstructionSite::generateImpliedEndTags()
447 {
448     while (hasImpliedEndTag(currentElement()))
449         m_openElements.pop();
450 }
451
452 void HTMLConstructionSite::findFosterSite(AttachmentSite& site)
453 {
454     HTMLElementStack::ElementRecord* lastTableElementRecord = m_openElements.topmost(tableTag.localName());
455     if (lastTableElementRecord) {
456         Element* lastTableElement = lastTableElementRecord->element();
457         if (ContainerNode* parent = lastTableElement->parentNode()) {
458             site.parent = parent;
459             site.nextChild = lastTableElement;
460             return;
461         }
462         site.parent = lastTableElementRecord->next()->element();
463         site.nextChild = 0;
464         return;
465     }
466     // Fragment case
467     site.parent = m_openElements.bottom(); // <html> element
468     site.nextChild = 0;
469 }
470
471 bool HTMLConstructionSite::shouldFosterParent() const
472 {
473     return m_redirectAttachToFosterParent
474         && causesFosterParenting(currentElement()->tagQName());
475 }
476
477 void HTMLConstructionSite::fosterParent(Node* node)
478 {
479     AttachmentSite site;
480     findFosterSite(site);
481     attachAtSite(site, node);
482 }
483
484 }