Shrink various loading-related enums to shrink CachedResource
[WebKit-https.git] / Source / WebCore / loader / SubresourceLoader.cpp
1 /*
2  * Copyright (C) 2006-2017 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *
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.
16  *
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.
27  */
28
29 #include "config.h"
30 #include "SubresourceLoader.h"
31
32 #include "CachedRawResource.h"
33 #include "CachedResourceLoader.h"
34 #include "CrossOriginAccessControl.h"
35 #include "DiagnosticLoggingClient.h"
36 #include "DiagnosticLoggingKeys.h"
37 #include "Document.h"
38 #include "DocumentLoader.h"
39 #include "Frame.h"
40 #include "FrameLoader.h"
41 #include "HTTPParsers.h"
42 #include "LinkLoader.h"
43 #include "Logging.h"
44 #include "MemoryCache.h"
45 #include "Page.h"
46 #include "ResourceLoadObserver.h"
47 #include "ResourceTiming.h"
48 #include "RuntimeEnabledFeatures.h"
49 #include "Settings.h"
50 #include <wtf/CompletionHandler.h>
51 #include <wtf/Ref.h>
52 #include <wtf/RefCountedLeakCounter.h>
53 #include <wtf/StdLibExtras.h>
54 #include <wtf/SystemTracing.h>
55 #include <wtf/text/CString.h>
56
57 #if PLATFORM(IOS)
58 #include <RuntimeApplicationChecks.h>
59 #endif
60
61 #if ENABLE(CONTENT_EXTENSIONS)
62 #include "ResourceLoadInfo.h"
63 #endif
64
65 #if USE(QUICK_LOOK)
66 #include "PreviewLoader.h"
67 #endif
68
69 namespace WebCore {
70
71 DEFINE_DEBUG_ONLY_GLOBAL(WTF::RefCountedLeakCounter, subresourceLoaderCounter, ("SubresourceLoader"));
72
73 SubresourceLoader::RequestCountTracker::RequestCountTracker(CachedResourceLoader& cachedResourceLoader, const CachedResource& resource)
74     : m_cachedResourceLoader(cachedResourceLoader)
75     , m_resource(resource)
76 {
77     m_cachedResourceLoader.incrementRequestCount(m_resource);
78 }
79
80 SubresourceLoader::RequestCountTracker::~RequestCountTracker()
81 {
82     m_cachedResourceLoader.decrementRequestCount(m_resource);
83 }
84
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)
90 {
91 #ifndef NDEBUG
92     subresourceLoaderCounter.increment();
93 #endif
94 #if ENABLE(CONTENT_EXTENSIONS)
95     m_resourceType = toResourceType(resource.type());
96 #endif
97     m_canCrossOriginRequestsAskUserForCredentials = resource.type() == CachedResource::Type::MainResource || frame.settings().allowCrossOriginSubresourcesToAskForCredentials();
98 }
99
100 SubresourceLoader::~SubresourceLoader()
101 {
102     ASSERT(m_state != Initialized);
103     ASSERT(reachedTerminalState());
104 #ifndef NDEBUG
105     subresourceLoaderCounter.decrement();
106 #endif
107 }
108
109 void SubresourceLoader::create(Frame& frame, CachedResource& resource, ResourceRequest&& request, const ResourceLoaderOptions& options, CompletionHandler<void(RefPtr<SubresourceLoader>&&)>&& completionHandler)
110 {
111     auto subloader(adoptRef(*new SubresourceLoader(frame, resource, options)));
112 #if PLATFORM(IOS)
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));
119     }
120 #endif
121     subloader->init(WTFMove(request), [subloader = subloader.copyRef(), completionHandler = WTFMove(completionHandler)] (bool initialized) mutable {
122         if (!initialized)
123             return completionHandler(nullptr);
124         completionHandler(WTFMove(subloader));
125     });
126 }
127     
128 #if PLATFORM(IOS)
129 void SubresourceLoader::startLoading()
130 {
131     // FIXME: this should probably be removed.
132     ASSERT(!IOSApplication::isWebProcess());
133     init(ResourceRequest(m_iOSOriginalRequest), [this, protectedThis = makeRef(*this)] (bool success) {
134         if (!success)
135             return;
136         m_iOSOriginalRequest = ResourceRequest();
137         start();
138     });
139 }
140 #endif
141
142 CachedResource* SubresourceLoader::cachedResource()
143 {
144     return m_resource;
145 }
146
147 void SubresourceLoader::cancelIfNotFinishing()
148 {
149     if (m_state != Initialized)
150         return;
151
152     ResourceLoader::cancel();
153 }
154
155 void SubresourceLoader::init(ResourceRequest&& request, CompletionHandler<void(bool)>&& completionHandler)
156 {
157     ResourceLoader::init(WTFMove(request), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)] (bool initialized) mutable {
158         if (!initialized)
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);
165     });
166 }
167
168 bool SubresourceLoader::isSubresourceLoader() const
169 {
170     return true;
171 }
172
173 void SubresourceLoader::willSendRequestInternal(ResourceRequest&& newRequest, const ResourceResponse& redirectResponse, CompletionHandler<void(ResourceRequest&&)>&& completionHandler)
174 {
175     // Store the previous URL because the call to ResourceLoader::willSendRequest will modify it.
176     URL previousURL = request().url();
177     Ref<SubresourceLoader> protectedThis(*this);
178
179     if (!newRequest.url().isValid()) {
180         cancel(cannotShowURLError());
181         return completionHandler(WTFMove(newRequest));
182     }
183
184     if (newRequest.requester() != ResourceRequestBase::Requester::Main) {
185         tracePoint(SubresourceLoadWillStart);
186         ResourceLoadObserver::shared().logSubresourceLoading(m_frame.get(), newRequest, redirectResponse);
187     }
188
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));
192
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));
196
197             if (request.isNull()) {
198                 cancel();
199                 return completionHandler(WTFMove(request));
200             }
201
202             if (m_resource->type() == CachedResource::Type::MainResource && !redirectResponse.isNull())
203                 m_documentLoader->willContinueMainResourceLoadAfterRedirect(request);
204             completionHandler(WTFMove(request));
205         });
206     };
207
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 };
213
214                 if (m_frame && m_frame->document())
215                     m_frame->document()->addConsoleMessage(MessageSource::Security, MessageLevel::Error, error.localizedDescription());
216
217                 cancel(error);
218                 return completionHandler(WTFMove(newRequest));
219             }
220
221             ResourceResponse opaqueRedirectedResponse = redirectResponse;
222             opaqueRedirectedResponse.setType(ResourceResponse::Type::Opaqueredirect);
223             opaqueRedirectedResponse.setTainting(ResourceResponse::Tainting::Opaqueredirect);
224             m_resource->responseReceived(opaqueRedirectedResponse);
225             if (reachedTerminalState())
226                 return;
227
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));
234         }
235
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);
245         }
246
247         if (!m_documentLoader->cachedResourceLoader().updateRequestAfterRedirection(m_resource->type(), newRequest, options())) {
248             cancel();
249             return completionHandler(WTFMove(newRequest));
250         }
251
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));
259         }
260
261         if (m_resource->isImage() && m_documentLoader->cachedResourceLoader().shouldDeferImageLoad(newRequest.url())) {
262             cancel();
263             return completionHandler(WTFMove(newRequest));
264         }
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));
268         });
269         return;
270     }
271
272     continueWillSendRequest(WTFMove(completionHandler), WTFMove(newRequest));
273 }
274
275 void SubresourceLoader::didSendData(unsigned long long bytesSent, unsigned long long totalBytesToBeSent)
276 {
277     ASSERT(m_state == Initialized);
278     Ref<SubresourceLoader> protectedThis(*this);
279     m_resource->didSendData(bytesSent, totalBytesToBeSent);
280 }
281
282 #if USE(QUICK_LOOK)
283
284 bool SubresourceLoader::shouldCreatePreviewLoaderForResponse(const ResourceResponse& response) const
285 {
286     if (m_resource->type() != CachedResource::Type::MainResource)
287         return false;
288
289     if (m_previewLoader)
290         return false;
291
292     return PreviewLoader::shouldCreateForMIMEType(response.mimeType());
293 }
294
295 #endif
296
297 void SubresourceLoader::didReceiveResponse(const ResourceResponse& response, CompletionHandler<void()>&& policyCompletionHandler)
298 {
299     ASSERT(!response.isNull());
300     ASSERT(m_state == Initialized);
301
302     CompletionHandlerCallingScope completionHandlerCaller(WTFMove(policyCompletionHandler));
303
304 #if USE(QUICK_LOOK)
305     if (shouldCreatePreviewLoaderForResponse(response)) {
306         m_previewLoader = PreviewLoader::create(*this, response);
307         return;
308     }
309 #endif
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));
316             return;
317         }
318     }
319 #endif
320
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>).
324 #if !PLATFORM(COCOA)
325     ASSERT(response.httpStatusCode() < 300 || response.httpStatusCode() >= 400 || response.httpStatusCode() == 304 || !response.httpHeaderField(HTTPHeaderName::Location));
326 #endif
327
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);
331
332     if (shouldIncludeCertificateInfo())
333         response.includeCertificateInfo();
334
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)] { });
347             return;
348         }
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);
353     }
354
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));
360         return;
361     }
362
363     m_resource->responseReceived(response);
364     if (reachedTerminalState())
365         return;
366
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())
372             return;
373
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;
378
379             // We don't count multiParts in a CachedResourceLoader's request count
380             m_requestCountTracker = std::nullopt;
381             if (!m_resource->isImage()) {
382                 cancel();
383                 return;
384             }
385         }
386
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());
391             clearResourceData();
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);
397         }
398
399         checkForHTTPStatusCodeError();
400
401         if (m_inAsyncResponsePolicyCheck)
402             m_policyForResponseCompletionHandler = completionHandlerCaller.release();
403     });
404 }
405
406 void SubresourceLoader::didReceiveResponsePolicy()
407 {
408     ASSERT(m_inAsyncResponsePolicyCheck);
409     m_inAsyncResponsePolicyCheck = false;
410     if (auto completionHandler = WTFMove(m_policyForResponseCompletionHandler))
411         completionHandler();
412 }
413
414 void SubresourceLoader::didReceiveData(const char* data, unsigned length, long long encodedDataLength, DataPayloadType dataPayloadType)
415 {
416 #if USE(QUICK_LOOK)
417     if (auto previewLoader = m_previewLoader.get()) {
418         if (previewLoader->didReceiveData(data, length))
419             return;
420     }
421 #endif
422
423     didReceiveDataOrBuffer(data, length, nullptr, encodedDataLength, dataPayloadType);
424 }
425
426 void SubresourceLoader::didReceiveBuffer(Ref<SharedBuffer>&& buffer, long long encodedDataLength, DataPayloadType dataPayloadType)
427 {
428 #if USE(QUICK_LOOK)
429     if (auto previewLoader = m_previewLoader.get()) {
430         if (previewLoader->didReceiveBuffer(buffer.get()))
431             return;
432     }
433 #endif
434
435     didReceiveDataOrBuffer(nullptr, 0, WTFMove(buffer), encodedDataLength, dataPayloadType);
436 }
437
438 void SubresourceLoader::didReceiveDataOrBuffer(const char* data, int length, RefPtr<SharedBuffer>&& buffer, long long encodedDataLength, DataPayloadType dataPayloadType)
439 {
440     ASSERT(m_resource);
441
442     if (m_resource->response().httpStatusCode() >= 400 && !m_resource->shouldIgnoreHTTPStatusCodeErrors())
443         return;
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);
450
451     ResourceLoader::didReceiveDataOrBuffer(data, length, buffer.copyRef(), encodedDataLength, dataPayloadType);
452
453     if (!m_loadingMultipartContent) {
454         if (auto* resourceData = this->resourceData())
455             m_resource->updateBuffer(*resourceData);
456         else
457             m_resource->updateData(buffer ? buffer->data() : data, buffer ? buffer->size() : length);
458     }
459 }
460
461 bool SubresourceLoader::checkForHTTPStatusCodeError()
462 {
463     if (m_resource->response().httpStatusCode() < 400 || m_resource->shouldIgnoreHTTPStatusCodeErrors())
464         return false;
465
466     m_state = Finishing;
467     m_resource->error(CachedResource::LoadError);
468     cancel();
469     return true;
470 }
471
472 static void logResourceLoaded(Frame* frame, CachedResource::Type type)
473 {
474     if (!frame || !frame->page())
475         return;
476
477     String resourceType;
478     switch (type) {
479     case CachedResource::Type::MainResource:
480         resourceType = DiagnosticLoggingKeys::mainResourceKey();
481         break;
482     case CachedResource::Type::ImageResource:
483         resourceType = DiagnosticLoggingKeys::imageKey();
484         break;
485 #if ENABLE(XSLT)
486     case CachedResource::Type::XSLStyleSheet:
487 #endif
488     case CachedResource::Type::CSSStyleSheet:
489         resourceType = DiagnosticLoggingKeys::styleSheetKey();
490         break;
491     case CachedResource::Type::Script:
492         resourceType = DiagnosticLoggingKeys::scriptKey();
493         break;
494     case CachedResource::Type::FontResource:
495 #if ENABLE(SVG_FONTS)
496     case CachedResource::Type::SVGFontResource:
497 #endif
498         resourceType = DiagnosticLoggingKeys::fontKey();
499         break;
500     case CachedResource::Type::Beacon:
501         ASSERT_NOT_REACHED();
502         break;
503     case CachedResource::Type::MediaResource:
504     case CachedResource::Type::Icon:
505     case CachedResource::Type::RawResource:
506         resourceType = DiagnosticLoggingKeys::rawKey();
507         break;
508     case CachedResource::Type::SVGDocumentResource:
509         resourceType = DiagnosticLoggingKeys::svgDocumentKey();
510         break;
511 #if ENABLE(APPLICATION_MANIFEST)
512     case CachedResource::Type::ApplicationManifest:
513         resourceType = DiagnosticLoggingKeys::applicationManifestKey();
514         break;
515 #endif
516     case CachedResource::Type::LinkPrefetch:
517 #if ENABLE(VIDEO_TRACK)
518     case CachedResource::Type::TextTrackResource:
519 #endif
520         resourceType = DiagnosticLoggingKeys::otherKey();
521         break;
522     }
523     
524     frame->page()->diagnosticLoggingClient().logDiagnosticMessage(DiagnosticLoggingKeys::resourceLoadedKey(), resourceType, ShouldSample::Yes);
525 }
526
527 bool SubresourceLoader::checkResponseCrossOriginAccessControl(const ResourceResponse& response, String& errorDescription)
528 {
529     if (!m_resource->isCrossOrigin() || options().mode != FetchOptions::Mode::Cors)
530         return true;
531
532 #if ENABLE(SERVICE_WORKER)
533     if (response.source() == ResourceResponse::Source::ServiceWorker)
534         return response.tainting() != ResourceResponse::Tainting::Opaque;
535 #endif
536
537     ASSERT(m_origin);
538
539     return passesAccessControlCheck(response, options().credentials == FetchOptions::Credentials::Include ? StoredCredentialsPolicy::Use : StoredCredentialsPolicy::DoNotUse, *m_origin, errorDescription);
540 }
541
542 bool SubresourceLoader::checkRedirectionCrossOriginAccessControl(const ResourceRequest& previousRequest, const ResourceResponse& redirectResponse, ResourceRequest& newRequest, String& errorMessage)
543 {
544     bool crossOriginFlag = m_resource->isCrossOrigin();
545     bool isNextRequestCrossOrigin = m_origin && !m_origin->canRequest(newRequest.url());
546
547     if (isNextRequestCrossOrigin)
548         m_resource->setCrossOrigin();
549
550     ASSERT(options().mode != FetchOptions::Mode::SameOrigin || !m_resource->isCrossOrigin());
551
552     if (options().mode != FetchOptions::Mode::Cors)
553         return true;
554
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;
558         return false;
559     }
560
561     ASSERT(m_origin);
562     if (crossOriginFlag && !passesAccessControlCheck(redirectResponse, options().storedCredentialsPolicy, *m_origin, errorMessage))
563         return false;
564
565     bool redirectingToNewOrigin = false;
566     if (m_resource->isCrossOrigin()) {
567         if (!crossOriginFlag && isNextRequestCrossOrigin)
568             redirectingToNewOrigin = true;
569         else
570             redirectingToNewOrigin = !protocolHostAndPortAreEqual(previousRequest.url(), newRequest.url());
571     }
572
573     // Implementing https://fetch.spec.whatwg.org/#concept-http-redirect-fetch step 10.
574     if (crossOriginFlag && redirectingToNewOrigin)
575         m_origin = SecurityOrigin::createUnique();
576
577     updateReferrerPolicy(redirectResponse.httpHeaderField(HTTPHeaderName::ReferrerPolicy));
578     
579     if (redirectingToNewOrigin) {
580         cleanHTTPRequestHeadersForAccessControl(newRequest, options().httpHeadersToKeep);
581         updateRequestForAccessControl(newRequest, *m_origin, options().storedCredentialsPolicy);
582     }
583     
584     updateRequestReferrer(newRequest, referrerPolicy(), previousRequest.httpReferrer());
585
586     return true;
587 }
588
589 void SubresourceLoader::updateReferrerPolicy(const String& referrerPolicyValue)
590 {
591     if (auto referrerPolicy = parseReferrerPolicy(referrerPolicyValue, ReferrerPolicySource::HTTPHeader)) {
592         ASSERT(*referrerPolicy != ReferrerPolicy::EmptyString);
593         setReferrerPolicy(*referrerPolicy);
594     }
595 }
596
597 void SubresourceLoader::didFinishLoading(const NetworkLoadMetrics& networkLoadMetrics)
598 {
599 #if USE(QUICK_LOOK)
600     if (auto previewLoader = m_previewLoader.get()) {
601         if (previewLoader->didFinishLoading())
602             return;
603     }
604 #endif
605
606     if (m_state != Initialized)
607         return;
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());
614
615     Ref<SubresourceLoader> protectedThis(*this);
616     CachedResourceHandle<CachedResource> protectResource(m_resource);
617
618     // FIXME: Remove this with deprecatedNetworkLoadMetrics.
619     m_loadTiming.setResponseEnd(MonotonicTime::now());
620
621     if (networkLoadMetrics.isComplete())
622         reportResourceTiming(networkLoadMetrics);
623     else {
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());
628     }
629
630     if (m_resource->type() != CachedResource::Type::MainResource)
631         tracePoint(SubresourceLoadDidEnd);
632
633     m_state = Finishing;
634     m_resource->finishLoading(resourceData());
635
636     if (wasCancelled())
637         return;
638
639     m_resource->finish();
640     ASSERT(!reachedTerminalState());
641     didFinishLoadingOnePart(networkLoadMetrics);
642     notifyDone(LoadCompletionType::Finish);
643
644     if (reachedTerminalState())
645         return;
646     releaseResources();
647 }
648
649 void SubresourceLoader::didFail(const ResourceError& error)
650 {
651
652 #if USE(QUICK_LOOK)
653     if (auto previewLoader = m_previewLoader.get())
654         previewLoader->didFail();
655 #endif
656
657     if (m_state != Initialized)
658         return;
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());
663
664
665     Ref<SubresourceLoader> protectedThis(*this);
666     CachedResourceHandle<CachedResource> protectResource(m_resource);
667     m_state = Finishing;
668
669     if (m_resource->type() != CachedResource::Type::MainResource)
670         tracePoint(SubresourceLoadDidEnd);
671
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())
681         return;
682     releaseResources();
683 }
684
685 void SubresourceLoader::willCancel(const ResourceError& error)
686 {
687 #if PLATFORM(IOS)
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)
692 #else
693     if (m_state != Initialized)
694 #endif
695         return;
696     ASSERT(!reachedTerminalState());
697     LOG(ResourceLoading, "Cancelled load of '%s'.\n", m_resource->url().string().latin1().data());
698
699     Ref<SubresourceLoader> protectedThis(*this);
700 #if PLATFORM(IOS)
701     m_state = m_state == Uninitialized ? CancelledWhileInitializing : Finishing;
702 #else
703     m_state = Finishing;
704 #endif
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);
710 }
711
712 void SubresourceLoader::didCancel(const ResourceError&)
713 {
714     if (m_state == Uninitialized)
715         return;
716
717     if (m_resource->type() != CachedResource::Type::MainResource)
718         tracePoint(SubresourceLoadDidEnd);
719
720     m_resource->cancelLoad();
721     notifyDone(LoadCompletionType::Cancel);
722 }
723
724 void SubresourceLoader::didRetrieveDerivedDataFromCache(const String& type, SharedBuffer& buffer)
725 {
726     if (m_state != Initialized)
727         return;
728     m_resource->didRetrieveDerivedDataFromCache(type, buffer);
729 }
730
731 void SubresourceLoader::notifyDone(LoadCompletionType type)
732 {
733     if (reachedTerminalState())
734         return;
735
736     m_requestCountTracker = std::nullopt;
737     bool shouldPerformPostLoadActions = true;
738 #if PLATFORM(IOS)
739     if (m_state == CancelledWhileInitializing)
740         shouldPerformPostLoadActions = false;
741 #endif
742     m_documentLoader->cachedResourceLoader().loadDone(type, shouldPerformPostLoadActions);
743     if (reachedTerminalState())
744         return;
745     m_documentLoader->removeSubresourceLoader(type, this);
746 }
747
748 void SubresourceLoader::releaseResources()
749 {
750     ASSERT(!reachedTerminalState());
751 #if PLATFORM(IOS)
752     if (m_state != Uninitialized && m_state != CancelledWhileInitializing)
753 #else
754     if (m_state != Uninitialized)
755 #endif
756         m_resource->clearLoader();
757     m_resource = nullptr;
758     ResourceLoader::releaseResources();
759 }
760
761 void SubresourceLoader::reportResourceTiming(const NetworkLoadMetrics& networkLoadMetrics)
762 {
763     if (!RuntimeEnabledFeatures::sharedFeatures().resourceTimingEnabled())
764         return;
765
766     if (!ResourceTimingInformation::shouldAddResourceTiming(*m_resource))
767         return;
768
769     Document* document = m_documentLoader->cachedResourceLoader().document();
770     if (!document)
771         return;
772
773     SecurityOrigin& origin = m_origin ? *m_origin : document->securityOrigin();
774     auto resourceTiming = ResourceTiming::fromLoad(*m_resource, m_resource->initiatorName(), m_loadTiming, networkLoadMetrics, origin);
775
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) {
780         ASSERT(m_origin);
781         ASSERT(is<CachedRawResource>(m_resource));
782         downcast<CachedRawResource>(*m_resource).finishedTimingForWorkerLoad(WTFMove(resourceTiming));
783         return;
784     }
785
786     ASSERT(options().initiatorContext == InitiatorContext::Document);
787     m_documentLoader->cachedResourceLoader().resourceTimingInformation().addResourceTiming(*m_resource, *document, WTFMove(resourceTiming));
788 }
789
790 const HTTPHeaderMap* SubresourceLoader::originalHeaders() const
791 {
792     return (m_resource  && m_resource->originalRequest()) ? &m_resource->originalRequest()->httpHeaderFields() : nullptr;
793 }
794
795 }