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