WebCore/dom/XMLDocumentParser.cpp
changeset 0 4f2f89ce4247
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebCore/dom/XMLDocumentParser.cpp	Fri Sep 17 09:02:29 2010 +0300
@@ -0,0 +1,367 @@
+/*
+ * Copyright (C) 2000 Peter Kelly (pmk@post.com)
+ * Copyright (C) 2005, 2006, 2008 Apple Inc. All rights reserved.
+ * Copyright (C) 2006 Alexey Proskuryakov (ap@webkit.org)
+ * Copyright (C) 2007 Samuel Weinig (sam@webkit.org)
+ * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies)
+ * Copyright (C) 2008 Holger Hans Peter Freyther
+ * Copyright (C) 2008 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB.  If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "config.h"
+#include "XMLDocumentParser.h"
+
+#include "CDATASection.h"
+#include "CachedScript.h"
+#include "Comment.h"
+#include "DocLoader.h"
+#include "Document.h"
+#include "DocumentFragment.h"
+#include "DocumentType.h"
+#include "Frame.h"
+#include "FrameLoader.h"
+#include "FrameView.h"
+#include "HTMLLinkElement.h"
+#include "HTMLNames.h"
+#include "HTMLStyleElement.h"
+#include "ImageLoader.h"
+#include "ProcessingInstruction.h"
+#include "ResourceError.h"
+#include "ResourceHandle.h"
+#include "ResourceRequest.h"
+#include "ResourceResponse.h"
+#include "ScriptController.h"
+#include "ScriptElement.h"
+#include "ScriptSourceCode.h"
+#include "ScriptValue.h"
+#include "TextResourceDecoder.h"
+#include "TreeDepthLimit.h"
+#include <wtf/text/CString.h>
+#include <wtf/StringExtras.h>
+#include <wtf/Threading.h>
+#include <wtf/Vector.h>
+
+#if ENABLE(SVG)
+#include "SVGNames.h"
+#include "SVGStyleElement.h"
+#endif
+
+using namespace std;
+
+namespace WebCore {
+
+using namespace HTMLNames;
+
+const int maxErrors = 25;
+
+#if ENABLE(WML)
+bool XMLDocumentParser::isWMLDocument() const
+{
+    return document()->isWMLDocument();
+}
+#endif
+
+void XMLDocumentParser::pushCurrentNode(Node* n)
+{
+    ASSERT(n);
+    ASSERT(m_currentNode);
+    if (n != document())
+        n->ref();
+    m_currentNodeStack.append(m_currentNode);
+    m_currentNode = n;
+    if (m_currentNodeStack.size() > maxDOMTreeDepth)
+        handleError(fatal, "Excessive node nesting.", lineNumber(), columnNumber());
+}
+
+void XMLDocumentParser::popCurrentNode()
+{
+    if (!m_currentNode)
+        return;
+    ASSERT(m_currentNodeStack.size());
+
+    if (m_currentNode != document())
+        m_currentNode->deref();
+
+    m_currentNode = m_currentNodeStack.last();
+    m_currentNodeStack.removeLast();
+}
+
+void XMLDocumentParser::clearCurrentNodeStack()
+{
+    if (m_currentNode && m_currentNode != document())
+        m_currentNode->deref();
+    m_currentNode = 0;
+
+    if (m_currentNodeStack.size()) { // Aborted parsing.
+        for (size_t i = m_currentNodeStack.size() - 1; i != 0; --i)
+            m_currentNodeStack[i]->deref();
+        if (m_currentNodeStack[0] && m_currentNodeStack[0] != document())
+            m_currentNodeStack[0]->deref();
+        m_currentNodeStack.clear();
+    }
+}
+
+void XMLDocumentParser::insert(const SegmentedString& source)
+{
+    // FIXME: This is a hack to work around the fact that XMLHttpRequest
+    // responseXML() calls Document::write() which in turn calls insert(). In
+    // HTML, that's correct, as insert() implies a synchronous parse.  For XML,
+    // all parsing is synchronous but document.write shouldn't be supported.
+    append(source);
+}
+
+void XMLDocumentParser::append(const SegmentedString& s)
+{
+    String parseString = s.toString();
+
+    if (m_sawXSLTransform || !m_sawFirstElement)
+        m_originalSourceForTransform += parseString;
+
+    if (m_parserStopped || m_sawXSLTransform)
+        return;
+
+    if (m_parserPaused) {
+        m_pendingSrc.append(s);
+        return;
+    }
+
+    doWrite(s.toString());
+
+    // After parsing, go ahead and dispatch image beforeload events.
+    ImageLoader::dispatchPendingBeforeLoadEvents();
+}
+
+void XMLDocumentParser::handleError(ErrorType type, const char* m, int lineNumber, int columnNumber)
+{
+    if (type == fatal || (m_errorCount < maxErrors && m_lastErrorLine != lineNumber && m_lastErrorColumn != columnNumber)) {
+        switch (type) {
+            case warning:
+                m_errorMessages += String::format("warning on line %d at column %d: %s", lineNumber, columnNumber, m);
+                break;
+            case fatal:
+            case nonFatal:
+                m_errorMessages += String::format("error on line %d at column %d: %s", lineNumber, columnNumber, m);
+        }
+
+        m_lastErrorLine = lineNumber;
+        m_lastErrorColumn = columnNumber;
+        ++m_errorCount;
+    }
+
+    if (type != warning)
+        m_sawError = true;
+
+    if (type == fatal)
+        stopParsing();
+}
+
+bool XMLDocumentParser::enterText()
+{
+#if !USE(QXMLSTREAM)
+    ASSERT(m_bufferedText.size() == 0);
+#endif
+    RefPtr<Node> newNode = Text::create(document(), "");
+    if (!m_currentNode->legacyParserAddChild(newNode.get()))
+        return false;
+    pushCurrentNode(newNode.get());
+    return true;
+}
+
+#if !USE(QXMLSTREAM)
+static inline String toString(const xmlChar* str, unsigned len)
+{
+    return UTF8Encoding().decode(reinterpret_cast<const char*>(str), len);
+}
+#endif
+
+
+void XMLDocumentParser::exitText()
+{
+    if (m_parserStopped)
+        return;
+
+    if (!m_currentNode || !m_currentNode->isTextNode())
+        return;
+
+#if !USE(QXMLSTREAM)
+    ExceptionCode ec = 0;
+    static_cast<Text*>(m_currentNode)->appendData(toString(m_bufferedText.data(), m_bufferedText.size()), ec);
+    Vector<xmlChar> empty;
+    m_bufferedText.swap(empty);
+#endif
+
+    if (m_view && m_currentNode && !m_currentNode->attached())
+        m_currentNode->attach();
+
+    popCurrentNode();
+}
+
+void XMLDocumentParser::end()
+{
+    doEnd();
+
+    // doEnd() could process a script tag, thus pausing parsing.
+    if (m_parserPaused)
+        return;
+
+    if (m_sawError)
+        insertErrorMessageBlock();
+    else {
+        exitText();
+        document()->updateStyleSelector();
+    }
+
+    clearCurrentNodeStack();
+    if (!m_parsingFragment)
+        document()->finishedParsing();
+}
+
+void XMLDocumentParser::finish()
+{
+    if (m_parserPaused)
+        m_finishCalled = true;
+    else
+        end();
+}
+
+bool XMLDocumentParser::finishWasCalled()
+{
+    return m_finishCalled;
+}
+
+static inline RefPtr<Element> createXHTMLParserErrorHeader(Document* doc, const String& errorMessages)
+{
+    RefPtr<Element> reportElement = doc->createElement(QualifiedName(nullAtom, "parsererror", xhtmlNamespaceURI), false);
+    reportElement->setAttribute(styleAttr, "display: block; white-space: pre; border: 2px solid #c77; padding: 0 1em 0 1em; margin: 1em; background-color: #fdd; color: black");
+
+    ExceptionCode ec = 0;
+    RefPtr<Element> h3 = doc->createElement(h3Tag, false);
+    reportElement->appendChild(h3.get(), ec);
+    h3->appendChild(doc->createTextNode("This page contains the following errors:"), ec);
+
+    RefPtr<Element> fixed = doc->createElement(divTag, false);
+    reportElement->appendChild(fixed.get(), ec);
+    fixed->setAttribute(styleAttr, "font-family:monospace;font-size:12px");
+    fixed->appendChild(doc->createTextNode(errorMessages), ec);
+
+    h3 = doc->createElement(h3Tag, false);
+    reportElement->appendChild(h3.get(), ec);
+    h3->appendChild(doc->createTextNode("Below is a rendering of the page up to the first error."), ec);
+
+    return reportElement;
+}
+
+void XMLDocumentParser::insertErrorMessageBlock()
+{
+#if USE(QXMLSTREAM)
+    if (m_parsingFragment)
+        return;
+#endif
+    // One or more errors occurred during parsing of the code. Display an error block to the user above
+    // the normal content (the DOM tree is created manually and includes line/col info regarding
+    // where the errors are located)
+
+    // Create elements for display
+    ExceptionCode ec = 0;
+    Document* doc = document();
+    Node* documentElement = doc->documentElement();
+    if (!documentElement) {
+        RefPtr<Node> rootElement = doc->createElement(htmlTag, false);
+        doc->appendChild(rootElement, ec);
+        RefPtr<Node> body = doc->createElement(bodyTag, false);
+        rootElement->appendChild(body, ec);
+        documentElement = body.get();
+    }
+#if ENABLE(SVG)
+    else if (documentElement->namespaceURI() == SVGNames::svgNamespaceURI) {
+        RefPtr<Node> rootElement = doc->createElement(htmlTag, false);
+        RefPtr<Node> body = doc->createElement(bodyTag, false);
+        rootElement->appendChild(body, ec);
+        body->appendChild(documentElement, ec);
+        doc->appendChild(rootElement.get(), ec);
+        documentElement = body.get();
+    }
+#endif
+#if ENABLE(WML)
+    else if (isWMLDocument()) {
+        RefPtr<Node> rootElement = doc->createElement(htmlTag, false);
+        RefPtr<Node> body = doc->createElement(bodyTag, false);
+        rootElement->appendChild(body, ec);
+        body->appendChild(documentElement, ec);
+        doc->appendChild(rootElement.get(), ec);
+        documentElement = body.get();
+    }
+#endif
+
+    RefPtr<Element> reportElement = createXHTMLParserErrorHeader(doc, m_errorMessages);
+    documentElement->insertBefore(reportElement, documentElement->firstChild(), ec);
+#if ENABLE(XSLT)
+    if (doc->transformSourceDocument()) {
+        RefPtr<Element> par = doc->createElement(pTag, false);
+        reportElement->appendChild(par, ec);
+        par->setAttribute(styleAttr, "white-space: normal");
+        par->appendChild(doc->createTextNode("This document was created as the result of an XSL transformation. The line and column numbers given are from the transformed result."), ec);
+    }
+#endif
+    doc->updateStyleIfNeeded();
+}
+
+void XMLDocumentParser::notifyFinished(CachedResource* unusedResource)
+{
+    ASSERT_UNUSED(unusedResource, unusedResource == m_pendingScript);
+    ASSERT(m_pendingScript->accessCount() > 0);
+
+    ScriptSourceCode sourceCode(m_pendingScript.get());
+    bool errorOccurred = m_pendingScript->errorOccurred();
+
+    m_pendingScript->removeClient(this);
+    m_pendingScript = 0;
+
+    RefPtr<Element> e = m_scriptElement;
+    m_scriptElement = 0;
+
+    ScriptElement* scriptElement = toScriptElement(e.get());
+    ASSERT(scriptElement);
+
+    if (errorOccurred)
+        scriptElement->dispatchErrorEvent();
+    else {
+        m_view->frame()->script()->executeScript(sourceCode);
+        scriptElement->dispatchLoadEvent();
+    }
+
+    m_scriptElement = 0;
+
+    if (!m_requestingScript)
+        resumeParsing();
+}
+
+bool XMLDocumentParser::isWaitingForScripts() const
+{
+    return m_pendingScript;
+}
+
+void XMLDocumentParser::pauseParsing()
+{
+    if (m_parsingFragment)
+        return;
+
+    m_parserPaused = true;
+}
+
+}