Home | History | Annotate | Download | only in cache
      1 /*
      2     Copyright (C) 1998 Lars Knoll (knoll (at) mpi-hd.mpg.de)
      3     Copyright (C) 2001 Dirk Mueller (mueller (at) kde.org)
      4     Copyright (C) 2002 Waldo Bastian (bastian (at) kde.org)
      5     Copyright (C) 2006 Samuel Weinig (sam.weinig (at) gmail.com)
      6     Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
      7 
      8     This library is free software; you can redistribute it and/or
      9     modify it under the terms of the GNU Library General Public
     10     License as published by the Free Software Foundation; either
     11     version 2 of the License, or (at your option) any later version.
     12 
     13     This library is distributed in the hope that it will be useful,
     14     but WITHOUT ANY WARRANTY; without even the implied warranty of
     15     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     16     Library General Public License for more details.
     17 
     18     You should have received a copy of the GNU Library General Public License
     19     along with this library; see the file COPYING.LIB.  If not, write to
     20     the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     21     Boston, MA 02110-1301, USA.
     22 */
     23 
     24 #include "config.h"
     25 #include "CachedResourceRequest.h"
     26 
     27 #include "MemoryCache.h"
     28 #include "CachedImage.h"
     29 #include "CachedResource.h"
     30 #include "CachedResourceLoader.h"
     31 #include "Frame.h"
     32 #include "FrameLoader.h"
     33 #include "Logging.h"
     34 #include "ResourceHandle.h"
     35 #include "ResourceLoadScheduler.h"
     36 #include "ResourceRequest.h"
     37 #include "ResourceResponse.h"
     38 #include "SharedBuffer.h"
     39 #include <wtf/Assertions.h>
     40 #include <wtf/Vector.h>
     41 #include <wtf/text/CString.h>
     42 
     43 namespace WebCore {
     44 
     45 static ResourceRequest::TargetType cachedResourceTypeToTargetType(CachedResource::Type type, ResourceLoadPriority priority)
     46 {
     47 #if !ENABLE(LINK_PREFETCH)
     48     UNUSED_PARAM(priority);
     49 #endif
     50     switch (type) {
     51     case CachedResource::CSSStyleSheet:
     52 #if ENABLE(XSLT)
     53     case CachedResource::XSLStyleSheet:
     54 #endif
     55         return ResourceRequest::TargetIsStyleSheet;
     56     case CachedResource::Script:
     57         return ResourceRequest::TargetIsScript;
     58     case CachedResource::FontResource:
     59         return ResourceRequest::TargetIsFontResource;
     60     case CachedResource::ImageResource:
     61         return ResourceRequest::TargetIsImage;
     62 #if ENABLE(LINK_PREFETCH)
     63     case CachedResource::LinkResource:
     64         if (priority == ResourceLoadPriorityLowest)
     65             return ResourceRequest::TargetIsPrefetch;
     66         return ResourceRequest::TargetIsSubresource;
     67 #endif
     68     }
     69     ASSERT_NOT_REACHED();
     70     return ResourceRequest::TargetIsSubresource;
     71 }
     72 
     73 CachedResourceRequest::CachedResourceRequest(CachedResourceLoader* cachedResourceLoader, CachedResource* resource, bool incremental)
     74     : m_cachedResourceLoader(cachedResourceLoader)
     75     , m_resource(resource)
     76     , m_incremental(incremental)
     77     , m_multipart(false)
     78     , m_finishing(false)
     79 {
     80     m_resource->setRequest(this);
     81 }
     82 
     83 CachedResourceRequest::~CachedResourceRequest()
     84 {
     85     m_resource->setRequest(0);
     86 }
     87 
     88 PassRefPtr<CachedResourceRequest> CachedResourceRequest::load(CachedResourceLoader* cachedResourceLoader, CachedResource* resource, bool incremental, SecurityCheckPolicy securityCheck, bool sendResourceLoadCallbacks)
     89 {
     90     RefPtr<CachedResourceRequest> request = adoptRef(new CachedResourceRequest(cachedResourceLoader, resource, incremental));
     91 
     92     ResourceRequest resourceRequest(resource->url());
     93     resourceRequest.setTargetType(cachedResourceTypeToTargetType(resource->type(), resource->loadPriority()));
     94 
     95     if (!resource->accept().isEmpty())
     96         resourceRequest.setHTTPAccept(resource->accept());
     97 
     98     if (resource->isCacheValidator()) {
     99         CachedResource* resourceToRevalidate = resource->resourceToRevalidate();
    100         ASSERT(resourceToRevalidate->canUseCacheValidator());
    101         ASSERT(resourceToRevalidate->isLoaded());
    102         const String& lastModified = resourceToRevalidate->response().httpHeaderField("Last-Modified");
    103         const String& eTag = resourceToRevalidate->response().httpHeaderField("ETag");
    104         if (!lastModified.isEmpty() || !eTag.isEmpty()) {
    105             ASSERT(cachedResourceLoader->cachePolicy() != CachePolicyReload);
    106             if (cachedResourceLoader->cachePolicy() == CachePolicyRevalidate)
    107                 resourceRequest.setHTTPHeaderField("Cache-Control", "max-age=0");
    108             if (!lastModified.isEmpty())
    109                 resourceRequest.setHTTPHeaderField("If-Modified-Since", lastModified);
    110             if (!eTag.isEmpty())
    111                 resourceRequest.setHTTPHeaderField("If-None-Match", eTag);
    112         }
    113     }
    114 
    115 #if ENABLE(LINK_PREFETCH)
    116     if (resource->type() == CachedResource::LinkResource)
    117         resourceRequest.setHTTPHeaderField("Purpose", "prefetch");
    118 #endif
    119 
    120     ResourceLoadPriority priority = resource->loadPriority();
    121     resourceRequest.setPriority(priority);
    122 
    123     RefPtr<SubresourceLoader> loader = resourceLoadScheduler()->scheduleSubresourceLoad(cachedResourceLoader->document()->frame(),
    124         request.get(), resourceRequest, priority, securityCheck, sendResourceLoadCallbacks);
    125     if (!loader || loader->reachedTerminalState()) {
    126         // FIXME: What if resources in other frames were waiting for this revalidation?
    127         LOG(ResourceLoading, "Cannot start loading '%s'", resource->url().latin1().data());
    128         cachedResourceLoader->decrementRequestCount(resource);
    129         cachedResourceLoader->loadFinishing();
    130         if (resource->resourceToRevalidate())
    131             memoryCache()->revalidationFailed(resource);
    132         resource->error(CachedResource::LoadError);
    133         cachedResourceLoader->loadDone(0);
    134         return 0;
    135     }
    136     request->m_loader = loader;
    137     return request.release();
    138 }
    139 
    140 void CachedResourceRequest::willSendRequest(SubresourceLoader*, ResourceRequest&, const ResourceResponse&)
    141 {
    142     m_resource->setRequestedFromNetworkingLayer();
    143 }
    144 
    145 void CachedResourceRequest::didFinishLoading(SubresourceLoader* loader, double)
    146 {
    147     if (m_finishing)
    148         return;
    149 
    150     ASSERT(loader == m_loader.get());
    151     ASSERT(!m_resource->resourceToRevalidate());
    152     LOG(ResourceLoading, "Received '%s'.", m_resource->url().latin1().data());
    153 
    154     // Prevent the document from being destroyed before we are done with
    155     // the cachedResourceLoader that it will delete when the document gets deleted.
    156     RefPtr<Document> protector(m_cachedResourceLoader->document());
    157     if (!m_multipart)
    158         m_cachedResourceLoader->decrementRequestCount(m_resource);
    159     m_finishing = true;
    160 
    161     // If we got a 4xx response, we're pretending to have received a network
    162     // error, so we can't send the successful data() and finish() callbacks.
    163     if (!m_resource->errorOccurred()) {
    164         m_cachedResourceLoader->loadFinishing();
    165         m_resource->data(loader->resourceData(), true);
    166         if (!m_resource->errorOccurred())
    167             m_resource->finish();
    168     }
    169     m_cachedResourceLoader->loadDone(this);
    170 }
    171 
    172 void CachedResourceRequest::didFail(SubresourceLoader*, const ResourceError&)
    173 {
    174     if (!m_loader)
    175         return;
    176     didFail();
    177 }
    178 
    179 void CachedResourceRequest::didFail(bool cancelled)
    180 {
    181     if (m_finishing)
    182         return;
    183 
    184     LOG(ResourceLoading, "Failed to load '%s' (cancelled=%d).\n", m_resource->url().latin1().data(), cancelled);
    185 
    186     // Prevent the document from being destroyed before we are done with
    187     // the cachedResourceLoader that it will delete when the document gets deleted.
    188     RefPtr<Document> protector(m_cachedResourceLoader->document());
    189     if (!m_multipart)
    190         m_cachedResourceLoader->decrementRequestCount(m_resource);
    191     m_finishing = true;
    192     m_loader->clearClient();
    193 
    194     if (m_resource->resourceToRevalidate())
    195         memoryCache()->revalidationFailed(m_resource);
    196 
    197     if (!cancelled) {
    198         m_cachedResourceLoader->loadFinishing();
    199         m_resource->error(CachedResource::LoadError);
    200     }
    201 
    202     if (cancelled || !m_resource->isPreloaded())
    203         memoryCache()->remove(m_resource);
    204 
    205     m_cachedResourceLoader->loadDone(this);
    206 }
    207 
    208 void CachedResourceRequest::didReceiveResponse(SubresourceLoader* loader, const ResourceResponse& response)
    209 {
    210     ASSERT(loader == m_loader.get());
    211     if (m_resource->isCacheValidator()) {
    212         if (response.httpStatusCode() == 304) {
    213             // 304 Not modified / Use local copy
    214             loader->clearClient();
    215             RefPtr<Document> protector(m_cachedResourceLoader->document());
    216             m_cachedResourceLoader->decrementRequestCount(m_resource);
    217             m_finishing = true;
    218 
    219             // Existing resource is ok, just use it updating the expiration time.
    220             memoryCache()->revalidationSucceeded(m_resource, response);
    221 
    222             if (m_cachedResourceLoader->frame())
    223                 m_cachedResourceLoader->frame()->loader()->checkCompleted();
    224 
    225             m_cachedResourceLoader->loadDone(this);
    226             return;
    227         }
    228         // Did not get 304 response, continue as a regular resource load.
    229         memoryCache()->revalidationFailed(m_resource);
    230     }
    231 
    232     m_resource->setResponse(response);
    233 
    234     String encoding = response.textEncodingName();
    235     if (!encoding.isNull())
    236         m_resource->setEncoding(encoding);
    237 
    238     if (m_multipart) {
    239         ASSERT(m_resource->isImage());
    240         static_cast<CachedImage*>(m_resource)->clear();
    241         if (m_cachedResourceLoader->frame())
    242             m_cachedResourceLoader->frame()->loader()->checkCompleted();
    243     } else if (response.isMultipart()) {
    244         m_multipart = true;
    245 
    246         // We don't count multiParts in a CachedResourceLoader's request count
    247         m_cachedResourceLoader->decrementRequestCount(m_resource);
    248 
    249         // If we get a multipart response, we must have a handle
    250         ASSERT(loader->handle());
    251         if (!m_resource->isImage())
    252             loader->handle()->cancel();
    253     }
    254 }
    255 
    256 void CachedResourceRequest::didReceiveData(SubresourceLoader* loader, const char* data, int size)
    257 {
    258     ASSERT(loader == m_loader.get());
    259     ASSERT(!m_resource->isCacheValidator());
    260 
    261     if (m_resource->errorOccurred())
    262         return;
    263 
    264     if (m_resource->response().httpStatusCode() >= 400) {
    265         if (!m_resource->shouldIgnoreHTTPStatusCodeErrors())
    266             m_resource->error(CachedResource::LoadError);
    267         return;
    268     }
    269 
    270     // Set the data.
    271     if (m_multipart) {
    272         // The loader delivers the data in a multipart section all at once, send eof.
    273         // The resource data will change as the next part is loaded, so we need to make a copy.
    274         RefPtr<SharedBuffer> copiedData = SharedBuffer::create(data, size);
    275         m_resource->data(copiedData.release(), true);
    276     } else if (m_incremental)
    277         m_resource->data(loader->resourceData(), false);
    278 }
    279 
    280 void CachedResourceRequest::didReceiveCachedMetadata(SubresourceLoader*, const char* data, int size)
    281 {
    282     ASSERT(!m_resource->isCacheValidator());
    283     m_resource->setSerializedCachedMetadata(data, size);
    284 }
    285 
    286 } //namespace WebCore
    287