2011-05-31 Abhishek Arya <inferno@chromium.org>
[WebKit-https.git] / Source / WebCore / dom / XMLDocumentParser.cpp
1 /*
2  * Copyright (C) 2000 Peter Kelly (pmk@post.com)
3  * Copyright (C) 2005, 2006, 2008 Apple Inc. All rights reserved.
4  * Copyright (C) 2006 Alexey Proskuryakov (ap@webkit.org)
5  * Copyright (C) 2007 Samuel Weinig (sam@webkit.org)
6  * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies)
7  * Copyright (C) 2008 Holger Hans Peter Freyther
8  * Copyright (C) 2008 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/)
9  *
10  * This library is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU Library General Public
12  * License as published by the Free Software Foundation; either
13  * version 2 of the License, or (at your option) any later version.
14  *
15  * This library is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  * Library General Public License for more details.
19  *
20  * You should have received a copy of the GNU Library General Public License
21  * along with this library; see the file COPYING.LIB.  If not, write to
22  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
23  * Boston, MA 02110-1301, USA.
24  */
25
26 #include "config.h"
27 #include "XMLDocumentParser.h"
28
29 #include "CDATASection.h"
30 #include "CachedScript.h"
31 #include "Comment.h"
32 #include "CachedResourceLoader.h"
33 #include "Document.h"
34 #include "DocumentFragment.h"
35 #include "DocumentType.h"
36 #include "Frame.h"
37 #include "FrameLoader.h"
38 #include "FrameView.h"
39 #include "HTMLLinkElement.h"
40 #include "HTMLNames.h"
41 #include "HTMLStyleElement.h"
42 #include "ImageLoader.h"
43 #include "ProcessingInstruction.h"
44 #include "ResourceError.h"
45 #include "ResourceHandle.h"
46 #include "ResourceRequest.h"
47 #include "ResourceResponse.h"
48 #include "ScriptElement.h"
49 #include "ScriptSourceCode.h"
50 #include "ScriptValue.h"
51 #include "TextResourceDecoder.h"
52 #include "TreeDepthLimit.h"
53 #include <wtf/text/WTFString.h>
54 #include <wtf/StringExtras.h>
55 #include <wtf/Threading.h>
56 #include <wtf/Vector.h>
57
58 #if ENABLE(SVG)
59 #include "SVGNames.h"
60 #include "SVGStyleElement.h"
61 #endif
62
63 using namespace std;
64
65 namespace WebCore {
66
67 using namespace HTMLNames;
68
69 const int maxErrors = 25;
70
71 void XMLDocumentParser::pushCurrentNode(ContainerNode* n)
72 {
73     ASSERT(n);
74     ASSERT(m_currentNode);
75     if (n != document())
76         n->ref();
77     m_currentNodeStack.append(m_currentNode);
78     m_currentNode = n;
79     if (m_currentNodeStack.size() > maxDOMTreeDepth)
80         handleError(fatal, "Excessive node nesting.", lineNumber(), columnNumber());
81 }
82
83 void XMLDocumentParser::popCurrentNode()
84 {
85     if (!m_currentNode)
86         return;
87     ASSERT(m_currentNodeStack.size());
88
89     if (m_currentNode != document())
90         m_currentNode->deref();
91
92     m_currentNode = m_currentNodeStack.last();
93     m_currentNodeStack.removeLast();
94 }
95
96 void XMLDocumentParser::clearCurrentNodeStack()
97 {
98     if (m_currentNode && m_currentNode != document())
99         m_currentNode->deref();
100     m_currentNode = 0;
101     m_leafTextNode = 0;
102
103     if (m_currentNodeStack.size()) { // Aborted parsing.
104         for (size_t i = m_currentNodeStack.size() - 1; i != 0; --i)
105             m_currentNodeStack[i]->deref();
106         if (m_currentNodeStack[0] && m_currentNodeStack[0] != document())
107             m_currentNodeStack[0]->deref();
108         m_currentNodeStack.clear();
109     }
110 }
111
112 void XMLDocumentParser::insert(const SegmentedString&)
113 {
114     ASSERT_NOT_REACHED();
115 }
116
117 void XMLDocumentParser::append(const SegmentedString& s)
118 {
119     String parseString = s.toString();
120
121     if (m_sawXSLTransform || !m_sawFirstElement)
122         m_originalSourceForTransform += parseString;
123
124     if (isStopped() || m_sawXSLTransform)
125         return;
126
127     if (m_parserPaused) {
128         m_pendingSrc.append(s);
129         return;
130     }
131
132     doWrite(s.toString());
133
134     // After parsing, go ahead and dispatch image beforeload events.
135     ImageLoader::dispatchPendingBeforeLoadEvents();
136 }
137
138 void XMLDocumentParser::handleError(ErrorType type, const char* m, int lineNumber, int columnNumber)
139 {
140     handleError(type, m, TextPosition1(WTF::OneBasedNumber::fromOneBasedInt(lineNumber), WTF::OneBasedNumber::fromOneBasedInt(columnNumber)));
141 }
142
143 void XMLDocumentParser::handleError(ErrorType type, const char* m, TextPosition1 position)
144 {
145     if (type == fatal || (m_errorCount < maxErrors && m_lastErrorPosition.m_line != position.m_line && m_lastErrorPosition.m_column != position.m_column)) {
146         switch (type) {
147             case warning:
148                 m_errorMessages += "warning on line " + String::number(position.m_line.oneBasedInt()) + " at column " + String::number(position.m_column.oneBasedInt()) + ": " + m;
149                 break;
150             case fatal:
151             case nonFatal:
152                 m_errorMessages += "error on line " + String::number(position.m_line.oneBasedInt()) + " at column " + String::number(position.m_column.oneBasedInt()) + ": " + m;
153         }
154
155         m_lastErrorPosition = position;
156         ++m_errorCount;
157     }
158
159     if (type != warning)
160         m_sawError = true;
161
162     if (type == fatal)
163         stopParsing();
164 }
165
166 void XMLDocumentParser::enterText()
167 {
168 #if !USE(QXMLSTREAM)
169     ASSERT(m_bufferedText.size() == 0);
170 #endif
171     ASSERT(!m_leafTextNode);
172     m_leafTextNode = Text::create(document(), "");
173     m_currentNode->parserAddChild(m_leafTextNode.get());
174 }
175
176 #if !USE(QXMLSTREAM)
177 static inline String toString(const xmlChar* string, size_t size) 
178
179     return String::fromUTF8(reinterpret_cast<const char*>(string), size); 
180 }
181 #endif
182
183
184 void XMLDocumentParser::exitText()
185 {
186     if (isStopped())
187         return;
188
189     if (!m_leafTextNode)
190         return;
191
192 #if !USE(QXMLSTREAM)
193     ExceptionCode ec = 0;
194     m_leafTextNode->appendData(toString(m_bufferedText.data(), m_bufferedText.size()), ec);
195     Vector<xmlChar> empty;
196     m_bufferedText.swap(empty);
197 #endif
198
199     if (m_view && !m_leafTextNode->attached())
200         m_leafTextNode->attach();
201
202     m_leafTextNode = 0;
203 }
204
205 void XMLDocumentParser::detach()
206 {
207     clearCurrentNodeStack();
208     ScriptableDocumentParser::detach();
209 }
210
211 void XMLDocumentParser::end()
212 {
213     // XMLDocumentParserLibxml2 will do bad things to the document if doEnd() is called.
214     // I don't believe XMLDocumentParserQt needs doEnd called in the fragment case.
215     ASSERT(!m_parsingFragment);
216
217     doEnd();
218
219     // doEnd() could process a script tag, thus pausing parsing.
220     if (m_parserPaused)
221         return;
222
223     if (m_sawError)
224         insertErrorMessageBlock();
225     else {
226         exitText();
227         document()->styleSelectorChanged(RecalcStyleImmediately);
228     }
229
230     if (isParsing())
231         prepareToStopParsing();
232     document()->setReadyState(Document::Interactive);
233     clearCurrentNodeStack();
234     document()->finishedParsing();
235 }
236
237 void XMLDocumentParser::finish()
238 {
239     // FIXME: We should ASSERT(!m_parserStopped) here, since it does not
240     // makes sense to call any methods on DocumentParser once it's been stopped.
241     // However, FrameLoader::stop calls Document::finishParsing unconditionally
242     // which in turn calls m_parser->finish().
243
244     if (m_parserPaused)
245         m_finishCalled = true;
246     else
247         end();
248 }
249
250 bool XMLDocumentParser::finishWasCalled()
251 {
252     return m_finishCalled;
253 }
254
255 static inline RefPtr<Element> createXHTMLParserErrorHeader(Document* doc, const String& errorMessages)
256 {
257     RefPtr<Element> reportElement = doc->createElement(QualifiedName(nullAtom, "parsererror", xhtmlNamespaceURI), false);
258     reportElement->setAttribute(styleAttr, "display: block; white-space: pre; border: 2px solid #c77; padding: 0 1em 0 1em; margin: 1em; background-color: #fdd; color: black");
259
260     ExceptionCode ec = 0;
261     RefPtr<Element> h3 = doc->createElement(h3Tag, false);
262     reportElement->appendChild(h3.get(), ec);
263     h3->appendChild(doc->createTextNode("This page contains the following errors:"), ec);
264
265     RefPtr<Element> fixed = doc->createElement(divTag, false);
266     reportElement->appendChild(fixed.get(), ec);
267     fixed->setAttribute(styleAttr, "font-family:monospace;font-size:12px");
268     fixed->appendChild(doc->createTextNode(errorMessages), ec);
269
270     h3 = doc->createElement(h3Tag, false);
271     reportElement->appendChild(h3.get(), ec);
272     h3->appendChild(doc->createTextNode("Below is a rendering of the page up to the first error."), ec);
273
274     return reportElement;
275 }
276
277 void XMLDocumentParser::insertErrorMessageBlock()
278 {
279 #if USE(QXMLSTREAM)
280     if (m_parsingFragment)
281         return;
282 #endif
283     // One or more errors occurred during parsing of the code. Display an error block to the user above
284     // the normal content (the DOM tree is created manually and includes line/col info regarding
285     // where the errors are located)
286
287     // Create elements for display
288     ExceptionCode ec = 0;
289     Document* document = this->document();
290     RefPtr<Element> documentElement = document->documentElement();
291     if (!documentElement) {
292         RefPtr<Element> rootElement = document->createElement(htmlTag, false);
293         document->appendChild(rootElement, ec);
294         RefPtr<Element> body = document->createElement(bodyTag, false);
295         rootElement->appendChild(body, ec);
296         documentElement = body.get();
297     }
298 #if ENABLE(SVG)
299     else if (documentElement->namespaceURI() == SVGNames::svgNamespaceURI) {
300         RefPtr<Element> rootElement = document->createElement(htmlTag, false);
301         RefPtr<Element> body = document->createElement(bodyTag, false);
302         rootElement->appendChild(body, ec);
303         body->appendChild(documentElement, ec);
304         document->appendChild(rootElement.get(), ec);
305         documentElement = body.get();
306     }
307 #endif
308     RefPtr<Element> reportElement = createXHTMLParserErrorHeader(document, m_errorMessages);
309     documentElement->insertBefore(reportElement, documentElement->firstChild(), ec);
310 #if ENABLE(XSLT)
311     if (document->transformSourceDocument()) {
312         RefPtr<Element> paragraph = document->createElement(pTag, false);
313         paragraph->setAttribute(styleAttr, "white-space: normal");
314         paragraph->appendChild(document->createTextNode("This document was created as the result of an XSL transformation. The line and column numbers given are from the transformed result."), ec);
315         reportElement->appendChild(paragraph.release(), ec);
316     }
317 #endif
318     document->updateStyleIfNeeded();
319 }
320
321 void XMLDocumentParser::notifyFinished(CachedResource* unusedResource)
322 {
323     ASSERT_UNUSED(unusedResource, unusedResource == m_pendingScript);
324     ASSERT(m_pendingScript->accessCount() > 0);
325
326     ScriptSourceCode sourceCode(m_pendingScript.get());
327     bool errorOccurred = m_pendingScript->errorOccurred();
328     bool wasCanceled = m_pendingScript->wasCanceled();
329
330     m_pendingScript->removeClient(this);
331     m_pendingScript = 0;
332
333     RefPtr<Element> e = m_scriptElement;
334     m_scriptElement = 0;
335
336     ScriptElement* scriptElement = toScriptElement(e.get());
337     ASSERT(scriptElement);
338
339     // JavaScript can detach this parser, make sure it's kept alive even if detached.
340     RefPtr<XMLDocumentParser> protect(this);
341     
342     if (errorOccurred)
343         scriptElement->dispatchErrorEvent();
344     else if (!wasCanceled) {
345         scriptElement->executeScript(sourceCode);
346         scriptElement->dispatchLoadEvent();
347     }
348
349     m_scriptElement = 0;
350
351     if (!isDetached() && !m_requestingScript)
352         resumeParsing();
353 }
354
355 bool XMLDocumentParser::isWaitingForScripts() const
356 {
357     return m_pendingScript;
358 }
359
360 void XMLDocumentParser::pauseParsing()
361 {
362     if (m_parsingFragment)
363         return;
364
365     m_parserPaused = true;
366 }
367
368 bool XMLDocumentParser::parseDocumentFragment(const String& chunk, DocumentFragment* fragment, Element* contextElement, FragmentScriptingPermission scriptingPermission)
369 {
370     if (!chunk.length())
371         return true;
372
373     // FIXME: We need to implement the HTML5 XML Fragment parsing algorithm:
374     // http://www.whatwg.org/specs/web-apps/current-work/multipage/the-xhtml-syntax.html#xml-fragment-parsing-algorithm
375     // For now we have a hack for script/style innerHTML support:
376     if (contextElement && (contextElement->hasLocalName(HTMLNames::scriptTag) || contextElement->hasLocalName(HTMLNames::styleTag))) {
377         fragment->parserAddChild(fragment->document()->createTextNode(chunk));
378         return true;
379     }
380
381     RefPtr<XMLDocumentParser> parser = XMLDocumentParser::create(fragment, contextElement, scriptingPermission);
382     bool wellFormed = parser->appendFragmentSource(chunk);
383     // Do not call finish().  Current finish() and doEnd() implementations touch the main Document/loader
384     // and can cause crashes in the fragment case.
385     parser->detach(); // Allows ~DocumentParser to assert it was detached before destruction.
386     return wellFormed; // appendFragmentSource()'s wellFormed is more permissive than wellFormed().
387 }
388
389 } // namespace WebCore