summaryrefslogtreecommitdiff
path: root/Source/WebCore/loader/SubresourceLoader.cpp
diff options
context:
space:
mode:
authorLorry Tar Creator <lorry-tar-importer@lorry>2017-06-27 06:07:23 +0000
committerLorry Tar Creator <lorry-tar-importer@lorry>2017-06-27 06:07:23 +0000
commit1bf1084f2b10c3b47fd1a588d85d21ed0eb41d0c (patch)
tree46dcd36c86e7fbc6e5df36deb463b33e9967a6f7 /Source/WebCore/loader/SubresourceLoader.cpp
parent32761a6cee1d0dee366b885b7b9c777e67885688 (diff)
downloadWebKitGtk-tarball-master.tar.gz
Diffstat (limited to 'Source/WebCore/loader/SubresourceLoader.cpp')
-rw-r--r--Source/WebCore/loader/SubresourceLoader.cpp416
1 files changed, 351 insertions, 65 deletions
diff --git a/Source/WebCore/loader/SubresourceLoader.cpp b/Source/WebCore/loader/SubresourceLoader.cpp
index 0f17264e0..dc6f5d77a 100644
--- a/Source/WebCore/loader/SubresourceLoader.cpp
+++ b/Source/WebCore/loader/SubresourceLoader.cpp
@@ -1,18 +1,18 @@
/*
- * Copyright (C) 2006, 2007, 2009 Apple Inc. All rights reserved.
+ * Copyright (C) 2006-2017 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
+ * notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Inc. ("Apple") nor the names of
* its contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
+ * from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
@@ -29,51 +29,68 @@
#include "config.h"
#include "SubresourceLoader.h"
+#include "CachedRawResource.h"
#include "CachedResourceLoader.h"
+#include "CrossOriginAccessControl.h"
+#include "DiagnosticLoggingClient.h"
+#include "DiagnosticLoggingKeys.h"
#include "Document.h"
#include "DocumentLoader.h"
#include "Frame.h"
#include "FrameLoader.h"
#include "Logging.h"
+#include "MainFrame.h"
#include "MemoryCache.h"
#include "Page.h"
-#include "PageActivityAssertionToken.h"
-#include "ResourceBuffer.h"
+#include "ResourceLoadObserver.h"
+#include "ResourceTiming.h"
+#include "RuntimeEnabledFeatures.h"
#include <wtf/Ref.h>
#include <wtf/RefCountedLeakCounter.h>
#include <wtf/StdLibExtras.h>
#include <wtf/text/CString.h>
#if PLATFORM(IOS)
-#include <RuntimeApplicationChecksIOS.h>
+#include <RuntimeApplicationChecks.h>
+#endif
+
+#if ENABLE(CONTENT_EXTENSIONS)
+#include "ResourceLoadInfo.h"
+#endif
+
+#if USE(QUICK_LOOK)
+#include "QuickLook.h"
#endif
namespace WebCore {
DEFINE_DEBUG_ONLY_GLOBAL(WTF::RefCountedLeakCounter, subresourceLoaderCounter, ("SubresourceLoader"));
-SubresourceLoader::RequestCountTracker::RequestCountTracker(CachedResourceLoader* cachedResourceLoader, CachedResource* resource)
+SubresourceLoader::RequestCountTracker::RequestCountTracker(CachedResourceLoader& cachedResourceLoader, const CachedResource& resource)
: m_cachedResourceLoader(cachedResourceLoader)
, m_resource(resource)
{
- m_cachedResourceLoader->incrementRequestCount(m_resource);
+ m_cachedResourceLoader.incrementRequestCount(m_resource);
}
SubresourceLoader::RequestCountTracker::~RequestCountTracker()
{
- m_cachedResourceLoader->decrementRequestCount(m_resource);
+ m_cachedResourceLoader.decrementRequestCount(m_resource);
}
-SubresourceLoader::SubresourceLoader(Frame* frame, CachedResource* resource, const ResourceLoaderOptions& options)
+SubresourceLoader::SubresourceLoader(Frame& frame, CachedResource& resource, const ResourceLoaderOptions& options)
: ResourceLoader(frame, options)
- , m_resource(resource)
+ , m_resource(&resource)
, m_loadingMultipartContent(false)
, m_state(Uninitialized)
- , m_requestCountTracker(adoptPtr(new RequestCountTracker(frame->document()->cachedResourceLoader(), resource)))
+ , m_requestCountTracker(std::in_place, frame.document()->cachedResourceLoader(), resource)
{
#ifndef NDEBUG
subresourceLoaderCounter.increment();
#endif
+#if ENABLE(CONTENT_EXTENSIONS)
+ m_resourceType = toResourceType(resource.type());
+#endif
}
SubresourceLoader::~SubresourceLoader()
@@ -85,11 +102,11 @@ SubresourceLoader::~SubresourceLoader()
#endif
}
-PassRefPtr<SubresourceLoader> SubresourceLoader::create(Frame* frame, CachedResource* resource, const ResourceRequest& request, const ResourceLoaderOptions& options)
+RefPtr<SubresourceLoader> SubresourceLoader::create(Frame& frame, CachedResource& resource, const ResourceRequest& request, const ResourceLoaderOptions& options)
{
RefPtr<SubresourceLoader> subloader(adoptRef(new SubresourceLoader(frame, resource, options)));
#if PLATFORM(IOS)
- if (!applicationIsWebProcess()) {
+ if (!IOSApplication::isWebProcess()) {
// On iOS, do not invoke synchronous resource load delegates while resource load scheduling
// is disabled to avoid re-entering style selection from a different thread (see <rdar://problem/9121719>).
// FIXME: This should be fixed for all ports in <https://bugs.webkit.org/show_bug.cgi?id=56647>.
@@ -99,13 +116,13 @@ PassRefPtr<SubresourceLoader> SubresourceLoader::create(Frame* frame, CachedReso
#endif
if (!subloader->init(request))
return nullptr;
- return subloader.release();
+ return subloader;
}
#if PLATFORM(IOS)
bool SubresourceLoader::startLoading()
{
- ASSERT(!applicationIsWebProcess());
+ ASSERT(!IOSApplication::isWebProcess());
if (!init(m_iOSOriginalRequest))
return false;
m_iOSOriginalRequest = ResourceRequest();
@@ -135,6 +152,12 @@ bool SubresourceLoader::init(const ResourceRequest& request)
ASSERT(!reachedTerminalState());
m_state = Initialized;
m_documentLoader->addSubresourceLoader(this);
+
+ // FIXME: https://bugs.webkit.org/show_bug.cgi?id=155633.
+ // SubresourceLoader could use the document origin as a default and set PotentiallyCrossOriginEnabled requests accordingly.
+ // This would simplify resource loader users as they would only need to set fetch mode to Cors.
+ m_origin = m_resource->origin();
+
return true;
}
@@ -143,70 +166,157 @@ bool SubresourceLoader::isSubresourceLoader()
return true;
}
-void SubresourceLoader::willSendRequest(ResourceRequest& newRequest, const ResourceResponse& redirectResponse)
+void SubresourceLoader::willSendRequestInternal(ResourceRequest& newRequest, const ResourceResponse& redirectResponse)
{
// Store the previous URL because the call to ResourceLoader::willSendRequest will modify it.
URL previousURL = request().url();
- Ref<SubresourceLoader> protect(*this);
+ Ref<SubresourceLoader> protectedThis(*this);
+
+ if (!newRequest.url().isValid()) {
+ cancel(cannotShowURLError());
+ return;
+ }
+
+ if (newRequest.requester() != ResourceRequestBase::Requester::Main)
+ ResourceLoadObserver::sharedObserver().logSubresourceLoading(m_frame.get(), newRequest, redirectResponse);
ASSERT(!newRequest.isNull());
if (!redirectResponse.isNull()) {
+ if (options().redirect != FetchOptions::Redirect::Follow) {
+ if (options().redirect == FetchOptions::Redirect::Error) {
+ cancel();
+ return;
+ }
+
+ ResourceResponse opaqueRedirectedResponse;
+ opaqueRedirectedResponse.setURL(redirectResponse.url());
+ opaqueRedirectedResponse.setType(ResourceResponse::Type::Opaqueredirect);
+ m_resource->responseReceived(opaqueRedirectedResponse);
+ didFinishLoading(currentTime());
+ return;
+ } else if (m_redirectCount++ >= options().maxRedirectCount) {
+ cancel(ResourceError(String(), 0, request().url(), ASCIILiteral("Too many redirections"), ResourceError::Type::General));
+ return;
+ }
+
// CachedResources are keyed off their original request URL.
// Requesting the same original URL a second time can redirect to a unique second resource.
// Therefore, if a redirect to a different destination URL occurs, we should no longer consider this a revalidation of the first resource.
// Doing so would have us reusing the resource from the first request if the second request's revalidation succeeds.
if (newRequest.isConditional() && m_resource->resourceToRevalidate() && newRequest.url() != m_resource->resourceToRevalidate()->response().url()) {
newRequest.makeUnconditional();
- memoryCache()->revalidationFailed(m_resource);
+ MemoryCache::singleton().revalidationFailed(*m_resource);
+ if (m_frame && m_frame->page())
+ m_frame->page()->diagnosticLoggingClient().logDiagnosticMessageWithResult(DiagnosticLoggingKeys::cachedResourceRevalidationKey(), emptyString(), DiagnosticLoggingResultFail, ShouldSample::Yes);
}
-
- if (!m_documentLoader->cachedResourceLoader().canRequest(m_resource->type(), newRequest.url(), options())) {
+
+ if (!m_documentLoader->cachedResourceLoader().updateRequestAfterRedirection(m_resource->type(), newRequest, options())) {
cancel();
return;
}
- if (m_resource->type() == CachedResource::ImageResource && m_documentLoader->cachedResourceLoader().shouldDeferImageLoad(newRequest.url())) {
+
+ String errorDescription;
+ if (!checkRedirectionCrossOriginAccessControl(request(), redirectResponse, newRequest, errorDescription)) {
+ String errorMessage = "Cross-origin redirection to " + newRequest.url().string() + " denied by Cross-Origin Resource Sharing policy: " + errorDescription;
+ if (m_frame && m_frame->document())
+ m_frame->document()->addConsoleMessage(MessageSource::Security, MessageLevel::Error, errorMessage);
+ cancel(ResourceError(String(), 0, request().url(), errorMessage, ResourceError::Type::AccessControl));
+ return;
+ }
+
+ if (m_resource->isImage() && m_documentLoader->cachedResourceLoader().shouldDeferImageLoad(newRequest.url())) {
cancel();
return;
}
- m_resource->willSendRequest(newRequest, redirectResponse);
+ m_loadTiming.addRedirect(redirectResponse.url(), newRequest.url());
+ m_resource->redirectReceived(newRequest, redirectResponse);
}
if (newRequest.isNull() || reachedTerminalState())
return;
- ResourceLoader::willSendRequest(newRequest, redirectResponse);
- if (newRequest.isNull())
+ ResourceLoader::willSendRequestInternal(newRequest, redirectResponse);
+
+ if (reachedTerminalState())
+ return;
+
+ if (newRequest.isNull()) {
cancel();
+ return;
+ }
+
+ if (m_resource->type() == CachedResource::MainResource && !redirectResponse.isNull())
+ m_documentLoader->willContinueMainResourceLoadAfterRedirect(newRequest);
}
void SubresourceLoader::didSendData(unsigned long long bytesSent, unsigned long long totalBytesToBeSent)
{
ASSERT(m_state == Initialized);
- Ref<SubresourceLoader> protect(*this);
+ Ref<SubresourceLoader> protectedThis(*this);
m_resource->didSendData(bytesSent, totalBytesToBeSent);
}
+#if USE(QUICK_LOOK)
+
+bool SubresourceLoader::shouldCreateQuickLookHandleForResponse(const ResourceResponse& response) const
+{
+ if (m_resource->type() != CachedResource::MainResource)
+ return false;
+
+ if (m_quickLookHandle)
+ return false;
+
+ return QuickLookHandle::shouldCreateForMIMEType(response.mimeType());
+}
+
+#endif
+
void SubresourceLoader::didReceiveResponse(const ResourceResponse& response)
{
ASSERT(!response.isNull());
ASSERT(m_state == Initialized);
+#if USE(QUICK_LOOK)
+ if (shouldCreateQuickLookHandleForResponse(response)) {
+ m_quickLookHandle = QuickLookHandle::create(*this, response);
+ return;
+ }
+#endif
+
+ // We want redirect responses to be processed through willSendRequestInternal. The only exception is redirection with no Location headers.
+ ASSERT(response.httpStatusCode() < 300 || response.httpStatusCode() >= 400 || response.httpStatusCode() == 304 || !response.httpHeaderField(HTTPHeaderName::Location));
+
// Reference the object in this method since the additional processing can do
// anything including removing the last reference to this object; one example of this is 3266216.
- Ref<SubresourceLoader> protect(*this);
+ Ref<SubresourceLoader> protectedThis(*this);
+
+ if (shouldIncludeCertificateInfo())
+ response.includeCertificateInfo();
if (m_resource->resourceToRevalidate()) {
if (response.httpStatusCode() == 304) {
// 304 Not modified / Use local copy
// Existing resource is ok, just use it updating the expiration time.
m_resource->setResponse(response);
- memoryCache()->revalidationSucceeded(m_resource, response);
+ MemoryCache::singleton().revalidationSucceeded(*m_resource, response);
+ if (m_frame && m_frame->page())
+ m_frame->page()->diagnosticLoggingClient().logDiagnosticMessageWithResult(DiagnosticLoggingKeys::cachedResourceRevalidationKey(), emptyString(), DiagnosticLoggingResultPass, ShouldSample::Yes);
if (!reachedTerminalState())
ResourceLoader::didReceiveResponse(response);
return;
}
// Did not get 304 response, continue as a regular resource load.
- memoryCache()->revalidationFailed(m_resource);
+ MemoryCache::singleton().revalidationFailed(*m_resource);
+ if (m_frame && m_frame->page())
+ m_frame->page()->diagnosticLoggingClient().logDiagnosticMessageWithResult(DiagnosticLoggingKeys::cachedResourceRevalidationKey(), emptyString(), DiagnosticLoggingResultFail, ShouldSample::Yes);
+ }
+
+ String errorDescription;
+ if (!checkResponseCrossOriginAccessControl(response, errorDescription)) {
+ if (m_frame && m_frame->document())
+ m_frame->document()->addConsoleMessage(MessageSource::Security, MessageLevel::Error, errorDescription);
+ cancel(ResourceError(String(), 0, request().url(), errorDescription, ResourceError::Type::AccessControl));
+ return;
}
m_resource->responseReceived(response);
@@ -223,21 +333,20 @@ void SubresourceLoader::didReceiveResponse(const ResourceResponse& response)
m_loadingMultipartContent = true;
// We don't count multiParts in a CachedResourceLoader's request count
- m_requestCountTracker.clear();
+ m_requestCountTracker = std::nullopt;
if (!m_resource->isImage()) {
cancel();
return;
}
}
- RefPtr<ResourceBuffer> buffer = resourceData();
+ auto* buffer = resourceData();
if (m_loadingMultipartContent && buffer && buffer->size()) {
// The resource data will change as the next part is loaded, so we need to make a copy.
- RefPtr<ResourceBuffer> copiedData = ResourceBuffer::create(buffer->data(), buffer->size());
- m_resource->finishLoading(copiedData.get());
+ m_resource->finishLoading(buffer->copy().ptr());
clearResourceData();
- // Since a subresource loader does not load multipart sections progressively, data was delivered to the loader all at once.
- // After the first multipart section is complete, signal to delegates that this load is "finished"
+ // Since a subresource loader does not load multipart sections progressively, data was delivered to the loader all at once.
+ // After the first multipart section is complete, signal to delegates that this load is "finished"
m_documentLoader->subresourceLoaderFinishedLoadingOnePart(this);
didFinishLoadingOnePart(0);
}
@@ -247,15 +356,29 @@ void SubresourceLoader::didReceiveResponse(const ResourceResponse& response)
void SubresourceLoader::didReceiveData(const char* data, unsigned length, long long encodedDataLength, DataPayloadType dataPayloadType)
{
- didReceiveDataOrBuffer(data, length, 0, encodedDataLength, dataPayloadType);
+#if USE(QUICK_LOOK)
+ if (auto quickLookHandle = m_quickLookHandle.get()) {
+ if (quickLookHandle->didReceiveData(data, length))
+ return;
+ }
+#endif
+
+ didReceiveDataOrBuffer(data, length, nullptr, encodedDataLength, dataPayloadType);
}
-void SubresourceLoader::didReceiveBuffer(PassRefPtr<SharedBuffer> buffer, long long encodedDataLength, DataPayloadType dataPayloadType)
+void SubresourceLoader::didReceiveBuffer(Ref<SharedBuffer>&& buffer, long long encodedDataLength, DataPayloadType dataPayloadType)
{
- didReceiveDataOrBuffer(0, 0, buffer, encodedDataLength, dataPayloadType);
+#if USE(QUICK_LOOK)
+ if (auto quickLookHandle = m_quickLookHandle.get()) {
+ if (quickLookHandle->didReceiveBuffer(buffer.get()))
+ return;
+ }
+#endif
+
+ didReceiveDataOrBuffer(nullptr, 0, WTFMove(buffer), encodedDataLength, dataPayloadType);
}
-void SubresourceLoader::didReceiveDataOrBuffer(const char* data, int length, PassRefPtr<SharedBuffer> prpBuffer, long long encodedDataLength, DataPayloadType dataPayloadType)
+void SubresourceLoader::didReceiveDataOrBuffer(const char* data, int length, RefPtr<SharedBuffer>&& buffer, long long encodedDataLength, DataPayloadType dataPayloadType)
{
if (m_resource->response().httpStatusCode() >= 400 && !m_resource->shouldIgnoreHTTPStatusCodeErrors())
return;
@@ -264,14 +387,13 @@ void SubresourceLoader::didReceiveDataOrBuffer(const char* data, int length, Pas
ASSERT(m_state == Initialized);
// Reference the object in this method since the additional processing can do
// anything including removing the last reference to this object; one example of this is 3266216.
- Ref<SubresourceLoader> protect(*this);
- RefPtr<SharedBuffer> buffer = prpBuffer;
-
- ResourceLoader::didReceiveDataOrBuffer(data, length, buffer, encodedDataLength, dataPayloadType);
+ Ref<SubresourceLoader> protectedThis(*this);
+
+ ResourceLoader::didReceiveDataOrBuffer(data, length, buffer.copyRef(), encodedDataLength, dataPayloadType);
if (!m_loadingMultipartContent) {
- if (ResourceBuffer* resourceData = this->resourceData())
- m_resource->addDataBuffer(resourceData);
+ if (auto* resourceData = this->resourceData())
+ m_resource->addDataBuffer(*resourceData);
else
m_resource->addData(buffer ? buffer->data() : data, buffer ? buffer->size() : length);
}
@@ -288,31 +410,151 @@ bool SubresourceLoader::checkForHTTPStatusCodeError()
return true;
}
+static void logResourceLoaded(Frame* frame, CachedResource::Type type)
+{
+ if (!frame || !frame->page())
+ return;
+
+ String resourceType;
+ switch (type) {
+ case CachedResource::MainResource:
+ resourceType = DiagnosticLoggingKeys::mainResourceKey();
+ break;
+ case CachedResource::ImageResource:
+ resourceType = DiagnosticLoggingKeys::imageKey();
+ break;
+#if ENABLE(XSLT)
+ case CachedResource::XSLStyleSheet:
+#endif
+ case CachedResource::CSSStyleSheet:
+ resourceType = DiagnosticLoggingKeys::styleSheetKey();
+ break;
+ case CachedResource::Script:
+ resourceType = DiagnosticLoggingKeys::scriptKey();
+ break;
+ case CachedResource::FontResource:
+#if ENABLE(SVG_FONTS)
+ case CachedResource::SVGFontResource:
+#endif
+ resourceType = DiagnosticLoggingKeys::fontKey();
+ break;
+ case CachedResource::MediaResource:
+ case CachedResource::RawResource:
+ resourceType = DiagnosticLoggingKeys::rawKey();
+ break;
+ case CachedResource::SVGDocumentResource:
+ resourceType = DiagnosticLoggingKeys::svgDocumentKey();
+ break;
+#if ENABLE(LINK_PREFETCH)
+ case CachedResource::LinkPrefetch:
+ case CachedResource::LinkSubresource:
+#endif
+#if ENABLE(VIDEO_TRACK)
+ case CachedResource::TextTrackResource:
+#endif
+ resourceType = DiagnosticLoggingKeys::otherKey();
+ break;
+ }
+ frame->page()->diagnosticLoggingClient().logDiagnosticMessage(DiagnosticLoggingKeys::resourceLoadedKey(), resourceType, ShouldSample::Yes);
+}
+
+bool SubresourceLoader::checkResponseCrossOriginAccessControl(const ResourceResponse& response, String& errorDescription)
+{
+ if (!m_resource->isCrossOrigin() || options().mode != FetchOptions::Mode::Cors)
+ return true;
+
+ ASSERT(m_origin);
+ return passesAccessControlCheck(response, options().allowCredentials, *m_origin, errorDescription);
+}
+
+bool SubresourceLoader::checkRedirectionCrossOriginAccessControl(const ResourceRequest& previousRequest, const ResourceResponse& redirectResponse, ResourceRequest& newRequest, String& errorMessage)
+{
+ bool crossOriginFlag = m_resource->isCrossOrigin();
+ bool isNextRequestCrossOrigin = m_origin && !m_origin->canRequest(newRequest.url());
+
+ if (isNextRequestCrossOrigin)
+ m_resource->setCrossOrigin();
+
+ ASSERT(options().mode != FetchOptions::Mode::SameOrigin || !m_resource->isCrossOrigin());
+
+ if (options().mode != FetchOptions::Mode::Cors)
+ return true;
+
+ // Implementing https://fetch.spec.whatwg.org/#concept-http-redirect-fetch step 8 & 9.
+ if (m_resource->isCrossOrigin() && !isValidCrossOriginRedirectionURL(newRequest.url())) {
+ errorMessage = ASCIILiteral("URL is either a non-HTTP URL or contains credentials.");
+ return false;
+ }
+
+ ASSERT(m_origin);
+ if (crossOriginFlag && !passesAccessControlCheck(redirectResponse, options().allowCredentials, *m_origin, errorMessage))
+ return false;
+
+ bool redirectingToNewOrigin = false;
+ if (m_resource->isCrossOrigin()) {
+ if (!crossOriginFlag && isNextRequestCrossOrigin)
+ redirectingToNewOrigin = true;
+ else
+ redirectingToNewOrigin = !protocolHostAndPortAreEqual(previousRequest.url(), newRequest.url());
+ }
+
+ // Implementing https://fetch.spec.whatwg.org/#concept-http-redirect-fetch step 10.
+ if (crossOriginFlag && redirectingToNewOrigin)
+ m_origin = SecurityOrigin::createUnique();
+
+ if (redirectingToNewOrigin) {
+ cleanRedirectedRequestForAccessControl(newRequest);
+ updateRequestForAccessControl(newRequest, *m_origin, options().allowCredentials);
+ }
+
+ return true;
+}
+
void SubresourceLoader::didFinishLoading(double finishTime)
{
+#if USE(QUICK_LOOK)
+ if (auto quickLookHandle = m_quickLookHandle.get()) {
+ if (quickLookHandle->didFinishLoading())
+ return;
+ }
+#endif
+
if (m_state != Initialized)
return;
ASSERT(!reachedTerminalState());
ASSERT(!m_resource->resourceToRevalidate());
- ASSERT(!m_resource->errorOccurred());
+ // FIXME (129394): We should cancel the load when a decode error occurs instead of continuing the load to completion.
+ ASSERT(!m_resource->errorOccurred() || m_resource->status() == CachedResource::DecodeError || !m_resource->isLoading());
LOG(ResourceLoading, "Received '%s'.", m_resource->url().string().latin1().data());
+ logResourceLoaded(m_frame.get(), m_resource->type());
- Ref<SubresourceLoader> protect(*this);
+ Ref<SubresourceLoader> protectedThis(*this);
+ CachedResourceHandle<CachedResource> protectResource(m_resource);
-#if PLATFORM(IOS)
- if (resourceData())
- resourceData()->setShouldUsePurgeableMemory(true);
+ // FIXME: <https://webkit.org/b/168351> [Resource Timing] Gather timing information with reliable responseEnd time
+ // The finishTime that is passed in is from the NetworkProcess and is more accurate.
+ // However, all other load times are generated from the web process or offsets.
+ // Mixing times from different processes can cause the finish time to be earlier than
+ // the response received time due to inter-process communication lag. This could be solved
+ // by gathering NetworkLoadTiming information at completion time instead of at
+ // didReceiveResponse time.
+ UNUSED_PARAM(finishTime);
+ MonotonicTime responseEndTime = MonotonicTime::now();
+ m_loadTiming.setResponseEnd(responseEndTime);
+
+#if ENABLE(WEB_TIMING)
+ reportResourceTiming();
#endif
- CachedResourceHandle<CachedResource> protectResource(m_resource);
+
m_state = Finishing;
- m_resource->setLoadFinishTime(finishTime);
+ m_resource->setLoadFinishTime(responseEndTime.secondsSinceEpoch().seconds()); // FIXME: Users of the loadFinishTime should use the LoadTiming struct instead.
m_resource->finishLoading(resourceData());
if (wasCancelled())
return;
m_resource->finish();
ASSERT(!reachedTerminalState());
- didFinishLoadingOnePart(finishTime);
+ didFinishLoadingOnePart(responseEndTime.secondsSinceEpoch().seconds());
notifyDone();
if (reachedTerminalState())
return;
@@ -321,19 +563,24 @@ void SubresourceLoader::didFinishLoading(double finishTime)
void SubresourceLoader::didFail(const ResourceError& error)
{
+#if USE(QUICK_LOOK)
+ if (auto quickLookHandle = m_quickLookHandle.get())
+ quickLookHandle->didFail();
+#endif
+
if (m_state != Initialized)
return;
ASSERT(!reachedTerminalState());
LOG(ResourceLoading, "Failed to load '%s'.\n", m_resource->url().string().latin1().data());
- Ref<SubresourceLoader> protect(*this);
+ Ref<SubresourceLoader> protectedThis(*this);
CachedResourceHandle<CachedResource> protectResource(m_resource);
m_state = Finishing;
if (m_resource->resourceToRevalidate())
- memoryCache()->revalidationFailed(m_resource);
+ MemoryCache::singleton().revalidationFailed(*m_resource);
m_resource->setResourceError(error);
if (!m_resource->isPreloaded())
- memoryCache()->remove(m_resource);
+ MemoryCache::singleton().remove(*m_resource);
m_resource->error(CachedResource::LoadError);
cleanupForError(error);
notifyDone();
@@ -356,16 +603,17 @@ void SubresourceLoader::willCancel(const ResourceError& error)
ASSERT(!reachedTerminalState());
LOG(ResourceLoading, "Cancelled load of '%s'.\n", m_resource->url().string().latin1().data());
- Ref<SubresourceLoader> protect(*this);
+ Ref<SubresourceLoader> protectedThis(*this);
#if PLATFORM(IOS)
m_state = m_state == Uninitialized ? CancelledWhileInitializing : Finishing;
#else
m_state = Finishing;
#endif
+ auto& memoryCache = MemoryCache::singleton();
if (m_resource->resourceToRevalidate())
- memoryCache()->revalidationFailed(m_resource);
+ memoryCache.revalidationFailed(*m_resource);
m_resource->setResourceError(error);
- memoryCache()->remove(m_resource);
+ memoryCache.remove(*m_resource);
}
void SubresourceLoader::didCancel(const ResourceError&)
@@ -377,16 +625,23 @@ void SubresourceLoader::didCancel(const ResourceError&)
notifyDone();
}
+void SubresourceLoader::didRetrieveDerivedDataFromCache(const String& type, SharedBuffer& buffer)
+{
+ if (m_state != Initialized)
+ return;
+ m_resource->didRetrieveDerivedDataFromCache(type, buffer);
+}
+
void SubresourceLoader::notifyDone()
{
if (reachedTerminalState())
return;
- m_requestCountTracker.clear();
+ m_requestCountTracker = std::nullopt;
#if PLATFORM(IOS)
- m_documentLoader->cachedResourceLoader().loadDone(m_resource, m_state != CancelledWhileInitializing);
+ m_documentLoader->cachedResourceLoader().loadDone(m_state != CancelledWhileInitializing);
#else
- m_documentLoader->cachedResourceLoader().loadDone(m_resource);
+ m_documentLoader->cachedResourceLoader().loadDone();
#endif
if (reachedTerminalState())
return;
@@ -402,8 +657,39 @@ void SubresourceLoader::releaseResources()
if (m_state != Uninitialized)
#endif
m_resource->clearLoader();
- m_resource = 0;
+ m_resource = nullptr;
ResourceLoader::releaseResources();
}
+#if ENABLE(WEB_TIMING)
+void SubresourceLoader::reportResourceTiming()
+{
+ if (!RuntimeEnabledFeatures::sharedFeatures().resourceTimingEnabled())
+ return;
+
+ if (!ResourceTimingInformation::shouldAddResourceTiming(*m_resource))
+ return;
+
+ Document* document = m_documentLoader->cachedResourceLoader().document();
+ if (!document)
+ return;
+
+ SecurityOrigin& origin = m_origin ? *m_origin : document->securityOrigin();
+ ResourceTiming resourceTiming = ResourceTiming::fromLoad(*m_resource, m_resource->initiatorName(), m_loadTiming, origin);
+
+ // Worker resources loaded here are all CachedRawResources loaded through WorkerThreadableLoader.
+ // Pass the ResourceTiming information on so that WorkerThreadableLoader may add them to the
+ // Worker's Performance object.
+ if (options().initiatorContext == InitiatorContext::Worker) {
+ ASSERT(m_origin);
+ ASSERT(is<CachedRawResource>(m_resource));
+ downcast<CachedRawResource>(*m_resource).finishedTimingForWorkerLoad(WTFMove(resourceTiming));
+ return;
+ }
+
+ ASSERT(options().initiatorContext == InitiatorContext::Document);
+ m_documentLoader->cachedResourceLoader().resourceTimingInformation().addResourceTiming(*m_resource, *document, WTFMove(resourceTiming));
+}
+#endif
+
}