Home | History | Annotate | Download | only in fetch
      1 /*
      2  * Copyright (C) 2006, 2007, 2010, 2011 Apple Inc. All rights reserved.
      3  *           (C) 2007 Graham Dennis (graham.dennis (at) gmail.com)
      4  *
      5  * Redistribution and use in source and binary forms, with or without
      6  * modification, are permitted provided that the following conditions
      7  * are met:
      8  *
      9  * 1.  Redistributions of source code must retain the above copyright
     10  *     notice, this list of conditions and the following disclaimer.
     11  * 2.  Redistributions in binary form must reproduce the above copyright
     12  *     notice, this list of conditions and the following disclaimer in the
     13  *     documentation and/or other materials provided with the distribution.
     14  * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
     15  *     its contributors may be used to endorse or promote products derived
     16  *     from this software without specific prior written permission.
     17  *
     18  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
     19  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
     20  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
     21  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
     22  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
     23  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
     24  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
     25  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     26  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     27  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     28  */
     29 
     30 #include "config.h"
     31 #include "core/fetch/ResourceLoader.h"
     32 
     33 #include "core/fetch/Resource.h"
     34 #include "core/fetch/ResourceLoaderHost.h"
     35 #include "core/fetch/ResourcePtr.h"
     36 #include "platform/Logging.h"
     37 #include "platform/SharedBuffer.h"
     38 #include "platform/exported/WrappedResourceRequest.h"
     39 #include "platform/exported/WrappedResourceResponse.h"
     40 #include "platform/network/ResourceError.h"
     41 #include "public/platform/Platform.h"
     42 #include "public/platform/WebData.h"
     43 #include "public/platform/WebURLError.h"
     44 #include "public/platform/WebURLRequest.h"
     45 #include "public/platform/WebURLResponse.h"
     46 #include "wtf/Assertions.h"
     47 #include "wtf/CurrentTime.h"
     48 
     49 namespace WebCore {
     50 
     51 ResourceLoader::RequestCountTracker::RequestCountTracker(ResourceLoaderHost* host, Resource* resource)
     52     : m_host(host)
     53     , m_resource(resource)
     54 {
     55     m_host->incrementRequestCount(m_resource);
     56 }
     57 
     58 ResourceLoader::RequestCountTracker::~RequestCountTracker()
     59 {
     60     m_host->decrementRequestCount(m_resource);
     61 }
     62 
     63 PassRefPtr<ResourceLoader> ResourceLoader::create(ResourceLoaderHost* host, Resource* resource, const ResourceRequest& request, const ResourceLoaderOptions& options)
     64 {
     65     RefPtr<ResourceLoader> loader(adoptRef(new ResourceLoader(host, resource, options)));
     66     loader->init(request);
     67     return loader.release();
     68 }
     69 
     70 ResourceLoader::ResourceLoader(ResourceLoaderHost* host, Resource* resource, const ResourceLoaderOptions& options)
     71     : m_host(host)
     72     , m_notifiedLoadComplete(false)
     73     , m_defersLoading(host->defersLoading())
     74     , m_options(options)
     75     , m_resource(resource)
     76     , m_state(Initialized)
     77     , m_connectionState(ConnectionStateNew)
     78     , m_requestCountTracker(adoptPtr(new RequestCountTracker(host, resource)))
     79 {
     80 }
     81 
     82 ResourceLoader::~ResourceLoader()
     83 {
     84     ASSERT(m_state == Terminated);
     85 }
     86 
     87 void ResourceLoader::releaseResources()
     88 {
     89     ASSERT(m_state != Terminated);
     90     m_requestCountTracker.clear();
     91     m_host->didLoadResource(m_resource);
     92     if (m_state == Terminated)
     93         return;
     94     m_resource->clearLoader();
     95     m_host->willTerminateResourceLoader(this);
     96 
     97     ASSERT(m_state != Terminated);
     98 
     99     // It's possible that when we release the loader, it will be
    100     // deallocated and release the last reference to this object.
    101     // We need to retain to avoid accessing the object after it
    102     // has been deallocated and also to avoid reentering this method.
    103     RefPtr<ResourceLoader> protector(this);
    104 
    105     m_host.clear();
    106     m_state = Terminated;
    107 
    108     if (m_loader) {
    109         m_loader->cancel();
    110         m_loader.clear();
    111     }
    112 
    113     m_deferredRequest = ResourceRequest();
    114 }
    115 
    116 void ResourceLoader::init(const ResourceRequest& passedRequest)
    117 {
    118     ResourceRequest request(passedRequest);
    119     m_host->willSendRequest(m_resource->identifier(), request, ResourceResponse(), m_options);
    120     request.setReportLoadTiming(true);
    121     ASSERT(m_state != Terminated);
    122     ASSERT(!request.isNull());
    123     m_originalRequest = m_request = request;
    124     m_host->didInitializeResourceLoader(this);
    125 }
    126 
    127 void ResourceLoader::start()
    128 {
    129     ASSERT(!m_loader);
    130     ASSERT(!m_request.isNull());
    131     ASSERT(m_deferredRequest.isNull());
    132 
    133     m_host->willStartLoadingResource(m_request);
    134 
    135     if (m_options.synchronousPolicy == RequestSynchronously) {
    136         requestSynchronously();
    137         return;
    138     }
    139 
    140     if (m_defersLoading) {
    141         m_deferredRequest = m_request;
    142         return;
    143     }
    144 
    145     if (m_state == Terminated)
    146         return;
    147 
    148     RELEASE_ASSERT(m_connectionState == ConnectionStateNew);
    149     m_connectionState = ConnectionStateStarted;
    150 
    151     m_loader = adoptPtr(blink::Platform::current()->createURLLoader());
    152     ASSERT(m_loader);
    153     blink::WrappedResourceRequest wrappedRequest(m_request);
    154     wrappedRequest.setAllowStoredCredentials(m_options.allowCredentials == AllowStoredCredentials);
    155     m_loader->loadAsynchronously(wrappedRequest, this);
    156 }
    157 
    158 void ResourceLoader::changeToSynchronous()
    159 {
    160     ASSERT(m_options.synchronousPolicy == RequestAsynchronously);
    161     ASSERT(m_loader);
    162     m_loader->cancel();
    163     m_loader.clear();
    164     m_request.setPriority(ResourceLoadPriorityHighest);
    165     m_connectionState = ConnectionStateNew;
    166     requestSynchronously();
    167 }
    168 
    169 void ResourceLoader::setDefersLoading(bool defers)
    170 {
    171     m_defersLoading = defers;
    172     if (m_loader)
    173         m_loader->setDefersLoading(defers);
    174     if (!defers && !m_deferredRequest.isNull()) {
    175         m_request = m_deferredRequest;
    176         m_deferredRequest = ResourceRequest();
    177         start();
    178     }
    179 }
    180 
    181 void ResourceLoader::didDownloadData(blink::WebURLLoader*, int length, int encodedDataLength)
    182 {
    183     RefPtr<ResourceLoader> protect(this);
    184     RELEASE_ASSERT(m_connectionState == ConnectionStateReceivedResponse);
    185     m_host->didDownloadData(m_resource, length, encodedDataLength, m_options);
    186     m_resource->didDownloadData(length);
    187 }
    188 
    189 void ResourceLoader::didFinishLoadingOnePart(double finishTime)
    190 {
    191     // If load has been cancelled after finishing (which could happen with a
    192     // JavaScript that changes the window location), do nothing.
    193     if (m_state == Terminated)
    194         return;
    195 
    196     if (m_notifiedLoadComplete)
    197         return;
    198     m_notifiedLoadComplete = true;
    199     m_host->didFinishLoading(m_resource, finishTime, m_options);
    200 }
    201 
    202 void ResourceLoader::didChangePriority(ResourceLoadPriority loadPriority)
    203 {
    204     if (m_loader) {
    205         m_host->didChangeLoadingPriority(m_resource, loadPriority);
    206         m_loader->didChangePriority(static_cast<blink::WebURLRequest::Priority>(loadPriority));
    207     }
    208 }
    209 
    210 void ResourceLoader::cancelIfNotFinishing()
    211 {
    212     if (m_state != Initialized)
    213         return;
    214     cancel();
    215 }
    216 
    217 void ResourceLoader::cancel()
    218 {
    219     cancel(ResourceError());
    220 }
    221 
    222 void ResourceLoader::cancel(const ResourceError& error)
    223 {
    224     // If the load has already completed - succeeded, failed, or previously cancelled - do nothing.
    225     if (m_state == Terminated)
    226         return;
    227     if (m_state == Finishing) {
    228         releaseResources();
    229         return;
    230     }
    231 
    232     ResourceError nonNullError = error.isNull() ? ResourceError::cancelledError(m_request.url()) : error;
    233 
    234     // This function calls out to clients at several points that might do
    235     // something that causes the last reference to this object to go away.
    236     RefPtr<ResourceLoader> protector(this);
    237 
    238     WTF_LOG(ResourceLoading, "Cancelled load of '%s'.\n", m_resource->url().string().latin1().data());
    239     if (m_state == Initialized)
    240         m_state = Finishing;
    241     m_resource->setResourceError(nonNullError);
    242 
    243     if (m_loader) {
    244         m_connectionState = ConnectionStateCanceled;
    245         m_loader->cancel();
    246         m_loader.clear();
    247     }
    248 
    249     m_host->didFailLoading(m_resource, nonNullError, m_options);
    250 
    251     if (m_state == Finishing)
    252         m_resource->error(Resource::LoadError);
    253     if (m_state != Terminated)
    254         releaseResources();
    255 }
    256 
    257 void ResourceLoader::willSendRequest(blink::WebURLLoader*, blink::WebURLRequest& passedRequest, const blink::WebURLResponse& passedRedirectResponse)
    258 {
    259     RefPtr<ResourceLoader> protect(this);
    260 
    261     ResourceRequest& request(passedRequest.toMutableResourceRequest());
    262     ASSERT(!request.isNull());
    263     const ResourceResponse& redirectResponse(passedRedirectResponse.toResourceResponse());
    264     ASSERT(!redirectResponse.isNull());
    265     if (!m_host->shouldRequest(m_resource, request, m_options)) {
    266         cancel();
    267         return;
    268     }
    269     m_host->redirectReceived(m_resource, redirectResponse);
    270     m_resource->willSendRequest(request, redirectResponse);
    271     if (request.isNull() || m_state == Terminated)
    272         return;
    273 
    274     m_host->willSendRequest(m_resource->identifier(), request, redirectResponse, m_options);
    275     request.setReportLoadTiming(true);
    276     ASSERT(!request.isNull());
    277     m_request = request;
    278 }
    279 
    280 void ResourceLoader::didReceiveCachedMetadata(blink::WebURLLoader*, const char* data, int length)
    281 {
    282     RELEASE_ASSERT(m_connectionState == ConnectionStateReceivedResponse || m_connectionState == ConnectionStateReceivingData);
    283     ASSERT(m_state == Initialized);
    284     m_resource->setSerializedCachedMetadata(data, length);
    285 }
    286 
    287 void ResourceLoader::didSendData(blink::WebURLLoader*, unsigned long long bytesSent, unsigned long long totalBytesToBeSent)
    288 {
    289     ASSERT(m_state == Initialized);
    290     RefPtr<ResourceLoader> protect(this);
    291     m_resource->didSendData(bytesSent, totalBytesToBeSent);
    292 }
    293 
    294 void ResourceLoader::didReceiveResponse(blink::WebURLLoader*, const blink::WebURLResponse& response)
    295 {
    296     ASSERT(!response.isNull());
    297     ASSERT(m_state == Initialized);
    298 
    299     bool isMultipartPayload = response.isMultipartPayload();
    300     bool isValidStateTransition = (m_connectionState == ConnectionStateStarted || m_connectionState == ConnectionStateReceivedResponse);
    301     // In the case of multipart loads, calls to didReceiveData & didReceiveResponse can be interleaved.
    302     RELEASE_ASSERT(isMultipartPayload || isValidStateTransition);
    303     m_connectionState = ConnectionStateReceivedResponse;
    304 
    305     // Reference the object in this method since the additional processing can do
    306     // anything including removing the last reference to this object.
    307     RefPtr<ResourceLoader> protect(this);
    308     m_resource->responseReceived(response.toResourceResponse());
    309     if (m_state == Terminated)
    310         return;
    311 
    312     m_host->didReceiveResponse(m_resource, response.toResourceResponse(), m_options);
    313 
    314     if (response.toResourceResponse().isMultipart()) {
    315         // We don't count multiParts in a ResourceFetcher's request count
    316         m_requestCountTracker.clear();
    317         if (!m_resource->isImage()) {
    318             cancel();
    319             return;
    320         }
    321     } else if (isMultipartPayload) {
    322         // Since a subresource loader does not load multipart sections progressively, data was delivered to the loader all at once.
    323         // After the first multipart section is complete, signal to delegates that this load is "finished"
    324         m_host->subresourceLoaderFinishedLoadingOnePart(this);
    325         didFinishLoadingOnePart(0);
    326     }
    327 
    328     if (m_resource->response().httpStatusCode() < 400 || m_resource->shouldIgnoreHTTPStatusCodeErrors())
    329         return;
    330     m_state = Finishing;
    331     m_resource->error(Resource::LoadError);
    332     cancel();
    333 }
    334 
    335 void ResourceLoader::didReceiveData(blink::WebURLLoader*, const char* data, int length, int encodedDataLength)
    336 {
    337     RELEASE_ASSERT(m_connectionState == ConnectionStateReceivedResponse || m_connectionState == ConnectionStateReceivingData);
    338     m_connectionState = ConnectionStateReceivingData;
    339 
    340     // It is possible to receive data on uninitialized resources if it had an error status code, and we are running a nested message
    341     // loop. When this occurs, ignoring the data is the correct action.
    342     if (m_resource->response().httpStatusCode() >= 400 && !m_resource->shouldIgnoreHTTPStatusCodeErrors())
    343         return;
    344     ASSERT(m_state == Initialized);
    345 
    346     // Reference the object in this method since the additional processing can do
    347     // anything including removing the last reference to this object.
    348     RefPtr<ResourceLoader> protect(this);
    349 
    350     // FIXME: If we get a resource with more than 2B bytes, this code won't do the right thing.
    351     // However, with today's computers and networking speeds, this won't happen in practice.
    352     // Could be an issue with a giant local file.
    353     m_host->didReceiveData(m_resource, data, length, encodedDataLength, m_options);
    354     m_resource->appendData(data, length);
    355 }
    356 
    357 void ResourceLoader::didFinishLoading(blink::WebURLLoader*, double finishTime)
    358 {
    359     RELEASE_ASSERT(m_connectionState == ConnectionStateReceivedResponse || m_connectionState == ConnectionStateReceivingData);
    360     m_connectionState = ConnectionStateFinishedLoading;
    361     if (m_state != Initialized)
    362         return;
    363     ASSERT(m_state != Terminated);
    364     WTF_LOG(ResourceLoading, "Received '%s'.", m_resource->url().string().latin1().data());
    365 
    366     RefPtr<ResourceLoader> protect(this);
    367     ResourcePtr<Resource> protectResource(m_resource);
    368     m_state = Finishing;
    369     m_resource->finish(finishTime);
    370     didFinishLoadingOnePart(finishTime);
    371 
    372     // If the load has been cancelled by a delegate in response to didFinishLoad(), do not release
    373     // the resources a second time, they have been released by cancel.
    374     if (m_state == Terminated)
    375         return;
    376     releaseResources();
    377 }
    378 
    379 void ResourceLoader::didFail(blink::WebURLLoader*, const blink::WebURLError& error)
    380 {
    381     m_connectionState = ConnectionStateFailed;
    382     ASSERT(m_state != Terminated);
    383     WTF_LOG(ResourceLoading, "Failed to load '%s'.\n", m_resource->url().string().latin1().data());
    384 
    385     RefPtr<ResourceLoader> protect(this);
    386     RefPtr<ResourceLoaderHost> protectHost(m_host);
    387     ResourcePtr<Resource> protectResource(m_resource);
    388     m_state = Finishing;
    389     m_resource->setResourceError(error);
    390     m_resource->error(Resource::LoadError);
    391 
    392     if (m_state == Terminated)
    393         return;
    394 
    395     if (!m_notifiedLoadComplete) {
    396         m_notifiedLoadComplete = true;
    397         m_host->didFailLoading(m_resource, error, m_options);
    398     }
    399 
    400     releaseResources();
    401 }
    402 
    403 bool ResourceLoader::isLoadedBy(ResourceLoaderHost* loader) const
    404 {
    405     return m_host->isLoadedBy(loader);
    406 }
    407 
    408 void ResourceLoader::requestSynchronously()
    409 {
    410     OwnPtr<blink::WebURLLoader> loader = adoptPtr(blink::Platform::current()->createURLLoader());
    411     ASSERT(loader);
    412 
    413     RefPtr<ResourceLoader> protect(this);
    414     RefPtr<ResourceLoaderHost> protectHost(m_host);
    415     ResourcePtr<Resource> protectResource(m_resource);
    416 
    417     RELEASE_ASSERT(m_connectionState == ConnectionStateNew);
    418     m_connectionState = ConnectionStateStarted;
    419 
    420     blink::WrappedResourceRequest requestIn(m_request);
    421     requestIn.setAllowStoredCredentials(m_options.allowCredentials == AllowStoredCredentials);
    422     blink::WebURLResponse responseOut;
    423     responseOut.initialize();
    424     blink::WebURLError errorOut;
    425     blink::WebData dataOut;
    426     loader->loadSynchronously(requestIn, responseOut, errorOut, dataOut);
    427     if (errorOut.reason) {
    428         didFail(0, errorOut);
    429         return;
    430     }
    431     didReceiveResponse(0, responseOut);
    432     if (m_state == Terminated)
    433         return;
    434     RefPtr<ResourceLoadInfo> resourceLoadInfo = responseOut.toResourceResponse().resourceLoadInfo();
    435     m_host->didReceiveData(m_resource, dataOut.data(), dataOut.size(), resourceLoadInfo ? resourceLoadInfo->encodedDataLength : -1, m_options);
    436     m_resource->setResourceBuffer(dataOut);
    437     didFinishLoading(0, monotonicallyIncreasingTime());
    438 }
    439 
    440 }
    441