diff options
author | Steve Block <steveblock@google.com> | 2011-05-06 11:45:16 +0100 |
---|---|---|
committer | Steve Block <steveblock@google.com> | 2011-05-12 13:44:10 +0100 |
commit | cad810f21b803229eb11403f9209855525a25d57 (patch) | |
tree | 29a6fd0279be608e0fe9ffe9841f722f0f4e4269 /Source/WebCore/xml | |
parent | 121b0cf4517156d0ac5111caf9830c51b69bae8f (diff) | |
download | external_webkit-cad810f21b803229eb11403f9209855525a25d57.zip external_webkit-cad810f21b803229eb11403f9209855525a25d57.tar.gz external_webkit-cad810f21b803229eb11403f9209855525a25d57.tar.bz2 |
Merge WebKit at r75315: Initial merge by git.
Change-Id: I570314b346ce101c935ed22a626b48c2af266b84
Diffstat (limited to 'Source/WebCore/xml')
73 files changed, 10011 insertions, 0 deletions
diff --git a/Source/WebCore/xml/DOMParser.cpp b/Source/WebCore/xml/DOMParser.cpp new file mode 100644 index 0000000..e6aa3b0 --- /dev/null +++ b/Source/WebCore/xml/DOMParser.cpp @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2003, 2006, 2008 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "config.h" +#include "DOMParser.h" + +#include "DOMImplementation.h" +#include "Document.h" +#include "PlatformString.h" + +namespace WebCore { + +PassRefPtr<Document> DOMParser::parseFromString(const String& str, const String& contentType) +{ + if (!DOMImplementation::isXMLMIMEType(contentType)) + return 0; + + RefPtr<Document> doc = DOMImplementation::createDocument(contentType, 0, KURL(), false); + + doc->open(); + doc->write(str); + doc->finishParsing(); + doc->close(); + + return doc.release(); +} + +} diff --git a/Source/WebCore/xml/DOMParser.h b/Source/WebCore/xml/DOMParser.h new file mode 100644 index 0000000..41329ba --- /dev/null +++ b/Source/WebCore/xml/DOMParser.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2003, 2006 Apple Computer, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef DOMParser_h +#define DOMParser_h + +#include <wtf/Forward.h> +#include <wtf/RefCounted.h> +#include <wtf/RefPtr.h> + +namespace WebCore { + +class Document; + +class DOMParser : public RefCounted<DOMParser> { +public: + static PassRefPtr<DOMParser> create() { return adoptRef(new DOMParser); } + + PassRefPtr<Document> parseFromString(const String&, const String& contentType); + +private: + DOMParser() { } +}; + +} + +#endif // XMLSerializer.h diff --git a/Source/WebCore/xml/DOMParser.idl b/Source/WebCore/xml/DOMParser.idl new file mode 100644 index 0000000..90a8b52 --- /dev/null +++ b/Source/WebCore/xml/DOMParser.idl @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2006 Apple Computer, Inc. + * + * 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. + */ + +module xpath { + interface [CanBeConstructed] DOMParser { + Document parseFromString(in DOMString str, in DOMString contentType); + }; +} diff --git a/Source/WebCore/xml/NativeXPathNSResolver.cpp b/Source/WebCore/xml/NativeXPathNSResolver.cpp new file mode 100644 index 0000000..11faea6 --- /dev/null +++ b/Source/WebCore/xml/NativeXPathNSResolver.cpp @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2007 Alexey Proskuryakov (ap@nypop.com) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "NativeXPathNSResolver.h" + +#if ENABLE(XPATH) + +#include "Node.h" +#include "PlatformString.h" +#include "XMLNames.h" + +namespace WebCore { + +NativeXPathNSResolver::NativeXPathNSResolver(PassRefPtr<Node> node) + : m_node(node) +{ +} + +NativeXPathNSResolver::~NativeXPathNSResolver() +{ +} + +String NativeXPathNSResolver::lookupNamespaceURI(const String& prefix) +{ + // This is not done by Node::lookupNamespaceURI as per the DOM3 Core spec, + // but the XPath spec says that we should do it for XPathNSResolver. + if (prefix == "xml") + return XMLNames::xmlNamespaceURI; + + return m_node ? m_node->lookupNamespaceURI(prefix) : String(); +} + +} // namespace WebCore + +#endif // ENABLE(XPATH) diff --git a/Source/WebCore/xml/NativeXPathNSResolver.h b/Source/WebCore/xml/NativeXPathNSResolver.h new file mode 100644 index 0000000..212b929 --- /dev/null +++ b/Source/WebCore/xml/NativeXPathNSResolver.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2007 Alexey Proskuryakov (ap@nypop.com) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef NativeXPathNSResolver_h +#define NativeXPathNSResolver_h + +#if ENABLE(XPATH) + +#include "XPathNSResolver.h" +#include "Node.h" +#include <wtf/RefPtr.h> + +namespace WebCore { + + class NativeXPathNSResolver : public XPathNSResolver { + public: + static PassRefPtr<NativeXPathNSResolver> create(PassRefPtr<Node> node) { return adoptRef(new NativeXPathNSResolver(node)); } + virtual ~NativeXPathNSResolver(); + + virtual String lookupNamespaceURI(const String& prefix); + + private: + NativeXPathNSResolver(PassRefPtr<Node>); + RefPtr<Node> m_node; + }; + +} // namespace WebCore + +#endif // ENABLE(XPATH) + +#endif // NativeXPathNSResolver_h diff --git a/Source/WebCore/xml/XMLHttpRequest.cpp b/Source/WebCore/xml/XMLHttpRequest.cpp new file mode 100644 index 0000000..fc7eb9e --- /dev/null +++ b/Source/WebCore/xml/XMLHttpRequest.cpp @@ -0,0 +1,1147 @@ +/* + * Copyright (C) 2004, 2006, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2005-2007 Alexey Proskuryakov <ap@webkit.org> + * Copyright (C) 2007, 2008 Julien Chaffraix <jchaffraix@webkit.org> + * Copyright (C) 2008 David Levin <levin@chromium.org> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "config.h" +#include "XMLHttpRequest.h" + +#include "ArrayBuffer.h" +#include "Blob.h" +#include "MemoryCache.h" +#include "CrossOriginAccessControl.h" +#include "DOMFormData.h" +#include "DOMImplementation.h" +#include "Document.h" +#include "Event.h" +#include "EventException.h" +#include "EventListener.h" +#include "EventNames.h" +#include "File.h" +#include "HTTPParsers.h" +#include "InspectorController.h" +#include "InspectorInstrumentation.h" +#include "ResourceError.h" +#include "ResourceRequest.h" +#include "SecurityOrigin.h" +#include "Settings.h" +#include "SharedBuffer.h" +#include "TextResourceDecoder.h" +#include "ThreadableLoader.h" +#include "XMLHttpRequestException.h" +#include "XMLHttpRequestProgressEvent.h" +#include "XMLHttpRequestUpload.h" +#include "markup.h" +#include <wtf/text/CString.h> +#include <wtf/StdLibExtras.h> +#include <wtf/RefCountedLeakCounter.h> +#include <wtf/UnusedParam.h> + +#if USE(JSC) +#include "JSDOMBinding.h" +#include "JSDOMWindow.h" +#include <runtime/Protect.h> +#endif + +namespace WebCore { + +#ifndef NDEBUG +static WTF::RefCountedLeakCounter xmlHttpRequestCounter("XMLHttpRequest"); +#endif + +struct XMLHttpRequestStaticData : Noncopyable { + XMLHttpRequestStaticData(); + String m_proxyHeaderPrefix; + String m_secHeaderPrefix; + HashSet<String, CaseFoldingHash> m_forbiddenRequestHeaders; +}; + +XMLHttpRequestStaticData::XMLHttpRequestStaticData() + : m_proxyHeaderPrefix("proxy-") + , m_secHeaderPrefix("sec-") +{ + m_forbiddenRequestHeaders.add("accept-charset"); + m_forbiddenRequestHeaders.add("accept-encoding"); + m_forbiddenRequestHeaders.add("access-control-request-headers"); + m_forbiddenRequestHeaders.add("access-control-request-method"); + m_forbiddenRequestHeaders.add("connection"); + m_forbiddenRequestHeaders.add("content-length"); + m_forbiddenRequestHeaders.add("content-transfer-encoding"); + m_forbiddenRequestHeaders.add("cookie"); + m_forbiddenRequestHeaders.add("cookie2"); + m_forbiddenRequestHeaders.add("date"); + m_forbiddenRequestHeaders.add("expect"); + m_forbiddenRequestHeaders.add("host"); + m_forbiddenRequestHeaders.add("keep-alive"); + m_forbiddenRequestHeaders.add("origin"); + m_forbiddenRequestHeaders.add("referer"); + m_forbiddenRequestHeaders.add("te"); + m_forbiddenRequestHeaders.add("trailer"); + m_forbiddenRequestHeaders.add("transfer-encoding"); + m_forbiddenRequestHeaders.add("upgrade"); + m_forbiddenRequestHeaders.add("user-agent"); + m_forbiddenRequestHeaders.add("via"); +} + +// Determines if a string is a valid token, as defined by +// "token" in section 2.2 of RFC 2616. +static bool isValidToken(const String& name) +{ + unsigned length = name.length(); + for (unsigned i = 0; i < length; i++) { + UChar c = name[i]; + + if (c >= 127 || c <= 32) + return false; + + if (c == '(' || c == ')' || c == '<' || c == '>' || c == '@' || + c == ',' || c == ';' || c == ':' || c == '\\' || c == '\"' || + c == '/' || c == '[' || c == ']' || c == '?' || c == '=' || + c == '{' || c == '}') + return false; + } + + return length > 0; +} + +static bool isValidHeaderValue(const String& name) +{ + // FIXME: This should really match name against + // field-value in section 4.2 of RFC 2616. + + return !name.contains('\r') && !name.contains('\n'); +} + +static bool isSetCookieHeader(const AtomicString& name) +{ + return equalIgnoringCase(name, "set-cookie") || equalIgnoringCase(name, "set-cookie2"); +} + +static void replaceCharsetInMediaType(String& mediaType, const String& charsetValue) +{ + unsigned int pos = 0, len = 0; + + findCharsetInMediaType(mediaType, pos, len); + + if (!len) { + // When no charset found, do nothing. + return; + } + + // Found at least one existing charset, replace all occurrences with new charset. + while (len) { + mediaType.replace(pos, len, charsetValue); + unsigned int start = pos + charsetValue.length(); + findCharsetInMediaType(mediaType, pos, len, start); + } +} + +static const XMLHttpRequestStaticData* staticData = 0; + +static const XMLHttpRequestStaticData* createXMLHttpRequestStaticData() +{ + staticData = new XMLHttpRequestStaticData; + return staticData; +} + +static const XMLHttpRequestStaticData* initializeXMLHttpRequestStaticData() +{ + // Uses dummy to avoid warnings about an unused variable. + AtomicallyInitializedStatic(const XMLHttpRequestStaticData*, dummy = createXMLHttpRequestStaticData()); + return dummy; +} + +XMLHttpRequest::XMLHttpRequest(ScriptExecutionContext* context) + : ActiveDOMObject(context, this) + , m_async(true) + , m_includeCredentials(false) + , m_state(UNSENT) + , m_createdDocument(false) + , m_error(false) + , m_uploadEventsAllowed(true) + , m_uploadComplete(false) + , m_sameOriginRequest(true) + , m_receivedLength(0) + , m_lastSendLineNumber(0) + , m_exceptionCode(0) + , m_progressEventThrottle(this) + , m_responseTypeCode(ResponseTypeDefault) +{ + initializeXMLHttpRequestStaticData(); +#ifndef NDEBUG + xmlHttpRequestCounter.increment(); +#endif +} + +XMLHttpRequest::~XMLHttpRequest() +{ + if (m_upload) + m_upload->disconnectXMLHttpRequest(); + +#ifndef NDEBUG + xmlHttpRequestCounter.decrement(); +#endif +} + +Document* XMLHttpRequest::document() const +{ + ASSERT(scriptExecutionContext()->isDocument()); + return static_cast<Document*>(scriptExecutionContext()); +} + +#if ENABLE(DASHBOARD_SUPPORT) +bool XMLHttpRequest::usesDashboardBackwardCompatibilityMode() const +{ + if (scriptExecutionContext()->isWorkerContext()) + return false; + Settings* settings = document()->settings(); + return settings && settings->usesDashboardBackwardCompatibilityMode(); +} +#endif + +XMLHttpRequest::State XMLHttpRequest::readyState() const +{ + return m_state; +} + +String XMLHttpRequest::responseText(ExceptionCode& ec) +{ + if (responseTypeCode() != ResponseTypeDefault && responseTypeCode() != ResponseTypeText) { + ec = INVALID_STATE_ERR; + return ""; + } + return m_responseBuilder.toStringPreserveCapacity(); +} + +Document* XMLHttpRequest::responseXML(ExceptionCode& ec) +{ + if (responseTypeCode() != ResponseTypeDefault && responseTypeCode() != ResponseTypeText && responseTypeCode() != ResponseTypeDocument) { + ec = INVALID_STATE_ERR; + return 0; + } + + if (m_state != DONE) + return 0; + + if (!m_createdDocument) { + if ((m_response.isHTTP() && !responseIsXML()) || scriptExecutionContext()->isWorkerContext()) { + // The W3C spec requires this. + m_responseXML = 0; + } else { + m_responseXML = Document::create(0, m_url); + m_responseXML->open(); + // FIXME: Set Last-Modified. + m_responseXML->write(m_responseBuilder.toStringPreserveCapacity()); + m_responseXML->finishParsing(); + m_responseXML->close(); + + if (!m_responseXML->wellFormed()) + m_responseXML = 0; + } + m_createdDocument = true; + } + + return m_responseXML.get(); +} + +#if ENABLE(XHR_RESPONSE_BLOB) +Blob* XMLHttpRequest::responseBlob(ExceptionCode& ec) const +{ + if (responseTypeCode() != ResponseTypeBlob) { + ec = INVALID_STATE_ERR; + return 0; + } + return m_responseBlob.get(); +} +#endif + +#if ENABLE(3D_CANVAS) || ENABLE(BLOB) +ArrayBuffer* XMLHttpRequest::responseArrayBuffer(ExceptionCode& ec) +{ + if (m_responseTypeCode != ResponseTypeArrayBuffer) { + ec = INVALID_STATE_ERR; + return 0; + } + + if (m_state != DONE) + return 0; + + if (!m_responseArrayBuffer.get() && m_binaryResponseBuilder.get() && m_binaryResponseBuilder->size() > 0) { + m_responseArrayBuffer = ArrayBuffer::create(const_cast<char*>(m_binaryResponseBuilder->data()), static_cast<unsigned>(m_binaryResponseBuilder->size())); + m_binaryResponseBuilder.clear(); + } + + if (m_responseArrayBuffer.get()) + return m_responseArrayBuffer.get(); + + return 0; +} +#endif + +void XMLHttpRequest::setResponseType(const String& responseType, ExceptionCode& ec) +{ + if (m_state != OPENED || m_loader) { + ec = INVALID_STATE_ERR; + return; + } + + if (responseType == "") + m_responseTypeCode = ResponseTypeDefault; + else if (responseType == "text") + m_responseTypeCode = ResponseTypeText; + else if (responseType == "document") + m_responseTypeCode = ResponseTypeDocument; + else if (responseType == "blob") { +#if ENABLE(XHR_RESPONSE_BLOB) + m_responseTypeCode = ResponseTypeBlob; +#endif + } else if (responseType == "arraybuffer") { +#if ENABLE(3D_CANVAS) || ENABLE(BLOB) + m_responseTypeCode = ResponseTypeArrayBuffer; +#endif + } else + ec = SYNTAX_ERR; +} + +String XMLHttpRequest::responseType() +{ + switch (m_responseTypeCode) { + case ResponseTypeDefault: + return ""; + case ResponseTypeText: + return "text"; + case ResponseTypeDocument: + return "document"; + case ResponseTypeBlob: + return "blob"; + case ResponseTypeArrayBuffer: + return "arraybuffer"; + } + return ""; +} + +XMLHttpRequestUpload* XMLHttpRequest::upload() +{ + if (!m_upload) + m_upload = XMLHttpRequestUpload::create(this); + return m_upload.get(); +} + +void XMLHttpRequest::changeState(State newState) +{ + if (m_state != newState) { + m_state = newState; + callReadyStateChangeListener(); + } +} + +void XMLHttpRequest::callReadyStateChangeListener() +{ + if (!scriptExecutionContext()) + return; + + InspectorInstrumentationCookie cookie = InspectorInstrumentation::willChangeXHRReadyState(scriptExecutionContext(), this); + + if (m_async || (m_state <= OPENED || m_state == DONE)) + m_progressEventThrottle.dispatchEvent(XMLHttpRequestProgressEvent::create(eventNames().readystatechangeEvent), m_state == DONE ? FlushProgressEvent : DoNotFlushProgressEvent); + + InspectorInstrumentation::didChangeXHRReadyState(cookie); + + if (m_state == DONE && !m_error) { + InspectorInstrumentationCookie cookie = InspectorInstrumentation::willLoadXHR(scriptExecutionContext(), this); + m_progressEventThrottle.dispatchEvent(XMLHttpRequestProgressEvent::create(eventNames().loadEvent)); + InspectorInstrumentation::didLoadXHR(cookie); + } +} + +void XMLHttpRequest::setWithCredentials(bool value, ExceptionCode& ec) +{ + if (m_state != OPENED || m_loader) { + ec = INVALID_STATE_ERR; + return; + } + + m_includeCredentials = value; +} + +#if ENABLE(XHR_RESPONSE_BLOB) +void XMLHttpRequest::setAsBlob(bool value, ExceptionCode& ec) +{ + if (m_state != OPENED || m_loader) { + ec = INVALID_STATE_ERR; + return; + } + + m_responseTypeCode = value ? ResponseTypeBlob : ResponseTypeDefault; +} +#endif + +void XMLHttpRequest::open(const String& method, const KURL& url, ExceptionCode& ec) +{ + open(method, url, true, ec); +} + +void XMLHttpRequest::open(const String& method, const KURL& url, bool async, ExceptionCode& ec) +{ + internalAbort(); + State previousState = m_state; + m_state = UNSENT; + m_error = false; + m_responseTypeCode = ResponseTypeDefault; + m_uploadComplete = false; + + // clear stuff from possible previous load + clearResponse(); + clearRequest(); + + ASSERT(m_state == UNSENT); + + if (!isValidToken(method)) { + ec = SYNTAX_ERR; + return; + } + + // Method names are case sensitive. But since Firefox uppercases method names it knows, we'll do the same. + String methodUpper(method.upper()); + + if (methodUpper == "TRACE" || methodUpper == "TRACK" || methodUpper == "CONNECT") { + ec = SECURITY_ERR; + return; + } + + m_url = url; + + if (methodUpper == "COPY" || methodUpper == "DELETE" || methodUpper == "GET" || methodUpper == "HEAD" + || methodUpper == "INDEX" || methodUpper == "LOCK" || methodUpper == "M-POST" || methodUpper == "MKCOL" || methodUpper == "MOVE" + || methodUpper == "OPTIONS" || methodUpper == "POST" || methodUpper == "PROPFIND" || methodUpper == "PROPPATCH" || methodUpper == "PUT" + || methodUpper == "UNLOCK") + m_method = methodUpper; + else + m_method = method; + + m_async = async; + + ASSERT(!m_loader); + + // Check previous state to avoid dispatching readyState event + // when calling open several times in a row. + if (previousState != OPENED) + changeState(OPENED); + else + m_state = OPENED; +} + +void XMLHttpRequest::open(const String& method, const KURL& url, bool async, const String& user, ExceptionCode& ec) +{ + KURL urlWithCredentials(url); + urlWithCredentials.setUser(user); + + open(method, urlWithCredentials, async, ec); +} + +void XMLHttpRequest::open(const String& method, const KURL& url, bool async, const String& user, const String& password, ExceptionCode& ec) +{ + KURL urlWithCredentials(url); + urlWithCredentials.setUser(user); + urlWithCredentials.setPass(password); + + open(method, urlWithCredentials, async, ec); +} + +bool XMLHttpRequest::initSend(ExceptionCode& ec) +{ + if (!scriptExecutionContext()) + return false; + + if (m_state != OPENED || m_loader) { + ec = INVALID_STATE_ERR; + return false; + } + + m_error = false; + return true; +} + +void XMLHttpRequest::send(ExceptionCode& ec) +{ + send(String(), ec); +} + +void XMLHttpRequest::send(Document* document, ExceptionCode& ec) +{ + ASSERT(document); + + if (!initSend(ec)) + return; + + if (m_method != "GET" && m_method != "HEAD" && m_url.protocolInHTTPFamily()) { + String contentType = getRequestHeader("Content-Type"); + if (contentType.isEmpty()) { +#if ENABLE(DASHBOARD_SUPPORT) + if (usesDashboardBackwardCompatibilityMode()) + setRequestHeaderInternal("Content-Type", "application/x-www-form-urlencoded"); + else +#endif + // FIXME: this should include the charset used for encoding. + setRequestHeaderInternal("Content-Type", "application/xml"); + } + + // FIXME: According to XMLHttpRequest Level 2, this should use the Document.innerHTML algorithm + // from the HTML5 specification to serialize the document. + String body = createMarkup(document); + + // FIXME: this should use value of document.inputEncoding to determine the encoding to use. + TextEncoding encoding = UTF8Encoding(); + m_requestEntityBody = FormData::create(encoding.encode(body.characters(), body.length(), EntitiesForUnencodables)); + if (m_upload) + m_requestEntityBody->setAlwaysStream(true); + } + + createRequest(ec); +} + +void XMLHttpRequest::send(const String& body, ExceptionCode& ec) +{ + if (!initSend(ec)) + return; + + if (!body.isNull() && m_method != "GET" && m_method != "HEAD" && m_url.protocolInHTTPFamily()) { + String contentType = getRequestHeader("Content-Type"); + if (contentType.isEmpty()) { +#if ENABLE(DASHBOARD_SUPPORT) + if (usesDashboardBackwardCompatibilityMode()) + setRequestHeaderInternal("Content-Type", "application/x-www-form-urlencoded"); + else +#endif + setRequestHeaderInternal("Content-Type", "application/xml"); + } else { + replaceCharsetInMediaType(contentType, "UTF-8"); + m_requestHeaders.set("Content-Type", contentType); + } + + m_requestEntityBody = FormData::create(UTF8Encoding().encode(body.characters(), body.length(), EntitiesForUnencodables)); + if (m_upload) + m_requestEntityBody->setAlwaysStream(true); + } + + createRequest(ec); +} + +void XMLHttpRequest::send(Blob* body, ExceptionCode& ec) +{ + if (!initSend(ec)) + return; + + if (m_method != "GET" && m_method != "HEAD" && m_url.protocolInHTTPFamily()) { + // FIXME: Should we set a Content-Type if one is not set. + // FIXME: add support for uploading bundles. + m_requestEntityBody = FormData::create(); + if (body->isFile()) + m_requestEntityBody->appendFile(static_cast<File*>(body)->path()); +#if ENABLE(BLOB) + else + m_requestEntityBody->appendBlob(body->url()); +#endif + } + + createRequest(ec); +} + +void XMLHttpRequest::send(DOMFormData* body, ExceptionCode& ec) +{ + if (!initSend(ec)) + return; + + if (m_method != "GET" && m_method != "HEAD" && m_url.protocolInHTTPFamily()) { + m_requestEntityBody = FormData::createMultiPart(*(static_cast<FormDataList*>(body)), body->encoding(), document()); + + // We need to ask the client to provide the generated file names if needed. When FormData fills the element + // for the file, it could set a flag to use the generated file name, i.e. a package file on Mac. + m_requestEntityBody->generateFiles(document()); + + String contentType = getRequestHeader("Content-Type"); + if (contentType.isEmpty()) { + contentType = "multipart/form-data; boundary="; + contentType += m_requestEntityBody->boundary().data(); + setRequestHeaderInternal("Content-Type", contentType); + } + } + + createRequest(ec); +} + +#if ENABLE(3D_CANVAS) || ENABLE(BLOB) +void XMLHttpRequest::send(ArrayBuffer* body, ExceptionCode& ec) +{ + if (!initSend(ec)) + return; + + if (m_method != "GET" && m_method != "HEAD" && m_url.protocolInHTTPFamily()) { + m_requestEntityBody = FormData::create(body->data(), body->byteLength()); + if (m_upload) + m_requestEntityBody->setAlwaysStream(true); + } + + createRequest(ec); +} +#endif + +void XMLHttpRequest::createRequest(ExceptionCode& ec) +{ +#if ENABLE(BLOB) + // Only GET request is supported for blob URL. + if (m_url.protocolIs("blob") && m_method != "GET") { + ec = XMLHttpRequestException::NETWORK_ERR; + return; + } +#endif + + // The presence of upload event listeners forces us to use preflighting because POSTing to an URL that does not + // permit cross origin requests should look exactly like POSTing to an URL that does not respond at all. + // Also, only async requests support upload progress events. + bool uploadEvents = false; + if (m_async) { + m_progressEventThrottle.dispatchEvent(XMLHttpRequestProgressEvent::create(eventNames().loadstartEvent)); + if (m_requestEntityBody && m_upload) { + uploadEvents = m_upload->hasEventListeners(); + m_upload->dispatchEvent(XMLHttpRequestProgressEvent::create(eventNames().loadstartEvent)); + } + } + + m_sameOriginRequest = scriptExecutionContext()->securityOrigin()->canRequest(m_url); + + // We also remember whether upload events should be allowed for this request in case the upload listeners are + // added after the request is started. + m_uploadEventsAllowed = m_sameOriginRequest || uploadEvents || !isSimpleCrossOriginAccessRequest(m_method, m_requestHeaders); + + ResourceRequest request(m_url); + request.setHTTPMethod(m_method); + + if (m_requestEntityBody) { + ASSERT(m_method != "GET"); + ASSERT(m_method != "HEAD"); + request.setHTTPBody(m_requestEntityBody.release()); + } + + if (m_requestHeaders.size() > 0) + request.addHTTPHeaderFields(m_requestHeaders); + + ThreadableLoaderOptions options; + options.sendLoadCallbacks = true; + options.sniffContent = false; + options.forcePreflight = uploadEvents; + options.allowCredentials = m_sameOriginRequest || m_includeCredentials; + options.crossOriginRequestPolicy = UseAccessControl; + + m_exceptionCode = 0; + m_error = false; + + if (m_async) { + if (m_upload) + request.setReportUploadProgress(true); + + // ThreadableLoader::create can return null here, for example if we're no longer attached to a page. + // This is true while running onunload handlers. + // FIXME: Maybe we need to be able to send XMLHttpRequests from onunload, <http://bugs.webkit.org/show_bug.cgi?id=10904>. + // FIXME: Maybe create() can return null for other reasons too? + m_loader = ThreadableLoader::create(scriptExecutionContext(), this, request, options); + if (m_loader) { + // Neither this object nor the JavaScript wrapper should be deleted while + // a request is in progress because we need to keep the listeners alive, + // and they are referenced by the JavaScript wrapper. + setPendingActivity(this); + } + } else + ThreadableLoader::loadResourceSynchronously(scriptExecutionContext(), request, *this, options); + + if (!m_exceptionCode && m_error) + m_exceptionCode = XMLHttpRequestException::NETWORK_ERR; + ec = m_exceptionCode; +} + +void XMLHttpRequest::abort() +{ + // internalAbort() calls dropProtection(), which may release the last reference. + RefPtr<XMLHttpRequest> protect(this); + + bool sendFlag = m_loader; + + internalAbort(); + + m_responseBuilder.clear(); + m_createdDocument = false; + m_responseXML = 0; +#if ENABLE(XHR_RESPONSE_BLOB) + m_responseBlob = 0; +#endif + + // Clear headers as required by the spec + m_requestHeaders.clear(); + + if ((m_state <= OPENED && !sendFlag) || m_state == DONE) + m_state = UNSENT; + else { + ASSERT(!m_loader); + changeState(DONE); + m_state = UNSENT; + } + + m_progressEventThrottle.dispatchEvent(XMLHttpRequestProgressEvent::create(eventNames().abortEvent)); + if (!m_uploadComplete) { + m_uploadComplete = true; + if (m_upload && m_uploadEventsAllowed) + m_upload->dispatchEvent(XMLHttpRequestProgressEvent::create(eventNames().abortEvent)); + } +} + +void XMLHttpRequest::internalAbort() +{ + bool hadLoader = m_loader; + + m_error = true; + + // FIXME: when we add the support for multi-part XHR, we will have to think be careful with this initialization. + m_receivedLength = 0; + + if (hadLoader) { + m_loader->cancel(); + m_loader = 0; + } + + m_decoder = 0; + + if (hadLoader) + dropProtection(); +} + +void XMLHttpRequest::clearResponse() +{ + m_response = ResourceResponse(); + m_responseBuilder.clear(); + m_createdDocument = false; + m_responseXML = 0; +#if ENABLE(XHR_RESPONSE_BLOB) + m_responseBlob = 0; +#endif +#if ENABLE(3D_CANVAS) || ENABLE(BLOB) + m_binaryResponseBuilder.clear(); + m_responseArrayBuffer.clear(); +#endif +} + +void XMLHttpRequest::clearRequest() +{ + m_requestHeaders.clear(); + m_requestEntityBody = 0; +} + +void XMLHttpRequest::genericError() +{ + clearResponse(); + clearRequest(); + m_error = true; + + changeState(DONE); +} + +void XMLHttpRequest::networkError() +{ + genericError(); + m_progressEventThrottle.dispatchEvent(XMLHttpRequestProgressEvent::create(eventNames().errorEvent)); + if (!m_uploadComplete) { + m_uploadComplete = true; + if (m_upload && m_uploadEventsAllowed) + m_upload->dispatchEvent(XMLHttpRequestProgressEvent::create(eventNames().errorEvent)); + } + internalAbort(); +} + +void XMLHttpRequest::abortError() +{ + genericError(); + m_progressEventThrottle.dispatchEvent(XMLHttpRequestProgressEvent::create(eventNames().abortEvent)); + if (!m_uploadComplete) { + m_uploadComplete = true; + if (m_upload && m_uploadEventsAllowed) + m_upload->dispatchEvent(XMLHttpRequestProgressEvent::create(eventNames().abortEvent)); + } +} + +void XMLHttpRequest::dropProtection() +{ +#if USE(JSC) + // The XHR object itself holds on to the responseText, and + // thus has extra cost even independent of any + // responseText or responseXML objects it has handed + // out. But it is protected from GC while loading, so this + // can't be recouped until the load is done, so only + // report the extra cost at that point. + JSC::JSGlobalData* globalData = scriptExecutionContext()->globalData(); + if (hasCachedDOMObjectWrapper(globalData, this)) + globalData->heap.reportExtraMemoryCost(m_responseBuilder.length() * 2); +#endif + + unsetPendingActivity(this); +} + +void XMLHttpRequest::overrideMimeType(const String& override) +{ + m_mimeTypeOverride = override; +} + +static void reportUnsafeUsage(ScriptExecutionContext* context, const String& message) +{ + if (!context) + return; + // FIXME: It's not good to report the bad usage without indicating what source line it came from. + // We should pass additional parameters so we can tell the console where the mistake occurred. + context->addMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, message, 1, String()); +} + +void XMLHttpRequest::setRequestHeader(const AtomicString& name, const String& value, ExceptionCode& ec) +{ + if (m_state != OPENED || m_loader) { +#if ENABLE(DASHBOARD_SUPPORT) + if (usesDashboardBackwardCompatibilityMode()) + return; +#endif + + ec = INVALID_STATE_ERR; + return; + } + + if (!isValidToken(name) || !isValidHeaderValue(value)) { + ec = SYNTAX_ERR; + return; + } + + // A privileged script (e.g. a Dashboard widget) can set any headers. + if (!scriptExecutionContext()->securityOrigin()->canLoadLocalResources() && !isSafeRequestHeader(name)) { + reportUnsafeUsage(scriptExecutionContext(), "Refused to set unsafe header \"" + name + "\""); + return; + } + + setRequestHeaderInternal(name, value); +} + +void XMLHttpRequest::setRequestHeaderInternal(const AtomicString& name, const String& value) +{ + pair<HTTPHeaderMap::iterator, bool> result = m_requestHeaders.add(name, value); + if (!result.second) + result.first->second += ", " + value; +} + +bool XMLHttpRequest::isSafeRequestHeader(const String& name) const +{ + return !staticData->m_forbiddenRequestHeaders.contains(name) && !name.startsWith(staticData->m_proxyHeaderPrefix, false) + && !name.startsWith(staticData->m_secHeaderPrefix, false); +} + +String XMLHttpRequest::getRequestHeader(const AtomicString& name) const +{ + return m_requestHeaders.get(name); +} + +String XMLHttpRequest::getAllResponseHeaders(ExceptionCode& ec) const +{ + if (m_state < HEADERS_RECEIVED) { + ec = INVALID_STATE_ERR; + return ""; + } + + Vector<UChar> stringBuilder; + + HTTPHeaderMap::const_iterator end = m_response.httpHeaderFields().end(); + for (HTTPHeaderMap::const_iterator it = m_response.httpHeaderFields().begin(); it!= end; ++it) { + // Hide Set-Cookie header fields from the XMLHttpRequest client for these reasons: + // 1) If the client did have access to the fields, then it could read HTTP-only + // cookies; those cookies are supposed to be hidden from scripts. + // 2) There's no known harm in hiding Set-Cookie header fields entirely; we don't + // know any widely used technique that requires access to them. + // 3) Firefox has implemented this policy. + if (isSetCookieHeader(it->first) && !scriptExecutionContext()->securityOrigin()->canLoadLocalResources()) + continue; + + if (!m_sameOriginRequest && !isOnAccessControlResponseHeaderWhitelist(it->first)) + continue; + + stringBuilder.append(it->first.characters(), it->first.length()); + stringBuilder.append(':'); + stringBuilder.append(' '); + stringBuilder.append(it->second.characters(), it->second.length()); + stringBuilder.append('\r'); + stringBuilder.append('\n'); + } + + return String::adopt(stringBuilder); +} + +String XMLHttpRequest::getResponseHeader(const AtomicString& name, ExceptionCode& ec) const +{ + if (m_state < HEADERS_RECEIVED) { + ec = INVALID_STATE_ERR; + return String(); + } + + // See comment in getAllResponseHeaders above. + if (isSetCookieHeader(name) && !scriptExecutionContext()->securityOrigin()->canLoadLocalResources()) { + reportUnsafeUsage(scriptExecutionContext(), "Refused to get unsafe header \"" + name + "\""); + return String(); + } + + if (!m_sameOriginRequest && !isOnAccessControlResponseHeaderWhitelist(name)) { + reportUnsafeUsage(scriptExecutionContext(), "Refused to get unsafe header \"" + name + "\""); + return String(); + } + return m_response.httpHeaderField(name); +} + +String XMLHttpRequest::responseMIMEType() const +{ + String mimeType = extractMIMETypeFromMediaType(m_mimeTypeOverride); + if (mimeType.isEmpty()) { + if (m_response.isHTTP()) + mimeType = extractMIMETypeFromMediaType(m_response.httpHeaderField("Content-Type")); + else + mimeType = m_response.mimeType(); + } + if (mimeType.isEmpty()) + mimeType = "text/xml"; + + return mimeType; +} + +bool XMLHttpRequest::responseIsXML() const +{ + return DOMImplementation::isXMLMIMEType(responseMIMEType()); +} + +int XMLHttpRequest::status(ExceptionCode& ec) const +{ + if (m_response.httpStatusCode()) + return m_response.httpStatusCode(); + + if (m_state == OPENED) { + // Firefox only raises an exception in this state; we match it. + // Note the case of local file requests, where we have no HTTP response code! Firefox never raises an exception for those, but we match HTTP case for consistency. + ec = INVALID_STATE_ERR; + } + + return 0; +} + +String XMLHttpRequest::statusText(ExceptionCode& ec) const +{ + if (!m_response.httpStatusText().isNull()) + return m_response.httpStatusText(); + + if (m_state == OPENED) { + // See comments in status() above. + ec = INVALID_STATE_ERR; + } + + return String(); +} + +void XMLHttpRequest::didFail(const ResourceError& error) +{ + + // If we are already in an error state, for instance we called abort(), bail out early. + if (m_error) + return; + + if (error.isCancellation()) { + m_exceptionCode = XMLHttpRequestException::ABORT_ERR; + abortError(); + return; + } + + // Network failures are already reported to Web Inspector by ResourceLoader. + if (error.domain() == errorDomainWebKitInternal) + reportUnsafeUsage(scriptExecutionContext(), "XMLHttpRequest cannot load " + error.failingURL() + ". " + error.localizedDescription()); + + m_exceptionCode = XMLHttpRequestException::NETWORK_ERR; + networkError(); +} + +void XMLHttpRequest::didFailRedirectCheck() +{ + networkError(); +} + +void XMLHttpRequest::didFinishLoading(unsigned long identifier) +{ + if (m_error) + return; + + if (m_state < HEADERS_RECEIVED) + changeState(HEADERS_RECEIVED); + + if (m_decoder) + m_responseBuilder.append(m_decoder->flush()); + + m_responseBuilder.shrinkToFit(); + +#if ENABLE(XHR_RESPONSE_BLOB) + // FIXME: Set m_responseBlob to something here in the ResponseTypeBlob case. +#endif + +#if ENABLE(INSPECTOR) + if (InspectorController* inspector = scriptExecutionContext()->inspectorController()) + inspector->resourceRetrievedByXMLHttpRequest(identifier, m_responseBuilder.toStringPreserveCapacity(), m_url, m_lastSendURL, m_lastSendLineNumber); +#endif + + bool hadLoader = m_loader; + m_loader = 0; + + changeState(DONE); + m_decoder = 0; + + if (hadLoader) + dropProtection(); +} + +void XMLHttpRequest::didSendData(unsigned long long bytesSent, unsigned long long totalBytesToBeSent) +{ + if (!m_upload) + return; + + if (m_uploadEventsAllowed) + m_upload->dispatchEvent(XMLHttpRequestProgressEvent::create(eventNames().progressEvent, true, bytesSent, totalBytesToBeSent)); + + if (bytesSent == totalBytesToBeSent && !m_uploadComplete) { + m_uploadComplete = true; + if (m_uploadEventsAllowed) + m_upload->dispatchEvent(XMLHttpRequestProgressEvent::create(eventNames().loadEvent)); + } +} + +void XMLHttpRequest::didReceiveResponse(const ResourceResponse& response) +{ + m_response = response; + m_responseEncoding = extractCharsetFromMediaType(m_mimeTypeOverride); + if (m_responseEncoding.isEmpty()) + m_responseEncoding = response.textEncodingName(); +} + +void XMLHttpRequest::didReceiveAuthenticationCancellation(const ResourceResponse& failureResponse) +{ + m_response = failureResponse; +} + +void XMLHttpRequest::didReceiveData(const char* data, int len) +{ + if (m_error) + return; + + if (m_state < HEADERS_RECEIVED) + changeState(HEADERS_RECEIVED); + + bool useDecoder = responseTypeCode() == ResponseTypeDefault || responseTypeCode() == ResponseTypeText || responseTypeCode() == ResponseTypeDocument; + + if (useDecoder && !m_decoder) { + if (!m_responseEncoding.isEmpty()) + m_decoder = TextResourceDecoder::create("text/plain", m_responseEncoding); + // allow TextResourceDecoder to look inside the m_response if it's XML or HTML + else if (responseIsXML()) { + m_decoder = TextResourceDecoder::create("application/xml"); + // Don't stop on encoding errors, unlike it is done for other kinds of XML resources. This matches the behavior of previous WebKit versions, Firefox and Opera. + m_decoder->useLenientXMLDecoding(); + } else if (responseMIMEType() == "text/html") + m_decoder = TextResourceDecoder::create("text/html", "UTF-8"); + else + m_decoder = TextResourceDecoder::create("text/plain", "UTF-8"); + } + + if (!len) + return; + + if (len == -1) + len = strlen(data); + + if (useDecoder) + m_responseBuilder.append(m_decoder->decode(data, len)); +#if ENABLE(3D_CANVAS) || ENABLE(BLOB) + else if (responseTypeCode() == ResponseTypeArrayBuffer) { + // Buffer binary data. + if (!m_binaryResponseBuilder) + m_binaryResponseBuilder = SharedBuffer::create(); + m_binaryResponseBuilder->append(data, len); + } +#endif + + if (!m_error) { + long long expectedLength = m_response.expectedContentLength(); + m_receivedLength += len; + + if (m_async) { + bool lengthComputable = expectedLength && m_receivedLength <= expectedLength; + m_progressEventThrottle.dispatchProgressEvent(lengthComputable, m_receivedLength, expectedLength); + } + + if (m_state != LOADING) + changeState(LOADING); + else + // Firefox calls readyStateChanged every time it receives data, 4449442 + callReadyStateChangeListener(); + } +} + +bool XMLHttpRequest::canSuspend() const +{ + return !m_loader; +} + +void XMLHttpRequest::suspend(ReasonForSuspension) +{ + m_progressEventThrottle.suspend(); +} + +void XMLHttpRequest::resume() +{ + m_progressEventThrottle.resume(); +} + +void XMLHttpRequest::stop() +{ + internalAbort(); +} + +void XMLHttpRequest::contextDestroyed() +{ + ASSERT(!m_loader); + ActiveDOMObject::contextDestroyed(); +} + +ScriptExecutionContext* XMLHttpRequest::scriptExecutionContext() const +{ + return ActiveDOMObject::scriptExecutionContext(); +} + +EventTargetData* XMLHttpRequest::eventTargetData() +{ + return &m_eventTargetData; +} + +EventTargetData* XMLHttpRequest::ensureEventTargetData() +{ + return &m_eventTargetData; +} + +} // namespace WebCore diff --git a/Source/WebCore/xml/XMLHttpRequest.h b/Source/WebCore/xml/XMLHttpRequest.h new file mode 100644 index 0000000..bc6815d --- /dev/null +++ b/Source/WebCore/xml/XMLHttpRequest.h @@ -0,0 +1,237 @@ +/* + * Copyright (C) 2003, 2006, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2005, 2006 Alexey Proskuryakov <ap@nypop.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef XMLHttpRequest_h +#define XMLHttpRequest_h + +#include "ActiveDOMObject.h" +#include "EventListener.h" +#include "EventNames.h" +#include "EventTarget.h" +#include "FormData.h" +#include "ResourceResponse.h" +#include "ThreadableLoaderClient.h" +#include "XMLHttpRequestProgressEventThrottle.h" +#include <wtf/OwnPtr.h> +#include <wtf/text/AtomicStringHash.h> +#include <wtf/text/StringBuilder.h> + +namespace WebCore { + +class ArrayBuffer; +class Blob; +class Document; +class DOMFormData; +class ResourceRequest; +class SharedBuffer; +class TextResourceDecoder; +class ThreadableLoader; + +class XMLHttpRequest : public RefCounted<XMLHttpRequest>, public EventTarget, private ThreadableLoaderClient, public ActiveDOMObject { +public: + static PassRefPtr<XMLHttpRequest> create(ScriptExecutionContext* context) { return adoptRef(new XMLHttpRequest(context)); } + ~XMLHttpRequest(); + + // These exact numeric values are important because JS expects them. + enum State { + UNSENT = 0, + OPENED = 1, + HEADERS_RECEIVED = 2, + LOADING = 3, + DONE = 4 + }; + + enum ResponseTypeCode { + ResponseTypeDefault, + ResponseTypeText, + ResponseTypeDocument, + ResponseTypeBlob, + ResponseTypeArrayBuffer + }; + + virtual XMLHttpRequest* toXMLHttpRequest() { return this; } + + virtual void contextDestroyed(); + virtual bool canSuspend() const; + virtual void suspend(ReasonForSuspension); + virtual void resume(); + virtual void stop(); + + virtual ScriptExecutionContext* scriptExecutionContext() const; + + const KURL& url() const { return m_url; } + String statusText(ExceptionCode&) const; + int status(ExceptionCode&) const; + State readyState() const; + bool withCredentials() const { return m_includeCredentials; } + void setWithCredentials(bool, ExceptionCode&); +#if ENABLE(XHR_RESPONSE_BLOB) + bool asBlob() const { return responseTypeCode() == ResponseTypeBlob; } + void setAsBlob(bool, ExceptionCode&); +#endif + void open(const String& method, const KURL&, ExceptionCode&); + void open(const String& method, const KURL&, bool async, ExceptionCode&); + void open(const String& method, const KURL&, bool async, const String& user, ExceptionCode&); + void open(const String& method, const KURL&, bool async, const String& user, const String& password, ExceptionCode&); + void send(ExceptionCode&); + void send(Document*, ExceptionCode&); + void send(const String&, ExceptionCode&); + void send(Blob*, ExceptionCode&); + void send(DOMFormData*, ExceptionCode&); +#if ENABLE(3D_CANVAS) || ENABLE(BLOB) + void send(ArrayBuffer*, ExceptionCode&); +#endif + void abort(); + void setRequestHeader(const AtomicString& name, const String& value, ExceptionCode&); + void overrideMimeType(const String& override); + String getAllResponseHeaders(ExceptionCode&) const; + String getResponseHeader(const AtomicString& name, ExceptionCode&) const; + String responseText(ExceptionCode&); + Document* responseXML(ExceptionCode&); + Document* optionalResponseXML() const { return m_responseXML.get(); } +#if ENABLE(XHR_RESPONSE_BLOB) + Blob* responseBlob(ExceptionCode&) const; + Blob* optionalResponseBlob() const { return m_responseBlob.get(); } +#endif + + void setResponseType(const String&, ExceptionCode&); + String responseType(); + ResponseTypeCode responseTypeCode() const { return m_responseTypeCode; } + +#if ENABLE(3D_CANVAS) || ENABLE(BLOB) + // response attribute has custom getter. + ArrayBuffer* responseArrayBuffer(ExceptionCode&); + ArrayBuffer* optionalResponseArrayBuffer() const { return m_responseArrayBuffer.get(); } +#endif + + void setLastSendLineNumber(unsigned lineNumber) { m_lastSendLineNumber = lineNumber; } + void setLastSendURL(const String& url) { m_lastSendURL = url; } + + XMLHttpRequestUpload* upload(); + XMLHttpRequestUpload* optionalUpload() const { return m_upload.get(); } + + DEFINE_ATTRIBUTE_EVENT_LISTENER(readystatechange); + DEFINE_ATTRIBUTE_EVENT_LISTENER(abort); + DEFINE_ATTRIBUTE_EVENT_LISTENER(error); + DEFINE_ATTRIBUTE_EVENT_LISTENER(load); + DEFINE_ATTRIBUTE_EVENT_LISTENER(loadstart); + DEFINE_ATTRIBUTE_EVENT_LISTENER(progress); + + using RefCounted<XMLHttpRequest>::ref; + using RefCounted<XMLHttpRequest>::deref; + +private: + XMLHttpRequest(ScriptExecutionContext*); + + virtual void refEventTarget() { ref(); } + virtual void derefEventTarget() { deref(); } + virtual EventTargetData* eventTargetData(); + virtual EventTargetData* ensureEventTargetData(); + + Document* document() const; + +#if ENABLE(DASHBOARD_SUPPORT) + bool usesDashboardBackwardCompatibilityMode() const; +#endif + + virtual void didSendData(unsigned long long bytesSent, unsigned long long totalBytesToBeSent); + virtual void didReceiveResponse(const ResourceResponse&); + virtual void didReceiveData(const char* data, int lengthReceived); + virtual void didFinishLoading(unsigned long identifier); + virtual void didFail(const ResourceError&); + virtual void didFailRedirectCheck(); + virtual void didReceiveAuthenticationCancellation(const ResourceResponse&); + + String responseMIMEType() const; + bool responseIsXML() const; + + bool initSend(ExceptionCode&); + + String getRequestHeader(const AtomicString& name) const; + void setRequestHeaderInternal(const AtomicString& name, const String& value); + bool isSafeRequestHeader(const String&) const; + + void changeState(State newState); + void callReadyStateChangeListener(); + void dropProtection(); + void internalAbort(); + void clearResponse(); + void clearRequest(); + + void createRequest(ExceptionCode&); + + void genericError(); + void networkError(); + void abortError(); + + RefPtr<XMLHttpRequestUpload> m_upload; + + KURL m_url; + String m_method; + HTTPHeaderMap m_requestHeaders; + RefPtr<FormData> m_requestEntityBody; + String m_mimeTypeOverride; + bool m_async; + bool m_includeCredentials; +#if ENABLE(XHR_RESPONSE_BLOB) + RefPtr<Blob> m_responseBlob; +#endif + + RefPtr<ThreadableLoader> m_loader; + State m_state; + + ResourceResponse m_response; + String m_responseEncoding; + + RefPtr<TextResourceDecoder> m_decoder; + + StringBuilder m_responseBuilder; + mutable bool m_createdDocument; + mutable RefPtr<Document> m_responseXML; + +#if ENABLE(3D_CANVAS) || ENABLE(BLOB) + RefPtr<SharedBuffer> m_binaryResponseBuilder; + mutable RefPtr<ArrayBuffer> m_responseArrayBuffer; +#endif + + bool m_error; + + bool m_uploadEventsAllowed; + bool m_uploadComplete; + + bool m_sameOriginRequest; + + // Used for onprogress tracking + long long m_receivedLength; + + unsigned m_lastSendLineNumber; + String m_lastSendURL; + ExceptionCode m_exceptionCode; + + EventTargetData m_eventTargetData; + + XMLHttpRequestProgressEventThrottle m_progressEventThrottle; + + // An enum corresponding to the allowed string values for the responseType attribute. + ResponseTypeCode m_responseTypeCode; +}; + +} // namespace WebCore + +#endif // XMLHttpRequest_h diff --git a/Source/WebCore/xml/XMLHttpRequest.idl b/Source/WebCore/xml/XMLHttpRequest.idl new file mode 100644 index 0000000..2b0b177 --- /dev/null +++ b/Source/WebCore/xml/XMLHttpRequest.idl @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2008 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +module xml { + + interface [ + CanBeConstructed, + CustomConstructFunction, + V8CustomConstructor, + CustomMarkFunction, + EventTarget, + NoStaticTables + ] XMLHttpRequest { + // From XMLHttpRequestEventTarget + // event handler attributes + attribute EventListener onabort; + attribute EventListener onerror; + attribute EventListener onload; + attribute EventListener onloadstart; + attribute EventListener onprogress; + + // event handler attributes + attribute EventListener onreadystatechange; + + // state + const unsigned short UNSENT = 0; + const unsigned short OPENED = 1; + const unsigned short HEADERS_RECEIVED = 2; + const unsigned short LOADING = 3; + const unsigned short DONE = 4; + + readonly attribute unsigned short readyState; + + // request +#if defined(ENABLE_XHR_RESPONSE_BLOB) && ENABLE_XHR_RESPONSE_BLOB + attribute [EnabledAtRuntime] boolean asBlob + setter raises(DOMException); +#endif + + attribute boolean withCredentials + setter raises(DOMException); + // void open(in DOMString method, in DOMString url); + // void open(in DOMString method, in DOMString url, in boolean async); + // void open(in DOMString method, in DOMString url, in boolean async, in DOMString user); + [Custom] void open(in DOMString method, in DOMString url, in boolean async, in DOMString user, in DOMString password) + raises(DOMException); + + [RequiresAllArguments=Raise] void setRequestHeader(in DOMString header, in DOMString value) + raises(DOMException); + + // void send(); + // void send(in DOMString data); + [Custom] void send(in Document data) + raises(DOMException); + + void abort(); + + readonly attribute XMLHttpRequestUpload upload; + + // response + [ConvertNullStringTo=Undefined] DOMString getAllResponseHeaders() + raises(DOMException); + [RequiresAllArguments=Raise, ConvertNullStringTo=Null] DOMString getResponseHeader(in DOMString header) + raises(DOMException); + readonly attribute [CustomGetter] DOMString responseText // The custom getter implements ConvertNullStringTo=Null + getter raises(DOMException); + readonly attribute Document responseXML + getter raises(DOMException); +#if defined(ENABLE_XHR_RESPONSE_BLOB) && ENABLE_XHR_RESPONSE_BLOB + readonly attribute [EnabledAtRuntime] Blob responseBlob + getter raises(DOMException); +#endif + + attribute DOMString responseType + setter raises(DOMException); + readonly attribute [CustomGetter] Object response + getter raises(DOMException); + + readonly attribute unsigned short status + getter raises(DOMException); + readonly attribute DOMString statusText + getter raises(DOMException); + + // Extension + [RequiresAllArguments=Raise] void overrideMimeType(in DOMString override); + + // EventTarget interface + void addEventListener(in DOMString type, + in EventListener listener, + in boolean useCapture); + void removeEventListener(in DOMString type, + in EventListener listener, + in boolean useCapture); + boolean dispatchEvent(in Event evt) + raises(EventException); + }; + +} diff --git a/Source/WebCore/xml/XMLHttpRequestException.h b/Source/WebCore/xml/XMLHttpRequestException.h new file mode 100644 index 0000000..737cab0 --- /dev/null +++ b/Source/WebCore/xml/XMLHttpRequestException.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2007, 2008 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef XMLHttpRequestException_h +#define XMLHttpRequestException_h + +#include "ExceptionBase.h" + +namespace WebCore { + + class XMLHttpRequestException : public ExceptionBase { + public: + static PassRefPtr<XMLHttpRequestException> create(const ExceptionCodeDescription& description) + { + return adoptRef(new XMLHttpRequestException(description)); + } + + static const int XMLHttpRequestExceptionOffset = 500; + static const int XMLHttpRequestExceptionMax = 699; + + enum XMLHttpRequestExceptionCode { + NETWORK_ERR = XMLHttpRequestExceptionOffset + 101, + ABORT_ERR + }; + + private: + XMLHttpRequestException(const ExceptionCodeDescription& description) + : ExceptionBase(description) + { + } + }; + +} // namespace WebCore + +#endif // XMLHttpRequestException_h diff --git a/Source/WebCore/xml/XMLHttpRequestException.idl b/Source/WebCore/xml/XMLHttpRequestException.idl new file mode 100644 index 0000000..ffea732 --- /dev/null +++ b/Source/WebCore/xml/XMLHttpRequestException.idl @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2007 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +module xml { + + interface [ + NoStaticTables, + DontCheckEnums + ] XMLHttpRequestException { + + readonly attribute unsigned short code; + readonly attribute DOMString name; + readonly attribute DOMString message; + +#if defined(LANGUAGE_JAVASCRIPT) && LANGUAGE_JAVASCRIPT + // Override in a Mozilla compatible format + [DontEnum] DOMString toString(); +#endif + + // XMLHttpRequestExceptionCode + const unsigned short NETWORK_ERR = 101; + const unsigned short ABORT_ERR = 102; + }; + +} diff --git a/Source/WebCore/xml/XMLHttpRequestProgressEvent.h b/Source/WebCore/xml/XMLHttpRequestProgressEvent.h new file mode 100644 index 0000000..009f1c0 --- /dev/null +++ b/Source/WebCore/xml/XMLHttpRequestProgressEvent.h @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2008, 2010 Apple Inc. All rights reserved. + * Copyright (C) 2008 Julien Chaffraix <jchaffraix@webkit.org>. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef XMLHttpRequestProgressEvent_h +#define XMLHttpRequestProgressEvent_h + +#include "ProgressEvent.h" + +namespace WebCore { + +class XMLHttpRequestProgressEvent : public ProgressEvent { +public: + static PassRefPtr<XMLHttpRequestProgressEvent> create() + { + return adoptRef(new XMLHttpRequestProgressEvent); + } + static PassRefPtr<XMLHttpRequestProgressEvent> create(const AtomicString& type, bool lengthComputable = false, unsigned long long loaded = 0, unsigned long long total = 0) + { + return adoptRef(new XMLHttpRequestProgressEvent(type, lengthComputable, loaded, total)); + } + + // Those 2 synonyms are included for compatibility with Firefox. + unsigned long long position() const { return loaded(); } + unsigned long long totalSize() const { return total(); } + +private: + virtual bool isXMLHttpRequestProgressEvent() const { return true; } + + XMLHttpRequestProgressEvent() { } + XMLHttpRequestProgressEvent(const AtomicString& type, bool lengthComputable, unsigned long long loaded, unsigned long long total) + : ProgressEvent(type, lengthComputable, loaded, total) + { + } +}; + +} // namespace WebCore + +#endif // XMLHttpRequestProgressEvent_h diff --git a/Source/WebCore/xml/XMLHttpRequestProgressEvent.idl b/Source/WebCore/xml/XMLHttpRequestProgressEvent.idl new file mode 100644 index 0000000..05c984e --- /dev/null +++ b/Source/WebCore/xml/XMLHttpRequestProgressEvent.idl @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2008, 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +module events { + + interface [ + NoStaticTables + // We should also inherit from LSProgressEvent when the idl is added. + ] XMLHttpRequestProgressEvent : ProgressEvent { + readonly attribute unsigned long long position; + readonly attribute unsigned long long totalSize; + }; + +} diff --git a/Source/WebCore/xml/XMLHttpRequestProgressEventThrottle.cpp b/Source/WebCore/xml/XMLHttpRequestProgressEventThrottle.cpp new file mode 100644 index 0000000..5d4afa3 --- /dev/null +++ b/Source/WebCore/xml/XMLHttpRequestProgressEventThrottle.cpp @@ -0,0 +1,154 @@ +/* + * Copyright (C) 2010 Julien Chaffraix <jchaffraix@webkit.org> + * All right reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "XMLHttpRequestProgressEventThrottle.h" + +#include "EventTarget.h" +#include "XMLHttpRequestProgressEvent.h" + +namespace WebCore { + +const double XMLHttpRequestProgressEventThrottle::minimumProgressEventDispatchingIntervalInSeconds = .05; // 50 ms per specification. + +XMLHttpRequestProgressEventThrottle::XMLHttpRequestProgressEventThrottle(EventTarget* target) + : m_target(target) + , m_loaded(0) + , m_total(0) + , m_suspended(false) +{ + ASSERT(target); +} + +XMLHttpRequestProgressEventThrottle::~XMLHttpRequestProgressEventThrottle() +{ +} + +void XMLHttpRequestProgressEventThrottle::dispatchProgressEvent(bool lengthComputable, unsigned long long loaded, unsigned long long total) +{ + ASSERT(!suspended()); + if (!isActive()) { + // The timer is not active so the least frequent event for now is every byte. + // Just go ahead and dispatch the event. + + // We should not have any pending loaded & total information from a previous run. + ASSERT(!m_loaded); + ASSERT(!m_total); + + dispatchEvent(XMLHttpRequestProgressEvent::create(eventNames().progressEvent, lengthComputable, loaded, total)); + startRepeating(minimumProgressEventDispatchingIntervalInSeconds); + return; + } + + // The timer is already active so minimumProgressEventDispatchingIntervalInSeconds is the least frequent event. + m_lengthComputable = lengthComputable; + m_loaded = loaded; + m_total = total; +} + +void XMLHttpRequestProgressEventThrottle::dispatchEvent(PassRefPtr<Event> event, ProgressEventAction progressEventAction) +{ + ASSERT(!suspended()); + // We should not have any pending events from a previous resume. + ASSERT(!m_pausedEvent); + + if (progressEventAction == FlushProgressEvent) + flushProgressEvent(); + + m_target->dispatchEvent(event); +} + +void XMLHttpRequestProgressEventThrottle::flushProgressEvent() +{ + if (!hasEventToDispatch()) + return; + + PassRefPtr<Event> event = XMLHttpRequestProgressEvent::create(eventNames().progressEvent, m_lengthComputable, m_loaded, m_total); + m_loaded = 0; + m_total = 0; + + // We stop the timer as this is called when no more events are supposed to occur. + stop(); + + m_target->dispatchEvent(event); +} + +void XMLHttpRequestProgressEventThrottle::dispatchPausedEvent() +{ + ASSERT(!suspended()); + if (!m_pausedEvent) + return; + + m_target->dispatchEvent(m_pausedEvent); + m_pausedEvent = 0; +} + +void XMLHttpRequestProgressEventThrottle::fired() +{ + ASSERT(isActive()); + ASSERT(!suspended()); + ASSERT(!m_pausedEvent); + if (!hasEventToDispatch()) { + // No progress event was queued since the previous dispatch, we can safely stop the timer. + stop(); + return; + } + + m_target->dispatchEvent(XMLHttpRequestProgressEvent::create(eventNames().progressEvent, m_lengthComputable, m_loaded, m_total)); + m_total = 0; + m_loaded = 0; +} + +bool XMLHttpRequestProgressEventThrottle::hasEventToDispatch() const +{ + return (m_total || m_loaded) && isActive(); +} + +void XMLHttpRequestProgressEventThrottle::suspend() +{ + ASSERT(!m_pausedEvent); + + m_suspended = true; + // If we have a progress event waiting to be dispatched, + // just queue it. + if (hasEventToDispatch()) { + m_pausedEvent = XMLHttpRequestProgressEvent::create(eventNames().progressEvent, m_lengthComputable, m_loaded, m_total); + m_total = 0; + m_loaded = 0; + } + stop(); +} + +void XMLHttpRequestProgressEventThrottle::resume() +{ + ASSERT(!m_loaded); + ASSERT(!m_total); + + m_suspended = false; + dispatchPausedEvent(); +} + +} // namespace WebCore diff --git a/Source/WebCore/xml/XMLHttpRequestProgressEventThrottle.h b/Source/WebCore/xml/XMLHttpRequestProgressEventThrottle.h new file mode 100644 index 0000000..036905e --- /dev/null +++ b/Source/WebCore/xml/XMLHttpRequestProgressEventThrottle.h @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2010 Julien Chaffraix <jchaffraix@webkit.org> + * All right reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef XMLHttpRequestProgressEventThrottle_h +#define XMLHttpRequestProgressEventThrottle_h + +#include "Timer.h" +#include "wtf/PassRefPtr.h" +#include "wtf/Vector.h" + +namespace WebCore { + +class Event; +class EventTarget; + +enum ProgressEventAction { + DoNotFlushProgressEvent, + FlushProgressEvent +}; + +// This implements the XHR2 progress event dispatching: "dispatch a progress event called progress +// about every 50ms or for every byte received, whichever is least frequent". +class XMLHttpRequestProgressEventThrottle : public TimerBase { +public: + XMLHttpRequestProgressEventThrottle(EventTarget*); + virtual ~XMLHttpRequestProgressEventThrottle(); + + void dispatchProgressEvent(bool lengthComputable, unsigned long long loaded, unsigned long long total); + void dispatchEvent(PassRefPtr<Event>, ProgressEventAction = DoNotFlushProgressEvent); + + void suspend(); + void resume(); + + bool suspended() const { return m_suspended; } + +private: + static const double minimumProgressEventDispatchingIntervalInSeconds; + + virtual void fired(); + void dispatchPausedEvent(); + void flushProgressEvent(); + + bool hasEventToDispatch() const; + + // Weak pointer to our XMLHttpRequest object as it is the one holding us. + EventTarget* m_target; + + bool m_lengthComputable; + unsigned long long m_loaded; + unsigned long long m_total; + + bool m_suspended; + RefPtr<Event> m_pausedEvent; +}; + +} // namespace WebCore + +#endif // XMLHttpRequestProgressEventThrottle_h diff --git a/Source/WebCore/xml/XMLHttpRequestUpload.cpp b/Source/WebCore/xml/XMLHttpRequestUpload.cpp new file mode 100644 index 0000000..739082d --- /dev/null +++ b/Source/WebCore/xml/XMLHttpRequestUpload.cpp @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2008 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "XMLHttpRequestUpload.h" + +#include "Event.h" +#include "EventException.h" +#include "EventNames.h" +#include "XMLHttpRequest.h" +#include "XMLHttpRequestProgressEvent.h" +#include <wtf/Assertions.h> +#include <wtf/text/AtomicString.h> + +namespace WebCore { + +XMLHttpRequestUpload::XMLHttpRequestUpload(XMLHttpRequest* xmlHttpRequest) + : m_xmlHttpRequest(xmlHttpRequest) +{ +} + +ScriptExecutionContext* XMLHttpRequestUpload::scriptExecutionContext() const +{ + XMLHttpRequest* xmlHttpRequest = associatedXMLHttpRequest(); + if (!xmlHttpRequest) + return 0; + return xmlHttpRequest->scriptExecutionContext(); +} + +EventTargetData* XMLHttpRequestUpload::eventTargetData() +{ + return &m_eventTargetData; +} + +EventTargetData* XMLHttpRequestUpload::ensureEventTargetData() +{ + return &m_eventTargetData; +} + +} // namespace WebCore diff --git a/Source/WebCore/xml/XMLHttpRequestUpload.h b/Source/WebCore/xml/XMLHttpRequestUpload.h new file mode 100644 index 0000000..984d86a --- /dev/null +++ b/Source/WebCore/xml/XMLHttpRequestUpload.h @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2008 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef XMLHttpRequestUpload_h +#define XMLHttpRequestUpload_h + +#include "EventListener.h" +#include "EventNames.h" +#include "EventTarget.h" +#include <wtf/Forward.h> +#include <wtf/HashMap.h> +#include <wtf/RefCounted.h> +#include <wtf/RefPtr.h> +#include <wtf/PassRefPtr.h> +#include <wtf/Vector.h> +#include <wtf/text/AtomicStringHash.h> + +namespace WebCore { + + class ScriptExecutionContext; + class XMLHttpRequest; + + class XMLHttpRequestUpload : public RefCounted<XMLHttpRequestUpload>, public EventTarget { + public: + static PassRefPtr<XMLHttpRequestUpload> create(XMLHttpRequest* xmlHttpRequest) + { + return adoptRef(new XMLHttpRequestUpload(xmlHttpRequest)); + } + + virtual XMLHttpRequestUpload* toXMLHttpRequestUpload() { return this; } + + XMLHttpRequest* associatedXMLHttpRequest() const { return m_xmlHttpRequest; } + void disconnectXMLHttpRequest() { m_xmlHttpRequest = 0; } + + ScriptExecutionContext* scriptExecutionContext() const; + + DEFINE_ATTRIBUTE_EVENT_LISTENER(abort); + DEFINE_ATTRIBUTE_EVENT_LISTENER(error); + DEFINE_ATTRIBUTE_EVENT_LISTENER(load); + DEFINE_ATTRIBUTE_EVENT_LISTENER(loadstart); + DEFINE_ATTRIBUTE_EVENT_LISTENER(progress); + + using RefCounted<XMLHttpRequestUpload>::ref; + using RefCounted<XMLHttpRequestUpload>::deref; + + private: + XMLHttpRequestUpload(XMLHttpRequest*); + + virtual void refEventTarget() { ref(); } + virtual void derefEventTarget() { deref(); } + virtual EventTargetData* eventTargetData(); + virtual EventTargetData* ensureEventTargetData(); + + XMLHttpRequest* m_xmlHttpRequest; + EventTargetData m_eventTargetData; + }; + +} // namespace WebCore + +#endif // XMLHttpRequestUpload_h diff --git a/Source/WebCore/xml/XMLHttpRequestUpload.idl b/Source/WebCore/xml/XMLHttpRequestUpload.idl new file mode 100644 index 0000000..5a18567 --- /dev/null +++ b/Source/WebCore/xml/XMLHttpRequestUpload.idl @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2008 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +module xml { + + interface [ + CustomMarkFunction, + EventTarget, + NoStaticTables + ] XMLHttpRequestUpload { + // From XMLHttpRequestEventTarget + // event handler attributes + attribute EventListener onabort; + attribute EventListener onerror; + attribute EventListener onload; + attribute EventListener onloadstart; + attribute EventListener onprogress; + + // EventTarget interface + void addEventListener(in DOMString type, + in EventListener listener, + in boolean useCapture); + void removeEventListener(in DOMString type, + in EventListener listener, + in boolean useCapture); + boolean dispatchEvent(in Event evt) + raises(EventException); + }; + +} diff --git a/Source/WebCore/xml/XMLSerializer.cpp b/Source/WebCore/xml/XMLSerializer.cpp new file mode 100644 index 0000000..7b49897 --- /dev/null +++ b/Source/WebCore/xml/XMLSerializer.cpp @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2003, 2006 Apple Inc. All rights reserved. + * Copyright (C) 2006 Samuel Weinig (sam@webkit.org) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "config.h" +#include "XMLSerializer.h" + +#include "Document.h" +#include "ExceptionCode.h" +#include "markup.h" + +namespace WebCore { + +String XMLSerializer::serializeToString(Node* node, ExceptionCode& ec) +{ + if (!node) + return String(); + + if (!node->document()) { + // Due to the fact that DocumentType nodes are created by the DOMImplementation + // and not the Document, it is possible for it to not have a Document associated + // with it. It should be the only type of node where this is possible. + ASSERT(node->nodeType() == Node::DOCUMENT_TYPE_NODE); + + ec = INVALID_ACCESS_ERR; + return String(); + } + + return createMarkup(node); +} + +} // namespace WebCore diff --git a/Source/WebCore/xml/XMLSerializer.h b/Source/WebCore/xml/XMLSerializer.h new file mode 100644 index 0000000..33e94b7 --- /dev/null +++ b/Source/WebCore/xml/XMLSerializer.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2003, 2006 Apple Computer, Inc. + * Copyright (C) 2006 Samuel Weinig (sam@webkit.org) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef XMLSerializer_h +#define XMLSerializer_h + +#include <wtf/RefCounted.h> +#include "PlatformString.h" + +namespace WebCore { + + typedef int ExceptionCode; + + class Node; + + class XMLSerializer : public RefCounted<XMLSerializer> { + public: + static PassRefPtr<XMLSerializer> create() { return adoptRef(new XMLSerializer); } + + String serializeToString(Node*, ExceptionCode&); + + private: + XMLSerializer() { } + }; + +} // namespace WebCore + +#endif // XMLSerializer_h diff --git a/Source/WebCore/xml/XMLSerializer.idl b/Source/WebCore/xml/XMLSerializer.idl new file mode 100644 index 0000000..8c59446 --- /dev/null +++ b/Source/WebCore/xml/XMLSerializer.idl @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2006 Apple Computer, Inc. + * Copyright (C) 2006 Samuel Weinig (sam@webkit.org) + * + * 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. + */ + +module xpath { + + interface [CanBeConstructed] XMLSerializer { + DOMString serializeToString(in Node node) + raises(DOMException); + }; + +} diff --git a/Source/WebCore/xml/XPathEvaluator.cpp b/Source/WebCore/xml/XPathEvaluator.cpp new file mode 100644 index 0000000..8fb203f --- /dev/null +++ b/Source/WebCore/xml/XPathEvaluator.cpp @@ -0,0 +1,77 @@ +/* + * Copyright 2005 Frerich Raabe <raabe@kde.org> + * Copyright (C) 2006 Apple Computer, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "XPathEvaluator.h" + +#if ENABLE(XPATH) + +#include "ExceptionCode.h" +#include "NativeXPathNSResolver.h" +#include "Node.h" +#include "XPathExpression.h" +#include "XPathResult.h" +#include "XPathUtil.h" + +namespace WebCore { + +using namespace XPath; + +PassRefPtr<XPathExpression> XPathEvaluator::createExpression(const String& expression, + XPathNSResolver* resolver, + ExceptionCode& ec) +{ + return XPathExpression::createExpression(expression, resolver, ec); +} + +PassRefPtr<XPathNSResolver> XPathEvaluator::createNSResolver(Node* nodeResolver) +{ + return NativeXPathNSResolver::create(nodeResolver); +} + +PassRefPtr<XPathResult> XPathEvaluator::evaluate(const String& expression, + Node* contextNode, + XPathNSResolver* resolver, + unsigned short type, + XPathResult* result, + ExceptionCode& ec) +{ + if (!isValidContextNode(contextNode)) { + ec = NOT_SUPPORTED_ERR; + return 0; + } + + ec = 0; + RefPtr<XPathExpression> expr = createExpression(expression, resolver, ec); + if (ec) + return 0; + + return expr->evaluate(contextNode, type, result, ec); +} + +} + +#endif // ENABLE(XPATH) diff --git a/Source/WebCore/xml/XPathEvaluator.h b/Source/WebCore/xml/XPathEvaluator.h new file mode 100644 index 0000000..70322a4 --- /dev/null +++ b/Source/WebCore/xml/XPathEvaluator.h @@ -0,0 +1,62 @@ +/* + * Copyright 2005 Frerich Raabe <raabe@kde.org> + * Copyright (C) 2006 Apple Computer, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef XPathEvaluator_h +#define XPathEvaluator_h + +#if ENABLE(XPATH) + +#include <wtf/Forward.h> +#include <wtf/RefCounted.h> +#include <wtf/PassRefPtr.h> + +namespace WebCore { + + typedef int ExceptionCode; + + class Node; + class XPathExpression; + class XPathNSResolver; + class XPathResult; + + class XPathEvaluator : public RefCounted<XPathEvaluator> { + public: + static PassRefPtr<XPathEvaluator> create() { return adoptRef(new XPathEvaluator); } + + PassRefPtr<XPathExpression> createExpression(const String& expression, XPathNSResolver*, ExceptionCode&); + PassRefPtr<XPathNSResolver> createNSResolver(Node* nodeResolver); + PassRefPtr<XPathResult> evaluate(const String& expression, Node* contextNode, + XPathNSResolver*, unsigned short type, XPathResult*, ExceptionCode&); + + private: + XPathEvaluator() { } + }; + +} + +#endif // ENABLE(XPATH) + +#endif // XPathEvaluator_h diff --git a/Source/WebCore/xml/XPathEvaluator.idl b/Source/WebCore/xml/XPathEvaluator.idl new file mode 100644 index 0000000..30df23a --- /dev/null +++ b/Source/WebCore/xml/XPathEvaluator.idl @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2006 Apple Computer, Inc. + * + * 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. + */ + +module xpath { + interface [CanBeConstructed, Conditional=XPATH] XPathEvaluator { + XPathExpression createExpression(in DOMString expression, + in XPathNSResolver resolver) + raises(DOMException); + + XPathNSResolver createNSResolver(in Node nodeResolver); + + XPathResult evaluate(in DOMString expression, + in Node contextNode, + in XPathNSResolver resolver, + in unsigned short type, + in XPathResult inResult) + raises(DOMException); + }; +} diff --git a/Source/WebCore/xml/XPathException.h b/Source/WebCore/xml/XPathException.h new file mode 100644 index 0000000..45ad628 --- /dev/null +++ b/Source/WebCore/xml/XPathException.h @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2007, 2008 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef XPathException_h +#define XPathException_h + +#include "ExceptionBase.h" + +#if ENABLE(XPATH) + +namespace WebCore { + + class XPathException : public ExceptionBase { + public: + static PassRefPtr<XPathException> create(const ExceptionCodeDescription& description) + { + return adoptRef(new XPathException(description)); + } + + static const int XPathExceptionOffset = 400; + static const int XPathExceptionMax = 499; + + enum XPathExceptionCode { + INVALID_EXPRESSION_ERR = XPathExceptionOffset + 51, + TYPE_ERR + }; + + private: + XPathException(const ExceptionCodeDescription& description) + : ExceptionBase(description) + { + } + }; + +} // namespace WebCore + +#endif // ENABLE(XPATH) + +#endif // XPathException_h diff --git a/Source/WebCore/xml/XPathException.idl b/Source/WebCore/xml/XPathException.idl new file mode 100644 index 0000000..b57df57 --- /dev/null +++ b/Source/WebCore/xml/XPathException.idl @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2007 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +module xpath { + + interface [ + Conditional=XPATH, + DontCheckEnums + ] XPathException { + + readonly attribute unsigned short code; + readonly attribute DOMString name; + readonly attribute DOMString message; + +#if defined(LANGUAGE_JAVASCRIPT) && LANGUAGE_JAVASCRIPT + // Override in a Mozilla compatible format + [DontEnum] DOMString toString(); +#endif + + // XPathExceptionCode + const unsigned short INVALID_EXPRESSION_ERR = 51; + const unsigned short TYPE_ERR = 52; + }; + +} diff --git a/Source/WebCore/xml/XPathExpression.cpp b/Source/WebCore/xml/XPathExpression.cpp new file mode 100644 index 0000000..6188426 --- /dev/null +++ b/Source/WebCore/xml/XPathExpression.cpp @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2005 Frerich Raabe <raabe@kde.org> + * Copyright (C) 2006, 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "XPathExpression.h" + +#if ENABLE(XPATH) + +#include "Document.h" +#include "PlatformString.h" +#include "XPathException.h" +#include "XPathExpressionNode.h" +#include "XPathNSResolver.h" +#include "XPathParser.h" +#include "XPathResult.h" +#include "XPathUtil.h" + +namespace WebCore { + +using namespace XPath; + +PassRefPtr<XPathExpression> XPathExpression::createExpression(const String& expression, XPathNSResolver* resolver, ExceptionCode& ec) +{ + RefPtr<XPathExpression> expr = XPathExpression::create(); + Parser parser; + + expr->m_topExpression = parser.parseStatement(expression, resolver, ec); + if (!expr->m_topExpression) + return 0; + + return expr.release(); +} + +XPathExpression::~XPathExpression() +{ + delete m_topExpression; +} + +PassRefPtr<XPathResult> XPathExpression::evaluate(Node* contextNode, unsigned short type, XPathResult*, ExceptionCode& ec) +{ + if (!isValidContextNode(contextNode)) { + ec = NOT_SUPPORTED_ERR; + return 0; + } + + EvaluationContext& evaluationContext = Expression::evaluationContext(); + evaluationContext.node = contextNode; + evaluationContext.size = 1; + evaluationContext.position = 1; + evaluationContext.hadTypeConversionError = false; + RefPtr<XPathResult> result = XPathResult::create(contextNode->document(), m_topExpression->evaluate()); + evaluationContext.node = 0; // Do not hold a reference to the context node, as this may prevent the whole document from being destroyed in time. + + if (evaluationContext.hadTypeConversionError) { + // It is not specified what to do if type conversion fails while evaluating an expression, and INVALID_EXPRESSION_ERR is not exactly right + // when the failure happens in an otherwise valid expression because of a variable. But XPathEvaluator does not support variables, so it's close enough. + ec = XPathException::INVALID_EXPRESSION_ERR; + return 0; + } + + if (type != XPathResult::ANY_TYPE) { + ec = 0; + result->convertTo(type, ec); + if (ec) + return 0; + } + + return result; +} + +} + +#endif // ENABLE(XPATH) diff --git a/Source/WebCore/xml/XPathExpression.h b/Source/WebCore/xml/XPathExpression.h new file mode 100644 index 0000000..633ec80 --- /dev/null +++ b/Source/WebCore/xml/XPathExpression.h @@ -0,0 +1,66 @@ +/* + * Copyright 2005 Frerich Raabe <raabe@kde.org> + * Copyright (C) 2006 Apple Computer, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef XPathExpression_h +#define XPathExpression_h + +#if ENABLE(XPATH) + +#include <wtf/Forward.h> +#include <wtf/RefCounted.h> +#include <wtf/PassRefPtr.h> + +namespace WebCore { + + typedef int ExceptionCode; + + class Node; + class XPathNSResolver; + class XPathResult; + + namespace XPath { + class Expression; + } + + class XPathExpression : public RefCounted<XPathExpression> { + public: + static PassRefPtr<XPathExpression> create() { return adoptRef(new XPathExpression); } + ~XPathExpression(); + + static PassRefPtr<XPathExpression> createExpression(const String& expression, XPathNSResolver*, ExceptionCode&); + PassRefPtr<XPathResult> evaluate(Node* contextNode, unsigned short type, XPathResult*, ExceptionCode&); + + private: + XPathExpression() { } + + XPath::Expression* m_topExpression; + }; + +} + +#endif // ENABLE(XPATH) + +#endif // XPathExpression_h diff --git a/Source/WebCore/xml/XPathExpression.idl b/Source/WebCore/xml/XPathExpression.idl new file mode 100644 index 0000000..6b6ceeb --- /dev/null +++ b/Source/WebCore/xml/XPathExpression.idl @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2006 Apple Computer, Inc. + * Copyright (C) 2006 Samuel Weinig <sam.weinig@gmail.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. + */ + +module xpath { + + + interface [ + Conditional=XPATH + ] XPathExpression { + [OldStyleObjC] XPathResult evaluate(in Node contextNode, + in unsigned short type, + in XPathResult inResult) + raises(DOMException); + }; + +} diff --git a/Source/WebCore/xml/XPathExpressionNode.cpp b/Source/WebCore/xml/XPathExpressionNode.cpp new file mode 100644 index 0000000..4656f8d --- /dev/null +++ b/Source/WebCore/xml/XPathExpressionNode.cpp @@ -0,0 +1,59 @@ +/* + * Copyright 2005 Frerich Raabe <raabe@kde.org> + * Copyright (C) 2006 Apple Computer, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "XPathExpressionNode.h" + +#if ENABLE(XPATH) + +#include "Node.h" +#include <wtf/StdLibExtras.h> + +namespace WebCore { +namespace XPath { + +EvaluationContext& Expression::evaluationContext() +{ + DEFINE_STATIC_LOCAL(EvaluationContext, evaluationContext, ()); + return evaluationContext; +} + +Expression::Expression() + : m_isContextNodeSensitive(false) + , m_isContextPositionSensitive(false) + , m_isContextSizeSensitive(false) +{ +} + +Expression::~Expression() +{ + deleteAllValues(m_subExpressions); +} + +} +} + +#endif // ENABLE(XPATH) diff --git a/Source/WebCore/xml/XPathExpressionNode.h b/Source/WebCore/xml/XPathExpressionNode.h new file mode 100644 index 0000000..c04d45b --- /dev/null +++ b/Source/WebCore/xml/XPathExpressionNode.h @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2005 Frerich Raabe <raabe@kde.org> + * Copyright (C) 2006, 2009 Apple Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef XPathExpressionNode_h +#define XPathExpressionNode_h + +#if ENABLE(XPATH) + +#include "Node.h" +#include "XPathValue.h" +#include <wtf/HashMap.h> +#include <wtf/Vector.h> +#include <wtf/text/StringHash.h> + +namespace WebCore { + + namespace XPath { + + struct EvaluationContext : FastAllocBase { + RefPtr<Node> node; + unsigned long size; + unsigned long position; + HashMap<String, String> variableBindings; + + bool hadTypeConversionError; + }; + + class ParseNode { + public: + virtual ~ParseNode() { } + }; + + class Expression : public ParseNode, public Noncopyable { + public: + static EvaluationContext& evaluationContext(); + + Expression(); + virtual ~Expression(); + + virtual Value evaluate() const = 0; + + void addSubExpression(Expression* expr) + { + m_subExpressions.append(expr); + m_isContextNodeSensitive |= expr->m_isContextNodeSensitive; + m_isContextPositionSensitive |= expr->m_isContextPositionSensitive; + m_isContextSizeSensitive |= expr->m_isContextSizeSensitive; + } + + bool isContextNodeSensitive() const { return m_isContextNodeSensitive; } + bool isContextPositionSensitive() const { return m_isContextPositionSensitive; } + bool isContextSizeSensitive() const { return m_isContextSizeSensitive; } + void setIsContextNodeSensitive(bool value) { m_isContextNodeSensitive = value; } + void setIsContextPositionSensitive(bool value) { m_isContextPositionSensitive = value; } + void setIsContextSizeSensitive(bool value) { m_isContextSizeSensitive = value; } + + virtual Value::Type resultType() const = 0; + + protected: + unsigned subExprCount() const { return m_subExpressions.size(); } + Expression* subExpr(unsigned i) { return m_subExpressions[i]; } + const Expression* subExpr(unsigned i) const { return m_subExpressions[i]; } + + private: + Vector<Expression*> m_subExpressions; + + // Evaluation details that can be used for optimization. + bool m_isContextNodeSensitive; + bool m_isContextPositionSensitive; + bool m_isContextSizeSensitive; + }; + + } + +} + +#endif // ENABLE(XPATH) + +#endif // EXPRESSION_H + diff --git a/Source/WebCore/xml/XPathFunctions.cpp b/Source/WebCore/xml/XPathFunctions.cpp new file mode 100644 index 0000000..2091aca --- /dev/null +++ b/Source/WebCore/xml/XPathFunctions.cpp @@ -0,0 +1,737 @@ +/* + * Copyright (C) 2005 Frerich Raabe <raabe@kde.org> + * Copyright (C) 2006, 2009 Apple Inc. + * Copyright (C) 2007 Alexey Proskuryakov <ap@webkit.org> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "XPathFunctions.h" + +#if ENABLE(XPATH) + +#include "Document.h" +#include "Element.h" +#include "NamedNodeMap.h" +#include "ProcessingInstruction.h" +#include "XMLNames.h" +#include "XPathUtil.h" +#include "XPathValue.h" +#include <wtf/MathExtras.h> + +namespace WebCore { +namespace XPath { + +static inline bool isWhitespace(UChar c) +{ + return c == ' ' || c == '\n' || c == '\r' || c == '\t'; +} + + +#define DEFINE_FUNCTION_CREATOR(Class) static Function* create##Class() { return new Class; } + +class Interval { +public: + static const int Inf = -1; + + Interval(); + Interval(int value); + Interval(int min, int max); + + bool contains(int value) const; + +private: + int m_min; + int m_max; +}; + +struct FunctionRec { + typedef Function *(*FactoryFn)(); + FactoryFn factoryFn; + Interval args; +}; + +static HashMap<String, FunctionRec>* functionMap; + +class FunLast : public Function { + virtual Value evaluate() const; + virtual Value::Type resultType() const { return Value::NumberValue; } +public: + FunLast() { setIsContextSizeSensitive(true); } +}; + +class FunPosition : public Function { + virtual Value evaluate() const; + virtual Value::Type resultType() const { return Value::NumberValue; } +public: + FunPosition() { setIsContextPositionSensitive(true); } +}; + +class FunCount : public Function { + virtual Value evaluate() const; + virtual Value::Type resultType() const { return Value::NumberValue; } +}; + +class FunId : public Function { + virtual Value evaluate() const; + virtual Value::Type resultType() const { return Value::NodeSetValue; } +}; + +class FunLocalName : public Function { + virtual Value evaluate() const; + virtual Value::Type resultType() const { return Value::StringValue; } +public: + FunLocalName() { setIsContextNodeSensitive(true); } // local-name() with no arguments uses context node. +}; + +class FunNamespaceURI : public Function { + virtual Value evaluate() const; + virtual Value::Type resultType() const { return Value::StringValue; } +public: + FunNamespaceURI() { setIsContextNodeSensitive(true); } // namespace-uri() with no arguments uses context node. +}; + +class FunName : public Function { + virtual Value evaluate() const; + virtual Value::Type resultType() const { return Value::StringValue; } +public: + FunName() { setIsContextNodeSensitive(true); } // name() with no arguments uses context node. +}; + +class FunString : public Function { + virtual Value evaluate() const; + virtual Value::Type resultType() const { return Value::StringValue; } +public: + FunString() { setIsContextNodeSensitive(true); } // string() with no arguments uses context node. +}; + +class FunConcat : public Function { + virtual Value evaluate() const; + virtual Value::Type resultType() const { return Value::StringValue; } +}; + +class FunStartsWith : public Function { + virtual Value evaluate() const; + virtual Value::Type resultType() const { return Value::BooleanValue; } +}; + +class FunContains : public Function { + virtual Value evaluate() const; + virtual Value::Type resultType() const { return Value::BooleanValue; } +}; + +class FunSubstringBefore : public Function { + virtual Value evaluate() const; + virtual Value::Type resultType() const { return Value::StringValue; } +}; + +class FunSubstringAfter : public Function { + virtual Value evaluate() const; + virtual Value::Type resultType() const { return Value::StringValue; } +}; + +class FunSubstring : public Function { + virtual Value evaluate() const; + virtual Value::Type resultType() const { return Value::StringValue; } +}; + +class FunStringLength : public Function { + virtual Value evaluate() const; + virtual Value::Type resultType() const { return Value::NumberValue; } +public: + FunStringLength() { setIsContextNodeSensitive(true); } // string-length() with no arguments uses context node. +}; + +class FunNormalizeSpace : public Function { + virtual Value evaluate() const; + virtual Value::Type resultType() const { return Value::StringValue; } +public: + FunNormalizeSpace() { setIsContextNodeSensitive(true); } // normalize-space() with no arguments uses context node. +}; + +class FunTranslate : public Function { + virtual Value evaluate() const; + virtual Value::Type resultType() const { return Value::StringValue; } +}; + +class FunBoolean : public Function { + virtual Value evaluate() const; + virtual Value::Type resultType() const { return Value::BooleanValue; } +}; + +class FunNot : public Function { + virtual Value evaluate() const; + virtual Value::Type resultType() const { return Value::BooleanValue; } +}; + +class FunTrue : public Function { + virtual Value evaluate() const; + virtual Value::Type resultType() const { return Value::BooleanValue; } +}; + +class FunFalse : public Function { + virtual Value evaluate() const; + virtual Value::Type resultType() const { return Value::BooleanValue; } +}; + +class FunLang : public Function { + virtual Value evaluate() const; + virtual Value::Type resultType() const { return Value::BooleanValue; } +public: + FunLang() { setIsContextNodeSensitive(true); } // lang() always works on context node. +}; + +class FunNumber : public Function { + virtual Value evaluate() const; + virtual Value::Type resultType() const { return Value::NumberValue; } +public: + FunNumber() { setIsContextNodeSensitive(true); } // number() with no arguments uses context node. +}; + +class FunSum : public Function { + virtual Value evaluate() const; + virtual Value::Type resultType() const { return Value::NumberValue; } +}; + +class FunFloor : public Function { + virtual Value evaluate() const; + virtual Value::Type resultType() const { return Value::NumberValue; } +}; + +class FunCeiling : public Function { + virtual Value evaluate() const; + virtual Value::Type resultType() const { return Value::NumberValue; } +}; + +class FunRound : public Function { + virtual Value evaluate() const; + virtual Value::Type resultType() const { return Value::NumberValue; } +public: + static double round(double); +}; + +DEFINE_FUNCTION_CREATOR(FunLast) +DEFINE_FUNCTION_CREATOR(FunPosition) +DEFINE_FUNCTION_CREATOR(FunCount) +DEFINE_FUNCTION_CREATOR(FunId) +DEFINE_FUNCTION_CREATOR(FunLocalName) +DEFINE_FUNCTION_CREATOR(FunNamespaceURI) +DEFINE_FUNCTION_CREATOR(FunName) + +DEFINE_FUNCTION_CREATOR(FunString) +DEFINE_FUNCTION_CREATOR(FunConcat) +DEFINE_FUNCTION_CREATOR(FunStartsWith) +DEFINE_FUNCTION_CREATOR(FunContains) +DEFINE_FUNCTION_CREATOR(FunSubstringBefore) +DEFINE_FUNCTION_CREATOR(FunSubstringAfter) +DEFINE_FUNCTION_CREATOR(FunSubstring) +DEFINE_FUNCTION_CREATOR(FunStringLength) +DEFINE_FUNCTION_CREATOR(FunNormalizeSpace) +DEFINE_FUNCTION_CREATOR(FunTranslate) + +DEFINE_FUNCTION_CREATOR(FunBoolean) +DEFINE_FUNCTION_CREATOR(FunNot) +DEFINE_FUNCTION_CREATOR(FunTrue) +DEFINE_FUNCTION_CREATOR(FunFalse) +DEFINE_FUNCTION_CREATOR(FunLang) + +DEFINE_FUNCTION_CREATOR(FunNumber) +DEFINE_FUNCTION_CREATOR(FunSum) +DEFINE_FUNCTION_CREATOR(FunFloor) +DEFINE_FUNCTION_CREATOR(FunCeiling) +DEFINE_FUNCTION_CREATOR(FunRound) + +#undef DEFINE_FUNCTION_CREATOR + +inline Interval::Interval() + : m_min(Inf), m_max(Inf) +{ +} + +inline Interval::Interval(int value) + : m_min(value), m_max(value) +{ +} + +inline Interval::Interval(int min, int max) + : m_min(min), m_max(max) +{ +} + +inline bool Interval::contains(int value) const +{ + if (m_min == Inf && m_max == Inf) + return true; + + if (m_min == Inf) + return value <= m_max; + + if (m_max == Inf) + return value >= m_min; + + return value >= m_min && value <= m_max; +} + +void Function::setArguments(const Vector<Expression*>& args) +{ + ASSERT(!subExprCount()); + + // Some functions use context node as implicit argument, so when explicit arguments are added, they may no longer be context node sensitive. + if (m_name != "lang" && !args.isEmpty()) + setIsContextNodeSensitive(false); + + Vector<Expression*>::const_iterator end = args.end(); + for (Vector<Expression*>::const_iterator it = args.begin(); it != end; it++) + addSubExpression(*it); +} + +Value FunLast::evaluate() const +{ + return Expression::evaluationContext().size; +} + +Value FunPosition::evaluate() const +{ + return Expression::evaluationContext().position; +} + +Value FunId::evaluate() const +{ + Value a = arg(0)->evaluate(); + Vector<UChar> idList; // A whitespace-separated list of IDs + + if (a.isNodeSet()) { + const NodeSet& nodes = a.toNodeSet(); + for (size_t i = 0; i < nodes.size(); ++i) { + String str = stringValue(nodes[i]); + idList.append(str.characters(), str.length()); + idList.append(' '); + } + } else { + String str = a.toString(); + idList.append(str.characters(), str.length()); + } + + Document* contextDocument = evaluationContext().node->document(); + NodeSet result; + HashSet<Node*> resultSet; + + size_t startPos = 0; + size_t length = idList.size(); + while (true) { + while (startPos < length && isWhitespace(idList[startPos])) + ++startPos; + + if (startPos == length) + break; + + size_t endPos = startPos; + while (endPos < length && !isWhitespace(idList[endPos])) + ++endPos; + + // If there are several nodes with the same id, id() should return the first one. + // In WebKit, getElementById behaves so, too, although its behavior in this case is formally undefined. + Node* node = contextDocument->getElementById(String(&idList[startPos], endPos - startPos)); + if (node && resultSet.add(node).second) + result.append(node); + + startPos = endPos; + } + + result.markSorted(false); + + return Value(result, Value::adopt); +} + +static inline String expandedNameLocalPart(Node* node) +{ + // The local part of an XPath expanded-name matches DOM local name for most node types, except for namespace nodes and processing instruction nodes. + ASSERT(node->nodeType() != Node::XPATH_NAMESPACE_NODE); // Not supported yet. + if (node->nodeType() == Node::PROCESSING_INSTRUCTION_NODE) + return static_cast<ProcessingInstruction*>(node)->target(); + return node->localName().string(); +} + +static inline String expandedName(Node* node) +{ + const AtomicString& prefix = node->prefix(); + return prefix.isEmpty() ? expandedNameLocalPart(node) : prefix + ":" + expandedNameLocalPart(node); +} + +Value FunLocalName::evaluate() const +{ + if (argCount() > 0) { + Value a = arg(0)->evaluate(); + if (!a.isNodeSet()) + return ""; + + Node* node = a.toNodeSet().firstNode(); + return node ? expandedNameLocalPart(node) : ""; + } + + return expandedNameLocalPart(evaluationContext().node.get()); +} + +Value FunNamespaceURI::evaluate() const +{ + if (argCount() > 0) { + Value a = arg(0)->evaluate(); + if (!a.isNodeSet()) + return ""; + + Node* node = a.toNodeSet().firstNode(); + return node ? node->namespaceURI().string() : ""; + } + + return evaluationContext().node->namespaceURI().string(); +} + +Value FunName::evaluate() const +{ + if (argCount() > 0) { + Value a = arg(0)->evaluate(); + if (!a.isNodeSet()) + return ""; + + Node* node = a.toNodeSet().firstNode(); + return node ? expandedName(node) : ""; + } + + return expandedName(evaluationContext().node.get()); +} + +Value FunCount::evaluate() const +{ + Value a = arg(0)->evaluate(); + + return double(a.toNodeSet().size()); +} + +Value FunString::evaluate() const +{ + if (!argCount()) + return Value(Expression::evaluationContext().node.get()).toString(); + return arg(0)->evaluate().toString(); +} + +Value FunConcat::evaluate() const +{ + Vector<UChar, 1024> result; + + unsigned count = argCount(); + for (unsigned i = 0; i < count; ++i) { + String str(arg(i)->evaluate().toString()); + result.append(str.characters(), str.length()); + } + + return String(result.data(), result.size()); +} + +Value FunStartsWith::evaluate() const +{ + String s1 = arg(0)->evaluate().toString(); + String s2 = arg(1)->evaluate().toString(); + + if (s2.isEmpty()) + return true; + + return s1.startsWith(s2); +} + +Value FunContains::evaluate() const +{ + String s1 = arg(0)->evaluate().toString(); + String s2 = arg(1)->evaluate().toString(); + + if (s2.isEmpty()) + return true; + + return s1.contains(s2) != 0; +} + +Value FunSubstringBefore::evaluate() const +{ + String s1 = arg(0)->evaluate().toString(); + String s2 = arg(1)->evaluate().toString(); + + if (s2.isEmpty()) + return ""; + + size_t i = s1.find(s2); + + if (i == notFound) + return ""; + + return s1.left(i); +} + +Value FunSubstringAfter::evaluate() const +{ + String s1 = arg(0)->evaluate().toString(); + String s2 = arg(1)->evaluate().toString(); + + size_t i = s1.find(s2); + if (i == notFound) + return ""; + + return s1.substring(i + s2.length()); +} + +Value FunSubstring::evaluate() const +{ + String s = arg(0)->evaluate().toString(); + double doublePos = arg(1)->evaluate().toNumber(); + if (isnan(doublePos)) + return ""; + long pos = static_cast<long>(FunRound::round(doublePos)); + bool haveLength = argCount() == 3; + long len = -1; + if (haveLength) { + double doubleLen = arg(2)->evaluate().toNumber(); + if (isnan(doubleLen)) + return ""; + len = static_cast<long>(FunRound::round(doubleLen)); + } + + if (pos > long(s.length())) + return ""; + + if (pos < 1) { + if (haveLength) { + len -= 1 - pos; + if (len < 1) + return ""; + } + pos = 1; + } + + return s.substring(pos - 1, len); +} + +Value FunStringLength::evaluate() const +{ + if (!argCount()) + return Value(Expression::evaluationContext().node.get()).toString().length(); + return arg(0)->evaluate().toString().length(); +} + +Value FunNormalizeSpace::evaluate() const +{ + if (!argCount()) { + String s = Value(Expression::evaluationContext().node.get()).toString(); + return s.simplifyWhiteSpace(); + } + + String s = arg(0)->evaluate().toString(); + return s.simplifyWhiteSpace(); +} + +Value FunTranslate::evaluate() const +{ + String s1 = arg(0)->evaluate().toString(); + String s2 = arg(1)->evaluate().toString(); + String s3 = arg(2)->evaluate().toString(); + String newString; + + // FIXME: Building a String a character at a time is quite slow. + for (unsigned i1 = 0; i1 < s1.length(); ++i1) { + UChar ch = s1[i1]; + size_t i2 = s2.find(ch); + + if (i2 == notFound) + newString += String(&ch, 1); + else if (i2 < s3.length()) { + UChar c2 = s3[i2]; + newString += String(&c2, 1); + } + } + + return newString; +} + +Value FunBoolean::evaluate() const +{ + return arg(0)->evaluate().toBoolean(); +} + +Value FunNot::evaluate() const +{ + return !arg(0)->evaluate().toBoolean(); +} + +Value FunTrue::evaluate() const +{ + return true; +} + +Value FunLang::evaluate() const +{ + String lang = arg(0)->evaluate().toString(); + + Attribute* languageAttribute = 0; + Node* node = evaluationContext().node.get(); + while (node) { + NamedNodeMap* attrs = node->attributes(); + if (attrs) + languageAttribute = attrs->getAttributeItem(XMLNames::langAttr); + if (languageAttribute) + break; + node = node->parentNode(); + } + + if (!languageAttribute) + return false; + + String langValue = languageAttribute->value(); + while (true) { + if (equalIgnoringCase(langValue, lang)) + return true; + + // Remove suffixes one by one. + size_t index = langValue.reverseFind('-'); + if (index == notFound) + break; + langValue = langValue.left(index); + } + + return false; +} + +Value FunFalse::evaluate() const +{ + return false; +} + +Value FunNumber::evaluate() const +{ + if (!argCount()) + return Value(Expression::evaluationContext().node.get()).toNumber(); + return arg(0)->evaluate().toNumber(); +} + +Value FunSum::evaluate() const +{ + Value a = arg(0)->evaluate(); + if (!a.isNodeSet()) + return 0.0; + + double sum = 0.0; + const NodeSet& nodes = a.toNodeSet(); + // To be really compliant, we should sort the node-set, as floating point addition is not associative. + // However, this is unlikely to ever become a practical issue, and sorting is slow. + + for (unsigned i = 0; i < nodes.size(); i++) + sum += Value(stringValue(nodes[i])).toNumber(); + + return sum; +} + +Value FunFloor::evaluate() const +{ + return floor(arg(0)->evaluate().toNumber()); +} + +Value FunCeiling::evaluate() const +{ + return ceil(arg(0)->evaluate().toNumber()); +} + +double FunRound::round(double val) +{ + if (!isnan(val) && !isinf(val)) { + if (signbit(val) && val >= -0.5) + val *= 0; // negative zero + else + val = floor(val + 0.5); + } + return val; +} + +Value FunRound::evaluate() const +{ + return round(arg(0)->evaluate().toNumber()); +} + +struct FunctionMapping { + const char* name; + FunctionRec function; +}; + +static void createFunctionMap() +{ + static const FunctionMapping functions[] = { + { "boolean", { &createFunBoolean, 1 } }, + { "ceiling", { &createFunCeiling, 1 } }, + { "concat", { &createFunConcat, Interval(2, Interval::Inf) } }, + { "contains", { &createFunContains, 2 } }, + { "count", { &createFunCount, 1 } }, + { "false", { &createFunFalse, 0 } }, + { "floor", { &createFunFloor, 1 } }, + { "id", { &createFunId, 1 } }, + { "lang", { &createFunLang, 1 } }, + { "last", { &createFunLast, 0 } }, + { "local-name", { &createFunLocalName, Interval(0, 1) } }, + { "name", { &createFunName, Interval(0, 1) } }, + { "namespace-uri", { &createFunNamespaceURI, Interval(0, 1) } }, + { "normalize-space", { &createFunNormalizeSpace, Interval(0, 1) } }, + { "not", { &createFunNot, 1 } }, + { "number", { &createFunNumber, Interval(0, 1) } }, + { "position", { &createFunPosition, 0 } }, + { "round", { &createFunRound, 1 } }, + { "starts-with", { &createFunStartsWith, 2 } }, + { "string", { &createFunString, Interval(0, 1) } }, + { "string-length", { &createFunStringLength, Interval(0, 1) } }, + { "substring", { &createFunSubstring, Interval(2, 3) } }, + { "substring-after", { &createFunSubstringAfter, 2 } }, + { "substring-before", { &createFunSubstringBefore, 2 } }, + { "sum", { &createFunSum, 1 } }, + { "translate", { &createFunTranslate, 3 } }, + { "true", { &createFunTrue, 0 } }, + }; + + functionMap = new HashMap<String, FunctionRec>; + for (size_t i = 0; i < WTF_ARRAY_LENGTH(functions); ++i) + functionMap->set(functions[i].name, functions[i].function); +} + +Function* createFunction(const String& name, const Vector<Expression*>& args) +{ + if (!functionMap) + createFunctionMap(); + + HashMap<String, FunctionRec>::iterator functionMapIter = functionMap->find(name); + FunctionRec* functionRec = 0; + + if (functionMapIter == functionMap->end() || !(functionRec = &functionMapIter->second)->args.contains(args.size())) + return 0; + + Function* function = functionRec->factoryFn(); + function->setArguments(args); + function->setName(name); + return function; +} + +} +} + +#endif // ENABLE(XPATH) diff --git a/Source/WebCore/xml/XPathFunctions.h b/Source/WebCore/xml/XPathFunctions.h new file mode 100644 index 0000000..62d687f --- /dev/null +++ b/Source/WebCore/xml/XPathFunctions.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2005 Frerich Raabe <raabe@kde.org> + * Copyright (C) 2006, 2009 Apple Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef XPathFunctions_h +#define XPathFunctions_h + +#if ENABLE(XPATH) + +#include "XPathExpressionNode.h" + +namespace WebCore { + + namespace XPath { + + class Function : public Expression { + public: + void setArguments(const Vector<Expression*>&); + void setName(const String& name) { m_name = name; } + protected: + Expression* arg(int pos) { return subExpr(pos); } + const Expression* arg(int pos) const { return subExpr(pos); } + unsigned int argCount() const { return subExprCount(); } + String name() const { return m_name; } + + private: + String m_name; + }; + + Function* createFunction(const String& name, const Vector<Expression*>& args = Vector<Expression*>()); + + } // namespace XPath + +} // namespace WebCore + +#endif // ENABLE(XPATH) + +#endif // XPathFunctions_h diff --git a/Source/WebCore/xml/XPathGrammar.y b/Source/WebCore/xml/XPathGrammar.y new file mode 100644 index 0000000..14e9fa3 --- /dev/null +++ b/Source/WebCore/xml/XPathGrammar.y @@ -0,0 +1,558 @@ +/* + * Copyright 2005 Frerich Raabe <raabe@kde.org> + * Copyright (C) 2006 Apple Inc. All rights reserved. + * Copyright (C) 2007 Alexey Proskuryakov <ap@webkit.org> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +%{ + +#include "config.h" + +#if ENABLE(XPATH) + +#include "XPathFunctions.h" +#include "XPathNSResolver.h" +#include "XPathParser.h" +#include "XPathPath.h" +#include "XPathPredicate.h" +#include "XPathVariableReference.h" +#include <wtf/FastMalloc.h> + +#define YYMALLOC fastMalloc +#define YYFREE fastFree + +#define YYENABLE_NLS 0 +#define YYLTYPE_IS_TRIVIAL 1 +#define YYDEBUG 0 +#define YYMAXDEPTH 10000 +#define YYPARSE_PARAM parserParameter +#define PARSER static_cast<Parser*>(parserParameter) + +using namespace WebCore; +using namespace XPath; + +%} + +%pure_parser + +%union +{ + Step::Axis axis; + Step::NodeTest* nodeTest; + NumericOp::Opcode numop; + EqTestOp::Opcode eqop; + String* str; + Expression* expr; + Vector<Predicate*>* predList; + Vector<Expression*>* argList; + Step* step; + LocationPath* locationPath; +} + +%{ + +static int xpathyylex(YYSTYPE* yylval) { return Parser::current()->lex(yylval); } +static void xpathyyerror(const char*) { } + +%} + +%left <numop> MULOP +%left <eqop> EQOP RELOP +%left PLUS MINUS +%left OR AND +%token <axis> AXISNAME +%token <str> NODETYPE PI FUNCTIONNAME LITERAL +%token <str> VARIABLEREFERENCE NUMBER +%token DOTDOT SLASHSLASH +%token <str> NAMETEST +%token XPATH_ERROR + +%type <locationPath> LocationPath +%type <locationPath> AbsoluteLocationPath +%type <locationPath> RelativeLocationPath +%type <step> Step +%type <axis> AxisSpecifier +%type <step> DescendantOrSelf +%type <nodeTest> NodeTest +%type <expr> Predicate +%type <predList> OptionalPredicateList +%type <predList> PredicateList +%type <step> AbbreviatedStep +%type <expr> Expr +%type <expr> PrimaryExpr +%type <expr> FunctionCall +%type <argList> ArgumentList +%type <expr> Argument +%type <expr> UnionExpr +%type <expr> PathExpr +%type <expr> FilterExpr +%type <expr> OrExpr +%type <expr> AndExpr +%type <expr> EqualityExpr +%type <expr> RelationalExpr +%type <expr> AdditiveExpr +%type <expr> MultiplicativeExpr +%type <expr> UnaryExpr + +%% + +Expr: + OrExpr + { + PARSER->m_topExpr = $1; + } + ; + +LocationPath: + RelativeLocationPath + { + $$->setAbsolute(false); + } + | + AbsoluteLocationPath + { + $$->setAbsolute(true); + } + ; + +AbsoluteLocationPath: + '/' + { + $$ = new LocationPath; + PARSER->registerParseNode($$); + } + | + '/' RelativeLocationPath + { + $$ = $2; + } + | + DescendantOrSelf RelativeLocationPath + { + $$ = $2; + $$->insertFirstStep($1); + PARSER->unregisterParseNode($1); + } + ; + +RelativeLocationPath: + Step + { + $$ = new LocationPath; + $$->appendStep($1); + PARSER->unregisterParseNode($1); + PARSER->registerParseNode($$); + } + | + RelativeLocationPath '/' Step + { + $$->appendStep($3); + PARSER->unregisterParseNode($3); + } + | + RelativeLocationPath DescendantOrSelf Step + { + $$->appendStep($2); + $$->appendStep($3); + PARSER->unregisterParseNode($2); + PARSER->unregisterParseNode($3); + } + ; + +Step: + NodeTest OptionalPredicateList + { + if ($2) { + $$ = new Step(Step::ChildAxis, *$1, *$2); + PARSER->deletePredicateVector($2); + } else + $$ = new Step(Step::ChildAxis, *$1); + PARSER->deleteNodeTest($1); + PARSER->registerParseNode($$); + } + | + NAMETEST OptionalPredicateList + { + String localName; + String namespaceURI; + if (!PARSER->expandQName(*$1, localName, namespaceURI)) { + PARSER->m_gotNamespaceError = true; + YYABORT; + } + + if ($2) { + $$ = new Step(Step::ChildAxis, Step::NodeTest(Step::NodeTest::NameTest, localName, namespaceURI), *$2); + PARSER->deletePredicateVector($2); + } else + $$ = new Step(Step::ChildAxis, Step::NodeTest(Step::NodeTest::NameTest, localName, namespaceURI)); + PARSER->deleteString($1); + PARSER->registerParseNode($$); + } + | + AxisSpecifier NodeTest OptionalPredicateList + { + if ($3) { + $$ = new Step($1, *$2, *$3); + PARSER->deletePredicateVector($3); + } else + $$ = new Step($1, *$2); + PARSER->deleteNodeTest($2); + PARSER->registerParseNode($$); + } + | + AxisSpecifier NAMETEST OptionalPredicateList + { + String localName; + String namespaceURI; + if (!PARSER->expandQName(*$2, localName, namespaceURI)) { + PARSER->m_gotNamespaceError = true; + YYABORT; + } + + if ($3) { + $$ = new Step($1, Step::NodeTest(Step::NodeTest::NameTest, localName, namespaceURI), *$3); + PARSER->deletePredicateVector($3); + } else + $$ = new Step($1, Step::NodeTest(Step::NodeTest::NameTest, localName, namespaceURI)); + PARSER->deleteString($2); + PARSER->registerParseNode($$); + } + | + AbbreviatedStep + ; + +AxisSpecifier: + AXISNAME + | + '@' + { + $$ = Step::AttributeAxis; + } + ; + +NodeTest: + NODETYPE '(' ')' + { + if (*$1 == "node") + $$ = new Step::NodeTest(Step::NodeTest::AnyNodeTest); + else if (*$1 == "text") + $$ = new Step::NodeTest(Step::NodeTest::TextNodeTest); + else if (*$1 == "comment") + $$ = new Step::NodeTest(Step::NodeTest::CommentNodeTest); + + PARSER->deleteString($1); + PARSER->registerNodeTest($$); + } + | + PI '(' ')' + { + $$ = new Step::NodeTest(Step::NodeTest::ProcessingInstructionNodeTest); + PARSER->deleteString($1); + PARSER->registerNodeTest($$); + } + | + PI '(' LITERAL ')' + { + $$ = new Step::NodeTest(Step::NodeTest::ProcessingInstructionNodeTest, $3->stripWhiteSpace()); + PARSER->deleteString($1); + PARSER->deleteString($3); + PARSER->registerNodeTest($$); + } + ; + +OptionalPredicateList: + /* empty */ + { + $$ = 0; + } + | + PredicateList + ; + +PredicateList: + Predicate + { + $$ = new Vector<Predicate*>; + $$->append(new Predicate($1)); + PARSER->unregisterParseNode($1); + PARSER->registerPredicateVector($$); + } + | + PredicateList Predicate + { + $$->append(new Predicate($2)); + PARSER->unregisterParseNode($2); + } + ; + +Predicate: + '[' Expr ']' + { + $$ = $2; + } + ; + +DescendantOrSelf: + SLASHSLASH + { + $$ = new Step(Step::DescendantOrSelfAxis, Step::NodeTest(Step::NodeTest::AnyNodeTest)); + PARSER->registerParseNode($$); + } + ; + +AbbreviatedStep: + '.' + { + $$ = new Step(Step::SelfAxis, Step::NodeTest(Step::NodeTest::AnyNodeTest)); + PARSER->registerParseNode($$); + } + | + DOTDOT + { + $$ = new Step(Step::ParentAxis, Step::NodeTest(Step::NodeTest::AnyNodeTest)); + PARSER->registerParseNode($$); + } + ; + +PrimaryExpr: + VARIABLEREFERENCE + { + $$ = new VariableReference(*$1); + PARSER->deleteString($1); + PARSER->registerParseNode($$); + } + | + '(' Expr ')' + { + $$ = $2; + } + | + LITERAL + { + $$ = new StringExpression(*$1); + PARSER->deleteString($1); + PARSER->registerParseNode($$); + } + | + NUMBER + { + $$ = new Number($1->toDouble()); + PARSER->deleteString($1); + PARSER->registerParseNode($$); + } + | + FunctionCall + ; + +FunctionCall: + FUNCTIONNAME '(' ')' + { + $$ = createFunction(*$1); + if (!$$) + YYABORT; + PARSER->deleteString($1); + PARSER->registerParseNode($$); + } + | + FUNCTIONNAME '(' ArgumentList ')' + { + $$ = createFunction(*$1, *$3); + if (!$$) + YYABORT; + PARSER->deleteString($1); + PARSER->deleteExpressionVector($3); + PARSER->registerParseNode($$); + } + ; + +ArgumentList: + Argument + { + $$ = new Vector<Expression*>; + $$->append($1); + PARSER->unregisterParseNode($1); + PARSER->registerExpressionVector($$); + } + | + ArgumentList ',' Argument + { + $$->append($3); + PARSER->unregisterParseNode($3); + } + ; + +Argument: + Expr + ; + +UnionExpr: + PathExpr + | + UnionExpr '|' PathExpr + { + $$ = new Union; + $$->addSubExpression($1); + $$->addSubExpression($3); + PARSER->unregisterParseNode($1); + PARSER->unregisterParseNode($3); + PARSER->registerParseNode($$); + } + ; + +PathExpr: + LocationPath + { + $$ = $1; + } + | + FilterExpr + | + FilterExpr '/' RelativeLocationPath + { + $3->setAbsolute(true); + $$ = new Path(static_cast<Filter*>($1), $3); + PARSER->unregisterParseNode($1); + PARSER->unregisterParseNode($3); + PARSER->registerParseNode($$); + } + | + FilterExpr DescendantOrSelf RelativeLocationPath + { + $3->insertFirstStep($2); + $3->setAbsolute(true); + $$ = new Path(static_cast<Filter*>($1), $3); + PARSER->unregisterParseNode($1); + PARSER->unregisterParseNode($2); + PARSER->unregisterParseNode($3); + PARSER->registerParseNode($$); + } + ; + +FilterExpr: + PrimaryExpr + | + PrimaryExpr PredicateList + { + $$ = new Filter($1, *$2); + PARSER->unregisterParseNode($1); + PARSER->deletePredicateVector($2); + PARSER->registerParseNode($$); + } + ; + +OrExpr: + AndExpr + | + OrExpr OR AndExpr + { + $$ = new LogicalOp(LogicalOp::OP_Or, $1, $3); + PARSER->unregisterParseNode($1); + PARSER->unregisterParseNode($3); + PARSER->registerParseNode($$); + } + ; + +AndExpr: + EqualityExpr + | + AndExpr AND EqualityExpr + { + $$ = new LogicalOp(LogicalOp::OP_And, $1, $3); + PARSER->unregisterParseNode($1); + PARSER->unregisterParseNode($3); + PARSER->registerParseNode($$); + } + ; + +EqualityExpr: + RelationalExpr + | + EqualityExpr EQOP RelationalExpr + { + $$ = new EqTestOp($2, $1, $3); + PARSER->unregisterParseNode($1); + PARSER->unregisterParseNode($3); + PARSER->registerParseNode($$); + } + ; + +RelationalExpr: + AdditiveExpr + | + RelationalExpr RELOP AdditiveExpr + { + $$ = new EqTestOp($2, $1, $3); + PARSER->unregisterParseNode($1); + PARSER->unregisterParseNode($3); + PARSER->registerParseNode($$); + } + ; + +AdditiveExpr: + MultiplicativeExpr + | + AdditiveExpr PLUS MultiplicativeExpr + { + $$ = new NumericOp(NumericOp::OP_Add, $1, $3); + PARSER->unregisterParseNode($1); + PARSER->unregisterParseNode($3); + PARSER->registerParseNode($$); + } + | + AdditiveExpr MINUS MultiplicativeExpr + { + $$ = new NumericOp(NumericOp::OP_Sub, $1, $3); + PARSER->unregisterParseNode($1); + PARSER->unregisterParseNode($3); + PARSER->registerParseNode($$); + } + ; + +MultiplicativeExpr: + UnaryExpr + | + MultiplicativeExpr MULOP UnaryExpr + { + $$ = new NumericOp($2, $1, $3); + PARSER->unregisterParseNode($1); + PARSER->unregisterParseNode($3); + PARSER->registerParseNode($$); + } + ; + +UnaryExpr: + UnionExpr + | + MINUS UnaryExpr + { + $$ = new Negative; + $$->addSubExpression($2); + PARSER->unregisterParseNode($2); + PARSER->registerParseNode($$); + } + ; + +%% + +#endif diff --git a/Source/WebCore/xml/XPathNSResolver.cpp b/Source/WebCore/xml/XPathNSResolver.cpp new file mode 100644 index 0000000..3dde1f2 --- /dev/null +++ b/Source/WebCore/xml/XPathNSResolver.cpp @@ -0,0 +1,40 @@ +/* + * Copyright 2005 Frerich Raabe <raabe@kde.org> + * Copyright (C) 2006 Apple Computer, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "XPathNSResolver.h" + +#if ENABLE(XPATH) + +namespace WebCore { + +XPathNSResolver::~XPathNSResolver() +{ +} + +} + +#endif // ENABLE(XPATH) diff --git a/Source/WebCore/xml/XPathNSResolver.h b/Source/WebCore/xml/XPathNSResolver.h new file mode 100644 index 0000000..7b7fd2e --- /dev/null +++ b/Source/WebCore/xml/XPathNSResolver.h @@ -0,0 +1,50 @@ +/* + * Copyright 2005 Frerich Raabe <raabe@kde.org> + * Copyright (C) 2006 Apple Computer, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef XPathNSResolver_h +#define XPathNSResolver_h + +#if ENABLE(XPATH) + +#include <wtf/Forward.h> +#include <wtf/RefCounted.h> + +namespace WebCore { + + class XPathNSResolver : public RefCounted<XPathNSResolver> { + public: + virtual ~XPathNSResolver(); + virtual String lookupNamespaceURI(const String& prefix) = 0; + + protected: + XPathNSResolver() { } + }; + +} + +#endif // ENABLE(XPATH) + +#endif // XPathNSResolver_h diff --git a/Source/WebCore/xml/XPathNSResolver.idl b/Source/WebCore/xml/XPathNSResolver.idl new file mode 100644 index 0000000..4e996c2 --- /dev/null +++ b/Source/WebCore/xml/XPathNSResolver.idl @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2006 Apple Computer, Inc. + * Copyright (C) 2006 Samuel Weinig <sam.weinig@gmail.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. + */ + +module xpath { + + interface [ObjCProtocol, Conditional=XPATH, OmitConstructor] XPathNSResolver { + [ConvertNullStringTo=Null] DOMString lookupNamespaceURI(in DOMString prefix); + }; + +} diff --git a/Source/WebCore/xml/XPathNamespace.cpp b/Source/WebCore/xml/XPathNamespace.cpp new file mode 100644 index 0000000..9646402 --- /dev/null +++ b/Source/WebCore/xml/XPathNamespace.cpp @@ -0,0 +1,81 @@ +/* + * Copyright 2005 Frerich Raabe <raabe@kde.org> + * Copyright (C) 2006, 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "XPathNamespace.h" + +#if ENABLE(XPATH) + +#include "Element.h" + +namespace WebCore { + +XPathNamespace::XPathNamespace(PassRefPtr<Element> ownerElement, const AtomicString& prefix, const AtomicString& uri) + : Node(ownerElement->document(), CreateOther) + , m_ownerElement(ownerElement) + , m_prefix(prefix) + , m_uri(uri) +{ +} + +Document* XPathNamespace::ownerDocument() const +{ + return m_ownerElement->ownerDocument(); +} + +Element* XPathNamespace::ownerElement() const +{ + return m_ownerElement.get(); +} + +const AtomicString& XPathNamespace::prefix() const +{ + return m_prefix; +} + +String XPathNamespace::nodeName() const +{ + return "#namespace"; +} + +String XPathNamespace::nodeValue() const +{ + return m_uri; +} + +const AtomicString& XPathNamespace::namespaceURI() const +{ + return m_uri; +} + +Node::NodeType XPathNamespace::nodeType() const +{ + return Node::XPATH_NAMESPACE_NODE; +} + +} + +#endif // ENABLE(XPATH) diff --git a/Source/WebCore/xml/XPathNamespace.h b/Source/WebCore/xml/XPathNamespace.h new file mode 100644 index 0000000..c34eeef --- /dev/null +++ b/Source/WebCore/xml/XPathNamespace.h @@ -0,0 +1,63 @@ +/* + * Copyright 2005 Frerich Raabe <raabe@kde.org> + * Copyright (C) 2006, 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef XPathNamespace_h +#define XPathNamespace_h + +#if ENABLE(XPATH) + +#include "Node.h" +#include <wtf/text/AtomicString.h> + +namespace WebCore { + + // FIXME: This class is never instantiated. Maybe it should be removed. + + class XPathNamespace : public Node { + private: + XPathNamespace(PassRefPtr<Element> ownerElement, const AtomicString& prefix, const AtomicString& uri); + + virtual Document* ownerDocument() const; + virtual Element* ownerElement() const; + + virtual const AtomicString& prefix() const; + virtual String nodeName() const; + virtual String nodeValue() const; + virtual const AtomicString& namespaceURI() const; + + virtual NodeType nodeType() const; + + RefPtr<Element> m_ownerElement; + AtomicString m_prefix; + AtomicString m_uri; + }; + +} + +#endif // ENABLE(XPATH) + +#endif // XPathNamespace_h + diff --git a/Source/WebCore/xml/XPathNodeSet.cpp b/Source/WebCore/xml/XPathNodeSet.cpp new file mode 100644 index 0000000..a8e4428 --- /dev/null +++ b/Source/WebCore/xml/XPathNodeSet.cpp @@ -0,0 +1,206 @@ +/* + * Copyright (C) 2007 Alexey Proskuryakov <ap@webkit.org> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#if ENABLE(XPATH) +#include "XPathNodeSet.h" + +#include "Attr.h" +#include "Element.h" +#include "Node.h" + +namespace WebCore { +namespace XPath { + +static inline Node* parentWithDepth(unsigned depth, const Vector<Node*>& parents) +{ + ASSERT(parents.size() >= depth + 1); + return parents[parents.size() - 1 - depth]; +} + +static void sortBlock(unsigned from, unsigned to, Vector<Vector<Node*> >& parentMatrix, bool mayContainAttributeNodes) +{ + ASSERT(from + 1 < to); // Should not call this function with less that two nodes to sort. + unsigned minDepth = UINT_MAX; + for (unsigned i = from; i < to; ++i) { + unsigned depth = parentMatrix[i].size() - 1; + if (minDepth > depth) + minDepth = depth; + } + + // Find the common ancestor. + unsigned commonAncestorDepth = minDepth; + Node* commonAncestor; + while (true) { + commonAncestor = parentWithDepth(commonAncestorDepth, parentMatrix[from]); + if (commonAncestorDepth == 0) + break; + + bool allEqual = true; + for (unsigned i = from + 1; i < to; ++i) { + if (commonAncestor != parentWithDepth(commonAncestorDepth, parentMatrix[i])) { + allEqual = false; + break; + } + } + if (allEqual) + break; + + --commonAncestorDepth; + } + + if (commonAncestorDepth == minDepth) { + // One of the nodes is the common ancestor => it is the first in document order. + // Find it and move it to the beginning. + for (unsigned i = from; i < to; ++i) + if (commonAncestor == parentMatrix[i][0]) { + parentMatrix[i].swap(parentMatrix[from]); + if (from + 2 < to) + sortBlock(from + 1, to, parentMatrix, mayContainAttributeNodes); + return; + } + } + + if (mayContainAttributeNodes && commonAncestor->isElementNode()) { + // The attribute nodes and namespace nodes of an element occur before the children of the element. + // The namespace nodes are defined to occur before the attribute nodes. + // The relative order of namespace nodes is implementation-dependent. + // The relative order of attribute nodes is implementation-dependent. + unsigned sortedEnd = from; + // FIXME: namespace nodes are not implemented. + for (unsigned i = sortedEnd; i < to; ++i) { + Node* n = parentMatrix[i][0]; + if (n->isAttributeNode() && static_cast<Attr*>(n)->ownerElement() == commonAncestor) + parentMatrix[i].swap(parentMatrix[sortedEnd++]); + } + if (sortedEnd != from) { + if (to - sortedEnd > 1) + sortBlock(sortedEnd, to, parentMatrix, mayContainAttributeNodes); + return; + } + } + + // Children nodes of the common ancestor induce a subdivision of our node-set. + // Sort it according to this subdivision, and recursively sort each group. + HashSet<Node*> parentNodes; + for (unsigned i = from; i < to; ++i) + parentNodes.add(parentWithDepth(commonAncestorDepth + 1, parentMatrix[i])); + + unsigned previousGroupEnd = from; + unsigned groupEnd = from; + for (Node* n = commonAncestor->firstChild(); n; n = n->nextSibling()) { + // If parentNodes contains the node, perform a linear search to move its children in the node-set to the beginning. + if (parentNodes.contains(n)) { + for (unsigned i = groupEnd; i < to; ++i) + if (parentWithDepth(commonAncestorDepth + 1, parentMatrix[i]) == n) + parentMatrix[i].swap(parentMatrix[groupEnd++]); + + if (groupEnd - previousGroupEnd > 1) + sortBlock(previousGroupEnd, groupEnd, parentMatrix, mayContainAttributeNodes); + + ASSERT(previousGroupEnd != groupEnd); + previousGroupEnd = groupEnd; +#ifndef NDEBUG + parentNodes.remove(n); +#endif + } + } + + ASSERT(parentNodes.isEmpty()); +} + +void NodeSet::sort() const +{ + if (m_isSorted) + return; + + unsigned nodeCount = m_nodes.size(); + if (nodeCount < 2) { + const_cast<bool&>(m_isSorted) = true; + return; + } + + bool containsAttributeNodes = false; + + Vector<Vector<Node*> > parentMatrix(nodeCount); + for (unsigned i = 0; i < nodeCount; ++i) { + Vector<Node*>& parentsVector = parentMatrix[i]; + Node* n = m_nodes[i].get(); + parentsVector.append(n); + if (n->isAttributeNode()) { + n = static_cast<Attr*>(n)->ownerElement(); + parentsVector.append(n); + containsAttributeNodes = true; + } + while ((n = n->parentNode())) + parentsVector.append(n); + } + sortBlock(0, nodeCount, parentMatrix, containsAttributeNodes); + + // It is not possible to just assign the result to m_nodes, because some nodes may get dereferenced and destroyed. + Vector<RefPtr<Node> > sortedNodes; + sortedNodes.reserveInitialCapacity(nodeCount); + for (unsigned i = 0; i < nodeCount; ++i) + sortedNodes.append(parentMatrix[i][0]); + + const_cast<Vector<RefPtr<Node> >& >(m_nodes).swap(sortedNodes); +} + +void NodeSet::reverse() +{ + if (m_nodes.isEmpty()) + return; + + unsigned from = 0; + unsigned to = m_nodes.size() - 1; + while (from < to) { + m_nodes[from].swap(m_nodes[to]); + ++from; + --to; + } +} + +Node* NodeSet::firstNode() const +{ + if (isEmpty()) + return 0; + + sort(); // FIXME: fully sorting the node-set just to find its first node is wasteful. + return m_nodes.at(0).get(); +} + +Node* NodeSet::anyNode() const +{ + if (isEmpty()) + return 0; + + return m_nodes.at(0).get(); +} + +} +} + +#endif // ENABLE(XPATH) diff --git a/Source/WebCore/xml/XPathNodeSet.h b/Source/WebCore/xml/XPathNodeSet.h new file mode 100644 index 0000000..d5c47be --- /dev/null +++ b/Source/WebCore/xml/XPathNodeSet.h @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2007 Alexey Proskuryakov <ap@webkit.org> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef XPathNodeSet_h +#define XPathNodeSet_h + +#if ENABLE(XPATH) + +#include <wtf/Vector.h> +#include <wtf/Forward.h> + +#include "Node.h" + +namespace WebCore { + + namespace XPath { + + class NodeSet : public FastAllocBase { + public: + NodeSet() : m_isSorted(true), m_subtreesAreDisjoint(false) { } + + size_t size() const { return m_nodes.size(); } + bool isEmpty() const { return !m_nodes.size(); } + Node* operator[](unsigned i) const { return m_nodes.at(i).get(); } + void reserveCapacity(size_t newCapacity) { m_nodes.reserveCapacity(newCapacity); } + void clear() { m_nodes.clear(); } + void swap(NodeSet& other) { std::swap(m_isSorted, other.m_isSorted); std::swap(m_subtreesAreDisjoint, other.m_subtreesAreDisjoint); m_nodes.swap(other.m_nodes); } + + // NodeSet itself does not verify that nodes in it are unique. + void append(Node* node) { m_nodes.append(node); } + void append(PassRefPtr<Node> node) { m_nodes.append(node); } + void append(const NodeSet& nodeSet) { m_nodes.append(nodeSet.m_nodes); } + + // Returns the set's first node in document order, or 0 if the set is empty. + Node* firstNode() const; + + // Returns 0 if the set is empty. + Node* anyNode() const; + + // NodeSet itself doesn't check if it contains nodes in document order - the caller should tell it if it does not. + void markSorted(bool isSorted) { m_isSorted = isSorted; } + bool isSorted() const { return m_isSorted || m_nodes.size() < 2; } + + void sort() const; + + // No node in the set is ancestor of another. Unlike m_isSorted, this is assumed to be false, unless the caller sets it to true. + void markSubtreesDisjoint(bool disjoint) { m_subtreesAreDisjoint = disjoint; } + bool subtreesAreDisjoint() const { return m_subtreesAreDisjoint || m_nodes.size() < 2; } + + void reverse(); + + private: + bool m_isSorted; + bool m_subtreesAreDisjoint; + Vector<RefPtr<Node> > m_nodes; + }; + + } +} + +#endif // ENABLE(XPATH) +#endif // XPathNodeSet_h diff --git a/Source/WebCore/xml/XPathParser.cpp b/Source/WebCore/xml/XPathParser.cpp new file mode 100644 index 0000000..ba6da7e --- /dev/null +++ b/Source/WebCore/xml/XPathParser.cpp @@ -0,0 +1,638 @@ +/* + * Copyright 2005 Maksim Orlovich <maksim@kde.org> + * Copyright (C) 2006 Apple Computer, Inc. + * Copyright (C) 2007 Alexey Proskuryakov <ap@webkit.org> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "XPathParser.h" + +#if ENABLE(XPATH) + +#include "ExceptionCode.h" +#include "XPathEvaluator.h" +#include "XPathException.h" +#include "XPathNSResolver.h" +#include "XPathStep.h" +#include <wtf/StdLibExtras.h> +#include <wtf/text/StringHash.h> + +int xpathyyparse(void*); + +using namespace WTF; +using namespace Unicode; + +namespace WebCore { +namespace XPath { + +class LocationPath; + +#include "XPathGrammar.h" + +Parser* Parser::currentParser = 0; + +enum XMLCat { NameStart, NameCont, NotPartOfName }; + +typedef HashMap<String, Step::Axis> AxisNamesMap; + +static XMLCat charCat(UChar aChar) +{ + //### might need to add some special cases from the XML spec. + + if (aChar == '_') + return NameStart; + + if (aChar == '.' || aChar == '-') + return NameCont; + CharCategory category = Unicode::category(aChar); + if (category & (Letter_Uppercase | Letter_Lowercase | Letter_Other | Letter_Titlecase | Number_Letter)) + return NameStart; + if (category & (Mark_NonSpacing | Mark_SpacingCombining | Mark_Enclosing | Letter_Modifier | Number_DecimalDigit)) + return NameCont; + return NotPartOfName; +} + +static void setUpAxisNamesMap(AxisNamesMap& axisNames) +{ + struct AxisName { + const char* name; + Step::Axis axis; + }; + const AxisName axisNameList[] = { + { "ancestor", Step::AncestorAxis }, + { "ancestor-or-self", Step::AncestorOrSelfAxis }, + { "attribute", Step::AttributeAxis }, + { "child", Step::ChildAxis }, + { "descendant", Step::DescendantAxis }, + { "descendant-or-self", Step::DescendantOrSelfAxis }, + { "following", Step::FollowingAxis }, + { "following-sibling", Step::FollowingSiblingAxis }, + { "namespace", Step::NamespaceAxis }, + { "parent", Step::ParentAxis }, + { "preceding", Step::PrecedingAxis }, + { "preceding-sibling", Step::PrecedingSiblingAxis }, + { "self", Step::SelfAxis } + }; + for (unsigned i = 0; i < sizeof(axisNameList) / sizeof(axisNameList[0]); ++i) + axisNames.set(axisNameList[i].name, axisNameList[i].axis); +} + +static bool isAxisName(const String& name, Step::Axis& type) +{ + DEFINE_STATIC_LOCAL(AxisNamesMap, axisNames, ()); + + if (axisNames.isEmpty()) + setUpAxisNamesMap(axisNames); + + AxisNamesMap::iterator it = axisNames.find(name); + if (it == axisNames.end()) + return false; + type = it->second; + return true; +} + +static bool isNodeTypeName(const String& name) +{ + DEFINE_STATIC_LOCAL(HashSet<String>, nodeTypeNames, ()); + if (nodeTypeNames.isEmpty()) { + nodeTypeNames.add("comment"); + nodeTypeNames.add("text"); + nodeTypeNames.add("processing-instruction"); + nodeTypeNames.add("node"); + } + return nodeTypeNames.contains(name); +} + +// Returns whether the current token can possibly be a binary operator, given +// the previous token. Necessary to disambiguate some of the operators +// (* (multiply), div, and, or, mod) in the [32] Operator rule +// (check http://www.w3.org/TR/xpath#exprlex). +bool Parser::isBinaryOperatorContext() const +{ + switch (m_lastTokenType) { + case 0: + case '@': case AXISNAME: case '(': case '[': case ',': + case AND: case OR: case MULOP: + case '/': case SLASHSLASH: case '|': case PLUS: case MINUS: + case EQOP: case RELOP: + return false; + default: + return true; + } +} + +void Parser::skipWS() +{ + while (m_nextPos < m_data.length() && isSpaceOrNewline(m_data[m_nextPos])) + ++m_nextPos; +} + +Token Parser::makeTokenAndAdvance(int code, int advance) +{ + m_nextPos += advance; + return Token(code); +} + +Token Parser::makeTokenAndAdvance(int code, NumericOp::Opcode val, int advance) +{ + m_nextPos += advance; + return Token(code, val); +} + +Token Parser::makeTokenAndAdvance(int code, EqTestOp::Opcode val, int advance) +{ + m_nextPos += advance; + return Token(code, val); +} + +// Returns next char if it's there and interesting, 0 otherwise +char Parser::peekAheadHelper() +{ + if (m_nextPos + 1 >= m_data.length()) + return 0; + UChar next = m_data[m_nextPos + 1]; + if (next >= 0xff) + return 0; + return next; +} + +char Parser::peekCurHelper() +{ + if (m_nextPos >= m_data.length()) + return 0; + UChar next = m_data[m_nextPos]; + if (next >= 0xff) + return 0; + return next; +} + +Token Parser::lexString() +{ + UChar delimiter = m_data[m_nextPos]; + int startPos = m_nextPos + 1; + + for (m_nextPos = startPos; m_nextPos < m_data.length(); ++m_nextPos) { + if (m_data[m_nextPos] == delimiter) { + String value = m_data.substring(startPos, m_nextPos - startPos); + if (value.isNull()) + value = ""; + ++m_nextPos; // Consume the char. + return Token(LITERAL, value); + } + } + + // Ouch, went off the end -- report error. + return Token(XPATH_ERROR); +} + +Token Parser::lexNumber() +{ + int startPos = m_nextPos; + bool seenDot = false; + + // Go until end or a non-digits character. + for (; m_nextPos < m_data.length(); ++m_nextPos) { + UChar aChar = m_data[m_nextPos]; + if (aChar >= 0xff) break; + + if (aChar < '0' || aChar > '9') { + if (aChar == '.' && !seenDot) + seenDot = true; + else + break; + } + } + + return Token(NUMBER, m_data.substring(startPos, m_nextPos - startPos)); +} + +bool Parser::lexNCName(String& name) +{ + int startPos = m_nextPos; + if (m_nextPos >= m_data.length()) + return false; + + if (charCat(m_data[m_nextPos]) != NameStart) + return false; + + // Keep going until we get a character that's not good for names. + for (; m_nextPos < m_data.length(); ++m_nextPos) + if (charCat(m_data[m_nextPos]) == NotPartOfName) + break; + + name = m_data.substring(startPos, m_nextPos - startPos); + return true; +} + +bool Parser::lexQName(String& name) +{ + String n1; + if (!lexNCName(n1)) + return false; + + skipWS(); + + // If the next character is :, what we just got it the prefix, if not, + // it's the whole thing. + if (peekAheadHelper() != ':') { + name = n1; + return true; + } + + String n2; + if (!lexNCName(n2)) + return false; + + name = n1 + ":" + n2; + return true; +} + +Token Parser::nextTokenInternal() +{ + skipWS(); + + if (m_nextPos >= m_data.length()) + return Token(0); + + char code = peekCurHelper(); + switch (code) { + case '(': case ')': case '[': case ']': + case '@': case ',': case '|': + return makeTokenAndAdvance(code); + case '\'': + case '\"': + return lexString(); + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + return lexNumber(); + case '.': { + char next = peekAheadHelper(); + if (next == '.') + return makeTokenAndAdvance(DOTDOT, 2); + if (next >= '0' && next <= '9') + return lexNumber(); + return makeTokenAndAdvance('.'); + } + case '/': + if (peekAheadHelper() == '/') + return makeTokenAndAdvance(SLASHSLASH, 2); + return makeTokenAndAdvance('/'); + case '+': + return makeTokenAndAdvance(PLUS); + case '-': + return makeTokenAndAdvance(MINUS); + case '=': + return makeTokenAndAdvance(EQOP, EqTestOp::OP_EQ); + case '!': + if (peekAheadHelper() == '=') + return makeTokenAndAdvance(EQOP, EqTestOp::OP_NE, 2); + return Token(XPATH_ERROR); + case '<': + if (peekAheadHelper() == '=') + return makeTokenAndAdvance(RELOP, EqTestOp::OP_LE, 2); + return makeTokenAndAdvance(RELOP, EqTestOp::OP_LT); + case '>': + if (peekAheadHelper() == '=') + return makeTokenAndAdvance(RELOP, EqTestOp::OP_GE, 2); + return makeTokenAndAdvance(RELOP, EqTestOp::OP_GT); + case '*': + if (isBinaryOperatorContext()) + return makeTokenAndAdvance(MULOP, NumericOp::OP_Mul); + ++m_nextPos; + return Token(NAMETEST, "*"); + case '$': { // $ QName + m_nextPos++; + String name; + if (!lexQName(name)) + return Token(XPATH_ERROR); + return Token(VARIABLEREFERENCE, name); + } + } + + String name; + if (!lexNCName(name)) + return Token(XPATH_ERROR); + + skipWS(); + // If we're in an operator context, check for any operator names + if (isBinaryOperatorContext()) { + if (name == "and") //### hash? + return Token(AND); + if (name == "or") + return Token(OR); + if (name == "mod") + return Token(MULOP, NumericOp::OP_Mod); + if (name == "div") + return Token(MULOP, NumericOp::OP_Div); + } + + // See whether we are at a : + if (peekCurHelper() == ':') { + m_nextPos++; + // Any chance it's an axis name? + if (peekCurHelper() == ':') { + m_nextPos++; + + //It might be an axis name. + Step::Axis axis; + if (isAxisName(name, axis)) + return Token(AXISNAME, axis); + // Ugh, :: is only valid in axis names -> error + return Token(XPATH_ERROR); + } + + // Seems like this is a fully qualified qname, or perhaps the * modified one from NameTest + skipWS(); + if (peekCurHelper() == '*') { + m_nextPos++; + return Token(NAMETEST, name + ":*"); + } + + // Make a full qname. + String n2; + if (!lexNCName(n2)) + return Token(XPATH_ERROR); + + name = name + ":" + n2; + } + + skipWS(); + if (peekCurHelper() == '(') { + //note: we don't swallow the (here! + + //either node type of function name + if (isNodeTypeName(name)) { + if (name == "processing-instruction") + return Token(PI, name); + + return Token(NODETYPE, name); + } + //must be a function name. + return Token(FUNCTIONNAME, name); + } + + // At this point, it must be NAMETEST. + return Token(NAMETEST, name); +} + +Token Parser::nextToken() +{ + Token toRet = nextTokenInternal(); + m_lastTokenType = toRet.type; + return toRet; +} + +Parser::Parser() +{ + reset(String()); +} + +Parser::~Parser() +{ +} + +void Parser::reset(const String& data) +{ + m_nextPos = 0; + m_data = data; + m_lastTokenType = 0; + + m_topExpr = 0; + m_gotNamespaceError = false; +} + +int Parser::lex(void* data) +{ + YYSTYPE* yylval = static_cast<YYSTYPE*>(data); + Token tok = nextToken(); + + switch (tok.type) { + case AXISNAME: + yylval->axis = tok.axis; + break; + case MULOP: + yylval->numop = tok.numop; + break; + case RELOP: + case EQOP: + yylval->eqop = tok.eqop; + break; + case NODETYPE: + case PI: + case FUNCTIONNAME: + case LITERAL: + case VARIABLEREFERENCE: + case NUMBER: + case NAMETEST: + yylval->str = new String(tok.str); + registerString(yylval->str); + break; + } + + return tok.type; +} + +bool Parser::expandQName(const String& qName, String& localName, String& namespaceURI) +{ + size_t colon = qName.find(':'); + if (colon != notFound) { + if (!m_resolver) + return false; + namespaceURI = m_resolver->lookupNamespaceURI(qName.left(colon)); + if (namespaceURI.isNull()) + return false; + localName = qName.substring(colon + 1); + } else + localName = qName; + + return true; +} + +Expression* Parser::parseStatement(const String& statement, PassRefPtr<XPathNSResolver> resolver, ExceptionCode& ec) +{ + reset(statement); + + m_resolver = resolver; + + Parser* oldParser = currentParser; + currentParser = this; + int parseError = xpathyyparse(this); + currentParser = oldParser; + + if (parseError) { + deleteAllValues(m_parseNodes); + m_parseNodes.clear(); + + HashSet<Vector<Predicate*>*>::iterator pend = m_predicateVectors.end(); + for (HashSet<Vector<Predicate*>*>::iterator it = m_predicateVectors.begin(); it != pend; ++it) { + deleteAllValues(**it); + delete *it; + } + m_predicateVectors.clear(); + + HashSet<Vector<Expression*>*>::iterator eend = m_expressionVectors.end(); + for (HashSet<Vector<Expression*>*>::iterator it = m_expressionVectors.begin(); it != eend; ++it) { + deleteAllValues(**it); + delete *it; + } + m_expressionVectors.clear(); + + deleteAllValues(m_strings); + m_strings.clear(); + + deleteAllValues(m_nodeTests); + m_nodeTests.clear(); + + m_topExpr = 0; + + if (m_gotNamespaceError) + ec = NAMESPACE_ERR; + else + ec = XPathException::INVALID_EXPRESSION_ERR; + return 0; + } + + ASSERT(m_parseNodes.size() == 1); + ASSERT(*m_parseNodes.begin() == m_topExpr); + ASSERT(m_expressionVectors.size() == 0); + ASSERT(m_predicateVectors.size() == 0); + ASSERT(m_strings.size() == 0); + ASSERT(m_nodeTests.size() == 0); + + m_parseNodes.clear(); + Expression* result = m_topExpr; + m_topExpr = 0; + + return result; +} + +void Parser::registerParseNode(ParseNode* node) +{ + if (node == 0) + return; + + ASSERT(!m_parseNodes.contains(node)); + + m_parseNodes.add(node); +} + +void Parser::unregisterParseNode(ParseNode* node) +{ + if (node == 0) + return; + + ASSERT(m_parseNodes.contains(node)); + + m_parseNodes.remove(node); +} + +void Parser::registerPredicateVector(Vector<Predicate*>* vector) +{ + if (vector == 0) + return; + + ASSERT(!m_predicateVectors.contains(vector)); + + m_predicateVectors.add(vector); +} + +void Parser::deletePredicateVector(Vector<Predicate*>* vector) +{ + if (vector == 0) + return; + + ASSERT(m_predicateVectors.contains(vector)); + + m_predicateVectors.remove(vector); + delete vector; +} + + +void Parser::registerExpressionVector(Vector<Expression*>* vector) +{ + if (vector == 0) + return; + + ASSERT(!m_expressionVectors.contains(vector)); + + m_expressionVectors.add(vector); +} + +void Parser::deleteExpressionVector(Vector<Expression*>* vector) +{ + if (vector == 0) + return; + + ASSERT(m_expressionVectors.contains(vector)); + + m_expressionVectors.remove(vector); + delete vector; +} + +void Parser::registerString(String* s) +{ + if (s == 0) + return; + + ASSERT(!m_strings.contains(s)); + + m_strings.add(s); +} + +void Parser::deleteString(String* s) +{ + if (s == 0) + return; + + ASSERT(m_strings.contains(s)); + + m_strings.remove(s); + delete s; +} + +void Parser::registerNodeTest(Step::NodeTest* t) +{ + if (t == 0) + return; + + ASSERT(!m_nodeTests.contains(t)); + + m_nodeTests.add(t); +} + +void Parser::deleteNodeTest(Step::NodeTest* t) +{ + if (t == 0) + return; + + ASSERT(m_nodeTests.contains(t)); + + m_nodeTests.remove(t); + delete t; +} + +} +} + +#endif // ENABLE(XPATH) diff --git a/Source/WebCore/xml/XPathParser.h b/Source/WebCore/xml/XPathParser.h new file mode 100644 index 0000000..0ee447a --- /dev/null +++ b/Source/WebCore/xml/XPathParser.h @@ -0,0 +1,132 @@ +/* + * Copyright 2005 Maksim Orlovich <maksim@kde.org> + * Copyright (C) 2006 Apple Computer, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef XPathParser_h +#define XPathParser_h + +#if ENABLE(XPATH) + +#include "XPathStep.h" +#include "XPathPredicate.h" + +namespace WebCore { + + typedef int ExceptionCode; + + class XPathNSResolver; + + namespace XPath { + + class Expression; + class ParseNode; + class Predicate; + + struct Token { + int type; + String str; + Step::Axis axis; + NumericOp::Opcode numop; + EqTestOp::Opcode eqop; + + Token(int t) : type(t) {} + Token(int t, const String& v): type(t), str(v) {} + Token(int t, Step::Axis v): type(t), axis(v) {} + Token(int t, NumericOp::Opcode v): type(t), numop(v) {} + Token(int t, EqTestOp::Opcode v): type(t), eqop(v) {} + }; + + class Parser : public Noncopyable { + public: + Parser(); + ~Parser(); + + XPathNSResolver* resolver() const { return m_resolver.get(); } + bool expandQName(const String& qName, String& localName, String& namespaceURI); + + Expression* parseStatement(const String& statement, PassRefPtr<XPathNSResolver>, ExceptionCode&); + + static Parser* current() { return currentParser; } + + int lex(void* yylval); + + Expression* m_topExpr; + bool m_gotNamespaceError; + + void registerParseNode(ParseNode*); + void unregisterParseNode(ParseNode*); + + void registerPredicateVector(Vector<Predicate*>*); + void deletePredicateVector(Vector<Predicate*>*); + + void registerExpressionVector(Vector<Expression*>*); + void deleteExpressionVector(Vector<Expression*>*); + + void registerString(String*); + void deleteString(String*); + + void registerNodeTest(Step::NodeTest*); + void deleteNodeTest(Step::NodeTest*); + + private: + bool isBinaryOperatorContext() const; + + void skipWS(); + Token makeTokenAndAdvance(int type, int advance = 1); + Token makeTokenAndAdvance(int type, NumericOp::Opcode, int advance = 1); + Token makeTokenAndAdvance(int type, EqTestOp::Opcode, int advance = 1); + char peekAheadHelper(); + char peekCurHelper(); + + Token lexString(); + Token lexNumber(); + bool lexNCName(String&); + bool lexQName(String&); + + Token nextToken(); + Token nextTokenInternal(); + + void reset(const String& data); + + static Parser* currentParser; + + unsigned m_nextPos; + String m_data; + int m_lastTokenType; + RefPtr<XPathNSResolver> m_resolver; + + HashSet<ParseNode*> m_parseNodes; + HashSet<Vector<Predicate*>*> m_predicateVectors; + HashSet<Vector<Expression*>*> m_expressionVectors; + HashSet<String*> m_strings; + HashSet<Step::NodeTest*> m_nodeTests; + }; + + } +} + +#endif // ENABLE(XPATH) + +#endif diff --git a/Source/WebCore/xml/XPathPath.cpp b/Source/WebCore/xml/XPathPath.cpp new file mode 100644 index 0000000..1a7ed3f --- /dev/null +++ b/Source/WebCore/xml/XPathPath.cpp @@ -0,0 +1,209 @@ +/* + * Copyright (C) 2005 Frerich Raabe <raabe@kde.org> + * Copyright (C) 2006, 2009 Apple Inc. + * Copyright (C) 2007 Alexey Proskuryakov <ap@webkit.org> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "XPathPath.h" + +#if ENABLE(XPATH) + +#include "Document.h" +#include "XPathPredicate.h" +#include "XPathStep.h" +#include "XPathValue.h" + +namespace WebCore { +namespace XPath { + +Filter::Filter(Expression* expr, const Vector<Predicate*>& predicates) + : m_expr(expr), m_predicates(predicates) +{ + setIsContextNodeSensitive(m_expr->isContextNodeSensitive()); + setIsContextPositionSensitive(m_expr->isContextPositionSensitive()); + setIsContextSizeSensitive(m_expr->isContextSizeSensitive()); +} + +Filter::~Filter() +{ + delete m_expr; + deleteAllValues(m_predicates); +} + +Value Filter::evaluate() const +{ + Value v = m_expr->evaluate(); + + NodeSet& nodes = v.modifiableNodeSet(); + nodes.sort(); + + EvaluationContext& evaluationContext = Expression::evaluationContext(); + for (unsigned i = 0; i < m_predicates.size(); i++) { + NodeSet newNodes; + evaluationContext.size = nodes.size(); + evaluationContext.position = 0; + + for (unsigned j = 0; j < nodes.size(); j++) { + Node* node = nodes[j]; + + evaluationContext.node = node; + ++evaluationContext.position; + + if (m_predicates[i]->evaluate()) + newNodes.append(node); + } + nodes.swap(newNodes); + } + + return v; +} + +LocationPath::LocationPath() + : m_absolute(false) +{ + setIsContextNodeSensitive(true); +} + +LocationPath::~LocationPath() +{ + deleteAllValues(m_steps); +} + +Value LocationPath::evaluate() const +{ + EvaluationContext& evaluationContext = Expression::evaluationContext(); + EvaluationContext backupContext = evaluationContext; + // For absolute location paths, the context node is ignored - the + // document's root node is used instead. + Node* context = evaluationContext.node.get(); + if (m_absolute && context->nodeType() != Node::DOCUMENT_NODE) + context = context->ownerDocument(); + + NodeSet nodes; + nodes.append(context); + evaluate(nodes); + + evaluationContext = backupContext; + return Value(nodes, Value::adopt); +} + +void LocationPath::evaluate(NodeSet& nodes) const +{ + bool resultIsSorted = nodes.isSorted(); + + for (unsigned i = 0; i < m_steps.size(); i++) { + Step* step = m_steps[i]; + NodeSet newNodes; + HashSet<Node*> newNodesSet; + + bool needToCheckForDuplicateNodes = !nodes.subtreesAreDisjoint() || (step->axis() != Step::ChildAxis && step->axis() != Step::SelfAxis + && step->axis() != Step::DescendantAxis && step->axis() != Step::DescendantOrSelfAxis && step->axis() != Step::AttributeAxis); + + if (needToCheckForDuplicateNodes) + resultIsSorted = false; + + // This is a simplified check that can be improved to handle more cases. + if (nodes.subtreesAreDisjoint() && (step->axis() == Step::ChildAxis || step->axis() == Step::SelfAxis)) + newNodes.markSubtreesDisjoint(true); + + for (unsigned j = 0; j < nodes.size(); j++) { + NodeSet matches; + step->evaluate(nodes[j], matches); + + if (!matches.isSorted()) + resultIsSorted = false; + + for (size_t nodeIndex = 0; nodeIndex < matches.size(); ++nodeIndex) { + Node* node = matches[nodeIndex]; + if (!needToCheckForDuplicateNodes || newNodesSet.add(node).second) + newNodes.append(node); + } + } + + nodes.swap(newNodes); + } + + nodes.markSorted(resultIsSorted); +} + +void LocationPath::appendStep(Step* step) +{ + unsigned stepCount = m_steps.size(); + if (stepCount) { + bool dropSecondStep; + optimizeStepPair(m_steps[stepCount - 1], step, dropSecondStep); + if (dropSecondStep) { + delete step; + return; + } + } + step->optimize(); + m_steps.append(step); +} + +void LocationPath::insertFirstStep(Step* step) +{ + if (m_steps.size()) { + bool dropSecondStep; + optimizeStepPair(step, m_steps[0], dropSecondStep); + if (dropSecondStep) { + delete m_steps[0]; + m_steps[0] = step; + return; + } + } + step->optimize(); + m_steps.insert(0, step); +} + +Path::Path(Filter* filter, LocationPath* path) + : m_filter(filter) + , m_path(path) +{ + setIsContextNodeSensitive(filter->isContextNodeSensitive()); + setIsContextPositionSensitive(filter->isContextPositionSensitive()); + setIsContextSizeSensitive(filter->isContextSizeSensitive()); +} + +Path::~Path() +{ + delete m_filter; + delete m_path; +} + +Value Path::evaluate() const +{ + Value v = m_filter->evaluate(); + + NodeSet& nodes = v.modifiableNodeSet(); + m_path->evaluate(nodes); + + return v; +} + +} +} + +#endif // ENABLE(XPATH) diff --git a/Source/WebCore/xml/XPathPath.h b/Source/WebCore/xml/XPathPath.h new file mode 100644 index 0000000..7dd17d9 --- /dev/null +++ b/Source/WebCore/xml/XPathPath.h @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2005 Frerich Raabe <raabe@kde.org> + * Copyright (C) 2006, 2009 Apple Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef XPathPath_h +#define XPathPath_h + +#if ENABLE(XPATH) + +#include "XPathExpressionNode.h" +#include "XPathNodeSet.h" + +namespace WebCore { + + namespace XPath { + + class Predicate; + class Step; + + class Filter : public Expression { + public: + Filter(Expression*, const Vector<Predicate*>& = Vector<Predicate*>()); + virtual ~Filter(); + + virtual Value evaluate() const; + + private: + virtual Value::Type resultType() const { return Value::NodeSetValue; } + + Expression* m_expr; + Vector<Predicate*> m_predicates; + }; + + class LocationPath : public Expression { + public: + LocationPath(); + virtual ~LocationPath(); + void setAbsolute(bool value) { m_absolute = value; setIsContextNodeSensitive(!m_absolute); } + + virtual Value evaluate() const; + void evaluate(NodeSet& nodes) const; // nodes is an input/output parameter + + void appendStep(Step* step); + void insertFirstStep(Step* step); + + private: + virtual Value::Type resultType() const { return Value::NodeSetValue; } + + Vector<Step*> m_steps; + bool m_absolute; + }; + + class Path : public Expression { + public: + Path(Filter*, LocationPath*); + virtual ~Path(); + + virtual Value evaluate() const; + + private: + virtual Value::Type resultType() const { return Value::NodeSetValue; } + + Filter* m_filter; + LocationPath* m_path; + }; + + } +} + +#endif // ENABLE(XPATH) + +#endif // XPath_Path_H diff --git a/Source/WebCore/xml/XPathPredicate.cpp b/Source/WebCore/xml/XPathPredicate.cpp new file mode 100644 index 0000000..2a6482f --- /dev/null +++ b/Source/WebCore/xml/XPathPredicate.cpp @@ -0,0 +1,282 @@ +/* + * Copyright 2005 Frerich Raabe <raabe@kde.org> + * Copyright (C) 2006 Apple Computer, Inc. + * Copyright (C) 2007 Alexey Proskuryakov <ap@webkit.org> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#if ENABLE(XPATH) + +#include "XPathPredicate.h" + +#include "Node.h" +#include "XPathFunctions.h" +#include "XPathUtil.h" +#include "XPathValue.h" +#include <math.h> +#include <wtf/MathExtras.h> + +namespace WebCore { +namespace XPath { + +Number::Number(double value) + : m_value(value) +{ +} + +Value Number::evaluate() const +{ + return m_value; +} + +StringExpression::StringExpression(const String& value) + : m_value(value) +{ +} + +Value StringExpression::evaluate() const +{ + return m_value; +} + +Value Negative::evaluate() const +{ + Value p(subExpr(0)->evaluate()); + return -p.toNumber(); +} + +NumericOp::NumericOp(Opcode opcode, Expression* lhs, Expression* rhs) + : m_opcode(opcode) +{ + addSubExpression(lhs); + addSubExpression(rhs); +} + +Value NumericOp::evaluate() const +{ + Value lhs(subExpr(0)->evaluate()); + Value rhs(subExpr(1)->evaluate()); + + double leftVal = lhs.toNumber(); + double rightVal = rhs.toNumber(); + + switch (m_opcode) { + case OP_Add: + return leftVal + rightVal; + case OP_Sub: + return leftVal - rightVal; + case OP_Mul: + return leftVal * rightVal; + case OP_Div: + return leftVal / rightVal; + case OP_Mod: + return fmod(leftVal, rightVal); + } + ASSERT_NOT_REACHED(); + return 0.0; +} + +EqTestOp::EqTestOp(Opcode opcode, Expression* lhs, Expression* rhs) + : m_opcode(opcode) +{ + addSubExpression(lhs); + addSubExpression(rhs); +} + +bool EqTestOp::compare(const Value& lhs, const Value& rhs) const +{ + if (lhs.isNodeSet()) { + const NodeSet& lhsSet = lhs.toNodeSet(); + if (rhs.isNodeSet()) { + // If both objects to be compared are node-sets, then the comparison will be true if and only if + // there is a node in the first node-set and a node in the second node-set such that the result of + // performing the comparison on the string-values of the two nodes is true. + const NodeSet& rhsSet = rhs.toNodeSet(); + for (unsigned lindex = 0; lindex < lhsSet.size(); ++lindex) + for (unsigned rindex = 0; rindex < rhsSet.size(); ++rindex) + if (compare(stringValue(lhsSet[lindex]), stringValue(rhsSet[rindex]))) + return true; + return false; + } + if (rhs.isNumber()) { + // If one object to be compared is a node-set and the other is a number, then the comparison will be true + // if and only if there is a node in the node-set such that the result of performing the comparison on the number + // to be compared and on the result of converting the string-value of that node to a number using the number function is true. + for (unsigned lindex = 0; lindex < lhsSet.size(); ++lindex) + if (compare(Value(stringValue(lhsSet[lindex])).toNumber(), rhs)) + return true; + return false; + } + if (rhs.isString()) { + // If one object to be compared is a node-set and the other is a string, then the comparison will be true + // if and only if there is a node in the node-set such that the result of performing the comparison on + // the string-value of the node and the other string is true. + for (unsigned lindex = 0; lindex < lhsSet.size(); ++lindex) + if (compare(stringValue(lhsSet[lindex]), rhs)) + return true; + return false; + } + if (rhs.isBoolean()) { + // If one object to be compared is a node-set and the other is a boolean, then the comparison will be true + // if and only if the result of performing the comparison on the boolean and on the result of converting + // the node-set to a boolean using the boolean function is true. + return compare(lhs.toBoolean(), rhs); + } + ASSERT(0); + } + if (rhs.isNodeSet()) { + const NodeSet& rhsSet = rhs.toNodeSet(); + if (lhs.isNumber()) { + for (unsigned rindex = 0; rindex < rhsSet.size(); ++rindex) + if (compare(lhs, Value(stringValue(rhsSet[rindex])).toNumber())) + return true; + return false; + } + if (lhs.isString()) { + for (unsigned rindex = 0; rindex < rhsSet.size(); ++rindex) + if (compare(lhs, stringValue(rhsSet[rindex]))) + return true; + return false; + } + if (lhs.isBoolean()) + return compare(lhs, rhs.toBoolean()); + ASSERT(0); + } + + // Neither side is a NodeSet. + switch (m_opcode) { + case OP_EQ: + case OP_NE: + bool equal; + if (lhs.isBoolean() || rhs.isBoolean()) + equal = lhs.toBoolean() == rhs.toBoolean(); + else if (lhs.isNumber() || rhs.isNumber()) + equal = lhs.toNumber() == rhs.toNumber(); + else + equal = lhs.toString() == rhs.toString(); + + if (m_opcode == OP_EQ) + return equal; + return !equal; + case OP_GT: + return lhs.toNumber() > rhs.toNumber(); + case OP_GE: + return lhs.toNumber() >= rhs.toNumber(); + case OP_LT: + return lhs.toNumber() < rhs.toNumber(); + case OP_LE: + return lhs.toNumber() <= rhs.toNumber(); + } + ASSERT(0); + return false; +} + +Value EqTestOp::evaluate() const +{ + Value lhs(subExpr(0)->evaluate()); + Value rhs(subExpr(1)->evaluate()); + + return compare(lhs, rhs); +} + +LogicalOp::LogicalOp(Opcode opcode, Expression* lhs, Expression* rhs) + : m_opcode(opcode) +{ + addSubExpression(lhs); + addSubExpression(rhs); +} + +bool LogicalOp::shortCircuitOn() const +{ + if (m_opcode == OP_And) + return false; //false and foo + + return true; //true or bar +} + +Value LogicalOp::evaluate() const +{ + Value lhs(subExpr(0)->evaluate()); + + // This is not only an optimization, http://www.w3.org/TR/xpath + // dictates that we must do short-circuit evaluation + bool lhsBool = lhs.toBoolean(); + if (lhsBool == shortCircuitOn()) + return lhsBool; + + return subExpr(1)->evaluate().toBoolean(); +} + +Value Union::evaluate() const +{ + Value lhsResult = subExpr(0)->evaluate(); + Value rhs = subExpr(1)->evaluate(); + + NodeSet& resultSet = lhsResult.modifiableNodeSet(); + const NodeSet& rhsNodes = rhs.toNodeSet(); + + HashSet<Node*> nodes; + for (size_t i = 0; i < resultSet.size(); ++i) + nodes.add(resultSet[i]); + + for (size_t i = 0; i < rhsNodes.size(); ++i) { + Node* node = rhsNodes[i]; + if (nodes.add(node).second) + resultSet.append(node); + } + + // It is also possible to use merge sort to avoid making the result unsorted; + // but this would waste the time in cases when order is not important. + resultSet.markSorted(false); + return lhsResult; +} + +Predicate::Predicate(Expression* expr) + : m_expr(expr) +{ +} + +Predicate::~Predicate() +{ + delete m_expr; +} + +bool Predicate::evaluate() const +{ + ASSERT(m_expr != 0); + + Value result(m_expr->evaluate()); + + // foo[3] means foo[position()=3] + if (result.isNumber()) + return EqTestOp(EqTestOp::OP_EQ, createFunction("position"), new Number(result.toNumber())).evaluate().toBoolean(); + + return result.toBoolean(); +} + +} +} + +#endif // ENABLE(XPATH) diff --git a/Source/WebCore/xml/XPathPredicate.h b/Source/WebCore/xml/XPathPredicate.h new file mode 100644 index 0000000..5f2482a --- /dev/null +++ b/Source/WebCore/xml/XPathPredicate.h @@ -0,0 +1,127 @@ +/* + * Copyright 2005 Frerich Raabe <raabe@kde.org> + * Copyright (C) 2006 Apple Computer, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef XPathPredicate_h +#define XPathPredicate_h + +#if ENABLE(XPATH) + +#include "XPathExpressionNode.h" +#include "XPathValue.h" + +namespace WebCore { + + namespace XPath { + + class Number : public Expression { + public: + Number(double); + private: + virtual Value evaluate() const; + virtual Value::Type resultType() const { return Value::NumberValue; } + + Value m_value; + }; + + class StringExpression : public Expression { + public: + StringExpression(const String&); + private: + virtual Value evaluate() const; + virtual Value::Type resultType() const { return Value::StringValue; } + + Value m_value; + }; + + class Negative : public Expression { + private: + virtual Value evaluate() const; + virtual Value::Type resultType() const { return Value::NumberValue; } + }; + + class NumericOp : public Expression { + public: + enum Opcode { + OP_Add, OP_Sub, OP_Mul, OP_Div, OP_Mod + }; + NumericOp(Opcode, Expression* lhs, Expression* rhs); + private: + virtual Value evaluate() const; + virtual Value::Type resultType() const { return Value::NumberValue; } + + Opcode m_opcode; + }; + + class EqTestOp : public Expression { + public: + enum Opcode { OP_EQ, OP_NE, OP_GT, OP_LT, OP_GE, OP_LE }; + EqTestOp(Opcode, Expression* lhs, Expression* rhs); + virtual Value evaluate() const; + private: + virtual Value::Type resultType() const { return Value::BooleanValue; } + bool compare(const Value&, const Value&) const; + + Opcode m_opcode; + }; + + class LogicalOp : public Expression { + public: + enum Opcode { OP_And, OP_Or }; + LogicalOp(Opcode, Expression* lhs, Expression* rhs); + private: + virtual Value::Type resultType() const { return Value::BooleanValue; } + bool shortCircuitOn() const; + virtual Value evaluate() const; + + Opcode m_opcode; + }; + + class Union : public Expression { + private: + virtual Value evaluate() const; + virtual Value::Type resultType() const { return Value::NodeSetValue; } + }; + + class Predicate : public Noncopyable { + public: + Predicate(Expression*); + ~Predicate(); + bool evaluate() const; + + bool isContextPositionSensitive() const { return m_expr->isContextPositionSensitive() || m_expr->resultType() == Value::NumberValue; } + bool isContextSizeSensitive() const { return m_expr->isContextSizeSensitive(); } + + private: + Expression* m_expr; + }; + + } + +} + +#endif // ENABLE(XPATH) + +#endif // XPathPredicate_h diff --git a/Source/WebCore/xml/XPathResult.cpp b/Source/WebCore/xml/XPathResult.cpp new file mode 100644 index 0000000..b608280 --- /dev/null +++ b/Source/WebCore/xml/XPathResult.cpp @@ -0,0 +1,219 @@ +/* + * Copyright (C) 2005 Frerich Raabe <raabe@kde.org> + * Copyright (C) 2006, 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "XPathResult.h" + +#if ENABLE(XPATH) + +#include "Document.h" +#include "Node.h" +#include "ExceptionCode.h" +#include "XPathEvaluator.h" +#include "XPathException.h" + +namespace WebCore { + +using namespace XPath; + +XPathResult::XPathResult(Document* document, const Value& value) + : m_value(value) +{ + switch (m_value.type()) { + case Value::BooleanValue: + m_resultType = BOOLEAN_TYPE; + return; + case Value::NumberValue: + m_resultType = NUMBER_TYPE; + return; + case Value::StringValue: + m_resultType = STRING_TYPE; + return; + case Value::NodeSetValue: + m_resultType = UNORDERED_NODE_ITERATOR_TYPE; + m_nodeSetPosition = 0; + m_nodeSet = m_value.toNodeSet(); + m_document = document; + m_domTreeVersion = document->domTreeVersion(); + return; + } + ASSERT_NOT_REACHED(); +} + +XPathResult::~XPathResult() +{ +} + +void XPathResult::convertTo(unsigned short type, ExceptionCode& ec) +{ + switch (type) { + case ANY_TYPE: + break; + case NUMBER_TYPE: + m_resultType = type; + m_value = m_value.toNumber(); + break; + case STRING_TYPE: + m_resultType = type; + m_value = m_value.toString(); + break; + case BOOLEAN_TYPE: + m_resultType = type; + m_value = m_value.toBoolean(); + break; + case UNORDERED_NODE_ITERATOR_TYPE: + case UNORDERED_NODE_SNAPSHOT_TYPE: + case ANY_UNORDERED_NODE_TYPE: + case FIRST_ORDERED_NODE_TYPE: // This is correct - singleNodeValue() will take care of ordering. + if (!m_value.isNodeSet()) { + ec = XPathException::TYPE_ERR; + return; + } + m_resultType = type; + break; + case ORDERED_NODE_ITERATOR_TYPE: + if (!m_value.isNodeSet()) { + ec = XPathException::TYPE_ERR; + return; + } + m_nodeSet.sort(); + m_resultType = type; + break; + case ORDERED_NODE_SNAPSHOT_TYPE: + if (!m_value.isNodeSet()) { + ec = XPathException::TYPE_ERR; + return; + } + m_value.toNodeSet().sort(); + m_resultType = type; + break; + } +} + +unsigned short XPathResult::resultType() const +{ + return m_resultType; +} + +double XPathResult::numberValue(ExceptionCode& ec) const +{ + if (resultType() != NUMBER_TYPE) { + ec = XPathException::TYPE_ERR; + return 0.0; + } + return m_value.toNumber(); +} + +String XPathResult::stringValue(ExceptionCode& ec) const +{ + if (resultType() != STRING_TYPE) { + ec = XPathException::TYPE_ERR; + return String(); + } + return m_value.toString(); +} + +bool XPathResult::booleanValue(ExceptionCode& ec) const +{ + if (resultType() != BOOLEAN_TYPE) { + ec = XPathException::TYPE_ERR; + return false; + } + return m_value.toBoolean(); +} + +Node* XPathResult::singleNodeValue(ExceptionCode& ec) const +{ + if (resultType() != ANY_UNORDERED_NODE_TYPE && resultType() != FIRST_ORDERED_NODE_TYPE) { + ec = XPathException::TYPE_ERR; + return 0; + } + + const NodeSet& nodes = m_value.toNodeSet(); + if (resultType() == FIRST_ORDERED_NODE_TYPE) + return nodes.firstNode(); + else + return nodes.anyNode(); +} + +bool XPathResult::invalidIteratorState() const +{ + if (resultType() != UNORDERED_NODE_ITERATOR_TYPE && resultType() != ORDERED_NODE_ITERATOR_TYPE) + return false; + + ASSERT(m_document); + return m_document->domTreeVersion() != m_domTreeVersion; +} + +unsigned long XPathResult::snapshotLength(ExceptionCode& ec) const +{ + if (resultType() != UNORDERED_NODE_SNAPSHOT_TYPE && resultType() != ORDERED_NODE_SNAPSHOT_TYPE) { + ec = XPathException::TYPE_ERR; + return 0; + } + + return m_value.toNodeSet().size(); +} + +Node* XPathResult::iterateNext(ExceptionCode& ec) +{ + if (resultType() != UNORDERED_NODE_ITERATOR_TYPE && resultType() != ORDERED_NODE_ITERATOR_TYPE) { + ec = XPathException::TYPE_ERR; + return 0; + } + + if (invalidIteratorState()) { + ec = INVALID_STATE_ERR; + return 0; + } + + if (m_nodeSetPosition + 1 > m_nodeSet.size()) + return 0; + + Node* node = m_nodeSet[m_nodeSetPosition]; + + m_nodeSetPosition++; + + return node; +} + +Node* XPathResult::snapshotItem(unsigned long index, ExceptionCode& ec) +{ + if (resultType() != UNORDERED_NODE_SNAPSHOT_TYPE && resultType() != ORDERED_NODE_SNAPSHOT_TYPE) { + ec = XPathException::TYPE_ERR; + return 0; + } + + const NodeSet& nodes = m_value.toNodeSet(); + if (index >= nodes.size()) + return 0; + + return nodes[index]; +} + +} + +#endif // ENABLE(XPATH) diff --git a/Source/WebCore/xml/XPathResult.h b/Source/WebCore/xml/XPathResult.h new file mode 100644 index 0000000..cbb51f5 --- /dev/null +++ b/Source/WebCore/xml/XPathResult.h @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2005 Frerich Raabe <raabe@kde.org> + * Copyright (C) 2006, 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef XPathResult_h +#define XPathResult_h + +#if ENABLE(XPATH) + +#include "XPathValue.h" +#include <wtf/Forward.h> +#include <wtf/RefCounted.h> + +namespace WebCore { + + typedef int ExceptionCode; + + class Document; + class Node; + + class XPathResult : public RefCounted<XPathResult> { + public: + enum XPathResultType { + ANY_TYPE = 0, + NUMBER_TYPE = 1, + STRING_TYPE = 2, + BOOLEAN_TYPE = 3, + UNORDERED_NODE_ITERATOR_TYPE = 4, + ORDERED_NODE_ITERATOR_TYPE = 5, + UNORDERED_NODE_SNAPSHOT_TYPE = 6, + ORDERED_NODE_SNAPSHOT_TYPE = 7, + ANY_UNORDERED_NODE_TYPE = 8, + FIRST_ORDERED_NODE_TYPE = 9 + }; + + static PassRefPtr<XPathResult> create(Document* document, const XPath::Value& value) { return adoptRef(new XPathResult(document, value)); } + ~XPathResult(); + + void convertTo(unsigned short type, ExceptionCode&); + + unsigned short resultType() const; + + double numberValue(ExceptionCode&) const; + String stringValue(ExceptionCode&) const; + bool booleanValue(ExceptionCode&) const; + Node* singleNodeValue(ExceptionCode&) const; + + bool invalidIteratorState() const; + unsigned long snapshotLength(ExceptionCode&) const; + Node* iterateNext(ExceptionCode&); + Node* snapshotItem(unsigned long index, ExceptionCode&); + + private: + XPathResult(Document*, const XPath::Value&); + + XPath::Value m_value; + unsigned m_nodeSetPosition; + XPath::NodeSet m_nodeSet; // FIXME: why duplicate the node set stored in m_value? + unsigned short m_resultType; + RefPtr<Document> m_document; + unsigned m_domTreeVersion; + }; + +} // namespace WebCore + +#endif // ENABLE(XPATH) + +#endif // XPathResult_h diff --git a/Source/WebCore/xml/XPathResult.idl b/Source/WebCore/xml/XPathResult.idl new file mode 100644 index 0000000..ebbff42 --- /dev/null +++ b/Source/WebCore/xml/XPathResult.idl @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2006 Apple Computer, Inc. + * + * 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. + */ + +module xpath { + + interface [Conditional=XPATH] XPathResult { + const unsigned short ANY_TYPE = 0; + const unsigned short NUMBER_TYPE = 1; + const unsigned short STRING_TYPE = 2; + const unsigned short BOOLEAN_TYPE = 3; + const unsigned short UNORDERED_NODE_ITERATOR_TYPE = 4; + const unsigned short ORDERED_NODE_ITERATOR_TYPE = 5; + const unsigned short UNORDERED_NODE_SNAPSHOT_TYPE = 6; + const unsigned short ORDERED_NODE_SNAPSHOT_TYPE = 7; + const unsigned short ANY_UNORDERED_NODE_TYPE = 8; + const unsigned short FIRST_ORDERED_NODE_TYPE = 9; + + readonly attribute unsigned short resultType; + readonly attribute double numberValue + getter raises (DOMException); + + readonly attribute DOMString stringValue + getter raises (DOMException); + + readonly attribute boolean booleanValue + getter raises (DOMException); + + readonly attribute Node singleNodeValue + getter raises (DOMException); + + readonly attribute boolean invalidIteratorState; + readonly attribute unsigned long snapshotLength + getter raises (DOMException); + + Node iterateNext() + raises (DOMException); + Node snapshotItem(in unsigned long index) + raises (DOMException); + }; + +} diff --git a/Source/WebCore/xml/XPathStep.cpp b/Source/WebCore/xml/XPathStep.cpp new file mode 100644 index 0000000..d82cd50 --- /dev/null +++ b/Source/WebCore/xml/XPathStep.cpp @@ -0,0 +1,398 @@ +/* + * Copyright (C) 2005 Frerich Raabe <raabe@kde.org> + * Copyright (C) 2006, 2009 Apple Inc. All rights reserved. + * Copyright (C) 2007 Alexey Proskuryakov <ap@webkit.org> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "XPathStep.h" + +#if ENABLE(XPATH) + +#include "Attr.h" +#include "Document.h" +#include "Element.h" +#include "NamedNodeMap.h" +#include "XMLNSNames.h" +#include "XPathParser.h" +#include "XPathUtil.h" + +namespace WebCore { +namespace XPath { + +Step::Step(Axis axis, const NodeTest& nodeTest, const Vector<Predicate*>& predicates) + : m_axis(axis) + , m_nodeTest(nodeTest) + , m_predicates(predicates) +{ +} + +Step::~Step() +{ + deleteAllValues(m_predicates); + deleteAllValues(m_nodeTest.mergedPredicates()); +} + +void Step::optimize() +{ + // Evaluate predicates as part of node test if possible to avoid building unnecessary NodeSets. + // E.g., there is no need to build a set of all "foo" nodes to evaluate "foo[@bar]", we can check the predicate while enumerating. + // This optimization can be applied to predicates that are not context node list sensitive, or to first predicate that is only context position sensitive, e.g. foo[position() mod 2 = 0]. + Vector<Predicate*> remainingPredicates; + for (size_t i = 0; i < m_predicates.size(); ++i) { + Predicate* predicate = m_predicates[i]; + if ((!predicate->isContextPositionSensitive() || m_nodeTest.mergedPredicates().isEmpty()) && !predicate->isContextSizeSensitive() && remainingPredicates.isEmpty()) { + m_nodeTest.mergedPredicates().append(predicate); + } else + remainingPredicates.append(predicate); + } + swap(remainingPredicates, m_predicates); +} + +void optimizeStepPair(Step* first, Step* second, bool& dropSecondStep) +{ + dropSecondStep = false; + + if (first->m_axis == Step::DescendantOrSelfAxis + && first->m_nodeTest.kind() == Step::NodeTest::AnyNodeTest + && !first->m_predicates.size() + && !first->m_nodeTest.mergedPredicates().size()) { + + ASSERT(first->m_nodeTest.data().isEmpty()); + ASSERT(first->m_nodeTest.namespaceURI().isEmpty()); + + // Optimize the common case of "//" AKA /descendant-or-self::node()/child::NodeTest to /descendant::NodeTest. + if (second->m_axis == Step::ChildAxis && second->predicatesAreContextListInsensitive()) { + first->m_axis = Step::DescendantAxis; + first->m_nodeTest = Step::NodeTest(second->m_nodeTest.kind(), second->m_nodeTest.data(), second->m_nodeTest.namespaceURI()); + swap(second->m_nodeTest.mergedPredicates(), first->m_nodeTest.mergedPredicates()); + swap(second->m_predicates, first->m_predicates); + first->optimize(); + dropSecondStep = true; + } + } +} + +bool Step::predicatesAreContextListInsensitive() const +{ + for (size_t i = 0; i < m_predicates.size(); ++i) { + Predicate* predicate = m_predicates[i]; + if (predicate->isContextPositionSensitive() || predicate->isContextSizeSensitive()) + return false; + } + + for (size_t i = 0; i < m_nodeTest.mergedPredicates().size(); ++i) { + Predicate* predicate = m_nodeTest.mergedPredicates()[i]; + if (predicate->isContextPositionSensitive() || predicate->isContextSizeSensitive()) + return false; + } + + return true; +} + +void Step::evaluate(Node* context, NodeSet& nodes) const +{ + EvaluationContext& evaluationContext = Expression::evaluationContext(); + evaluationContext.position = 0; + + nodesInAxis(context, nodes); + + // Check predicates that couldn't be merged into node test. + for (unsigned i = 0; i < m_predicates.size(); i++) { + Predicate* predicate = m_predicates[i]; + + NodeSet newNodes; + if (!nodes.isSorted()) + newNodes.markSorted(false); + + for (unsigned j = 0; j < nodes.size(); j++) { + Node* node = nodes[j]; + + evaluationContext.node = node; + evaluationContext.size = nodes.size(); + evaluationContext.position = j + 1; + if (predicate->evaluate()) + newNodes.append(node); + } + + nodes.swap(newNodes); + } +} + +static inline Node::NodeType primaryNodeType(Step::Axis axis) +{ + switch (axis) { + case Step::AttributeAxis: + return Node::ATTRIBUTE_NODE; + case Step::NamespaceAxis: + return Node::XPATH_NAMESPACE_NODE; + default: + return Node::ELEMENT_NODE; + } +} + +// Evaluate NodeTest without considering merged predicates. +static inline bool nodeMatchesBasicTest(Node* node, Step::Axis axis, const Step::NodeTest& nodeTest) +{ + switch (nodeTest.kind()) { + case Step::NodeTest::TextNodeTest: + return node->nodeType() == Node::TEXT_NODE || node->nodeType() == Node::CDATA_SECTION_NODE; + case Step::NodeTest::CommentNodeTest: + return node->nodeType() == Node::COMMENT_NODE; + case Step::NodeTest::ProcessingInstructionNodeTest: { + const AtomicString& name = nodeTest.data(); + return node->nodeType() == Node::PROCESSING_INSTRUCTION_NODE && (name.isEmpty() || node->nodeName() == name); + } + case Step::NodeTest::AnyNodeTest: + return true; + case Step::NodeTest::NameTest: { + const AtomicString& name = nodeTest.data(); + const AtomicString& namespaceURI = nodeTest.namespaceURI(); + + if (axis == Step::AttributeAxis) { + ASSERT(node->isAttributeNode()); + + // In XPath land, namespace nodes are not accessible on the attribute axis. + if (node->namespaceURI() == XMLNSNames::xmlnsNamespaceURI) + return false; + + if (name == starAtom) + return namespaceURI.isEmpty() || node->namespaceURI() == namespaceURI; + + return node->localName() == name && node->namespaceURI() == namespaceURI; + } + + // Node test on the namespace axis is not implemented yet, the caller has a check for it. + ASSERT(axis != Step::NamespaceAxis); + + // For other axes, the principal node type is element. + ASSERT(primaryNodeType(axis) == Node::ELEMENT_NODE); + if (node->nodeType() != Node::ELEMENT_NODE) + return false; + + if (name == starAtom) + return namespaceURI.isEmpty() || namespaceURI == node->namespaceURI(); + + if (node->document()->isHTMLDocument()) { + if (node->isHTMLElement()) { + // Paths without namespaces should match HTML elements in HTML documents despite those having an XHTML namespace. Names are compared case-insensitively. + return equalIgnoringCase(static_cast<Element*>(node)->localName(), name) && (namespaceURI.isNull() || namespaceURI == node->namespaceURI()); + } + // An expression without any prefix shouldn't match no-namespace nodes (because HTML5 says so). + return static_cast<Element*>(node)->hasLocalName(name) && namespaceURI == node->namespaceURI() && !namespaceURI.isNull(); + } + return static_cast<Element*>(node)->hasLocalName(name) && namespaceURI == node->namespaceURI(); + } + } + ASSERT_NOT_REACHED(); + return false; +} + +static inline bool nodeMatches(Node* node, Step::Axis axis, const Step::NodeTest& nodeTest) +{ + if (!nodeMatchesBasicTest(node, axis, nodeTest)) + return false; + + EvaluationContext& evaluationContext = Expression::evaluationContext(); + + // Only the first merged predicate may depend on position. + ++evaluationContext.position; + + const Vector<Predicate*>& mergedPredicates = nodeTest.mergedPredicates(); + for (unsigned i = 0; i < mergedPredicates.size(); i++) { + Predicate* predicate = mergedPredicates[i]; + + evaluationContext.node = node; + // No need to set context size - we only get here when evaluating predicates that do not depend on it. + if (!predicate->evaluate()) + return false; + } + + return true; +} + +// Result nodes are ordered in axis order. Node test (including merged predicates) is applied. +void Step::nodesInAxis(Node* context, NodeSet& nodes) const +{ + ASSERT(nodes.isEmpty()); + switch (m_axis) { + case ChildAxis: + if (context->isAttributeNode()) // In XPath model, attribute nodes do not have children. + return; + + for (Node* n = context->firstChild(); n; n = n->nextSibling()) + if (nodeMatches(n, ChildAxis, m_nodeTest)) + nodes.append(n); + return; + case DescendantAxis: + if (context->isAttributeNode()) // In XPath model, attribute nodes do not have children. + return; + + for (Node* n = context->firstChild(); n; n = n->traverseNextNode(context)) + if (nodeMatches(n, DescendantAxis, m_nodeTest)) + nodes.append(n); + return; + case ParentAxis: + if (context->isAttributeNode()) { + Element* n = static_cast<Attr*>(context)->ownerElement(); + if (nodeMatches(n, ParentAxis, m_nodeTest)) + nodes.append(n); + } else { + ContainerNode* n = context->parentNode(); + if (n && nodeMatches(n, ParentAxis, m_nodeTest)) + nodes.append(n); + } + return; + case AncestorAxis: { + Node* n = context; + if (context->isAttributeNode()) { + n = static_cast<Attr*>(context)->ownerElement(); + if (nodeMatches(n, AncestorAxis, m_nodeTest)) + nodes.append(n); + } + for (n = n->parentNode(); n; n = n->parentNode()) + if (nodeMatches(n, AncestorAxis, m_nodeTest)) + nodes.append(n); + nodes.markSorted(false); + return; + } + case FollowingSiblingAxis: + if (context->nodeType() == Node::ATTRIBUTE_NODE || + context->nodeType() == Node::XPATH_NAMESPACE_NODE) + return; + + for (Node* n = context->nextSibling(); n; n = n->nextSibling()) + if (nodeMatches(n, FollowingSiblingAxis, m_nodeTest)) + nodes.append(n); + return; + case PrecedingSiblingAxis: + if (context->nodeType() == Node::ATTRIBUTE_NODE || + context->nodeType() == Node::XPATH_NAMESPACE_NODE) + return; + + for (Node* n = context->previousSibling(); n; n = n->previousSibling()) + if (nodeMatches(n, PrecedingSiblingAxis, m_nodeTest)) + nodes.append(n); + + nodes.markSorted(false); + return; + case FollowingAxis: + if (context->isAttributeNode()) { + Node* p = static_cast<Attr*>(context)->ownerElement(); + while ((p = p->traverseNextNode())) + if (nodeMatches(p, FollowingAxis, m_nodeTest)) + nodes.append(p); + } else { + for (Node* p = context; !isRootDomNode(p); p = p->parentNode()) { + for (Node* n = p->nextSibling(); n; n = n->nextSibling()) { + if (nodeMatches(n, FollowingAxis, m_nodeTest)) + nodes.append(n); + for (Node* c = n->firstChild(); c; c = c->traverseNextNode(n)) + if (nodeMatches(c, FollowingAxis, m_nodeTest)) + nodes.append(c); + } + } + } + return; + case PrecedingAxis: { + if (context->isAttributeNode()) + context = static_cast<Attr*>(context)->ownerElement(); + + Node* n = context; + while (ContainerNode* parent = n->parentNode()) { + for (n = n->traversePreviousNode(); n != parent; n = n->traversePreviousNode()) + if (nodeMatches(n, PrecedingAxis, m_nodeTest)) + nodes.append(n); + n = parent; + } + nodes.markSorted(false); + return; + } + case AttributeAxis: { + if (context->nodeType() != Node::ELEMENT_NODE) + return; + + // Avoid lazily creating attribute nodes for attributes that we do not need anyway. + if (m_nodeTest.kind() == NodeTest::NameTest && m_nodeTest.data() != starAtom) { + RefPtr<Node> n = static_cast<Element*>(context)->getAttributeNodeNS(m_nodeTest.namespaceURI(), m_nodeTest.data()); + if (n && n->namespaceURI() != XMLNSNames::xmlnsNamespaceURI) { // In XPath land, namespace nodes are not accessible on the attribute axis. + if (nodeMatches(n.get(), AttributeAxis, m_nodeTest)) // Still need to check merged predicates. + nodes.append(n.release()); + } + return; + } + + NamedNodeMap* attrs = context->attributes(); + if (!attrs) + return; + + for (unsigned i = 0; i < attrs->length(); ++i) { + RefPtr<Attr> attr = attrs->attributeItem(i)->createAttrIfNeeded(static_cast<Element*>(context)); + if (nodeMatches(attr.get(), AttributeAxis, m_nodeTest)) + nodes.append(attr.release()); + } + return; + } + case NamespaceAxis: + // XPath namespace nodes are not implemented yet. + return; + case SelfAxis: + if (nodeMatches(context, SelfAxis, m_nodeTest)) + nodes.append(context); + return; + case DescendantOrSelfAxis: + if (nodeMatches(context, DescendantOrSelfAxis, m_nodeTest)) + nodes.append(context); + if (context->isAttributeNode()) // In XPath model, attribute nodes do not have children. + return; + + for (Node* n = context->firstChild(); n; n = n->traverseNextNode(context)) + if (nodeMatches(n, DescendantOrSelfAxis, m_nodeTest)) + nodes.append(n); + return; + case AncestorOrSelfAxis: { + if (nodeMatches(context, AncestorOrSelfAxis, m_nodeTest)) + nodes.append(context); + Node* n = context; + if (context->isAttributeNode()) { + n = static_cast<Attr*>(context)->ownerElement(); + if (nodeMatches(n, AncestorOrSelfAxis, m_nodeTest)) + nodes.append(n); + } + for (n = n->parentNode(); n; n = n->parentNode()) + if (nodeMatches(n, AncestorOrSelfAxis, m_nodeTest)) + nodes.append(n); + + nodes.markSorted(false); + return; + } + } + ASSERT_NOT_REACHED(); +} + + +} +} + +#endif // ENABLE(XPATH) diff --git a/Source/WebCore/xml/XPathStep.h b/Source/WebCore/xml/XPathStep.h new file mode 100644 index 0000000..ec022b3 --- /dev/null +++ b/Source/WebCore/xml/XPathStep.h @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2005 Frerich Raabe <raabe@kde.org> + * Copyright (C) 2006, 2009 Apple Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef XPathStep_h +#define XPathStep_h + +#if ENABLE(XPATH) + +#include "Node.h" +#include "XPathExpressionNode.h" +#include "XPathNodeSet.h" + +namespace WebCore { + + namespace XPath { + + class Predicate; + + class Step : public ParseNode, public Noncopyable { + public: + enum Axis { + AncestorAxis, AncestorOrSelfAxis, AttributeAxis, + ChildAxis, DescendantAxis, DescendantOrSelfAxis, + FollowingAxis, FollowingSiblingAxis, NamespaceAxis, + ParentAxis, PrecedingAxis, PrecedingSiblingAxis, + SelfAxis + }; + + class NodeTest : public FastAllocBase { + public: + enum Kind { + TextNodeTest, CommentNodeTest, ProcessingInstructionNodeTest, AnyNodeTest, NameTest + }; + + NodeTest(Kind kind) : m_kind(kind) {} + NodeTest(Kind kind, const String& data) : m_kind(kind), m_data(data) {} + NodeTest(Kind kind, const String& data, const String& namespaceURI) : m_kind(kind), m_data(data), m_namespaceURI(namespaceURI) {} + + Kind kind() const { return m_kind; } + const AtomicString& data() const { return m_data; } + const AtomicString& namespaceURI() const { return m_namespaceURI; } + Vector<Predicate*>& mergedPredicates() { return m_mergedPredicates; } + const Vector<Predicate*>& mergedPredicates() const { return m_mergedPredicates; } + + private: + Kind m_kind; + AtomicString m_data; + AtomicString m_namespaceURI; + + // When possible, we merge some or all predicates with node test for better performance. + Vector<Predicate*> m_mergedPredicates; + }; + + Step(Axis, const NodeTest& nodeTest, const Vector<Predicate*>& predicates = Vector<Predicate*>()); + ~Step(); + + void optimize(); + + void evaluate(Node* context, NodeSet&) const; + + Axis axis() const { return m_axis; } + const NodeTest& nodeTest() const { return m_nodeTest; } + + private: + friend void optimizeStepPair(Step*, Step*, bool&); + bool predicatesAreContextListInsensitive() const; + + void parseNodeTest(const String&); + void nodesInAxis(Node* context, NodeSet&) const; + String namespaceFromNodetest(const String& nodeTest) const; + + Axis m_axis; + NodeTest m_nodeTest; + Vector<Predicate*> m_predicates; + }; + + void optimizeStepPair(Step*, Step*, bool& dropSecondStep); + } + +} + +#endif // ENABLE(XPATH) + +#endif // XPathStep_h diff --git a/Source/WebCore/xml/XPathUtil.cpp b/Source/WebCore/xml/XPathUtil.cpp new file mode 100644 index 0000000..0100bea --- /dev/null +++ b/Source/WebCore/xml/XPathUtil.cpp @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2005 Frerich Raabe <raabe@kde.org> + * Copyright (C) 2006, 2009 Apple Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "XPathUtil.h" + +#if ENABLE(XPATH) + +#include "ContainerNode.h" + +namespace WebCore { +namespace XPath { + +bool isRootDomNode(Node* node) +{ + return node && !node->parentNode(); +} + +String stringValue(Node* node) +{ + switch (node->nodeType()) { + case Node::ATTRIBUTE_NODE: + case Node::PROCESSING_INSTRUCTION_NODE: + case Node::COMMENT_NODE: + case Node::TEXT_NODE: + case Node::CDATA_SECTION_NODE: + case Node::XPATH_NAMESPACE_NODE: + return node->nodeValue(); + default: + if (isRootDomNode(node) || node->nodeType() == Node::ELEMENT_NODE) { + Vector<UChar> result; + result.reserveCapacity(1024); + + for (Node* n = node->firstChild(); n; n = n->traverseNextNode(node)) { + if (n->isTextNode()) { + const String& nodeValue = n->nodeValue(); + result.append(nodeValue.characters(), nodeValue.length()); + } + } + + return String::adopt(result); + } + } + + return String(); +} + +bool isValidContextNode(Node* node) +{ + if (!node) + return false; + switch (node->nodeType()) { + case Node::ATTRIBUTE_NODE: + case Node::CDATA_SECTION_NODE: + case Node::COMMENT_NODE: + case Node::DOCUMENT_NODE: + case Node::ELEMENT_NODE: + case Node::PROCESSING_INSTRUCTION_NODE: + case Node::XPATH_NAMESPACE_NODE: + return true; + case Node::DOCUMENT_FRAGMENT_NODE: + case Node::DOCUMENT_TYPE_NODE: + case Node::ENTITY_NODE: + case Node::ENTITY_REFERENCE_NODE: + case Node::NOTATION_NODE: + return false; + case Node::TEXT_NODE: + return !(node->parentNode() && node->parentNode()->isAttributeNode()); + } + ASSERT_NOT_REACHED(); + return false; +} + +} +} + +#endif // ENABLE(XPATH) diff --git a/Source/WebCore/xml/XPathUtil.h b/Source/WebCore/xml/XPathUtil.h new file mode 100644 index 0000000..cfb34dc --- /dev/null +++ b/Source/WebCore/xml/XPathUtil.h @@ -0,0 +1,56 @@ +/* + * Copyright 2005 Frerich Raabe <raabe@kde.org> + * Copyright (C) 2006 Apple Computer, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef XPathUtil_h +#define XPathUtil_h + +#if ENABLE(XPATH) + +#include <wtf/Forward.h> +#include <wtf/Vector.h> + +namespace WebCore { + + class Node; + + namespace XPath { + + /* @return whether the given node is the root node */ + bool isRootDomNode(Node*); + + /* @return the 'string-value' of the given node as specified by http://www.w3.org/TR/xpath */ + String stringValue(Node*); + + /* @return whether the given node is a valid context node */ + bool isValidContextNode(Node*); + + } + +} + +#endif // ENABLE(XPATH) + +#endif // XPath_Util_H diff --git a/Source/WebCore/xml/XPathValue.cpp b/Source/WebCore/xml/XPathValue.cpp new file mode 100644 index 0000000..29e211e --- /dev/null +++ b/Source/WebCore/xml/XPathValue.cpp @@ -0,0 +1,145 @@ +/* + * Copyright 2005 Frerich Raabe <raabe@kde.org> + * Copyright (C) 2006 Apple Computer, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "XPathValue.h" + +#if ENABLE(XPATH) + +#include "Node.h" +#include "XPathExpressionNode.h" +#include "XPathUtil.h" +#include <limits> +#include <wtf/MathExtras.h> +#include <wtf/StdLibExtras.h> + +using std::numeric_limits; + +namespace WebCore { +namespace XPath { + +const Value::AdoptTag Value::adopt = {}; + +const NodeSet& Value::toNodeSet() const +{ + if (!isNodeSet()) + Expression::evaluationContext().hadTypeConversionError = true; + + if (!m_data) { + DEFINE_STATIC_LOCAL(NodeSet, emptyNodeSet, ()); + return emptyNodeSet; + } + + return m_data->m_nodeSet; +} + +NodeSet& Value::modifiableNodeSet() +{ + if (!isNodeSet()) + Expression::evaluationContext().hadTypeConversionError = true; + + if (!m_data) + m_data = ValueData::create(); + + m_type = NodeSetValue; + return m_data->m_nodeSet; +} + +bool Value::toBoolean() const +{ + switch (m_type) { + case NodeSetValue: + return !m_data->m_nodeSet.isEmpty(); + case BooleanValue: + return m_bool; + case NumberValue: + return m_number != 0 && !isnan(m_number); + case StringValue: + return !m_data->m_string.isEmpty(); + } + ASSERT_NOT_REACHED(); + return false; +} + +double Value::toNumber() const +{ + switch (m_type) { + case NodeSetValue: + return Value(toString()).toNumber(); + case NumberValue: + return m_number; + case StringValue: { + const String& str = m_data->m_string.simplifyWhiteSpace(); + + // String::toDouble() supports exponential notation, which is not allowed in XPath. + unsigned len = str.length(); + for (unsigned i = 0; i < len; ++i) { + UChar c = str[i]; + if (!isASCIIDigit(c) && c != '.' && c != '-') + return numeric_limits<double>::quiet_NaN(); + } + + bool canConvert; + double value = str.toDouble(&canConvert); + if (canConvert) + return value; + return numeric_limits<double>::quiet_NaN(); + } + case BooleanValue: + return m_bool; + } + ASSERT_NOT_REACHED(); + return 0.0; +} + +String Value::toString() const +{ + switch (m_type) { + case NodeSetValue: + if (m_data->m_nodeSet.isEmpty()) + return ""; + return stringValue(m_data->m_nodeSet.firstNode()); + case StringValue: + return m_data->m_string; + case NumberValue: + if (isnan(m_number)) + return "NaN"; + if (m_number == 0) + return "0"; + if (isinf(m_number)) + return signbit(m_number) ? "-Infinity" : "Infinity"; + return String::number(m_number); + case BooleanValue: + return m_bool ? "true" : "false"; + } + ASSERT_NOT_REACHED(); + return String(); +} + +} +} + +#endif // ENABLE(XPATH) diff --git a/Source/WebCore/xml/XPathValue.h b/Source/WebCore/xml/XPathValue.h new file mode 100644 index 0000000..a0cd24d --- /dev/null +++ b/Source/WebCore/xml/XPathValue.h @@ -0,0 +1,106 @@ +/* + * Copyright 2005 Frerich Raabe <raabe@kde.org> + * Copyright (C) 2006 Apple Computer, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef XPathValue_h +#define XPathValue_h + +#if ENABLE(XPATH) + +#include "PlatformString.h" +#include "XPathNodeSet.h" + +namespace WebCore { + + namespace XPath { + + class ValueData : public RefCounted<ValueData> { + public: + static PassRefPtr<ValueData> create() { return adoptRef(new ValueData); } + static PassRefPtr<ValueData> create(const NodeSet& nodeSet) { return adoptRef(new ValueData(nodeSet)); } + static PassRefPtr<ValueData> create(const String& string) { return adoptRef(new ValueData(string)); } + + NodeSet m_nodeSet; + String m_string; + + private: + ValueData() { } + ValueData(const NodeSet& nodeSet) : m_nodeSet(nodeSet) { } + ValueData(const String& string) : m_string(string) { } + }; + + // Copying Value objects makes their data partially shared, so care has to be taken when dealing with copies. + class Value { + public: + enum Type { NodeSetValue, BooleanValue, NumberValue, StringValue }; + + Value(unsigned value) : m_type(NumberValue), m_bool(false), m_number(value) {} + Value(unsigned long value) : m_type(NumberValue), m_bool(false), m_number(value) {} + Value(double value) : m_type(NumberValue), m_bool(false), m_number(value) {} + + Value(const char* value) : m_type(StringValue), m_bool(false), m_number(0), m_data(ValueData::create(value)) {} + Value(const String& value) : m_type(StringValue), m_bool(false), m_number(0), m_data(ValueData::create(value)) {} + Value(const NodeSet& value) : m_type(NodeSetValue), m_bool(false), m_number(0), m_data(ValueData::create(value)) {} + Value(Node* value) : m_type(NodeSetValue), m_bool(false), m_number(0), m_data(ValueData::create()) { m_data->m_nodeSet.append(value); } + + // This is needed to safely implement constructing from bool - with normal function overloading, any pointer type would match. + template<typename T> Value(T); + + static const struct AdoptTag {} adopt; + Value(NodeSet& value, const AdoptTag&) : m_type(NodeSetValue), m_bool(false), m_number(0), m_data(ValueData::create()) { value.swap(m_data->m_nodeSet); } + + Type type() const { return m_type; } + + bool isNodeSet() const { return m_type == NodeSetValue; } + bool isBoolean() const { return m_type == BooleanValue; } + bool isNumber() const { return m_type == NumberValue; } + bool isString() const { return m_type == StringValue; } + + const NodeSet& toNodeSet() const; + NodeSet& modifiableNodeSet(); + bool toBoolean() const; + double toNumber() const; + String toString() const; + + private: + Type m_type; + bool m_bool; + double m_number; + RefPtr<ValueData> m_data; + }; + + template<> + inline Value::Value(bool value) + : m_type(BooleanValue) + , m_bool(value) + , m_number(0) + { + } + } +} + +#endif // ENABLE(XPATH) + +#endif // XPath_Value_H diff --git a/Source/WebCore/xml/XPathVariableReference.cpp b/Source/WebCore/xml/XPathVariableReference.cpp new file mode 100644 index 0000000..efe9cf9 --- /dev/null +++ b/Source/WebCore/xml/XPathVariableReference.cpp @@ -0,0 +1,55 @@ +/* + * Copyright 2005 Frerich Raabe <raabe@kde.org> + * Copyright (C) 2006 Apple Computer, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "XPathVariableReference.h" + +#if ENABLE(XPATH) + +#include "Node.h" +#include "XPathValue.h" + +namespace WebCore { +namespace XPath { + +VariableReference::VariableReference(const String& name) + : m_name(name) +{ +} + +Value VariableReference::evaluate() const +{ + HashMap<String, String>& bindings = evaluationContext().variableBindings; + if (!bindings.contains(m_name)) + // FIXME: Is this the right thing to do if an unknown variable is referenced? + return ""; + return bindings.get(m_name); +} + +} +} + +#endif // ENABLE(XPATH) diff --git a/Source/WebCore/xml/XPathVariableReference.h b/Source/WebCore/xml/XPathVariableReference.h new file mode 100644 index 0000000..5e5a59a --- /dev/null +++ b/Source/WebCore/xml/XPathVariableReference.h @@ -0,0 +1,52 @@ +/* + * Copyright 2005 Frerich Raabe <raabe@kde.org> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef XPathVariableReference_h +#define XPathVariableReference_h + +#if ENABLE(XPATH) + +#include "XPathExpressionNode.h" + +namespace WebCore { + + namespace XPath { + + // Variable references are not used with XPathEvaluator. + class VariableReference : public Expression { + public: + VariableReference(const String& name); + private: + virtual Value evaluate() const; + virtual Value::Type resultType() const { ASSERT_NOT_REACHED(); return Value::NumberValue; } + String m_name; + }; + + } +} + +#endif // ENABLE(XPATH) + +#endif // XPath_VariableReference_H diff --git a/Source/WebCore/xml/XSLImportRule.cpp b/Source/WebCore/xml/XSLImportRule.cpp new file mode 100644 index 0000000..c32da4e --- /dev/null +++ b/Source/WebCore/xml/XSLImportRule.cpp @@ -0,0 +1,117 @@ +/* + * This file is part of the XSL implementation. + * + * Copyright (C) 2004, 2005, 2006, 2008 Apple Inc. All rights reserved. + * + * 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 "XSLImportRule.h" + +#if ENABLE(XSLT) + +#include "CachedXSLStyleSheet.h" +#include "CachedResourceLoader.h" +#include "XSLStyleSheet.h" + +namespace WebCore { + +XSLImportRule::XSLImportRule(XSLStyleSheet* parent, const String& href) + : StyleBase(parent) + , m_strHref(href) + , m_cachedSheet(0) + , m_loading(false) +{ +} + +XSLImportRule::~XSLImportRule() +{ + if (m_styleSheet) + m_styleSheet->setParent(0); + + if (m_cachedSheet) + m_cachedSheet->removeClient(this); +} + +XSLStyleSheet* XSLImportRule::parentStyleSheet() const +{ + return (parent() && parent()->isXSLStyleSheet()) ? static_cast<XSLStyleSheet*>(parent()) : 0; +} + +void XSLImportRule::setXSLStyleSheet(const String& href, const KURL& baseURL, const String& sheet) +{ + if (m_styleSheet) + m_styleSheet->setParent(0); + + m_styleSheet = XSLStyleSheet::create(this, href, baseURL); + + XSLStyleSheet* parent = parentStyleSheet(); + if (parent) + m_styleSheet->setParentStyleSheet(parent); + + m_styleSheet->parseString(sheet); + m_loading = false; + + if (parent) + parent->checkLoaded(); +} + +bool XSLImportRule::isLoading() +{ + return (m_loading || (m_styleSheet && m_styleSheet->isLoading())); +} + +void XSLImportRule::loadSheet() +{ + CachedResourceLoader* cachedResourceLoader = 0; + StyleBase* root = this; + StyleBase* parent; + while ((parent = root->parent())) + root = parent; + if (root->isXSLStyleSheet()) + cachedResourceLoader = static_cast<XSLStyleSheet*>(root)->cachedResourceLoader(); + + String absHref = m_strHref; + XSLStyleSheet* parentSheet = parentStyleSheet(); + if (!parentSheet->finalURL().isNull()) + // use parent styleheet's URL as the base URL + absHref = KURL(parentSheet->finalURL(), m_strHref).string(); + + // Check for a cycle in our import chain. If we encounter a stylesheet + // in our parent chain with the same URL, then just bail. + for (parent = this->parent(); parent; parent = parent->parent()) { + if (parent->isXSLStyleSheet() && absHref == static_cast<XSLStyleSheet*>(parent)->finalURL().string()) + return; + } + + m_cachedSheet = cachedResourceLoader->requestXSLStyleSheet(absHref); + + if (m_cachedSheet) { + m_cachedSheet->addClient(this); + + // If the imported sheet is in the cache, then setXSLStyleSheet gets called, + // and the sheet even gets parsed (via parseString). In this case we have + // loaded (even if our subresources haven't), so if we have a stylesheet after + // checking the cache, then we've clearly loaded. + if (!m_styleSheet) + m_loading = true; + } +} + +} // namespace WebCore + +#endif // ENABLE(XSLT) diff --git a/Source/WebCore/xml/XSLImportRule.h b/Source/WebCore/xml/XSLImportRule.h new file mode 100644 index 0000000..63c271e --- /dev/null +++ b/Source/WebCore/xml/XSLImportRule.h @@ -0,0 +1,88 @@ +/* + * This file is part of the XSL implementation. + * + * Copyright (C) 2004, 2006, 2008 Apple Inc. All rights reserved. + * + * 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. + * + */ + +#ifndef XSLImportRule_h +#define XSLImportRule_h + +#if ENABLE(XSLT) + +#include "CachedResourceClient.h" +#include "CachedResourceHandle.h" +#include "StyleBase.h" +#include "XSLStyleSheet.h" + +namespace WebCore { + +class CachedXSLStyleSheet; + +class XSLImportRule : public StyleBase, private CachedResourceClient { +public: + static PassRefPtr<XSLImportRule> create(XSLStyleSheet* parentSheet, const String& href) + { + return adoptRef(new XSLImportRule(parentSheet, href)); + } + + virtual ~XSLImportRule(); + + const String& href() const { return m_strHref; } + XSLStyleSheet* styleSheet() const { return m_styleSheet.get(); } + + XSLStyleSheet* parentStyleSheet() const; + + bool isLoading(); + void loadSheet(); + +#ifdef ANDROID_INSTRUMENT + void* operator new(size_t size) { + return StyleBase::operator new(size); + } + void* operator new[](size_t size) { + return StyleBase::operator new[](size); + } + + void operator delete(void* p, size_t size) { + StyleBase::operator delete(p, size); + } + void operator delete[](void* p, size_t size) { + StyleBase::operator delete[](p, size); + } +#endif + +private: + XSLImportRule(XSLStyleSheet* parentSheet, const String& href); + + virtual bool isImportRule() { return true; } + + // from CachedResourceClient + virtual void setXSLStyleSheet(const String& href, const KURL& baseURL, const String& sheet); + + String m_strHref; + RefPtr<XSLStyleSheet> m_styleSheet; + CachedResourceHandle<CachedXSLStyleSheet> m_cachedSheet; + bool m_loading; +}; + +} // namespace WebCore + +#endif // ENABLE(XSLT) + +#endif // XSLImportRule_h diff --git a/Source/WebCore/xml/XSLStyleSheet.h b/Source/WebCore/xml/XSLStyleSheet.h new file mode 100644 index 0000000..4312771 --- /dev/null +++ b/Source/WebCore/xml/XSLStyleSheet.h @@ -0,0 +1,122 @@ +/* + * This file is part of the XSL implementation. + * + * Copyright (C) 2004, 2006, 2008 Apple Inc. All rights reserved. + * + * 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. + * + */ + +#ifndef XSLStyleSheet_h +#define XSLStyleSheet_h + +#if ENABLE(XSLT) + +#include "ProcessingInstruction.h" +#include "StyleSheet.h" + +#if !USE(QXMLQUERY) +#include <libxml/parser.h> +#include <libxslt/transform.h> +#endif + +#include <wtf/PassRefPtr.h> + +namespace WebCore { + +class XSLImportRule; + +class XSLStyleSheet : public StyleSheet { +public: +#if !USE(QXMLQUERY) + static PassRefPtr<XSLStyleSheet> create(XSLImportRule* parentImport, const String& originalURL, const KURL& finalURL) + { + return adoptRef(new XSLStyleSheet(parentImport, originalURL, finalURL)); + } +#endif + static PassRefPtr<XSLStyleSheet> create(ProcessingInstruction* parentNode, const String& originalURL, const KURL& finalURL) + { + return adoptRef(new XSLStyleSheet(parentNode, originalURL, finalURL, false)); + } + static PassRefPtr<XSLStyleSheet> createEmbedded(ProcessingInstruction* parentNode, const KURL& finalURL) + { + return adoptRef(new XSLStyleSheet(parentNode, finalURL.string(), finalURL, true)); + } + + // Taking an arbitrary node is unsafe, because owner node pointer can become stale. + // XSLTProcessor ensures that the stylesheet doesn't outlive its parent, in part by not exposing it to JavaScript. + static PassRefPtr<XSLStyleSheet> createForXSLTProcessor(Node* parentNode, const String& originalURL, const KURL& finalURL) + { + return adoptRef(new XSLStyleSheet(parentNode, originalURL, finalURL, false)); + } + + virtual ~XSLStyleSheet(); + + virtual bool isXSLStyleSheet() const { return true; } + + virtual String type() const { return "text/xml"; } + + virtual bool parseString(const String &string, bool strict = true); + + virtual bool isLoading(); + virtual void checkLoaded(); + + void loadChildSheets(); + void loadChildSheet(const String& href); + + CachedResourceLoader* cachedResourceLoader(); + + Document* ownerDocument() { return m_ownerDocument; } + void setParentStyleSheet(XSLStyleSheet* parent); + +#if USE(QXMLQUERY) + String sheetString() const { return m_sheetString; } +#else + xmlDocPtr document(); + xsltStylesheetPtr compileStyleSheet(); + xmlDocPtr locateStylesheetSubResource(xmlDocPtr parentDoc, const xmlChar* uri); +#endif + + void clearDocuments(); + + void markAsProcessed(); + bool processed() const { return m_processed; } + +private: + XSLStyleSheet(Node* parentNode, const String& originalURL, const KURL& finalURL, bool embedded); +#if !USE(QXMLQUERY) + XSLStyleSheet(XSLImportRule* parentImport, const String& originalURL, const KURL& finalURL); +#endif + + Document* m_ownerDocument; + bool m_embedded; + bool m_processed; + +#if USE(QXMLQUERY) + String m_sheetString; +#else + xmlDocPtr m_stylesheetDoc; + bool m_stylesheetDocTaken; +#endif + + XSLStyleSheet* m_parentStyleSheet; +}; + +} // namespace WebCore + +#endif // ENABLE(XSLT) + +#endif // XSLStyleSheet_h diff --git a/Source/WebCore/xml/XSLStyleSheetLibxslt.cpp b/Source/WebCore/xml/XSLStyleSheetLibxslt.cpp new file mode 100644 index 0000000..eb735f4 --- /dev/null +++ b/Source/WebCore/xml/XSLStyleSheetLibxslt.cpp @@ -0,0 +1,314 @@ +/* + * This file is part of the XSL implementation. + * + * Copyright (C) 2004, 2005, 2006, 2008 Apple Inc. All rights reserved. + * + * 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 "XSLStyleSheet.h" + +#if ENABLE(XSLT) + +#include "Console.h" +#include "DOMWindow.h" +#include "CachedResourceLoader.h" +#include "Document.h" +#include "Frame.h" +#include "Node.h" +#include "TransformSource.h" +#include "XMLDocumentParser.h" +#include "XMLDocumentParserScope.h" +#include "XSLImportRule.h" +#include "XSLTProcessor.h" +#include <wtf/text/CString.h> + +#include <libxml/uri.h> +#include <libxslt/xsltutils.h> + +#if PLATFORM(MAC) +#include "SoftLinking.h" +#endif + +#if PLATFORM(MAC) +SOFT_LINK_LIBRARY(libxslt) +SOFT_LINK(libxslt, xsltIsBlank, int, (xmlChar *str), (str)) +SOFT_LINK(libxslt, xsltGetNsProp, xmlChar *, (xmlNodePtr node, const xmlChar *name, const xmlChar *nameSpace), (node, name, nameSpace)) +SOFT_LINK(libxslt, xsltParseStylesheetDoc, xsltStylesheetPtr, (xmlDocPtr doc), (doc)) +SOFT_LINK(libxslt, xsltLoadStylesheetPI, xsltStylesheetPtr, (xmlDocPtr doc), (doc)) +#endif + +namespace WebCore { + +XSLStyleSheet::XSLStyleSheet(XSLImportRule* parentRule, const String& originalURL, const KURL& finalURL) + : StyleSheet(parentRule, originalURL, finalURL) + , m_ownerDocument(0) + , m_embedded(false) + , m_processed(false) // Child sheets get marked as processed when the libxslt engine has finally seen them. + , m_stylesheetDoc(0) + , m_stylesheetDocTaken(false) + , m_parentStyleSheet(0) +{ +} + +XSLStyleSheet::XSLStyleSheet(Node* parentNode, const String& originalURL, const KURL& finalURL, bool embedded) + : StyleSheet(parentNode, originalURL, finalURL) + , m_ownerDocument(parentNode->document()) + , m_embedded(embedded) + , m_processed(true) // The root sheet starts off processed. + , m_stylesheetDoc(0) + , m_stylesheetDocTaken(false) + , m_parentStyleSheet(0) +{ +} + +XSLStyleSheet::~XSLStyleSheet() +{ + if (!m_stylesheetDocTaken) + xmlFreeDoc(m_stylesheetDoc); +} + +bool XSLStyleSheet::isLoading() +{ + unsigned len = length(); + for (unsigned i = 0; i < len; ++i) { + StyleBase* rule = item(i); + if (rule->isImportRule()) { + XSLImportRule* import = static_cast<XSLImportRule*>(rule); + if (import->isLoading()) + return true; + } + } + return false; +} + +void XSLStyleSheet::checkLoaded() +{ + if (isLoading()) + return; + if (parent()) + parent()->checkLoaded(); + if (ownerNode()) + ownerNode()->sheetLoaded(); +} + +xmlDocPtr XSLStyleSheet::document() +{ + if (m_embedded && ownerDocument() && ownerDocument()->transformSource()) + return (xmlDocPtr)ownerDocument()->transformSource()->platformSource(); + return m_stylesheetDoc; +} + +void XSLStyleSheet::clearDocuments() +{ + m_stylesheetDoc = 0; + unsigned len = length(); + for (unsigned i = 0; i < len; ++i) { + StyleBase* rule = item(i); + if (rule->isImportRule()) { + XSLImportRule* import = static_cast<XSLImportRule*>(rule); + if (import->styleSheet()) + import->styleSheet()->clearDocuments(); + } + } +} + +CachedResourceLoader* XSLStyleSheet::cachedResourceLoader() +{ + if (!m_ownerDocument) + return 0; + return m_ownerDocument->cachedResourceLoader(); +} + +bool XSLStyleSheet::parseString(const String& string, bool) +{ + // Parse in a single chunk into an xmlDocPtr + const UChar BOM = 0xFEFF; + const unsigned char BOMHighByte = *reinterpret_cast<const unsigned char*>(&BOM); + if (!m_stylesheetDocTaken) + xmlFreeDoc(m_stylesheetDoc); + m_stylesheetDocTaken = false; + + Console* console = 0; + if (Frame* frame = ownerDocument()->frame()) + console = frame->domWindow()->console(); + + XMLDocumentParserScope scope(cachedResourceLoader(), XSLTProcessor::genericErrorFunc, XSLTProcessor::parseErrorFunc, console); + + const char* buffer = reinterpret_cast<const char*>(string.characters()); + int size = string.length() * sizeof(UChar); + + xmlParserCtxtPtr ctxt = xmlCreateMemoryParserCtxt(buffer, size); + if (!ctxt) + return 0; + + if (m_parentStyleSheet) { + // The XSL transform may leave the newly-transformed document + // with references to the symbol dictionaries of the style sheet + // and any of its children. XML document disposal can corrupt memory + // if a document uses more than one symbol dictionary, so we + // ensure that all child stylesheets use the same dictionaries as their + // parents. + xmlDictFree(ctxt->dict); + ctxt->dict = m_parentStyleSheet->m_stylesheetDoc->dict; + xmlDictReference(ctxt->dict); + } + + m_stylesheetDoc = xmlCtxtReadMemory(ctxt, buffer, size, + finalURL().string().utf8().data(), + BOMHighByte == 0xFF ? "UTF-16LE" : "UTF-16BE", + XML_PARSE_NOENT | XML_PARSE_DTDATTR | XML_PARSE_NOWARNING | XML_PARSE_NOCDATA); + xmlFreeParserCtxt(ctxt); + + loadChildSheets(); + + return m_stylesheetDoc; +} + +void XSLStyleSheet::loadChildSheets() +{ + if (!document()) + return; + + xmlNodePtr stylesheetRoot = document()->children; + + // Top level children may include other things such as DTD nodes, we ignore those. + while (stylesheetRoot && stylesheetRoot->type != XML_ELEMENT_NODE) + stylesheetRoot = stylesheetRoot->next; + + if (m_embedded) { + // We have to locate (by ID) the appropriate embedded stylesheet element, so that we can walk the + // import/include list. + xmlAttrPtr idNode = xmlGetID(document(), (const xmlChar*)(finalURL().string().utf8().data())); + if (!idNode) + return; + stylesheetRoot = idNode->parent; + } else { + // FIXME: Need to handle an external URI with a # in it. This is a pretty minor edge case, so we'll deal + // with it later. + } + + if (stylesheetRoot) { + // Walk the children of the root element and look for import/include elements. + // Imports must occur first. + xmlNodePtr curr = stylesheetRoot->children; + while (curr) { + if (curr->type != XML_ELEMENT_NODE) { + curr = curr->next; + continue; + } + if (IS_XSLT_ELEM(curr) && IS_XSLT_NAME(curr, "import")) { + xmlChar* uriRef = xsltGetNsProp(curr, (const xmlChar*)"href", XSLT_NAMESPACE); + loadChildSheet(String::fromUTF8((const char*)uriRef)); + xmlFree(uriRef); + } else + break; + curr = curr->next; + } + + // Now handle includes. + while (curr) { + if (curr->type == XML_ELEMENT_NODE && IS_XSLT_ELEM(curr) && IS_XSLT_NAME(curr, "include")) { + xmlChar* uriRef = xsltGetNsProp(curr, (const xmlChar*)"href", XSLT_NAMESPACE); + loadChildSheet(String::fromUTF8((const char*)uriRef)); + xmlFree(uriRef); + } + curr = curr->next; + } + } +} + +void XSLStyleSheet::loadChildSheet(const String& href) +{ + RefPtr<XSLImportRule> childRule = XSLImportRule::create(this, href); + append(childRule); + childRule->loadSheet(); +} + +xsltStylesheetPtr XSLStyleSheet::compileStyleSheet() +{ + // FIXME: Hook up error reporting for the stylesheet compilation process. + if (m_embedded) + return xsltLoadStylesheetPI(document()); + + // xsltParseStylesheetDoc makes the document part of the stylesheet + // so we have to release our pointer to it. + ASSERT(!m_stylesheetDocTaken); + xsltStylesheetPtr result = xsltParseStylesheetDoc(m_stylesheetDoc); + if (result) + m_stylesheetDocTaken = true; + return result; +} + +void XSLStyleSheet::setParentStyleSheet(XSLStyleSheet* parent) +{ + m_parentStyleSheet = parent; + if (parent) + m_ownerDocument = parent->ownerDocument(); +} + +xmlDocPtr XSLStyleSheet::locateStylesheetSubResource(xmlDocPtr parentDoc, const xmlChar* uri) +{ + bool matchedParent = (parentDoc == document()); + unsigned len = length(); + for (unsigned i = 0; i < len; ++i) { + StyleBase* rule = item(i); + if (rule->isImportRule()) { + XSLImportRule* import = static_cast<XSLImportRule*>(rule); + XSLStyleSheet* child = import->styleSheet(); + if (!child) + continue; + if (matchedParent) { + if (child->processed()) + continue; // libxslt has been given this sheet already. + + // Check the URI of the child stylesheet against the doc URI. + // In order to ensure that libxml canonicalized both URLs, we get the original href + // string from the import rule and canonicalize it using libxml before comparing it + // with the URI argument. + CString importHref = import->href().utf8(); + xmlChar* base = xmlNodeGetBase(parentDoc, (xmlNodePtr)parentDoc); + xmlChar* childURI = xmlBuildURI((const xmlChar*)importHref.data(), base); + bool equalURIs = xmlStrEqual(uri, childURI); + xmlFree(base); + xmlFree(childURI); + if (equalURIs) { + child->markAsProcessed(); + return child->document(); + } + } else { + xmlDocPtr result = import->styleSheet()->locateStylesheetSubResource(parentDoc, uri); + if (result) + return result; + } + } + } + + return 0; +} + +void XSLStyleSheet::markAsProcessed() +{ + ASSERT(!m_processed); + ASSERT(!m_stylesheetDocTaken); + m_processed = true; + m_stylesheetDocTaken = true; +} + +} // namespace WebCore + +#endif // ENABLE(XSLT) diff --git a/Source/WebCore/xml/XSLStyleSheetQt.cpp b/Source/WebCore/xml/XSLStyleSheetQt.cpp new file mode 100644 index 0000000..855d6ba --- /dev/null +++ b/Source/WebCore/xml/XSLStyleSheetQt.cpp @@ -0,0 +1,102 @@ +/* + * This file is part of the XSL implementation. + * + * Copyright (C) 2009 Jakub Wieczorek <faw217@gmail.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 "XSLStyleSheet.h" + +#if ENABLE(XSLT) + +#include "DOMWindow.h" +#include "Document.h" +#include "Node.h" +#include "NotImplemented.h" +#include "XSLTProcessor.h" + +namespace WebCore { + +XSLStyleSheet::XSLStyleSheet(Node* parentNode, const String& originalURL, const KURL& finalURL, bool embedded) + : StyleSheet(parentNode, originalURL, finalURL) + , m_ownerDocument(parentNode->document()) + , m_embedded(embedded) +{ +} + +XSLStyleSheet::~XSLStyleSheet() +{ +} + +bool XSLStyleSheet::isLoading() +{ + notImplemented(); + return false; +} + +void XSLStyleSheet::checkLoaded() +{ + if (ownerNode()) + ownerNode()->sheetLoaded(); +} + +void XSLStyleSheet::clearDocuments() +{ + notImplemented(); +} + +CachedResourceLoader* XSLStyleSheet::cachedResourceLoader() +{ + if (!m_ownerDocument) + return 0; + return m_ownerDocument->cachedResourceLoader(); +} + +bool XSLStyleSheet::parseString(const String& string, bool) +{ + // FIXME: Fix QXmlQuery so that it allows compiling the stylesheet before setting the document + // to be transformed. This way we could not only check if the stylesheet is correct before using it + // but also turn XSLStyleSheet::sheetString() into XSLStyleSheet::query() that returns a QXmlQuery. + + m_sheetString = string; + return !m_sheetString.isEmpty(); +} + +void XSLStyleSheet::loadChildSheets() +{ + notImplemented(); +} + +void XSLStyleSheet::loadChildSheet(const String&) +{ + notImplemented(); +} + +void XSLStyleSheet::setParentStyleSheet(XSLStyleSheet*) +{ + notImplemented(); +} + +void XSLStyleSheet::markAsProcessed() +{ + notImplemented(); +} + +} // namespace WebCore + +#endif // ENABLE(XSLT) diff --git a/Source/WebCore/xml/XSLTExtensions.cpp b/Source/WebCore/xml/XSLTExtensions.cpp new file mode 100644 index 0000000..069ddd8 --- /dev/null +++ b/Source/WebCore/xml/XSLTExtensions.cpp @@ -0,0 +1,89 @@ +/** + * Copyright (C) 2001-2002 Thomas Broyer, Charlie Bozeman and Daniel Veillard. + * Copyright (C) 2007 Alexey Proskuryakov <ap@webkit.org> + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is fur- + * nished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FIT- + * NESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CON- + * NECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Except as contained in this notice, the name of the authors shall not + * be used in advertising or otherwise to promote the sale, use or other deal- + * ings in this Software without prior written authorization from him. + */ + +#include "config.h" + +#if ENABLE(XSLT) +#include "XSLTExtensions.h" + +#include <libxml/xpathInternals.h> + +#include <libxslt/xsltutils.h> +#include <libxslt/extensions.h> +#include <libxslt/extra.h> + +#if PLATFORM(MAC) +#include "SoftLinking.h" +#endif + +#if PLATFORM(MAC) +SOFT_LINK_LIBRARY(libxslt) +SOFT_LINK(libxslt, xsltRegisterExtFunction, int, (xsltTransformContextPtr ctxt, const xmlChar *name, const xmlChar *URI, xmlXPathFunction function), (ctxt, name, URI, function)) +SOFT_LINK(libxslt, xsltFunctionNodeSet, void, (xmlXPathParserContextPtr ctxt, int nargs), (ctxt, nargs)) +#endif + +namespace WebCore { + +// FIXME: This code is taken from libexslt 1.1.11; should sync with newer versions. +static void exsltNodeSetFunction(xmlXPathParserContextPtr ctxt, int nargs) +{ + xmlChar *strval; + xmlNodePtr retNode; + xmlXPathObjectPtr ret; + + if (nargs != 1) { + xmlXPathSetArityError(ctxt); + return; + } + + if (xmlXPathStackIsNodeSet(ctxt)) { + xsltFunctionNodeSet(ctxt, nargs); + return; + } + + strval = xmlXPathPopString(ctxt); + retNode = xmlNewDocText(NULL, strval); + ret = xmlXPathNewValueTree(retNode); + + // FIXME: It might be helpful to push any errors from xmlXPathNewValueTree + // up to the Javascript Console. + if (ret != NULL) + ret->type = XPATH_NODESET; + + if (strval != NULL) + xmlFree(strval); + + valuePush(ctxt, ret); +} + +void registerXSLTExtensions(xsltTransformContextPtr ctxt) +{ + xsltRegisterExtFunction(ctxt, (const xmlChar*)"node-set", (const xmlChar*)"http://exslt.org/common", exsltNodeSetFunction); +} + +} + +#endif diff --git a/Source/WebCore/xml/XSLTExtensions.h b/Source/WebCore/xml/XSLTExtensions.h new file mode 100644 index 0000000..4e4224f --- /dev/null +++ b/Source/WebCore/xml/XSLTExtensions.h @@ -0,0 +1,40 @@ +/** + * Copyright (C) 2007 Alexey Proskuryakov <ap@webkit.org> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef XSLTExtensions_h +#define XSLTExtensions_h + +#if ENABLE(XSLT) + +#include <libxslt/xsltInternals.h> + +namespace WebCore { + +void registerXSLTExtensions(xsltTransformContextPtr ctxt); + +} + +#endif +#endif diff --git a/Source/WebCore/xml/XSLTProcessor.cpp b/Source/WebCore/xml/XSLTProcessor.cpp new file mode 100644 index 0000000..9b6a067 --- /dev/null +++ b/Source/WebCore/xml/XSLTProcessor.cpp @@ -0,0 +1,183 @@ +/* + * This file is part of the XSL implementation. + * + * Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple, Inc. All rights reserved. + * Copyright (C) 2005, 2006 Alexey Proskuryakov <ap@webkit.org> + * + * 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" + +#if ENABLE(XSLT) + +#include "XSLTProcessor.h" + +#include "DOMImplementation.h" +#include "CachedResourceLoader.h" +#include "DocumentFragment.h" +#include "Frame.h" +#include "FrameLoader.h" +#include "FrameView.h" +#include "HTMLBodyElement.h" +#include "HTMLDocument.h" +#include "Page.h" +#include "Text.h" +#include "TextResourceDecoder.h" +#include "markup.h" + +#include <wtf/Assertions.h> +#include <wtf/Vector.h> + +namespace WebCore { + +static inline void transformTextStringToXHTMLDocumentString(String& text) +{ + // Modify the output so that it is a well-formed XHTML document with a <pre> tag enclosing the text. + text.replace('&', "&"); + text.replace('<', "<"); + text = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n" + "<html xmlns=\"http://www.w3.org/1999/xhtml\">\n" + "<head><title/></head>\n" + "<body>\n" + "<pre>" + text + "</pre>\n" + "</body>\n" + "</html>\n"; +} + +XSLTProcessor::~XSLTProcessor() +{ + // Stylesheet shouldn't outlive its root node. + ASSERT(!m_stylesheetRootNode || !m_stylesheet || m_stylesheet->hasOneRef()); +} + +PassRefPtr<Document> XSLTProcessor::createDocumentFromSource(const String& sourceString, + const String& sourceEncoding, const String& sourceMIMEType, Node* sourceNode, Frame* frame) +{ + RefPtr<Document> ownerDocument = sourceNode->document(); + bool sourceIsDocument = (sourceNode == ownerDocument.get()); + String documentSource = sourceString; + + RefPtr<Document> result; + if (sourceMIMEType == "text/plain") { + result = Document::create(frame, sourceIsDocument ? ownerDocument->url() : KURL()); + transformTextStringToXHTMLDocumentString(documentSource); + } else + result = DOMImplementation::createDocument(sourceMIMEType, frame, sourceIsDocument ? ownerDocument->url() : KURL(), false); + + // Before parsing, we need to save & detach the old document and get the new document + // in place. We have to do this only if we're rendering the result document. + if (frame) { + if (FrameView* view = frame->view()) + view->clear(); + result->setTransformSourceDocument(frame->document()); + frame->setDocument(result); + } + + result->open(); + + RefPtr<TextResourceDecoder> decoder = TextResourceDecoder::create(sourceMIMEType); + decoder->setEncoding(sourceEncoding.isEmpty() ? UTF8Encoding() : TextEncoding(sourceEncoding), TextResourceDecoder::EncodingFromXMLHeader); + result->setDecoder(decoder.release()); + + result->write(documentSource); + result->close(); + + return result.release(); +} + +static inline RefPtr<DocumentFragment> createFragmentFromSource(const String& sourceString, const String& sourceMIMEType, Document* outputDoc) +{ + RefPtr<DocumentFragment> fragment = outputDoc->createDocumentFragment(); + + if (sourceMIMEType == "text/html") { + // As far as I can tell, there isn't a spec for how transformToFragment + // is supposed to work. Based on the documentation I can find, it looks + // like we want to start parsing the fragment in the InBody insertion + // mode. Unfortunately, that's an implementation detail of the parser. + // We achieve that effect here by passing in a fake body element as + // context for the fragment. + RefPtr<HTMLBodyElement> fakeBody = HTMLBodyElement::create(outputDoc); + fragment->parseHTML(sourceString, fakeBody.get()); + } else if (sourceMIMEType == "text/plain") + fragment->parserAddChild(Text::create(outputDoc, sourceString)); + else { + bool successfulParse = fragment->parseXML(sourceString, 0); + if (!successfulParse) + return 0; + } + + // FIXME: Do we need to mess with URLs here? + + return fragment; +} + +PassRefPtr<Document> XSLTProcessor::transformToDocument(Node* sourceNode) +{ + String resultMIMEType; + String resultString; + String resultEncoding; + if (!transformToString(sourceNode, resultMIMEType, resultString, resultEncoding)) + return 0; + return createDocumentFromSource(resultString, resultEncoding, resultMIMEType, sourceNode, 0); +} + +PassRefPtr<DocumentFragment> XSLTProcessor::transformToFragment(Node* sourceNode, Document* outputDoc) +{ + String resultMIMEType; + String resultString; + String resultEncoding; + + // If the output document is HTML, default to HTML method. + if (outputDoc->isHTMLDocument()) + resultMIMEType = "text/html"; + + if (!transformToString(sourceNode, resultMIMEType, resultString, resultEncoding)) + return 0; + return createFragmentFromSource(resultString, resultMIMEType, outputDoc); +} + +void XSLTProcessor::setParameter(const String& /*namespaceURI*/, const String& localName, const String& value) +{ + // FIXME: namespace support? + // should make a QualifiedName here but we'd have to expose the impl + m_parameters.set(localName, value); +} + +String XSLTProcessor::getParameter(const String& /*namespaceURI*/, const String& localName) const +{ + // FIXME: namespace support? + // should make a QualifiedName here but we'd have to expose the impl + return m_parameters.get(localName); +} + +void XSLTProcessor::removeParameter(const String& /*namespaceURI*/, const String& localName) +{ + // FIXME: namespace support? + m_parameters.remove(localName); +} + +void XSLTProcessor::reset() +{ + m_stylesheet.clear(); + m_stylesheetRootNode.clear(); + m_parameters.clear(); +} + +} // namespace WebCore + +#endif // ENABLE(XSLT) diff --git a/Source/WebCore/xml/XSLTProcessor.h b/Source/WebCore/xml/XSLTProcessor.h new file mode 100644 index 0000000..451e12c --- /dev/null +++ b/Source/WebCore/xml/XSLTProcessor.h @@ -0,0 +1,86 @@ +/* + * This file is part of the XSL implementation. + * + * Copyright (C) 2004, 2007, 2008 Apple, Inc. All rights reserved. + * + * 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. + * + */ + +#ifndef XSLTProcessor_h +#define XSLTProcessor_h + +#if ENABLE(XSLT) + +#include "Node.h" +#include "XSLStyleSheet.h" +#include <wtf/HashMap.h> +#include <wtf/text/StringHash.h> + +#if !USE(QXMLQUERY) +#include <libxml/parserInternals.h> +#include <libxslt/documents.h> +#endif + +namespace WebCore { + +class Frame; +class Document; +class DocumentFragment; + +class XSLTProcessor : public RefCounted<XSLTProcessor> { +public: + static PassRefPtr<XSLTProcessor> create() { return adoptRef(new XSLTProcessor); } + ~XSLTProcessor(); + + void setXSLStyleSheet(PassRefPtr<XSLStyleSheet> styleSheet) { m_stylesheet = styleSheet; } + bool transformToString(Node* source, String& resultMIMEType, String& resultString, String& resultEncoding); + PassRefPtr<Document> createDocumentFromSource(const String& source, const String& sourceEncoding, const String& sourceMIMEType, Node* sourceNode, Frame* frame); + + // DOM methods + void importStylesheet(PassRefPtr<Node> style) { m_stylesheetRootNode = style; } + PassRefPtr<DocumentFragment> transformToFragment(Node* source, Document* ouputDoc); + PassRefPtr<Document> transformToDocument(Node* source); + + void setParameter(const String& namespaceURI, const String& localName, const String& value); + String getParameter(const String& namespaceURI, const String& localName) const; + void removeParameter(const String& namespaceURI, const String& localName); + void clearParameters() { m_parameters.clear(); } + + void reset(); + +#if !USE(QXMLQUERY) + static void parseErrorFunc(void* userData, xmlError*); + static void genericErrorFunc(void* userData, const char* msg, ...); + + // Only for libXSLT callbacks + XSLStyleSheet* xslStylesheet() const { return m_stylesheet.get(); } +#endif + + typedef HashMap<String, String> ParameterMap; + +private: + XSLTProcessor() { } + + RefPtr<XSLStyleSheet> m_stylesheet; + RefPtr<Node> m_stylesheetRootNode; + ParameterMap m_parameters; +}; + +} + +#endif +#endif diff --git a/Source/WebCore/xml/XSLTProcessor.idl b/Source/WebCore/xml/XSLTProcessor.idl new file mode 100644 index 0000000..dac4b21 --- /dev/null +++ b/Source/WebCore/xml/XSLTProcessor.idl @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2008, 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +module xml { + + // Eventually we should implement XSLTException: + // http://lxr.mozilla.org/seamonkey/source/content/xsl/public/nsIXSLTException.idl + // http://bugs.webkit.org/show_bug.cgi?id=5446 + + interface [ + Conditional=XSLT, + CanBeConstructed, + CustomConstructFunction, + V8CustomConstructor + ] XSLTProcessor { + + [Custom] void importStylesheet(in Node stylesheet); + [Custom] DocumentFragment transformToFragment(in Node source, in Document docVal); + [Custom] Document transformToDocument(in Node source); + + [Custom] void setParameter(in DOMString namespaceURI, in DOMString localName, in DOMString value); + [Custom, ConvertNullStringTo=Undefined] DOMString getParameter(in DOMString namespaceURI, in DOMString localName); + [Custom] void removeParameter(in DOMString namespaceURI, in DOMString localName); + void clearParameters(); + + void reset(); + + }; + +} diff --git a/Source/WebCore/xml/XSLTProcessorLibxslt.cpp b/Source/WebCore/xml/XSLTProcessorLibxslt.cpp new file mode 100644 index 0000000..89e1a87 --- /dev/null +++ b/Source/WebCore/xml/XSLTProcessorLibxslt.cpp @@ -0,0 +1,354 @@ +/* + * This file is part of the XSL implementation. + * + * Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple, Inc. All rights reserved. + * Copyright (C) 2005, 2006 Alexey Proskuryakov <ap@webkit.org> + * + * 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" + +#if ENABLE(XSLT) + +#include "XSLTProcessor.h" + +#include "Console.h" +#include "DOMWindow.h" +#include "CachedResourceLoader.h" +#include "Frame.h" +#include "ResourceError.h" +#include "ResourceHandle.h" +#include "ResourceRequest.h" +#include "ResourceResponse.h" +#include "TransformSource.h" +#include "XMLDocumentParser.h" +#include "XSLStyleSheet.h" +#include "XSLTExtensions.h" +#include "XSLTUnicodeSort.h" +#include "markup.h" +#include <libxslt/imports.h> +#include <libxslt/variables.h> +#include <libxslt/xsltutils.h> +#include <wtf/Assertions.h> +#include <wtf/Vector.h> +#include <wtf/text/CString.h> +#include <wtf/text/StringBuffer.h> +#include <wtf/unicode/UTF8.h> + +#if PLATFORM(MAC) +#include "SoftLinking.h" + +SOFT_LINK_LIBRARY(libxslt); +SOFT_LINK(libxslt, xsltFreeStylesheet, void, (xsltStylesheetPtr sheet), (sheet)) +SOFT_LINK(libxslt, xsltFreeTransformContext, void, (xsltTransformContextPtr ctxt), (ctxt)) +SOFT_LINK(libxslt, xsltNewTransformContext, xsltTransformContextPtr, (xsltStylesheetPtr style, xmlDocPtr doc), (style, doc)) +SOFT_LINK(libxslt, xsltApplyStylesheetUser, xmlDocPtr, (xsltStylesheetPtr style, xmlDocPtr doc, const char** params, const char* output, FILE* profile, xsltTransformContextPtr userCtxt), (style, doc, params, output, profile, userCtxt)) +SOFT_LINK(libxslt, xsltQuoteUserParams, int, (xsltTransformContextPtr ctxt, const char** params), (ctxt, params)) +SOFT_LINK(libxslt, xsltSetCtxtSortFunc, void, (xsltTransformContextPtr ctxt, xsltSortFunc handler), (ctxt, handler)) +SOFT_LINK(libxslt, xsltSetLoaderFunc, void, (xsltDocLoaderFunc f), (f)) +SOFT_LINK(libxslt, xsltSaveResultTo, int, (xmlOutputBufferPtr buf, xmlDocPtr result, xsltStylesheetPtr style), (buf, result, style)) +SOFT_LINK(libxslt, xsltNextImport, xsltStylesheetPtr, (xsltStylesheetPtr style), (style)) +#endif + +namespace WebCore { + +void XSLTProcessor::genericErrorFunc(void*, const char*, ...) +{ + // It would be nice to do something with this error message. +} + +void XSLTProcessor::parseErrorFunc(void* userData, xmlError* error) +{ + Console* console = static_cast<Console*>(userData); + if (!console) + return; + + MessageLevel level; + switch (error->level) { + case XML_ERR_NONE: + level = TipMessageLevel; + break; + case XML_ERR_WARNING: + level = WarningMessageLevel; + break; + case XML_ERR_ERROR: + case XML_ERR_FATAL: + default: + level = ErrorMessageLevel; + break; + } + + console->addMessage(XMLMessageSource, LogMessageType, level, error->message, error->line, error->file); +} + +// FIXME: There seems to be no way to control the ctxt pointer for loading here, thus we have globals. +static XSLTProcessor* globalProcessor = 0; +static CachedResourceLoader* globalCachedResourceLoader = 0; +static xmlDocPtr docLoaderFunc(const xmlChar* uri, + xmlDictPtr, + int options, + void* ctxt, + xsltLoadType type) +{ + if (!globalProcessor) + return 0; + + switch (type) { + case XSLT_LOAD_DOCUMENT: { + xsltTransformContextPtr context = (xsltTransformContextPtr)ctxt; + xmlChar* base = xmlNodeGetBase(context->document->doc, context->node); + KURL url(KURL(ParsedURLString, reinterpret_cast<const char*>(base)), reinterpret_cast<const char*>(uri)); + xmlFree(base); + ResourceError error; + ResourceResponse response; + + Vector<char> data; + + bool requestAllowed = globalCachedResourceLoader->frame() && globalCachedResourceLoader->document()->securityOrigin()->canRequest(url); + if (requestAllowed) { + globalCachedResourceLoader->frame()->loader()->loadResourceSynchronously(url, AllowStoredCredentials, error, response, data); + requestAllowed = globalCachedResourceLoader->document()->securityOrigin()->canRequest(response.url()); + } + if (!requestAllowed) { + data.clear(); + globalCachedResourceLoader->printAccessDeniedMessage(url); + } + + Console* console = 0; + if (Frame* frame = globalProcessor->xslStylesheet()->ownerDocument()->frame()) + console = frame->domWindow()->console(); + xmlSetStructuredErrorFunc(console, XSLTProcessor::parseErrorFunc); + xmlSetGenericErrorFunc(console, XSLTProcessor::genericErrorFunc); + + // We don't specify an encoding here. Neither Gecko nor WinIE respects + // the encoding specified in the HTTP headers. + xmlDocPtr doc = xmlReadMemory(data.data(), data.size(), (const char*)uri, 0, options); + + xmlSetStructuredErrorFunc(0, 0); + xmlSetGenericErrorFunc(0, 0); + + return doc; + } + case XSLT_LOAD_STYLESHEET: + return globalProcessor->xslStylesheet()->locateStylesheetSubResource(((xsltStylesheetPtr)ctxt)->doc, uri); + default: + break; + } + + return 0; +} + +static inline void setXSLTLoadCallBack(xsltDocLoaderFunc func, XSLTProcessor* processor, CachedResourceLoader* cachedResourceLoader) +{ + xsltSetLoaderFunc(func); + globalProcessor = processor; + globalCachedResourceLoader = cachedResourceLoader; +} + +static int writeToVector(void* context, const char* buffer, int len) +{ + Vector<UChar>& resultOutput = *static_cast<Vector<UChar>*>(context); + + if (!len) + return 0; + + StringBuffer stringBuffer(len); + UChar* bufferUChar = stringBuffer.characters(); + UChar* bufferUCharEnd = bufferUChar + len; + + const char* stringCurrent = buffer; + WTF::Unicode::ConversionResult result = WTF::Unicode::convertUTF8ToUTF16(&stringCurrent, buffer + len, &bufferUChar, bufferUCharEnd); + if (result != WTF::Unicode::conversionOK && result != WTF::Unicode::sourceExhausted) { + ASSERT_NOT_REACHED(); + return -1; + } + + int utf16Length = bufferUChar - stringBuffer.characters(); + resultOutput.append(stringBuffer.characters(), utf16Length); + return stringCurrent - buffer; +} + +static bool saveResultToString(xmlDocPtr resultDoc, xsltStylesheetPtr sheet, String& resultString) +{ + xmlOutputBufferPtr outputBuf = xmlAllocOutputBuffer(0); + if (!outputBuf) + return false; + + Vector<UChar> resultVector; + outputBuf->context = &resultVector; + outputBuf->writecallback = writeToVector; + + int retval = xsltSaveResultTo(outputBuf, resultDoc, sheet); + xmlOutputBufferClose(outputBuf); + if (retval < 0) + return false; + + // Workaround for <http://bugzilla.gnome.org/show_bug.cgi?id=495668>: libxslt appends an extra line feed to the result. + if (resultVector.size() > 0 && resultVector[resultVector.size() - 1] == '\n') + resultVector.removeLast(); + + resultString = String::adopt(resultVector); + + return true; +} + +static const char** xsltParamArrayFromParameterMap(XSLTProcessor::ParameterMap& parameters) +{ + if (parameters.isEmpty()) + return 0; + + const char** parameterArray = (const char**)fastMalloc(((parameters.size() * 2) + 1) * sizeof(char*)); + + XSLTProcessor::ParameterMap::iterator end = parameters.end(); + unsigned index = 0; + for (XSLTProcessor::ParameterMap::iterator it = parameters.begin(); it != end; ++it) { + parameterArray[index++] = fastStrDup(it->first.utf8().data()); + parameterArray[index++] = fastStrDup(it->second.utf8().data()); + } + parameterArray[index] = 0; + + return parameterArray; +} + +static void freeXsltParamArray(const char** params) +{ + const char** temp = params; + if (!params) + return; + + while (*temp) { + fastFree((void*)*(temp++)); + fastFree((void*)*(temp++)); + } + fastFree(params); +} + +static xsltStylesheetPtr xsltStylesheetPointer(RefPtr<XSLStyleSheet>& cachedStylesheet, Node* stylesheetRootNode) +{ + if (!cachedStylesheet && stylesheetRootNode) { + cachedStylesheet = XSLStyleSheet::createForXSLTProcessor(stylesheetRootNode->parentNode() ? stylesheetRootNode->parentNode() : stylesheetRootNode, + stylesheetRootNode->document()->url().string(), + stylesheetRootNode->document()->url()); // FIXME: Should we use baseURL here? + + // According to Mozilla documentation, the node must be a Document node, an xsl:stylesheet or xsl:transform element. + // But we just use text content regardless of node type. + cachedStylesheet->parseString(createMarkup(stylesheetRootNode)); + } + + if (!cachedStylesheet || !cachedStylesheet->document()) + return 0; + + return cachedStylesheet->compileStyleSheet(); +} + +static inline xmlDocPtr xmlDocPtrFromNode(Node* sourceNode, bool& shouldDelete) +{ + RefPtr<Document> ownerDocument = sourceNode->document(); + bool sourceIsDocument = (sourceNode == ownerDocument.get()); + + xmlDocPtr sourceDoc = 0; + if (sourceIsDocument && ownerDocument->transformSource()) + sourceDoc = (xmlDocPtr)ownerDocument->transformSource()->platformSource(); + if (!sourceDoc) { + sourceDoc = (xmlDocPtr)xmlDocPtrForString(ownerDocument->cachedResourceLoader(), createMarkup(sourceNode), + sourceIsDocument ? ownerDocument->url().string() : String()); + shouldDelete = sourceDoc; + } + return sourceDoc; +} + +static inline String resultMIMEType(xmlDocPtr resultDoc, xsltStylesheetPtr sheet) +{ + // There are three types of output we need to be able to deal with: + // HTML (create an HTML document), XML (create an XML document), + // and text (wrap in a <pre> and create an XML document). + + const xmlChar* resultType = 0; + XSLT_GET_IMPORT_PTR(resultType, sheet, method); + if (!resultType && resultDoc->type == XML_HTML_DOCUMENT_NODE) + resultType = (const xmlChar*)"html"; + + if (xmlStrEqual(resultType, (const xmlChar*)"html")) + return "text/html"; + if (xmlStrEqual(resultType, (const xmlChar*)"text")) + return "text/plain"; + + return "application/xml"; +} + +bool XSLTProcessor::transformToString(Node* sourceNode, String& mimeType, String& resultString, String& resultEncoding) +{ + RefPtr<Document> ownerDocument = sourceNode->document(); + + setXSLTLoadCallBack(docLoaderFunc, this, ownerDocument->cachedResourceLoader()); + xsltStylesheetPtr sheet = xsltStylesheetPointer(m_stylesheet, m_stylesheetRootNode.get()); + if (!sheet) { + setXSLTLoadCallBack(0, 0, 0); + return false; + } + m_stylesheet->clearDocuments(); + + xmlChar* origMethod = sheet->method; + if (!origMethod && mimeType == "text/html") + sheet->method = (xmlChar*)"html"; + + bool success = false; + bool shouldFreeSourceDoc = false; + if (xmlDocPtr sourceDoc = xmlDocPtrFromNode(sourceNode, shouldFreeSourceDoc)) { + // The XML declaration would prevent parsing the result as a fragment, and it's not needed even for documents, + // as the result of this function is always immediately parsed. + sheet->omitXmlDeclaration = true; + + xsltTransformContextPtr transformContext = xsltNewTransformContext(sheet, sourceDoc); + registerXSLTExtensions(transformContext); + + // <http://bugs.webkit.org/show_bug.cgi?id=16077>: XSLT processor <xsl:sort> algorithm only compares by code point. + xsltSetCtxtSortFunc(transformContext, xsltUnicodeSortFunction); + + // This is a workaround for a bug in libxslt. + // The bug has been fixed in version 1.1.13, so once we ship that this can be removed. + if (!transformContext->globalVars) + transformContext->globalVars = xmlHashCreate(20); + + const char** params = xsltParamArrayFromParameterMap(m_parameters); + xsltQuoteUserParams(transformContext, params); + xmlDocPtr resultDoc = xsltApplyStylesheetUser(sheet, sourceDoc, 0, 0, 0, transformContext); + + xsltFreeTransformContext(transformContext); + freeXsltParamArray(params); + + if (shouldFreeSourceDoc) + xmlFreeDoc(sourceDoc); + + if ((success = saveResultToString(resultDoc, sheet, resultString))) { + mimeType = resultMIMEType(resultDoc, sheet); + resultEncoding = (char*)resultDoc->encoding; + } + xmlFreeDoc(resultDoc); + } + + sheet->method = origMethod; + setXSLTLoadCallBack(0, 0, 0); + xsltFreeStylesheet(sheet); + m_stylesheet = 0; + + return success; +} + +} // namespace WebCore + +#endif // ENABLE(XSLT) diff --git a/Source/WebCore/xml/XSLTProcessorQt.cpp b/Source/WebCore/xml/XSLTProcessorQt.cpp new file mode 100644 index 0000000..717ad8b --- /dev/null +++ b/Source/WebCore/xml/XSLTProcessorQt.cpp @@ -0,0 +1,182 @@ +/* + * This file is part of the XSL implementation. + * + * Copyright (C) 2009 Jakub Wieczorek <faw217@gmail.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" + +#if ENABLE(XSLT) + +#include "XSLTProcessor.h" + +#include "Console.h" +#include "DOMWindow.h" +#include "Frame.h" +#include "TransformSource.h" +#include "markup.h" +#include <wtf/Assertions.h> +#include <wtf/Vector.h> + +#include <qabstractmessagehandler.h> +#include <qabstracturiresolver.h> +#include <qbuffer.h> +#include <qsourcelocation.h> +#include <qxmlquery.h> + +namespace WebCore { + +class XSLTMessageHandler : public QAbstractMessageHandler { + +public: + XSLTMessageHandler(Document* document = 0); + virtual void handleMessage(QtMsgType type, const QString& description, + const QUrl& identifier, const QSourceLocation& sourceLocation); + +private: + Document* m_document; +}; + +XSLTMessageHandler::XSLTMessageHandler(Document* document) + : QAbstractMessageHandler() + , m_document(document) +{ +} + +void XSLTMessageHandler::handleMessage(QtMsgType type, const QString& description, + const QUrl&, const QSourceLocation& sourceLocation) +{ + if (!m_document->frame()) + return; + + MessageLevel level; + switch (type) { + case QtDebugMsg: + level = TipMessageLevel; + break; + case QtWarningMsg: + level = WarningMessageLevel; + break; + case QtCriticalMsg: + case QtFatalMsg: + level = ErrorMessageLevel; + break; + default: + level = LogMessageLevel; + break; + } + + Console* console = m_document->frame()->domWindow()->console(); + console->addMessage(XMLMessageSource, LogMessageType, level, description, + sourceLocation.line(), sourceLocation.uri().toString()); +} + +class XSLTUriResolver : public QAbstractUriResolver { + +public: + XSLTUriResolver(Document* document); + virtual QUrl resolve(const QUrl& relative, const QUrl& baseURI) const; + +private: + Document* m_document; +}; + +XSLTUriResolver::XSLTUriResolver(Document* document) + : QAbstractUriResolver() + , m_document(document) +{ +} + +QUrl XSLTUriResolver::resolve(const QUrl& relative, const QUrl& baseURI) const +{ + QUrl url = baseURI.resolved(relative); + + if (!m_document->frame() || !m_document->securityOrigin()->canRequest(url)) + return QUrl(); + return url; +} + +bool XSLTProcessor::transformToString(Node* sourceNode, String&, String& resultString, String&) +{ + bool success = false; + + RefPtr<XSLStyleSheet> stylesheet = m_stylesheet; + if (!stylesheet && m_stylesheetRootNode) { + Node* node = m_stylesheetRootNode.get(); + stylesheet = XSLStyleSheet::createForXSLTProcessor(node->parentNode() ? node->parentNode() : node, + node->document()->url().string(), + node->document()->url()); // FIXME: Should we use baseURL here? + + // According to Mozilla documentation, the node must be a Document node, an xsl:stylesheet or xsl:transform element. + // But we just use text content regardless of node type. + stylesheet->parseString(createMarkup(node)); + } + + if (!stylesheet || stylesheet->sheetString().isEmpty()) + return success; + + RefPtr<Document> ownerDocument = sourceNode->document(); + bool sourceIsDocument = (sourceNode == ownerDocument.get()); + + QXmlQuery query(QXmlQuery::XSLT20); + + XSLTMessageHandler messageHandler(ownerDocument.get()); + XSLTUriResolver uriResolver(ownerDocument.get()); + query.setMessageHandler(&messageHandler); + + XSLTProcessor::ParameterMap::iterator end = m_parameters.end(); + for (XSLTProcessor::ParameterMap::iterator it = m_parameters.begin(); it != end; ++it) + query.bindVariable(QString(it->first), QXmlItem(QVariant(QString(it->second)))); + + QString source; + if (sourceIsDocument && ownerDocument->transformSource()) + source = ownerDocument->transformSource()->platformSource(); + if (!sourceIsDocument || source.isEmpty()) + source = createMarkup(sourceNode); + + QBuffer inputBuffer; + QBuffer styleSheetBuffer; + QBuffer outputBuffer; + + inputBuffer.setData(source.toUtf8()); + styleSheetBuffer.setData(QString(stylesheet->sheetString()).toUtf8()); + + inputBuffer.open(QIODevice::ReadOnly); + styleSheetBuffer.open(QIODevice::ReadOnly); + outputBuffer.open(QIODevice::ReadWrite); + + query.setFocus(&inputBuffer); + query.setQuery(&styleSheetBuffer, QUrl(stylesheet->href())); + + query.setUriResolver(&uriResolver); + + success = query.evaluateTo(&outputBuffer); + outputBuffer.reset(); + resultString = QString::fromUtf8(outputBuffer.readAll()).trimmed(); + + if (m_stylesheet) { + m_stylesheet->clearDocuments(); + m_stylesheet = 0; + } + + return success; +} + +} // namespace WebCore + +#endif // ENABLE(XSLT) diff --git a/Source/WebCore/xml/XSLTUnicodeSort.cpp b/Source/WebCore/xml/XSLTUnicodeSort.cpp new file mode 100644 index 0000000..b0b9c72 --- /dev/null +++ b/Source/WebCore/xml/XSLTUnicodeSort.cpp @@ -0,0 +1,322 @@ +/* + * Copyright (C) 2007, 2008, 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "XSLTUnicodeSort.h" + +#if ENABLE(XSLT) + +#include "PlatformString.h" +#include <libxslt/templates.h> +#include <libxslt/xsltutils.h> +#include <wtf/unicode/Collator.h> + +#if PLATFORM(MAC) +#include "SoftLinking.h" +#endif + +#if PLATFORM(MAC) + +SOFT_LINK_LIBRARY(libxslt) +SOFT_LINK(libxslt, xsltComputeSortResult, xmlXPathObjectPtr*, (xsltTransformContextPtr ctxt, xmlNodePtr sort), (ctxt, sort)) +SOFT_LINK(libxslt, xsltEvalAttrValueTemplate, xmlChar*, (xsltTransformContextPtr ctxt, xmlNodePtr node, const xmlChar *name, const xmlChar *ns), (ctxt, node, name, ns)) + +static void xsltTransformErrorTrampoline(xsltTransformContextPtr, xsltStylesheetPtr, xmlNodePtr, const char* message, ...) WTF_ATTRIBUTE_PRINTF(4, 5); + +void xsltTransformErrorTrampoline(xsltTransformContextPtr context, xsltStylesheetPtr style, xmlNodePtr node, const char* message, ...) +{ + va_list args; + va_start(args, message); + char* messageWithArgs; + vasprintf(&messageWithArgs, message, args); + va_end(args); + + static void (*xsltTransformErrorPointer)(xsltTransformContextPtr, xsltStylesheetPtr, xmlNodePtr, const char*, ...) WTF_ATTRIBUTE_PRINTF(4, 5) + = reinterpret_cast<void (*)(xsltTransformContextPtr, xsltStylesheetPtr, xmlNodePtr, const char*, ...)>(dlsym(libxsltLibrary(), "xsltTransformError")); + xsltTransformErrorPointer(context, style, node, "%s", messageWithArgs); + + free(messageWithArgs); +} + +#define xsltTransformError xsltTransformErrorTrampoline + +#endif + +namespace WebCore { + +// Based on default implementation from libxslt 1.1.22 and xsltICUSort.c example. +void xsltUnicodeSortFunction(xsltTransformContextPtr ctxt, xmlNodePtr *sorts, int nbsorts) +{ +#ifdef XSLT_REFACTORED + xsltStyleItemSortPtr comp; +#else + xsltStylePreCompPtr comp; +#endif + xmlXPathObjectPtr *resultsTab[XSLT_MAX_SORT]; + xmlXPathObjectPtr *results = NULL, *res; + xmlNodeSetPtr list = NULL; + int descending, number, desc, numb; + int len = 0; + int i, j, incr; + int tst; + int depth; + xmlNodePtr node; + xmlXPathObjectPtr tmp; + int tempstype[XSLT_MAX_SORT], temporder[XSLT_MAX_SORT]; + + if ((ctxt == NULL) || (sorts == NULL) || (nbsorts <= 0) || + (nbsorts >= XSLT_MAX_SORT)) + return; + if (sorts[0] == NULL) + return; + comp = static_cast<xsltStylePreComp*>(sorts[0]->psvi); + if (comp == NULL) + return; + + list = ctxt->nodeList; + if ((list == NULL) || (list->nodeNr <= 1)) + return; /* nothing to do */ + + for (j = 0; j < nbsorts; j++) { + comp = static_cast<xsltStylePreComp*>(sorts[j]->psvi); + tempstype[j] = 0; + if ((comp->stype == NULL) && (comp->has_stype != 0)) { + comp->stype = + xsltEvalAttrValueTemplate(ctxt, sorts[j], + (const xmlChar *) "data-type", + XSLT_NAMESPACE); + if (comp->stype != NULL) { + tempstype[j] = 1; + if (xmlStrEqual(comp->stype, (const xmlChar *) "text")) + comp->number = 0; + else if (xmlStrEqual(comp->stype, (const xmlChar *) "number")) + comp->number = 1; + else { + xsltTransformError(ctxt, NULL, sorts[j], + "xsltDoSortFunction: no support for data-type = %s\n", + comp->stype); + comp->number = 0; /* use default */ + } + } + } + temporder[j] = 0; + if ((comp->order == NULL) && (comp->has_order != 0)) { + comp->order = xsltEvalAttrValueTemplate(ctxt, sorts[j], + (const xmlChar *) "order", + XSLT_NAMESPACE); + if (comp->order != NULL) { + temporder[j] = 1; + if (xmlStrEqual(comp->order, (const xmlChar *) "ascending")) + comp->descending = 0; + else if (xmlStrEqual(comp->order, + (const xmlChar *) "descending")) + comp->descending = 1; + else { + xsltTransformError(ctxt, NULL, sorts[j], + "xsltDoSortFunction: invalid value %s for order\n", + comp->order); + comp->descending = 0; /* use default */ + } + } + } + } + + len = list->nodeNr; + + resultsTab[0] = xsltComputeSortResult(ctxt, sorts[0]); + for (i = 1;i < XSLT_MAX_SORT;i++) + resultsTab[i] = NULL; + + results = resultsTab[0]; + + comp = static_cast<xsltStylePreComp*>(sorts[0]->psvi); + descending = comp->descending; + number = comp->number; + if (results == NULL) + return; + + // We are passing a language identifier to a function that expects a locale identifier. + // The implementation of Collator should be lenient, and accept both "en-US" and "en_US", for example. + // This lets an author to really specify sorting rules, e.g. "de_DE@collation=phonebook", which isn't + // possible with language alone. + Collator collator(comp->has_lang ? (const char*)comp->lang : "en"); + collator.setOrderLowerFirst(comp->lower_first); + + /* Shell's sort of node-set */ + for (incr = len / 2; incr > 0; incr /= 2) { + for (i = incr; i < len; i++) { + j = i - incr; + if (results[i] == NULL) + continue; + + while (j >= 0) { + if (results[j] == NULL) + tst = 1; + else { + if (number) { + /* We make NaN smaller than number in accordance + with XSLT spec */ + if (xmlXPathIsNaN(results[j]->floatval)) { + if (xmlXPathIsNaN(results[j + incr]->floatval)) + tst = 0; + else + tst = -1; + } else if (xmlXPathIsNaN(results[j + incr]->floatval)) + tst = 1; + else if (results[j]->floatval == + results[j + incr]->floatval) + tst = 0; + else if (results[j]->floatval > + results[j + incr]->floatval) + tst = 1; + else tst = -1; + } else { + String str1 = String::fromUTF8((const char*)results[j]->stringval); + String str2 = String::fromUTF8((const char*)results[j + incr]->stringval); + tst = collator.collate(str1.characters(), str1.length(), str2.characters(), str2.length()); + } + if (descending) + tst = -tst; + } + if (tst == 0) { + /* + * Okay we need to use multi level sorts + */ + depth = 1; + while (depth < nbsorts) { + if (sorts[depth] == NULL) + break; + comp = static_cast<xsltStylePreComp*>(sorts[depth]->psvi); + if (comp == NULL) + break; + desc = comp->descending; + numb = comp->number; + + /* + * Compute the result of the next level for the + * full set, this might be optimized ... or not + */ + if (resultsTab[depth] == NULL) + resultsTab[depth] = xsltComputeSortResult(ctxt, + sorts[depth]); + res = resultsTab[depth]; + if (res == NULL) + break; + if (res[j] == NULL) { + if (res[j+incr] != NULL) + tst = 1; + } else { + if (numb) { + /* We make NaN smaller than number in + accordance with XSLT spec */ + if (xmlXPathIsNaN(res[j]->floatval)) { + if (xmlXPathIsNaN(res[j + + incr]->floatval)) + tst = 0; + else + tst = -1; + } else if (xmlXPathIsNaN(res[j + incr]-> + floatval)) + tst = 1; + else if (res[j]->floatval == res[j + incr]-> + floatval) + tst = 0; + else if (res[j]->floatval > + res[j + incr]->floatval) + tst = 1; + else tst = -1; + } else { + String str1 = String::fromUTF8((const char*)res[j]->stringval); + String str2 = String::fromUTF8((const char*)res[j + incr]->stringval); + tst = collator.collate(str1.characters(), str1.length(), str2.characters(), str2.length()); + } + if (desc) + tst = -tst; + } + + /* + * if we still can't differenciate at this level + * try one level deeper. + */ + if (tst != 0) + break; + depth++; + } + } + if (tst == 0) { + tst = results[j]->index > results[j + incr]->index; + } + if (tst > 0) { + tmp = results[j]; + results[j] = results[j + incr]; + results[j + incr] = tmp; + node = list->nodeTab[j]; + list->nodeTab[j] = list->nodeTab[j + incr]; + list->nodeTab[j + incr] = node; + depth = 1; + while (depth < nbsorts) { + if (sorts[depth] == NULL) + break; + if (resultsTab[depth] == NULL) + break; + res = resultsTab[depth]; + tmp = res[j]; + res[j] = res[j + incr]; + res[j + incr] = tmp; + depth++; + } + j -= incr; + } else + break; + } + } + } + + for (j = 0; j < nbsorts; j++) { + comp = static_cast<xsltStylePreComp*>(sorts[j]->psvi); + if (tempstype[j] == 1) { + /* The data-type needs to be recomputed each time */ + xmlFree((void *)(comp->stype)); + comp->stype = NULL; + } + if (temporder[j] == 1) { + /* The order needs to be recomputed each time */ + xmlFree((void *)(comp->order)); + comp->order = NULL; + } + if (resultsTab[j] != NULL) { + for (i = 0;i < len;i++) + xmlXPathFreeObject(resultsTab[j][i]); + xmlFree(resultsTab[j]); + } + } +} + +} + +#endif diff --git a/Source/WebCore/xml/XSLTUnicodeSort.h b/Source/WebCore/xml/XSLTUnicodeSort.h new file mode 100644 index 0000000..c8d395b --- /dev/null +++ b/Source/WebCore/xml/XSLTUnicodeSort.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2007 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef XSLTUnicodeSort_h +#define XSLTUnicodeSort_h + +#if ENABLE(XSLT) + +#include <libxslt/xsltInternals.h> + +namespace WebCore { + + void xsltUnicodeSortFunction(xsltTransformContextPtr ctxt, xmlNodePtr* sorts, int nbsorts); + +} + +#endif +#endif diff --git a/Source/WebCore/xml/xmlattrs.in b/Source/WebCore/xml/xmlattrs.in new file mode 100644 index 0000000..6cc47be --- /dev/null +++ b/Source/WebCore/xml/xmlattrs.in @@ -0,0 +1,6 @@ +namespace="XML" +namespaceURI="http://www.w3.org/XML/1998/namespace" + +base +lang +space diff --git a/Source/WebCore/xml/xmlnsattrs.in b/Source/WebCore/xml/xmlnsattrs.in new file mode 100644 index 0000000..7ac415a --- /dev/null +++ b/Source/WebCore/xml/xmlnsattrs.in @@ -0,0 +1,4 @@ +namespace="XMLNS" +namespaceURI="http://www.w3.org/2000/xmlns/" + +xmlns |