diff options
author | The Android Open Source Project <initial-contribution@android.com> | 2009-03-03 19:30:52 -0800 |
---|---|---|
committer | The Android Open Source Project <initial-contribution@android.com> | 2009-03-03 19:30:52 -0800 |
commit | 8e35f3cfc7fba1d1c829dc557ebad6409cbe16a2 (patch) | |
tree | 11425ea0b299d6fb89c6d3618a22d97d5bf68d0f /WebCore/xml | |
parent | 648161bb0edfc3d43db63caed5cc5213bc6cb78f (diff) | |
download | external_webkit-8e35f3cfc7fba1d1c829dc557ebad6409cbe16a2.zip external_webkit-8e35f3cfc7fba1d1c829dc557ebad6409cbe16a2.tar.gz external_webkit-8e35f3cfc7fba1d1c829dc557ebad6409cbe16a2.tar.bz2 |
auto import from //depot/cupcake/@135843
Diffstat (limited to 'WebCore/xml')
67 files changed, 9426 insertions, 0 deletions
diff --git a/WebCore/xml/DOMParser.cpp b/WebCore/xml/DOMParser.cpp new file mode 100644 index 0000000..448dcb1 --- /dev/null +++ b/WebCore/xml/DOMParser.cpp @@ -0,0 +1,42 @@ +/* + * 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 "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, false); + + doc->open(); + doc->write(str); + doc->finishParsing(); + doc->close(); + + return doc.release(); +} + +} diff --git a/WebCore/xml/DOMParser.h b/WebCore/xml/DOMParser.h new file mode 100644 index 0000000..5036d22 --- /dev/null +++ b/WebCore/xml/DOMParser.h @@ -0,0 +1,41 @@ +/* + * 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/RefCounted.h> +#include "Document.h" + +namespace WebCore { + + class String; + + class DOMParser : public RefCounted<DOMParser> { + public: + static PassRefPtr<DOMParser> create() { return adoptRef(new DOMParser); } + + PassRefPtr<Document> parseFromString(const String& str, const String& contentType); + + private: + DOMParser() { } + }; + +} + +#endif // XMLSerializer.h diff --git a/WebCore/xml/DOMParser.idl b/WebCore/xml/DOMParser.idl new file mode 100644 index 0000000..9caaa21 --- /dev/null +++ b/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 [GenerateConstructor, CanBeConstructed] DOMParser { + Document parseFromString(in DOMString str, in DOMString contentType); + }; +} diff --git a/WebCore/xml/NativeXPathNSResolver.cpp b/WebCore/xml/NativeXPathNSResolver.cpp new file mode 100644 index 0000000..11faea6 --- /dev/null +++ b/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/WebCore/xml/NativeXPathNSResolver.h b/WebCore/xml/NativeXPathNSResolver.h new file mode 100644 index 0000000..212b929 --- /dev/null +++ b/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/WebCore/xml/XMLHttpRequest.cpp b/WebCore/xml/XMLHttpRequest.cpp new file mode 100644 index 0000000..67aba0b --- /dev/null +++ b/WebCore/xml/XMLHttpRequest.cpp @@ -0,0 +1,1317 @@ +/* + * 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> + * + * 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 "CString.h" +#include "Console.h" +#include "DOMImplementation.h" +#include "DOMWindow.h" +#include "Event.h" +#include "EventException.h" +#include "EventListener.h" +#include "EventNames.h" +#include "File.h" +#include "Frame.h" +#include "FrameLoader.h" +#include "HTTPParsers.h" +#include "InspectorController.h" +#include "JSDOMBinding.h" +#include "JSDOMWindow.h" +#include "KURL.h" +#include "KURLHash.h" +#include "Page.h" +#include "Settings.h" +#include "SubresourceLoader.h" +#include "SystemTime.h" +#include "TextResourceDecoder.h" +#include "XMLHttpRequestException.h" +#include "XMLHttpRequestProgressEvent.h" +#include "XMLHttpRequestUpload.h" +#include "markup.h" +#include <runtime/JSLock.h> + +namespace WebCore { + +struct PreflightResultCacheItem { + PreflightResultCacheItem(unsigned expiryDelta, bool credentials, HashSet<String>* methods, HashSet<String, CaseFoldingHash>* headers) + : m_absoluteExpiryTime(currentTime() + expiryDelta) + , m_credentials(credentials) + , m_methods(methods) + , m_headers(headers) + { + } + + // FIXME: A better solution to holding onto the absolute expiration time might be + // to start a timer for the expiration delta, that removes this from the cache when + // it fires. + double m_absoluteExpiryTime; + bool m_credentials; + OwnPtr<HashSet<String> > m_methods; + OwnPtr<HashSet<String, CaseFoldingHash> > m_headers; +}; + +typedef HashMap<std::pair<String, KURL>, PreflightResultCacheItem*> PreflightResultCache; + +static PreflightResultCache& preflightResultCache() +{ + static PreflightResultCache cache; + return cache; +} + +static void appendPreflightResultCacheEntry(String origin, KURL url, unsigned expiryDelta, + bool credentials, HashSet<String>* methods, HashSet<String, CaseFoldingHash>* headers) +{ + ASSERT(!preflightResultCache().contains(std::make_pair(origin, url))); + + PreflightResultCacheItem* item = new PreflightResultCacheItem(expiryDelta, credentials, methods, headers); + preflightResultCache().set(std::make_pair(origin, url), item); +} + +static bool isSafeRequestHeader(const String& name) +{ + static HashSet<String, CaseFoldingHash> forbiddenHeaders; + static String proxyString("proxy-"); + static String secString("sec-"); + + if (forbiddenHeaders.isEmpty()) { + forbiddenHeaders.add("accept-charset"); + forbiddenHeaders.add("accept-encoding"); + forbiddenHeaders.add("connection"); + forbiddenHeaders.add("content-length"); + forbiddenHeaders.add("content-transfer-encoding"); + forbiddenHeaders.add("date"); + forbiddenHeaders.add("expect"); + forbiddenHeaders.add("host"); + forbiddenHeaders.add("keep-alive"); + forbiddenHeaders.add("referer"); + forbiddenHeaders.add("te"); + forbiddenHeaders.add("trailer"); + forbiddenHeaders.add("transfer-encoding"); + forbiddenHeaders.add("upgrade"); + forbiddenHeaders.add("via"); + } + + return !forbiddenHeaders.contains(name) && !name.startsWith(proxyString, false) && + !name.startsWith(secString, false); +} + +static bool isOnAccessControlSimpleRequestHeaderWhitelist(const String& name) +{ + return equalIgnoringCase(name, "accept") || equalIgnoringCase(name, "accept-language") || equalIgnoringCase(name, "content-type"); +} + +static bool isOnAccessControlResponseHeaderWhitelist(const String& name) +{ + static HashSet<String, CaseFoldingHash> allowedHeaders; + if (allowedHeaders.isEmpty()) { + allowedHeaders.add("cache-control"); + allowedHeaders.add("content-language"); + allowedHeaders.add("content-type"); + allowedHeaders.add("expires"); + allowedHeaders.add("last-modified"); + allowedHeaders.add("pragma"); + } + + return allowedHeaders.contains(name); +} + +// 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 true; +} + +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'); +} + +XMLHttpRequest::XMLHttpRequest(Document* doc) + : ActiveDOMObject(doc, this) + , m_async(true) + , m_includeCredentials(false) + , m_state(UNSENT) + , m_identifier(std::numeric_limits<unsigned long>::max()) + , m_responseText("") + , m_createdDocument(false) + , m_error(false) + , m_uploadComplete(false) + , m_sameOriginRequest(true) + , m_inPreflight(false) + , m_receivedLength(0) + , m_lastSendLineNumber(0) +{ + ASSERT(document()); +} + +XMLHttpRequest::~XMLHttpRequest() +{ + if (m_upload) + m_upload->disconnectXMLHttpRequest(); +} + +Document* XMLHttpRequest::document() const +{ + ASSERT(scriptExecutionContext()->isDocument()); + return static_cast<Document*>(scriptExecutionContext()); +} + +XMLHttpRequest::State XMLHttpRequest::readyState() const +{ + return m_state; +} + +const JSC::UString& XMLHttpRequest::responseText() const +{ + return m_responseText; +} + +Document* XMLHttpRequest::responseXML() const +{ + if (m_state != DONE) + return 0; + + if (!m_createdDocument) { + if (m_response.isHTTP() && !responseIsXML()) { + // The W3C spec requires this. + m_responseXML = 0; + } else { + m_responseXML = document()->implementation()->createDocument(0); + m_responseXML->open(); + m_responseXML->setURL(m_url); + // FIXME: set Last-Modified and cookies (currently, those are only available for HTMLDocuments). + m_responseXML->write(String(m_responseText)); + m_responseXML->finishParsing(); + m_responseXML->close(); + + if (!m_responseXML->wellFormed()) + m_responseXML = 0; + } + m_createdDocument = true; + } + + return m_responseXML.get(); +} + +XMLHttpRequestUpload* XMLHttpRequest::upload() +{ + if (!m_upload) + m_upload = XMLHttpRequestUpload::create(this); + return m_upload.get(); +} + +void XMLHttpRequest::addEventListener(const AtomicString& eventType, PassRefPtr<EventListener> eventListener, bool) +{ + EventListenersMap::iterator iter = m_eventListeners.find(eventType); + if (iter == m_eventListeners.end()) { + ListenerVector listeners; + listeners.append(eventListener); + m_eventListeners.add(eventType, listeners); + } else { + ListenerVector& listeners = iter->second; + for (ListenerVector::iterator listenerIter = listeners.begin(); listenerIter != listeners.end(); ++listenerIter) + if (*listenerIter == eventListener) + return; + + listeners.append(eventListener); + m_eventListeners.add(eventType, listeners); + } +} + +void XMLHttpRequest::removeEventListener(const AtomicString& eventType, EventListener* eventListener, bool) +{ + EventListenersMap::iterator iter = m_eventListeners.find(eventType); + if (iter == m_eventListeners.end()) + return; + + ListenerVector& listeners = iter->second; + for (ListenerVector::const_iterator listenerIter = listeners.begin(); listenerIter != listeners.end(); ++listenerIter) + if (*listenerIter == eventListener) { + listeners.remove(listenerIter - listeners.begin()); + return; + } +} + +bool XMLHttpRequest::dispatchEvent(PassRefPtr<Event> evt, ExceptionCode& ec) +{ + // FIXME: check for other error conditions enumerated in the spec. + if (evt->type().isEmpty()) { + ec = EventException::UNSPECIFIED_EVENT_TYPE_ERR; + return true; + } + + ListenerVector listenersCopy = m_eventListeners.get(evt->type()); + for (ListenerVector::const_iterator listenerIter = listenersCopy.begin(); listenerIter != listenersCopy.end(); ++listenerIter) { + evt->setTarget(this); + evt->setCurrentTarget(this); + listenerIter->get()->handleEvent(evt.get(), false); + } + + return !evt->defaultPrevented(); +} + +void XMLHttpRequest::changeState(State newState) +{ + if (m_state != newState) { + m_state = newState; + callReadyStateChangeListener(); + } +} + +void XMLHttpRequest::callReadyStateChangeListener() +{ + if (!document() || !document()->frame()) + return; + + dispatchReadyStateChangeEvent(); + + if (m_state == DONE) + dispatchLoadEvent(); +} + +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_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 (!document()) + 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.protocolIs("http") || m_url.protocolIs("https"))) { + String contentType = getRequestHeader("Content-Type"); + if (contentType.isEmpty()) { +#if ENABLE(DASHBOARD_SUPPORT) + Settings* settings = document->settings(); + if (settings && settings->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.protocolIs("http") || m_url.protocolIs("https"))) { + String contentType = getRequestHeader("Content-Type"); + if (contentType.isEmpty()) { +#if ENABLE(DASHBOARD_SUPPORT) + Settings* settings = document()->settings(); + if (settings && settings->usesDashboardBackwardCompatibilityMode()) + setRequestHeaderInternal("Content-Type", "application/x-www-form-urlencoded"); + else +#endif + setRequestHeaderInternal("Content-Type", "application/xml"); + } + + m_requestEntityBody = FormData::create(UTF8Encoding().encode(body.characters(), body.length(), EntitiesForUnencodables)); + if (m_upload) + m_requestEntityBody->setAlwaysStream(true); + } + + createRequest(ec); +} + +void XMLHttpRequest::send(File* body, ExceptionCode& ec) +{ + if (!initSend(ec)) + return; + + if (m_method != "GET" && m_method != "HEAD" && (m_url.protocolIs("http") || m_url.protocolIs("https"))) { + // FIXME: Should we set a Content-Type if one is not set. + // FIXME: add support for uploading bundles. + m_requestEntityBody = FormData::create(); + m_requestEntityBody->appendFile(body->path(), false); + } + + createRequest(ec); +} + +void XMLHttpRequest::createRequest(ExceptionCode& ec) +{ + if (m_async) { + dispatchLoadStartEvent(); + if (m_requestEntityBody && m_upload) + m_upload->dispatchLoadStartEvent(); + } + + m_sameOriginRequest = document()->securityOrigin()->canRequest(m_url); + + if (!m_sameOriginRequest) { + makeCrossSiteAccessRequest(ec); + return; + } + + makeSameOriginRequest(ec); +} + +void XMLHttpRequest::makeSameOriginRequest(ExceptionCode& ec) +{ + ASSERT(m_sameOriginRequest); + + ResourceRequest request(m_url); + request.setHTTPMethod(m_method); + + if (m_requestEntityBody) { + ASSERT(m_method != "GET"); + request.setHTTPBody(m_requestEntityBody.release()); + } + + if (m_requestHeaders.size() > 0) + request.addHTTPHeaderFields(m_requestHeaders); + + if (m_async) + loadRequestAsynchronously(request); + else + loadRequestSynchronously(request, ec); +} + +bool XMLHttpRequest::isSimpleCrossSiteAccessRequest() const +{ + if (m_method != "GET" && m_method != "POST") + return false; + + HTTPHeaderMap::const_iterator end = m_requestHeaders.end(); + for (HTTPHeaderMap::const_iterator it = m_requestHeaders.begin(); it != end; ++it) { + if (!isOnAccessControlSimpleRequestHeaderWhitelist(it->first)) + return false; + } + + return true; +} + +void XMLHttpRequest::makeCrossSiteAccessRequest(ExceptionCode& ec) +{ + ASSERT(!m_sameOriginRequest); + + if (isSimpleCrossSiteAccessRequest()) + makeSimpleCrossSiteAccessRequest(ec); + else + makeCrossSiteAccessRequestWithPreflight(ec); +} + +void XMLHttpRequest::makeSimpleCrossSiteAccessRequest(ExceptionCode& ec) +{ + ASSERT(isSimpleCrossSiteAccessRequest()); + + KURL url = m_url; + url.setUser(String()); + url.setPass(String()); + + ResourceRequest request(url); + request.setHTTPMethod(m_method); + request.setAllowHTTPCookies(m_includeCredentials); + request.setHTTPOrigin(document()->securityOrigin()->toString()); + + if (m_requestHeaders.size() > 0) + request.addHTTPHeaderFields(m_requestHeaders); + + if (m_async) + loadRequestAsynchronously(request); + else + loadRequestSynchronously(request, ec); +} + +static bool canSkipPrelight(PreflightResultCache::iterator cacheIt, bool includeCredentials, const String& method, const HTTPHeaderMap& requestHeaders) +{ + PreflightResultCacheItem* item = cacheIt->second; + if (item->m_absoluteExpiryTime < currentTime()) + return false; + if (includeCredentials && !item->m_credentials) + return false; + if (!item->m_methods->contains(method) && method != "GET" && method != "POST") + return false; + HTTPHeaderMap::const_iterator end = requestHeaders.end(); + for (HTTPHeaderMap::const_iterator it = requestHeaders.begin(); it != end; ++it) { + if (!item->m_headers->contains(it->first) && !isOnAccessControlSimpleRequestHeaderWhitelist(it->first)) + return false; + } + + return true; +} + +void XMLHttpRequest::makeCrossSiteAccessRequestWithPreflight(ExceptionCode& ec) +{ + String origin = document()->securityOrigin()->toString(); + KURL url = m_url; + url.setUser(String()); + url.setPass(String()); + + bool skipPreflight = false; + + PreflightResultCache::iterator cacheIt = preflightResultCache().find(std::make_pair(origin, url)); + if (cacheIt != preflightResultCache().end()) { + skipPreflight = canSkipPrelight(cacheIt, m_includeCredentials, m_method, m_requestHeaders); + if (!skipPreflight) { + delete cacheIt->second; + preflightResultCache().remove(cacheIt); + } + } + + if (!skipPreflight) { + m_inPreflight = true; + ResourceRequest preflightRequest(url); + preflightRequest.setHTTPMethod("OPTIONS"); + preflightRequest.setHTTPHeaderField("Origin", origin); + preflightRequest.setHTTPHeaderField("Access-Control-Request-Method", m_method); + + if (m_requestHeaders.size() > 0) { + Vector<UChar> headerBuffer; + HTTPHeaderMap::const_iterator it = m_requestHeaders.begin(); + append(headerBuffer, it->first); + ++it; + + HTTPHeaderMap::const_iterator end = m_requestHeaders.end(); + for (; it != end; ++it) { + headerBuffer.append(','); + headerBuffer.append(' '); + append(headerBuffer, it->first); + } + + preflightRequest.setHTTPHeaderField("Access-Control-Request-Headers", String::adopt(headerBuffer)); + preflightRequest.addHTTPHeaderFields(m_requestHeaders); + } + + if (m_async) { + loadRequestAsynchronously(preflightRequest); + return; + } + + loadRequestSynchronously(preflightRequest, ec); + m_inPreflight = false; + + if (ec) + return; + } + + // Send the actual request. + ResourceRequest request(url); + request.setHTTPMethod(m_method); + request.setAllowHTTPCookies(m_includeCredentials); + request.setHTTPHeaderField("Origin", origin); + + if (m_requestHeaders.size() > 0) + request.addHTTPHeaderFields(m_requestHeaders); + + if (m_requestEntityBody) { + ASSERT(m_method != "GET"); + request.setHTTPBody(m_requestEntityBody.release()); + } + + if (m_async) { + loadRequestAsynchronously(request); + return; + } + + loadRequestSynchronously(request, ec); +} + +void XMLHttpRequest::handleAsynchronousPreflightResult() +{ + ASSERT(m_inPreflight); + ASSERT(m_async); + + m_inPreflight = false; + + KURL url = m_url; + url.setUser(String()); + url.setPass(String()); + + ResourceRequest request(url); + request.setHTTPMethod(m_method); + request.setAllowHTTPCookies(m_includeCredentials); + request.setHTTPOrigin(document()->securityOrigin()->toString()); + + if (m_requestHeaders.size() > 0) + request.addHTTPHeaderFields(m_requestHeaders); + + if (m_requestEntityBody) { + ASSERT(m_method != "GET"); + request.setHTTPBody(m_requestEntityBody.release()); + } + + loadRequestAsynchronously(request); +} + +void XMLHttpRequest::loadRequestSynchronously(ResourceRequest& request, ExceptionCode& ec) +{ + ASSERT(!m_async); + Vector<char> data; + ResourceError error; + ResourceResponse response; + + if (document()->frame()) + m_identifier = document()->frame()->loader()->loadResourceSynchronously(request, error, response, data); + + m_loader = 0; + + // No exception for file:/// resources, see <rdar://problem/4962298>. + // Also, if we have an HTTP response, then it wasn't a network error in fact. + if (error.isNull() || request.url().isLocalFile() || response.httpStatusCode() > 0) { + processSyncLoadResults(data, response, ec); + return; + } + + if (error.isCancellation()) { + abortError(); + ec = XMLHttpRequestException::ABORT_ERR; + return; + } + + networkError(); + ec = XMLHttpRequestException::NETWORK_ERR; +} + + +void XMLHttpRequest::loadRequestAsynchronously(ResourceRequest& request) +{ + ASSERT(m_async); + // SubresourceLoader::create can return null here, for example if we're no longer attached to a page. + // This is true while running onunload handlers. + // FIXME: 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? + // We need to keep content sniffing enabled for local files due to CFNetwork not providing a MIME type + // for local files otherwise, <rdar://problem/5671813>. + bool sendResourceLoadCallbacks = !m_inPreflight; + m_loader = SubresourceLoader::create(document()->frame(), this, request, false, sendResourceLoadCallbacks, request.url().isLocalFile()); + + 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); + } +} + +void XMLHttpRequest::abort() +{ + bool sendFlag = m_loader; + + internalAbort(); + + // 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; + } + + dispatchAbortEvent(); + if (!m_uploadComplete) { + m_uploadComplete = true; + if (m_upload) + m_upload->dispatchAbortEvent(); + } +} + +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(); + { + JSC::JSLock lock(false); + m_responseText = ""; + } + m_createdDocument = false; + m_responseXML = 0; +} + +void XMLHttpRequest::clearRequest() +{ + m_requestHeaders.clear(); + m_requestEntityBody = 0; +} + +void XMLHttpRequest::genericError() +{ + clearResponse(); + clearRequest(); + m_error = true; + + // The spec says we should "Synchronously switch the state to DONE." and then "Synchronously dispatch a readystatechange event on the object" + // but this does not match Firefox. +} + +void XMLHttpRequest::networkError() +{ + genericError(); + dispatchErrorEvent(); + if (!m_uploadComplete) { + m_uploadComplete = true; + if (m_upload) + m_upload->dispatchErrorEvent(); + } +} + +void XMLHttpRequest::abortError() +{ + genericError(); + dispatchAbortEvent(); + if (!m_uploadComplete) { + m_uploadComplete = true; + if (m_upload) + m_upload->dispatchAbortEvent(); + } +} + +void XMLHttpRequest::dropProtection() +{ + // 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. + + if (JSDOMWindow* window = toJSDOMWindow(document()->frame())) { + if (JSC::JSValue* wrapper = getCachedDOMObjectWrapper(*window->globalData(), this)) + JSC::Heap::heap(wrapper)->reportExtraMemoryCost(m_responseText.size() * 2); + } + + unsetPendingActivity(this); +} + +void XMLHttpRequest::overrideMimeType(const String& override) +{ + m_mimeTypeOverride = override; +} + +void XMLHttpRequest::setRequestHeader(const String& name, const String& value, ExceptionCode& ec) +{ + if (m_state != OPENED || m_loader) { +#if ENABLE(DASHBOARD_SUPPORT) + Settings* settings = document() ? document()->settings() : 0; + if (settings && settings->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 (!document()->securityOrigin()->canLoadLocalResources() && !isSafeRequestHeader(name)) { + if (document() && document()->frame()) + document()->frame()->domWindow()->console()->addMessage(JSMessageSource, ErrorMessageLevel, "Refused to set unsafe header \"" + name + "\"", 1, String()); + return; + } + + setRequestHeaderInternal(name, value); +} + +void XMLHttpRequest::setRequestHeaderInternal(const String& name, const String& value) +{ + pair<HTTPHeaderMap::iterator, bool> result = m_requestHeaders.add(name, value); + if (!result.second) + result.first->second += ", " + value; +} + +String XMLHttpRequest::getRequestHeader(const String& name) const +{ + return m_requestHeaders.get(name); +} + +String XMLHttpRequest::getAllResponseHeaders(ExceptionCode& ec) const +{ + if (m_state < LOADING) { + ec = INVALID_STATE_ERR; + return ""; + } + + Vector<UChar> stringBuilder; + String separator(": "); + + HTTPHeaderMap::const_iterator end = m_response.httpHeaderFields().end(); + for (HTTPHeaderMap::const_iterator it = m_response.httpHeaderFields().begin(); it!= end; ++it) { + if (!m_sameOriginRequest && !isOnAccessControlResponseHeaderWhitelist(it->first)) + continue; + + stringBuilder.append(it->first.characters(), it->first.length()); + stringBuilder.append(separator.characters(), separator.length()); + stringBuilder.append(it->second.characters(), it->second.length()); + stringBuilder.append((UChar)'\r'); + stringBuilder.append((UChar)'\n'); + } + + return String::adopt(stringBuilder); +} + +String XMLHttpRequest::getResponseHeader(const String& name, ExceptionCode& ec) const +{ + if (m_state < LOADING) { + ec = INVALID_STATE_ERR; + return ""; + } + + if (!isValidToken(name)) + return ""; + + if (!m_sameOriginRequest && !isOnAccessControlResponseHeaderWhitelist(name)) + return ""; + + 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 +{ + // FIXME: <http://bugs.webkit.org/show_bug.cgi?id=3547> XMLHttpRequest.statusText returns always "OK". + if (m_response.httpStatusCode()) + return "OK"; + + if (m_state == OPENED) { + // See comments in getStatus() above. + ec = INVALID_STATE_ERR; + } + + return String(); +} + +void XMLHttpRequest::processSyncLoadResults(const Vector<char>& data, const ResourceResponse& response, ExceptionCode& ec) +{ + if (m_sameOriginRequest && !document()->securityOrigin()->canRequest(response.url())) { + abort(); + return; + } + + didReceiveResponse(0, response); + changeState(HEADERS_RECEIVED); + + const char* bytes = static_cast<const char*>(data.data()); + int len = static_cast<int>(data.size()); + didReceiveData(0, bytes, len); + + didFinishLoading(0); + if (m_error) + ec = XMLHttpRequestException::NETWORK_ERR; +} + +void XMLHttpRequest::didFail(SubresourceLoader* loader, 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()) { + abortError(); + return; + } + + networkError(); + return; +} + +void XMLHttpRequest::didFinishLoading(SubresourceLoader* loader) +{ + if (m_error) + return; + + if (m_inPreflight) { + didFinishLoadingPreflight(loader); + return; + } + + ASSERT(loader == m_loader); + + if (m_state < HEADERS_RECEIVED) + changeState(HEADERS_RECEIVED); + + { + JSC::JSLock lock(false); + if (m_decoder) + m_responseText += m_decoder->flush(); + } + + if (Frame* frame = document()->frame()) { + if (Page* page = frame->page()) { + page->inspectorController()->resourceRetrievedByXMLHttpRequest(m_loader ? m_loader->identifier() : m_identifier, m_responseText); + page->inspectorController()->addMessageToConsole(JSMessageSource, LogMessageLevel, "XHR finished loading: \"" + m_url + "\".", m_lastSendLineNumber, m_lastSendURL); + } + } + + bool hadLoader = m_loader; + m_loader = 0; + + changeState(DONE); + m_decoder = 0; + + if (hadLoader) + dropProtection(); +} + +void XMLHttpRequest::didFinishLoadingPreflight(SubresourceLoader* loader) +{ + ASSERT(m_inPreflight); + ASSERT(!m_sameOriginRequest); + + // FIXME: this can probably be moved to didReceiveResponsePreflight. + if (m_async) + handleAsynchronousPreflightResult(); + + if (m_loader) + unsetPendingActivity(this); +} + +void XMLHttpRequest::willSendRequest(SubresourceLoader*, ResourceRequest& request, const ResourceResponse& redirectResponse) +{ + // FIXME: This needs to be fixed to follow the redirect correctly even for cross-domain requests. + if (!document()->securityOrigin()->canRequest(request.url())) { + internalAbort(); + networkError(); + } +} + +void XMLHttpRequest::didSendData(SubresourceLoader*, unsigned long long bytesSent, unsigned long long totalBytesToBeSent) +{ + if (!m_upload) + return; + + m_upload->dispatchProgressEvent(bytesSent, totalBytesToBeSent); + + if (bytesSent == totalBytesToBeSent && !m_uploadComplete) { + m_uploadComplete = true; + m_upload->dispatchLoadEvent(); + } +} + +bool XMLHttpRequest::accessControlCheck(const ResourceResponse& response) +{ + const String& accessControlOriginString = response.httpHeaderField("Access-Control-Origin"); + if (accessControlOriginString == "*" && !m_includeCredentials) + return true; + + KURL accessControlOriginURL(accessControlOriginString); + if (!accessControlOriginURL.isValid()) + return false; + + RefPtr<SecurityOrigin> accessControlOrigin = SecurityOrigin::create(accessControlOriginURL); + if (!accessControlOrigin->isSameSchemeHostPort(document()->securityOrigin())) + return false; + + if (m_includeCredentials) { + const String& accessControlCredentialsString = response.httpHeaderField("Access-Control-Credentials"); + if (accessControlCredentialsString != "true") + return false; + } + + return true; +} + +void XMLHttpRequest::didReceiveResponse(SubresourceLoader* loader, const ResourceResponse& response) +{ + if (m_inPreflight) { + didReceiveResponsePreflight(loader, response); + return; + } + + if (!m_sameOriginRequest) { + if (!accessControlCheck(response)) { + networkError(); + return; + } + } + + m_response = response; + m_responseEncoding = extractCharsetFromMediaType(m_mimeTypeOverride); + if (m_responseEncoding.isEmpty()) + m_responseEncoding = response.textEncodingName(); +} + +template<class HashType> +static bool parseAccessControlAllowList(const String& string, HashSet<String, HashType>* set) +{ + int start = 0; + int end; + while ((end = string.find(',', start)) != -1) { + if (start == end) + return false; + + // FIXME: this could be made more efficient by not not allocating twice. + set->add(string.substring(start, end - start).stripWhiteSpace()); + start = end + 1; + } + if (start != static_cast<int>(string.length())) + set->add(string.substring(start).stripWhiteSpace()); + + return true; +} + +static bool parseAccessControlMaxAge(const String& string, unsigned& expiryDelta) +{ + // FIXME: this will not do the correct thing for a number starting with a '+' + bool ok = false; + expiryDelta = string.toUIntStrict(&ok); + return ok; +} + +void XMLHttpRequest::didReceiveResponsePreflight(SubresourceLoader*, const ResourceResponse& response) +{ + ASSERT(m_inPreflight); + ASSERT(!m_sameOriginRequest); + + if (!accessControlCheck(response)) { + networkError(); + return; + } + + OwnPtr<HashSet<String> > methods(new HashSet<String>); + if (!parseAccessControlAllowList(response.httpHeaderField("Access-Control-Allow-Methods"), methods.get())) { + networkError(); + return; + } + + if (!methods->contains(m_method) && m_method != "GET" && m_method != "POST") { + networkError(); + return; + } + + OwnPtr<HashSet<String, CaseFoldingHash> > headers(new HashSet<String, CaseFoldingHash>); + if (!parseAccessControlAllowList(response.httpHeaderField("Access-Control-Allow-Headers"), headers.get())) { + networkError(); + return; + } + + HTTPHeaderMap::const_iterator end = m_requestHeaders.end(); + for (HTTPHeaderMap::const_iterator it = m_requestHeaders.begin(); it != end; ++it) { + if (!headers->contains(it->first) && !isOnAccessControlSimpleRequestHeaderWhitelist(it->first)) { + networkError(); + return; + } + } + + unsigned expiryDelta = 0; + if (!parseAccessControlMaxAge(response.httpHeaderField("Access-Control-Max-Age"), expiryDelta)) + expiryDelta = 5; + + appendPreflightResultCacheEntry(document()->securityOrigin()->toString(), m_url, expiryDelta, m_includeCredentials, methods.release(), headers.release()); +} + +void XMLHttpRequest::receivedCancellation(SubresourceLoader*, const AuthenticationChallenge& challenge) +{ + m_response = challenge.failureResponse(); +} + +void XMLHttpRequest::didReceiveData(SubresourceLoader*, const char* data, int len) +{ + if (m_inPreflight) + return; + + if (m_state < HEADERS_RECEIVED) + changeState(HEADERS_RECEIVED); + + if (!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"); + else if (responseMIMEType() == "text/html") + m_decoder = TextResourceDecoder::create("text/html", "UTF-8"); + else + m_decoder = TextResourceDecoder::create("text/plain", "UTF-8"); + } + if (len == 0) + return; + + if (len == -1) + len = strlen(data); + + String decoded = m_decoder->decode(data, len); + + { + JSC::JSLock lock(false); + m_responseText += decoded; + } + + if (!m_error) { + updateAndDispatchOnProgress(len); + + if (m_state != LOADING) + changeState(LOADING); + else + // Firefox calls readyStateChanged every time it receives data, 4449442 + callReadyStateChangeListener(); + } +} + +void XMLHttpRequest::updateAndDispatchOnProgress(unsigned int len) +{ + long long expectedLength = m_response.expectedContentLength(); + m_receivedLength += len; + + // FIXME: the spec requires that we dispatch the event according to the least + // frequent method between every 350ms (+/-200ms) and for every byte received. + dispatchProgressEvent(expectedLength); +} + +void XMLHttpRequest::dispatchReadyStateChangeEvent() +{ + RefPtr<Event> evt = Event::create(eventNames().readystatechangeEvent, false, false); + if (m_onReadyStateChangeListener) { + evt->setTarget(this); + evt->setCurrentTarget(this); + m_onReadyStateChangeListener->handleEvent(evt.get(), false); + } + + ExceptionCode ec = 0; + dispatchEvent(evt.release(), ec); + ASSERT(!ec); +} + +void XMLHttpRequest::dispatchXMLHttpRequestProgressEvent(EventListener* listener, const AtomicString& type, bool lengthComputable, unsigned loaded, unsigned total) +{ + RefPtr<XMLHttpRequestProgressEvent> evt = XMLHttpRequestProgressEvent::create(type, lengthComputable, loaded, total); + if (listener) { + evt->setTarget(this); + evt->setCurrentTarget(this); + listener->handleEvent(evt.get(), false); + } + + ExceptionCode ec = 0; + dispatchEvent(evt.release(), ec); + ASSERT(!ec); +} + +void XMLHttpRequest::dispatchAbortEvent() +{ + dispatchXMLHttpRequestProgressEvent(m_onAbortListener.get(), eventNames().abortEvent, false, 0, 0); +} + +void XMLHttpRequest::dispatchErrorEvent() +{ + dispatchXMLHttpRequestProgressEvent(m_onErrorListener.get(), eventNames().errorEvent, false, 0, 0); +} + +void XMLHttpRequest::dispatchLoadEvent() +{ + dispatchXMLHttpRequestProgressEvent(m_onLoadListener.get(), eventNames().loadEvent, false, 0, 0); +} + +void XMLHttpRequest::dispatchLoadStartEvent() +{ + dispatchXMLHttpRequestProgressEvent(m_onLoadStartListener.get(), eventNames().loadstartEvent, false, 0, 0); +} + +void XMLHttpRequest::dispatchProgressEvent(long long expectedLength) +{ + dispatchXMLHttpRequestProgressEvent(m_onProgressListener.get(), eventNames().progressEvent, expectedLength && m_receivedLength <= expectedLength, + static_cast<unsigned>(m_receivedLength), static_cast<unsigned>(expectedLength)); +} + +void XMLHttpRequest::stop() +{ + internalAbort(); +} + +void XMLHttpRequest::contextDestroyed() +{ + ActiveDOMObject::contextDestroyed(); + internalAbort(); +} + +ScriptExecutionContext* XMLHttpRequest::scriptExecutionContext() const +{ + return ActiveDOMObject::scriptExecutionContext(); +} + +} // namespace WebCore diff --git a/WebCore/xml/XMLHttpRequest.h b/WebCore/xml/XMLHttpRequest.h new file mode 100644 index 0000000..6962ab1 --- /dev/null +++ b/WebCore/xml/XMLHttpRequest.h @@ -0,0 +1,232 @@ +/* + * 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 "AtomicStringHash.h" +#include "EventListener.h" +#include "EventTarget.h" +#include "FormData.h" +#include "ResourceResponse.h" +#include "SubresourceLoaderClient.h" +#include <wtf/OwnPtr.h> + +namespace WebCore { + +class Document; +class File; +class TextResourceDecoder; + +class XMLHttpRequest : public RefCounted<XMLHttpRequest>, public EventTarget, private SubresourceLoaderClient, public ActiveDOMObject { +public: + static PassRefPtr<XMLHttpRequest> create(Document* document) { return adoptRef(new XMLHttpRequest(document)); } + ~XMLHttpRequest(); + + // These exact numeric values are important because JS expects them. + enum State { + UNSENT = 0, + OPENED = 1, + HEADERS_RECEIVED = 2, + LOADING = 3, + DONE = 4 + }; + + virtual XMLHttpRequest* toXMLHttpRequest() { return this; } + + virtual void contextDestroyed(); + virtual void stop(); + + virtual ScriptExecutionContext* scriptExecutionContext() const; + + String statusText(ExceptionCode&) const; + int status(ExceptionCode&) const; + State readyState() const; + 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(File*, ExceptionCode&); + void abort(); + void setRequestHeader(const String& name, const String& value, ExceptionCode&); + void overrideMimeType(const String& override); + String getAllResponseHeaders(ExceptionCode&) const; + String getResponseHeader(const String& name, ExceptionCode&) const; + const JSC::UString& responseText() const; + Document* responseXML() const; + void setLastSendLineNumber(unsigned lineNumber) { m_lastSendLineNumber = lineNumber; } + void setLastSendURL(JSC::UString url) { m_lastSendURL = url; } + + XMLHttpRequestUpload* upload(); + XMLHttpRequestUpload* optionalUpload() const { return m_upload.get(); } + + void setOnreadystatechange(PassRefPtr<EventListener> eventListener) { m_onReadyStateChangeListener = eventListener; } + EventListener* onreadystatechange() const { return m_onReadyStateChangeListener.get(); } + + void setOnabort(PassRefPtr<EventListener> eventListener) { m_onAbortListener = eventListener; } + EventListener* onabort() const { return m_onAbortListener.get(); } + + void setOnerror(PassRefPtr<EventListener> eventListener) { m_onErrorListener = eventListener; } + EventListener* onerror() const { return m_onErrorListener.get(); } + + void setOnload(PassRefPtr<EventListener> eventListener) { m_onLoadListener = eventListener; } + EventListener* onload() const { return m_onLoadListener.get(); } + + void setOnloadstart(PassRefPtr<EventListener> eventListener) { m_onLoadStartListener = eventListener; } + EventListener* onloadstart() const { return m_onLoadStartListener.get(); } + + void setOnprogress(PassRefPtr<EventListener> eventListener) { m_onProgressListener = eventListener; } + EventListener* onprogress() const { return m_onProgressListener.get(); } + + typedef Vector<RefPtr<EventListener> > ListenerVector; + typedef HashMap<AtomicString, ListenerVector> EventListenersMap; + + // useCapture is not used, even for add/remove pairing (for Firefox compatibility). + virtual void addEventListener(const AtomicString& eventType, PassRefPtr<EventListener>, bool useCapture); + virtual void removeEventListener(const AtomicString& eventType, EventListener*, bool useCapture); + virtual bool dispatchEvent(PassRefPtr<Event>, ExceptionCode&); + EventListenersMap& eventListeners() { return m_eventListeners; } + + using RefCounted<XMLHttpRequest>::ref; + using RefCounted<XMLHttpRequest>::deref; + +private: + XMLHttpRequest(Document*); + + virtual void refEventTarget() { ref(); } + virtual void derefEventTarget() { deref(); } + + Document* document() const; + + virtual void willSendRequest(SubresourceLoader*, ResourceRequest& request, const ResourceResponse& redirectResponse); + virtual void didSendData(SubresourceLoader*, unsigned long long bytesSent, unsigned long long totalBytesToBeSent); + virtual void didReceiveResponse(SubresourceLoader*, const ResourceResponse&); + virtual void didReceiveData(SubresourceLoader*, const char* data, int size); + virtual void didFail(SubresourceLoader*, const ResourceError&); + virtual void didFinishLoading(SubresourceLoader*); + virtual void receivedCancellation(SubresourceLoader*, const AuthenticationChallenge&); + + // Special versions for the preflight + void didReceiveResponsePreflight(SubresourceLoader*, const ResourceResponse&); + void didFinishLoadingPreflight(SubresourceLoader*); + + void processSyncLoadResults(const Vector<char>& data, const ResourceResponse&, ExceptionCode&); + void updateAndDispatchOnProgress(unsigned int len); + + String responseMIMEType() const; + bool responseIsXML() const; + + bool initSend(ExceptionCode&); + + String getRequestHeader(const String& name) const; + void setRequestHeaderInternal(const String& name, const String& value); + + void changeState(State newState); + void callReadyStateChangeListener(); + void dropProtection(); + void internalAbort(); + void clearResponse(); + void clearRequest(); + + void createRequest(ExceptionCode&); + + void makeSameOriginRequest(ExceptionCode&); + void makeCrossSiteAccessRequest(ExceptionCode&); + + void makeSimpleCrossSiteAccessRequest(ExceptionCode&); + void makeCrossSiteAccessRequestWithPreflight(ExceptionCode&); + void handleAsynchronousPreflightResult(); + + void loadRequestSynchronously(ResourceRequest&, ExceptionCode&); + void loadRequestAsynchronously(ResourceRequest&); + + bool isSimpleCrossSiteAccessRequest() const; + String accessControlOrigin() const; + bool accessControlCheck(const ResourceResponse&); + + void genericError(); + void networkError(); + void abortError(); + + void dispatchReadyStateChangeEvent(); + void dispatchXMLHttpRequestProgressEvent(EventListener* listener, const AtomicString& type, bool lengthComputable, unsigned loaded, unsigned total); + void dispatchAbortEvent(); + void dispatchErrorEvent(); + void dispatchLoadEvent(); + void dispatchLoadStartEvent(); + void dispatchProgressEvent(long long expectedLength); + + RefPtr<EventListener> m_onReadyStateChangeListener; + RefPtr<EventListener> m_onAbortListener; + RefPtr<EventListener> m_onErrorListener; + RefPtr<EventListener> m_onLoadListener; + RefPtr<EventListener> m_onLoadStartListener; + RefPtr<EventListener> m_onProgressListener; + EventListenersMap m_eventListeners; + + 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; + + RefPtr<SubresourceLoader> m_loader; + State m_state; + + ResourceResponse m_response; + String m_responseEncoding; + + RefPtr<TextResourceDecoder> m_decoder; + unsigned long m_identifier; + + // Unlike most strings in the DOM, we keep this as a JSC::UString, not a WebCore::String. + // That's because these strings can easily get huge (they are filled from the network with + // no parsing) and because JS can easily observe many intermediate states, so it's very useful + // to be able to share the buffer with JavaScript versions of the whole or partial string. + // In contrast, this string doesn't interact much with the rest of the engine so it's not that + // big a cost that it isn't a String. + JSC::UString m_responseText; + mutable bool m_createdDocument; + mutable RefPtr<Document> m_responseXML; + + bool m_error; + + bool m_uploadComplete; + + bool m_sameOriginRequest; + bool m_allowAccess; + bool m_inPreflight; + + // Used for onprogress tracking + long long m_receivedLength; + + unsigned m_lastSendLineNumber; + JSC::UString m_lastSendURL; +}; + +} // namespace WebCore + +#endif // XMLHttpRequest_h diff --git a/WebCore/xml/XMLHttpRequest.idl b/WebCore/xml/XMLHttpRequest.idl new file mode 100644 index 0000000..fa6b9ca --- /dev/null +++ b/WebCore/xml/XMLHttpRequest.idl @@ -0,0 +1,99 @@ +/* + * 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 + ] 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 + // 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); + + [Custom] 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); + [Custom, ConvertNullStringTo=Null] DOMString getResponseHeader(in DOMString header) + raises(DOMException); + readonly attribute [ConvertNullStringTo=Null] DOMString responseText; + readonly attribute Document responseXML; + readonly attribute unsigned short status + getter raises(DOMException); + readonly attribute DOMString statusText + getter raises(DOMException); + + // Extension + [Custom] void overrideMimeType(in DOMString override); + + // EventTarget interface + [Custom] void addEventListener(in DOMString type, + in EventListener listener, + in boolean useCapture); + [Custom] void removeEventListener(in DOMString type, + in EventListener listener, + in boolean useCapture); + boolean dispatchEvent(in Event evt) + raises(EventException); + }; + +} diff --git a/WebCore/xml/XMLHttpRequestException.h b/WebCore/xml/XMLHttpRequestException.h new file mode 100644 index 0000000..737cab0 --- /dev/null +++ b/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/WebCore/xml/XMLHttpRequestException.idl b/WebCore/xml/XMLHttpRequestException.idl new file mode 100644 index 0000000..2feb574 --- /dev/null +++ b/WebCore/xml/XMLHttpRequestException.idl @@ -0,0 +1,49 @@ +/* + * 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 [ + GenerateConstructor + ] XMLHttpRequestException { + + readonly attribute unsigned short code; + readonly attribute DOMString name; + readonly attribute DOMString message; + +#if defined(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/WebCore/xml/XMLHttpRequestProgressEvent.h b/WebCore/xml/XMLHttpRequestProgressEvent.h new file mode 100644 index 0000000..02bfdea --- /dev/null +++ b/WebCore/xml/XMLHttpRequestProgressEvent.h @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2008 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, unsigned loaded, unsigned total) + { + return adoptRef(new XMLHttpRequestProgressEvent(type, lengthComputable, loaded, total)); + } + + virtual bool isXMLHttpRequestProgressEvent() const { return true; } + + // Those 2 methods are to be compatible with Firefox and are only a wrapper on top of the real implementation. + unsigned position() const { return loaded(); } + unsigned totalSize() const { return total(); } + + private: + XMLHttpRequestProgressEvent() { } + XMLHttpRequestProgressEvent(const AtomicString& type, bool lengthComputable, unsigned loaded, unsigned total) + : ProgressEvent(type, lengthComputable, loaded, total) + { + } + }; + +} // namespace WebCore + +#endif // XMLHttpRequestProgressEvent_h diff --git a/WebCore/xml/XMLHttpRequestProgressEvent.idl b/WebCore/xml/XMLHttpRequestProgressEvent.idl new file mode 100644 index 0000000..6de9690 --- /dev/null +++ b/WebCore/xml/XMLHttpRequestProgressEvent.idl @@ -0,0 +1,36 @@ +/* + * 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 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 [ + GenerateConstructor + // We should also inherit from LSProgressEvent when the idl is added. + ] XMLHttpRequestProgressEvent : ProgressEvent { + readonly attribute unsigned long position; + readonly attribute unsigned long totalSize; + }; + +} diff --git a/WebCore/xml/XMLHttpRequestUpload.cpp b/WebCore/xml/XMLHttpRequestUpload.cpp new file mode 100644 index 0000000..cef1798 --- /dev/null +++ b/WebCore/xml/XMLHttpRequestUpload.cpp @@ -0,0 +1,144 @@ +/* + * 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 "AtomicString.h" +#include "Event.h" +#include "EventException.h" +#include "EventNames.h" +#include "Frame.h" +#include "XMLHttpRequest.h" +#include "XMLHttpRequestProgressEvent.h" +#include <wtf/Assertions.h> + +namespace WebCore { + +XMLHttpRequestUpload::XMLHttpRequestUpload(XMLHttpRequest* xmlHttpRequest) + : m_xmlHttpRequest(xmlHttpRequest) +{ +} + +ScriptExecutionContext* XMLHttpRequestUpload::scriptExecutionContext() const +{ + XMLHttpRequest* xmlHttpRequest = associatedXMLHttpRequest(); + if (!xmlHttpRequest) + return 0; + return xmlHttpRequest->scriptExecutionContext(); +} + +void XMLHttpRequestUpload::addEventListener(const AtomicString& eventType, PassRefPtr<EventListener> eventListener, bool) +{ + EventListenersMap::iterator iter = m_eventListeners.find(eventType); + if (iter == m_eventListeners.end()) { + ListenerVector listeners; + listeners.append(eventListener); + m_eventListeners.add(eventType, listeners); + } else { + ListenerVector& listeners = iter->second; + for (ListenerVector::iterator listenerIter = listeners.begin(); listenerIter != listeners.end(); ++listenerIter) { + if (*listenerIter == eventListener) + return; + } + + listeners.append(eventListener); + m_eventListeners.add(eventType, listeners); + } +} + +void XMLHttpRequestUpload::removeEventListener(const AtomicString& eventType, EventListener* eventListener, bool) +{ + EventListenersMap::iterator iter = m_eventListeners.find(eventType); + if (iter == m_eventListeners.end()) + return; + + ListenerVector& listeners = iter->second; + for (ListenerVector::const_iterator listenerIter = listeners.begin(); listenerIter != listeners.end(); ++listenerIter) { + if (*listenerIter == eventListener) { + listeners.remove(listenerIter - listeners.begin()); + return; + } + } +} + +bool XMLHttpRequestUpload::dispatchEvent(PassRefPtr<Event> evt, ExceptionCode& ec) +{ + // FIXME: check for other error conditions enumerated in the spec. + if (evt->type().isEmpty()) { + ec = EventException::UNSPECIFIED_EVENT_TYPE_ERR; + return true; + } + + ListenerVector listenersCopy = m_eventListeners.get(evt->type()); + for (ListenerVector::const_iterator listenerIter = listenersCopy.begin(); listenerIter != listenersCopy.end(); ++listenerIter) { + evt->setTarget(this); + evt->setCurrentTarget(this); + listenerIter->get()->handleEvent(evt.get(), false); + } + + return !evt->defaultPrevented(); +} + +void XMLHttpRequestUpload::dispatchXMLHttpRequestProgressEvent(EventListener* listener, const AtomicString& type, bool lengthComputable, unsigned loaded, unsigned total) +{ + RefPtr<XMLHttpRequestProgressEvent> evt = XMLHttpRequestProgressEvent::create(type, lengthComputable, loaded, total); + if (listener) { + evt->setTarget(this); + evt->setCurrentTarget(this); + listener->handleEvent(evt.get(), false); + } + + ExceptionCode ec = 0; + dispatchEvent(evt.release(), ec); + ASSERT(!ec); +} + +void XMLHttpRequestUpload::dispatchAbortEvent() +{ + dispatchXMLHttpRequestProgressEvent(m_onAbortListener.get(), eventNames().abortEvent, false, 0, 0); +} + +void XMLHttpRequestUpload::dispatchErrorEvent() +{ + dispatchXMLHttpRequestProgressEvent(m_onErrorListener.get(), eventNames().errorEvent, false, 0, 0); +} + +void XMLHttpRequestUpload::dispatchLoadEvent() +{ + dispatchXMLHttpRequestProgressEvent(m_onLoadListener.get(), eventNames().loadEvent, false, 0, 0); +} + +void XMLHttpRequestUpload::dispatchLoadStartEvent() +{ + dispatchXMLHttpRequestProgressEvent(m_onLoadStartListener.get(), eventNames().loadstartEvent, false, 0, 0); +} + +void XMLHttpRequestUpload::dispatchProgressEvent(long long bytesSent, long long totalBytesToBeSent) +{ + dispatchXMLHttpRequestProgressEvent(m_onProgressListener.get(), eventNames().progressEvent, true, static_cast<unsigned>(bytesSent), static_cast<unsigned>(totalBytesToBeSent)); +} + +} // namespace WebCore diff --git a/WebCore/xml/XMLHttpRequestUpload.h b/WebCore/xml/XMLHttpRequestUpload.h new file mode 100644 index 0000000..93fa7f9 --- /dev/null +++ b/WebCore/xml/XMLHttpRequestUpload.h @@ -0,0 +1,110 @@ +/* + * 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 "AtomicStringHash.h" +#include "EventListener.h" +#include "EventTarget.h" +#include <wtf/HashMap.h> +#include <wtf/RefCounted.h> +#include <wtf/RefPtr.h> +#include <wtf/PassRefPtr.h> +#include <wtf/Vector.h> + +namespace WebCore { + + class AtomicStringImpl; + 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; + + void dispatchAbortEvent(); + void dispatchErrorEvent(); + void dispatchLoadEvent(); + void dispatchLoadStartEvent(); + void dispatchProgressEvent(long long bytesSent, long long totalBytesToBeSent); + + void setOnabort(PassRefPtr<EventListener> eventListener) { m_onAbortListener = eventListener; } + EventListener* onabort() const { return m_onAbortListener.get(); } + + void setOnerror(PassRefPtr<EventListener> eventListener) { m_onErrorListener = eventListener; } + EventListener* onerror() const { return m_onErrorListener.get(); } + + void setOnload(PassRefPtr<EventListener> eventListener) { m_onLoadListener = eventListener; } + EventListener* onload() const { return m_onLoadListener.get(); } + + void setOnloadstart(PassRefPtr<EventListener> eventListener) { m_onLoadStartListener = eventListener; } + EventListener* onloadstart() const { return m_onLoadStartListener.get(); } + + void setOnprogress(PassRefPtr<EventListener> eventListener) { m_onProgressListener = eventListener; } + EventListener* onprogress() const { return m_onProgressListener.get(); } + + typedef Vector<RefPtr<EventListener> > ListenerVector; + typedef HashMap<AtomicString, ListenerVector> EventListenersMap; + + virtual void addEventListener(const AtomicString& eventType, PassRefPtr<EventListener>, bool useCapture); + virtual void removeEventListener(const AtomicString& eventType, EventListener*, bool useCapture); + virtual bool dispatchEvent(PassRefPtr<Event>, ExceptionCode&); + EventListenersMap& eventListeners() { return m_eventListeners; } + + using RefCounted<XMLHttpRequestUpload>::ref; + using RefCounted<XMLHttpRequestUpload>::deref; + + private: + XMLHttpRequestUpload(XMLHttpRequest*); + + void dispatchXMLHttpRequestProgressEvent(EventListener*, const AtomicString& type, bool lengthComputable, unsigned loaded, unsigned total); + + virtual void refEventTarget() { ref(); } + virtual void derefEventTarget() { deref(); } + + RefPtr<EventListener> m_onAbortListener; + RefPtr<EventListener> m_onErrorListener; + RefPtr<EventListener> m_onLoadListener; + RefPtr<EventListener> m_onLoadStartListener; + RefPtr<EventListener> m_onProgressListener; + EventListenersMap m_eventListeners; + + XMLHttpRequest* m_xmlHttpRequest; + }; + +} // namespace WebCore + +#endif // XMLHttpRequestUpload_h diff --git a/WebCore/xml/XMLHttpRequestUpload.idl b/WebCore/xml/XMLHttpRequestUpload.idl new file mode 100644 index 0000000..c066710 --- /dev/null +++ b/WebCore/xml/XMLHttpRequestUpload.idl @@ -0,0 +1,54 @@ +/* + * 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 [ + GenerateConstructor, + CustomMarkFunction + ] XMLHttpRequestUpload { + // From XMLHttpRequestEventTarget + // event handler attributes + attribute EventListener onabort; + attribute EventListener onerror; + attribute EventListener onload; + attribute EventListener onloadstart; + attribute EventListener onprogress; + + // EventTarget interface + [Custom] void addEventListener(in DOMString type, + in EventListener listener, + in boolean useCapture); + [Custom] void removeEventListener(in DOMString type, + in EventListener listener, + in boolean useCapture); + boolean dispatchEvent(in Event evt) + raises(EventException); + }; + +} diff --git a/WebCore/xml/XMLSerializer.cpp b/WebCore/xml/XMLSerializer.cpp new file mode 100644 index 0000000..ac7dc19 --- /dev/null +++ b/WebCore/xml/XMLSerializer.cpp @@ -0,0 +1,48 @@ +/* + * This file is part of the KDE libraries + * 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 + */ + +#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/WebCore/xml/XMLSerializer.h b/WebCore/xml/XMLSerializer.h new file mode 100644 index 0000000..33e94b7 --- /dev/null +++ b/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/WebCore/xml/XMLSerializer.idl b/WebCore/xml/XMLSerializer.idl new file mode 100644 index 0000000..6dcc3a4 --- /dev/null +++ b/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 [GenerateConstructor, CanBeConstructed] XMLSerializer { + DOMString serializeToString(in Node node) + raises(DOMException); + }; + +} diff --git a/WebCore/xml/XPathEvaluator.cpp b/WebCore/xml/XPathEvaluator.cpp new file mode 100644 index 0000000..8fb203f --- /dev/null +++ b/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/WebCore/xml/XPathEvaluator.h b/WebCore/xml/XPathEvaluator.h new file mode 100644 index 0000000..c8e456e --- /dev/null +++ b/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/RefCounted.h> +#include <wtf/PassRefPtr.h> + +namespace WebCore { + + typedef int ExceptionCode; + + class Node; + class String; + 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/WebCore/xml/XPathEvaluator.idl b/WebCore/xml/XPathEvaluator.idl new file mode 100644 index 0000000..da6155b --- /dev/null +++ b/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 [GenerateConstructor, CanBeConstructed, Conditional=XPATH] XPathEvaluator { + XPathExpression createExpression(in DOMString expression, + in XPathNSResolver resolver) + raises(core::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/WebCore/xml/XPathException.h b/WebCore/xml/XPathException.h new file mode 100644 index 0000000..45ad628 --- /dev/null +++ b/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/WebCore/xml/XPathException.idl b/WebCore/xml/XPathException.idl new file mode 100644 index 0000000..6e25514 --- /dev/null +++ b/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 [ + GenerateConstructor, + Conditional=XPATH + ] XPathException { + + readonly attribute unsigned short code; + readonly attribute DOMString name; + readonly attribute DOMString message; + +#if defined(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/WebCore/xml/XPathExpression.cpp b/WebCore/xml/XPathExpression.cpp new file mode 100644 index 0000000..446a7ad --- /dev/null +++ b/WebCore/xml/XPathExpression.cpp @@ -0,0 +1,93 @@ +/* + * 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 "XPathExpression.h" + +#if ENABLE(XPATH) + +#include "Document.h" +#include "ExceptionCode.h" +#include "Node.h" +#include "PlatformString.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; + } + + EventTargetNode* eventTarget = contextNode->ownerDocument() + ? contextNode->ownerDocument() + : static_cast<EventTargetNode*>(contextNode); + + EvaluationContext& evaluationContext = Expression::evaluationContext(); + evaluationContext.node = contextNode; + evaluationContext.size = 1; + evaluationContext.position = 1; + RefPtr<XPathResult> result = XPathResult::create(eventTarget, 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 (type != XPathResult::ANY_TYPE) { + ec = 0; + result->convertTo(type, ec); + if (ec) + return 0; + } + + return result; +} + +} + +#endif // ENABLE(XPATH) diff --git a/WebCore/xml/XPathExpression.h b/WebCore/xml/XPathExpression.h new file mode 100644 index 0000000..a2b75d7 --- /dev/null +++ b/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/RefCounted.h> +#include <wtf/PassRefPtr.h> + +namespace WebCore { + + typedef int ExceptionCode; + + class Node; + class String; + 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/WebCore/xml/XPathExpression.idl b/WebCore/xml/XPathExpression.idl new file mode 100644 index 0000000..c1fc15e --- /dev/null +++ b/WebCore/xml/XPathExpression.idl @@ -0,0 +1,34 @@ +/* + * 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, + GenerateConstructor + ] XPathExpression { + [OldStyleObjC] XPathResult evaluate(in Node contextNode, + in unsigned short type, + in XPathResult inResult) + raises(DOMException); + }; + +} diff --git a/WebCore/xml/XPathExpressionNode.cpp b/WebCore/xml/XPathExpressionNode.cpp new file mode 100644 index 0000000..88e349e --- /dev/null +++ b/WebCore/xml/XPathExpressionNode.cpp @@ -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. + */ + +#include "config.h" +#include "XPathExpressionNode.h" + +#if ENABLE(XPATH) + +#include "Node.h" +#include "XPathValue.h" + +namespace WebCore { +namespace XPath { + +EvaluationContext& Expression::evaluationContext() +{ + static EvaluationContext evaluationContext; + return evaluationContext; +} + +Expression::Expression() +{ +} + +Expression::~Expression() +{ + deleteAllValues(m_subExpressions); +} + +} +} + +#endif // ENABLE(XPATH) diff --git a/WebCore/xml/XPathExpressionNode.h b/WebCore/xml/XPathExpressionNode.h new file mode 100644 index 0000000..9c5f79b --- /dev/null +++ b/WebCore/xml/XPathExpressionNode.h @@ -0,0 +1,85 @@ +/* + * 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 XPathExpressionNode_h +#define XPathExpressionNode_h + +#if ENABLE(XPATH) + +#include "StringHash.h" +#include "Node.h" +#include <wtf/HashMap.h> +#include <wtf/Vector.h> + +namespace WebCore { + + namespace XPath { + + class Value; + + struct EvaluationContext { + EvaluationContext() : node(0), size(0), position(0) { } + + RefPtr<Node> node; + unsigned long size; + unsigned long position; + HashMap<String, String> variableBindings; + + }; + + class ParseNode { + public: + virtual ~ParseNode() { } + }; + + class Expression : public ParseNode, Noncopyable { + public: + static EvaluationContext& evaluationContext(); + + Expression(); + virtual ~Expression(); + + virtual Value evaluate() const = 0; + + void addSubExpression(Expression* expr) { m_subExpressions.append(expr); } + + 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; + }; + + } + +} + +#endif // ENABLE(XPATH) + +#endif // EXPRESSION_H + diff --git a/WebCore/xml/XPathFunctions.cpp b/WebCore/xml/XPathFunctions.cpp new file mode 100644 index 0000000..841b436 --- /dev/null +++ b/WebCore/xml/XPathFunctions.cpp @@ -0,0 +1,683 @@ +/* + * 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" +#include "XPathFunctions.h" + +#if ENABLE(XPATH) + +#include "Document.h" +#include "Element.h" +#include "NamedAttrMap.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; +}; + +class FunPosition : public Function { + virtual Value evaluate() const; +}; + +class FunCount : public Function { + virtual Value evaluate() const; +}; + +class FunId : public Function { + virtual Value evaluate() const; +}; + +class FunLocalName : public Function { + virtual Value evaluate() const; +}; + +class FunNamespaceURI : public Function { + virtual Value evaluate() const; +}; + +class FunName : public Function { + virtual Value evaluate() const; +}; + +class FunString : public Function { + virtual Value evaluate() const; +}; + +class FunConcat : public Function { + virtual Value evaluate() const; +}; + +class FunStartsWith : public Function { + virtual Value evaluate() const; +}; + +class FunContains : public Function { + virtual Value evaluate() const; +}; + +class FunSubstringBefore : public Function { + virtual Value evaluate() const; +}; + +class FunSubstringAfter : public Function { + virtual Value evaluate() const; +}; + +class FunSubstring : public Function { + virtual Value evaluate() const; +}; + +class FunStringLength : public Function { + virtual Value evaluate() const; +}; + +class FunNormalizeSpace : public Function { + virtual Value evaluate() const; +}; + +class FunTranslate : public Function { + virtual Value evaluate() const; +}; + +class FunBoolean : public Function { + virtual Value evaluate() const; +}; + +class FunNot : public Function { + virtual Value evaluate() const; +}; + +class FunTrue : public Function { + virtual Value evaluate() const; +}; + +class FunFalse : public Function { + virtual Value evaluate() const; +}; + +class FunLang : public Function { + virtual Value evaluate() const; +}; + +class FunNumber : public Function { + virtual Value evaluate() const; +}; + +class FunSum : public Function { + virtual Value evaluate() const; +}; + +class FunFloor : public Function { + virtual Value evaluate() const; +}; + +class FunCeiling : public Function { + virtual Value evaluate() const; +}; + +class FunRound : public Function { + virtual Value evaluate() const; +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) +{ + 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); +} + +Value FunLocalName::evaluate() const +{ + Node* node = 0; + if (argCount() > 0) { + Value a = arg(0)->evaluate(); + if (!a.isNodeSet()) + return ""; + + node = a.toNodeSet().firstNode(); + if (!node) + return ""; + } + + if (!node) + node = evaluationContext().node.get(); + + return node->localName().string(); +} + +Value FunNamespaceURI::evaluate() const +{ + Node* node = 0; + if (argCount() > 0) { + Value a = arg(0)->evaluate(); + if (!a.isNodeSet()) + return ""; + + node = a.toNodeSet().firstNode(); + if (!node) + return ""; + } + + if (!node) + node = evaluationContext().node.get(); + + return node->namespaceURI().string(); +} + +Value FunName::evaluate() const +{ + Node* node = 0; + if (argCount() > 0) { + Value a = arg(0)->evaluate(); + if (!a.isNodeSet()) + return ""; + + node = a.toNodeSet().firstNode(); + if (!node) + return ""; + } + + if (!node) + node = evaluationContext().node.get(); + + const AtomicString& prefix = node->prefix(); + return prefix.isEmpty() ? node->localName().string() : prefix + ":" + node->localName(); +} + +Value FunCount::evaluate() const +{ + Value a = arg(0)->evaluate(); + + if (!a.isNodeSet()) + return 0.0; + + 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 ""; + + int i = s1.find(s2); + + if (i == -1) + return ""; + + return s1.left(i); +} + +Value FunSubstringAfter::evaluate() const +{ + String s1 = arg(0)->evaluate().toString(); + String s2 = arg(1)->evaluate().toString(); + + int i = s1.find(s2); + if (i == -1) + return ""; + + return s1.substring(i + s2.length()); +} + +Value FunSubstring::evaluate() const +{ + String s = arg(0)->evaluate().toString(); + long pos = static_cast<long>(FunRound::round(arg(1)->evaluate().toNumber())); + 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 (haveLength && pos < 1) { + len -= 1 - pos; + pos = 1; + if (len < 1) + return ""; + } + + 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]; + int i2 = s2.find(ch); + + if (i2 == -1) + newString += String(&ch, 1); + else if ((unsigned)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(); + + RefPtr<Node> langNode = 0; + Node* node = evaluationContext().node.get(); + while (node) { + NamedAttrMap* attrs = node->attributes(); + if (attrs) + langNode = attrs->getNamedItemNS(XMLNames::xmlNamespaceURI, "lang"); + if (langNode) + break; + node = node->parentNode(); + } + + if (!langNode) + return false; + + String langNodeValue = langNode->nodeValue(); + while (true) { + if (equalIgnoringCase(langNodeValue, lang)) + return true; + + // Remove suffixes one by one. + int index = langNodeValue.reverseFind('-'); + if (index == -1) + break; + langNodeValue = langNodeValue.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()); +} + +static void createFunctionMap() +{ + struct FunctionMapping { + const char *name; + FunctionRec function; + }; + 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 } }, + }; + const unsigned int numFunctions = sizeof(functions) / sizeof(functions[0]); + + functionMap = new HashMap<String, FunctionRec>; + for (unsigned i = 0; i < numFunctions; ++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/WebCore/xml/XPathFunctions.h b/WebCore/xml/XPathFunctions.h new file mode 100644 index 0000000..f22d3ef --- /dev/null +++ b/WebCore/xml/XPathFunctions.h @@ -0,0 +1,62 @@ +/* + * functions.h - 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 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*>()); + + } + +} + +#endif // ENABLE(XPATH) + +#endif // XPath_Functions_H + diff --git a/WebCore/xml/XPathGrammar.y b/WebCore/xml/XPathGrammar.y new file mode 100644 index 0000000..50a69c2 --- /dev/null +++ b/WebCore/xml/XPathGrammar.y @@ -0,0 +1,554 @@ +/* + * 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" + +#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; +} + +%{ + +int xpathyylex(YYSTYPE* yylval) { return Parser::current()->lex(yylval); } +void xpathyyerror(const char* str) { } + +%} + +%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/WebCore/xml/XPathNSResolver.cpp b/WebCore/xml/XPathNSResolver.cpp new file mode 100644 index 0000000..3dde1f2 --- /dev/null +++ b/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/WebCore/xml/XPathNSResolver.h b/WebCore/xml/XPathNSResolver.h new file mode 100644 index 0000000..04b5f80 --- /dev/null +++ b/WebCore/xml/XPathNSResolver.h @@ -0,0 +1,51 @@ +/* + * 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/RefCounted.h> + +namespace WebCore { + + class String; + + 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/WebCore/xml/XPathNSResolver.idl b/WebCore/xml/XPathNSResolver.idl new file mode 100644 index 0000000..48c0113 --- /dev/null +++ b/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] XPathNSResolver { + [ConvertNullStringTo=Null] DOMString lookupNamespaceURI(in DOMString prefix); + }; + +} diff --git a/WebCore/xml/XPathNamespace.cpp b/WebCore/xml/XPathNamespace.cpp new file mode 100644 index 0000000..3c8c42c --- /dev/null +++ b/WebCore/xml/XPathNamespace.cpp @@ -0,0 +1,85 @@ +/* + * 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 "XPathNamespace.h" + +#if ENABLE(XPATH) + +#include "Element.h" + +namespace WebCore { + +XPathNamespace::XPathNamespace(PassRefPtr<Element> ownerElement, const String& prefix, const String& uri) + : Node(ownerElement->document()) + , m_ownerElement(ownerElement) + , m_prefix(prefix) + , m_uri(uri) +{ +} + +XPathNamespace::~XPathNamespace() +{ +} + +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/WebCore/xml/XPathNamespace.h b/WebCore/xml/XPathNamespace.h new file mode 100644 index 0000000..c0e4280 --- /dev/null +++ b/WebCore/xml/XPathNamespace.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 XPathNamespace_h +#define XPathNamespace_h + +#if ENABLE(XPATH) + +#include "AtomicString.h" +#include "Node.h" + +namespace WebCore { + + class Document; + class Element; + + class XPathNamespace : public Node { + public: + XPathNamespace(PassRefPtr<Element> ownerElement, const String& prefix, const String& uri); + virtual ~XPathNamespace(); + + 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 Node::NodeType nodeType() const; + + private: + RefPtr<Element> m_ownerElement; + AtomicString m_prefix; + AtomicString m_uri; + }; + +} + +#endif // ENABLE(XPATH) + +#endif // XPathNamespace_h + diff --git a/WebCore/xml/XPathNodeSet.cpp b/WebCore/xml/XPathNodeSet.cpp new file mode 100644 index 0000000..e29c096 --- /dev/null +++ b/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->parent())) + 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.reserveCapacity(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/WebCore/xml/XPathNodeSet.h b/WebCore/xml/XPathNodeSet.h new file mode 100644 index 0000000..2ab6f1f --- /dev/null +++ b/WebCore/xml/XPathNodeSet.h @@ -0,0 +1,82 @@ +/* + * 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: + + NodeSet() : m_isSorted(true) {} + NodeSet(const NodeSet& other) : m_isSorted(other.m_isSorted), m_nodes(other.m_nodes) {} + NodeSet& operator=(const NodeSet& other) { m_isSorted = other.m_isSorted; m_nodes = other.m_nodes; return *this; } + + 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); 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 is contains sorted data - the caller should tell it if it does not. + void markSorted(bool isSorted) { m_isSorted = isSorted; } + bool isSorted() const { return m_isSorted; } + + void sort() const; + + void reverse(); + + private: + bool m_isSorted; + Vector<RefPtr<Node> > m_nodes; + }; + + } +} + +#endif // ENABLE(XPATH) +#endif // XPathNodeSet_h diff --git a/WebCore/xml/XPathParser.cpp b/WebCore/xml/XPathParser.cpp new file mode 100644 index 0000000..77c3011 --- /dev/null +++ b/WebCore/xml/XPathParser.cpp @@ -0,0 +1,633 @@ +/* + * 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 "StringHash.h" +#include "XPathEvaluator.h" +#include "XPathException.h" +#include "XPathNSResolver.h" +#include "XPathStep.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 }; + +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(HashMap<String, Step::Axis>& 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) +{ + static HashMap<String, Step::Axis> axisNames; + + if (axisNames.isEmpty()) + setUpAxisNamesMap(axisNames); + + HashMap<String, Step::Axis>::iterator it = axisNames.find(name); + if (it == axisNames.end()) + return false; + type = it->second; + return true; +} + +static bool isNodeTypeName(const String& name) +{ + static 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 last parsed token matches the [32] Operator rule + * (check http://www.w3.org/TR/xpath#exprlex). Necessary to disambiguate + * the tokens. + */ +bool Parser::isOperatorContext() const +{ + if (m_nextPos == 0) + return false; + + switch (m_lastTokenType) { + case AND: case OR: case MULOP: + case '/': case SLASHSLASH: case '|': case PLUS: case MINUS: + case EQOP: case RELOP: + case '@': case AXISNAME: case '(': case '[': + 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 (isOperatorContext()) + 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 (isOperatorContext()) { + 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()); +} + +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) +{ + int colon = qName.find(':'); + if (colon >= 0) { + 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/WebCore/xml/XPathParser.h b/WebCore/xml/XPathParser.h new file mode 100644 index 0000000..8d6da3f --- /dev/null +++ b/WebCore/xml/XPathParser.h @@ -0,0 +1,131 @@ +/* + * 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 : Noncopyable { + public: + 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 isOperatorContext() 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/WebCore/xml/XPathPath.cpp b/WebCore/xml/XPathPath.cpp new file mode 100644 index 0000000..bc7b153 --- /dev/null +++ b/WebCore/xml/XPathPath.cpp @@ -0,0 +1,201 @@ +/* + * 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" +#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) +{ +} + +Filter::~Filter() +{ + delete m_expr; + deleteAllValues(m_predicates); +} + +Value Filter::evaluate() const +{ + Value v = m_expr->evaluate(); + + if (!v.isNodeSet()) + return v; + + 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) +{ +} + +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 +{ + for (unsigned i = 0; i < m_steps.size(); i++) { + Step* step = m_steps[i]; + NodeSet newNodes; + HashSet<Node*> newNodesSet; + + for (unsigned j = 0; j < nodes.size(); j++) { + NodeSet matches; + step->evaluate(nodes[j], matches); + + for (size_t nodeIndex = 0; nodeIndex < matches.size(); ++nodeIndex) { + Node* node = matches[nodeIndex]; + if (newNodesSet.add(node).second) + newNodes.append(node); + } + } + + nodes.swap(newNodes); + } + + nodes.markSorted(false); +} + +void LocationPath::optimizeStepPair(unsigned index) +{ + Step* first = m_steps[index]; + + if (first->axis() == Step::DescendantOrSelfAxis + && first->nodeTest().kind() == Step::NodeTest::AnyNodeTest + && first->predicates().size() == 0) { + + Step* second = m_steps[index + 1]; + if (second->axis() == Step::ChildAxis + && second->nodeTest().namespaceURI().isEmpty() + && second->nodeTest().kind() == Step::NodeTest::NameTest + && second->nodeTest().data() == "*") { + + // Optimize the common case of "//*" AKA descendant-or-self::node()/child::*. + first->setAxis(Step::DescendantAxis); + second->setAxis(Step::SelfAxis); + second->setNodeTest(Step::NodeTest::ElementNodeTest); + ASSERT(second->nodeTest().data().isEmpty()); + } + } +} + +void LocationPath::appendStep(Step* step) +{ + m_steps.append(step); + + unsigned stepCount = m_steps.size(); + if (stepCount > 1) + optimizeStepPair(stepCount - 2); +} + +void LocationPath::insertFirstStep(Step* step) +{ + m_steps.insert(0, step); + + if (m_steps.size() > 1) + optimizeStepPair(0); +} + +Path::Path(Filter* filter, LocationPath* path) + : m_filter(filter), + m_path(path) +{ +} + +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/WebCore/xml/XPathPath.h b/WebCore/xml/XPathPath.h new file mode 100644 index 0000000..97692b2 --- /dev/null +++ b/WebCore/xml/XPathPath.h @@ -0,0 +1,93 @@ +/* + * path.h - 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 XPathPath_h +#define XPathPath_h + +#if ENABLE(XPATH) + +#include "XPathExpressionNode.h" +#include "XPathNodeSet.h" + +int xpathyyparse(void*); + +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: + Expression* m_expr; + Vector<Predicate*> m_predicates; + }; + + class LocationPath : public Expression { + public: + LocationPath(); + virtual ~LocationPath(); + void setAbsolute(bool value) { m_absolute = value; } + + virtual Value evaluate() const; + void evaluate(NodeSet& nodes) const; // nodes is an input/output parameter + + void appendStep(Step* step); + void insertFirstStep(Step* step); + + private: + void optimizeStepPair(unsigned index); + + Vector<Step*> m_steps; + bool m_absolute; + }; + + class Path : public Expression + { + public: + Path(Filter*, LocationPath*); + virtual ~Path(); + + virtual Value evaluate() const; + + private: + Filter* m_filter; + LocationPath* m_path; + }; + + } +} + +#endif // ENABLE(XPATH) + +#endif // XPath_Path_H diff --git a/WebCore/xml/XPathPredicate.cpp b/WebCore/xml/XPathPredicate.cpp new file mode 100644 index 0000000..7b3e4d8 --- /dev/null +++ b/WebCore/xml/XPathPredicate.cpp @@ -0,0 +1,284 @@ +/* + * 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(); + if (!lhsResult.isNodeSet() || !rhs.isNodeSet()) + return NodeSet(); + + 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/WebCore/xml/XPathPredicate.h b/WebCore/xml/XPathPredicate.h new file mode 100644 index 0000000..8d1b0d8 --- /dev/null +++ b/WebCore/xml/XPathPredicate.h @@ -0,0 +1,111 @@ +/* + * 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; + Value m_value; + }; + + class StringExpression : public Expression { + public: + StringExpression(const String&); + private: + virtual Value evaluate() const; + Value m_value; + }; + + class Negative : public Expression { + private: + virtual Value evaluate() const; + }; + + 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; + 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: + 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: + bool shortCircuitOn() const; + virtual Value evaluate() const; + Opcode m_opcode; + }; + + class Union : public Expression { + private: + virtual Value evaluate() const; + }; + + class Predicate : Noncopyable { + public: + Predicate(Expression*); + ~Predicate(); + bool evaluate() const; + private: + Expression* m_expr; + }; + + } + +} + +#endif // ENABLE(XPATH) + +#endif // XPathPredicate_h diff --git a/WebCore/xml/XPathResult.cpp b/WebCore/xml/XPathResult.cpp new file mode 100644 index 0000000..285350e --- /dev/null +++ b/WebCore/xml/XPathResult.cpp @@ -0,0 +1,245 @@ +/* + * 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 "XPathResult.h" + +#if ENABLE(XPATH) + +#include "EventListener.h" +#include "EventNames.h" +#include "EventTargetNode.h" +#include "ExceptionCode.h" +#include "XPathEvaluator.h" +#include "XPathException.h" + +namespace WebCore { + +using namespace XPath; + +class InvalidatingEventListener : public EventListener { +public: + static PassRefPtr<InvalidatingEventListener> create(XPathResult* result) { return adoptRef(new InvalidatingEventListener(result)); } + virtual void handleEvent(Event*, bool) { m_result->invalidateIteratorState(); } + +private: + InvalidatingEventListener(XPathResult* result) : m_result(result) { } + XPathResult* m_result; +}; + +XPathResult::XPathResult(EventTargetNode* eventTarget, const Value& value) + : m_value(value) + , m_eventTarget(eventTarget) +{ + m_eventListener = InvalidatingEventListener::create(this); + m_eventTarget->addEventListener(eventNames().DOMSubtreeModifiedEvent, m_eventListener, false); + 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_invalidIteratorState = false; + return; + } + ASSERT_NOT_REACHED(); +} + +XPathResult::~XPathResult() +{ + if (m_eventTarget) + m_eventTarget->removeEventListener(eventNames().DOMSubtreeModifiedEvent, m_eventListener.get(), false); +} + +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(); +} + +void XPathResult::invalidateIteratorState() +{ + m_invalidIteratorState = true; + + ASSERT(m_eventTarget); + ASSERT(m_eventListener); + + m_eventTarget->removeEventListener(eventNames().DOMSubtreeModifiedEvent, m_eventListener.get(), false); + + m_eventTarget = 0; +} + +bool XPathResult::invalidIteratorState() const +{ + if (resultType() != UNORDERED_NODE_ITERATOR_TYPE && resultType() != ORDERED_NODE_ITERATOR_TYPE) + return false; + + return m_invalidIteratorState; +} + +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 (m_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/WebCore/xml/XPathResult.h b/WebCore/xml/XPathResult.h new file mode 100644 index 0000000..ecd5cac --- /dev/null +++ b/WebCore/xml/XPathResult.h @@ -0,0 +1,94 @@ +/* + * 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 XPathResult_h +#define XPathResult_h + +#if ENABLE(XPATH) + +#include <wtf/RefCounted.h> +#include "XPathValue.h" + +namespace WebCore { + + typedef int ExceptionCode; + + class EventListener; + class EventTargetNode; + class Node; + class String; + + 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(EventTargetNode* eventTarget, const XPath::Value& value) { return adoptRef(new XPathResult(eventTarget, 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&); + + void invalidateIteratorState(); + + private: + XPathResult(EventTargetNode*, 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; + bool m_invalidIteratorState; + RefPtr<EventTargetNode> m_eventTarget; + RefPtr<EventListener> m_eventListener; + }; + +} + +#endif // ENABLE(XPATH) + +#endif // XPathResult_h diff --git a/WebCore/xml/XPathResult.idl b/WebCore/xml/XPathResult.idl new file mode 100644 index 0000000..bc36c3e --- /dev/null +++ b/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 [GenerateConstructor, 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/WebCore/xml/XPathStep.cpp b/WebCore/xml/XPathStep.cpp new file mode 100644 index 0000000..abdcba5 --- /dev/null +++ b/WebCore/xml/XPathStep.cpp @@ -0,0 +1,306 @@ +/* + * 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" +#include "XPathStep.h" + +#if ENABLE(XPATH) + +#include "Document.h" +#include "Element.h" +#include "NamedAttrMap.h" +#include "XPathNSResolver.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); +} + +void Step::evaluate(Node* context, NodeSet& nodes) const +{ + nodesInAxis(context, nodes); + + EvaluationContext& evaluationContext = Expression::evaluationContext(); + + 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]; + + Expression::evaluationContext().node = node; + evaluationContext.size = nodes.size(); + evaluationContext.position = j + 1; + if (predicate->evaluate()) + newNodes.append(node); + } + + nodes.swap(newNodes); + } +} + +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)) + 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)) + nodes.append(n); + return; + case ParentAxis: + if (context->isAttributeNode()) { + Node* n = static_cast<Attr*>(context)->ownerElement(); + if (nodeMatches(n)) + nodes.append(n); + } else { + Node* n = context->parentNode(); + if (n && nodeMatches(n)) + nodes.append(n); + } + return; + case AncestorAxis: { + Node* n = context; + if (context->isAttributeNode()) { + n = static_cast<Attr*>(context)->ownerElement(); + if (nodeMatches(n)) + nodes.append(n); + } + for (n = n->parentNode(); n; n = n->parentNode()) + if (nodeMatches(n)) + 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)) + 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)) + 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)) + 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)) + nodes.append(n); + for (Node* c = n->firstChild(); c; c = c->traverseNextNode(n)) + if (nodeMatches(c)) + nodes.append(c); + } + } + } + return; + case PrecedingAxis: { + if (context->isAttributeNode()) + context = static_cast<Attr*>(context)->ownerElement(); + + Node* n = context; + while (Node* parent = n->parent()) { + for (n = n->traversePreviousNode(); n != parent; n = n->traversePreviousNode()) + if (nodeMatches(n)) + 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() != "*") { + RefPtr<Node> n = static_cast<Element*>(context)->getAttributeNodeNS(m_nodeTest.namespaceURI(), m_nodeTest.data()); + if (n && n->namespaceURI() != "http://www.w3.org/2000/xmlns/") // In XPath land, namespace nodes are not accessible on the attribute axis. + nodes.append(n.release()); + return; + } + + NamedAttrMap* attrs = context->attributes(); + if (!attrs) + return; + + for (unsigned long i = 0; i < attrs->length(); ++i) { + RefPtr<Node> n = attrs->item(i); + if (nodeMatches(n.get())) + nodes.append(n.release()); + } + return; + } + case NamespaceAxis: + // XPath namespace nodes are not implemented yet. + return; + case SelfAxis: + if (nodeMatches(context)) + nodes.append(context); + return; + case DescendantOrSelfAxis: + if (nodeMatches(context)) + 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)) + nodes.append(n); + return; + case AncestorOrSelfAxis: { + if (nodeMatches(context)) + nodes.append(context); + Node* n = context; + if (context->isAttributeNode()) { + n = static_cast<Attr*>(context)->ownerElement(); + if (nodeMatches(n)) + nodes.append(n); + } + for (n = n->parentNode(); n; n = n->parentNode()) + if (nodeMatches(n)) + nodes.append(n); + + nodes.markSorted(false); + return; + } + } + ASSERT_NOT_REACHED(); +} + + +bool Step::nodeMatches(Node* node) const +{ + switch (m_nodeTest.kind()) { + case NodeTest::TextNodeTest: + return node->nodeType() == Node::TEXT_NODE || node->nodeType() == Node::CDATA_SECTION_NODE; + case NodeTest::CommentNodeTest: + return node->nodeType() == Node::COMMENT_NODE; + case NodeTest::ProcessingInstructionNodeTest: { + const String& name = m_nodeTest.data(); + return node->nodeType() == Node::PROCESSING_INSTRUCTION_NODE && (name.isEmpty() || node->nodeName() == name); + } + case NodeTest::ElementNodeTest: + return node->isElementNode(); + case NodeTest::AnyNodeTest: + return true; + case NodeTest::NameTest: { + const String& name = m_nodeTest.data(); + const String& namespaceURI = m_nodeTest.namespaceURI(); + + if (m_axis == AttributeAxis) { + ASSERT(node->isAttributeNode()); + + // In XPath land, namespace nodes are not accessible on the attribute axis. + if (node->namespaceURI() == "http://www.w3.org/2000/xmlns/") + return false; + + if (name == "*") + return namespaceURI.isEmpty() || node->namespaceURI() == namespaceURI; + + return node->localName() == name && node->namespaceURI() == namespaceURI; + } + + if (m_axis == NamespaceAxis) { + // Node test on the namespace axis is not implemented yet + return false; + } + + if (name == "*") + return node->nodeType() == primaryNodeType(m_axis) && (namespaceURI.isEmpty() || namespaceURI == node->namespaceURI()); + + // We use tagQName here because we don't want the element name in uppercase + // like we get with HTML elements. + // Paths without namespaces should match HTML elements in HTML documents despite those having an XHTML namespace. + return node->nodeType() == Node::ELEMENT_NODE + && static_cast<Element*>(node)->tagQName().localName() == name + && ((node->isHTMLElement() && node->document()->isHTMLDocument() && namespaceURI.isNull()) || namespaceURI == node->namespaceURI()); + } + } + ASSERT_NOT_REACHED(); + return false; +} + +Node::NodeType Step::primaryNodeType(Axis axis) const +{ + switch (axis) { + case AttributeAxis: + return Node::ATTRIBUTE_NODE; + case NamespaceAxis: + return Node::XPATH_NAMESPACE_NODE; + default: + return Node::ELEMENT_NODE; + } +} + +} +} + +#endif // ENABLE(XPATH) diff --git a/WebCore/xml/XPathStep.h b/WebCore/xml/XPathStep.h new file mode 100644 index 0000000..f1420d0 --- /dev/null +++ b/WebCore/xml/XPathStep.h @@ -0,0 +1,104 @@ +/* + * 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 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, Noncopyable { + public: + enum Axis { + AncestorAxis, AncestorOrSelfAxis, AttributeAxis, + ChildAxis, DescendantAxis, DescendantOrSelfAxis, + FollowingAxis, FollowingSiblingAxis, NamespaceAxis, + ParentAxis, PrecedingAxis, PrecedingSiblingAxis, + SelfAxis + }; + + class NodeTest { + public: + enum Kind { + TextNodeTest, CommentNodeTest, ProcessingInstructionNodeTest, AnyNodeTest, NameTest, + ElementNodeTest // XPath 2.0 + }; + + 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 String data() const { return m_data; } + const String namespaceURI() const { return m_namespaceURI; } + + private: + Kind m_kind; + String m_data; + String m_namespaceURI; + }; + + Step(Axis, const NodeTest& nodeTest, const Vector<Predicate*>& predicates = Vector<Predicate*>()); + ~Step(); + + void evaluate(Node* context, NodeSet&) const; + + Axis axis() const { return m_axis; } + NodeTest nodeTest() const { return m_nodeTest; } + const Vector<Predicate*>& predicates() const { return m_predicates; } + + void setAxis(Axis axis) { m_axis = axis; } + void setNodeTest(NodeTest nodeTest) { m_nodeTest = nodeTest; } + void setPredicates(const Vector<Predicate*>& predicates) { m_predicates = predicates; } + + private: + void parseNodeTest(const String&); + void nodesInAxis(Node* context, NodeSet&) const; + bool nodeMatches(Node*) const; + String namespaceFromNodetest(const String& nodeTest) const; + Node::NodeType primaryNodeType(Axis) const; + + Axis m_axis; + NodeTest m_nodeTest; + Vector<Predicate*> m_predicates; + }; + + } + +} + +#endif // ENABLE(XPATH) + +#endif // XPath_Step_H diff --git a/WebCore/xml/XPathUtil.cpp b/WebCore/xml/XPathUtil.cpp new file mode 100644 index 0000000..ab4b1d4 --- /dev/null +++ b/WebCore/xml/XPathUtil.cpp @@ -0,0 +1,96 @@ +/* + * 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 "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) { + String str; + + for (Node* n = node->firstChild(); n; n = n->traverseNextNode(node)) + if (n->isTextNode()) + str += n->nodeValue(); + + return str; + } + } + + 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/WebCore/xml/XPathUtil.h b/WebCore/xml/XPathUtil.h new file mode 100644 index 0000000..30f21ae --- /dev/null +++ b/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/Vector.h> + +namespace WebCore { + + class Node; + class String; + + 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/WebCore/xml/XPathValue.cpp b/WebCore/xml/XPathValue.cpp new file mode 100644 index 0000000..b3cad38 --- /dev/null +++ b/WebCore/xml/XPathValue.cpp @@ -0,0 +1,128 @@ +/* + * 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 "XPathUtil.h" + +#include <wtf/MathExtras.h> +#include <limits> + +using std::numeric_limits; + +namespace WebCore { +namespace XPath { + +const Value::AdoptTag Value::adopt = {}; + +const NodeSet& Value::toNodeSet() const +{ + if (!m_data) { + static NodeSet emptyNodeSet; + return emptyNodeSet; + } + + return m_data->m_nodeSet; +} + +NodeSet& Value::modifiableNodeSet() +{ + 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: { + bool canConvert; + double value = m_data->m_string.simplifyWhiteSpace().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/WebCore/xml/XPathValue.h b/WebCore/xml/XPathValue.h new file mode 100644 index 0000000..a0cd24d --- /dev/null +++ b/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/WebCore/xml/XPathVariableReference.cpp b/WebCore/xml/XPathVariableReference.cpp new file mode 100644 index 0000000..efe9cf9 --- /dev/null +++ b/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/WebCore/xml/XPathVariableReference.h b/WebCore/xml/XPathVariableReference.h new file mode 100644 index 0000000..811176d --- /dev/null +++ b/WebCore/xml/XPathVariableReference.h @@ -0,0 +1,50 @@ +/* + * 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 { + + class VariableReference : public Expression { + public: + VariableReference(const String& name); + private: + virtual Value evaluate() const; + String m_name; + }; + + } +} + +#endif // ENABLE(XPATH) + +#endif // XPath_VariableReference_H diff --git a/WebCore/xml/XSLImportRule.cpp b/WebCore/xml/XSLImportRule.cpp new file mode 100644 index 0000000..2efafa3 --- /dev/null +++ b/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 "DocLoader.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& url, const String& sheet) +{ + if (m_styleSheet) + m_styleSheet->setParent(0); + + m_styleSheet = XSLStyleSheet::create(this, url); + + XSLStyleSheet* parent = parentStyleSheet(); + if (parent) + m_styleSheet->setOwnerDocument(parent->ownerDocument()); + + 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() +{ + DocLoader* docLoader = 0; + StyleBase* root = this; + StyleBase* parent; + while ((parent = root->parent())) + root = parent; + if (root->isXSLStyleSheet()) + docLoader = static_cast<XSLStyleSheet*>(root)->docLoader(); + + String absHref = m_strHref; + XSLStyleSheet* parentSheet = parentStyleSheet(); + if (!parentSheet->href().isNull()) + // use parent styleheet's URL as the base URL + absHref = KURL(KURL(parentSheet->href()), 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)->href()) + return; + } + + m_cachedSheet = docLoader->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/WebCore/xml/XSLImportRule.h b/WebCore/xml/XSLImportRule.h new file mode 100644 index 0000000..fc7a7f8 --- /dev/null +++ b/WebCore/xml/XSLImportRule.h @@ -0,0 +1,72 @@ +/* + * 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(); + +private: + XSLImportRule(XSLStyleSheet* parentSheet, const String& href); + + virtual bool isImportRule() { return true; } + + // from CachedResourceClient + virtual void setXSLStyleSheet(const String& url, 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/WebCore/xml/XSLStyleSheet.cpp b/WebCore/xml/XSLStyleSheet.cpp new file mode 100644 index 0000000..9443652 --- /dev/null +++ b/WebCore/xml/XSLStyleSheet.cpp @@ -0,0 +1,288 @@ +/* + * 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 "CString.h" +#include "Console.h" +#include "DOMWindow.h" +#include "DocLoader.h" +#include "Document.h" +#include "Frame.h" +#include "loader.h" +#include "Node.h" +#include "XMLTokenizer.h" +#include "XSLImportRule.h" +#include "XSLTProcessor.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& href) + : StyleSheet(parentRule, href) + , m_ownerDocument(0) + , m_stylesheetDoc(0) + , m_embedded(false) + , m_processed(false) // Child sheets get marked as processed when the libxslt engine has finally seen them. + , m_stylesheetDocTaken(false) +{ +} + +XSLStyleSheet::XSLStyleSheet(Node* parentNode, const String& href, bool embedded) + : StyleSheet(parentNode, href) + , m_ownerDocument(parentNode->document()) + , m_stylesheetDoc(0) + , m_embedded(embedded) + , m_processed(true) // The root sheet starts off processed. + , m_stylesheetDocTaken(false) +{ +} + +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()) + return (xmlDocPtr)ownerDocument()->transformSource(); + 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(); + } + } +} + +DocLoader* XSLStyleSheet::docLoader() +{ + if (!m_ownerDocument) + return 0; + return m_ownerDocument->docLoader(); +} + +bool XSLStyleSheet::parseString(const String& string, bool strict) +{ + // Parse in a single chunk into an xmlDocPtr + const UChar BOM = 0xFEFF; + const unsigned char BOMHighByte = *reinterpret_cast<const unsigned char*>(&BOM); + setLoaderForLibXMLCallbacks(docLoader()); + if (!m_stylesheetDocTaken) + xmlFreeDoc(m_stylesheetDoc); + m_stylesheetDocTaken = false; + + Console* console = 0; + if (Frame* frame = ownerDocument()->frame()) + console = frame->domWindow()->console(); + xmlSetStructuredErrorFunc(console, XSLTProcessor::parseErrorFunc); + xmlSetGenericErrorFunc(console, XSLTProcessor::genericErrorFunc); + + m_stylesheetDoc = xmlReadMemory(reinterpret_cast<const char*>(string.characters()), string.length() * sizeof(UChar), + href().utf8().data(), + BOMHighByte == 0xFF ? "UTF-16LE" : "UTF-16BE", + XML_PARSE_NOENT | XML_PARSE_DTDATTR | XML_PARSE_NOWARNING | XML_PARSE_NOCDATA); + loadChildSheets(); + + xmlSetStructuredErrorFunc(0, 0); + xmlSetGenericErrorFunc(0, 0); + + setLoaderForLibXMLCallbacks(0); + 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*)(href().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; +} + +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/WebCore/xml/XSLStyleSheet.h b/WebCore/xml/XSLStyleSheet.h new file mode 100644 index 0000000..8946529 --- /dev/null +++ b/WebCore/xml/XSLStyleSheet.h @@ -0,0 +1,99 @@ +/* + * 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 "StyleSheet.h" +#include <libxml/parser.h> +#include <libxslt/transform.h> +#include <wtf/PassRefPtr.h> + +namespace WebCore { + +class DocLoader; +class Document; +class XSLImportRule; + +class XSLStyleSheet : public StyleSheet { +public: + static PassRefPtr<XSLStyleSheet> create(XSLImportRule* parentImport, const String& href) + { + return adoptRef(new XSLStyleSheet(parentImport, href)); + } + static PassRefPtr<XSLStyleSheet> create(Node* parentNode, const String& href) + { + return adoptRef(new XSLStyleSheet(parentNode, href, false)); + } + static PassRefPtr<XSLStyleSheet> createEmbedded(Node* parentNode, const String& href) + { + return adoptRef(new XSLStyleSheet(parentNode, href, true)); + } + + 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); + + xsltStylesheetPtr compileStyleSheet(); + + DocLoader* docLoader(); + + Document* ownerDocument() { return m_ownerDocument; } + void setOwnerDocument(Document* doc) { m_ownerDocument = doc; } + + xmlDocPtr document(); + + void clearDocuments(); + + xmlDocPtr locateStylesheetSubResource(xmlDocPtr parentDoc, const xmlChar* uri); + + void markAsProcessed(); + bool processed() const { return m_processed; } + +private: + XSLStyleSheet(Node* parentNode, const String& href, bool embedded); + XSLStyleSheet(XSLImportRule* parentImport, const String& href); + + Document* m_ownerDocument; + xmlDocPtr m_stylesheetDoc; + bool m_embedded; + bool m_processed; + bool m_stylesheetDocTaken; +}; + +} // namespace WebCore + +#endif // ENABLE(XSLT) + +#endif // XSLStyleSheet_h diff --git a/WebCore/xml/XSLTExtensions.cpp b/WebCore/xml/XSLTExtensions.cpp new file mode 100644 index 0000000..d89f08b --- /dev/null +++ b/WebCore/xml/XSLTExtensions.cpp @@ -0,0 +1,88 @@ +/** + * 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 <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/WebCore/xml/XSLTExtensions.h b/WebCore/xml/XSLTExtensions.h new file mode 100644 index 0000000..4e4224f --- /dev/null +++ b/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/WebCore/xml/XSLTProcessor.cpp b/WebCore/xml/XSLTProcessor.cpp new file mode 100644 index 0000000..ab554c4 --- /dev/null +++ b/WebCore/xml/XSLTProcessor.cpp @@ -0,0 +1,461 @@ +/* + * 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 "CString.h" +#include "Console.h" +#include "DOMImplementation.h" +#include "DOMWindow.h" +#include "DocLoader.h" +#include "DocumentFragment.h" +#include "Frame.h" +#include "FrameLoader.h" +#include "FrameView.h" +#include "HTMLDocument.h" +#include "HTMLTokenizer.h" +#include "Page.h" +#include "ResourceError.h" +#include "ResourceHandle.h" +#include "ResourceRequest.h" +#include "ResourceResponse.h" +#include "Text.h" +#include "TextResourceDecoder.h" +#include "XMLTokenizer.h" +#include "XSLTExtensions.h" +#include "XSLTUnicodeSort.h" +#include "loader.h" +#include "markup.h" +#include <libxslt/imports.h> +#include <libxslt/variables.h> +#include <libxslt/xsltutils.h> +#include <wtf/Assertions.h> +#include <wtf/Platform.h> +#include <wtf/Vector.h> +#if PLATFORM(MAC) +#include "SoftLinking.h" +#endif + +#if PLATFORM(MAC) +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* userData, const char* msg, ...) +{ + // 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, 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 DocLoader* globalDocLoader = 0; +static xmlDocPtr docLoaderFunc(const xmlChar* uri, + xmlDictPtr dict, + 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(reinterpret_cast<const char*>(base)), reinterpret_cast<const char*>(uri)); + xmlFree(base); + ResourceError error; + ResourceResponse response; + + Vector<char> data; + + bool requestAllowed = globalDocLoader->frame() && globalDocLoader->doc()->securityOrigin()->canRequest(url); + if (requestAllowed) { + globalDocLoader->frame()->loader()->loadResourceSynchronously(url, error, response, data); + requestAllowed = globalDocLoader->doc()->securityOrigin()->canRequest(response.url()); + } + if (!requestAllowed) { + data.clear(); + globalDocLoader->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, DocLoader* loader) +{ + xsltSetLoaderFunc(func); + globalProcessor = processor; + globalDocLoader = loader; +} + +static int writeToVector(void* context, const char* buffer, int len) +{ + Vector<UChar>& resultOutput = *static_cast<Vector<UChar>*>(context); + String decodedChunk = String::fromUTF8(buffer, len); + resultOutput.append(decodedChunk.characters(), decodedChunk.length()); + return len; +} + +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 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"; +} + +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++] = strdup(it->first.utf8().data()); + parameterArray[index++] = strdup(it->second.utf8().data()); + } + parameterArray[index] = 0; + + return parameterArray; +} + +static void freeXsltParamArray(const char** params) +{ + const char** temp = params; + if (!params) + return; + + while (*temp) { + free((void*)*(temp++)); // strdup returns malloc'd blocks, so we have to use free() here + free((void*)*(temp++)); + } + fastFree(params); +} + + +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 = ownerDocument->implementation()->createDocument(frame); + transformTextStringToXHTMLDocumentString(documentSource); + } else + result = ownerDocument->implementation()->createDocument(sourceMIMEType, frame, 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); + } + + if (sourceIsDocument) + result->setURL(ownerDocument->url()); + 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->finishParsing(); + result->close(); + + return result.release(); +} + +static inline RefPtr<DocumentFragment> createFragmentFromSource(const String& sourceString, const String& sourceMIMEType, Node* sourceNode, Document* outputDoc) +{ + RefPtr<DocumentFragment> fragment = new DocumentFragment(outputDoc); + + if (sourceMIMEType == "text/html") + parseHTMLDocumentFragment(sourceString, fragment.get()); + else if (sourceMIMEType == "text/plain") + fragment->addChild(new Text(outputDoc, sourceString)); + else { + bool successfulParse = parseXMLDocumentFragment(sourceString, fragment.get(), outputDoc->documentElement()); + if (!successfulParse) + return 0; + } + + // FIXME: Do we need to mess with URLs here? + + return fragment; +} + +static xsltStylesheetPtr xsltStylesheetPointer(RefPtr<XSLStyleSheet>& cachedStylesheet, Node* stylesheetRootNode) +{ + if (!cachedStylesheet && stylesheetRootNode) { + cachedStylesheet = XSLStyleSheet::create(stylesheetRootNode->parent() ? stylesheetRootNode->parent() : stylesheetRootNode, + stylesheetRootNode->document()->url().string()); + 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) + sourceDoc = (xmlDocPtr)ownerDocument->transformSource(); + if (!sourceDoc) { + sourceDoc = (xmlDocPtr)xmlDocPtrForString(ownerDocument->docLoader(), createMarkup(sourceNode), + sourceIsDocument ? ownerDocument->url().string() : String()); + shouldDelete = (sourceDoc != 0); + } + 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 == 0 && resultDoc->type == XML_HTML_DOCUMENT_NODE) + resultType = (const xmlChar*)"html"; + + if (xmlStrEqual(resultType, (const xmlChar*)"html")) + return "text/html"; + else 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->docLoader()); + 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 == NULL) + 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; +} + +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, sourceNode, 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); +} + +} // namespace WebCore + +#endif // ENABLE(XSLT) diff --git a/WebCore/xml/XSLTProcessor.h b/WebCore/xml/XSLTProcessor.h new file mode 100644 index 0000000..9ee2aad --- /dev/null +++ b/WebCore/xml/XSLTProcessor.h @@ -0,0 +1,81 @@ +/* + * 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 "StringHash.h" +#include "XSLStyleSheet.h" +#include <libxml/parserInternals.h> +#include <libxslt/documents.h> +#include <wtf/HashMap.h> + +namespace WebCore { + +class Frame; +class Document; +class DocumentFragment; + +class XSLTProcessor : public RefCounted<XSLTProcessor> { +public: + static PassRefPtr<XSLTProcessor> create() { return adoptRef(new 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() { m_stylesheet.clear(); m_stylesheetRootNode.clear(); m_parameters.clear(); } + + static void parseErrorFunc(void* userData, xmlError*); + static void genericErrorFunc(void* userData, const char* msg, ...); + +public: + // Only for libXSLT callbacks + XSLStyleSheet* xslStylesheet() const { return m_stylesheet.get(); } + + typedef HashMap<String, String> ParameterMap; + +private: + XSLTProcessor() { } + + RefPtr<XSLStyleSheet> m_stylesheet; + RefPtr<Node> m_stylesheetRootNode; + ParameterMap m_parameters; +}; + +} + +#endif +#endif diff --git a/WebCore/xml/XSLTProcessor.idl b/WebCore/xml/XSLTProcessor.idl new file mode 100644 index 0000000..0a6ff93 --- /dev/null +++ b/WebCore/xml/XSLTProcessor.idl @@ -0,0 +1,52 @@ +/* + * 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 { + + // 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 + ] 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/WebCore/xml/XSLTUnicodeSort.cpp b/WebCore/xml/XSLTUnicodeSort.cpp new file mode 100644 index 0000000..ed66112 --- /dev/null +++ b/WebCore/xml/XSLTUnicodeSort.cpp @@ -0,0 +1,352 @@ +/* + * 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. + */ + +#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 init_xsltTransformError(xsltTransformContextPtr ctxt, xsltStylesheetPtr style, xmlNodePtr node, const char *, ...) WTF_ATTRIBUTE_PRINTF(4, 5); +static void (*softLink_xsltTransformError)(xsltTransformContextPtr ctxt, xsltStylesheetPtr style, xmlNodePtr node, const char *, ...) WTF_ATTRIBUTE_PRINTF(4, 5) = init_xsltTransformError; + +static void init_xsltTransformError(xsltTransformContextPtr ctxt, xsltStylesheetPtr style, xmlNodePtr node, const char* msg, ...) +{ + softLink_xsltTransformError = (void (*) (xsltTransformContextPtr ctxt, xsltStylesheetPtr style, xmlNodePtr node, const char *, ...))dlsym(libxsltLibrary(), "xsltTransformError"); + ASSERT(softLink_xsltTransformError); + + va_list args; + va_start(args, msg); +#if PLATFORM(WIN_OS) + char str[1024]; + vsnprintf(str, sizeof(str) - 1, msg, args); +#else + char* str; + vasprintf(&str, msg, args); +#endif + va_end(args); + + softLink_xsltTransformError(ctxt, style, node, "%s", str); + +#if !PLATFORM(WIN_OS) + free(str); +#endif +} + +inline void xsltTransformError(xsltTransformContextPtr ctxt, xsltStylesheetPtr style, xmlNodePtr node, const char* msg, ...) WTF_ATTRIBUTE_PRINTF(4, 5); + +inline void xsltTransformError(xsltTransformContextPtr ctxt, xsltStylesheetPtr style, xmlNodePtr node, const char* msg, ...) +{ + va_list args; + va_start(args, msg); +#if PLATFORM(WIN_OS) + char str[1024]; + vsnprintf(str, sizeof(str) - 1, msg, args); +#else + char* str; + vasprintf(&str, msg, args); +#endif + va_end(args); + + softLink_xsltTransformError(ctxt, style, node, "%s", str); + +#if !PLATFORM(WIN_OS) + free(str); +#endif +} + +#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/WebCore/xml/XSLTUnicodeSort.h b/WebCore/xml/XSLTUnicodeSort.h new file mode 100644 index 0000000..c8d395b --- /dev/null +++ b/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/WebCore/xml/xmlattrs.in b/WebCore/xml/xmlattrs.in new file mode 100644 index 0000000..6cc47be --- /dev/null +++ b/WebCore/xml/xmlattrs.in @@ -0,0 +1,6 @@ +namespace="XML" +namespaceURI="http://www.w3.org/XML/1998/namespace" + +base +lang +space |