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