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