/* * Copyright (C) 2009 Google 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: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 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. * * Neither the name of Google Inc. 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "config.h" #include "ResourceHandle.h" #include "ChromiumBridge.h" #include "ResourceHandleClient.h" #include "ResourceRequest.h" #include "SharedBuffer.h" #include "WebKit.h" #include "WebKitClient.h" #include "WebURLError.h" #include "WebURLLoader.h" #include "WebURLLoaderClient.h" #include "WebURLRequest.h" #include "WebURLResponse.h" #include "WrappedResourceRequest.h" #include "WrappedResourceResponse.h" using namespace WebKit; namespace WebCore { // ResourceHandleInternal ----------------------------------------------------- class ResourceHandleInternal : public WebURLLoaderClient { public: ResourceHandleInternal(const ResourceRequest& request, ResourceHandleClient* client) : m_request(request) , m_owner(0) , m_client(client) , m_state(ConnectionStateNew) { } void start(); void cancel(); void setDefersLoading(bool); bool allowStoredCredentials() const; // WebURLLoaderClient methods: virtual void willSendRequest(WebURLLoader*, WebURLRequest&, const WebURLResponse&); virtual void didSendData( WebURLLoader*, unsigned long long bytesSent, unsigned long long totalBytesToBeSent); virtual void didReceiveResponse(WebURLLoader*, const WebURLResponse&); virtual void didReceiveData(WebURLLoader*, const char* data, int dataLength); virtual void didReceiveCachedMetadata(WebURLLoader*, const char* data, int dataLength); virtual void didFinishLoading(WebURLLoader*, double finishTime); virtual void didFail(WebURLLoader*, const WebURLError&); enum ConnectionState { ConnectionStateNew, ConnectionStateStarted, ConnectionStateReceivedResponse, ConnectionStateReceivingData, ConnectionStateFinishedLoading, ConnectionStateCanceled, ConnectionStateFailed, }; ResourceRequest m_request; ResourceHandle* m_owner; ResourceHandleClient* m_client; OwnPtr m_loader; // Used for sanity checking to make sure we don't experience illegal state // transitions. ConnectionState m_state; }; void ResourceHandleInternal::start() { if (m_state != ConnectionStateNew) CRASH(); m_state = ConnectionStateStarted; m_loader.set(webKitClient()->createURLLoader()); ASSERT(m_loader.get()); WrappedResourceRequest wrappedRequest(m_request); wrappedRequest.setAllowStoredCredentials(allowStoredCredentials()); m_loader->loadAsynchronously(wrappedRequest, this); } void ResourceHandleInternal::cancel() { m_state = ConnectionStateCanceled; m_loader->cancel(); // Do not make any further calls to the client. m_client = 0; } void ResourceHandleInternal::setDefersLoading(bool value) { m_loader->setDefersLoading(value); } bool ResourceHandleInternal::allowStoredCredentials() const { return m_client && m_client->shouldUseCredentialStorage(m_owner); } void ResourceHandleInternal::willSendRequest( WebURLLoader*, WebURLRequest& request, const WebURLResponse& response) { ASSERT(m_client); ASSERT(!request.isNull()); ASSERT(!response.isNull()); m_client->willSendRequest(m_owner, request.toMutableResourceRequest(), response.toResourceResponse()); } void ResourceHandleInternal::didSendData( WebURLLoader*, unsigned long long bytesSent, unsigned long long totalBytesToBeSent) { ASSERT(m_client); m_client->didSendData(m_owner, bytesSent, totalBytesToBeSent); } void ResourceHandleInternal::didReceiveResponse(WebURLLoader*, const WebURLResponse& response) { ASSERT(m_client); ASSERT(!response.isNull()); bool isMultipart = response.isMultipartPayload(); bool isValidStateTransition = (m_state == ConnectionStateStarted || m_state == ConnectionStateReceivedResponse); // In the case of multipart loads, calls to didReceiveData & didReceiveResponse can be interleaved. if (!isMultipart && !isValidStateTransition) CRASH(); m_state = ConnectionStateReceivedResponse; m_client->didReceiveResponse(m_owner, response.toResourceResponse()); } void ResourceHandleInternal::didReceiveData( WebURLLoader*, const char* data, int dataLength) { ASSERT(m_client); if (m_state != ConnectionStateReceivedResponse && m_state != ConnectionStateReceivingData) CRASH(); m_state = ConnectionStateReceivingData; // FIXME(yurys): it looks like lengthReceived is always the same as // dataLength and that the latter parameter can be eliminated. // See WebKit bug: https://bugs.webkit.org/show_bug.cgi?id=31019 m_client->didReceiveData(m_owner, data, dataLength, dataLength); } void ResourceHandleInternal::didReceiveCachedMetadata(WebURLLoader*, const char* data, int dataLength) { ASSERT(m_client); if (m_state != ConnectionStateReceivedResponse && m_state != ConnectionStateReceivingData) CRASH(); m_client->didReceiveCachedMetadata(m_owner, data, dataLength); } void ResourceHandleInternal::didFinishLoading(WebURLLoader*, double finishTime) { ASSERT(m_client); if (m_state != ConnectionStateReceivedResponse && m_state != ConnectionStateReceivingData) CRASH(); m_state = ConnectionStateFinishedLoading; m_client->didFinishLoading(m_owner, finishTime); } void ResourceHandleInternal::didFail(WebURLLoader*, const WebURLError& error) { ASSERT(m_client); m_state = ConnectionStateFailed; m_client->didFail(m_owner, error); } // ResourceHandle ------------------------------------------------------------- ResourceHandle::ResourceHandle(const ResourceRequest& request, ResourceHandleClient* client, bool defersLoading, bool shouldContentSniff) : d(new ResourceHandleInternal(request, client)) { d->m_owner = this; // FIXME: Figure out what to do with the bool params. } PassRefPtr ResourceHandle::create(NetworkingContext* context, const ResourceRequest& request, ResourceHandleClient* client, bool defersLoading, bool shouldContentSniff) { RefPtr newHandle = adoptRef(new ResourceHandle( request, client, defersLoading, shouldContentSniff)); if (newHandle->start(context)) return newHandle.release(); return 0; } ResourceRequest& ResourceHandle::firstRequest() { return d->m_request; } ResourceHandleClient* ResourceHandle::client() const { return d->m_client; } void ResourceHandle::setClient(ResourceHandleClient* client) { d->m_client = client; } void ResourceHandle::setDefersLoading(bool value) { d->setDefersLoading(value); } bool ResourceHandle::start(NetworkingContext* context) { d->start(); return true; } bool ResourceHandle::hasAuthenticationChallenge() const { return false; } void ResourceHandle::clearAuthentication() { } void ResourceHandle::cancel() { d->cancel(); } ResourceHandle::~ResourceHandle() { d->m_owner = 0; } PassRefPtr ResourceHandle::bufferedData() { return 0; } bool ResourceHandle::loadsBlocked() { return false; // This seems to be related to sync XMLHttpRequest... } // static bool ResourceHandle::supportsBufferedData() { return false; // The loader will buffer manually if it needs to. } // static void ResourceHandle::loadResourceSynchronously(NetworkingContext* context, const ResourceRequest& request, StoredCredentials storedCredentials, ResourceError& error, ResourceResponse& response, Vector& data) { OwnPtr loader(webKitClient()->createURLLoader()); ASSERT(loader.get()); WrappedResourceRequest requestIn(request); requestIn.setAllowStoredCredentials(storedCredentials == AllowStoredCredentials); WrappedResourceResponse responseOut(response); WebURLError errorOut; WebData dataOut; loader->loadSynchronously(requestIn, responseOut, errorOut, dataOut); error = errorOut; data.clear(); data.append(dataOut.data(), dataOut.size()); } // static bool ResourceHandle::willLoadFromCache(ResourceRequest& request, Frame*) { // This method is used to determine if a POST request can be repeated from // cache, but you cannot really know until you actually try to read from the // cache. Even if we checked now, something else could come along and wipe // out the cache entry by the time we fetch it. // // So, we always say yes here, to prevent the FrameLoader from initiating a // reload. Then in FrameLoaderClientImpl::dispatchWillSendRequest, we // fix-up the cache policy of the request to force a load from the cache. // ASSERT(request.httpMethod() == "POST"); return true; } // static void ResourceHandle::cacheMetadata(const ResourceResponse& response, const Vector& data) { ChromiumBridge::cacheMetadata(response.url(), response.responseTime(), data); } } // namespace WebCore