2 * Copyright (C) 2006-2017 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. Neither the name of Apple Inc. ("Apple") nor the names of
14 * its contributors may be used to endorse or promote products derived
15 * from this software without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
18 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
21 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 #include "SubresourceLoader.h"
32 #include "CachedRawResource.h"
33 #include "CachedResourceLoader.h"
34 #include "CrossOriginAccessControl.h"
35 #include "DiagnosticLoggingClient.h"
36 #include "DiagnosticLoggingKeys.h"
38 #include "DocumentLoader.h"
40 #include "FrameLoader.h"
41 #include "HTTPParsers.h"
42 #include "LinkLoader.h"
44 #include "MemoryCache.h"
46 #include "ResourceLoadObserver.h"
47 #include "ResourceTiming.h"
48 #include "RuntimeEnabledFeatures.h"
50 #include <wtf/CompletionHandler.h>
52 #include <wtf/RefCountedLeakCounter.h>
53 #include <wtf/StdLibExtras.h>
54 #include <wtf/SystemTracing.h>
55 #include <wtf/text/CString.h>
58 #include <RuntimeApplicationChecks.h>
61 #if ENABLE(CONTENT_EXTENSIONS)
62 #include "ResourceLoadInfo.h"
66 #include "PreviewLoader.h"
71 DEFINE_DEBUG_ONLY_GLOBAL(WTF::RefCountedLeakCounter, subresourceLoaderCounter, ("SubresourceLoader"));
73 SubresourceLoader::RequestCountTracker::RequestCountTracker(CachedResourceLoader& cachedResourceLoader, const CachedResource& resource)
74 : m_cachedResourceLoader(cachedResourceLoader)
75 , m_resource(resource)
77 m_cachedResourceLoader.incrementRequestCount(m_resource);
80 SubresourceLoader::RequestCountTracker::~RequestCountTracker()
82 m_cachedResourceLoader.decrementRequestCount(m_resource);
85 SubresourceLoader::SubresourceLoader(Frame& frame, CachedResource& resource, const ResourceLoaderOptions& options)
86 : ResourceLoader(frame, options)
87 , m_resource(&resource)
88 , m_state(Uninitialized)
89 , m_requestCountTracker(std::in_place, frame.document()->cachedResourceLoader(), resource)
92 subresourceLoaderCounter.increment();
94 #if ENABLE(CONTENT_EXTENSIONS)
95 m_resourceType = toResourceType(resource.type());
97 m_canCrossOriginRequestsAskUserForCredentials = resource.type() == CachedResource::Type::MainResource || frame.settings().allowCrossOriginSubresourcesToAskForCredentials();
100 SubresourceLoader::~SubresourceLoader()
102 ASSERT(m_state != Initialized);
103 ASSERT(reachedTerminalState());
105 subresourceLoaderCounter.decrement();
109 void SubresourceLoader::create(Frame& frame, CachedResource& resource, ResourceRequest&& request, const ResourceLoaderOptions& options, CompletionHandler<void(RefPtr<SubresourceLoader>&&)>&& completionHandler)
111 auto subloader(adoptRef(*new SubresourceLoader(frame, resource, options)));
113 if (!IOSApplication::isWebProcess()) {
114 // On iOS, do not invoke synchronous resource load delegates while resource load scheduling
115 // is disabled to avoid re-entering style selection from a different thread (see <rdar://problem/9121719>).
116 // FIXME: This should be fixed for all ports in <https://bugs.webkit.org/show_bug.cgi?id=56647>.
117 subloader->m_iOSOriginalRequest = request;
118 return completionHandler(WTFMove(subloader));
121 subloader->init(WTFMove(request), [subloader = subloader.copyRef(), completionHandler = WTFMove(completionHandler)] (bool initialized) mutable {
123 return completionHandler(nullptr);
124 completionHandler(WTFMove(subloader));
129 void SubresourceLoader::startLoading()
131 // FIXME: this should probably be removed.
132 ASSERT(!IOSApplication::isWebProcess());
133 init(ResourceRequest(m_iOSOriginalRequest), [this, protectedThis = makeRef(*this)] (bool success) {
136 m_iOSOriginalRequest = ResourceRequest();
142 CachedResource* SubresourceLoader::cachedResource()
147 void SubresourceLoader::cancelIfNotFinishing()
149 if (m_state != Initialized)
152 ResourceLoader::cancel();
155 void SubresourceLoader::init(ResourceRequest&& request, CompletionHandler<void(bool)>&& completionHandler)
157 ResourceLoader::init(WTFMove(request), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)] (bool initialized) mutable {
159 return completionHandler(false);
160 ASSERT(!reachedTerminalState());
161 m_state = Initialized;
162 m_documentLoader->addSubresourceLoader(this);
163 m_origin = m_resource->origin();
164 completionHandler(true);
168 bool SubresourceLoader::isSubresourceLoader() const
173 void SubresourceLoader::willSendRequestInternal(ResourceRequest&& newRequest, const ResourceResponse& redirectResponse, CompletionHandler<void(ResourceRequest&&)>&& completionHandler)
175 // Store the previous URL because the call to ResourceLoader::willSendRequest will modify it.
176 URL previousURL = request().url();
177 Ref<SubresourceLoader> protectedThis(*this);
179 if (!newRequest.url().isValid()) {
180 cancel(cannotShowURLError());
181 return completionHandler(WTFMove(newRequest));
184 if (newRequest.requester() != ResourceRequestBase::Requester::Main) {
185 tracePoint(SubresourceLoadWillStart);
186 ResourceLoadObserver::shared().logSubresourceLoading(m_frame.get(), newRequest, redirectResponse);
189 auto continueWillSendRequest = [this, protectedThis = makeRef(*this), redirectResponse] (CompletionHandler<void(ResourceRequest&&)>&& completionHandler, ResourceRequest&& newRequest) mutable {
190 if (newRequest.isNull() || reachedTerminalState())
191 return completionHandler(WTFMove(newRequest));
193 ResourceLoader::willSendRequestInternal(WTFMove(newRequest), redirectResponse, [this, protectedThis = WTFMove(protectedThis), completionHandler = WTFMove(completionHandler), redirectResponse] (ResourceRequest&& request) mutable {
194 if (reachedTerminalState())
195 return completionHandler(WTFMove(request));
197 if (request.isNull()) {
199 return completionHandler(WTFMove(request));
202 if (m_resource->type() == CachedResource::Type::MainResource && !redirectResponse.isNull())
203 m_documentLoader->willContinueMainResourceLoadAfterRedirect(request);
204 completionHandler(WTFMove(request));
208 ASSERT(!newRequest.isNull());
209 if (!redirectResponse.isNull()) {
210 if (options().redirect != FetchOptions::Redirect::Follow) {
211 if (options().redirect == FetchOptions::Redirect::Error) {
212 ResourceError error { errorDomainWebKitInternal, 0, request().url(), makeString("Not allowed to follow a redirection while loading ", request().url().string()), ResourceError::Type::AccessControl };
214 if (m_frame && m_frame->document())
215 m_frame->document()->addConsoleMessage(MessageSource::Security, MessageLevel::Error, error.localizedDescription());
218 return completionHandler(WTFMove(newRequest));
221 ResourceResponse opaqueRedirectedResponse = redirectResponse;
222 opaqueRedirectedResponse.setType(ResourceResponse::Type::Opaqueredirect);
223 opaqueRedirectedResponse.setTainting(ResourceResponse::Tainting::Opaqueredirect);
224 m_resource->responseReceived(opaqueRedirectedResponse);
225 if (reachedTerminalState())
228 NetworkLoadMetrics emptyMetrics;
229 didFinishLoading(emptyMetrics);
230 return completionHandler(WTFMove(newRequest));
231 } else if (m_redirectCount++ >= options().maxRedirectCount) {
232 cancel(ResourceError(String(), 0, request().url(), "Too many redirections"_s, ResourceError::Type::General));
233 return completionHandler(WTFMove(newRequest));
236 // CachedResources are keyed off their original request URL.
237 // Requesting the same original URL a second time can redirect to a unique second resource.
238 // Therefore, if a redirect to a different destination URL occurs, we should no longer consider this a revalidation of the first resource.
239 // Doing so would have us reusing the resource from the first request if the second request's revalidation succeeds.
240 if (newRequest.isConditional() && m_resource->resourceToRevalidate() && newRequest.url() != m_resource->resourceToRevalidate()->response().url()) {
241 newRequest.makeUnconditional();
242 MemoryCache::singleton().revalidationFailed(*m_resource);
243 if (m_frame && m_frame->page())
244 m_frame->page()->diagnosticLoggingClient().logDiagnosticMessageWithResult(DiagnosticLoggingKeys::cachedResourceRevalidationKey(), emptyString(), DiagnosticLoggingResultFail, ShouldSample::Yes);
247 if (!m_documentLoader->cachedResourceLoader().updateRequestAfterRedirection(m_resource->type(), newRequest, options())) {
249 return completionHandler(WTFMove(newRequest));
252 String errorDescription;
253 if (!checkRedirectionCrossOriginAccessControl(request(), redirectResponse, newRequest, errorDescription)) {
254 String errorMessage = "Cross-origin redirection to " + newRequest.url().string() + " denied by Cross-Origin Resource Sharing policy: " + errorDescription;
255 if (m_frame && m_frame->document())
256 m_frame->document()->addConsoleMessage(MessageSource::Security, MessageLevel::Error, errorMessage);
257 cancel(ResourceError(String(), 0, request().url(), errorMessage, ResourceError::Type::AccessControl));
258 return completionHandler(WTFMove(newRequest));
261 if (m_resource->isImage() && m_documentLoader->cachedResourceLoader().shouldDeferImageLoad(newRequest.url())) {
263 return completionHandler(WTFMove(newRequest));
265 m_loadTiming.addRedirect(redirectResponse.url(), newRequest.url());
266 m_resource->redirectReceived(WTFMove(newRequest), redirectResponse, [completionHandler = WTFMove(completionHandler), continueWillSendRequest = WTFMove(continueWillSendRequest)] (ResourceRequest&& request) mutable {
267 continueWillSendRequest(WTFMove(completionHandler), WTFMove(request));
272 continueWillSendRequest(WTFMove(completionHandler), WTFMove(newRequest));
275 void SubresourceLoader::didSendData(unsigned long long bytesSent, unsigned long long totalBytesToBeSent)
277 ASSERT(m_state == Initialized);
278 Ref<SubresourceLoader> protectedThis(*this);
279 m_resource->didSendData(bytesSent, totalBytesToBeSent);
284 bool SubresourceLoader::shouldCreatePreviewLoaderForResponse(const ResourceResponse& response) const
286 if (m_resource->type() != CachedResource::Type::MainResource)
292 return PreviewLoader::shouldCreateForMIMEType(response.mimeType());
297 void SubresourceLoader::didReceiveResponse(const ResourceResponse& response, CompletionHandler<void()>&& policyCompletionHandler)
299 ASSERT(!response.isNull());
300 ASSERT(m_state == Initialized);
302 CompletionHandlerCallingScope completionHandlerCaller(WTFMove(policyCompletionHandler));
305 if (shouldCreatePreviewLoaderForResponse(response)) {
306 m_previewLoader = PreviewLoader::create(*this, response);
310 #if ENABLE(SERVICE_WORKER)
311 // Implementing step 10 of https://fetch.spec.whatwg.org/#main-fetch for service worker responses.
312 if (response.source() == ResourceResponse::Source::ServiceWorker && response.url() != request().url()) {
313 auto& loader = m_documentLoader->cachedResourceLoader();
314 if (!loader.allowedByContentSecurityPolicy(m_resource->type(), response.url(), options(), ContentSecurityPolicy::RedirectResponseReceived::Yes)) {
315 cancel(ResourceError({ }, 0, response.url(), { }, ResourceError::Type::General));
321 // We want redirect responses to be processed through willSendRequestInternal.
322 // The only exception is redirection with no Location headers. Or in rare circumstances,
323 // cases of too many redirects from CFNetwork (<rdar://problem/30610988>).
325 ASSERT(response.httpStatusCode() < 300 || response.httpStatusCode() >= 400 || response.httpStatusCode() == 304 || !response.httpHeaderField(HTTPHeaderName::Location));
328 // Reference the object in this method since the additional processing can do
329 // anything including removing the last reference to this object; one example of this is 3266216.
330 Ref<SubresourceLoader> protectedThis(*this);
332 if (shouldIncludeCertificateInfo())
333 response.includeCertificateInfo();
335 if (m_resource->resourceToRevalidate()) {
336 if (response.httpStatusCode() == 304) {
337 // 304 Not modified / Use local copy
338 // Existing resource is ok, just use it updating the expiration time.
339 ResourceResponse revalidationResponse = response;
340 revalidationResponse.setSource(ResourceResponse::Source::MemoryCacheAfterValidation);
341 m_resource->setResponse(revalidationResponse);
342 MemoryCache::singleton().revalidationSucceeded(*m_resource, revalidationResponse);
343 if (m_frame && m_frame->page())
344 m_frame->page()->diagnosticLoggingClient().logDiagnosticMessageWithResult(DiagnosticLoggingKeys::cachedResourceRevalidationKey(), emptyString(), DiagnosticLoggingResultPass, ShouldSample::Yes);
345 if (!reachedTerminalState())
346 ResourceLoader::didReceiveResponse(revalidationResponse, [completionHandlerCaller = WTFMove(completionHandlerCaller)] { });
349 // Did not get 304 response, continue as a regular resource load.
350 MemoryCache::singleton().revalidationFailed(*m_resource);
351 if (m_frame && m_frame->page())
352 m_frame->page()->diagnosticLoggingClient().logDiagnosticMessageWithResult(DiagnosticLoggingKeys::cachedResourceRevalidationKey(), emptyString(), DiagnosticLoggingResultFail, ShouldSample::Yes);
355 String errorDescription;
356 if (!checkResponseCrossOriginAccessControl(response, errorDescription)) {
357 if (m_frame && m_frame->document())
358 m_frame->document()->addConsoleMessage(MessageSource::Security, MessageLevel::Error, errorDescription);
359 cancel(ResourceError(String(), 0, request().url(), errorDescription, ResourceError::Type::AccessControl));
363 m_resource->responseReceived(response);
364 if (reachedTerminalState())
367 bool isResponseMultipart = response.isMultipart();
368 if (options().mode != FetchOptions::Mode::Navigate)
369 LinkLoader::loadLinksFromHeader(response.httpHeaderField(HTTPHeaderName::Link), m_documentLoader->url(), *m_frame->document(), LinkLoader::MediaAttributeCheck::SkipMediaAttributeCheck);
370 ResourceLoader::didReceiveResponse(response, [this, protectedThis = WTFMove(protectedThis), isResponseMultipart, completionHandlerCaller = WTFMove(completionHandlerCaller)]() mutable {
371 if (reachedTerminalState())
374 // FIXME: Main resources have a different set of rules for multipart than images do.
375 // Hopefully we can merge those 2 paths.
376 if (isResponseMultipart && m_resource->type() != CachedResource::Type::MainResource) {
377 m_loadingMultipartContent = true;
379 // We don't count multiParts in a CachedResourceLoader's request count
380 m_requestCountTracker = std::nullopt;
381 if (!m_resource->isImage()) {
387 auto* buffer = resourceData();
388 if (m_loadingMultipartContent && buffer && buffer->size()) {
389 // The resource data will change as the next part is loaded, so we need to make a copy.
390 m_resource->finishLoading(buffer->copy().ptr());
392 // Since a subresource loader does not load multipart sections progressively, data was delivered to the loader all at once.
393 // After the first multipart section is complete, signal to delegates that this load is "finished"
394 NetworkLoadMetrics emptyMetrics;
395 m_documentLoader->subresourceLoaderFinishedLoadingOnePart(this);
396 didFinishLoadingOnePart(emptyMetrics);
399 checkForHTTPStatusCodeError();
401 if (m_inAsyncResponsePolicyCheck)
402 m_policyForResponseCompletionHandler = completionHandlerCaller.release();
406 void SubresourceLoader::didReceiveResponsePolicy()
408 ASSERT(m_inAsyncResponsePolicyCheck);
409 m_inAsyncResponsePolicyCheck = false;
410 if (auto completionHandler = WTFMove(m_policyForResponseCompletionHandler))
414 void SubresourceLoader::didReceiveData(const char* data, unsigned length, long long encodedDataLength, DataPayloadType dataPayloadType)
417 if (auto previewLoader = m_previewLoader.get()) {
418 if (previewLoader->didReceiveData(data, length))
423 didReceiveDataOrBuffer(data, length, nullptr, encodedDataLength, dataPayloadType);
426 void SubresourceLoader::didReceiveBuffer(Ref<SharedBuffer>&& buffer, long long encodedDataLength, DataPayloadType dataPayloadType)
429 if (auto previewLoader = m_previewLoader.get()) {
430 if (previewLoader->didReceiveBuffer(buffer.get()))
435 didReceiveDataOrBuffer(nullptr, 0, WTFMove(buffer), encodedDataLength, dataPayloadType);
438 void SubresourceLoader::didReceiveDataOrBuffer(const char* data, int length, RefPtr<SharedBuffer>&& buffer, long long encodedDataLength, DataPayloadType dataPayloadType)
442 if (m_resource->response().httpStatusCode() >= 400 && !m_resource->shouldIgnoreHTTPStatusCodeErrors())
444 ASSERT(!m_resource->resourceToRevalidate());
445 ASSERT(!m_resource->errorOccurred());
446 ASSERT(m_state == Initialized);
447 // Reference the object in this method since the additional processing can do
448 // anything including removing the last reference to this object; one example of this is 3266216.
449 Ref<SubresourceLoader> protectedThis(*this);
451 ResourceLoader::didReceiveDataOrBuffer(data, length, buffer.copyRef(), encodedDataLength, dataPayloadType);
453 if (!m_loadingMultipartContent) {
454 if (auto* resourceData = this->resourceData())
455 m_resource->updateBuffer(*resourceData);
457 m_resource->updateData(buffer ? buffer->data() : data, buffer ? buffer->size() : length);
461 bool SubresourceLoader::checkForHTTPStatusCodeError()
463 if (m_resource->response().httpStatusCode() < 400 || m_resource->shouldIgnoreHTTPStatusCodeErrors())
467 m_resource->error(CachedResource::LoadError);
472 static void logResourceLoaded(Frame* frame, CachedResource::Type type)
474 if (!frame || !frame->page())
479 case CachedResource::Type::MainResource:
480 resourceType = DiagnosticLoggingKeys::mainResourceKey();
482 case CachedResource::Type::ImageResource:
483 resourceType = DiagnosticLoggingKeys::imageKey();
486 case CachedResource::Type::XSLStyleSheet:
488 case CachedResource::Type::CSSStyleSheet:
489 resourceType = DiagnosticLoggingKeys::styleSheetKey();
491 case CachedResource::Type::Script:
492 resourceType = DiagnosticLoggingKeys::scriptKey();
494 case CachedResource::Type::FontResource:
495 #if ENABLE(SVG_FONTS)
496 case CachedResource::Type::SVGFontResource:
498 resourceType = DiagnosticLoggingKeys::fontKey();
500 case CachedResource::Type::Beacon:
501 ASSERT_NOT_REACHED();
503 case CachedResource::Type::MediaResource:
504 case CachedResource::Type::Icon:
505 case CachedResource::Type::RawResource:
506 resourceType = DiagnosticLoggingKeys::rawKey();
508 case CachedResource::Type::SVGDocumentResource:
509 resourceType = DiagnosticLoggingKeys::svgDocumentKey();
511 #if ENABLE(APPLICATION_MANIFEST)
512 case CachedResource::Type::ApplicationManifest:
513 resourceType = DiagnosticLoggingKeys::applicationManifestKey();
516 case CachedResource::Type::LinkPrefetch:
517 #if ENABLE(VIDEO_TRACK)
518 case CachedResource::Type::TextTrackResource:
520 resourceType = DiagnosticLoggingKeys::otherKey();
524 frame->page()->diagnosticLoggingClient().logDiagnosticMessage(DiagnosticLoggingKeys::resourceLoadedKey(), resourceType, ShouldSample::Yes);
527 bool SubresourceLoader::checkResponseCrossOriginAccessControl(const ResourceResponse& response, String& errorDescription)
529 if (!m_resource->isCrossOrigin() || options().mode != FetchOptions::Mode::Cors)
532 #if ENABLE(SERVICE_WORKER)
533 if (response.source() == ResourceResponse::Source::ServiceWorker)
534 return response.tainting() != ResourceResponse::Tainting::Opaque;
539 return passesAccessControlCheck(response, options().credentials == FetchOptions::Credentials::Include ? StoredCredentialsPolicy::Use : StoredCredentialsPolicy::DoNotUse, *m_origin, errorDescription);
542 bool SubresourceLoader::checkRedirectionCrossOriginAccessControl(const ResourceRequest& previousRequest, const ResourceResponse& redirectResponse, ResourceRequest& newRequest, String& errorMessage)
544 bool crossOriginFlag = m_resource->isCrossOrigin();
545 bool isNextRequestCrossOrigin = m_origin && !m_origin->canRequest(newRequest.url());
547 if (isNextRequestCrossOrigin)
548 m_resource->setCrossOrigin();
550 ASSERT(options().mode != FetchOptions::Mode::SameOrigin || !m_resource->isCrossOrigin());
552 if (options().mode != FetchOptions::Mode::Cors)
555 // Implementing https://fetch.spec.whatwg.org/#concept-http-redirect-fetch step 8 & 9.
556 if (m_resource->isCrossOrigin() && !isValidCrossOriginRedirectionURL(newRequest.url())) {
557 errorMessage = "URL is either a non-HTTP URL or contains credentials."_s;
562 if (crossOriginFlag && !passesAccessControlCheck(redirectResponse, options().storedCredentialsPolicy, *m_origin, errorMessage))
565 bool redirectingToNewOrigin = false;
566 if (m_resource->isCrossOrigin()) {
567 if (!crossOriginFlag && isNextRequestCrossOrigin)
568 redirectingToNewOrigin = true;
570 redirectingToNewOrigin = !protocolHostAndPortAreEqual(previousRequest.url(), newRequest.url());
573 // Implementing https://fetch.spec.whatwg.org/#concept-http-redirect-fetch step 10.
574 if (crossOriginFlag && redirectingToNewOrigin)
575 m_origin = SecurityOrigin::createUnique();
577 updateReferrerPolicy(redirectResponse.httpHeaderField(HTTPHeaderName::ReferrerPolicy));
579 if (redirectingToNewOrigin) {
580 cleanHTTPRequestHeadersForAccessControl(newRequest, options().httpHeadersToKeep);
581 updateRequestForAccessControl(newRequest, *m_origin, options().storedCredentialsPolicy);
584 updateRequestReferrer(newRequest, referrerPolicy(), previousRequest.httpReferrer());
589 void SubresourceLoader::updateReferrerPolicy(const String& referrerPolicyValue)
591 if (auto referrerPolicy = parseReferrerPolicy(referrerPolicyValue, ReferrerPolicySource::HTTPHeader)) {
592 ASSERT(*referrerPolicy != ReferrerPolicy::EmptyString);
593 setReferrerPolicy(*referrerPolicy);
597 void SubresourceLoader::didFinishLoading(const NetworkLoadMetrics& networkLoadMetrics)
600 if (auto previewLoader = m_previewLoader.get()) {
601 if (previewLoader->didFinishLoading())
606 if (m_state != Initialized)
608 ASSERT(!reachedTerminalState());
609 ASSERT(!m_resource->resourceToRevalidate());
610 // FIXME (129394): We should cancel the load when a decode error occurs instead of continuing the load to completion.
611 ASSERT(!m_resource->errorOccurred() || m_resource->status() == CachedResource::DecodeError || !m_resource->isLoading());
612 LOG(ResourceLoading, "Received '%s'.", m_resource->url().string().latin1().data());
613 logResourceLoaded(m_frame.get(), m_resource->type());
615 Ref<SubresourceLoader> protectedThis(*this);
616 CachedResourceHandle<CachedResource> protectResource(m_resource);
618 // FIXME: Remove this with deprecatedNetworkLoadMetrics.
619 m_loadTiming.setResponseEnd(MonotonicTime::now());
621 if (networkLoadMetrics.isComplete())
622 reportResourceTiming(networkLoadMetrics);
624 // This is the legacy path for platforms (and ResourceHandle paths) that do not provide
625 // complete load metrics in didFinishLoad. In those cases, fall back to the possibility
626 // that they populated partial load timing information on the ResourceResponse.
627 reportResourceTiming(m_resource->response().deprecatedNetworkLoadMetrics());
630 if (m_resource->type() != CachedResource::Type::MainResource)
631 tracePoint(SubresourceLoadDidEnd);
634 m_resource->finishLoading(resourceData());
639 m_resource->finish();
640 ASSERT(!reachedTerminalState());
641 didFinishLoadingOnePart(networkLoadMetrics);
642 notifyDone(LoadCompletionType::Finish);
644 if (reachedTerminalState())
649 void SubresourceLoader::didFail(const ResourceError& error)
653 if (auto previewLoader = m_previewLoader.get())
654 previewLoader->didFail();
657 if (m_state != Initialized)
659 ASSERT(!reachedTerminalState());
660 LOG(ResourceLoading, "Failed to load '%s'.\n", m_resource->url().string().latin1().data());
661 if (m_frame->document() && error.isAccessControl())
662 m_frame->document()->addConsoleMessage(MessageSource::Security, MessageLevel::Error, error.localizedDescription());
665 Ref<SubresourceLoader> protectedThis(*this);
666 CachedResourceHandle<CachedResource> protectResource(m_resource);
669 if (m_resource->type() != CachedResource::Type::MainResource)
670 tracePoint(SubresourceLoadDidEnd);
672 if (m_resource->resourceToRevalidate())
673 MemoryCache::singleton().revalidationFailed(*m_resource);
674 m_resource->setResourceError(error);
675 if (!m_resource->isPreloaded())
676 MemoryCache::singleton().remove(*m_resource);
677 m_resource->error(CachedResource::LoadError);
678 cleanupForError(error);
679 notifyDone(LoadCompletionType::Cancel);
680 if (reachedTerminalState())
685 void SubresourceLoader::willCancel(const ResourceError& error)
688 // Since we defer initialization to scheduling time on iOS but
689 // CachedResourceLoader stores resources in the memory cache immediately,
690 // m_resource might be cached despite its loader not being initialized.
691 if (m_state != Initialized && m_state != Uninitialized)
693 if (m_state != Initialized)
696 ASSERT(!reachedTerminalState());
697 LOG(ResourceLoading, "Cancelled load of '%s'.\n", m_resource->url().string().latin1().data());
699 Ref<SubresourceLoader> protectedThis(*this);
701 m_state = m_state == Uninitialized ? CancelledWhileInitializing : Finishing;
705 auto& memoryCache = MemoryCache::singleton();
706 if (m_resource->resourceToRevalidate())
707 memoryCache.revalidationFailed(*m_resource);
708 m_resource->setResourceError(error);
709 memoryCache.remove(*m_resource);
712 void SubresourceLoader::didCancel(const ResourceError&)
714 if (m_state == Uninitialized)
717 if (m_resource->type() != CachedResource::Type::MainResource)
718 tracePoint(SubresourceLoadDidEnd);
720 m_resource->cancelLoad();
721 notifyDone(LoadCompletionType::Cancel);
724 void SubresourceLoader::didRetrieveDerivedDataFromCache(const String& type, SharedBuffer& buffer)
726 if (m_state != Initialized)
728 m_resource->didRetrieveDerivedDataFromCache(type, buffer);
731 void SubresourceLoader::notifyDone(LoadCompletionType type)
733 if (reachedTerminalState())
736 m_requestCountTracker = std::nullopt;
737 bool shouldPerformPostLoadActions = true;
739 if (m_state == CancelledWhileInitializing)
740 shouldPerformPostLoadActions = false;
742 m_documentLoader->cachedResourceLoader().loadDone(type, shouldPerformPostLoadActions);
743 if (reachedTerminalState())
745 m_documentLoader->removeSubresourceLoader(type, this);
748 void SubresourceLoader::releaseResources()
750 ASSERT(!reachedTerminalState());
752 if (m_state != Uninitialized && m_state != CancelledWhileInitializing)
754 if (m_state != Uninitialized)
756 m_resource->clearLoader();
757 m_resource = nullptr;
758 ResourceLoader::releaseResources();
761 void SubresourceLoader::reportResourceTiming(const NetworkLoadMetrics& networkLoadMetrics)
763 if (!RuntimeEnabledFeatures::sharedFeatures().resourceTimingEnabled())
766 if (!ResourceTimingInformation::shouldAddResourceTiming(*m_resource))
769 Document* document = m_documentLoader->cachedResourceLoader().document();
773 SecurityOrigin& origin = m_origin ? *m_origin : document->securityOrigin();
774 auto resourceTiming = ResourceTiming::fromLoad(*m_resource, m_resource->initiatorName(), m_loadTiming, networkLoadMetrics, origin);
776 // Worker resources loaded here are all CachedRawResources loaded through WorkerThreadableLoader.
777 // Pass the ResourceTiming information on so that WorkerThreadableLoader may add them to the
778 // Worker's Performance object.
779 if (options().initiatorContext == InitiatorContext::Worker) {
781 ASSERT(is<CachedRawResource>(m_resource));
782 downcast<CachedRawResource>(*m_resource).finishedTimingForWorkerLoad(WTFMove(resourceTiming));
786 ASSERT(options().initiatorContext == InitiatorContext::Document);
787 m_documentLoader->cachedResourceLoader().resourceTimingInformation().addResourceTiming(*m_resource, *document, WTFMove(resourceTiming));
790 const HTTPHeaderMap* SubresourceLoader::originalHeaders() const
792 return (m_resource && m_resource->originalRequest()) ? &m_resource->originalRequest()->httpHeaderFields() : nullptr;