diff options
author | The Android Open Source Project <initial-contribution@android.com> | 2008-12-17 18:05:15 -0800 |
---|---|---|
committer | The Android Open Source Project <initial-contribution@android.com> | 2008-12-17 18:05:15 -0800 |
commit | 1cbdecfa9fc428ac2d8aca0fa91c9580b3d57353 (patch) | |
tree | 4457a7306ea5acb43fe05bfe0973b1f7faf97ba2 /WebCore/xml | |
parent | 9364f22aed35e1a1e9d07c121510f80be3ab0502 (diff) | |
download | external_webkit-1cbdecfa9fc428ac2d8aca0fa91c9580b3d57353.zip external_webkit-1cbdecfa9fc428ac2d8aca0fa91c9580b3d57353.tar.gz external_webkit-1cbdecfa9fc428ac2d8aca0fa91c9580b3d57353.tar.bz2 |
Code drop from //branches/cupcake/...@124589
Diffstat (limited to 'WebCore/xml')
40 files changed, 1796 insertions, 590 deletions
diff --git a/WebCore/xml/DOMParser.cpp b/WebCore/xml/DOMParser.cpp index 8be4d14..448dcb1 100644 --- a/WebCore/xml/DOMParser.cpp +++ b/WebCore/xml/DOMParser.cpp @@ -1,6 +1,5 @@ /* - * This file is part of the KDE libraries - * Copyright (C) 2003, 2006 Apple Computer, Inc. + * Copyright (C) 2003, 2006, 2008 Apple Inc. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -30,7 +29,7 @@ PassRefPtr<Document> DOMParser::parseFromString(const String& str, const String& if (!DOMImplementation::isXMLMIMEType(contentType)) return 0; - RefPtr<Document> doc = DOMImplementation::instance()->createDocument(contentType, 0, false); + RefPtr<Document> doc = DOMImplementation::createDocument(contentType, 0, false); doc->open(); doc->write(str); diff --git a/WebCore/xml/DOMParser.h b/WebCore/xml/DOMParser.h index 2097a6e..5036d22 100644 --- a/WebCore/xml/DOMParser.h +++ b/WebCore/xml/DOMParser.h @@ -1,5 +1,4 @@ /* - * 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 @@ -24,12 +23,19 @@ #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/NativeXPathNSResolver.h b/WebCore/xml/NativeXPathNSResolver.h index c3070d8..212b929 100644 --- a/WebCore/xml/NativeXPathNSResolver.h +++ b/WebCore/xml/NativeXPathNSResolver.h @@ -29,21 +29,20 @@ #if ENABLE(XPATH) #include "XPathNSResolver.h" -#include <wtf/Forward.h> +#include "Node.h" #include <wtf/RefPtr.h> namespace WebCore { - class Node; - class NativeXPathNSResolver : public XPathNSResolver { public: - NativeXPathNSResolver(PassRefPtr<Node>); + 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; }; diff --git a/WebCore/xml/XMLHttpRequest.cpp b/WebCore/xml/XMLHttpRequest.cpp index 7413f72..67aba0b 100644 --- a/WebCore/xml/XMLHttpRequest.cpp +++ b/WebCore/xml/XMLHttpRequest.cpp @@ -1,8 +1,7 @@ /* - * This file is part of the KDE libraries - * Copyright (C) 2004, 2006 Apple Computer, Inc. + * 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> + * Copyright (C) 2007, 2008 Julien Chaffraix <jchaffraix@webkit.org> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -23,78 +22,75 @@ #include "XMLHttpRequest.h" #include "CString.h" -#include "Cache.h" +#include "Console.h" #include "DOMImplementation.h" +#include "DOMWindow.h" #include "Event.h" #include "EventException.h" #include "EventListener.h" #include "EventNames.h" -#include "ExceptionCode.h" -#include "FormData.h" +#include "File.h" #include "Frame.h" #include "FrameLoader.h" -#include "HTMLDocument.h" #include "HTTPParsers.h" +#include "InspectorController.h" +#include "JSDOMBinding.h" +#include "JSDOMWindow.h" +#include "KURL.h" +#include "KURLHash.h" #include "Page.h" -#include "PlatformString.h" -#include "RegularExpression.h" -#include "ResourceHandle.h" -#include "ResourceRequest.h" #include "Settings.h" #include "SubresourceLoader.h" -#include "TextEncoding.h" +#include "SystemTime.h" #include "TextResourceDecoder.h" #include "XMLHttpRequestException.h" -#include "kjs_binding.h" -#include <kjs/protect.h> -#include <wtf/Vector.h> +#include "XMLHttpRequestProgressEvent.h" +#include "XMLHttpRequestUpload.h" +#include "markup.h" +#include <runtime/JSLock.h> namespace WebCore { -using namespace EventNames; +struct PreflightResultCacheItem { + PreflightResultCacheItem(unsigned expiryDelta, bool credentials, HashSet<String>* methods, HashSet<String, CaseFoldingHash>* headers) + : m_absoluteExpiryTime(currentTime() + expiryDelta) + , m_credentials(credentials) + , m_methods(methods) + , m_headers(headers) + { + } -typedef HashSet<XMLHttpRequest*> RequestsSet; + // FIXME: A better solution to holding onto the absolute expiration time might be + // to start a timer for the expiration delta, that removes this from the cache when + // it fires. + double m_absoluteExpiryTime; + bool m_credentials; + OwnPtr<HashSet<String> > m_methods; + OwnPtr<HashSet<String, CaseFoldingHash> > m_headers; +}; -static HashMap<Document*, RequestsSet*>& requestsByDocument() -{ - static HashMap<Document*, RequestsSet*> map; - return map; -} +typedef HashMap<std::pair<String, KURL>, PreflightResultCacheItem*> PreflightResultCache; -static void addToRequestsByDocument(Document* doc, XMLHttpRequest* req) +static PreflightResultCache& preflightResultCache() { - 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 PreflightResultCache cache; + return cache; } -static void removeFromRequestsByDocument(Document* doc, XMLHttpRequest* req) +static void appendPreflightResultCacheEntry(String origin, KURL url, unsigned expiryDelta, + bool credentials, HashSet<String>* methods, HashSet<String, CaseFoldingHash>* headers) { - ASSERT(doc); - ASSERT(req); + ASSERT(!preflightResultCache().contains(std::make_pair(origin, url))); - RequestsSet* requests = requestsByDocument().get(doc); - ASSERT(requests); - ASSERT(requests->contains(req)); - requests->remove(req); - if (requests->isEmpty()) { - requestsByDocument().remove(doc); - delete requests; - } + PreflightResultCacheItem* item = new PreflightResultCacheItem(expiryDelta, credentials, methods, headers); + preflightResultCache().set(std::make_pair(origin, url), item); } static bool isSafeRequestHeader(const String& name) { static HashSet<String, CaseFoldingHash> forbiddenHeaders; static String proxyString("proxy-"); + static String secString("sec-"); if (forbiddenHeaders.isEmpty()) { forbiddenHeaders.add("accept-charset"); @@ -114,7 +110,28 @@ static bool isSafeRequestHeader(const String& name) forbiddenHeaders.add("via"); } - return !forbiddenHeaders.contains(name) && !name.startsWith(proxyString, false); + return !forbiddenHeaders.contains(name) && !name.startsWith(proxyString, false) && + !name.startsWith(secString, false); +} + +static bool isOnAccessControlSimpleRequestHeaderWhitelist(const String& name) +{ + return equalIgnoringCase(name, "accept") || equalIgnoringCase(name, "accept-language") || equalIgnoringCase(name, "content-type"); +} + +static bool isOnAccessControlResponseHeaderWhitelist(const String& name) +{ + static HashSet<String, CaseFoldingHash> allowedHeaders; + if (allowedHeaders.isEmpty()) { + allowedHeaders.add("cache-control"); + allowedHeaders.add("content-language"); + allowedHeaders.add("content-type"); + allowedHeaders.add("expires"); + allowedHeaders.add("last-modified"); + allowedHeaders.add("pragma"); + } + + return allowedHeaders.contains(name); } // Determines if a string is a valid token, as defined by @@ -145,20 +162,50 @@ static bool isValidHeaderValue(const String& name) return !name.contains('\r') && !name.contains('\n'); } - -XMLHttpRequestState XMLHttpRequest::getReadyState() const + +XMLHttpRequest::XMLHttpRequest(Document* doc) + : ActiveDOMObject(doc, this) + , m_async(true) + , m_includeCredentials(false) + , m_state(UNSENT) + , m_identifier(std::numeric_limits<unsigned long>::max()) + , m_responseText("") + , m_createdDocument(false) + , m_error(false) + , m_uploadComplete(false) + , m_sameOriginRequest(true) + , m_inPreflight(false) + , m_receivedLength(0) + , m_lastSendLineNumber(0) +{ + ASSERT(document()); +} + +XMLHttpRequest::~XMLHttpRequest() +{ + if (m_upload) + m_upload->disconnectXMLHttpRequest(); +} + +Document* XMLHttpRequest::document() const +{ + ASSERT(scriptExecutionContext()->isDocument()); + return static_cast<Document*>(scriptExecutionContext()); +} + +XMLHttpRequest::State XMLHttpRequest::readyState() const { return m_state; } -const KJS::UString& XMLHttpRequest::getResponseText(ExceptionCode& ec) const +const JSC::UString& XMLHttpRequest::responseText() const { return m_responseText; } -Document* XMLHttpRequest::getResponseXML(ExceptionCode& ec) const +Document* XMLHttpRequest::responseXML() const { - if (m_state != Loaded) + if (m_state != DONE) return 0; if (!m_createdDocument) { @@ -166,9 +213,9 @@ Document* XMLHttpRequest::getResponseXML(ExceptionCode& ec) const // The W3C spec requires this. m_responseXML = 0; } else { - m_responseXML = m_doc->implementation()->createDocument(0); + m_responseXML = document()->implementation()->createDocument(0); m_responseXML->open(); - m_responseXML->setURL(m_url.deprecatedString()); + 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(); @@ -183,33 +230,20 @@ Document* XMLHttpRequest::getResponseXML(ExceptionCode& ec) const 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) +XMLHttpRequestUpload* XMLHttpRequest::upload() { - m_onLoadListener = eventListener; + if (!m_upload) + m_upload = XMLHttpRequestUpload::create(this); + return m_upload.get(); } void XMLHttpRequest::addEventListener(const AtomicString& eventType, PassRefPtr<EventListener> eventListener, bool) { - EventListenersMap::iterator iter = m_eventListeners.find(eventType.impl()); + EventListenersMap::iterator iter = m_eventListeners.find(eventType); if (iter == m_eventListeners.end()) { ListenerVector listeners; listeners.append(eventListener); - m_eventListeners.add(eventType.impl(), listeners); + m_eventListeners.add(eventType, listeners); } else { ListenerVector& listeners = iter->second; for (ListenerVector::iterator listenerIter = listeners.begin(); listenerIter != listeners.end(); ++listenerIter) @@ -217,13 +251,13 @@ void XMLHttpRequest::addEventListener(const AtomicString& eventType, PassRefPtr< return; listeners.append(eventListener); - m_eventListeners.add(eventType.impl(), listeners); + m_eventListeners.add(eventType, listeners); } } void XMLHttpRequest::removeEventListener(const AtomicString& eventType, EventListener* eventListener, bool) { - EventListenersMap::iterator iter = m_eventListeners.find(eventType.impl()); + EventListenersMap::iterator iter = m_eventListeners.find(eventType); if (iter == m_eventListeners.end()) return; @@ -235,7 +269,7 @@ void XMLHttpRequest::removeEventListener(const AtomicString& eventType, EventLis } } -bool XMLHttpRequest::dispatchEvent(PassRefPtr<Event> evt, ExceptionCode& ec, bool /*tempEvent*/) +bool XMLHttpRequest::dispatchEvent(PassRefPtr<Event> evt, ExceptionCode& ec) { // FIXME: check for other error conditions enumerated in the spec. if (evt->type().isEmpty()) { @@ -243,7 +277,7 @@ bool XMLHttpRequest::dispatchEvent(PassRefPtr<Event> evt, ExceptionCode& ec, boo return true; } - ListenerVector listenersCopy = m_eventListeners.get(evt->type().impl()); + ListenerVector listenersCopy = m_eventListeners.get(evt->type()); for (ListenerVector::const_iterator listenerIter = listenersCopy.begin(); listenerIter != listenersCopy.end(); ++listenerIter) { evt->setTarget(this); evt->setCurrentTarget(this); @@ -253,25 +287,7 @@ bool XMLHttpRequest::dispatchEvent(PassRefPtr<Event> evt, ExceptionCode& ec, boo 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) +void XMLHttpRequest::changeState(State newState) { if (m_state != newState) { m_state = newState; @@ -281,73 +297,29 @@ void XMLHttpRequest::changeState(XMLHttpRequestState newState) void XMLHttpRequest::callReadyStateChangeListener() { - if (!m_doc || !m_doc->frame()) + if (!document() || !document()->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()) -#ifdef ANDROID_FILE_SECURITY - if (FrameLoader::shouldTreatURLAsLocal(url.string())) -#endif - 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; + dispatchReadyStateChangeEvent(); - return false; + if (m_state == DONE) + dispatchLoadEvent(); } void XMLHttpRequest::open(const String& method, const KURL& url, bool async, ExceptionCode& ec) { - abort(); - m_aborted = false; + internalAbort(); + State previousState = m_state; + m_state = UNSENT; + m_error = false; + + m_uploadComplete = false; // clear stuff from possible previous load - m_requestHeaders.clear(); - m_response = ResourceResponse(); - { - KJS::JSLock lock; - m_responseText = ""; - } - m_createdDocument = false; - m_responseXML = 0; + clearResponse(); + clearRequest(); - ASSERT(m_state == Uninitialized); - - if (!urlMatchesDocumentDomain(url)) { - ec = XMLHttpRequestException::PERMISSION_DENIED; - return; - } + ASSERT(m_state == UNSENT); if (!isValidToken(method)) { ec = SYNTAX_ERR; @@ -358,7 +330,7 @@ void XMLHttpRequest::open(const String& method, const KURL& url, bool async, Exc String methodUpper(method.upper()); if (methodUpper == "TRACE" || methodUpper == "TRACK" || methodUpper == "CONNECT") { - ec = XMLHttpRequestException::PERMISSION_DENIED; + ec = SECURITY_ERR; return; } @@ -368,19 +340,26 @@ void XMLHttpRequest::open(const String& method, const KURL& url, bool async, Exc || 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.deprecatedString(); + m_method = methodUpper; else - m_method = method.deprecatedString(); + m_method = method; m_async = async; - changeState(Open); + ASSERT(!m_loader); + + // Check previous state to avoid dispatching readyState event + // when calling open several times in a row. + if (previousState != OPENED) + changeState(OPENED); + else + m_state = OPENED; } void XMLHttpRequest::open(const String& method, const KURL& url, bool async, const String& user, ExceptionCode& ec) { KURL urlWithCredentials(url); - urlWithCredentials.setUser(user.deprecatedString()); + urlWithCredentials.setUser(user); open(method, urlWithCredentials, async, ec); } @@ -388,105 +367,395 @@ void XMLHttpRequest::open(const String& method, const KURL& url, bool async, con 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.deprecatedString()); - urlWithCredentials.setPass(password.deprecatedString()); + urlWithCredentials.setUser(user); + urlWithCredentials.setPass(password); open(method, urlWithCredentials, async, ec); } -void XMLHttpRequest::send(const String& body, ExceptionCode& ec) +bool XMLHttpRequest::initSend(ExceptionCode& ec) { - if (!m_doc) - return; + if (!document()) + return false; - if (m_state != Open) { + if (m_state != OPENED || m_loader) { ec = INVALID_STATE_ERR; - return; + return false; } - - // FIXME: Should this abort or raise an exception instead if we already have a m_loader going? - if (m_loader) + + m_error = false; + return true; +} + +void XMLHttpRequest::send(ExceptionCode& ec) +{ + send(String(), ec); +} + +void XMLHttpRequest::send(Document* document, ExceptionCode& ec) +{ + ASSERT(document); + + if (!initSend(ec)) return; - m_aborted = false; + if (m_method != "GET" && m_method != "HEAD" && (m_url.protocolIs("http") || m_url.protocolIs("https"))) { + String contentType = getRequestHeader("Content-Type"); + if (contentType.isEmpty()) { +#if ENABLE(DASHBOARD_SUPPORT) + Settings* settings = document->settings(); + if (settings && settings->usesDashboardBackwardCompatibilityMode()) + setRequestHeaderInternal("Content-Type", "application/x-www-form-urlencoded"); + else +#endif + // FIXME: this should include the charset used for encoding. + setRequestHeaderInternal("Content-Type", "application/xml"); + } - 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")) { + // FIXME: According to XMLHttpRequest Level 2, this should use the Document.innerHTML algorithm + // from the HTML5 specification to serialize the document. + String body = createMarkup(document); + + // FIXME: this should use value of document.inputEncoding to determine the encoding to use. + TextEncoding encoding = UTF8Encoding(); + m_requestEntityBody = FormData::create(encoding.encode(body.characters(), body.length(), EntitiesForUnencodables)); + if (m_upload) + m_requestEntityBody->setAlwaysStream(true); + } + + createRequest(ec); +} + +void XMLHttpRequest::send(const String& body, ExceptionCode& ec) +{ + if (!initSend(ec)) + return; + + if (!body.isNull() && m_method != "GET" && m_method != "HEAD" && (m_url.protocolIs("http") || m_url.protocolIs("https"))) { String contentType = getRequestHeader("Content-Type"); if (contentType.isEmpty()) { - ExceptionCode ec = 0; - Settings* settings = m_doc->settings(); +#if ENABLE(DASHBOARD_SUPPORT) + Settings* settings = document()->settings(); if (settings && settings->usesDashboardBackwardCompatibilityMode()) - setRequestHeader("Content-Type", "application/x-www-form-urlencoded", ec); + setRequestHeaderInternal("Content-Type", "application/x-www-form-urlencoded"); else - setRequestHeader("Content-Type", "application/xml", ec); - ASSERT(ec == 0); +#endif + setRequestHeaderInternal("Content-Type", "application/xml"); } - // 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(); + m_requestEntityBody = FormData::create(UTF8Encoding().encode(body.characters(), body.length(), EntitiesForUnencodables)); + if (m_upload) + m_requestEntityBody->setAlwaysStream(true); + } + + createRequest(ec); +} - request.setHTTPBody(PassRefPtr<FormData>(new FormData(m_encoding.encode(body.characters(), body.length())))); +void XMLHttpRequest::send(File* body, ExceptionCode& ec) +{ + if (!initSend(ec)) + return; + + if (m_method != "GET" && m_method != "HEAD" && (m_url.protocolIs("http") || m_url.protocolIs("https"))) { + // FIXME: Should we set a Content-Type if one is not set. + // FIXME: add support for uploading bundles. + m_requestEntityBody = FormData::create(); + m_requestEntityBody->appendFile(body->path(), false); } + createRequest(ec); +} + +void XMLHttpRequest::createRequest(ExceptionCode& ec) +{ + if (m_async) { + dispatchLoadStartEvent(); + if (m_requestEntityBody && m_upload) + m_upload->dispatchLoadStartEvent(); + } + + m_sameOriginRequest = document()->securityOrigin()->canRequest(m_url); + + if (!m_sameOriginRequest) { + makeCrossSiteAccessRequest(ec); + return; + } + + makeSameOriginRequest(ec); +} + +void XMLHttpRequest::makeSameOriginRequest(ExceptionCode& ec) +{ + ASSERT(m_sameOriginRequest); + + ResourceRequest request(m_url); + request.setHTTPMethod(m_method); + + if (m_requestEntityBody) { + ASSERT(m_method != "GET"); + request.setHTTPBody(m_requestEntityBody.release()); + } + + if (m_requestHeaders.size() > 0) + request.addHTTPHeaderFields(m_requestHeaders); + + if (m_async) + loadRequestAsynchronously(request); + else + loadRequestSynchronously(request, ec); +} + +bool XMLHttpRequest::isSimpleCrossSiteAccessRequest() const +{ + if (m_method != "GET" && m_method != "POST") + return false; + + HTTPHeaderMap::const_iterator end = m_requestHeaders.end(); + for (HTTPHeaderMap::const_iterator it = m_requestHeaders.begin(); it != end; ++it) { + if (!isOnAccessControlSimpleRequestHeaderWhitelist(it->first)) + return false; + } + + return true; +} + +void XMLHttpRequest::makeCrossSiteAccessRequest(ExceptionCode& ec) +{ + ASSERT(!m_sameOriginRequest); + + if (isSimpleCrossSiteAccessRequest()) + makeSimpleCrossSiteAccessRequest(ec); + else + makeCrossSiteAccessRequestWithPreflight(ec); +} + +void XMLHttpRequest::makeSimpleCrossSiteAccessRequest(ExceptionCode& ec) +{ + ASSERT(isSimpleCrossSiteAccessRequest()); + + KURL url = m_url; + url.setUser(String()); + url.setPass(String()); + + ResourceRequest request(url); + request.setHTTPMethod(m_method); + request.setAllowHTTPCookies(m_includeCredentials); + request.setHTTPOrigin(document()->securityOrigin()->toString()); + if (m_requestHeaders.size() > 0) request.addHTTPHeaderFields(m_requestHeaders); - if (!m_async) { - Vector<char> data; - ResourceError error; - ResourceResponse response; + if (m_async) + loadRequestAsynchronously(request); + else + loadRequestSynchronously(request, ec); +} + +static bool canSkipPrelight(PreflightResultCache::iterator cacheIt, bool includeCredentials, const String& method, const HTTPHeaderMap& requestHeaders) +{ + PreflightResultCacheItem* item = cacheIt->second; + if (item->m_absoluteExpiryTime < currentTime()) + return false; + if (includeCredentials && !item->m_credentials) + return false; + if (!item->m_methods->contains(method) && method != "GET" && method != "POST") + return false; + HTTPHeaderMap::const_iterator end = requestHeaders.end(); + for (HTTPHeaderMap::const_iterator it = requestHeaders.begin(); it != end; ++it) { + if (!item->m_headers->contains(it->first) && !isOnAccessControlSimpleRequestHeaderWhitelist(it->first)) + return false; + } + + return true; +} - { - // 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); +void XMLHttpRequest::makeCrossSiteAccessRequestWithPreflight(ExceptionCode& ec) +{ + String origin = document()->securityOrigin()->toString(); + KURL url = m_url; + url.setUser(String()); + url.setPass(String()); + + bool skipPreflight = false; + + PreflightResultCache::iterator cacheIt = preflightResultCache().find(std::make_pair(origin, url)); + if (cacheIt != preflightResultCache().end()) { + skipPreflight = canSkipPrelight(cacheIt, m_includeCredentials, m_method, m_requestHeaders); + if (!skipPreflight) { + delete cacheIt->second; + preflightResultCache().remove(cacheIt); } + } - 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; + if (!skipPreflight) { + m_inPreflight = true; + ResourceRequest preflightRequest(url); + preflightRequest.setHTTPMethod("OPTIONS"); + preflightRequest.setHTTPHeaderField("Origin", origin); + preflightRequest.setHTTPHeaderField("Access-Control-Request-Method", m_method); + + if (m_requestHeaders.size() > 0) { + Vector<UChar> headerBuffer; + HTTPHeaderMap::const_iterator it = m_requestHeaders.begin(); + append(headerBuffer, it->first); + ++it; + + HTTPHeaderMap::const_iterator end = m_requestHeaders.end(); + for (; it != end; ++it) { + headerBuffer.append(','); + headerBuffer.append(' '); + append(headerBuffer, it->first); + } + + preflightRequest.setHTTPHeaderField("Access-Control-Request-Headers", String::adopt(headerBuffer)); + preflightRequest.addHTTPHeaderFields(m_requestHeaders); + } + + if (m_async) { + loadRequestAsynchronously(preflightRequest); + return; + } + + loadRequestSynchronously(preflightRequest, ec); + m_inPreflight = false; + + if (ec) + return; + } + + // Send the actual request. + ResourceRequest request(url); + request.setHTTPMethod(m_method); + request.setAllowHTTPCookies(m_includeCredentials); + request.setHTTPHeaderField("Origin", origin); + + if (m_requestHeaders.size() > 0) + request.addHTTPHeaderFields(m_requestHeaders); + + if (m_requestEntityBody) { + ASSERT(m_method != "GET"); + request.setHTTPBody(m_requestEntityBody.release()); + } + + if (m_async) { + loadRequestAsynchronously(request); + return; + } + + loadRequestSynchronously(request, ec); +} + +void XMLHttpRequest::handleAsynchronousPreflightResult() +{ + ASSERT(m_inPreflight); + ASSERT(m_async); + + m_inPreflight = false; + + KURL url = m_url; + url.setUser(String()); + url.setPass(String()); + + ResourceRequest request(url); + request.setHTTPMethod(m_method); + request.setAllowHTTPCookies(m_includeCredentials); + request.setHTTPOrigin(document()->securityOrigin()->toString()); + + if (m_requestHeaders.size() > 0) + request.addHTTPHeaderFields(m_requestHeaders); + if (m_requestEntityBody) { + ASSERT(m_method != "GET"); + request.setHTTPBody(m_requestEntityBody.release()); + } + + loadRequestAsynchronously(request); +} + +void XMLHttpRequest::loadRequestSynchronously(ResourceRequest& request, ExceptionCode& ec) +{ + ASSERT(!m_async); + Vector<char> data; + ResourceError error; + ResourceResponse response; + + if (document()->frame()) + m_identifier = document()->frame()->loader()->loadResourceSynchronously(request, error, response, data); + + m_loader = 0; + + // No exception for file:/// resources, see <rdar://problem/4962298>. + // Also, if we have an HTTP response, then it wasn't a network error in fact. + if (error.isNull() || request.url().isLocalFile() || response.httpStatusCode() > 0) { + processSyncLoadResults(data, response, ec); + return; + } + + if (error.isCancellation()) { + abortError(); + ec = XMLHttpRequestException::ABORT_ERR; return; } + networkError(); + ec = XMLHttpRequestException::NETWORK_ERR; +} + + +void XMLHttpRequest::loadRequestAsynchronously(ResourceRequest& request) +{ + ASSERT(m_async); // SubresourceLoader::create can return null here, for example if we're no longer attached to a page. // This is true while running onunload handlers. // FIXME: We need to be able to send XMLHttpRequests from onunload, <http://bugs.webkit.org/show_bug.cgi?id=10904>. // FIXME: Maybe create can return null for other reasons too? // We need to keep content sniffing enabled for local files due to CFNetwork not providing a MIME type // for local files otherwise, <rdar://problem/5671813>. - m_loader = SubresourceLoader::create(m_doc->frame(), this, request, false, true, request.url().isLocalFile()); + bool sendResourceLoadCallbacks = !m_inPreflight; + m_loader = SubresourceLoader::create(document()->frame(), this, request, false, sendResourceLoadCallbacks, request.url().isLocalFile()); if (m_loader) { // Neither this object nor the JavaScript wrapper should be deleted while // a request is in progress because we need to keep the listeners alive, // and they are referenced by the JavaScript wrapper. - ref(); - - KJS::JSLock lock; - KJS::gcProtectNullTolerant(KJS::ScriptInterpreter::getDOMObject(this)); + setPendingActivity(this); } } void XMLHttpRequest::abort() { - bool hadLoader = m_loader; + bool sendFlag = m_loader; + + internalAbort(); - m_aborted = true; + // Clear headers as required by the spec + m_requestHeaders.clear(); + if ((m_state <= OPENED && !sendFlag) || m_state == DONE) + m_state = UNSENT; + else { + ASSERT(!m_loader); + changeState(DONE); + m_state = UNSENT; + } + + dispatchAbortEvent(); + if (!m_uploadComplete) { + m_uploadComplete = true; + if (m_upload) + m_upload->dispatchAbortEvent(); + } +} + +void XMLHttpRequest::internalAbort() +{ + bool hadLoader = m_loader; + + m_error = true; + + // FIXME: when we add the support for multi-part XHR, we will have to think be careful with this initialization. + m_receivedLength = 0; + if (hadLoader) { m_loader->cancel(); m_loader = 0; @@ -496,42 +765,87 @@ void XMLHttpRequest::abort() if (hadLoader) dropProtection(); +} + +void XMLHttpRequest::clearResponse() +{ + m_response = ResourceResponse(); + { + JSC::JSLock lock(false); + m_responseText = ""; + } + m_createdDocument = false; + m_responseXML = 0; +} + +void XMLHttpRequest::clearRequest() +{ + m_requestHeaders.clear(); + m_requestEntityBody = 0; +} + +void XMLHttpRequest::genericError() +{ + clearResponse(); + clearRequest(); + m_error = true; - m_state = Uninitialized; + // The spec says we should "Synchronously switch the state to DONE." and then "Synchronously dispatch a readystatechange event on the object" + // but this does not match Firefox. +} + +void XMLHttpRequest::networkError() +{ + genericError(); + dispatchErrorEvent(); + if (!m_uploadComplete) { + m_uploadComplete = true; + if (m_upload) + m_upload->dispatchErrorEvent(); + } +} + +void XMLHttpRequest::abortError() +{ + genericError(); + dispatchAbortEvent(); + if (!m_uploadComplete) { + m_uploadComplete = true; + if (m_upload) + m_upload->dispatchAbortEvent(); + } } void XMLHttpRequest::dropProtection() { - { - KJS::JSLock lock; - KJS::JSValue* wrapper = KJS::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); + // The XHR object itself holds on to the responseText, and + // thus has extra cost even independent of any + // responseText or responseXML objects it has handed + // out. But it is protected from GC while loading, so this + // can't be recouped until the load is done, so only + // report the extra cost at that point. + + if (JSDOMWindow* window = toJSDOMWindow(document()->frame())) { + if (JSC::JSValue* wrapper = getCachedDOMObjectWrapper(*window->globalData(), this)) + JSC::Heap::heap(wrapper)->reportExtraMemoryCost(m_responseText.size() * 2); } - deref(); + unsetPendingActivity(this); } -void XMLHttpRequest::overrideMIMEType(const String& override) +void XMLHttpRequest::overrideMimeType(const String& override) { m_mimeTypeOverride = override; } void XMLHttpRequest::setRequestHeader(const String& name, const String& value, ExceptionCode& ec) { - if (m_state != Open) { - Settings* settings = m_doc ? m_doc->settings() : 0; + if (m_state != OPENED || m_loader) { +#if ENABLE(DASHBOARD_SUPPORT) + Settings* settings = document() ? document()->settings() : 0; if (settings && settings->usesDashboardBackwardCompatibilityMode()) return; +#endif ec = INVALID_STATE_ERR; return; @@ -541,21 +855,22 @@ void XMLHttpRequest::setRequestHeader(const String& name, const String& value, E 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()); + if (!document()->securityOrigin()->canLoadLocalResources() && !isSafeRequestHeader(name)) { + if (document() && document()->frame()) + document()->frame()->domWindow()->console()->addMessage(JSMessageSource, ErrorMessageLevel, "Refused to set unsafe header \"" + name + "\"", 1, String()); return; } - if (!m_requestHeaders.contains(name)) { - m_requestHeaders.set(name, value); - return; - } - - String oldValue = m_requestHeaders.get(name); - m_requestHeaders.set(name, oldValue + ", " + value); + setRequestHeaderInternal(name, value); +} + +void XMLHttpRequest::setRequestHeaderInternal(const String& name, const String& value) +{ + pair<HTTPHeaderMap::iterator, bool> result = m_requestHeaders.add(name, value); + if (!result.second) + result.first->second += ", " + value; } String XMLHttpRequest::getRequestHeader(const String& name) const @@ -565,7 +880,7 @@ String XMLHttpRequest::getRequestHeader(const String& name) const String XMLHttpRequest::getAllResponseHeaders(ExceptionCode& ec) const { - if (m_state < Receiving) { + if (m_state < LOADING) { ec = INVALID_STATE_ERR; return ""; } @@ -575,6 +890,9 @@ String XMLHttpRequest::getAllResponseHeaders(ExceptionCode& ec) const HTTPHeaderMap::const_iterator end = m_response.httpHeaderFields().end(); for (HTTPHeaderMap::const_iterator it = m_response.httpHeaderFields().begin(); it!= end; ++it) { + if (!m_sameOriginRequest && !isOnAccessControlResponseHeaderWhitelist(it->first)) + continue; + stringBuilder.append(it->first.characters(), it->first.length()); stringBuilder.append(separator.characters(), separator.length()); stringBuilder.append(it->second.characters(), it->second.length()); @@ -587,7 +905,7 @@ String XMLHttpRequest::getAllResponseHeaders(ExceptionCode& ec) const String XMLHttpRequest::getResponseHeader(const String& name, ExceptionCode& ec) const { - if (m_state < Receiving) { + if (m_state < LOADING) { ec = INVALID_STATE_ERR; return ""; } @@ -595,6 +913,9 @@ String XMLHttpRequest::getResponseHeader(const String& name, ExceptionCode& ec) if (!isValidToken(name)) return ""; + if (!m_sameOriginRequest && !isOnAccessControlResponseHeaderWhitelist(name)) + return ""; + return m_response.httpHeaderField(name); } @@ -618,102 +939,251 @@ bool XMLHttpRequest::responseIsXML() const return DOMImplementation::isXMLMIMEType(responseMIMEType()); } -int XMLHttpRequest::getStatus(ExceptionCode& ec) const +int XMLHttpRequest::status(ExceptionCode& ec) const { - if (m_state == Uninitialized) - return 0; - - if (m_response.httpStatusCode() == 0) { - if (m_state != Receiving && m_state != Loaded) - // status MUST be available in these states, but we don't get any headers from non-HTTP requests - ec = INVALID_STATE_ERR; + if (m_response.httpStatusCode()) + return m_response.httpStatusCode(); + + if (m_state == OPENED) { + // Firefox only raises an exception in this state; we match it. + // Note the case of local file requests, where we have no HTTP response code! Firefox never raises an exception for those, but we match HTTP case for consistency. + ec = INVALID_STATE_ERR; } - return m_response.httpStatusCode(); + return 0; } -String XMLHttpRequest::getStatusText(ExceptionCode& ec) const +String XMLHttpRequest::statusText(ExceptionCode& ec) const { - if (m_state == Uninitialized) - return ""; - - if (m_response.httpStatusCode() == 0) { - if (m_state != Receiving && m_state != Loaded) - // statusText MUST be available in these states, but we don't get any headers from non-HTTP requests - ec = INVALID_STATE_ERR; - return String(); + // FIXME: <http://bugs.webkit.org/show_bug.cgi?id=3547> XMLHttpRequest.statusText returns always "OK". + if (m_response.httpStatusCode()) + return "OK"; + + if (m_state == OPENED) { + // See comments in getStatus() above. + ec = INVALID_STATE_ERR; } - // FIXME: should try to preserve status text in response - return "OK"; + return String(); } -void XMLHttpRequest::processSyncLoadResults(const Vector<char>& data, const ResourceResponse& response) +void XMLHttpRequest::processSyncLoadResults(const Vector<char>& data, const ResourceResponse& response, ExceptionCode& ec) { - if (!urlMatchesDocumentDomain(response.url())) { + if (m_sameOriginRequest && !document()->securityOrigin()->canRequest(response.url())) { abort(); return; } - + didReceiveResponse(0, response); - changeState(Sent); - if (m_aborted) - return; + changeState(HEADERS_RECEIVED); 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); + if (m_error) + ec = XMLHttpRequestException::NETWORK_ERR; } -void XMLHttpRequest::didFail(SubresourceLoader* loader, const ResourceError&) +void XMLHttpRequest::didFail(SubresourceLoader* loader, const ResourceError& error) { - didFinishLoading(loader); + // If we are already in an error state, for instance we called abort(), bail out early. + if (m_error) + return; + + if (error.isCancellation()) { + abortError(); + return; + } + + networkError(); + return; } void XMLHttpRequest::didFinishLoading(SubresourceLoader* loader) { - if (m_aborted) + if (m_error) return; - + + if (m_inPreflight) { + didFinishLoadingPreflight(loader); + return; + } + ASSERT(loader == m_loader); - if (m_state < Sent) - changeState(Sent); + if (m_state < HEADERS_RECEIVED) + changeState(HEADERS_RECEIVED); { - KJS::JSLock lock; + JSC::JSLock lock(false); if (m_decoder) m_responseText += m_decoder->flush(); } + if (Frame* frame = document()->frame()) { + if (Page* page = frame->page()) { + page->inspectorController()->resourceRetrievedByXMLHttpRequest(m_loader ? m_loader->identifier() : m_identifier, m_responseText); + page->inspectorController()->addMessageToConsole(JSMessageSource, LogMessageLevel, "XHR finished loading: \"" + m_url + "\".", m_lastSendLineNumber, m_lastSendURL); + } + } + bool hadLoader = m_loader; m_loader = 0; - changeState(Loaded); + changeState(DONE); m_decoder = 0; if (hadLoader) dropProtection(); } +void XMLHttpRequest::didFinishLoadingPreflight(SubresourceLoader* loader) +{ + ASSERT(m_inPreflight); + ASSERT(!m_sameOriginRequest); + + // FIXME: this can probably be moved to didReceiveResponsePreflight. + if (m_async) + handleAsynchronousPreflightResult(); + + if (m_loader) + unsetPendingActivity(this); +} + void XMLHttpRequest::willSendRequest(SubresourceLoader*, ResourceRequest& request, const ResourceResponse& redirectResponse) { - if (!urlMatchesDocumentDomain(request.url())) - abort(); + // FIXME: This needs to be fixed to follow the redirect correctly even for cross-domain requests. + if (!document()->securityOrigin()->canRequest(request.url())) { + internalAbort(); + networkError(); + } } -void XMLHttpRequest::didReceiveResponse(SubresourceLoader*, const ResourceResponse& response) +void XMLHttpRequest::didSendData(SubresourceLoader*, unsigned long long bytesSent, unsigned long long totalBytesToBeSent) { + if (!m_upload) + return; + + m_upload->dispatchProgressEvent(bytesSent, totalBytesToBeSent); + + if (bytesSent == totalBytesToBeSent && !m_uploadComplete) { + m_uploadComplete = true; + m_upload->dispatchLoadEvent(); + } +} + +bool XMLHttpRequest::accessControlCheck(const ResourceResponse& response) +{ + const String& accessControlOriginString = response.httpHeaderField("Access-Control-Origin"); + if (accessControlOriginString == "*" && !m_includeCredentials) + return true; + + KURL accessControlOriginURL(accessControlOriginString); + if (!accessControlOriginURL.isValid()) + return false; + + RefPtr<SecurityOrigin> accessControlOrigin = SecurityOrigin::create(accessControlOriginURL); + if (!accessControlOrigin->isSameSchemeHostPort(document()->securityOrigin())) + return false; + + if (m_includeCredentials) { + const String& accessControlCredentialsString = response.httpHeaderField("Access-Control-Credentials"); + if (accessControlCredentialsString != "true") + return false; + } + + return true; +} + +void XMLHttpRequest::didReceiveResponse(SubresourceLoader* loader, const ResourceResponse& response) +{ + if (m_inPreflight) { + didReceiveResponsePreflight(loader, response); + return; + } + + if (!m_sameOriginRequest) { + if (!accessControlCheck(response)) { + networkError(); + return; + } + } + m_response = response; - m_encoding = extractCharsetFromMediaType(m_mimeTypeOverride); - if (m_encoding.isEmpty()) - m_encoding = response.textEncodingName(); + m_responseEncoding = extractCharsetFromMediaType(m_mimeTypeOverride); + if (m_responseEncoding.isEmpty()) + m_responseEncoding = response.textEncodingName(); +} + +template<class HashType> +static bool parseAccessControlAllowList(const String& string, HashSet<String, HashType>* set) +{ + int start = 0; + int end; + while ((end = string.find(',', start)) != -1) { + if (start == end) + return false; + + // FIXME: this could be made more efficient by not not allocating twice. + set->add(string.substring(start, end - start).stripWhiteSpace()); + start = end + 1; + } + if (start != static_cast<int>(string.length())) + set->add(string.substring(start).stripWhiteSpace()); + + return true; +} + +static bool parseAccessControlMaxAge(const String& string, unsigned& expiryDelta) +{ + // FIXME: this will not do the correct thing for a number starting with a '+' + bool ok = false; + expiryDelta = string.toUIntStrict(&ok); + return ok; +} + +void XMLHttpRequest::didReceiveResponsePreflight(SubresourceLoader*, const ResourceResponse& response) +{ + ASSERT(m_inPreflight); + ASSERT(!m_sameOriginRequest); + + if (!accessControlCheck(response)) { + networkError(); + return; + } + + OwnPtr<HashSet<String> > methods(new HashSet<String>); + if (!parseAccessControlAllowList(response.httpHeaderField("Access-Control-Allow-Methods"), methods.get())) { + networkError(); + return; + } + + if (!methods->contains(m_method) && m_method != "GET" && m_method != "POST") { + networkError(); + return; + } + + OwnPtr<HashSet<String, CaseFoldingHash> > headers(new HashSet<String, CaseFoldingHash>); + if (!parseAccessControlAllowList(response.httpHeaderField("Access-Control-Allow-Headers"), headers.get())) { + networkError(); + return; + } + HTTPHeaderMap::const_iterator end = m_requestHeaders.end(); + for (HTTPHeaderMap::const_iterator it = m_requestHeaders.begin(); it != end; ++it) { + if (!headers->contains(it->first) && !isOnAccessControlSimpleRequestHeaderWhitelist(it->first)) { + networkError(); + return; + } + } + + unsigned expiryDelta = 0; + if (!parseAccessControlMaxAge(response.httpHeaderField("Access-Control-Max-Age"), expiryDelta)) + expiryDelta = 5; + + appendPreflightResultCacheEntry(document()->securityOrigin()->toString(), m_url, expiryDelta, m_includeCredentials, methods.release(), headers.release()); } void XMLHttpRequest::receivedCancellation(SubresourceLoader*, const AuthenticationChallenge& challenge) @@ -723,19 +1193,22 @@ void XMLHttpRequest::receivedCancellation(SubresourceLoader*, const Authenticati void XMLHttpRequest::didReceiveData(SubresourceLoader*, const char* data, int len) { - if (m_state < Sent) - changeState(Sent); + if (m_inPreflight) + return; + + if (m_state < HEADERS_RECEIVED) + changeState(HEADERS_RECEIVED); if (!m_decoder) { - if (!m_encoding.isEmpty()) - m_decoder = new TextResourceDecoder("text/plain", m_encoding); + if (!m_responseEncoding.isEmpty()) + m_decoder = TextResourceDecoder::create("text/plain", m_responseEncoding); // allow TextResourceDecoder to look inside the m_response if it's XML or HTML else if (responseIsXML()) - m_decoder = new TextResourceDecoder("application/xml"); + m_decoder = TextResourceDecoder::create("application/xml"); else if (responseMIMEType() == "text/html") - m_decoder = new TextResourceDecoder("text/html", "UTF-8"); + m_decoder = TextResourceDecoder::create("text/html", "UTF-8"); else - m_decoder = new TextResourceDecoder("text/plain", "UTF-8"); + m_decoder = TextResourceDecoder::create("text/plain", "UTF-8"); } if (len == 0) return; @@ -746,42 +1219,99 @@ void XMLHttpRequest::didReceiveData(SubresourceLoader*, const char* data, int le String decoded = m_decoder->decode(data, len); { - KJS::JSLock lock; + JSC::JSLock lock(false); m_responseText += decoded; } - if (!m_aborted) { - if (m_state != Receiving) - changeState(Receiving); + if (!m_error) { + updateAndDispatchOnProgress(len); + + if (m_state != LOADING) + changeState(LOADING); else // Firefox calls readyStateChanged every time it receives data, 4449442 callReadyStateChangeListener(); } } -void XMLHttpRequest::cancelRequests(Document* m_doc) +void XMLHttpRequest::updateAndDispatchOnProgress(unsigned int len) { - 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)->abort(); + long long expectedLength = m_response.expectedContentLength(); + m_receivedLength += len; + + // FIXME: the spec requires that we dispatch the event according to the least + // frequent method between every 350ms (+/-200ms) and for every byte received. + dispatchProgressEvent(expectedLength); } -void XMLHttpRequest::detachRequests(Document* m_doc) +void XMLHttpRequest::dispatchReadyStateChangeEvent() { - 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)->abort(); + RefPtr<Event> evt = Event::create(eventNames().readystatechangeEvent, false, false); + if (m_onReadyStateChangeListener) { + evt->setTarget(this); + evt->setCurrentTarget(this); + m_onReadyStateChangeListener->handleEvent(evt.get(), false); } - delete requests; + + ExceptionCode ec = 0; + dispatchEvent(evt.release(), ec); + ASSERT(!ec); +} + +void XMLHttpRequest::dispatchXMLHttpRequestProgressEvent(EventListener* listener, const AtomicString& type, bool lengthComputable, unsigned loaded, unsigned total) +{ + RefPtr<XMLHttpRequestProgressEvent> evt = XMLHttpRequestProgressEvent::create(type, lengthComputable, loaded, total); + if (listener) { + evt->setTarget(this); + evt->setCurrentTarget(this); + listener->handleEvent(evt.get(), false); + } + + ExceptionCode ec = 0; + dispatchEvent(evt.release(), ec); + ASSERT(!ec); +} + +void XMLHttpRequest::dispatchAbortEvent() +{ + dispatchXMLHttpRequestProgressEvent(m_onAbortListener.get(), eventNames().abortEvent, false, 0, 0); +} + +void XMLHttpRequest::dispatchErrorEvent() +{ + dispatchXMLHttpRequestProgressEvent(m_onErrorListener.get(), eventNames().errorEvent, false, 0, 0); +} + +void XMLHttpRequest::dispatchLoadEvent() +{ + dispatchXMLHttpRequestProgressEvent(m_onLoadListener.get(), eventNames().loadEvent, false, 0, 0); +} + +void XMLHttpRequest::dispatchLoadStartEvent() +{ + dispatchXMLHttpRequestProgressEvent(m_onLoadStartListener.get(), eventNames().loadstartEvent, false, 0, 0); +} + +void XMLHttpRequest::dispatchProgressEvent(long long expectedLength) +{ + dispatchXMLHttpRequestProgressEvent(m_onProgressListener.get(), eventNames().progressEvent, expectedLength && m_receivedLength <= expectedLength, + static_cast<unsigned>(m_receivedLength), static_cast<unsigned>(expectedLength)); +} + +void XMLHttpRequest::stop() +{ + internalAbort(); +} + +void XMLHttpRequest::contextDestroyed() +{ + ActiveDOMObject::contextDestroyed(); + internalAbort(); +} + +ScriptExecutionContext* XMLHttpRequest::scriptExecutionContext() const +{ + return ActiveDOMObject::scriptExecutionContext(); } -} // end namespace +} // namespace WebCore diff --git a/WebCore/xml/XMLHttpRequest.h b/WebCore/xml/XMLHttpRequest.h index 6a22c31..6962ab1 100644 --- a/WebCore/xml/XMLHttpRequest.h +++ b/WebCore/xml/XMLHttpRequest.h @@ -1,7 +1,5 @@ -// -*- c-basic-offset: 2 -*- /* - * This file is part of the KDE libraries - * Copyright (C) 2003, 2006 Apple Computer, Inc. + * 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 @@ -22,136 +20,211 @@ #ifndef XMLHttpRequest_h #define XMLHttpRequest_h +#include "ActiveDOMObject.h" +#include "AtomicStringHash.h" +#include "EventListener.h" #include "EventTarget.h" -#include "HTTPHeaderMap.h" -#include "KURL.h" -#include "PlatformString.h" +#include "FormData.h" #include "ResourceResponse.h" -#include "StringHash.h" #include "SubresourceLoaderClient.h" -#include <kjs/ustring.h> - -#include <wtf/HashMap.h> -#include <wtf/Vector.h> +#include <wtf/OwnPtr.h> namespace WebCore { -class TextResourceDecoder; class Document; -class Event; -class EventListener; -class String; - -typedef int ExceptionCode; - -// 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 File; +class TextResourceDecoder; -class XMLHttpRequest : public RefCounted<XMLHttpRequest>, public EventTarget, private SubresourceLoaderClient { +class XMLHttpRequest : public RefCounted<XMLHttpRequest>, public EventTarget, private SubresourceLoaderClient, public ActiveDOMObject { public: - XMLHttpRequest(Document*); + static PassRefPtr<XMLHttpRequest> create(Document* document) { return adoptRef(new XMLHttpRequest(document)); } ~XMLHttpRequest(); + // These exact numeric values are important because JS expects them. + enum State { + UNSENT = 0, + OPENED = 1, + HEADERS_RECEIVED = 2, + LOADING = 3, + DONE = 4 + }; + virtual XMLHttpRequest* toXMLHttpRequest() { return this; } - static void detachRequests(Document*); - static void cancelRequests(Document*); + virtual void contextDestroyed(); + virtual void stop(); + + virtual ScriptExecutionContext* scriptExecutionContext() const; - String getStatusText(ExceptionCode&) const; - int getStatus(ExceptionCode&) const; - XMLHttpRequestState getReadyState() const; + String statusText(ExceptionCode&) const; + int status(ExceptionCode&) const; + State readyState() const; void open(const String& method, const KURL&, bool async, ExceptionCode&); void open(const String& method, const KURL&, bool async, const String& user, ExceptionCode&); void open(const String& method, const KURL&, bool async, const String& user, const String& password, ExceptionCode&); - void send(const String& body, ExceptionCode&); + void send(ExceptionCode&); + void send(Document*, ExceptionCode&); + void send(const String&, ExceptionCode&); + void send(File*, ExceptionCode&); void abort(); void setRequestHeader(const String& name, const String& value, ExceptionCode&); - void overrideMIMEType(const String& override); + 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; + const JSC::UString& responseText() const; + Document* responseXML() const; + void setLastSendLineNumber(unsigned lineNumber) { m_lastSendLineNumber = lineNumber; } + void setLastSendURL(JSC::UString url) { m_lastSendURL = url; } - void setOnReadyStateChangeListener(EventListener*); - EventListener* onReadyStateChangeListener() const; - void setOnLoadListener(EventListener*); - EventListener* onLoadListener() const; + XMLHttpRequestUpload* upload(); + XMLHttpRequestUpload* optionalUpload() const { return m_upload.get(); } + + void setOnreadystatechange(PassRefPtr<EventListener> eventListener) { m_onReadyStateChangeListener = eventListener; } + EventListener* onreadystatechange() const { return m_onReadyStateChangeListener.get(); } + + void setOnabort(PassRefPtr<EventListener> eventListener) { m_onAbortListener = eventListener; } + EventListener* onabort() const { return m_onAbortListener.get(); } + + void setOnerror(PassRefPtr<EventListener> eventListener) { m_onErrorListener = eventListener; } + EventListener* onerror() const { return m_onErrorListener.get(); } + + void setOnload(PassRefPtr<EventListener> eventListener) { m_onLoadListener = eventListener; } + EventListener* onload() const { return m_onLoadListener.get(); } + + void setOnloadstart(PassRefPtr<EventListener> eventListener) { m_onLoadStartListener = eventListener; } + EventListener* onloadstart() const { return m_onLoadStartListener.get(); } + + void setOnprogress(PassRefPtr<EventListener> eventListener) { m_onProgressListener = eventListener; } + EventListener* onprogress() const { return m_onProgressListener.get(); } typedef Vector<RefPtr<EventListener> > ListenerVector; - typedef HashMap<AtomicStringImpl*, ListenerVector> EventListenersMap; + typedef HashMap<AtomicString, ListenerVector> EventListenersMap; // useCapture is not used, even for add/remove pairing (for Firefox compatibility). virtual void addEventListener(const AtomicString& eventType, PassRefPtr<EventListener>, bool useCapture); virtual void removeEventListener(const AtomicString& eventType, EventListener*, bool useCapture); - virtual bool dispatchEvent(PassRefPtr<Event>, ExceptionCode&, bool tempEvent = false); + virtual bool dispatchEvent(PassRefPtr<Event>, ExceptionCode&); 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; + Document* document() const; virtual void willSendRequest(SubresourceLoader*, ResourceRequest& request, const ResourceResponse& redirectResponse); + virtual void didSendData(SubresourceLoader*, unsigned long long bytesSent, unsigned long long totalBytesToBeSent); virtual void didReceiveResponse(SubresourceLoader*, const ResourceResponse&); virtual void didReceiveData(SubresourceLoader*, const char* data, int size); virtual void didFail(SubresourceLoader*, const ResourceError&); virtual void didFinishLoading(SubresourceLoader*); virtual void receivedCancellation(SubresourceLoader*, const AuthenticationChallenge&); - void processSyncLoadResults(const Vector<char>& data, const ResourceResponse&); + // Special versions for the preflight + void didReceiveResponsePreflight(SubresourceLoader*, const ResourceResponse&); + void didFinishLoadingPreflight(SubresourceLoader*); + + void processSyncLoadResults(const Vector<char>& data, const ResourceResponse&, ExceptionCode&); + void updateAndDispatchOnProgress(unsigned int len); String responseMIMEType() const; bool responseIsXML() const; + bool initSend(ExceptionCode&); + String getRequestHeader(const String& name) const; + void setRequestHeaderInternal(const String& name, const String& value); - void changeState(XMLHttpRequestState newState); + void changeState(State newState); void callReadyStateChangeListener(); void dropProtection(); + void internalAbort(); + void clearResponse(); + void clearRequest(); + + void createRequest(ExceptionCode&); + + void makeSameOriginRequest(ExceptionCode&); + void makeCrossSiteAccessRequest(ExceptionCode&); + + void makeSimpleCrossSiteAccessRequest(ExceptionCode&); + void makeCrossSiteAccessRequestWithPreflight(ExceptionCode&); + void handleAsynchronousPreflightResult(); + + void loadRequestSynchronously(ResourceRequest&, ExceptionCode&); + void loadRequestAsynchronously(ResourceRequest&); - Document* m_doc; + bool isSimpleCrossSiteAccessRequest() const; + String accessControlOrigin() const; + bool accessControlCheck(const ResourceResponse&); + + void genericError(); + void networkError(); + void abortError(); + + void dispatchReadyStateChangeEvent(); + void dispatchXMLHttpRequestProgressEvent(EventListener* listener, const AtomicString& type, bool lengthComputable, unsigned loaded, unsigned total); + void dispatchAbortEvent(); + void dispatchErrorEvent(); + void dispatchLoadEvent(); + void dispatchLoadStartEvent(); + void dispatchProgressEvent(long long expectedLength); RefPtr<EventListener> m_onReadyStateChangeListener; + RefPtr<EventListener> m_onAbortListener; + RefPtr<EventListener> m_onErrorListener; RefPtr<EventListener> m_onLoadListener; + RefPtr<EventListener> m_onLoadStartListener; + RefPtr<EventListener> m_onProgressListener; EventListenersMap m_eventListeners; + RefPtr<XMLHttpRequestUpload> m_upload; + KURL m_url; - DeprecatedString m_method; + String m_method; HTTPHeaderMap m_requestHeaders; + RefPtr<FormData> m_requestEntityBody; String m_mimeTypeOverride; bool m_async; + bool m_includeCredentials; RefPtr<SubresourceLoader> m_loader; - XMLHttpRequestState m_state; + State m_state; ResourceResponse m_response; - String m_encoding; + String m_responseEncoding; RefPtr<TextResourceDecoder> m_decoder; + unsigned long m_identifier; - // Unlike most strings in the DOM, we keep this as a KJS::UString, not a WebCore::String. + // Unlike most strings in the DOM, we keep this as a JSC::UString, not a WebCore::String. // That's because these strings can easily get huge (they are filled from the network with // no parsing) and because JS can easily observe many intermediate states, so it's very useful // to be able to share the buffer with JavaScript versions of the whole or partial string. // In contrast, this string doesn't interact much with the rest of the engine so it's not that // big a cost that it isn't a String. - KJS::UString m_responseText; + JSC::UString m_responseText; mutable bool m_createdDocument; mutable RefPtr<Document> m_responseXML; - bool m_aborted; + bool m_error; + + bool m_uploadComplete; + + bool m_sameOriginRequest; + bool m_allowAccess; + bool m_inPreflight; + + // Used for onprogress tracking + long long m_receivedLength; + + unsigned m_lastSendLineNumber; + JSC::UString m_lastSendURL; }; } // namespace WebCore diff --git a/WebCore/xml/XMLHttpRequest.idl b/WebCore/xml/XMLHttpRequest.idl new file mode 100644 index 0000000..fa6b9ca --- /dev/null +++ b/WebCore/xml/XMLHttpRequest.idl @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2008 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +module xml { + + interface [ + CustomMarkFunction + ] XMLHttpRequest { + // From XMLHttpRequestEventTarget + // event handler attributes + attribute EventListener onabort; + attribute EventListener onerror; + attribute EventListener onload; + attribute EventListener onloadstart; + attribute EventListener onprogress; + + // event handler attributes + attribute EventListener onreadystatechange; + + // state + const unsigned short UNSENT = 0; + const unsigned short OPENED = 1; + const unsigned short HEADERS_RECEIVED = 2; + const unsigned short LOADING = 3; + const unsigned short DONE = 4; + + readonly attribute unsigned short readyState; + + // request + // void open(in DOMString method, in DOMString url); + // void open(in DOMString method, in DOMString url, in boolean async); + // void open(in DOMString method, in DOMString url, in boolean async, in DOMString user); + [Custom] void open(in DOMString method, in DOMString url, in boolean async, in DOMString user, in DOMString password) + raises(DOMException); + + [Custom] void setRequestHeader(in DOMString header, in DOMString value) + raises(DOMException); + + // void send(); + // void send(in DOMString data); + [Custom] void send(in Document data) + raises(DOMException); + + void abort(); + + readonly attribute XMLHttpRequestUpload upload; + + // response + [ConvertNullStringTo=Undefined] DOMString getAllResponseHeaders() + raises(DOMException); + [Custom, ConvertNullStringTo=Null] DOMString getResponseHeader(in DOMString header) + raises(DOMException); + readonly attribute [ConvertNullStringTo=Null] DOMString responseText; + readonly attribute Document responseXML; + readonly attribute unsigned short status + getter raises(DOMException); + readonly attribute DOMString statusText + getter raises(DOMException); + + // Extension + [Custom] void overrideMimeType(in DOMString override); + + // EventTarget interface + [Custom] void addEventListener(in DOMString type, + in EventListener listener, + in boolean useCapture); + [Custom] void removeEventListener(in DOMString type, + in EventListener listener, + in boolean useCapture); + boolean dispatchEvent(in Event evt) + raises(EventException); + }; + +} diff --git a/WebCore/xml/XMLHttpRequestException.h b/WebCore/xml/XMLHttpRequestException.h index 1540d4b..737cab0 100644 --- a/WebCore/xml/XMLHttpRequestException.h +++ b/WebCore/xml/XMLHttpRequestException.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007 Apple Inc. All rights reserved. + * 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 @@ -35,18 +35,24 @@ namespace WebCore { class XMLHttpRequestException : public ExceptionBase { public: - XMLHttpRequestException(const ExceptionCodeDescription& description) - : ExceptionBase(description) + static PassRefPtr<XMLHttpRequestException> create(const ExceptionCodeDescription& description) { + return adoptRef(new XMLHttpRequestException(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 + NETWORK_ERR = XMLHttpRequestExceptionOffset + 101, + ABORT_ERR }; + + private: + XMLHttpRequestException(const ExceptionCodeDescription& description) + : ExceptionBase(description) + { + } }; } // namespace WebCore diff --git a/WebCore/xml/XMLHttpRequestException.idl b/WebCore/xml/XMLHttpRequestException.idl index e6aa182..2feb574 100644 --- a/WebCore/xml/XMLHttpRequestException.idl +++ b/WebCore/xml/XMLHttpRequestException.idl @@ -43,6 +43,7 @@ module xml { // XMLHttpRequestExceptionCode const unsigned short NETWORK_ERR = 101; + const unsigned short ABORT_ERR = 102; }; } diff --git a/WebCore/xml/XMLHttpRequestProgressEvent.h b/WebCore/xml/XMLHttpRequestProgressEvent.h new file mode 100644 index 0000000..02bfdea --- /dev/null +++ b/WebCore/xml/XMLHttpRequestProgressEvent.h @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2008 Apple Inc. All rights reserved. + * Copyright (C) 2008 Julien Chaffraix <jchaffraix@webkit.org>. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef XMLHttpRequestProgressEvent_h +#define XMLHttpRequestProgressEvent_h + +#include "ProgressEvent.h" + +namespace WebCore { + + class XMLHttpRequestProgressEvent : public ProgressEvent { + public: + static PassRefPtr<XMLHttpRequestProgressEvent> create() + { + return adoptRef(new XMLHttpRequestProgressEvent); + } + static PassRefPtr<XMLHttpRequestProgressEvent> create(const AtomicString& type, bool lengthComputable, unsigned loaded, unsigned total) + { + return adoptRef(new XMLHttpRequestProgressEvent(type, lengthComputable, loaded, total)); + } + + virtual bool isXMLHttpRequestProgressEvent() const { return true; } + + // Those 2 methods are to be compatible with Firefox and are only a wrapper on top of the real implementation. + unsigned position() const { return loaded(); } + unsigned totalSize() const { return total(); } + + private: + XMLHttpRequestProgressEvent() { } + XMLHttpRequestProgressEvent(const AtomicString& type, bool lengthComputable, unsigned loaded, unsigned total) + : ProgressEvent(type, lengthComputable, loaded, total) + { + } + }; + +} // namespace WebCore + +#endif // XMLHttpRequestProgressEvent_h diff --git a/WebCore/xml/XMLHttpRequestProgressEvent.idl b/WebCore/xml/XMLHttpRequestProgressEvent.idl new file mode 100644 index 0000000..6de9690 --- /dev/null +++ b/WebCore/xml/XMLHttpRequestProgressEvent.idl @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2008 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +module events { + + interface [ + GenerateConstructor + // We should also inherit from LSProgressEvent when the idl is added. + ] XMLHttpRequestProgressEvent : ProgressEvent { + readonly attribute unsigned long position; + readonly attribute unsigned long totalSize; + }; + +} diff --git a/WebCore/xml/XMLHttpRequestUpload.cpp b/WebCore/xml/XMLHttpRequestUpload.cpp new file mode 100644 index 0000000..cef1798 --- /dev/null +++ b/WebCore/xml/XMLHttpRequestUpload.cpp @@ -0,0 +1,144 @@ +/* + * Copyright (C) 2008 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "XMLHttpRequestUpload.h" + +#include "AtomicString.h" +#include "Event.h" +#include "EventException.h" +#include "EventNames.h" +#include "Frame.h" +#include "XMLHttpRequest.h" +#include "XMLHttpRequestProgressEvent.h" +#include <wtf/Assertions.h> + +namespace WebCore { + +XMLHttpRequestUpload::XMLHttpRequestUpload(XMLHttpRequest* xmlHttpRequest) + : m_xmlHttpRequest(xmlHttpRequest) +{ +} + +ScriptExecutionContext* XMLHttpRequestUpload::scriptExecutionContext() const +{ + XMLHttpRequest* xmlHttpRequest = associatedXMLHttpRequest(); + if (!xmlHttpRequest) + return 0; + return xmlHttpRequest->scriptExecutionContext(); +} + +void XMLHttpRequestUpload::addEventListener(const AtomicString& eventType, PassRefPtr<EventListener> eventListener, bool) +{ + EventListenersMap::iterator iter = m_eventListeners.find(eventType); + if (iter == m_eventListeners.end()) { + ListenerVector listeners; + listeners.append(eventListener); + m_eventListeners.add(eventType, listeners); + } else { + ListenerVector& listeners = iter->second; + for (ListenerVector::iterator listenerIter = listeners.begin(); listenerIter != listeners.end(); ++listenerIter) { + if (*listenerIter == eventListener) + return; + } + + listeners.append(eventListener); + m_eventListeners.add(eventType, listeners); + } +} + +void XMLHttpRequestUpload::removeEventListener(const AtomicString& eventType, EventListener* eventListener, bool) +{ + EventListenersMap::iterator iter = m_eventListeners.find(eventType); + if (iter == m_eventListeners.end()) + return; + + ListenerVector& listeners = iter->second; + for (ListenerVector::const_iterator listenerIter = listeners.begin(); listenerIter != listeners.end(); ++listenerIter) { + if (*listenerIter == eventListener) { + listeners.remove(listenerIter - listeners.begin()); + return; + } + } +} + +bool XMLHttpRequestUpload::dispatchEvent(PassRefPtr<Event> evt, ExceptionCode& ec) +{ + // FIXME: check for other error conditions enumerated in the spec. + if (evt->type().isEmpty()) { + ec = EventException::UNSPECIFIED_EVENT_TYPE_ERR; + return true; + } + + ListenerVector listenersCopy = m_eventListeners.get(evt->type()); + for (ListenerVector::const_iterator listenerIter = listenersCopy.begin(); listenerIter != listenersCopy.end(); ++listenerIter) { + evt->setTarget(this); + evt->setCurrentTarget(this); + listenerIter->get()->handleEvent(evt.get(), false); + } + + return !evt->defaultPrevented(); +} + +void XMLHttpRequestUpload::dispatchXMLHttpRequestProgressEvent(EventListener* listener, const AtomicString& type, bool lengthComputable, unsigned loaded, unsigned total) +{ + RefPtr<XMLHttpRequestProgressEvent> evt = XMLHttpRequestProgressEvent::create(type, lengthComputable, loaded, total); + if (listener) { + evt->setTarget(this); + evt->setCurrentTarget(this); + listener->handleEvent(evt.get(), false); + } + + ExceptionCode ec = 0; + dispatchEvent(evt.release(), ec); + ASSERT(!ec); +} + +void XMLHttpRequestUpload::dispatchAbortEvent() +{ + dispatchXMLHttpRequestProgressEvent(m_onAbortListener.get(), eventNames().abortEvent, false, 0, 0); +} + +void XMLHttpRequestUpload::dispatchErrorEvent() +{ + dispatchXMLHttpRequestProgressEvent(m_onErrorListener.get(), eventNames().errorEvent, false, 0, 0); +} + +void XMLHttpRequestUpload::dispatchLoadEvent() +{ + dispatchXMLHttpRequestProgressEvent(m_onLoadListener.get(), eventNames().loadEvent, false, 0, 0); +} + +void XMLHttpRequestUpload::dispatchLoadStartEvent() +{ + dispatchXMLHttpRequestProgressEvent(m_onLoadStartListener.get(), eventNames().loadstartEvent, false, 0, 0); +} + +void XMLHttpRequestUpload::dispatchProgressEvent(long long bytesSent, long long totalBytesToBeSent) +{ + dispatchXMLHttpRequestProgressEvent(m_onProgressListener.get(), eventNames().progressEvent, true, static_cast<unsigned>(bytesSent), static_cast<unsigned>(totalBytesToBeSent)); +} + +} // namespace WebCore diff --git a/WebCore/xml/XMLHttpRequestUpload.h b/WebCore/xml/XMLHttpRequestUpload.h new file mode 100644 index 0000000..93fa7f9 --- /dev/null +++ b/WebCore/xml/XMLHttpRequestUpload.h @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2008 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef XMLHttpRequestUpload_h +#define XMLHttpRequestUpload_h + +#include "AtomicStringHash.h" +#include "EventListener.h" +#include "EventTarget.h" +#include <wtf/HashMap.h> +#include <wtf/RefCounted.h> +#include <wtf/RefPtr.h> +#include <wtf/PassRefPtr.h> +#include <wtf/Vector.h> + +namespace WebCore { + + class AtomicStringImpl; + class ScriptExecutionContext; + class XMLHttpRequest; + + class XMLHttpRequestUpload : public RefCounted<XMLHttpRequestUpload>, public EventTarget { + public: + static PassRefPtr<XMLHttpRequestUpload> create(XMLHttpRequest* xmlHttpRequest) + { + return adoptRef(new XMLHttpRequestUpload(xmlHttpRequest)); + } + + virtual XMLHttpRequestUpload* toXMLHttpRequestUpload() { return this; } + + XMLHttpRequest* associatedXMLHttpRequest() const { return m_xmlHttpRequest; } + void disconnectXMLHttpRequest() { m_xmlHttpRequest = 0; } + + ScriptExecutionContext* scriptExecutionContext() const; + + void dispatchAbortEvent(); + void dispatchErrorEvent(); + void dispatchLoadEvent(); + void dispatchLoadStartEvent(); + void dispatchProgressEvent(long long bytesSent, long long totalBytesToBeSent); + + void setOnabort(PassRefPtr<EventListener> eventListener) { m_onAbortListener = eventListener; } + EventListener* onabort() const { return m_onAbortListener.get(); } + + void setOnerror(PassRefPtr<EventListener> eventListener) { m_onErrorListener = eventListener; } + EventListener* onerror() const { return m_onErrorListener.get(); } + + void setOnload(PassRefPtr<EventListener> eventListener) { m_onLoadListener = eventListener; } + EventListener* onload() const { return m_onLoadListener.get(); } + + void setOnloadstart(PassRefPtr<EventListener> eventListener) { m_onLoadStartListener = eventListener; } + EventListener* onloadstart() const { return m_onLoadStartListener.get(); } + + void setOnprogress(PassRefPtr<EventListener> eventListener) { m_onProgressListener = eventListener; } + EventListener* onprogress() const { return m_onProgressListener.get(); } + + typedef Vector<RefPtr<EventListener> > ListenerVector; + typedef HashMap<AtomicString, ListenerVector> EventListenersMap; + + virtual void addEventListener(const AtomicString& eventType, PassRefPtr<EventListener>, bool useCapture); + virtual void removeEventListener(const AtomicString& eventType, EventListener*, bool useCapture); + virtual bool dispatchEvent(PassRefPtr<Event>, ExceptionCode&); + EventListenersMap& eventListeners() { return m_eventListeners; } + + using RefCounted<XMLHttpRequestUpload>::ref; + using RefCounted<XMLHttpRequestUpload>::deref; + + private: + XMLHttpRequestUpload(XMLHttpRequest*); + + void dispatchXMLHttpRequestProgressEvent(EventListener*, const AtomicString& type, bool lengthComputable, unsigned loaded, unsigned total); + + virtual void refEventTarget() { ref(); } + virtual void derefEventTarget() { deref(); } + + RefPtr<EventListener> m_onAbortListener; + RefPtr<EventListener> m_onErrorListener; + RefPtr<EventListener> m_onLoadListener; + RefPtr<EventListener> m_onLoadStartListener; + RefPtr<EventListener> m_onProgressListener; + EventListenersMap m_eventListeners; + + XMLHttpRequest* m_xmlHttpRequest; + }; + +} // namespace WebCore + +#endif // XMLHttpRequestUpload_h diff --git a/WebCore/xml/XMLHttpRequestUpload.idl b/WebCore/xml/XMLHttpRequestUpload.idl new file mode 100644 index 0000000..c066710 --- /dev/null +++ b/WebCore/xml/XMLHttpRequestUpload.idl @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2008 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +module xml { + + interface [ + GenerateConstructor, + CustomMarkFunction + ] XMLHttpRequestUpload { + // From XMLHttpRequestEventTarget + // event handler attributes + attribute EventListener onabort; + attribute EventListener onerror; + attribute EventListener onload; + attribute EventListener onloadstart; + attribute EventListener onprogress; + + // EventTarget interface + [Custom] void addEventListener(in DOMString type, + in EventListener listener, + in boolean useCapture); + [Custom] void removeEventListener(in DOMString type, + in EventListener listener, + in boolean useCapture); + boolean dispatchEvent(in Event evt) + raises(EventException); + }; + +} diff --git a/WebCore/xml/XMLSerializer.h b/WebCore/xml/XMLSerializer.h index 34f1a05..33e94b7 100644 --- a/WebCore/xml/XMLSerializer.h +++ b/WebCore/xml/XMLSerializer.h @@ -1,5 +1,4 @@ /* - * This file is part of the KDE libraries * Copyright (C) 2003, 2006 Apple Computer, Inc. * Copyright (C) 2006 Samuel Weinig (sam@webkit.org) * @@ -32,7 +31,12 @@ namespace WebCore { class XMLSerializer : public RefCounted<XMLSerializer> { public: + static PassRefPtr<XMLSerializer> create() { return adoptRef(new XMLSerializer); } + String serializeToString(Node*, ExceptionCode&); + + private: + XMLSerializer() { } }; } // namespace WebCore diff --git a/WebCore/xml/XPathEvaluator.cpp b/WebCore/xml/XPathEvaluator.cpp index 1707012..8fb203f 100644 --- a/WebCore/xml/XPathEvaluator.cpp +++ b/WebCore/xml/XPathEvaluator.cpp @@ -49,7 +49,7 @@ PassRefPtr<XPathExpression> XPathEvaluator::createExpression(const String& expre PassRefPtr<XPathNSResolver> XPathEvaluator::createNSResolver(Node* nodeResolver) { - return new NativeXPathNSResolver(nodeResolver); + return NativeXPathNSResolver::create(nodeResolver); } PassRefPtr<XPathResult> XPathEvaluator::evaluate(const String& expression, diff --git a/WebCore/xml/XPathEvaluator.h b/WebCore/xml/XPathEvaluator.h index 03dc7db..c8e456e 100644 --- a/WebCore/xml/XPathEvaluator.h +++ b/WebCore/xml/XPathEvaluator.h @@ -30,7 +30,7 @@ #if ENABLE(XPATH) #include <wtf/RefCounted.h> -#include <wtf/Forward.h> +#include <wtf/PassRefPtr.h> namespace WebCore { @@ -44,10 +44,15 @@ namespace WebCore { 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() { } }; } diff --git a/WebCore/xml/XPathException.h b/WebCore/xml/XPathException.h index aa3585c..45ad628 100644 --- a/WebCore/xml/XPathException.h +++ b/WebCore/xml/XPathException.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007 Apple Inc. All rights reserved. + * 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 @@ -29,17 +29,17 @@ #ifndef XPathException_h #define XPathException_h -#if ENABLE(XPATH) - #include "ExceptionBase.h" +#if ENABLE(XPATH) + namespace WebCore { class XPathException : public ExceptionBase { public: - XPathException(const ExceptionCodeDescription& description) - : ExceptionBase(description) + static PassRefPtr<XPathException> create(const ExceptionCodeDescription& description) { + return adoptRef(new XPathException(description)); } static const int XPathExceptionOffset = 400; @@ -49,6 +49,12 @@ namespace WebCore { INVALID_EXPRESSION_ERR = XPathExceptionOffset + 51, TYPE_ERR }; + + private: + XPathException(const ExceptionCodeDescription& description) + : ExceptionBase(description) + { + } }; } // namespace WebCore diff --git a/WebCore/xml/XPathExpression.cpp b/WebCore/xml/XPathExpression.cpp index 38369f8..446a7ad 100644 --- a/WebCore/xml/XPathExpression.cpp +++ b/WebCore/xml/XPathExpression.cpp @@ -45,7 +45,7 @@ using namespace XPath; PassRefPtr<XPathExpression> XPathExpression::createExpression(const String& expression, XPathNSResolver* resolver, ExceptionCode& ec) { - RefPtr<XPathExpression> expr = new XPathExpression; + RefPtr<XPathExpression> expr = XPathExpression::create(); Parser parser; expr->m_topExpression = parser.parseStatement(expression, resolver, ec); @@ -75,7 +75,7 @@ PassRefPtr<XPathResult> XPathExpression::evaluate(Node* contextNode, unsigned sh evaluationContext.node = contextNode; evaluationContext.size = 1; evaluationContext.position = 1; - RefPtr<XPathResult> result = new XPathResult(eventTarget, m_topExpression->evaluate()); + 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) { diff --git a/WebCore/xml/XPathExpression.h b/WebCore/xml/XPathExpression.h index 430e3c9..a2b75d7 100644 --- a/WebCore/xml/XPathExpression.h +++ b/WebCore/xml/XPathExpression.h @@ -30,7 +30,7 @@ #if ENABLE(XPATH) #include <wtf/RefCounted.h> -#include <wtf/Forward.h> +#include <wtf/PassRefPtr.h> namespace WebCore { @@ -47,11 +47,15 @@ namespace WebCore { 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; }; diff --git a/WebCore/xml/XPathExpression.idl b/WebCore/xml/XPathExpression.idl index 1afe888..c1fc15e 100644 --- a/WebCore/xml/XPathExpression.idl +++ b/WebCore/xml/XPathExpression.idl @@ -21,7 +21,10 @@ module xpath { - interface [Conditional=XPATH] XPathExpression { + interface [ + Conditional=XPATH, + GenerateConstructor + ] XPathExpression { [OldStyleObjC] XPathResult evaluate(in Node contextNode, in unsigned short type, in XPathResult inResult) diff --git a/WebCore/xml/XPathExpressionNode.h b/WebCore/xml/XPathExpressionNode.h index 6a483f1..9c5f79b 100644 --- a/WebCore/xml/XPathExpressionNode.h +++ b/WebCore/xml/XPathExpressionNode.h @@ -30,13 +30,12 @@ #if ENABLE(XPATH) #include "StringHash.h" +#include "Node.h" #include <wtf/HashMap.h> #include <wtf/Vector.h> namespace WebCore { - class Node; - namespace XPath { class Value; diff --git a/WebCore/xml/XPathFunctions.cpp b/WebCore/xml/XPathFunctions.cpp index 9e12d28..841b436 100644 --- a/WebCore/xml/XPathFunctions.cpp +++ b/WebCore/xml/XPathFunctions.cpp @@ -326,7 +326,7 @@ Value FunLocalName::evaluate() const if (!node) node = evaluationContext().node.get(); - return node->localName().domString(); + return node->localName().string(); } Value FunNamespaceURI::evaluate() const @@ -345,7 +345,7 @@ Value FunNamespaceURI::evaluate() const if (!node) node = evaluationContext().node.get(); - return node->namespaceURI().domString(); + return node->namespaceURI().string(); } Value FunName::evaluate() const @@ -365,7 +365,7 @@ Value FunName::evaluate() const node = evaluationContext().node.get(); const AtomicString& prefix = node->prefix(); - return prefix.isEmpty() ? node->localName().domString() : prefix + ":" + node->localName(); + return prefix.isEmpty() ? node->localName().string() : prefix + ":" + node->localName(); } Value FunCount::evaluate() const diff --git a/WebCore/xml/XPathGrammar.y b/WebCore/xml/XPathGrammar.y index 68c6c6e..50a69c2 100644 --- a/WebCore/xml/XPathGrammar.y +++ b/WebCore/xml/XPathGrammar.y @@ -82,7 +82,7 @@ void xpathyyerror(const char* str) { } %token <str> VARIABLEREFERENCE NUMBER %token DOTDOT SLASHSLASH %token <str> NAMETEST -%token ERROR +%token XPATH_ERROR %type <locationPath> LocationPath %type <locationPath> AbsoluteLocationPath diff --git a/WebCore/xml/XPathNSResolver.h b/WebCore/xml/XPathNSResolver.h index b6120c1..04b5f80 100644 --- a/WebCore/xml/XPathNSResolver.h +++ b/WebCore/xml/XPathNSResolver.h @@ -39,6 +39,9 @@ namespace WebCore { public: virtual ~XPathNSResolver(); virtual String lookupNamespaceURI(const String& prefix) = 0; + + protected: + XPathNSResolver() { } }; } diff --git a/WebCore/xml/XPathParser.cpp b/WebCore/xml/XPathParser.cpp index 75d2285..77c3011 100644 --- a/WebCore/xml/XPathParser.cpp +++ b/WebCore/xml/XPathParser.cpp @@ -202,7 +202,7 @@ Token Parser::lexString() } // Ouch, went off the end -- report error. - return Token(ERROR); + return Token(XPATH_ERROR); } Token Parser::lexNumber() @@ -306,7 +306,7 @@ Token Parser::nextTokenInternal() case '!': if (peekAheadHelper() == '=') return makeTokenAndAdvance(EQOP, EqTestOp::OP_NE, 2); - return Token(ERROR); + return Token(XPATH_ERROR); case '<': if (peekAheadHelper() == '=') return makeTokenAndAdvance(RELOP, EqTestOp::OP_LE, 2); @@ -324,14 +324,14 @@ Token Parser::nextTokenInternal() m_nextPos++; String name; if (!lexQName(name)) - return Token(ERROR); + return Token(XPATH_ERROR); return Token(VARIABLEREFERENCE, name); } } String name; if (!lexNCName(name)) - return Token(ERROR); + return Token(XPATH_ERROR); skipWS(); // If we're in an operator context, check for any operator names @@ -358,7 +358,7 @@ Token Parser::nextTokenInternal() if (isAxisName(name, axis)) return Token(AXISNAME, axis); // Ugh, :: is only valid in axis names -> error - return Token(ERROR); + return Token(XPATH_ERROR); } // Seems like this is a fully qualified qname, or perhaps the * modified one from NameTest @@ -371,7 +371,7 @@ Token Parser::nextTokenInternal() // Make a full qname. String n2; if (!lexNCName(n2)) - return Token(ERROR); + return Token(XPATH_ERROR); name = name + ":" + n2; } diff --git a/WebCore/xml/XPathResult.cpp b/WebCore/xml/XPathResult.cpp index e7c4db3..285350e 100644 --- a/WebCore/xml/XPathResult.cpp +++ b/WebCore/xml/XPathResult.cpp @@ -42,9 +42,11 @@ using namespace XPath; class InvalidatingEventListener : public EventListener { public: - InvalidatingEventListener(XPathResult* result) : m_result(result) { } + static PassRefPtr<InvalidatingEventListener> create(XPathResult* result) { return adoptRef(new InvalidatingEventListener(result)); } virtual void handleEvent(Event*, bool) { m_result->invalidateIteratorState(); } + private: + InvalidatingEventListener(XPathResult* result) : m_result(result) { } XPathResult* m_result; }; @@ -52,8 +54,8 @@ 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); + m_eventListener = InvalidatingEventListener::create(this); + m_eventTarget->addEventListener(eventNames().DOMSubtreeModifiedEvent, m_eventListener, false); switch (m_value.type()) { case Value::BooleanValue: m_resultType = BOOLEAN_TYPE; @@ -77,7 +79,7 @@ XPathResult::XPathResult(EventTargetNode* eventTarget, const Value& value) XPathResult::~XPathResult() { if (m_eventTarget) - m_eventTarget->removeEventListener(EventNames::DOMSubtreeModifiedEvent, m_eventListener.get(), false); + m_eventTarget->removeEventListener(eventNames().DOMSubtreeModifiedEvent, m_eventListener.get(), false); } void XPathResult::convertTo(unsigned short type, ExceptionCode& ec) @@ -179,7 +181,7 @@ void XPathResult::invalidateIteratorState() ASSERT(m_eventTarget); ASSERT(m_eventListener); - m_eventTarget->removeEventListener(EventNames::DOMSubtreeModifiedEvent, m_eventListener.get(), false); + m_eventTarget->removeEventListener(eventNames().DOMSubtreeModifiedEvent, m_eventListener.get(), false); m_eventTarget = 0; } diff --git a/WebCore/xml/XPathResult.h b/WebCore/xml/XPathResult.h index 356992d..ecd5cac 100644 --- a/WebCore/xml/XPathResult.h +++ b/WebCore/xml/XPathResult.h @@ -56,7 +56,7 @@ namespace WebCore { FIRST_ORDERED_NODE_TYPE = 9 }; - XPathResult(EventTargetNode*, const XPath::Value&); + static PassRefPtr<XPathResult> create(EventTargetNode* eventTarget, const XPath::Value& value) { return adoptRef(new XPathResult(eventTarget, value)); } ~XPathResult(); void convertTo(unsigned short type, ExceptionCode&); @@ -76,6 +76,8 @@ namespace WebCore { 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? diff --git a/WebCore/xml/XPathUtil.cpp b/WebCore/xml/XPathUtil.cpp index a7cd21f..ab4b1d4 100644 --- a/WebCore/xml/XPathUtil.cpp +++ b/WebCore/xml/XPathUtil.cpp @@ -29,7 +29,7 @@ #if ENABLE(XPATH) -#include "Node.h" +#include "ContainerNode.h" namespace WebCore { namespace XPath { @@ -66,16 +66,28 @@ String stringValue(Node* node) 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()); + if (!node) + return false; + switch (node->nodeType()) { + case Node::ATTRIBUTE_NODE: + case Node::CDATA_SECTION_NODE: + case Node::COMMENT_NODE: + case Node::DOCUMENT_NODE: + case Node::ELEMENT_NODE: + case Node::PROCESSING_INSTRUCTION_NODE: + case Node::XPATH_NAMESPACE_NODE: + return true; + case Node::DOCUMENT_FRAGMENT_NODE: + case Node::DOCUMENT_TYPE_NODE: + case Node::ENTITY_NODE: + case Node::ENTITY_REFERENCE_NODE: + case Node::NOTATION_NODE: + return false; + case Node::TEXT_NODE: + return !(node->parentNode() && node->parentNode()->isAttributeNode()); + } + ASSERT_NOT_REACHED(); + return false; } } diff --git a/WebCore/xml/XPathValue.cpp b/WebCore/xml/XPathValue.cpp index b21942f..b3cad38 100644 --- a/WebCore/xml/XPathValue.cpp +++ b/WebCore/xml/XPathValue.cpp @@ -55,7 +55,7 @@ const NodeSet& Value::toNodeSet() const NodeSet& Value::modifiableNodeSet() { if (!m_data) - m_data = new ValueData; + m_data = ValueData::create(); m_type = NodeSetValue; return m_data->m_nodeSet; diff --git a/WebCore/xml/XPathValue.h b/WebCore/xml/XPathValue.h index b2ad0d6..a0cd24d 100644 --- a/WebCore/xml/XPathValue.h +++ b/WebCore/xml/XPathValue.h @@ -37,13 +37,18 @@ namespace WebCore { namespace XPath { class ValueData : public RefCounted<ValueData> { - public: - ValueData() {} - ValueData(const NodeSet& nodeSet) : m_nodeSet(nodeSet) {} - ValueData(const String& string) : m_string(string) {} + 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. @@ -51,20 +56,20 @@ namespace WebCore { 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(unsigned value) : m_type(NumberValue), m_bool(false), m_number(value) {} + Value(unsigned long value) : m_type(NumberValue), m_bool(false), m_number(value) {} + Value(double value) : m_type(NumberValue), m_bool(false), m_number(value) {} - Value(const char* value) : m_type(StringValue), m_data(new ValueData(value)) {} - Value(const String& value) : m_type(StringValue), m_data(new ValueData(value)) {} - Value(const NodeSet& value) : m_type(NodeSetValue), m_data(new ValueData(value)) {} - Value(Node* value) : m_type(NodeSetValue), m_data(new ValueData) { m_data->m_nodeSet.append(value); } + Value(const char* value) : m_type(StringValue), m_bool(false), m_number(0), m_data(ValueData::create(value)) {} + Value(const String& value) : m_type(StringValue), m_bool(false), m_number(0), m_data(ValueData::create(value)) {} + Value(const NodeSet& value) : m_type(NodeSetValue), m_bool(false), m_number(0), m_data(ValueData::create(value)) {} + Value(Node* value) : m_type(NodeSetValue), m_bool(false), m_number(0), m_data(ValueData::create()) { m_data->m_nodeSet.append(value); } // This is needed to safely implement constructing from bool - with normal function overloading, any pointer type would match. template<typename T> Value(T); static const struct AdoptTag {} adopt; - Value(NodeSet& value, const AdoptTag&) : m_type(NodeSetValue), m_data(new ValueData) { value.swap(m_data->m_nodeSet); } + Value(NodeSet& value, const AdoptTag&) : m_type(NodeSetValue), m_bool(false), m_number(0), m_data(ValueData::create()) { value.swap(m_data->m_nodeSet); } Type type() const { return m_type; } @@ -90,6 +95,7 @@ namespace WebCore { inline Value::Value(bool value) : m_type(BooleanValue) , m_bool(value) + , m_number(0) { } } diff --git a/WebCore/xml/XSLImportRule.cpp b/WebCore/xml/XSLImportRule.cpp index bfdec20..2efafa3 100644 --- a/WebCore/xml/XSLImportRule.cpp +++ b/WebCore/xml/XSLImportRule.cpp @@ -1,7 +1,7 @@ -/** +/* * This file is part of the XSL implementation. * - * Copyright (C) 2004, 2005, 2006 Apple Computer, Inc. + * 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 @@ -30,7 +30,7 @@ namespace WebCore { -XSLImportRule::XSLImportRule(StyleBase* parent, const String& href) +XSLImportRule::XSLImportRule(XSLStyleSheet* parent, const String& href) : StyleBase(parent) , m_strHref(href) , m_cachedSheet(0) @@ -44,7 +44,7 @@ XSLImportRule::~XSLImportRule() m_styleSheet->setParent(0); if (m_cachedSheet) - m_cachedSheet->deref(this); + m_cachedSheet->removeClient(this); } XSLStyleSheet* XSLImportRule::parentStyleSheet() const @@ -57,7 +57,7 @@ void XSLImportRule::setXSLStyleSheet(const String& url, const String& sheet) if (m_styleSheet) m_styleSheet->setParent(0); - m_styleSheet = new XSLStyleSheet(this, url); + m_styleSheet = XSLStyleSheet::create(this, url); XSLStyleSheet* parent = parentStyleSheet(); if (parent) @@ -66,7 +66,8 @@ void XSLImportRule::setXSLStyleSheet(const String& url, const String& sheet) m_styleSheet->parseString(sheet); m_loading = false; - checkLoaded(); + if (parent) + parent->checkLoaded(); } bool XSLImportRule::isLoading() @@ -88,19 +89,19 @@ void XSLImportRule::loadSheet() XSLStyleSheet* parentSheet = parentStyleSheet(); if (!parentSheet->href().isNull()) // use parent styleheet's URL as the base URL - absHref = KURL(parentSheet->href().deprecatedString(), m_strHref.deprecatedString()).string(); + 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()) + for (parent = this->parent(); parent; parent = parent->parent()) { + if (parent->isXSLStyleSheet() && absHref == static_cast<XSLStyleSheet*>(parent)->href()) return; } m_cachedSheet = docLoader->requestXSLStyleSheet(absHref); if (m_cachedSheet) { - m_cachedSheet->ref(this); + m_cachedSheet->addClient(this); // If the imported sheet is in the cache, then setXSLStyleSheet gets called, // and the sheet even gets parsed (via parseString). In this case we have diff --git a/WebCore/xml/XSLImportRule.h b/WebCore/xml/XSLImportRule.h index e4bbc4b..fc7a7f8 100644 --- a/WebCore/xml/XSLImportRule.h +++ b/WebCore/xml/XSLImportRule.h @@ -1,7 +1,7 @@ /* * This file is part of the XSL implementation. * - * Copyright (C) 2004, 2006 Apple Computer, Inc. + * 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 @@ -26,6 +26,7 @@ #if ENABLE(XSLT) #include "CachedResourceClient.h" +#include "CachedResourceHandle.h" #include "StyleBase.h" #include "XSLStyleSheet.h" @@ -33,27 +34,34 @@ namespace WebCore { class CachedXSLStyleSheet; -class XSLImportRule : public CachedResourceClient, public StyleBase { +class XSLImportRule : public StyleBase, private CachedResourceClient { public: - XSLImportRule(StyleBase* parent, const String& href); + static PassRefPtr<XSLImportRule> create(XSLStyleSheet* parentSheet, const String& href) + { + return adoptRef(new XSLImportRule(parentSheet, href)); + } + virtual ~XSLImportRule(); - String href() const { return m_strHref; } + const String& href() const { return m_strHref; } XSLStyleSheet* styleSheet() const { return m_styleSheet.get(); } - - virtual bool isImportRule() { return true; } + XSLStyleSheet* parentStyleSheet() const; + + bool isLoading(); + void loadSheet(); +private: + XSLImportRule(XSLStyleSheet* parentSheet, const String& href); + + virtual bool isImportRule() { return true; } + // from CachedResourceClient virtual void setXSLStyleSheet(const String& url, const String& sheet); - bool isLoading(); - void loadSheet(); - -protected: String m_strHref; RefPtr<XSLStyleSheet> m_styleSheet; - CachedXSLStyleSheet* m_cachedSheet; + CachedResourceHandle<CachedXSLStyleSheet> m_cachedSheet; bool m_loading; }; diff --git a/WebCore/xml/XSLStyleSheet.cpp b/WebCore/xml/XSLStyleSheet.cpp index 22a0464..9443652 100644 --- a/WebCore/xml/XSLStyleSheet.cpp +++ b/WebCore/xml/XSLStyleSheet.cpp @@ -1,7 +1,7 @@ -/** +/* * This file is part of the XSL implementation. * - * Copyright (C) 2004, 2005, 2006 Apple Computer, Inc. + * 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 @@ -25,11 +25,13 @@ #if ENABLE(XSLT) #include "CString.h" +#include "Console.h" +#include "DOMWindow.h" #include "DocLoader.h" #include "Document.h" +#include "Frame.h" #include "loader.h" #include "Node.h" -#include "Page.h" #include "XMLTokenizer.h" #include "XSLImportRule.h" #include "XSLTProcessor.h" @@ -97,8 +99,8 @@ void XSLStyleSheet::checkLoaded() return; if (parent()) parent()->checkLoaded(); - if (m_parentNode) - m_parentNode->sheetLoaded(); + if (ownerNode()) + ownerNode()->sheetLoaded(); } xmlDocPtr XSLStyleSheet::document() @@ -139,10 +141,11 @@ bool XSLStyleSheet::parseString(const String& string, bool strict) xmlFreeDoc(m_stylesheetDoc); m_stylesheetDocTaken = false; - Chrome* chrome = 0; - if (Page* page = ownerDocument()->page()) - chrome = page->chrome(); - xmlSetStructuredErrorFunc(chrome, XSLTProcessor::parseErrorFunc); + Console* console = 0; + if (Frame* frame = ownerDocument()->frame()) + console = frame->domWindow()->console(); + xmlSetStructuredErrorFunc(console, XSLTProcessor::parseErrorFunc); + xmlSetGenericErrorFunc(console, XSLTProcessor::genericErrorFunc); m_stylesheetDoc = xmlReadMemory(reinterpret_cast<const char*>(string.characters()), string.length() * sizeof(UChar), href().utf8().data(), @@ -151,6 +154,7 @@ bool XSLStyleSheet::parseString(const String& string, bool strict) loadChildSheets(); xmlSetStructuredErrorFunc(0, 0); + xmlSetGenericErrorFunc(0, 0); setLoaderForLibXMLCallbacks(0); return m_stylesheetDoc; @@ -190,7 +194,7 @@ void XSLStyleSheet::loadChildSheets() } if (IS_XSLT_ELEM(curr) && IS_XSLT_NAME(curr, "import")) { xmlChar* uriRef = xsltGetNsProp(curr, (const xmlChar*)"href", XSLT_NAMESPACE); - loadChildSheet(DeprecatedString::fromUtf8((const char*)uriRef)); + loadChildSheet(String::fromUTF8((const char*)uriRef)); xmlFree(uriRef); } else break; @@ -201,7 +205,7 @@ void XSLStyleSheet::loadChildSheets() 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(DeprecatedString::fromUtf8((const char*)uriRef)); + loadChildSheet(String::fromUTF8((const char*)uriRef)); xmlFree(uriRef); } curr = curr->next; @@ -209,9 +213,9 @@ void XSLStyleSheet::loadChildSheets() } } -void XSLStyleSheet::loadChildSheet(const DeprecatedString& href) +void XSLStyleSheet::loadChildSheet(const String& href) { - RefPtr<XSLImportRule> childRule = new XSLImportRule(this, href); + RefPtr<XSLImportRule> childRule = XSLImportRule::create(this, href); append(childRule); childRule->loadSheet(); } diff --git a/WebCore/xml/XSLStyleSheet.h b/WebCore/xml/XSLStyleSheet.h index 43c7bed..8946529 100644 --- a/WebCore/xml/XSLStyleSheet.h +++ b/WebCore/xml/XSLStyleSheet.h @@ -1,7 +1,7 @@ /* * This file is part of the XSL implementation. * - * Copyright (C) 2004, 2006 Apple Computer, Inc. + * 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 @@ -28,6 +28,7 @@ #include "StyleSheet.h" #include <libxml/parser.h> #include <libxslt/transform.h> +#include <wtf/PassRefPtr.h> namespace WebCore { @@ -37,9 +38,20 @@ 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(); + static PassRefPtr<XSLStyleSheet> create(XSLImportRule* parentImport, const String& href) + { + return adoptRef(new XSLStyleSheet(parentImport, href)); + } + static PassRefPtr<XSLStyleSheet> create(Node* parentNode, const String& href) + { + return adoptRef(new XSLStyleSheet(parentNode, href, false)); + } + static PassRefPtr<XSLStyleSheet> createEmbedded(Node* parentNode, const String& href) + { + return adoptRef(new XSLStyleSheet(parentNode, href, true)); + } + + virtual ~XSLStyleSheet(); virtual bool isXSLStyleSheet() const { return true; } @@ -51,7 +63,7 @@ public: virtual void checkLoaded(); void loadChildSheets(); - void loadChildSheet(const DeprecatedString& href); + void loadChildSheet(const String& href); xsltStylesheetPtr compileStyleSheet(); @@ -69,7 +81,10 @@ public: void markAsProcessed(); bool processed() const { return m_processed; } -protected: +private: + XSLStyleSheet(Node* parentNode, const String& href, bool embedded); + XSLStyleSheet(XSLImportRule* parentImport, const String& href); + Document* m_ownerDocument; xmlDocPtr m_stylesheetDoc; bool m_embedded; diff --git a/WebCore/xml/XSLTProcessor.cpp b/WebCore/xml/XSLTProcessor.cpp index 07efe75..ab554c4 100644 --- a/WebCore/xml/XSLTProcessor.cpp +++ b/WebCore/xml/XSLTProcessor.cpp @@ -1,7 +1,7 @@ -/** +/* * This file is part of the XSL implementation. * - * Copyright (C) 2004, 2005, 2006, 2007 Apple, Inc. + * 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 @@ -27,8 +27,9 @@ #include "XSLTProcessor.h" #include "CString.h" -#include "Cache.h" +#include "Console.h" #include "DOMImplementation.h" +#include "DOMWindow.h" #include "DocLoader.h" #include "DocumentFragment.h" #include "Frame.h" @@ -37,6 +38,7 @@ #include "HTMLDocument.h" #include "HTMLTokenizer.h" #include "Page.h" +#include "ResourceError.h" #include "ResourceHandle.h" #include "ResourceRequest.h" #include "ResourceResponse.h" @@ -72,10 +74,15 @@ SOFT_LINK(libxslt, xsltNextImport, xsltStylesheetPtr, (xsltStylesheetPtr style), namespace WebCore { +void XSLTProcessor::genericErrorFunc(void* userData, const char* msg, ...) +{ + // It would be nice to do something with this error message. +} + void XSLTProcessor::parseErrorFunc(void* userData, xmlError* error) { - Chrome* chrome = static_cast<Chrome*>(userData); - if (!chrome) + Console* console = static_cast<Console*>(userData); + if (!console) return; MessageLevel level; @@ -93,7 +100,7 @@ void XSLTProcessor::parseErrorFunc(void* userData, xmlError* error) break; } - chrome->addMessageToConsole(XMLMessageSource, level, error->message, error->line, error->file); + console->addMessage(XMLMessageSource, level, error->message, error->line, error->file); } // FIXME: There seems to be no way to control the ctxt pointer for loading here, thus we have globals. @@ -112,26 +119,35 @@ static xmlDocPtr docLoaderFunc(const xmlChar* uri, case XSLT_LOAD_DOCUMENT: { xsltTransformContextPtr context = (xsltTransformContextPtr)ctxt; xmlChar* base = xmlNodeGetBase(context->document->doc, context->node); - KURL url((const char*)base, (const char*)uri); + 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()) + bool requestAllowed = globalDocLoader->frame() && globalDocLoader->doc()->securityOrigin()->canRequest(url); + if (requestAllowed) { 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); - + requestAllowed = globalDocLoader->doc()->securityOrigin()->canRequest(response.url()); + } + if (!requestAllowed) { + data.clear(); + globalDocLoader->printAccessDeniedMessage(url); + } + + Console* console = 0; + if (Frame* frame = globalProcessor->xslStylesheet()->ownerDocument()->frame()) + console = frame->domWindow()->console(); + xmlSetStructuredErrorFunc(console, XSLTProcessor::parseErrorFunc); + xmlSetGenericErrorFunc(console, XSLTProcessor::genericErrorFunc); + // We don't specify an encoding here. Neither Gecko nor WinIE respects // the encoding specified in the HTTP headers. xmlDocPtr doc = xmlReadMemory(data.data(), data.size(), (const char*)uri, 0, options); xmlSetStructuredErrorFunc(0, 0); + xmlSetGenericErrorFunc(0, 0); return doc; } @@ -230,7 +246,7 @@ static void freeXsltParamArray(const char** params) } -RefPtr<Document> XSLTProcessor::createDocumentFromSource(const String& sourceString, +PassRefPtr<Document> XSLTProcessor::createDocumentFromSource(const String& sourceString, const String& sourceEncoding, const String& sourceMIMEType, Node* sourceNode, Frame* frame) { RefPtr<Document> ownerDocument = sourceNode->document(); @@ -253,25 +269,22 @@ RefPtr<Document> XSLTProcessor::createDocumentFromSource(const String& sourceStr frame->setDocument(result); } - result->open(); - if (sourceIsDocument) { + if (sourceIsDocument) result->setURL(ownerDocument->url()); - result->setBaseURL(ownerDocument->baseURL()); - } - result->determineParseMode(documentSource); // Make sure we parse in the correct mode. + result->open(); - RefPtr<TextResourceDecoder> decoder = new TextResourceDecoder(sourceMIMEType); + RefPtr<TextResourceDecoder> decoder = TextResourceDecoder::create(sourceMIMEType); decoder->setEncoding(sourceEncoding.isEmpty() ? UTF8Encoding() : TextEncoding(sourceEncoding), TextResourceDecoder::EncodingFromXMLHeader); - result->setDecoder(decoder.get()); + result->setDecoder(decoder.release()); result->write(documentSource); result->finishParsing(); result->close(); - return result; + return result.release(); } -static inline RefPtr<DocumentFragment> createFragmentFromSource(String sourceString, String sourceMIMEType, Node* sourceNode, Document* outputDoc) +static inline RefPtr<DocumentFragment> createFragmentFromSource(const String& sourceString, const String& sourceMIMEType, Node* sourceNode, Document* outputDoc) { RefPtr<DocumentFragment> fragment = new DocumentFragment(outputDoc); @@ -293,7 +306,8 @@ static inline RefPtr<DocumentFragment> createFragmentFromSource(String sourceStr static xsltStylesheetPtr xsltStylesheetPointer(RefPtr<XSLStyleSheet>& cachedStylesheet, Node* stylesheetRootNode) { if (!cachedStylesheet && stylesheetRootNode) { - cachedStylesheet = new XSLStyleSheet(stylesheetRootNode->parent() ? stylesheetRootNode->parent() : stylesheetRootNode, stylesheetRootNode->document()->url()); + cachedStylesheet = XSLStyleSheet::create(stylesheetRootNode->parent() ? stylesheetRootNode->parent() : stylesheetRootNode, + stylesheetRootNode->document()->url().string()); cachedStylesheet->parseString(createMarkup(stylesheetRootNode)); } @@ -312,7 +326,8 @@ static inline xmlDocPtr xmlDocPtrFromNode(Node* sourceNode, bool& shouldDelete) if (sourceIsDocument) sourceDoc = (xmlDocPtr)ownerDocument->transformSource(); if (!sourceDoc) { - sourceDoc = (xmlDocPtr)xmlDocPtrForString(ownerDocument->docLoader(), createMarkup(sourceNode), sourceIsDocument ? ownerDocument->url() : DeprecatedString()); + sourceDoc = (xmlDocPtr)xmlDocPtrForString(ownerDocument->docLoader(), createMarkup(sourceNode), + sourceIsDocument ? ownerDocument->url().string() : String()); shouldDelete = (sourceDoc != 0); } return sourceDoc; @@ -396,7 +411,7 @@ bool XSLTProcessor::transformToString(Node* sourceNode, String& mimeType, String return success; } -RefPtr<Document> XSLTProcessor::transformToDocument(Node* sourceNode) +PassRefPtr<Document> XSLTProcessor::transformToDocument(Node* sourceNode) { String resultMIMEType; String resultString; @@ -406,7 +421,7 @@ RefPtr<Document> XSLTProcessor::transformToDocument(Node* sourceNode) return createDocumentFromSource(resultString, resultEncoding, resultMIMEType, sourceNode, 0); } -RefPtr<DocumentFragment> XSLTProcessor::transformToFragment(Node* sourceNode, Document* outputDoc) +PassRefPtr<DocumentFragment> XSLTProcessor::transformToFragment(Node* sourceNode, Document* outputDoc) { String resultMIMEType; String resultString; diff --git a/WebCore/xml/XSLTProcessor.h b/WebCore/xml/XSLTProcessor.h index 3a92a59..9ee2aad 100644 --- a/WebCore/xml/XSLTProcessor.h +++ b/WebCore/xml/XSLTProcessor.h @@ -1,7 +1,7 @@ /* * This file is part of the XSL implementation. * - * Copyright (C) 2004, 2007 Apple, Inc. + * Copyright (C) 2004, 2007, 2008 Apple, Inc. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -25,53 +25,53 @@ #if ENABLE(XSLT) +#include "Node.h" #include "StringHash.h" #include "XSLStyleSheet.h" -#include <wtf/HashMap.h> #include <libxml/parserInternals.h> #include <libxslt/documents.h> +#include <wtf/HashMap.h> namespace WebCore { class Frame; -class Node; class Document; class DocumentFragment; -class XSLTProcessor : public RefCounted<XSLTProcessor> -{ +class XSLTProcessor : public RefCounted<XSLTProcessor> { public: - void setXSLStylesheet(XSLStyleSheet* styleSheet) { m_stylesheet = styleSheet; } + static PassRefPtr<XSLTProcessor> create() { return adoptRef(new XSLTProcessor); } + + void setXSLStyleSheet(PassRefPtr<XSLStyleSheet> styleSheet) { m_stylesheet = styleSheet; } bool transformToString(Node* source, String& resultMIMEType, String& resultString, String& resultEncoding); - RefPtr<Document> createDocumentFromSource(const String& source, const String& sourceEncoding, const String& sourceMIMEType, Node* sourceNode, Frame* frame); + PassRefPtr<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 importStylesheet(PassRefPtr<Node> style) { m_stylesheetRootNode = style; } + PassRefPtr<DocumentFragment> transformToFragment(Node* source, Document* ouputDoc); + PassRefPtr<Document> transformToDocument(Node* source); void setParameter(const String& namespaceURI, const String& localName, const String& value); String getParameter(const String& namespaceURI, const String& localName) const; void removeParameter(const String& namespaceURI, const String& localName); void clearParameters() { m_parameters.clear(); } - void reset() { m_stylesheet = NULL; m_stylesheetRootNode = NULL; m_parameters.clear(); } + void reset() { m_stylesheet.clear(); m_stylesheetRootNode.clear(); m_parameters.clear(); } static void parseErrorFunc(void* userData, xmlError*); - + static void genericErrorFunc(void* userData, const char* msg, ...); + public: // Only for libXSLT callbacks XSLStyleSheet* xslStylesheet() const { return m_stylesheet.get(); } - + typedef HashMap<String, String> ParameterMap; private: - // Convert a libxml doc ptr to a KHTML DOM Document - RefPtr<Document> documentFromXMLDocPtr(xmlDocPtr resultDoc, xsltStylesheetPtr sheet, Document* ownerDocument, bool sourceIsDocument); + XSLTProcessor() { } RefPtr<XSLStyleSheet> m_stylesheet; RefPtr<Node> m_stylesheetRootNode; - ParameterMap m_parameters; }; diff --git a/WebCore/xml/XSLTProcessor.idl b/WebCore/xml/XSLTProcessor.idl new file mode 100644 index 0000000..0a6ff93 --- /dev/null +++ b/WebCore/xml/XSLTProcessor.idl @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2008 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +module xml { + + // Eventually we should implement XSLTException: + // http://lxr.mozilla.org/seamonkey/source/content/xsl/public/nsIXSLTException.idl + // http://bugs.webkit.org/show_bug.cgi?id=5446 + + interface [ + Conditional=XSLT + ] XSLTProcessor { + + [Custom] void importStylesheet(in Node stylesheet); + [Custom] DocumentFragment transformToFragment(in Node source, in Document docVal); + [Custom] Document transformToDocument(in Node source); + + [Custom] void setParameter(in DOMString namespaceURI, in DOMString localName, in DOMString value); + [Custom, ConvertNullStringTo=Undefined] DOMString getParameter(in DOMString namespaceURI, in DOMString localName); + [Custom] void removeParameter(in DOMString namespaceURI, in DOMString localName); + void clearParameters(); + + void reset(); + + }; + +} diff --git a/WebCore/xml/XSLTUnicodeSort.cpp b/WebCore/xml/XSLTUnicodeSort.cpp index e1ecdc4..ed66112 100644 --- a/WebCore/xml/XSLTUnicodeSort.cpp +++ b/WebCore/xml/XSLTUnicodeSort.cpp @@ -31,15 +31,12 @@ #if ENABLE(XSLT) +#include "PlatformString.h" + #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 +#include <wtf/unicode/Collator.h> #if PLATFORM(MAC) #include "SoftLinking.h" @@ -122,14 +119,6 @@ void xsltUnicodeSortFunction(xsltTransformContextPtr ctxt, xmlNodePtr *sorts, in 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; @@ -201,28 +190,12 @@ void xsltUnicodeSortFunction(xsltTransformContextPtr ctxt, xmlNodePtr *sorts, in 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 + // We are passing a language identifier to a function that expects a locale identifier. + // The implementation of Collator should be lenient, and accept both "en-US" and "en_US", for example. + // This lets an author to really specify sorting rules, e.g. "de_DE@collation=phonebook", which isn't + // possible with language alone. + Collator collator(comp->has_lang ? (const char*)comp->lang : "en"); + collator.setOrderLowerFirst(comp->lower_first); /* Shell's sort of node-set */ for (incr = len / 2; incr > 0; incr /= 2) { @@ -253,20 +226,9 @@ void xsltUnicodeSortFunction(xsltTransformContextPtr ctxt, xmlNodePtr *sorts, in 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 + String str1 = String::fromUTF8((const char*)results[j]->stringval); + String str2 = String::fromUTF8((const char*)results[j + incr]->stringval); + tst = collator.collate(str1.characters(), str1.length(), str2.characters(), str2.length()); } if (descending) tst = -tst; @@ -319,20 +281,9 @@ void xsltUnicodeSortFunction(xsltTransformContextPtr ctxt, xmlNodePtr *sorts, in 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 + String str1 = String::fromUTF8((const char*)res[j]->stringval); + String str2 = String::fromUTF8((const char*)res[j + incr]->stringval); + tst = collator.collate(str1.characters(), str1.length(), str2.characters(), str2.length()); } if (desc) tst = -tst; @@ -376,11 +327,6 @@ void xsltUnicodeSortFunction(xsltTransformContextPtr ctxt, xmlNodePtr *sorts, in } } -#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) { diff --git a/WebCore/xml/XSLTUnicodeSort.h b/WebCore/xml/XSLTUnicodeSort.h index ff5b253..c8d395b 100644 --- a/WebCore/xml/XSLTUnicodeSort.h +++ b/WebCore/xml/XSLTUnicodeSort.h @@ -28,8 +28,6 @@ #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> diff --git a/WebCore/xml/xmlattrs.in b/WebCore/xml/xmlattrs.in index bfda2f2..6cc47be 100644 --- a/WebCore/xml/xmlattrs.in +++ b/WebCore/xml/xmlattrs.in @@ -1,3 +1,6 @@ +namespace="XML" +namespaceURI="http://www.w3.org/XML/1998/namespace" + base lang space |