| /* |
| Copyright (C) 1998 Lars Knoll (knoll@mpi-hd.mpg.de) |
| Copyright (C) 2001 Dirk Mueller (mueller@kde.org) |
| Copyright (C) 2002 Waldo Bastian (bastian@kde.org) |
| Copyright (C) 2006 Samuel Weinig (sam.weinig@gmail.com) |
| Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. |
| |
| This library is free software; you can redistribute it and/or |
| modify it under the terms of the GNU Library General Public |
| License as published by the Free Software Foundation; either |
| version 2 of the License, or (at your option) any later version. |
| |
| This library is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| Library General Public License for more details. |
| |
| You should have received a copy of the GNU Library General Public License |
| along with this library; see the file COPYING.LIB. If not, write to |
| the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
| Boston, MA 02110-1301, USA. |
| */ |
| |
| #include "config.h" |
| #include "CachedResourceRequest.h" |
| |
| #include "MemoryCache.h" |
| #include "CachedImage.h" |
| #include "CachedResource.h" |
| #include "CachedResourceLoader.h" |
| #include "Frame.h" |
| #include "FrameLoader.h" |
| #include "Logging.h" |
| #include "ResourceHandle.h" |
| #include "ResourceLoadScheduler.h" |
| #include "ResourceRequest.h" |
| #include "ResourceResponse.h" |
| #include "SharedBuffer.h" |
| #include <wtf/Assertions.h> |
| #include <wtf/Vector.h> |
| #include <wtf/text/CString.h> |
| |
| namespace WebCore { |
| |
| static ResourceRequest::TargetType cachedResourceTypeToTargetType(CachedResource::Type type) |
| { |
| switch (type) { |
| case CachedResource::CSSStyleSheet: |
| #if ENABLE(XSLT) |
| case CachedResource::XSLStyleSheet: |
| #endif |
| return ResourceRequest::TargetIsStyleSheet; |
| case CachedResource::Script: |
| return ResourceRequest::TargetIsScript; |
| case CachedResource::FontResource: |
| return ResourceRequest::TargetIsFontResource; |
| case CachedResource::ImageResource: |
| return ResourceRequest::TargetIsImage; |
| #if ENABLE(LINK_PREFETCH) |
| case CachedResource::LinkPrefetch: |
| return ResourceRequest::TargetIsPrefetch; |
| #endif |
| } |
| ASSERT_NOT_REACHED(); |
| return ResourceRequest::TargetIsSubresource; |
| } |
| |
| CachedResourceRequest::CachedResourceRequest(CachedResourceLoader* cachedResourceLoader, CachedResource* resource, bool incremental) |
| : m_cachedResourceLoader(cachedResourceLoader) |
| , m_resource(resource) |
| , m_incremental(incremental) |
| , m_multipart(false) |
| , m_finishing(false) |
| { |
| m_resource->setRequest(this); |
| } |
| |
| CachedResourceRequest::~CachedResourceRequest() |
| { |
| m_resource->setRequest(0); |
| } |
| |
| PassRefPtr<CachedResourceRequest> CachedResourceRequest::load(CachedResourceLoader* cachedResourceLoader, CachedResource* resource, bool incremental, SecurityCheckPolicy securityCheck, bool sendResourceLoadCallbacks) |
| { |
| RefPtr<CachedResourceRequest> request = adoptRef(new CachedResourceRequest(cachedResourceLoader, resource, incremental)); |
| |
| ResourceRequest resourceRequest(resource->url()); |
| resourceRequest.setTargetType(cachedResourceTypeToTargetType(resource->type())); |
| |
| if (!resource->accept().isEmpty()) |
| resourceRequest.setHTTPAccept(resource->accept()); |
| |
| if (resource->isCacheValidator()) { |
| CachedResource* resourceToRevalidate = resource->resourceToRevalidate(); |
| ASSERT(resourceToRevalidate->canUseCacheValidator()); |
| ASSERT(resourceToRevalidate->isLoaded()); |
| const String& lastModified = resourceToRevalidate->response().httpHeaderField("Last-Modified"); |
| const String& eTag = resourceToRevalidate->response().httpHeaderField("ETag"); |
| if (!lastModified.isEmpty() || !eTag.isEmpty()) { |
| ASSERT(cachedResourceLoader->cachePolicy() != CachePolicyReload); |
| if (cachedResourceLoader->cachePolicy() == CachePolicyRevalidate) |
| resourceRequest.setHTTPHeaderField("Cache-Control", "max-age=0"); |
| if (!lastModified.isEmpty()) |
| resourceRequest.setHTTPHeaderField("If-Modified-Since", lastModified); |
| if (!eTag.isEmpty()) |
| resourceRequest.setHTTPHeaderField("If-None-Match", eTag); |
| } |
| } |
| |
| #if ENABLE(LINK_PREFETCH) |
| if (resource->type() == CachedResource::LinkPrefetch) |
| resourceRequest.setHTTPHeaderField("Purpose", "prefetch"); |
| #endif |
| |
| ResourceLoadPriority priority = resource->loadPriority(); |
| |
| RefPtr<SubresourceLoader> loader = resourceLoadScheduler()->scheduleSubresourceLoad(cachedResourceLoader->document()->frame(), |
| request.get(), resourceRequest, priority, securityCheck, sendResourceLoadCallbacks); |
| if (!loader || loader->reachedTerminalState()) { |
| // FIXME: What if resources in other frames were waiting for this revalidation? |
| LOG(ResourceLoading, "Cannot start loading '%s'", resource->url().latin1().data()); |
| cachedResourceLoader->decrementRequestCount(resource); |
| cachedResourceLoader->loadFinishing(); |
| if (resource->resourceToRevalidate()) |
| cache()->revalidationFailed(resource); |
| resource->error(CachedResource::LoadError); |
| cachedResourceLoader->loadDone(0); |
| return 0; |
| } |
| request->m_loader = loader; |
| return request.release(); |
| } |
| |
| void CachedResourceRequest::willSendRequest(SubresourceLoader*, ResourceRequest&, const ResourceResponse&) |
| { |
| m_resource->setRequestedFromNetworkingLayer(); |
| } |
| |
| void CachedResourceRequest::didFinishLoading(SubresourceLoader* loader) |
| { |
| if (m_finishing) |
| return; |
| |
| ASSERT(loader == m_loader.get()); |
| ASSERT(!m_resource->resourceToRevalidate()); |
| LOG(ResourceLoading, "Received '%s'.", m_resource->url().latin1().data()); |
| |
| // Prevent the document from being destroyed before we are done with |
| // the cachedResourceLoader that it will delete when the document gets deleted. |
| RefPtr<Document> protector(m_cachedResourceLoader->document()); |
| if (!m_multipart) |
| m_cachedResourceLoader->decrementRequestCount(m_resource); |
| m_finishing = true; |
| |
| // If we got a 4xx response, we're pretending to have received a network |
| // error, so we can't send the successful data() and finish() callbacks. |
| if (!m_resource->errorOccurred()) { |
| m_cachedResourceLoader->loadFinishing(); |
| m_resource->data(loader->resourceData(), true); |
| if (!m_resource->errorOccurred()) |
| m_resource->finish(); |
| } |
| m_cachedResourceLoader->loadDone(this); |
| } |
| |
| void CachedResourceRequest::didFail(SubresourceLoader*, const ResourceError&) |
| { |
| if (!m_loader) |
| return; |
| didFail(); |
| } |
| |
| void CachedResourceRequest::didFail(bool cancelled) |
| { |
| if (m_finishing) |
| return; |
| |
| LOG(ResourceLoading, "Failed to load '%s' (cancelled=%d).\n", m_resource->url().latin1().data(), cancelled); |
| |
| // Prevent the document from being destroyed before we are done with |
| // the cachedResourceLoader that it will delete when the document gets deleted. |
| RefPtr<Document> protector(m_cachedResourceLoader->document()); |
| if (!m_multipart) |
| m_cachedResourceLoader->decrementRequestCount(m_resource); |
| m_finishing = true; |
| m_loader->clearClient(); |
| |
| if (m_resource->resourceToRevalidate()) |
| cache()->revalidationFailed(m_resource); |
| |
| if (!cancelled) { |
| m_cachedResourceLoader->loadFinishing(); |
| m_resource->error(CachedResource::LoadError); |
| } |
| |
| if (cancelled || !m_resource->isPreloaded()) |
| cache()->remove(m_resource); |
| |
| m_cachedResourceLoader->loadDone(this); |
| } |
| |
| void CachedResourceRequest::didReceiveResponse(SubresourceLoader* loader, const ResourceResponse& response) |
| { |
| ASSERT(loader == m_loader.get()); |
| if (m_resource->isCacheValidator()) { |
| if (response.httpStatusCode() == 304) { |
| // 304 Not modified / Use local copy |
| loader->clearClient(); |
| RefPtr<Document> protector(m_cachedResourceLoader->document()); |
| m_cachedResourceLoader->decrementRequestCount(m_resource); |
| m_finishing = true; |
| |
| // Existing resource is ok, just use it updating the expiration time. |
| cache()->revalidationSucceeded(m_resource, response); |
| |
| if (m_cachedResourceLoader->frame()) |
| m_cachedResourceLoader->frame()->loader()->checkCompleted(); |
| |
| m_cachedResourceLoader->loadDone(this); |
| return; |
| } |
| // Did not get 304 response, continue as a regular resource load. |
| cache()->revalidationFailed(m_resource); |
| } |
| |
| m_resource->setResponse(response); |
| |
| String encoding = response.textEncodingName(); |
| if (!encoding.isNull()) |
| m_resource->setEncoding(encoding); |
| |
| if (m_multipart) { |
| ASSERT(m_resource->isImage()); |
| static_cast<CachedImage*>(m_resource)->clear(); |
| if (m_cachedResourceLoader->frame()) |
| m_cachedResourceLoader->frame()->loader()->checkCompleted(); |
| } else if (response.isMultipart()) { |
| m_multipart = true; |
| |
| // We don't count multiParts in a CachedResourceLoader's request count |
| m_cachedResourceLoader->decrementRequestCount(m_resource); |
| |
| // If we get a multipart response, we must have a handle |
| ASSERT(loader->handle()); |
| if (!m_resource->isImage()) |
| loader->handle()->cancel(); |
| } |
| } |
| |
| void CachedResourceRequest::didReceiveData(SubresourceLoader* loader, const char* data, int size) |
| { |
| ASSERT(loader == m_loader.get()); |
| ASSERT(!m_resource->isCacheValidator()); |
| |
| if (m_resource->errorOccurred()) |
| return; |
| |
| if (m_resource->response().httpStatusCode() >= 400) { |
| if (!m_resource->shouldIgnoreHTTPStatusCodeErrors()) |
| m_resource->error(CachedResource::LoadError); |
| return; |
| } |
| |
| // Set the data. |
| if (m_multipart) { |
| // The loader delivers the data in a multipart section all at once, send eof. |
| // The resource data will change as the next part is loaded, so we need to make a copy. |
| RefPtr<SharedBuffer> copiedData = SharedBuffer::create(data, size); |
| m_resource->data(copiedData.release(), true); |
| } else if (m_incremental) |
| m_resource->data(loader->resourceData(), false); |
| } |
| |
| void CachedResourceRequest::didReceiveCachedMetadata(SubresourceLoader*, const char* data, int size) |
| { |
| ASSERT(!m_resource->isCacheValidator()); |
| m_resource->setSerializedCachedMetadata(data, size); |
| } |
| |
| } //namespace WebCore |