1/*
2 * Copyright (C) 2009 Google Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met:
7 *
8 *     * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 *     * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following disclaimer
12 * in the documentation and/or other materials provided with the
13 * distribution.
14 *     * Neither the name of Google Inc. nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31#include "config.h"
32#include "ResourceHandle.h"
33
34#include "PlatformBridge.h"
35#include "ResourceHandleClient.h"
36#include "ResourceRequest.h"
37#include "SharedBuffer.h"
38
39#include "WebKit.h"
40#include "WebKitClient.h"
41#include "WebURLError.h"
42#include "WebURLLoader.h"
43#include "WebURLLoaderClient.h"
44#include "WebURLRequest.h"
45#include "WebURLResponse.h"
46#include "WrappedResourceRequest.h"
47#include "WrappedResourceResponse.h"
48
49using namespace WebKit;
50
51namespace WebCore {
52
53// ResourceHandleInternal -----------------------------------------------------
54
55class ResourceHandleInternal : public WebURLLoaderClient {
56public:
57    ResourceHandleInternal(const ResourceRequest& request, ResourceHandleClient* client)
58        : m_request(request)
59        , m_owner(0)
60        , m_client(client)
61        , m_state(ConnectionStateNew)
62    {
63    }
64
65    void start();
66    void cancel();
67    void setDefersLoading(bool);
68    bool allowStoredCredentials() const;
69
70    // WebURLLoaderClient methods:
71    virtual void willSendRequest(WebURLLoader*, WebURLRequest&, const WebURLResponse&);
72    virtual void didSendData(
73        WebURLLoader*, unsigned long long bytesSent, unsigned long long totalBytesToBeSent);
74    virtual void didReceiveResponse(WebURLLoader*, const WebURLResponse&);
75    virtual void didReceiveData(WebURLLoader*, const char* data, int dataLength, int encodedDataLength);
76
77    virtual void didReceiveCachedMetadata(WebURLLoader*, const char* data, int dataLength);
78    virtual void didFinishLoading(WebURLLoader*, double finishTime);
79    virtual void didFail(WebURLLoader*, const WebURLError&);
80
81    enum ConnectionState {
82        ConnectionStateNew,
83        ConnectionStateStarted,
84        ConnectionStateReceivedResponse,
85        ConnectionStateReceivingData,
86        ConnectionStateFinishedLoading,
87        ConnectionStateCanceled,
88        ConnectionStateFailed,
89    };
90
91    ResourceRequest m_request;
92    ResourceHandle* m_owner;
93    ResourceHandleClient* m_client;
94    OwnPtr<WebURLLoader> m_loader;
95
96    // Used for sanity checking to make sure we don't experience illegal state
97    // transitions.
98    ConnectionState m_state;
99};
100
101void ResourceHandleInternal::start()
102{
103    if (m_state != ConnectionStateNew)
104        CRASH();
105    m_state = ConnectionStateStarted;
106
107    m_loader.set(webKitClient()->createURLLoader());
108    ASSERT(m_loader.get());
109
110    WrappedResourceRequest wrappedRequest(m_request);
111    wrappedRequest.setAllowStoredCredentials(allowStoredCredentials());
112    m_loader->loadAsynchronously(wrappedRequest, this);
113}
114
115void ResourceHandleInternal::cancel()
116{
117    m_state = ConnectionStateCanceled;
118    m_loader->cancel();
119
120    // Do not make any further calls to the client.
121    m_client = 0;
122}
123
124void ResourceHandleInternal::setDefersLoading(bool value)
125{
126    m_loader->setDefersLoading(value);
127}
128
129bool ResourceHandleInternal::allowStoredCredentials() const
130{
131    return m_client && m_client->shouldUseCredentialStorage(m_owner);
132}
133
134void ResourceHandleInternal::willSendRequest(
135    WebURLLoader*, WebURLRequest& request, const WebURLResponse& response)
136{
137    ASSERT(m_client);
138    ASSERT(!request.isNull());
139    ASSERT(!response.isNull());
140    m_client->willSendRequest(m_owner, request.toMutableResourceRequest(), response.toResourceResponse());
141}
142
143void ResourceHandleInternal::didSendData(
144    WebURLLoader*, unsigned long long bytesSent, unsigned long long totalBytesToBeSent)
145{
146    ASSERT(m_client);
147    m_client->didSendData(m_owner, bytesSent, totalBytesToBeSent);
148}
149
150void ResourceHandleInternal::didReceiveResponse(WebURLLoader*, const WebURLResponse& response)
151{
152    ASSERT(m_client);
153    ASSERT(!response.isNull());
154    bool isMultipart = response.isMultipartPayload();
155    bool isValidStateTransition = (m_state == ConnectionStateStarted || m_state == ConnectionStateReceivedResponse);
156    // In the case of multipart loads, calls to didReceiveData & didReceiveResponse can be interleaved.
157    if (!isMultipart && !isValidStateTransition)
158        CRASH();
159    m_state = ConnectionStateReceivedResponse;
160    m_client->didReceiveResponse(m_owner, response.toResourceResponse());
161}
162
163void ResourceHandleInternal::didReceiveData(WebURLLoader*, const char* data, int dataLength, int encodedDataLength)
164{
165    ASSERT(m_client);
166    if (m_state != ConnectionStateReceivedResponse && m_state != ConnectionStateReceivingData)
167        CRASH();
168    m_state = ConnectionStateReceivingData;
169
170    m_client->didReceiveData(m_owner, data, dataLength, encodedDataLength);
171}
172
173void ResourceHandleInternal::didReceiveCachedMetadata(WebURLLoader*, const char* data, int dataLength)
174{
175    ASSERT(m_client);
176    if (m_state != ConnectionStateReceivedResponse && m_state != ConnectionStateReceivingData)
177        CRASH();
178
179    m_client->didReceiveCachedMetadata(m_owner, data, dataLength);
180}
181
182void ResourceHandleInternal::didFinishLoading(WebURLLoader*, double finishTime)
183{
184    ASSERT(m_client);
185    if (m_state != ConnectionStateReceivedResponse && m_state != ConnectionStateReceivingData)
186        CRASH();
187    m_state = ConnectionStateFinishedLoading;
188    m_client->didFinishLoading(m_owner, finishTime);
189}
190
191void ResourceHandleInternal::didFail(WebURLLoader*, const WebURLError& error)
192{
193    ASSERT(m_client);
194    m_state = ConnectionStateFailed;
195    m_client->didFail(m_owner, error);
196}
197
198// ResourceHandle -------------------------------------------------------------
199
200ResourceHandle::ResourceHandle(const ResourceRequest& request,
201                               ResourceHandleClient* client,
202                               bool defersLoading,
203                               bool shouldContentSniff)
204    : d(new ResourceHandleInternal(request, client))
205{
206    d->m_owner = this;
207
208    // FIXME: Figure out what to do with the bool params.
209}
210
211PassRefPtr<ResourceHandle> ResourceHandle::create(NetworkingContext* context,
212                                                  const ResourceRequest& request,
213                                                  ResourceHandleClient* client,
214                                                  bool defersLoading,
215                                                  bool shouldContentSniff)
216{
217    RefPtr<ResourceHandle> newHandle = adoptRef(new ResourceHandle(
218        request, client, defersLoading, shouldContentSniff));
219
220    if (newHandle->start(context))
221        return newHandle.release();
222
223    return 0;
224}
225
226ResourceRequest& ResourceHandle::firstRequest()
227{
228    return d->m_request;
229}
230
231ResourceHandleClient* ResourceHandle::client() const
232{
233    return d->m_client;
234}
235
236void ResourceHandle::setClient(ResourceHandleClient* client)
237{
238    d->m_client = client;
239}
240
241void ResourceHandle::setDefersLoading(bool value)
242{
243    d->setDefersLoading(value);
244}
245
246bool ResourceHandle::start(NetworkingContext* context)
247{
248    d->start();
249    return true;
250}
251
252bool ResourceHandle::hasAuthenticationChallenge() const
253{
254    return false;
255}
256
257void ResourceHandle::clearAuthentication()
258{
259}
260
261void ResourceHandle::cancel()
262{
263    d->cancel();
264}
265
266ResourceHandle::~ResourceHandle()
267{
268    d->m_owner = 0;
269}
270
271PassRefPtr<SharedBuffer> ResourceHandle::bufferedData()
272{
273    return 0;
274}
275
276bool ResourceHandle::loadsBlocked()
277{
278    return false;  // This seems to be related to sync XMLHttpRequest...
279}
280
281// static
282bool ResourceHandle::supportsBufferedData()
283{
284    return false;  // The loader will buffer manually if it needs to.
285}
286
287// static
288void ResourceHandle::loadResourceSynchronously(NetworkingContext* context,
289                                               const ResourceRequest& request,
290                                               StoredCredentials storedCredentials,
291                                               ResourceError& error,
292                                               ResourceResponse& response,
293                                               Vector<char>& data)
294{
295    OwnPtr<WebURLLoader> loader(webKitClient()->createURLLoader());
296    ASSERT(loader.get());
297
298    WrappedResourceRequest requestIn(request);
299    requestIn.setAllowStoredCredentials(storedCredentials == AllowStoredCredentials);
300    WrappedResourceResponse responseOut(response);
301    WebURLError errorOut;
302    WebData dataOut;
303
304    loader->loadSynchronously(requestIn, responseOut, errorOut, dataOut);
305
306    error = errorOut;
307    data.clear();
308    data.append(dataOut.data(), dataOut.size());
309}
310
311// static
312bool ResourceHandle::willLoadFromCache(ResourceRequest& request, Frame*)
313{
314    // This method is used to determine if a POST request can be repeated from
315    // cache, but you cannot really know until you actually try to read from the
316    // cache.  Even if we checked now, something else could come along and wipe
317    // out the cache entry by the time we fetch it.
318    //
319    // So, we always say yes here, to prevent the FrameLoader from initiating a
320    // reload.  Then in FrameLoaderClientImpl::dispatchWillSendRequest, we
321    // fix-up the cache policy of the request to force a load from the cache.
322    //
323    ASSERT(request.httpMethod() == "POST");
324    return true;
325}
326
327// static
328void ResourceHandle::cacheMetadata(const ResourceResponse& response, const Vector<char>& data)
329{
330    PlatformBridge::cacheMetadata(response.url(), response.responseTime(), data);
331}
332
333} // namespace WebCore
334