2010-07-13 Eric Seidel <eric@webkit.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 "HTMLHtmlElement.h"
36 #include "HTMLNames.h"
37 #include "HTMLScriptElement.h"
38 #include "HTMLToken.h"
39 #include "HTMLTokenizer.h"
40 #include "LegacyHTMLDocumentParser.h"
41 #include "LegacyHTMLTreeBuilder.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 } // namespace
74
75 template<typename ChildType>
76 PassRefPtr<ChildType> HTMLConstructionSite::attach(Node* parent, PassRefPtr<ChildType> prpChild)
77 {
78     RefPtr<ChildType> child = prpChild;
79
80     // FIXME: It's confusing that HTMLConstructionSite::attach does the magic
81     // redirection to the foster parent but HTMLConstructionSite::attachAtSite
82     // doesn't.  It feels like we're missing a concept somehow.
83     if (m_redirectAttachToFosterParent) {
84         fosterParent(child.get());
85         ASSERT(child->attached());
86         return child.release();
87     }
88
89     parent->parserAddChild(child);
90     // It's slightly unfortunate that we need to hold a reference to child
91     // here to call attach().  We should investigate whether we can rely on
92     // |parent| to hold a ref at this point.  In the common case (at least
93     // for elements), however, we'll get to use this ref in the stack of
94     // open elements.
95     ASSERT(parent->attached());
96     ASSERT(!child->attached());
97     child->attach();
98     return child.release();
99 }
100
101 void HTMLConstructionSite::attachAtSite(const AttachmentSite& site, PassRefPtr<Node> prpChild)
102 {
103     RefPtr<Node> child = prpChild;
104
105     if (site.nextChild) {
106         // FIXME: We need an insertElement which does not send mutation events.
107         ExceptionCode ec = 0;
108         site.parent->insertBefore(child, site.nextChild, ec);
109         ASSERT(!ec);
110         ASSERT(site.parent->attached());
111         if (!child->attached())
112             child->attach();
113         return;
114     }
115     site.parent->parserAddChild(child);
116     // It's slightly unfortunate that we need to hold a reference to child
117     // here to call attach().  We should investigate whether we can rely on
118     // |site.parent| to hold a ref at this point.
119     ASSERT(site.parent->attached());
120     if (!child->attached())
121         child->attach();
122 }
123
124 HTMLConstructionSite::HTMLConstructionSite(Document* document, FragmentScriptingPermission scriptingPermission)
125     : m_document(document)
126     , m_fragmentScriptingPermission(scriptingPermission)
127     , m_redirectAttachToFosterParent(false)
128 {
129 }
130
131 HTMLConstructionSite::~HTMLConstructionSite()
132 {
133 }
134
135 void HTMLConstructionSite::insertHTMLHtmlStartTagBeforeHTML(AtomicHTMLToken& token)
136 {
137     RefPtr<Element> element = HTMLHtmlElement::create(m_document);
138     element->setAttributeMap(token.takeAtributes(), m_fragmentScriptingPermission);
139     m_openElements.pushHTMLHtmlElement(attach(m_document, element.release()));
140 }
141
142 void HTMLConstructionSite::mergeAttributesFromTokenIntoElement(AtomicHTMLToken& token, Element* element)
143 {
144     if (!token.attributes())
145         return;
146
147     NamedNodeMap* attributes = element->attributes(false);
148     for (unsigned i = 0; i < token.attributes()->length(); ++i) {
149         Attribute* attribute = token.attributes()->attributeItem(i);
150         if (!attributes->getAttributeItem(attribute->name()))
151             element->setAttribute(attribute->name(), attribute->value());
152     }
153 }
154
155 void HTMLConstructionSite::insertHTMLHtmlStartTagInBody(AtomicHTMLToken& token)
156 {
157     // FIXME: parse error
158     mergeAttributesFromTokenIntoElement(token, m_openElements.htmlElement());
159 }
160
161 void HTMLConstructionSite::insertHTMLBodyStartTagInBody(AtomicHTMLToken& token)
162 {
163     // FIXME: parse error
164     notImplemented(); // fragment case
165     mergeAttributesFromTokenIntoElement(token, m_openElements.bodyElement());
166 }
167
168 void HTMLConstructionSite::insertDoctype(AtomicHTMLToken& token)
169 {
170     ASSERT(token.type() == HTMLToken::DOCTYPE);
171     attach(m_document, DocumentType::create(m_document, token.name(), String::adopt(token.publicIdentifier()), String::adopt(token.systemIdentifier())));
172     // FIXME: Move quirks mode detection from DocumentType element to here.
173     notImplemented();
174     if (token.forceQuirks())
175         m_document->setParseMode(Document::Compat);
176 }
177
178 void HTMLConstructionSite::insertComment(AtomicHTMLToken& token)
179 {
180     ASSERT(token.type() == HTMLToken::Comment);
181     attach(currentElement(), Comment::create(m_document, token.comment()));
182 }
183
184 void HTMLConstructionSite::insertCommentOnDocument(AtomicHTMLToken& token)
185 {
186     ASSERT(token.type() == HTMLToken::Comment);
187     attach(m_document, Comment::create(m_document, token.comment()));
188 }
189
190 void HTMLConstructionSite::insertCommentOnHTMLHtmlElement(AtomicHTMLToken& token)
191 {
192     ASSERT(token.type() == HTMLToken::Comment);
193     attach(m_openElements.htmlElement(), Comment::create(m_document, token.comment()));
194 }
195
196 PassRefPtr<Element> HTMLConstructionSite::attachToCurrent(PassRefPtr<Element> child)
197 {
198     return attach(currentElement(), child);
199 }
200
201 void HTMLConstructionSite::insertHTMLHtmlElement(AtomicHTMLToken& token)
202 {
203     ASSERT(!m_redirectAttachToFosterParent);
204     m_openElements.pushHTMLHtmlElement(attachToCurrent(createHTMLElement(token)));
205 }
206
207 void HTMLConstructionSite::insertHTMLHeadElement(AtomicHTMLToken& token)
208 {
209     ASSERT(!m_redirectAttachToFosterParent);
210     m_head = attachToCurrent(createHTMLElement(token));
211     m_openElements.pushHTMLHeadElement(m_head);
212 }
213
214 void HTMLConstructionSite::insertHTMLBodyElement(AtomicHTMLToken& token)
215 {
216     ASSERT(!m_redirectAttachToFosterParent);
217     m_openElements.pushHTMLBodyElement(attachToCurrent(createHTMLElement(token)));
218 }
219
220 void HTMLConstructionSite::insertHTMLElement(AtomicHTMLToken& token)
221 {
222     m_openElements.push(attachToCurrent(createHTMLElement(token)));
223 }
224
225 void HTMLConstructionSite::insertSelfClosingHTMLElement(AtomicHTMLToken& token)
226 {
227     ASSERT(token.type() == HTMLToken::StartTag);
228     attachToCurrent(createHTMLElement(token));
229     // FIXME: Do we want to acknowledge the token's self-closing flag?
230     // http://www.whatwg.org/specs/web-apps/current-work/multipage/tokenization.html#acknowledge-self-closing-flag
231 }
232
233 void HTMLConstructionSite::insertFormattingElement(AtomicHTMLToken& token)
234 {
235     // http://www.whatwg.org/specs/web-apps/current-work/multipage/parsing.html#the-stack-of-open-elements
236     // Possible active formatting elements include:
237     // a, b, big, code, em, font, i, nobr, s, small, strike, strong, tt, and u.
238     insertHTMLElement(token);
239     m_activeFormattingElements.append(currentElement());
240 }
241
242 void HTMLConstructionSite::insertScriptElement(AtomicHTMLToken& token)
243 {
244     RefPtr<HTMLScriptElement> element = HTMLScriptElement::create(scriptTag, m_document, true);
245     element->setAttributeMap(token.takeAtributes(), m_fragmentScriptingPermission);
246     m_openElements.push(attachToCurrent(element.release()));
247 }
248
249 void HTMLConstructionSite::insertForeignElement(AtomicHTMLToken& token, const AtomicString& namespaceURI)
250 {
251     ASSERT(token.type() == HTMLToken::StartTag);
252     notImplemented(); // parseError when xmlns or xmlns:xlink are wrong.
253
254     RefPtr<Element> element = attachToCurrent(createElement(token, namespaceURI));
255     if (!token.selfClosing())
256         m_openElements.push(element);
257 }
258
259 void HTMLConstructionSite::insertTextNode(const String& characters)
260 {
261     AttachmentSite site;
262     site.parent = currentElement();
263     site.nextChild = 0;
264     if (m_redirectAttachToFosterParent)
265         findFosterSite(site);
266
267     Node* previousChild = site.nextChild ? site.nextChild->previousSibling() : site.parent->lastChild();
268     if (previousChild && previousChild->isTextNode()) {
269         // FIXME: We're only supposed to append to this text node if it
270         // was the last text node inserted by the parser.
271         CharacterData* textNode = static_cast<CharacterData*>(previousChild);
272         textNode->parserAppendData(characters);
273         return;
274     }
275
276     attachAtSite(site, Text::create(m_document, characters));
277 }
278
279 PassRefPtr<Element> HTMLConstructionSite::createElement(AtomicHTMLToken& token, const AtomicString& namespaceURI)
280 {
281     QualifiedName tagName(nullAtom, token.name(), namespaceURI);
282     RefPtr<Element> element = m_document->createElement(tagName, true);
283     element->setAttributeMap(token.takeAtributes(), m_fragmentScriptingPermission);
284     return element.release();
285 }
286
287 PassRefPtr<Element> HTMLConstructionSite::createHTMLElement(AtomicHTMLToken& token)
288 {
289     RefPtr<Element> element = createElement(token, xhtmlNamespaceURI);
290     ASSERT(element->isHTMLElement());
291     return element.release();
292 }
293
294 PassRefPtr<Element> HTMLConstructionSite::createHTMLElementFromElementRecord(HTMLElementStack::ElementRecord* record)
295 {
296     // FIXME: This will change to use
297     // return createHTMLElementFromSavedElement(record->element());
298     // in a later patch once tested.
299     AtomicHTMLToken fakeToken(HTMLToken::StartTag, record->element()->localName());
300     return createHTMLElement(fakeToken);
301 }
302
303 namespace {
304
305 PassRefPtr<NamedNodeMap> cloneAttributes(Element* element)
306 {
307     NamedNodeMap* attributes = element->attributes(true);
308     if (!attributes)
309         return 0;
310
311     RefPtr<NamedNodeMap> newAttributes = NamedNodeMap::create();
312     for (size_t i = 0; i < attributes->length(); ++i) {
313         Attribute* attribute = attributes->attributeItem(i);
314         RefPtr<Attribute> clone = Attribute::createMapped(attribute->name(), attribute->value());
315         newAttributes->addAttribute(clone);
316     }
317     return newAttributes.release();
318 }
319
320 }
321
322 PassRefPtr<Element> HTMLConstructionSite::createHTMLElementFromSavedElement(Element* element)
323 {
324     // FIXME: This method is wrong.  We should be using the original token.
325     // Using an Element* causes us to fail examples like this:
326     // <b id="1"><p><script>document.getElementById("1").id = "2"</script></p>TEXT</b>
327     // When reconstructActiveFormattingElements calls this method to open
328     // a second <b> tag to wrap TEXT, it will have id "2", even though the HTML5
329     // spec implies it should be "1".  Minefield matches the HTML5 spec here.
330
331     ASSERT(element->isHTMLElement()); // otherwise localName() might be wrong.
332     AtomicHTMLToken fakeToken(HTMLToken::StartTag, element->localName(), cloneAttributes(element));
333     return createHTMLElement(fakeToken);
334 }
335
336 bool HTMLConstructionSite::indexOfFirstUnopenFormattingElement(unsigned& firstUnopenElementIndex) const
337 {
338     if (m_activeFormattingElements.isEmpty())
339         return false;
340     unsigned index = m_activeFormattingElements.size();
341     do {
342         --index;
343         const HTMLFormattingElementList::Entry& entry = m_activeFormattingElements.at(index);
344         if (entry.isMarker() || m_openElements.contains(entry.element())) {
345             firstUnopenElementIndex = index + 1;
346             return firstUnopenElementIndex < m_activeFormattingElements.size();
347         }
348     } while (index);
349     firstUnopenElementIndex = index;
350     return true;
351 }
352
353 void HTMLConstructionSite::reconstructTheActiveFormattingElements()
354 {
355     unsigned firstUnopenElementIndex;
356     if (!indexOfFirstUnopenFormattingElement(firstUnopenElementIndex))
357         return;
358
359     unsigned unopenEntryIndex = firstUnopenElementIndex;
360     ASSERT(unopenEntryIndex < m_activeFormattingElements.size());
361     for (; unopenEntryIndex < m_activeFormattingElements.size(); ++unopenEntryIndex) {
362         HTMLFormattingElementList::Entry& unopenedEntry = m_activeFormattingElements.at(unopenEntryIndex);
363         RefPtr<Element> reconstructed = createHTMLElementFromSavedElement(unopenedEntry.element());
364         m_openElements.push(attachToCurrent(reconstructed.release()));
365         unopenedEntry.replaceElement(currentElement());
366     }
367 }
368
369 void HTMLConstructionSite::generateImpliedEndTagsWithExclusion(const AtomicString& tagName)
370 {
371     while (hasImpliedEndTag(currentElement()) && !currentElement()->hasLocalName(tagName))
372         m_openElements.pop();
373 }
374
375 void HTMLConstructionSite::generateImpliedEndTags()
376 {
377     while (hasImpliedEndTag(currentElement()))
378         m_openElements.pop();
379 }
380
381 void HTMLConstructionSite::findFosterSite(AttachmentSite& site)
382 {
383     HTMLElementStack::ElementRecord* lastTableElementRecord = m_openElements.topmost(tableTag.localName());
384     if (lastTableElementRecord) {
385         Element* lastTableElement = lastTableElementRecord->element();
386         if (Node* parent = lastTableElement->parent()) {
387             site.parent = parent;
388             site.nextChild = lastTableElement;
389             return;
390         }
391         site.parent = lastTableElementRecord->next()->element();
392         site.nextChild = 0;
393         return;
394     }
395     // Fragment case
396     site.parent = m_openElements.bottom(); // <html> element
397     site.nextChild = 0;
398 }
399
400 void HTMLConstructionSite::fosterParent(Node* node)
401 {
402     AttachmentSite site;
403     findFosterSite(site);
404     attachAtSite(site, node);
405 }
406
407 }