2010-08-20 Tony Gentilcore <tonyg@chromium.org>
[WebKit-https.git] / WebCore / html / 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 "LegacyHTMLTreeBuilder.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 "ScriptController.h"
52 #include "Settings.h"
53 #include "Text.h"
54 #include <wtf/UnusedParam.h>
55
56 namespace WebCore {
57
58 using namespace HTMLNames;
59
60 namespace {
61
62 bool hasImpliedEndTag(Element* element)
63 {
64     return element->hasTagName(ddTag)
65         || element->hasTagName(dtTag)
66         || element->hasTagName(liTag)
67         || element->hasTagName(optionTag)
68         || element->hasTagName(optgroupTag)
69         || element->hasTagName(pTag)
70         || element->hasTagName(rpTag)
71         || element->hasTagName(rtTag);
72 }
73
74 bool causesFosterParenting(const QualifiedName& tagName)
75 {
76     return tagName == tableTag
77         || tagName == tbodyTag
78         || tagName == tfootTag
79         || tagName == theadTag
80         || tagName == trTag;
81 }
82
83 } // namespace
84
85 template<typename ChildType>
86 PassRefPtr<ChildType> HTMLConstructionSite::attach(Node* parent, PassRefPtr<ChildType> prpChild)
87 {
88     RefPtr<ChildType> child = prpChild;
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->parent() || !child->parent()->attached());
96         return child.release();
97     }
98
99     parent->parserAddChild(child);
100
101     // An event handler (DOM Mutation, beforeload, et al.) could have removed
102     // the child, in which case we shouldn't try attaching it.
103     if (!child->parentNode())
104         return child.release();
105
106     // It's slightly unfortunate that we need to hold a reference to child
107     // here to call attach().  We should investigate whether we can rely on
108     // |parent| to hold a ref at this point.  In the common case (at least
109     // for elements), however, we'll get to use this ref in the stack of
110     // open elements.
111     if (parent->attached() && !child->attached())
112         child->attach();
113     return child.release();
114 }
115
116 void HTMLConstructionSite::attachAtSite(const AttachmentSite& site, PassRefPtr<Node> prpChild)
117 {
118     RefPtr<Node> child = prpChild;
119
120     if (site.nextChild) {
121         site.parent->parserInsertBefore(child, site.nextChild);
122         if (site.parent->attached() && !child->attached())
123             child->attach();
124         return;
125     }
126     site.parent->parserAddChild(child);
127     // It's slightly unfortunate that we need to hold a reference to child
128     // here to call attach().  We should investigate whether we can rely on
129     // |site.parent| to hold a ref at this point.
130     if (site.parent->attached() && !child->attached())
131         child->attach();
132 }
133
134 HTMLConstructionSite::HTMLConstructionSite(Document* document, FragmentScriptingPermission scriptingPermission, bool isParsingFragment)
135     : m_document(document)
136     , m_fragmentScriptingPermission(scriptingPermission)
137     , m_isParsingFragment(isParsingFragment)
138     , m_redirectAttachToFosterParent(false)
139 {
140 }
141
142 HTMLConstructionSite::~HTMLConstructionSite()
143 {
144 }
145
146 void HTMLConstructionSite::detach()
147 {
148     m_document = 0;
149 }
150
151 void HTMLConstructionSite::setForm(HTMLFormElement* form)
152 {
153     // This method should only be needed for HTMLTreeBuilder in the fragment case.
154     ASSERT(!m_form);
155     m_form = form;
156 }
157
158 PassRefPtr<HTMLFormElement> HTMLConstructionSite::takeForm()
159 {
160     return m_form.release();
161 }
162
163 void HTMLConstructionSite::dispatchDocumentElementAvailableIfNeeded()
164 {
165     ASSERT(m_document);
166     if (m_document->frame() && !m_isParsingFragment)
167         m_document->frame()->loader()->dispatchDocumentElementAvailable();
168 }
169
170 void HTMLConstructionSite::insertHTMLHtmlStartTagBeforeHTML(AtomicHTMLToken& token)
171 {
172     RefPtr<Element> element = HTMLHtmlElement::create(m_document);
173     element->setAttributeMap(token.takeAtributes(), m_fragmentScriptingPermission);
174     m_openElements.pushHTMLHtmlElement(attach(m_document, element.release()));
175     dispatchDocumentElementAvailableIfNeeded();
176 }
177
178 void HTMLConstructionSite::mergeAttributesFromTokenIntoElement(AtomicHTMLToken& token, Element* element)
179 {
180     if (!token.attributes())
181         return;
182
183     NamedNodeMap* attributes = element->attributes(false);
184     for (unsigned i = 0; i < token.attributes()->length(); ++i) {
185         Attribute* attribute = token.attributes()->attributeItem(i);
186         if (!attributes->getAttributeItem(attribute->name()))
187             element->setAttribute(attribute->name(), attribute->value());
188     }
189 }
190
191 void HTMLConstructionSite::insertHTMLHtmlStartTagInBody(AtomicHTMLToken& token)
192 {
193     // FIXME: parse error
194     mergeAttributesFromTokenIntoElement(token, m_openElements.htmlElement());
195 }
196
197 void HTMLConstructionSite::insertHTMLBodyStartTagInBody(AtomicHTMLToken& token)
198 {
199     // FIXME: parse error
200     mergeAttributesFromTokenIntoElement(token, m_openElements.bodyElement());
201 }
202
203 void HTMLConstructionSite::insertDoctype(AtomicHTMLToken& token)
204 {
205     ASSERT(token.type() == HTMLToken::DOCTYPE);
206     attach(m_document, DocumentType::create(m_document, token.name(), String::adopt(token.publicIdentifier()), String::adopt(token.systemIdentifier())));
207     // FIXME: Move quirks mode detection from DocumentType element to here.
208     notImplemented();
209     if (token.forceQuirks())
210         m_document->setParseMode(Document::Compat);
211 }
212
213 void HTMLConstructionSite::insertComment(AtomicHTMLToken& token)
214 {
215     ASSERT(token.type() == HTMLToken::Comment);
216     attach(currentElement(), Comment::create(m_document, token.comment()));
217 }
218
219 void HTMLConstructionSite::insertCommentOnDocument(AtomicHTMLToken& token)
220 {
221     ASSERT(token.type() == HTMLToken::Comment);
222     attach(m_document, Comment::create(m_document, token.comment()));
223 }
224
225 void HTMLConstructionSite::insertCommentOnHTMLHtmlElement(AtomicHTMLToken& token)
226 {
227     ASSERT(token.type() == HTMLToken::Comment);
228     attach(m_openElements.htmlElement(), Comment::create(m_document, token.comment()));
229 }
230
231 PassRefPtr<Element> HTMLConstructionSite::attachToCurrent(PassRefPtr<Element> child)
232 {
233     return attach(currentElement(), child);
234 }
235
236 void HTMLConstructionSite::insertHTMLHtmlElement(AtomicHTMLToken& token)
237 {
238     ASSERT(!shouldFosterParent());
239     m_openElements.pushHTMLHtmlElement(attachToCurrent(createHTMLElement(token)));
240     dispatchDocumentElementAvailableIfNeeded();
241 }
242
243 void HTMLConstructionSite::insertHTMLHeadElement(AtomicHTMLToken& token)
244 {
245     ASSERT(!shouldFosterParent());
246     m_head = attachToCurrent(createHTMLElement(token));
247     m_openElements.pushHTMLHeadElement(m_head);
248 }
249
250 void HTMLConstructionSite::insertHTMLBodyElement(AtomicHTMLToken& token)
251 {
252     ASSERT(!shouldFosterParent());
253     m_openElements.pushHTMLBodyElement(attachToCurrent(createHTMLElement(token)));
254 }
255
256 void HTMLConstructionSite::insertHTMLFormElement(AtomicHTMLToken& token)
257 {
258     insertHTMLElement(token);
259     ASSERT(currentElement()->isHTMLElement());
260     ASSERT(currentElement()->hasTagName(formTag));
261     m_form = static_cast<HTMLFormElement*>(currentElement());
262 }
263
264 void HTMLConstructionSite::insertHTMLElement(AtomicHTMLToken& token)
265 {
266     m_openElements.push(attachToCurrent(createHTMLElement(token)));
267 }
268
269 void HTMLConstructionSite::insertSelfClosingHTMLElement(AtomicHTMLToken& token)
270 {
271     ASSERT(token.type() == HTMLToken::StartTag);
272     RefPtr<Element> element = attachToCurrent(createHTMLElement(token));
273     // Normally HTMLElementStack is responsible for calling finishParsingChildren,
274     // but self-closing elements are never in the element stack so the stack
275     // doesn't get a chance to tell them that we're done parsing their children.
276     element->finishParsingChildren();
277     // FIXME: Do we want to acknowledge the token's self-closing flag?
278     // http://www.whatwg.org/specs/web-apps/current-work/multipage/tokenization.html#acknowledge-self-closing-flag
279 }
280
281 void HTMLConstructionSite::insertFormattingElement(AtomicHTMLToken& token)
282 {
283     // http://www.whatwg.org/specs/web-apps/current-work/multipage/parsing.html#the-stack-of-open-elements
284     // Possible active formatting elements include:
285     // a, b, big, code, em, font, i, nobr, s, small, strike, strong, tt, and u.
286     insertHTMLElement(token);
287     m_activeFormattingElements.append(currentElement());
288 }
289
290 void HTMLConstructionSite::insertScriptElement(AtomicHTMLToken& token)
291 {
292     RefPtr<HTMLScriptElement> element = HTMLScriptElement::create(scriptTag, m_document, true);
293     element->setAttributeMap(token.takeAtributes(), m_fragmentScriptingPermission);
294     m_openElements.push(attachToCurrent(element.release()));
295 }
296
297 void HTMLConstructionSite::insertForeignElement(AtomicHTMLToken& token, const AtomicString& namespaceURI)
298 {
299     ASSERT(token.type() == HTMLToken::StartTag);
300     notImplemented(); // parseError when xmlns or xmlns:xlink are wrong.
301
302     RefPtr<Element> element = attachToCurrent(createElement(token, namespaceURI));
303     if (!token.selfClosing())
304         m_openElements.push(element);
305 }
306
307 void HTMLConstructionSite::insertTextNode(const String& characters)
308 {
309     AttachmentSite site;
310     site.parent = currentElement();
311     site.nextChild = 0;
312     if (shouldFosterParent())
313         findFosterSite(site);
314
315     Node* previousChild = site.nextChild ? site.nextChild->previousSibling() : site.parent->lastChild();
316     if (previousChild && previousChild->isTextNode()) {
317         // FIXME: We're only supposed to append to this text node if it
318         // was the last text node inserted by the parser.
319         CharacterData* textNode = static_cast<CharacterData*>(previousChild);
320         textNode->parserAppendData(characters);
321         return;
322     }
323
324     attachAtSite(site, Text::create(m_document, characters));
325 }
326
327 PassRefPtr<Element> HTMLConstructionSite::createElement(AtomicHTMLToken& token, const AtomicString& namespaceURI)
328 {
329     QualifiedName tagName(nullAtom, token.name(), namespaceURI);
330     RefPtr<Element> element = m_document->createElement(tagName, true);
331     element->setAttributeMap(token.takeAtributes(), m_fragmentScriptingPermission);
332     return element.release();
333 }
334
335 PassRefPtr<Element> HTMLConstructionSite::createHTMLElement(AtomicHTMLToken& token)
336 {
337     QualifiedName tagName(nullAtom, token.name(), xhtmlNamespaceURI);
338     // FIXME: This can't use HTMLConstructionSite::createElement because we
339     // have to pass the current form element.  We should rework form association
340     // to occur after construction to allow better code sharing here.
341     RefPtr<Element> element = HTMLElementFactory::createHTMLElement(tagName, m_document, form(), true);
342     element->setAttributeMap(token.takeAtributes(), m_fragmentScriptingPermission);
343     ASSERT(element->isHTMLElement());
344     return element.release();
345 }
346
347 PassRefPtr<Element> HTMLConstructionSite::createHTMLElementFromElementRecord(HTMLElementStack::ElementRecord* record)
348 {
349     return createHTMLElementFromSavedElement(record->element());
350 }
351
352 namespace {
353
354 PassRefPtr<NamedNodeMap> cloneAttributes(Element* element)
355 {
356     NamedNodeMap* attributes = element->attributes(true);
357     if (!attributes)
358         return 0;
359
360     RefPtr<NamedNodeMap> newAttributes = NamedNodeMap::create();
361     for (size_t i = 0; i < attributes->length(); ++i) {
362         Attribute* attribute = attributes->attributeItem(i);
363         RefPtr<Attribute> clone = Attribute::createMapped(attribute->name(), attribute->value());
364         newAttributes->addAttribute(clone);
365     }
366     return newAttributes.release();
367 }
368
369 }
370
371 PassRefPtr<Element> HTMLConstructionSite::createHTMLElementFromSavedElement(Element* element)
372 {
373     // FIXME: This method is wrong.  We should be using the original token.
374     // Using an Element* causes us to fail examples like this:
375     // <b id="1"><p><script>document.getElementById("1").id = "2"</script></p>TEXT</b>
376     // When reconstructTheActiveFormattingElements calls this method to open
377     // a second <b> tag to wrap TEXT, it will have id "2", even though the HTML5
378     // spec implies it should be "1".  Minefield matches the HTML5 spec here.
379
380     ASSERT(element->isHTMLElement()); // otherwise localName() might be wrong.
381     AtomicHTMLToken fakeToken(HTMLToken::StartTag, element->localName(), cloneAttributes(element));
382     return createHTMLElement(fakeToken);
383 }
384
385 bool HTMLConstructionSite::indexOfFirstUnopenFormattingElement(unsigned& firstUnopenElementIndex) const
386 {
387     if (m_activeFormattingElements.isEmpty())
388         return false;
389     unsigned index = m_activeFormattingElements.size();
390     do {
391         --index;
392         const HTMLFormattingElementList::Entry& entry = m_activeFormattingElements.at(index);
393         if (entry.isMarker() || m_openElements.contains(entry.element())) {
394             firstUnopenElementIndex = index + 1;
395             return firstUnopenElementIndex < m_activeFormattingElements.size();
396         }
397     } while (index);
398     firstUnopenElementIndex = index;
399     return true;
400 }
401
402 void HTMLConstructionSite::reconstructTheActiveFormattingElements()
403 {
404     unsigned firstUnopenElementIndex;
405     if (!indexOfFirstUnopenFormattingElement(firstUnopenElementIndex))
406         return;
407
408     unsigned unopenEntryIndex = firstUnopenElementIndex;
409     ASSERT(unopenEntryIndex < m_activeFormattingElements.size());
410     for (; unopenEntryIndex < m_activeFormattingElements.size(); ++unopenEntryIndex) {
411         HTMLFormattingElementList::Entry& unopenedEntry = m_activeFormattingElements.at(unopenEntryIndex);
412         RefPtr<Element> reconstructed = createHTMLElementFromSavedElement(unopenedEntry.element());
413         m_openElements.push(attachToCurrent(reconstructed.release()));
414         unopenedEntry.replaceElement(currentElement());
415     }
416 }
417
418 void HTMLConstructionSite::generateImpliedEndTagsWithExclusion(const AtomicString& tagName)
419 {
420     while (hasImpliedEndTag(currentElement()) && !currentElement()->hasLocalName(tagName))
421         m_openElements.pop();
422 }
423
424 void HTMLConstructionSite::generateImpliedEndTags()
425 {
426     while (hasImpliedEndTag(currentElement()))
427         m_openElements.pop();
428 }
429
430 void HTMLConstructionSite::findFosterSite(AttachmentSite& site)
431 {
432     HTMLElementStack::ElementRecord* lastTableElementRecord = m_openElements.topmost(tableTag.localName());
433     if (lastTableElementRecord) {
434         Element* lastTableElement = lastTableElementRecord->element();
435         if (Node* parent = lastTableElement->parent()) {
436             site.parent = parent;
437             site.nextChild = lastTableElement;
438             return;
439         }
440         site.parent = lastTableElementRecord->next()->element();
441         site.nextChild = 0;
442         return;
443     }
444     // Fragment case
445     site.parent = m_openElements.bottom(); // <html> element
446     site.nextChild = 0;
447 }
448
449 bool HTMLConstructionSite::shouldFosterParent() const
450 {
451     return m_redirectAttachToFosterParent
452         && causesFosterParenting(currentElement()->tagQName());
453 }
454
455 void HTMLConstructionSite::fosterParent(Node* node)
456 {
457     AttachmentSite site;
458     findFosterSite(site);
459     attachAtSite(site, node);
460 }
461
462 }