SubresourceLoader.cpp [plain text]
#include "config.h"
#include "SubresourceLoader.h"
#include "CachedResourceLoader.h"
#include "Document.h"
#include "DocumentLoader.h"
#include "Frame.h"
#include "FrameLoader.h"
#include "Logging.h"
#include "MemoryCache.h"
#include "SecurityOrigin.h"
#include "SecurityPolicy.h"
#include <wtf/RefCountedLeakCounter.h>
#include <wtf/StdLibExtras.h>
#include <wtf/text/CString.h>
namespace WebCore {
DEFINE_DEBUG_ONLY_GLOBAL(WTF::RefCountedLeakCounter, subresourceLoaderCounter, ("SubresourceLoader"));
SubresourceLoader::RequestCountTracker::RequestCountTracker(CachedResourceLoader* cachedResourceLoader, CachedResource* resource)
: m_cachedResourceLoader(cachedResourceLoader)
, m_resource(resource)
{
m_cachedResourceLoader->incrementRequestCount(m_resource);
}
SubresourceLoader::RequestCountTracker::~RequestCountTracker()
{
m_cachedResourceLoader->decrementRequestCount(m_resource);
}
SubresourceLoader::SubresourceLoader(Frame* frame, CachedResource* resource, const ResourceLoaderOptions& options)
: ResourceLoader(frame, options)
, m_resource(resource)
, m_document(frame->document())
, m_loadingMultipartContent(false)
, m_state(Uninitialized)
, m_requestCountTracker(adoptPtr(new RequestCountTracker(frame->document()->cachedResourceLoader(), resource)))
{
#ifndef NDEBUG
subresourceLoaderCounter.increment();
#endif
}
SubresourceLoader::~SubresourceLoader()
{
ASSERT(m_state != Initialized);
ASSERT(!m_document);
ASSERT(reachedTerminalState());
#ifndef NDEBUG
subresourceLoaderCounter.decrement();
#endif
}
PassRefPtr<SubresourceLoader> SubresourceLoader::create(Frame* frame, CachedResource* resource, const ResourceRequest& request, const ResourceLoaderOptions& options)
{
if (!frame)
return 0;
FrameLoader* frameLoader = frame->loader();
if (options.securityCheck == DoSecurityCheck && (frameLoader->state() == FrameStateProvisional || !frameLoader->activeDocumentLoader() || frameLoader->activeDocumentLoader()->isStopping()))
return 0;
ResourceRequest newRequest = request;
String outgoingReferrer;
String outgoingOrigin;
if (request.httpReferrer().isNull()) {
outgoingReferrer = frameLoader->outgoingReferrer();
outgoingOrigin = frameLoader->outgoingOrigin();
} else {
outgoingReferrer = request.httpReferrer();
outgoingOrigin = SecurityOrigin::createFromString(outgoingReferrer)->toString();
}
outgoingReferrer = SecurityPolicy::generateReferrerHeader(frame->document()->referrerPolicy(), request.url(), outgoingReferrer);
if (outgoingReferrer.isEmpty())
newRequest.clearHTTPReferrer();
else if (!request.httpReferrer())
newRequest.setHTTPReferrer(outgoingReferrer);
FrameLoader::addHTTPOriginIfNeeded(newRequest, outgoingOrigin);
frameLoader->addExtraFieldsToSubresourceRequest(newRequest);
RefPtr<SubresourceLoader> subloader(adoptRef(new SubresourceLoader(frame, resource, options)));
subloader->m_iOSOriginalRequest = newRequest;
return subloader.release();
}
bool SubresourceLoader::startLoading()
{
if (!init(m_iOSOriginalRequest))
return false;
m_iOSOriginalRequest = ResourceRequest();
start();
return true;
}
void SubresourceLoader::cancelIfNotFinishing()
{
if (m_state != Initialized)
return;
ResourceLoader::cancel();
}
bool SubresourceLoader::init(const ResourceRequest& request)
{
if (!ResourceLoader::init(request))
return false;
ASSERT(!reachedTerminalState());
m_state = Initialized;
m_documentLoader->addSubresourceLoader(this);
return true;
}
void SubresourceLoader::willSendRequest(ResourceRequest& newRequest, const ResourceResponse& redirectResponse)
{
KURL previousURL = request().url();
ResourceLoader::willSendRequest(newRequest, redirectResponse);
if (!previousURL.isNull() && !newRequest.isNull() && previousURL != newRequest.url()) {
if (!m_document->cachedResourceLoader()->canRequest(m_resource->type(), newRequest.url())) {
cancel();
return;
}
m_resource->willSendRequest(newRequest, redirectResponse);
}
}
void SubresourceLoader::didSendData(unsigned long long bytesSent, unsigned long long totalBytesToBeSent)
{
ASSERT(m_state == Initialized);
RefPtr<SubresourceLoader> protect(this);
m_resource->didSendData(bytesSent, totalBytesToBeSent);
}
void SubresourceLoader::didReceiveResponse(const ResourceResponse& response)
{
ASSERT(!response.isNull());
ASSERT(m_state == Initialized);
RefPtr<SubresourceLoader> protect(this);
if (m_resource->resourceToRevalidate()) {
if (response.httpStatusCode() == 304) {
memoryCache()->revalidationSucceeded(m_resource, response);
if (!reachedTerminalState())
ResourceLoader::didReceiveResponse(response);
return;
}
memoryCache()->revalidationFailed(m_resource);
}
m_resource->setResponse(response);
if (reachedTerminalState())
return;
ResourceLoader::didReceiveResponse(response);
if (response.isMultipart()) {
m_loadingMultipartContent = true;
m_requestCountTracker.clear();
if (!m_resource->isImage()) {
cancel();
return;
}
}
RefPtr<SharedBuffer> buffer = resourceData();
if (m_loadingMultipartContent && buffer && buffer->size()) {
sendDataToResource(buffer->data(), buffer->size());
clearResourceData();
m_documentLoader->subresourceLoaderFinishedLoadingOnePart(this);
didFinishLoadingOnePart(0);
}
}
void SubresourceLoader::didReceiveData(const char* data, int length, long long encodedDataLength, bool allAtOnce)
{
ASSERT(!m_resource->resourceToRevalidate());
ASSERT(!m_resource->errorOccurred());
ASSERT(m_state == Initialized);
RefPtr<SubresourceLoader> protect(this);
ResourceLoader::didReceiveData(data, length, encodedDataLength, allAtOnce);
if (errorLoadingResource() || m_loadingMultipartContent)
return;
sendDataToResource(data, length);
}
bool SubresourceLoader::errorLoadingResource()
{
if (m_resource->response().httpStatusCode() < 400 || m_resource->shouldIgnoreHTTPStatusCodeErrors())
return false;
m_resource->error(CachedResource::LoadError);
m_state = Finishing;
cancel();
return true;
}
void SubresourceLoader::sendDataToResource(const char* data, int length)
{
if (m_loadingMultipartContent || !resourceData()) {
RefPtr<SharedBuffer> copiedData = SharedBuffer::create(data, length);
m_resource->data(copiedData.release(), m_loadingMultipartContent);
} else
m_resource->data(resourceData(), false);
}
void SubresourceLoader::didReceiveCachedMetadata(const char* data, int length)
{
ASSERT(m_state == Initialized);
ASSERT(!m_resource->resourceToRevalidate());
m_resource->setSerializedCachedMetadata(data, length);
}
void SubresourceLoader::didFinishLoading(double finishTime)
{
if (m_state != Initialized)
return;
ASSERT(!reachedTerminalState());
ASSERT(!m_resource->resourceToRevalidate());
ASSERT(!m_resource->errorOccurred());
LOG(ResourceLoading, "Received '%s'.", m_resource->url().string().latin1().data());
RefPtr<SubresourceLoader> protect(this);
if (resourceData())
resourceData()->shouldUsePurgeableMemory(true);
CachedResourceHandle<CachedResource> protectResource(m_resource);
m_state = Finishing;
m_resource->setLoadFinishTime(finishTime);
m_resource->data(resourceData(), true);
m_resource->finish();
ResourceLoader::didFinishLoading(finishTime);
}
void SubresourceLoader::didFail(const ResourceError& error)
{
if (m_state != Initialized)
return;
ASSERT(!reachedTerminalState());
ASSERT(!m_resource->resourceToRevalidate());
LOG(ResourceLoading, "Failed to load '%s'.\n", m_resource->url().string().latin1().data());
RefPtr<SubresourceLoader> protect(this);
CachedResourceHandle<CachedResource> protectResource(m_resource);
m_state = Finishing;
m_resource->error(CachedResource::LoadError);
if (!m_resource->isPreloaded())
memoryCache()->remove(m_resource);
ResourceLoader::didFail(error);
}
void SubresourceLoader::willCancel(const ResourceError&)
{
if (m_state != Initialized)
return;
ASSERT(!reachedTerminalState());
LOG(ResourceLoading, "Cancelled load of '%s'.\n", m_resource->url().string().latin1().data());
RefPtr<SubresourceLoader> protect(this);
m_state = Finishing;
if (m_resource->resourceToRevalidate())
memoryCache()->revalidationFailed(m_resource);
memoryCache()->remove(m_resource);
}
void SubresourceLoader::clearCachedResourceAfterSynchronousCancel()
{
m_resource->clearLoaderAfterSynchronousCancel();
}
void SubresourceLoader::releaseResources()
{
ASSERT(!reachedTerminalState());
if (m_state != Uninitialized) {
m_requestCountTracker.clear();
m_document->cachedResourceLoader()->loadDone();
if (reachedTerminalState())
return;
m_resource->stopLoading();
m_documentLoader->removeSubresourceLoader(this);
}
m_document = 0;
ResourceLoader::releaseResources();
}
}