1/*
2 * Copyright (C) 2006, 2007, 2009 Apple 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
6 * are met:
7 *
8 * 1.  Redistributions of source code must retain the above copyright
9 *     notice, this list of conditions and the following disclaimer.
10 * 2.  Redistributions in binary form must reproduce the above copyright
11 *     notice, this list of conditions and the following disclaimer in the
12 *     documentation and/or other materials provided with the distribution.
13 * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
14 *     its contributors may be used to endorse or promote products derived
15 *     from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
18 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
21 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29#include "config.h"
30#include "SubresourceLoader.h"
31
32#include "CachedResourceLoader.h"
33#include "Document.h"
34#include "DocumentLoader.h"
35#include "Frame.h"
36#include "FrameLoader.h"
37#include "Logging.h"
38#include "MemoryCache.h"
39#include "Page.h"
40#include "PageActivityAssertionToken.h"
41#include "ResourceBuffer.h"
42#include <wtf/RefCountedLeakCounter.h>
43#include <wtf/StdLibExtras.h>
44#include <wtf/text/CString.h>
45
46namespace WebCore {
47
48DEFINE_DEBUG_ONLY_GLOBAL(WTF::RefCountedLeakCounter, subresourceLoaderCounter, ("SubresourceLoader"));
49
50SubresourceLoader::RequestCountTracker::RequestCountTracker(CachedResourceLoader* cachedResourceLoader, CachedResource* resource)
51    : m_cachedResourceLoader(cachedResourceLoader)
52    , m_resource(resource)
53{
54    m_cachedResourceLoader->incrementRequestCount(m_resource);
55}
56
57SubresourceLoader::RequestCountTracker::~RequestCountTracker()
58{
59    m_cachedResourceLoader->decrementRequestCount(m_resource);
60}
61
62SubresourceLoader::SubresourceLoader(Frame* frame, CachedResource* resource, const ResourceLoaderOptions& options)
63    : ResourceLoader(frame, options)
64    , m_resource(resource)
65    , m_loadingMultipartContent(false)
66    , m_state(Uninitialized)
67    , m_requestCountTracker(adoptPtr(new RequestCountTracker(frame->document()->cachedResourceLoader(), resource)))
68{
69#ifndef NDEBUG
70    subresourceLoaderCounter.increment();
71#endif
72}
73
74SubresourceLoader::~SubresourceLoader()
75{
76    ASSERT(m_state != Initialized);
77    ASSERT(reachedTerminalState());
78#ifndef NDEBUG
79    subresourceLoaderCounter.decrement();
80#endif
81}
82
83PassRefPtr<SubresourceLoader> SubresourceLoader::create(Frame* frame, CachedResource* resource, const ResourceRequest& request, const ResourceLoaderOptions& options)
84{
85    RefPtr<SubresourceLoader> subloader(adoptRef(new SubresourceLoader(frame, resource, options)));
86    if (!subloader->init(request))
87        return 0;
88    return subloader.release();
89}
90
91CachedResource* SubresourceLoader::cachedResource()
92{
93    return m_resource;
94}
95
96void SubresourceLoader::cancelIfNotFinishing()
97{
98    if (m_state != Initialized)
99        return;
100
101    ResourceLoader::cancel();
102}
103
104bool SubresourceLoader::init(const ResourceRequest& request)
105{
106    if (!ResourceLoader::init(request))
107        return false;
108
109    ASSERT(!reachedTerminalState());
110    m_state = Initialized;
111    if (m_frame && m_frame->page() && !m_activityAssertion)
112        m_activityAssertion = m_frame->page()->createActivityToken();
113    m_documentLoader->addSubresourceLoader(this);
114    return true;
115}
116
117bool SubresourceLoader::isSubresourceLoader()
118{
119    return true;
120}
121
122void SubresourceLoader::willSendRequest(ResourceRequest& newRequest, const ResourceResponse& redirectResponse)
123{
124    // Store the previous URL because the call to ResourceLoader::willSendRequest will modify it.
125    KURL previousURL = request().url();
126    RefPtr<SubresourceLoader> protect(this);
127
128    ASSERT(!newRequest.isNull());
129    if (!redirectResponse.isNull()) {
130        // CachedResources are keyed off their original request URL.
131        // Requesting the same original URL a second time can redirect to a unique second resource.
132        // Therefore, if a redirect to a different destination URL occurs, we should no longer consider this a revalidation of the first resource.
133        // Doing so would have us reusing the resource from the first request if the second request's revalidation succeeds.
134        if (newRequest.isConditional() && m_resource->resourceToRevalidate() && newRequest.url() != m_resource->resourceToRevalidate()->response().url()) {
135            newRequest.makeUnconditional();
136            memoryCache()->revalidationFailed(m_resource);
137        }
138
139        if (!m_documentLoader->cachedResourceLoader()->canRequest(m_resource->type(), newRequest.url())) {
140            cancel();
141            return;
142        }
143        if (m_resource->type() == CachedResource::ImageResource && m_documentLoader->cachedResourceLoader()->shouldDeferImageLoad(newRequest.url())) {
144            cancel();
145            return;
146        }
147        m_resource->willSendRequest(newRequest, redirectResponse);
148    }
149
150    if (newRequest.isNull() || reachedTerminalState())
151        return;
152
153    ResourceLoader::willSendRequest(newRequest, redirectResponse);
154    if (newRequest.isNull())
155        cancel();
156}
157
158void SubresourceLoader::didSendData(unsigned long long bytesSent, unsigned long long totalBytesToBeSent)
159{
160    ASSERT(m_state == Initialized);
161    RefPtr<SubresourceLoader> protect(this);
162    m_resource->didSendData(bytesSent, totalBytesToBeSent);
163}
164
165void SubresourceLoader::didReceiveResponse(const ResourceResponse& response)
166{
167    ASSERT(!response.isNull());
168    ASSERT(m_state == Initialized);
169
170    // Reference the object in this method since the additional processing can do
171    // anything including removing the last reference to this object; one example of this is 3266216.
172    RefPtr<SubresourceLoader> protect(this);
173
174    if (m_resource->resourceToRevalidate()) {
175        if (response.httpStatusCode() == 304) {
176            // 304 Not modified / Use local copy
177            // Existing resource is ok, just use it updating the expiration time.
178            m_resource->setResponse(response);
179            memoryCache()->revalidationSucceeded(m_resource, response);
180            if (!reachedTerminalState())
181                ResourceLoader::didReceiveResponse(response);
182            return;
183        }
184        // Did not get 304 response, continue as a regular resource load.
185        memoryCache()->revalidationFailed(m_resource);
186    }
187
188    m_resource->responseReceived(response);
189    if (reachedTerminalState())
190        return;
191
192    ResourceLoader::didReceiveResponse(response);
193    if (reachedTerminalState())
194        return;
195
196    // FIXME: Main resources have a different set of rules for multipart than images do.
197    // Hopefully we can merge those 2 paths.
198    if (response.isMultipart() && m_resource->type() != CachedResource::MainResource) {
199        m_loadingMultipartContent = true;
200
201        // We don't count multiParts in a CachedResourceLoader's request count
202        m_requestCountTracker.clear();
203        if (!m_resource->isImage()) {
204            cancel();
205            return;
206        }
207    }
208
209    RefPtr<ResourceBuffer> buffer = resourceData();
210    if (m_loadingMultipartContent && buffer && buffer->size()) {
211        // The resource data will change as the next part is loaded, so we need to make a copy.
212        RefPtr<ResourceBuffer> copiedData = ResourceBuffer::create(buffer->data(), buffer->size());
213        m_resource->finishLoading(copiedData.get());
214        clearResourceData();
215        // Since a subresource loader does not load multipart sections progressively, data was delivered to the loader all at once.
216        // After the first multipart section is complete, signal to delegates that this load is "finished"
217        m_documentLoader->subresourceLoaderFinishedLoadingOnePart(this);
218        didFinishLoadingOnePart(0);
219    }
220
221    checkForHTTPStatusCodeError();
222}
223
224void SubresourceLoader::didReceiveData(const char* data, int length, long long encodedDataLength, DataPayloadType dataPayloadType)
225{
226    didReceiveDataOrBuffer(data, length, 0, encodedDataLength, dataPayloadType);
227}
228
229void SubresourceLoader::didReceiveBuffer(PassRefPtr<SharedBuffer> buffer, long long encodedDataLength, DataPayloadType dataPayloadType)
230{
231    didReceiveDataOrBuffer(0, 0, buffer, encodedDataLength, dataPayloadType);
232}
233
234void SubresourceLoader::didReceiveDataOrBuffer(const char* data, int length, PassRefPtr<SharedBuffer> prpBuffer, long long encodedDataLength, DataPayloadType dataPayloadType)
235{
236    if (m_resource->response().httpStatusCode() >= 400 && !m_resource->shouldIgnoreHTTPStatusCodeErrors())
237        return;
238    ASSERT(!m_resource->resourceToRevalidate());
239    ASSERT(!m_resource->errorOccurred());
240    ASSERT(m_state == Initialized);
241    // Reference the object in this method since the additional processing can do
242    // anything including removing the last reference to this object; one example of this is 3266216.
243    RefPtr<SubresourceLoader> protect(this);
244    RefPtr<SharedBuffer> buffer = prpBuffer;
245
246    ResourceLoader::didReceiveDataOrBuffer(data, length, buffer, encodedDataLength, dataPayloadType);
247
248    if (!m_loadingMultipartContent) {
249        if (ResourceBuffer* resourceData = this->resourceData())
250            m_resource->addDataBuffer(resourceData);
251        else
252            m_resource->addData(buffer ? buffer->data() : data, buffer ? buffer->size() : length);
253    }
254}
255
256bool SubresourceLoader::checkForHTTPStatusCodeError()
257{
258    if (m_resource->response().httpStatusCode() < 400 || m_resource->shouldIgnoreHTTPStatusCodeErrors())
259        return false;
260
261    m_state = Finishing;
262    m_activityAssertion.clear();
263    m_resource->error(CachedResource::LoadError);
264    cancel();
265    return true;
266}
267
268void SubresourceLoader::didFinishLoading(double finishTime)
269{
270    if (m_state != Initialized)
271        return;
272    ASSERT(!reachedTerminalState());
273    ASSERT(!m_resource->resourceToRevalidate());
274    ASSERT(!m_resource->errorOccurred());
275    LOG(ResourceLoading, "Received '%s'.", m_resource->url().string().latin1().data());
276
277    RefPtr<SubresourceLoader> protect(this);
278    CachedResourceHandle<CachedResource> protectResource(m_resource);
279    m_state = Finishing;
280    m_activityAssertion.clear();
281    m_resource->setLoadFinishTime(finishTime);
282    m_resource->finishLoading(resourceData());
283
284    if (wasCancelled())
285        return;
286    m_resource->finish();
287    ASSERT(!reachedTerminalState());
288    didFinishLoadingOnePart(finishTime);
289    notifyDone();
290    if (reachedTerminalState())
291        return;
292    releaseResources();
293}
294
295void SubresourceLoader::didFail(const ResourceError& error)
296{
297    if (m_state != Initialized)
298        return;
299    ASSERT(!reachedTerminalState());
300    LOG(ResourceLoading, "Failed to load '%s'.\n", m_resource->url().string().latin1().data());
301
302    RefPtr<SubresourceLoader> protect(this);
303    CachedResourceHandle<CachedResource> protectResource(m_resource);
304    m_state = Finishing;
305    m_activityAssertion.clear();
306    if (m_resource->resourceToRevalidate())
307        memoryCache()->revalidationFailed(m_resource);
308    m_resource->setResourceError(error);
309    if (!m_resource->isPreloaded())
310        memoryCache()->remove(m_resource);
311    m_resource->error(CachedResource::LoadError);
312    cleanupForError(error);
313    notifyDone();
314    if (reachedTerminalState())
315        return;
316    releaseResources();
317}
318
319void SubresourceLoader::willCancel(const ResourceError& error)
320{
321    if (m_state != Initialized)
322        return;
323    ASSERT(!reachedTerminalState());
324    LOG(ResourceLoading, "Cancelled load of '%s'.\n", m_resource->url().string().latin1().data());
325
326    RefPtr<SubresourceLoader> protect(this);
327    m_state = Finishing;
328    m_activityAssertion.clear();
329    if (m_resource->resourceToRevalidate())
330        memoryCache()->revalidationFailed(m_resource);
331    m_resource->setResourceError(error);
332    memoryCache()->remove(m_resource);
333}
334
335void SubresourceLoader::didCancel(const ResourceError&)
336{
337    if (m_state == Uninitialized)
338        return;
339
340    m_resource->cancelLoad();
341    notifyDone();
342}
343
344void SubresourceLoader::notifyDone()
345{
346    if (reachedTerminalState())
347        return;
348
349    m_requestCountTracker.clear();
350    m_documentLoader->cachedResourceLoader()->loadDone(m_resource);
351    if (reachedTerminalState())
352        return;
353    m_documentLoader->removeSubresourceLoader(this);
354}
355
356void SubresourceLoader::releaseResources()
357{
358    ASSERT(!reachedTerminalState());
359    if (m_state != Uninitialized)
360        m_resource->clearLoader();
361    m_resource = 0;
362    ResourceLoader::releaseResources();
363}
364
365}
366