diff options
author | Upstream <upstream-import@none> | 1970-01-12 13:46:40 +0000 |
---|---|---|
committer | Upstream <upstream-import@none> | 1970-01-12 13:46:40 +0000 |
commit | d8543bb6618c17b12da906afa77d216f58cf4058 (patch) | |
tree | c58dc05ed86825bd0ef8d305d58c8205106b540f /WebCore/xml | |
download | external_webkit-d8543bb6618c17b12da906afa77d216f58cf4058.zip external_webkit-d8543bb6618c17b12da906afa77d216f58cf4058.tar.gz external_webkit-d8543bb6618c17b12da906afa77d216f58cf4058.tar.bz2 |
external/webkit r30707
Diffstat (limited to 'WebCore/xml')
60 files changed, 8244 insertions, 0 deletions
diff --git a/WebCore/xml/DOMParser.cpp b/WebCore/xml/DOMParser.cpp new file mode 100644 index 0000000..8be4d14 --- /dev/null +++ b/WebCore/xml/DOMParser.cpp @@ -0,0 +1,43 @@ +/* + * This file is part of the KDE libraries + * 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 + */ + +#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::instance()->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..fcaa37d --- /dev/null +++ b/WebCore/xml/XMLHttpRequest.cpp @@ -0,0 +1,790 @@ +/* + * Copyright (C) 2004, 2006, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2005-2007 Alexey Proskuryakov <ap@webkit.org> + * Copyright (C) 2007 Julien Chaffraix <julien.chaffraix@gmail.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 + */ + +#include "config.h" +#include "XMLHttpRequest.h" + +#include "CString.h" +#include "DOMImplementation.h" +#include "Event.h" +#include "EventException.h" +#include "EventListener.h" +#include "EventNames.h" +#include "Frame.h" +#include "FrameLoader.h" +#include "HTTPParsers.h" +#include "Page.h" +#include "Settings.h" +#include "SubresourceLoader.h" +#include "TextResourceDecoder.h" +#include "XMLHttpRequestException.h" +#include "kjs_binding.h" + +namespace WebCore { + +using namespace EventNames; + +typedef HashSet<XMLHttpRequest*> RequestsSet; + +static HashMap<Document*, RequestsSet*>& requestsByDocument() +{ + static HashMap<Document*, RequestsSet*> map; + return map; +} + +static void addToRequestsByDocument(Document* doc, XMLHttpRequest* req) +{ + ASSERT(doc); + ASSERT(req); + + RequestsSet* requests = requestsByDocument().get(doc); + if (!requests) { + requests = new RequestsSet; + requestsByDocument().set(doc, requests); + } + + ASSERT(!requests->contains(req)); + requests->add(req); +} + +static void removeFromRequestsByDocument(Document* doc, XMLHttpRequest* req) +{ + ASSERT(doc); + ASSERT(req); + + RequestsSet* requests = requestsByDocument().get(doc); + ASSERT(requests); + ASSERT(requests->contains(req)); + requests->remove(req); + if (requests->isEmpty()) { + requestsByDocument().remove(doc); + delete requests; + } +} + +static bool isSafeRequestHeader(const String& name) +{ + static HashSet<String, CaseFoldingHash> forbiddenHeaders; + static String proxyString("proxy-"); + + 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); +} + +// 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'); +} + +XMLHttpRequestState XMLHttpRequest::getReadyState() const +{ + return m_state; +} + +const KJS::UString& XMLHttpRequest::getResponseText(ExceptionCode& ec) const +{ + return m_responseText; +} + +Document* XMLHttpRequest::getResponseXML(ExceptionCode& ec) const +{ + if (m_state != Loaded) + return 0; + + if (!m_createdDocument) { + if (m_response.isHTTP() && !responseIsXML()) { + // The W3C spec requires this. + m_responseXML = 0; + } else { + m_responseXML = m_doc->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(); +} + +EventListener* XMLHttpRequest::onReadyStateChangeListener() const +{ + return m_onReadyStateChangeListener.get(); +} + +void XMLHttpRequest::setOnReadyStateChangeListener(EventListener* eventListener) +{ + m_onReadyStateChangeListener = eventListener; +} + +EventListener* XMLHttpRequest::onLoadListener() const +{ + return m_onLoadListener.get(); +} + +void XMLHttpRequest::setOnLoadListener(EventListener* eventListener) +{ + m_onLoadListener = eventListener; +} + +void XMLHttpRequest::addEventListener(const AtomicString& eventType, PassRefPtr<EventListener> eventListener, bool) +{ + EventListenersMap::iterator iter = m_eventListeners.find(eventType.impl()); + if (iter == m_eventListeners.end()) { + ListenerVector listeners; + listeners.append(eventListener); + m_eventListeners.add(eventType.impl(), 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.impl(), listeners); + } +} + +void XMLHttpRequest::removeEventListener(const AtomicString& eventType, EventListener* eventListener, bool) +{ + EventListenersMap::iterator iter = m_eventListeners.find(eventType.impl()); + 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, bool /*tempEvent*/) +{ + // 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().impl()); + 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(); +} + +XMLHttpRequest::XMLHttpRequest(Document* d) + : m_doc(d) + , m_async(true) + , m_state(Uninitialized) + , m_responseText("") + , m_createdDocument(false) + , m_aborted(false) +{ + ASSERT(m_doc); + addToRequestsByDocument(m_doc, this); +} + +XMLHttpRequest::~XMLHttpRequest() +{ + if (m_doc) + removeFromRequestsByDocument(m_doc, this); +} + +void XMLHttpRequest::changeState(XMLHttpRequestState newState) +{ + if (m_state != newState) { + m_state = newState; + callReadyStateChangeListener(); + } +} + +void XMLHttpRequest::callReadyStateChangeListener() +{ + if (!m_doc || !m_doc->frame()) + return; + + RefPtr<Event> evt = new Event(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, false); + ASSERT(!ec); + + if (m_state == Loaded) { + evt = new Event(loadEvent, false, false); + if (m_onLoadListener) { + evt->setTarget(this); + evt->setCurrentTarget(this); + m_onLoadListener->handleEvent(evt.get(), false); + } + + dispatchEvent(evt, ec, false); + ASSERT(!ec); + } +} + +bool XMLHttpRequest::urlMatchesDocumentDomain(const KURL& url) const +{ + // a local file can load anything + if (m_doc->isAllowedToLoadLocalResources()) + return true; + + // but a remote document can only load from the same port on the server + KURL documentURL(m_doc->url()); + if (documentURL.protocol().lower() == url.protocol().lower() + && documentURL.host().lower() == url.host().lower() + && documentURL.port() == url.port()) + return true; + + return false; +} + +void XMLHttpRequest::open(const String& method, const KURL& url, bool async, ExceptionCode& ec) +{ + internalAbort(); + XMLHttpRequestState previousState = m_state; + m_state = Uninitialized; + m_aborted = false; + + // clear stuff from possible previous load + m_requestHeaders.clear(); + m_response = ResourceResponse(); + { + KJS::JSLock lock; + m_responseText = ""; + } + m_createdDocument = false; + m_responseXML = 0; + + ASSERT(m_state == Uninitialized); + + if (!urlMatchesDocumentDomain(url)) { + ec = XMLHttpRequestException::PERMISSION_DENIED; + return; + } + + 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 = XMLHttpRequestException::PERMISSION_DENIED; + 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 != Open) + changeState(Open); + else + m_state = Open; +} + +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); +} + +void XMLHttpRequest::send(const String& body, ExceptionCode& ec) +{ + if (!m_doc) + return; + + if (m_state != Open || m_loader) { + ec = INVALID_STATE_ERR; + return; + } + + m_aborted = false; + + ResourceRequest request(m_url); + request.setHTTPMethod(m_method); + + if (!body.isNull() && m_method != "GET" && m_method != "HEAD" && (m_url.protocol().lower() == "http" || m_url.protocol().lower() == "https")) { + String contentType = getRequestHeader("Content-Type"); + if (contentType.isEmpty()) { + ExceptionCode ec = 0; + Settings* settings = m_doc->settings(); + if (settings && settings->usesDashboardBackwardCompatibilityMode()) + setRequestHeader("Content-Type", "application/x-www-form-urlencoded", ec); + else + setRequestHeader("Content-Type", "application/xml", ec); + ASSERT(ec == 0); + } + + // FIXME: must use xmlEncoding for documents. + String charset = "UTF-8"; + + TextEncoding m_encoding(charset); + if (!m_encoding.isValid()) // FIXME: report an error? + m_encoding = UTF8Encoding(); + request.setHTTPBody(FormData::create(m_encoding.encode(body.characters(), body.length()))); + } + + if (m_requestHeaders.size() > 0) + request.addHTTPHeaderFields(m_requestHeaders); + + if (!m_async) { + Vector<char> data; + ResourceError error; + ResourceResponse response; + + { + // avoid deadlock in case the loader wants to use JS on a background thread + KJS::JSLock::DropAllLocks dropLocks; + if (m_doc->frame()) + m_doc->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); + else + ec = XMLHttpRequestException::NETWORK_ERR; + + return; + } + + // 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>. + m_loader = SubresourceLoader::create(m_doc->frame(), this, request, false, true, 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. + ref(); + + KJS::JSLock lock; + gcProtectNullTolerant(ScriptInterpreter::getDOMObject(this)); + } +} + +void XMLHttpRequest::abort() +{ + bool sendFlag = m_loader; + + internalAbort(); + + // Clear headers as required by the spec + m_requestHeaders.clear(); + + if ((m_state <= Open && !sendFlag) || m_state == Loaded) + m_state = Uninitialized; + else { + ASSERT(!m_loader); + changeState(Loaded); + m_state = Uninitialized; + } +} + +void XMLHttpRequest::internalAbort() +{ + bool hadLoader = m_loader; + + m_aborted = true; + + if (hadLoader) { + m_loader->cancel(); + m_loader = 0; + } + + m_decoder = 0; + + if (hadLoader) + dropProtection(); +} + +void XMLHttpRequest::dropProtection() +{ + { + KJS::JSLock lock; + KJS::JSValue* wrapper = ScriptInterpreter::getDOMObject(this); + KJS::gcUnprotectNullTolerant(wrapper); + + // 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 (wrapper) + KJS::Collector::reportExtraMemoryCost(m_responseText.size() * 2); + } + + deref(); +} + +void XMLHttpRequest::overrideMIMEType(const String& override) +{ + m_mimeTypeOverride = override; +} + +void XMLHttpRequest::setRequestHeader(const String& name, const String& value, ExceptionCode& ec) +{ + if (m_state != Open || m_loader) { + Settings* settings = m_doc ? m_doc->settings() : 0; + if (settings && settings->usesDashboardBackwardCompatibilityMode()) + return; + + 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 (!m_doc->isAllowedToLoadLocalResources() && !isSafeRequestHeader(name)) { + if (m_doc && m_doc->frame() && m_doc->frame()->page()) + m_doc->frame()->page()->chrome()->addMessageToConsole(JSMessageSource, ErrorMessageLevel, "Refused to set unsafe header " + name, 1, String()); + return; + } + + if (!m_requestHeaders.contains(name)) { + m_requestHeaders.set(name, value); + return; + } + + String oldValue = m_requestHeaders.get(name); + m_requestHeaders.set(name, oldValue + ", " + value); +} + +String XMLHttpRequest::getRequestHeader(const String& name) const +{ + return m_requestHeaders.get(name); +} + +String XMLHttpRequest::getAllResponseHeaders(ExceptionCode& ec) const +{ + if (m_state < Receiving) { + 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) { + 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 < Receiving) { + ec = INVALID_STATE_ERR; + return ""; + } + + if (!isValidToken(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::getStatus(ExceptionCode& ec) const +{ + if (m_response.httpStatusCode()) + return m_response.httpStatusCode(); + + if (m_state == Open) { + // 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::getStatusText(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 == Open) { + // See comments in getStatus() above. + ec = INVALID_STATE_ERR; + } + + return String(); +} + +void XMLHttpRequest::processSyncLoadResults(const Vector<char>& data, const ResourceResponse& response) +{ + if (!urlMatchesDocumentDomain(response.url())) { + internalAbort(); + return; + } + + didReceiveResponse(0, response); + changeState(Sent); + if (m_aborted) + return; + + const char* bytes = static_cast<const char*>(data.data()); + int len = static_cast<int>(data.size()); + + didReceiveData(0, bytes, len); + if (m_aborted) + return; + + didFinishLoading(0); +} + +void XMLHttpRequest::didFail(SubresourceLoader* loader, const ResourceError&) +{ + didFinishLoading(loader); +} + +void XMLHttpRequest::didFinishLoading(SubresourceLoader* loader) +{ + if (m_aborted) + return; + + ASSERT(loader == m_loader); + + if (m_state < Sent) + changeState(Sent); + + { + KJS::JSLock lock; + if (m_decoder) + m_responseText += m_decoder->flush(); + } + + bool hadLoader = m_loader; + m_loader = 0; + + changeState(Loaded); + m_decoder = 0; + + if (hadLoader) + dropProtection(); +} + +void XMLHttpRequest::willSendRequest(SubresourceLoader*, ResourceRequest& request, const ResourceResponse& redirectResponse) +{ + if (!urlMatchesDocumentDomain(request.url())) + internalAbort(); +} + +void XMLHttpRequest::didReceiveResponse(SubresourceLoader*, const ResourceResponse& response) +{ + m_response = response; + m_encoding = extractCharsetFromMediaType(m_mimeTypeOverride); + if (m_encoding.isEmpty()) + m_encoding = response.textEncodingName(); + +} + +void XMLHttpRequest::receivedCancellation(SubresourceLoader*, const AuthenticationChallenge& challenge) +{ + m_response = challenge.failureResponse(); +} + +void XMLHttpRequest::didReceiveData(SubresourceLoader*, const char* data, int len) +{ + if (m_state < Sent) + changeState(Sent); + + if (!m_decoder) { + if (!m_encoding.isEmpty()) + m_decoder = new TextResourceDecoder("text/plain", m_encoding); + // allow TextResourceDecoder to look inside the m_response if it's XML or HTML + else if (responseIsXML()) + m_decoder = new TextResourceDecoder("application/xml"); + else if (responseMIMEType() == "text/html") + m_decoder = new TextResourceDecoder("text/html", "UTF-8"); + else + m_decoder = new TextResourceDecoder("text/plain", "UTF-8"); + } + if (len == 0) + return; + + if (len == -1) + len = strlen(data); + + String decoded = m_decoder->decode(data, len); + + { + KJS::JSLock lock; + m_responseText += decoded; + } + + if (!m_aborted) { + if (m_state != Receiving) + changeState(Receiving); + else + // Firefox calls readyStateChanged every time it receives data, 4449442 + callReadyStateChangeListener(); + } +} + +void XMLHttpRequest::cancelRequests(Document* m_doc) +{ + RequestsSet* requests = requestsByDocument().get(m_doc); + if (!requests) + return; + RequestsSet copy = *requests; + RequestsSet::const_iterator end = copy.end(); + for (RequestsSet::const_iterator it = copy.begin(); it != end; ++it) + (*it)->internalAbort(); +} + +void XMLHttpRequest::detachRequests(Document* m_doc) +{ + RequestsSet* requests = requestsByDocument().get(m_doc); + if (!requests) + return; + requestsByDocument().remove(m_doc); + RequestsSet::const_iterator end = requests->end(); + for (RequestsSet::const_iterator it = requests->begin(); it != end; ++it) { + (*it)->m_doc = 0; + (*it)->internalAbort(); + } + delete requests; +} + +} // end namespace diff --git a/WebCore/xml/XMLHttpRequest.h b/WebCore/xml/XMLHttpRequest.h new file mode 100644 index 0000000..a1c7119 --- /dev/null +++ b/WebCore/xml/XMLHttpRequest.h @@ -0,0 +1,147 @@ +/* + * 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 "EventTarget.h" +#include "ResourceResponse.h" +#include "SubresourceLoaderClient.h" + +namespace WebCore { + +class Document; +class TextResourceDecoder; + +// these exact numeric values are important because JS expects them +enum XMLHttpRequestState { + Uninitialized = 0, // The initial value. + Open = 1, // The open() method has been successfully called. + Sent = 2, // The user agent successfully completed the request, but no data has yet been received. + Receiving = 3, // Immediately before receiving the message body (if any). All HTTP headers have been received. + Loaded = 4 // The data transfer has been completed. +}; + +class XMLHttpRequest : public RefCounted<XMLHttpRequest>, public EventTarget, private SubresourceLoaderClient { +public: + static PassRefPtr<XMLHttpRequest> create(Document *document) { return adoptRef(new XMLHttpRequest(document)); } + ~XMLHttpRequest(); + + virtual XMLHttpRequest* toXMLHttpRequest() { return this; } + + static void detachRequests(Document*); + static void cancelRequests(Document*); + + String getStatusText(ExceptionCode&) const; + int getStatus(ExceptionCode&) const; + XMLHttpRequestState getReadyState() 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(const String& body, 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 KJS::UString& getResponseText(ExceptionCode&) const; + Document* getResponseXML(ExceptionCode&) const; + + void setOnReadyStateChangeListener(EventListener*); + EventListener* onReadyStateChangeListener() const; + void setOnLoadListener(EventListener*); + EventListener* onLoadListener() const; + + typedef Vector<RefPtr<EventListener> > ListenerVector; + typedef HashMap<AtomicStringImpl*, 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&, bool tempEvent = false); + EventListenersMap& eventListeners() { return m_eventListeners; } + + Document* document() const { return m_doc; } + + using RefCounted<XMLHttpRequest>::ref; + using RefCounted<XMLHttpRequest>::deref; + +private: + XMLHttpRequest(Document*); + + virtual void refEventTarget() { ref(); } + virtual void derefEventTarget() { deref(); } + + bool urlMatchesDocumentDomain(const KURL&) const; + + virtual void willSendRequest(SubresourceLoader*, ResourceRequest& request, const ResourceResponse& redirectResponse); + 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&); + + void processSyncLoadResults(const Vector<char>& data, const ResourceResponse&); + + String responseMIMEType() const; + bool responseIsXML() const; + + String getRequestHeader(const String& name) const; + + void changeState(XMLHttpRequestState newState); + void callReadyStateChangeListener(); + void dropProtection(); + void internalAbort(); + + Document* m_doc; + + RefPtr<EventListener> m_onReadyStateChangeListener; + RefPtr<EventListener> m_onLoadListener; + EventListenersMap m_eventListeners; + + KURL m_url; + String m_method; + HTTPHeaderMap m_requestHeaders; + String m_mimeTypeOverride; + bool m_async; + + RefPtr<SubresourceLoader> m_loader; + XMLHttpRequestState m_state; + + ResourceResponse m_response; + String m_encoding; + + RefPtr<TextResourceDecoder> m_decoder; + + // Unlike most strings in the DOM, we keep this as a KJS::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. + KJS::UString m_responseText; + mutable bool m_createdDocument; + mutable RefPtr<Document> m_responseXML; + + bool m_aborted; +}; + +} // namespace WebCore + +#endif // XMLHttpRequest_h diff --git a/WebCore/xml/XMLHttpRequestException.h b/WebCore/xml/XMLHttpRequestException.h new file mode 100644 index 0000000..1540d4b --- /dev/null +++ b/WebCore/xml/XMLHttpRequestException.h @@ -0,0 +1,54 @@ +/* + * 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 XMLHttpRequestException_h +#define XMLHttpRequestException_h + +#include "ExceptionBase.h" + +namespace WebCore { + + class XMLHttpRequestException : public ExceptionBase { + public: + XMLHttpRequestException(const ExceptionCodeDescription& description) + : ExceptionBase(description) + { + } + + static const int XMLHttpRequestExceptionOffset = 500; + static const int XMLHttpRequestExceptionMax = 699; + + enum XMLHttpRequestExceptionCode { + PERMISSION_DENIED = XMLHttpRequestExceptionOffset, // Use SECURITY_ERR when that's in DOM Core, http://bugs.webkit.org/show_bug.cgi?id=12182 + NETWORK_ERR = XMLHttpRequestExceptionOffset + 101 + }; + }; + +} // namespace WebCore + +#endif // XMLHttpRequestException_h diff --git a/WebCore/xml/XMLHttpRequestException.idl b/WebCore/xml/XMLHttpRequestException.idl new file mode 100644 index 0000000..e6aa182 --- /dev/null +++ b/WebCore/xml/XMLHttpRequestException.idl @@ -0,0 +1,48 @@ +/* + * 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; + }; + +} 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..aa3585c --- /dev/null +++ b/WebCore/xml/XPathException.h @@ -0,0 +1,58 @@ +/* + * 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 XPathException_h +#define XPathException_h + +#if ENABLE(XPATH) + +#include "ExceptionBase.h" + +namespace WebCore { + + class XPathException : public ExceptionBase { + public: + XPathException(const ExceptionCodeDescription& description) + : ExceptionBase(description) + { + } + + static const int XPathExceptionOffset = 400; + static const int XPathExceptionMax = 499; + + enum XPathExceptionCode { + INVALID_EXPRESSION_ERR = XPathExceptionOffset + 51, + TYPE_ERR + }; + }; + +} // 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..1afe888 --- /dev/null +++ b/WebCore/xml/XPathExpression.idl @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2006 Apple Computer, Inc. + * Copyright (C) 2006 Samuel Weinig <sam.weinig@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +module xpath { + + + interface [Conditional=XPATH] XPathExpression { + [OldStyleObjC] XPathResult evaluate(in Node contextNode, + in unsigned short type, + in XPathResult inResult) + raises(DOMException); + }; + +} diff --git a/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..6a483f1 --- /dev/null +++ b/WebCore/xml/XPathExpressionNode.h @@ -0,0 +1,86 @@ +/* + * 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 <wtf/HashMap.h> +#include <wtf/Vector.h> + +namespace WebCore { + + class Node; + + 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..68c6c6e --- /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 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..75d2285 --- /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(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(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(ERROR); + return Token(VARIABLEREFERENCE, name); + } + } + + String name; + if (!lexNCName(name)) + return Token(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(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(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..e7c4db3 --- /dev/null +++ b/WebCore/xml/XPathResult.cpp @@ -0,0 +1,243 @@ +/* + * 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: + InvalidatingEventListener(XPathResult* result) : m_result(result) { } + virtual void handleEvent(Event*, bool) { m_result->invalidateIteratorState(); } +private: + XPathResult* m_result; +}; + +XPathResult::XPathResult(EventTargetNode* eventTarget, const Value& value) + : m_value(value) + , m_eventTarget(eventTarget) +{ + m_eventListener = new InvalidatingEventListener(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..a7cd21f --- /dev/null +++ b/WebCore/xml/XPathUtil.cpp @@ -0,0 +1,84 @@ +/* + * 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 "Node.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) +{ + return node && ( + node->nodeType() == Node::ELEMENT_NODE || + node->nodeType() == Node::ATTRIBUTE_NODE || + node->nodeType() == Node::TEXT_NODE || + node->nodeType() == Node::CDATA_SECTION_NODE || + node->nodeType() == Node::PROCESSING_INSTRUCTION_NODE || + node->nodeType() == Node::COMMENT_NODE || + node->nodeType() == Node::DOCUMENT_NODE || + node->nodeType() == Node::XPATH_NAMESPACE_NODE) && + !(node->nodeType() == Node::TEXT_NODE && node->parentNode() && node->parentNode()->isAttributeNode()); +} + +} +} + +#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..7eae512 --- /dev/null +++ b/WebCore/xml/XPathValue.h @@ -0,0 +1,105 @@ +/* + * 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_number(value) {} + Value(unsigned long value) : m_type(NumberValue), m_number(value) {} + Value(double value) : m_type(NumberValue), m_number(value) {} + + Value(const char* value) : m_type(StringValue), m_data(ValueData::create(value)) {} + Value(const String& value) : m_type(StringValue), m_data(ValueData::create(value)) {} + Value(const NodeSet& value) : m_type(NodeSetValue), m_data(ValueData::create(value)) {} + Value(Node* value) : m_type(NodeSetValue), 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_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) + { + } + } +} + +#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..2f28add --- /dev/null +++ b/WebCore/xml/XSLImportRule.cpp @@ -0,0 +1,116 @@ +/* + * 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(StyleBase* 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->deref(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 = new XSLStyleSheet(this, url); + + XSLStyleSheet* parent = parentStyleSheet(); + if (parent) + m_styleSheet->setOwnerDocument(parent->ownerDocument()); + + m_styleSheet->parseString(sheet); + m_loading = false; + + 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 = static_cast<StyleBase*>(this)->parent(); parent; parent = parent->parent()) { + if (absHref == parent->baseURL()) + return; + } + + m_cachedSheet = docLoader->requestXSLStyleSheet(absHref); + + if (m_cachedSheet) { + m_cachedSheet->ref(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..e4bbc4b --- /dev/null +++ b/WebCore/xml/XSLImportRule.h @@ -0,0 +1,64 @@ +/* + * This file is part of the XSL implementation. + * + * Copyright (C) 2004, 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. + * + */ + +#ifndef XSLImportRule_h +#define XSLImportRule_h + +#if ENABLE(XSLT) + +#include "CachedResourceClient.h" +#include "StyleBase.h" +#include "XSLStyleSheet.h" + +namespace WebCore { + +class CachedXSLStyleSheet; + +class XSLImportRule : public CachedResourceClient, public StyleBase { +public: + XSLImportRule(StyleBase* parent, const String& href); + virtual ~XSLImportRule(); + + String href() const { return m_strHref; } + XSLStyleSheet* styleSheet() const { return m_styleSheet.get(); } + + virtual bool isImportRule() { return true; } + XSLStyleSheet* parentStyleSheet() const; + + // from CachedResourceClient + virtual void setXSLStyleSheet(const String& url, const String& sheet); + + bool isLoading(); + void loadSheet(); + +protected: + String m_strHref; + RefPtr<XSLStyleSheet> m_styleSheet; + 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..64c712f --- /dev/null +++ b/WebCore/xml/XSLStyleSheet.cpp @@ -0,0 +1,284 @@ +/* + * 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 "DocLoader.h" +#include "Document.h" +#include "loader.h" +#include "Node.h" +#include "Page.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 (m_parentNode) + m_parentNode->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; + + Chrome* chrome = 0; + if (Page* page = ownerDocument()->page()) + chrome = page->chrome(); + xmlSetStructuredErrorFunc(chrome, XSLTProcessor::parseErrorFunc); + + 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); + + 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 = new XSLImportRule(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..0509cc0 --- /dev/null +++ b/WebCore/xml/XSLStyleSheet.h @@ -0,0 +1,84 @@ +/* + * 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> + +namespace WebCore { + +class DocLoader; +class Document; +class XSLImportRule; + +class XSLStyleSheet : public StyleSheet { +public: + XSLStyleSheet(Node* parentNode, const String& href = String(), bool embedded = false); + XSLStyleSheet(XSLImportRule* parentImport, const String& href = String()); + ~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; } + +protected: + 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..b8ce7f3 --- /dev/null +++ b/WebCore/xml/XSLTProcessor.cpp @@ -0,0 +1,448 @@ +/* + * 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 "Cache.h" +#include "DOMImplementation.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::parseErrorFunc(void* userData, xmlError* error) +{ + Chrome* chrome = static_cast<Chrome*>(userData); + if (!chrome) + 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; + } + + chrome->addMessageToConsole(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; + + if (globalDocLoader->frame()) + globalDocLoader->frame()->loader()->loadResourceSynchronously(url, error, response, data); + + Chrome* chrome = 0; + if (Page* page = globalProcessor->xslStylesheet()->ownerDocument()->page()) + chrome = page->chrome(); + xmlSetStructuredErrorFunc(chrome, XSLTProcessor::parseErrorFunc); + + // 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); + + 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); +} + + +RefPtr<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); + } + + result->open(); + if (sourceIsDocument) { + result->setURL(ownerDocument->url()); + result->setBaseURL(ownerDocument->baseURL()); + } + + RefPtr<TextResourceDecoder> decoder = new TextResourceDecoder(sourceMIMEType); + decoder->setEncoding(sourceEncoding.isEmpty() ? UTF8Encoding() : TextEncoding(sourceEncoding), TextResourceDecoder::EncodingFromXMLHeader); + result->setDecoder(decoder.get()); + + result->write(documentSource); + result->finishParsing(); + result->close(); + + return result; +} + +static inline RefPtr<DocumentFragment> createFragmentFromSource(String sourceString, 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 = new XSLStyleSheet(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; +} + +RefPtr<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); +} + +RefPtr<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..f96ad15 --- /dev/null +++ b/WebCore/xml/XSLTProcessor.h @@ -0,0 +1,84 @@ +/* + * This file is part of the XSL implementation. + * + * Copyright (C) 2004, 2007 Apple, 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. + * + */ + +#ifndef XSLTProcessor_h +#define XSLTProcessor_h + +#if ENABLE(XSLT) + +#include "StringHash.h" +#include "XSLStyleSheet.h" +#include <wtf/HashMap.h> +#include <libxml/parserInternals.h> +#include <libxslt/documents.h> + +namespace WebCore { + +class Frame; +class Node; +class Document; +class DocumentFragment; + +class XSLTProcessor : public RefCounted<XSLTProcessor> { +public: + static PassRefPtr<XSLTProcessor> create() { return adoptRef(new XSLTProcessor); } + + void setXSLStylesheet(XSLStyleSheet* styleSheet) { m_stylesheet = styleSheet; } + bool transformToString(Node* source, String& resultMIMEType, String& resultString, String& resultEncoding); + RefPtr<Document> createDocumentFromSource(const String& source, const String& sourceEncoding, const String& sourceMIMEType, Node* sourceNode, Frame* frame); + + // DOM methods + void importStylesheet(Node* style) { m_stylesheetRootNode = style; } + RefPtr<DocumentFragment> transformToFragment(Node* source, Document* ouputDoc); + RefPtr<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 = NULL; m_stylesheetRootNode = NULL; m_parameters.clear(); } + + static void parseErrorFunc(void* userData, xmlError*); + +public: + // Only for libXSLT callbacks + XSLStyleSheet* xslStylesheet() const { return m_stylesheet.get(); } + + typedef HashMap<String, String> ParameterMap; + +private: + XSLTProcessor() { } + + // Convert a libxml doc ptr to a KHTML DOM Document + RefPtr<Document> documentFromXMLDocPtr(xmlDocPtr resultDoc, xsltStylesheetPtr sheet, Document* ownerDocument, bool sourceIsDocument); + + RefPtr<XSLStyleSheet> m_stylesheet; + RefPtr<Node> m_stylesheetRootNode; + + ParameterMap m_parameters; +}; + +} + +#endif +#endif diff --git a/WebCore/xml/XSLTUnicodeSort.cpp b/WebCore/xml/XSLTUnicodeSort.cpp new file mode 100644 index 0000000..e1ecdc4 --- /dev/null +++ b/WebCore/xml/XSLTUnicodeSort.cpp @@ -0,0 +1,406 @@ +/* + * 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 <libxslt/templates.h> +#include <libxslt/xsltutils.h> + +#if USE(ICU_UNICODE) +#include <unicode/ucnv.h> +#include <unicode/ucol.h> +#include <unicode/ustring.h> +#define WTF_USE_ICU_COLLATION !UCONFIG_NO_COLLATION +#endif + +#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 USE(ICU_COLLATION) + UCollator *coll = 0; + UConverter *conv; + UErrorCode status; + UChar *target,*target2; + int targetlen, target2len; +#endif + + 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; + +#if USE(ICU_COLLATION) + status = U_ZERO_ERROR; + conv = ucnv_open("UTF8", &status); + if (U_FAILURE(status)) + xsltTransformError(ctxt, NULL, NULL, "xsltICUSortFunction: Error opening converter\n"); + + if (comp->has_lang) + coll = ucol_open((const char*)comp->lang, &status); + if (U_FAILURE(status) || !comp->has_lang) { + status = U_ZERO_ERROR; + coll = ucol_open("en", &status); + } + if (U_FAILURE(status)) + xsltTransformError(ctxt, NULL, NULL, "xsltICUSortFunction: Error opening collator\n"); + + if (comp->lower_first) + ucol_setAttribute(coll,UCOL_CASE_FIRST,UCOL_LOWER_FIRST,&status); + else + ucol_setAttribute(coll,UCOL_CASE_FIRST,UCOL_UPPER_FIRST,&status); + if (U_FAILURE(status)) + xsltTransformError(ctxt, NULL, NULL, "xsltICUSortFunction: Error setting collator attribute\n"); +#endif + + /* 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 { +#if USE(ICU_COLLATION) + targetlen = xmlStrlen(results[j]->stringval) + 1; + target2len = xmlStrlen(results[j + incr]->stringval) + 1; + target = (UChar*)xmlMalloc(targetlen * sizeof(UChar)); + target2 = (UChar*)xmlMalloc(target2len * sizeof(UChar)); + targetlen = ucnv_toUChars(conv, target, targetlen, (const char*)results[j]->stringval, -1, &status); + target2len = ucnv_toUChars(conv, target2, target2len, (const char*)results[j+incr]->stringval, -1, &status); + tst = ucol_strcoll(coll, target, u_strlen(target), target2, u_strlen(target2)); + xmlFree(target); + xmlFree(target2); +#else + tst = xmlStrcmp(results[j]->stringval, + results[j + incr]->stringval); +#endif + } + 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 { +#if USE(ICU_COLLATION) + targetlen = xmlStrlen(res[j]->stringval) + 1; + target2len = xmlStrlen(res[j + incr]->stringval) + 1; + target = (UChar*)xmlMalloc(targetlen * sizeof(UChar)); + target2 = (UChar*)xmlMalloc(target2len * sizeof(UChar)); + targetlen = ucnv_toUChars(conv, target, targetlen, (const char*)res[j]->stringval, -1, &status); + target2len = ucnv_toUChars(conv, target2, target2len, (const char*)res[j+incr]->stringval, -1, &status); + tst = ucol_strcoll(coll, target, u_strlen(target), target2, u_strlen(target2)); + xmlFree(target); + xmlFree(target2); +#else + tst = xmlStrcmp(res[j]->stringval, + res[j + incr]->stringval); +#endif + } + 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; + } + } + } + +#if USE(ICU_COLLATION) + ucol_close(coll); + ucnv_close(conv); +#endif + + 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..ff5b253 --- /dev/null +++ b/WebCore/xml/XSLTUnicodeSort.h @@ -0,0 +1,44 @@ +/* + * 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 + +// FIXME: Only works as advertised for ICU with collation support enabled yet, falls back on binary comparison otherwise.. +// We need to make an abstraction for Unicode collation to implement this for other libraries. +#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..bfda2f2 --- /dev/null +++ b/WebCore/xml/xmlattrs.in @@ -0,0 +1,3 @@ +base +lang +space |