diff options
Diffstat (limited to 'WebCore/dom/XMLDocumentParser.cpp')
-rw-r--r-- | WebCore/dom/XMLDocumentParser.cpp | 403 |
1 files changed, 403 insertions, 0 deletions
diff --git a/WebCore/dom/XMLDocumentParser.cpp b/WebCore/dom/XMLDocumentParser.cpp new file mode 100644 index 0000000..c6d9f89 --- /dev/null +++ b/WebCore/dom/XMLDocumentParser.cpp @@ -0,0 +1,403 @@ +/* + * 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 (isDetached() || 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(); +} + +void XMLDocumentParser::enterText() +{ +#if !USE(QXMLSTREAM) + ASSERT(m_bufferedText.size() == 0); +#endif + RefPtr<Node> newNode = Text::create(document(), ""); + m_currentNode->deprecatedParserAddChild(newNode.get()); + pushCurrentNode(newNode.get()); +} + +#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::detach() +{ + clearCurrentNodeStack(); + ScriptableDocumentParser::detach(); +} + +void XMLDocumentParser::end() +{ + // XMLDocumentParserLibxml2 will do bad things to the document if doEnd() is called. + // I don't believe XMLDocumentParserQt needs doEnd called in the fragment case. + ASSERT(!m_parsingFragment); + + doEnd(); + + // doEnd() could process a script tag, thus pausing parsing. + if (m_parserPaused) + return; + + if (m_sawError) + insertErrorMessageBlock(); + else { + exitText(); + document()->updateStyleSelector(); + } + + clearCurrentNodeStack(); + document()->finishedParsing(); +} + +void XMLDocumentParser::finish() +{ + // FIXME: We should ASSERT(!m_parserStopped) here, since it does not + // makes sense to call any methods on DocumentParser once it's been stopped. + // However, FrameLoader::stop calls Document::finishParsing unconditionally + // which in turn calls m_parser->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); + + // JavaScript can detach this parser, make sure it's kept alive even if detached. + RefPtr<XMLDocumentParser> protect(this); + + if (errorOccurred) + scriptElement->dispatchErrorEvent(); + else { + m_view->frame()->script()->executeScript(sourceCode); + scriptElement->dispatchLoadEvent(); + } + + m_scriptElement = 0; + + if (!isDetached() && !m_requestingScript) + resumeParsing(); +} + +bool XMLDocumentParser::isWaitingForScripts() const +{ + return m_pendingScript; +} + +void XMLDocumentParser::pauseParsing() +{ + if (m_parsingFragment) + return; + + m_parserPaused = true; +} + +bool XMLDocumentParser::parseDocumentFragment(const String& chunk, DocumentFragment* fragment, Element* contextElement, FragmentScriptingPermission scriptingPermission) +{ + if (!chunk.length()) + return true; + + // FIXME: We need to implement the HTML5 XML Fragment parsing algorithm: + // http://www.whatwg.org/specs/web-apps/current-work/multipage/the-xhtml-syntax.html#xml-fragment-parsing-algorithm + // For now we have a hack for script/style innerHTML support: + if (contextElement && (contextElement->hasLocalName(HTMLNames::scriptTag) || contextElement->hasLocalName(HTMLNames::styleTag))) { + fragment->parserAddChild(fragment->document()->createTextNode(chunk)); + return true; + } + + RefPtr<XMLDocumentParser> parser = XMLDocumentParser::create(fragment, contextElement, scriptingPermission); + bool wellFormed = parser->appendFragmentSource(chunk); + // Do not call finish(). Current finish() and doEnd() implementations touch the main Document/loader + // and can cause crashes in the fragment case. + parser->detach(); // Allows ~DocumentParser to assert it was detached before destruction. + return wellFormed; // appendFragmentSource()'s wellFormed is more permissive than wellFormed(). +} + +} // namespace WebCore |