34a8e16426ec1b45e1bfc25df9b815397af5d8de
[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 "Logging.h"
42 #include "MainFrame.h"
43 #include "MemoryCache.h"
44 #include "Page.h"
45 #include "ResourceLoadObserver.h"
46 #include "ResourceTiming.h"
47 #include "RuntimeEnabledFeatures.h"
48 #include <wtf/CompletionHandler.h>
49 #include <wtf/Ref.h>
50 #include <wtf/RefCountedLeakCounter.h>
51 #include <wtf/StdLibExtras.h>
52 #include <wtf/SystemTracing.h>
53 #include <wtf/text/CString.h>
54
55 #if PLATFORM(IOS)
56 #include <RuntimeApplicationChecks.h>
57 #endif
58
59 #if ENABLE(CONTENT_EXTENSIONS)
60 #include "ResourceLoadInfo.h"
61 #endif
62
63 #if USE(QUICK_LOOK)
64 #include "PreviewLoader.h"
65 #endif
66
67 namespace WebCore {
68
69 DEFINE_DEBUG_ONLY_GLOBAL(WTF::RefCountedLeakCounter, subresourceLoaderCounter, ("SubresourceLoader"));
70
71 SubresourceLoader::RequestCountTracker::RequestCountTracker(CachedResourceLoader& cachedResourceLoader, const CachedResource& resource)
72     : m_cachedResourceLoader(cachedResourceLoader)
73     , m_resource(resource)
74 {
75     m_cachedResourceLoader.incrementRequestCount(m_resource);
76 }
77
78 SubresourceLoader::RequestCountTracker::~RequestCountTracker()
79 {
80     m_cachedResourceLoader.decrementRequestCount(m_resource);
81 }
82
83 SubresourceLoader::SubresourceLoader(Frame& frame, CachedResource& resource, const ResourceLoaderOptions& options)
84     : ResourceLoader(frame, options)
85     , m_resource(&resource)
86     , m_state(Uninitialized)
87     , m_requestCountTracker(std::in_place, frame.document()->cachedResourceLoader(), resource)
88 {
89 #ifndef NDEBUG
90     subresourceLoaderCounter.increment();
91 #endif
92 #if ENABLE(CONTENT_EXTENSIONS)
93     m_resourceType = toResourceType(resource.type());
94 #endif
95 }
96
97 SubresourceLoader::~SubresourceLoader()
98 {
99     ASSERT(m_state != Initialized);
100     ASSERT(reachedTerminalState());
101 #ifndef NDEBUG
102     subresourceLoaderCounter.decrement();
103 #endif
104 }
105
106 void SubresourceLoader::create(Frame& frame, CachedResource& resource, ResourceRequest&& request, const ResourceLoaderOptions& options, CompletionHandler<void(RefPtr<SubresourceLoader>&&)>&& completionHandler)
107 {
108     auto subloader(adoptRef(*new SubresourceLoader(frame, resource, options)));
109 #if PLATFORM(IOS)
110     if (!IOSApplication::isWebProcess()) {
111         // On iOS, do not invoke synchronous resource load delegates while resource load scheduling
112         // is disabled to avoid re-entering style selection from a different thread (see <rdar://problem/9121719>).
113         // FIXME: This should be fixed for all ports in <https://bugs.webkit.org/show_bug.cgi?id=56647>.
114         subloader->m_iOSOriginalRequest = request;
115         return completionHandler(WTFMove(subloader));
116     }
117 #endif
118     subloader->init(WTFMove(request), [subloader = subloader.copyRef(), completionHandler = WTFMove(completionHandler)] (bool initialized) mutable {
119         if (!initialized)
120             return completionHandler(nullptr);
121         completionHandler(WTFMove(subloader));
122     });
123 }
124     
125 #if PLATFORM(IOS)
126 void SubresourceLoader::startLoading()
127 {
128     // FIXME: this should probably be removed.
129     ASSERT(!IOSApplication::isWebProcess());
130     init(ResourceRequest(m_iOSOriginalRequest), [this, protectedThis = makeRef(*this)] (bool success) {
131         if (!success)
132             return;
133         m_iOSOriginalRequest = ResourceRequest();
134         start();
135     });
136 }
137 #endif
138
139 CachedResource* SubresourceLoader::cachedResource()
140 {
141     return m_resource;
142 }
143
144 void SubresourceLoader::cancelIfNotFinishing()
145 {
146     if (m_state != Initialized)
147         return;
148
149     ResourceLoader::cancel();
150 }
151
152 void SubresourceLoader::init(ResourceRequest&& request, CompletionHandler<void(bool)>&& completionHandler)
153 {
154     ResourceLoader::init(WTFMove(request), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)] (bool initialized) mutable {
155         if (!initialized)
156             return completionHandler(false);
157         ASSERT(!reachedTerminalState());
158         m_state = Initialized;
159         m_documentLoader->addSubresourceLoader(this);
160         m_origin = m_resource->origin();
161         completionHandler(true);
162     });
163 }
164
165 bool SubresourceLoader::isSubresourceLoader()
166 {
167     return true;
168 }
169
170 void SubresourceLoader::willSendRequestInternal(ResourceRequest&& newRequest, const ResourceResponse& redirectResponse, CompletionHandler<void(ResourceRequest&&)>&& completionHandler)
171 {
172     // Store the previous URL because the call to ResourceLoader::willSendRequest will modify it.
173     URL previousURL = request().url();
174     Ref<SubresourceLoader> protectedThis(*this);
175
176     if (!newRequest.url().isValid()) {
177         cancel(cannotShowURLError());
178         return completionHandler(WTFMove(newRequest));
179     }
180
181     if (newRequest.requester() != ResourceRequestBase::Requester::Main) {
182         TracePoint(SubresourceLoadWillStart);
183         ResourceLoadObserver::shared().logSubresourceLoading(m_frame.get(), newRequest, redirectResponse);
184     }
185
186     auto continueWillSendRequest = [this, protectedThis = makeRef(*this), redirectResponse] (CompletionHandler<void(ResourceRequest&&)>&& completionHandler, ResourceRequest&& newRequest) mutable {
187         if (newRequest.isNull() || reachedTerminalState())
188             return completionHandler(WTFMove(newRequest));
189
190         ResourceLoader::willSendRequestInternal(WTFMove(newRequest), redirectResponse, [this, protectedThis = WTFMove(protectedThis), completionHandler = WTFMove(completionHandler), redirectResponse] (ResourceRequest&& request) mutable {
191             if (reachedTerminalState())
192                 return completionHandler(WTFMove(request));
193
194             if (request.isNull()) {
195                 cancel();
196                 return completionHandler(WTFMove(request));
197             }
198
199             if (m_resource->type() == CachedResource::MainResource && !redirectResponse.isNull())
200                 m_documentLoader->willContinueMainResourceLoadAfterRedirect(request);
201             completionHandler(WTFMove(request));
202         });
203     };
204
205     ASSERT(!newRequest.isNull());
206     if (!redirectResponse.isNull()) {
207         if (options().redirect != FetchOptions::Redirect::Follow) {
208             if (options().redirect == FetchOptions::Redirect::Error) {
209                 cancel();
210                 return completionHandler(WTFMove(newRequest));
211             }
212
213             ResourceResponse opaqueRedirectedResponse = redirectResponse;
214             opaqueRedirectedResponse.setType(ResourceResponse::Type::Opaqueredirect);
215             opaqueRedirectedResponse.setTainting(ResourceResponse::Tainting::Opaqueredirect);
216             m_resource->responseReceived(opaqueRedirectedResponse);
217             if (reachedTerminalState())
218                 return;
219
220             NetworkLoadMetrics emptyMetrics;
221             didFinishLoading(emptyMetrics);
222             return completionHandler(WTFMove(newRequest));
223         } else if (m_redirectCount++ >= options().maxRedirectCount) {
224             cancel(ResourceError(String(), 0, request().url(), ASCIILiteral("Too many redirections"), ResourceError::Type::General));
225             return completionHandler(WTFMove(newRequest));
226         }
227
228         // CachedResources are keyed off their original request URL.
229         // Requesting the same original URL a second time can redirect to a unique second resource.
230         // Therefore, if a redirect to a different destination URL occurs, we should no longer consider this a revalidation of the first resource.
231         // Doing so would have us reusing the resource from the first request if the second request's revalidation succeeds.
232         if (newRequest.isConditional() && m_resource->resourceToRevalidate() && newRequest.url() != m_resource->resourceToRevalidate()->response().url()) {
233             newRequest.makeUnconditional();
234             MemoryCache::singleton().revalidationFailed(*m_resource);
235             if (m_frame && m_frame->page())
236                 m_frame->page()->diagnosticLoggingClient().logDiagnosticMessageWithResult(DiagnosticLoggingKeys::cachedResourceRevalidationKey(), emptyString(), DiagnosticLoggingResultFail, ShouldSample::Yes);
237         }
238
239         if (!m_documentLoader->cachedResourceLoader().updateRequestAfterRedirection(m_resource->type(), newRequest, options())) {
240             cancel();
241             return completionHandler(WTFMove(newRequest));
242         }
243
244         String errorDescription;
245         if (!checkRedirectionCrossOriginAccessControl(request(), redirectResponse, newRequest, errorDescription)) {
246             String errorMessage = "Cross-origin redirection to " + newRequest.url().string() + " denied by Cross-Origin Resource Sharing policy: " + errorDescription;
247             if (m_frame && m_frame->document())
248                 m_frame->document()->addConsoleMessage(MessageSource::Security, MessageLevel::Error, errorMessage);
249             cancel(ResourceError(String(), 0, request().url(), errorMessage, ResourceError::Type::AccessControl));
250             return completionHandler(WTFMove(newRequest));
251         }
252
253         if (m_resource->isImage() && m_documentLoader->cachedResourceLoader().shouldDeferImageLoad(newRequest.url())) {
254             cancel();
255             return completionHandler(WTFMove(newRequest));
256         }
257         m_loadTiming.addRedirect(redirectResponse.url(), newRequest.url());
258         m_resource->redirectReceived(WTFMove(newRequest), redirectResponse, [completionHandler = WTFMove(completionHandler), continueWillSendRequest = WTFMove(continueWillSendRequest)] (ResourceRequest&& request) mutable {
259             continueWillSendRequest(WTFMove(completionHandler), WTFMove(request));
260         });
261         return;
262     }
263
264     continueWillSendRequest(WTFMove(completionHandler), WTFMove(newRequest));
265 }
266
267 void SubresourceLoader::didSendData(unsigned long long bytesSent, unsigned long long totalBytesToBeSent)
268 {
269     ASSERT(m_state == Initialized);
270     Ref<SubresourceLoader> protectedThis(*this);
271     m_resource->didSendData(bytesSent, totalBytesToBeSent);
272 }
273
274 #if USE(QUICK_LOOK)
275
276 bool SubresourceLoader::shouldCreatePreviewLoaderForResponse(const ResourceResponse& response) const
277 {
278     if (m_resource->type() != CachedResource::MainResource)
279         return false;
280
281     if (m_previewLoader)
282         return false;
283
284     return PreviewLoader::shouldCreateForMIMEType(response.mimeType());
285 }
286
287 #endif
288
289 void SubresourceLoader::didReceiveResponse(const ResourceResponse& response)
290 {
291     ASSERT(!response.isNull());
292     ASSERT(m_state == Initialized);
293
294 #if USE(QUICK_LOOK)
295     if (shouldCreatePreviewLoaderForResponse(response)) {
296         m_previewLoader = PreviewLoader::create(*this, response);
297         return;
298     }
299 #endif
300
301     // We want redirect responses to be processed through willSendRequestInternal.
302     // The only exception is redirection with no Location headers. Or in rare circumstances,
303     // cases of too many redirects from CFNetwork (<rdar://problem/30610988>).
304 #if !PLATFORM(COCOA)
305     ASSERT(response.httpStatusCode() < 300 || response.httpStatusCode() >= 400 || response.httpStatusCode() == 304 || !response.httpHeaderField(HTTPHeaderName::Location));
306 #endif
307
308     // Reference the object in this method since the additional processing can do
309     // anything including removing the last reference to this object; one example of this is 3266216.
310     Ref<SubresourceLoader> protectedThis(*this);
311
312     if (shouldIncludeCertificateInfo())
313         response.includeCertificateInfo();
314
315     if (m_resource->resourceToRevalidate()) {
316         if (response.httpStatusCode() == 304) {
317             // 304 Not modified / Use local copy
318             // Existing resource is ok, just use it updating the expiration time.
319             ResourceResponse revalidationResponse = response;
320             revalidationResponse.setSource(ResourceResponse::Source::MemoryCacheAfterValidation);
321             m_resource->setResponse(revalidationResponse);
322             MemoryCache::singleton().revalidationSucceeded(*m_resource, revalidationResponse);
323             if (m_frame && m_frame->page())
324                 m_frame->page()->diagnosticLoggingClient().logDiagnosticMessageWithResult(DiagnosticLoggingKeys::cachedResourceRevalidationKey(), emptyString(), DiagnosticLoggingResultPass, ShouldSample::Yes);
325             if (!reachedTerminalState())
326                 ResourceLoader::didReceiveResponse(revalidationResponse);
327             return;
328         }
329         // Did not get 304 response, continue as a regular resource load.
330         MemoryCache::singleton().revalidationFailed(*m_resource);
331         if (m_frame && m_frame->page())
332             m_frame->page()->diagnosticLoggingClient().logDiagnosticMessageWithResult(DiagnosticLoggingKeys::cachedResourceRevalidationKey(), emptyString(), DiagnosticLoggingResultFail, ShouldSample::Yes);
333     }
334
335     String errorDescription;
336     if (!checkResponseCrossOriginAccessControl(response, errorDescription)) {
337         if (m_frame && m_frame->document())
338             m_frame->document()->addConsoleMessage(MessageSource::Security, MessageLevel::Error, errorDescription);
339         cancel(ResourceError(String(), 0, request().url(), errorDescription, ResourceError::Type::AccessControl));
340         return;
341     }
342
343     m_resource->responseReceived(response);
344     if (reachedTerminalState())
345         return;
346
347     ResourceLoader::didReceiveResponse(response);
348     if (reachedTerminalState())
349         return;
350
351     // FIXME: Main resources have a different set of rules for multipart than images do.
352     // Hopefully we can merge those 2 paths.
353     if (response.isMultipart() && m_resource->type() != CachedResource::MainResource) {
354         m_loadingMultipartContent = true;
355
356         // We don't count multiParts in a CachedResourceLoader's request count
357         m_requestCountTracker = std::nullopt;
358         if (!m_resource->isImage()) {
359             cancel();
360             return;
361         }
362     }
363
364     auto* buffer = resourceData();
365     if (m_loadingMultipartContent && buffer && buffer->size()) {
366         // The resource data will change as the next part is loaded, so we need to make a copy.
367         m_resource->finishLoading(buffer->copy().ptr());
368         clearResourceData();
369         // Since a subresource loader does not load multipart sections progressively, data was delivered to the loader all at once.
370         // After the first multipart section is complete, signal to delegates that this load is "finished"
371         NetworkLoadMetrics emptyMetrics;
372         m_documentLoader->subresourceLoaderFinishedLoadingOnePart(this);
373         didFinishLoadingOnePart(emptyMetrics);
374     }
375
376     checkForHTTPStatusCodeError();
377 }
378
379 void SubresourceLoader::didReceiveData(const char* data, unsigned length, long long encodedDataLength, DataPayloadType dataPayloadType)
380 {
381 #if USE(QUICK_LOOK)
382     if (auto previewLoader = m_previewLoader.get()) {
383         if (previewLoader->didReceiveData(data, length))
384             return;
385     }
386 #endif
387
388     didReceiveDataOrBuffer(data, length, nullptr, encodedDataLength, dataPayloadType);
389 }
390
391 void SubresourceLoader::didReceiveBuffer(Ref<SharedBuffer>&& buffer, long long encodedDataLength, DataPayloadType dataPayloadType)
392 {
393 #if USE(QUICK_LOOK)
394     if (auto previewLoader = m_previewLoader.get()) {
395         if (previewLoader->didReceiveBuffer(buffer.get()))
396             return;
397     }
398 #endif
399
400     didReceiveDataOrBuffer(nullptr, 0, WTFMove(buffer), encodedDataLength, dataPayloadType);
401 }
402
403 void SubresourceLoader::didReceiveDataOrBuffer(const char* data, int length, RefPtr<SharedBuffer>&& buffer, long long encodedDataLength, DataPayloadType dataPayloadType)
404 {
405     ASSERT(m_resource);
406
407     if (m_resource->response().httpStatusCode() >= 400 && !m_resource->shouldIgnoreHTTPStatusCodeErrors())
408         return;
409     ASSERT(!m_resource->resourceToRevalidate());
410     ASSERT(!m_resource->errorOccurred());
411     ASSERT(m_state == Initialized);
412     // Reference the object in this method since the additional processing can do
413     // anything including removing the last reference to this object; one example of this is 3266216.
414     Ref<SubresourceLoader> protectedThis(*this);
415
416     ResourceLoader::didReceiveDataOrBuffer(data, length, buffer.copyRef(), encodedDataLength, dataPayloadType);
417
418     if (!m_loadingMultipartContent) {
419         if (auto* resourceData = this->resourceData())
420             m_resource->updateBuffer(*resourceData);
421         else
422             m_resource->updateData(buffer ? buffer->data() : data, buffer ? buffer->size() : length);
423     }
424 }
425
426 bool SubresourceLoader::checkForHTTPStatusCodeError()
427 {
428     if (m_resource->response().httpStatusCode() < 400 || m_resource->shouldIgnoreHTTPStatusCodeErrors())
429         return false;
430
431     m_state = Finishing;
432     m_resource->error(CachedResource::LoadError);
433     cancel();
434     return true;
435 }
436
437 static void logResourceLoaded(Frame* frame, CachedResource::Type type)
438 {
439     if (!frame || !frame->page())
440         return;
441
442     String resourceType;
443     switch (type) {
444     case CachedResource::MainResource:
445         resourceType = DiagnosticLoggingKeys::mainResourceKey();
446         break;
447     case CachedResource::ImageResource:
448         resourceType = DiagnosticLoggingKeys::imageKey();
449         break;
450 #if ENABLE(XSLT)
451     case CachedResource::XSLStyleSheet:
452 #endif
453     case CachedResource::CSSStyleSheet:
454         resourceType = DiagnosticLoggingKeys::styleSheetKey();
455         break;
456     case CachedResource::Script:
457         resourceType = DiagnosticLoggingKeys::scriptKey();
458         break;
459     case CachedResource::FontResource:
460 #if ENABLE(SVG_FONTS)
461     case CachedResource::SVGFontResource:
462 #endif
463         resourceType = DiagnosticLoggingKeys::fontKey();
464         break;
465     case CachedResource::Beacon:
466         ASSERT_NOT_REACHED();
467         break;
468     case CachedResource::MediaResource:
469     case CachedResource::Icon:
470     case CachedResource::RawResource:
471         resourceType = DiagnosticLoggingKeys::rawKey();
472         break;
473     case CachedResource::SVGDocumentResource:
474         resourceType = DiagnosticLoggingKeys::svgDocumentKey();
475         break;
476 #if ENABLE(APPLICATION_MANIFEST)
477     case CachedResource::ApplicationManifest:
478         resourceType = DiagnosticLoggingKeys::applicationManifestKey();
479         break;
480 #endif
481 #if ENABLE(LINK_PREFETCH)
482     case CachedResource::LinkPrefetch:
483     case CachedResource::LinkSubresource:
484 #endif
485 #if ENABLE(VIDEO_TRACK)
486     case CachedResource::TextTrackResource:
487 #endif
488         resourceType = DiagnosticLoggingKeys::otherKey();
489         break;
490     }
491     
492     frame->page()->diagnosticLoggingClient().logDiagnosticMessage(DiagnosticLoggingKeys::resourceLoadedKey(), resourceType, ShouldSample::Yes);
493 }
494
495 bool SubresourceLoader::checkResponseCrossOriginAccessControl(const ResourceResponse& response, String& errorDescription)
496 {
497     if (!m_resource->isCrossOrigin() || options().mode != FetchOptions::Mode::Cors)
498         return true;
499
500 #if ENABLE(SERVICE_WORKER)
501     if (response.source() == ResourceResponse::Source::ServiceWorker)
502         return response.tainting() != ResourceResponse::Tainting::Opaque;
503 #endif
504
505     ASSERT(m_origin);
506     return passesAccessControlCheck(response, options().storedCredentialsPolicy, *m_origin, errorDescription);
507 }
508
509 bool SubresourceLoader::checkRedirectionCrossOriginAccessControl(const ResourceRequest& previousRequest, const ResourceResponse& redirectResponse, ResourceRequest& newRequest, String& errorMessage)
510 {
511     bool crossOriginFlag = m_resource->isCrossOrigin();
512     bool isNextRequestCrossOrigin = m_origin && !m_origin->canRequest(newRequest.url());
513
514     if (isNextRequestCrossOrigin)
515         m_resource->setCrossOrigin();
516
517     ASSERT(options().mode != FetchOptions::Mode::SameOrigin || !m_resource->isCrossOrigin());
518
519     if (options().mode != FetchOptions::Mode::Cors)
520         return true;
521
522     // Implementing https://fetch.spec.whatwg.org/#concept-http-redirect-fetch step 8 & 9.
523     if (m_resource->isCrossOrigin() && !isValidCrossOriginRedirectionURL(newRequest.url())) {
524         errorMessage = ASCIILiteral("URL is either a non-HTTP URL or contains credentials.");
525         return false;
526     }
527
528     ASSERT(m_origin);
529     if (crossOriginFlag && !passesAccessControlCheck(redirectResponse, options().storedCredentialsPolicy, *m_origin, errorMessage))
530         return false;
531
532     bool redirectingToNewOrigin = false;
533     if (m_resource->isCrossOrigin()) {
534         if (!crossOriginFlag && isNextRequestCrossOrigin)
535             redirectingToNewOrigin = true;
536         else
537             redirectingToNewOrigin = !protocolHostAndPortAreEqual(previousRequest.url(), newRequest.url());
538     }
539
540     // Implementing https://fetch.spec.whatwg.org/#concept-http-redirect-fetch step 10.
541     if (crossOriginFlag && redirectingToNewOrigin)
542         m_origin = SecurityOrigin::createUnique();
543
544     if (redirectingToNewOrigin) {
545         cleanRedirectedRequestForAccessControl(newRequest);
546         updateRequestForAccessControl(newRequest, *m_origin, options().storedCredentialsPolicy);
547     }
548
549     return true;
550 }
551
552 void SubresourceLoader::didFinishLoading(const NetworkLoadMetrics& networkLoadMetrics)
553 {
554 #if USE(QUICK_LOOK)
555     if (auto previewLoader = m_previewLoader.get()) {
556         if (previewLoader->didFinishLoading())
557             return;
558     }
559 #endif
560
561     if (m_state != Initialized)
562         return;
563     ASSERT(!reachedTerminalState());
564     ASSERT(!m_resource->resourceToRevalidate());
565     // FIXME (129394): We should cancel the load when a decode error occurs instead of continuing the load to completion.
566     ASSERT(!m_resource->errorOccurred() || m_resource->status() == CachedResource::DecodeError || !m_resource->isLoading());
567     LOG(ResourceLoading, "Received '%s'.", m_resource->url().string().latin1().data());
568     logResourceLoaded(m_frame.get(), m_resource->type());
569
570     Ref<SubresourceLoader> protectedThis(*this);
571     CachedResourceHandle<CachedResource> protectResource(m_resource);
572
573     // FIXME: Remove this with deprecatedNetworkLoadMetrics.
574     m_loadTiming.setResponseEnd(MonotonicTime::now());
575
576     if (networkLoadMetrics.isComplete())
577         reportResourceTiming(networkLoadMetrics);
578     else {
579         // This is the legacy path for platforms (and ResourceHandle paths) that do not provide
580         // complete load metrics in didFinishLoad. In those cases, fall back to the possibility
581         // that they populated partial load timing information on the ResourceResponse.
582         reportResourceTiming(m_resource->response().deprecatedNetworkLoadMetrics());
583     }
584
585     if (m_resource->type() != CachedResource::MainResource)
586         TracePoint(SubresourceLoadDidEnd);
587
588     m_state = Finishing;
589     m_resource->finishLoading(resourceData());
590
591     if (wasCancelled())
592         return;
593
594     m_resource->finish();
595     ASSERT(!reachedTerminalState());
596     didFinishLoadingOnePart(networkLoadMetrics);
597     notifyDone();
598
599     if (reachedTerminalState())
600         return;
601     releaseResources();
602 }
603
604 void SubresourceLoader::didFail(const ResourceError& error)
605 {
606 #if USE(QUICK_LOOK)
607     if (auto previewLoader = m_previewLoader.get())
608         previewLoader->didFail();
609 #endif
610
611     if (m_state != Initialized)
612         return;
613     ASSERT(!reachedTerminalState());
614     LOG(ResourceLoading, "Failed to load '%s'.\n", m_resource->url().string().latin1().data());
615
616     Ref<SubresourceLoader> protectedThis(*this);
617     CachedResourceHandle<CachedResource> protectResource(m_resource);
618     m_state = Finishing;
619
620     if (m_resource->type() != CachedResource::MainResource)
621         TracePoint(SubresourceLoadDidEnd);
622
623     if (m_resource->resourceToRevalidate())
624         MemoryCache::singleton().revalidationFailed(*m_resource);
625     m_resource->setResourceError(error);
626     if (!m_resource->isPreloaded())
627         MemoryCache::singleton().remove(*m_resource);
628     m_resource->error(CachedResource::LoadError);
629     cleanupForError(error);
630     notifyDone();
631     if (reachedTerminalState())
632         return;
633     releaseResources();
634 }
635
636 void SubresourceLoader::willCancel(const ResourceError& error)
637 {
638 #if PLATFORM(IOS)
639     // Since we defer initialization to scheduling time on iOS but
640     // CachedResourceLoader stores resources in the memory cache immediately,
641     // m_resource might be cached despite its loader not being initialized.
642     if (m_state != Initialized && m_state != Uninitialized)
643 #else
644     if (m_state != Initialized)
645 #endif
646         return;
647     ASSERT(!reachedTerminalState());
648     LOG(ResourceLoading, "Cancelled load of '%s'.\n", m_resource->url().string().latin1().data());
649
650     Ref<SubresourceLoader> protectedThis(*this);
651 #if PLATFORM(IOS)
652     m_state = m_state == Uninitialized ? CancelledWhileInitializing : Finishing;
653 #else
654     m_state = Finishing;
655 #endif
656     auto& memoryCache = MemoryCache::singleton();
657     if (m_resource->resourceToRevalidate())
658         memoryCache.revalidationFailed(*m_resource);
659     m_resource->setResourceError(error);
660     memoryCache.remove(*m_resource);
661 }
662
663 void SubresourceLoader::didCancel(const ResourceError&)
664 {
665     if (m_state == Uninitialized)
666         return;
667
668     if (m_resource->type() != CachedResource::MainResource)
669         TracePoint(SubresourceLoadDidEnd);
670
671     m_resource->cancelLoad();
672     notifyDone();
673 }
674
675 void SubresourceLoader::didRetrieveDerivedDataFromCache(const String& type, SharedBuffer& buffer)
676 {
677     if (m_state != Initialized)
678         return;
679     m_resource->didRetrieveDerivedDataFromCache(type, buffer);
680 }
681
682 void SubresourceLoader::notifyDone()
683 {
684     if (reachedTerminalState())
685         return;
686
687     m_requestCountTracker = std::nullopt;
688 #if PLATFORM(IOS)
689     m_documentLoader->cachedResourceLoader().loadDone(m_state != CancelledWhileInitializing);
690 #else
691     m_documentLoader->cachedResourceLoader().loadDone();
692 #endif
693     if (reachedTerminalState())
694         return;
695     m_documentLoader->removeSubresourceLoader(this);
696 }
697
698 void SubresourceLoader::releaseResources()
699 {
700     ASSERT(!reachedTerminalState());
701 #if PLATFORM(IOS)
702     if (m_state != Uninitialized && m_state != CancelledWhileInitializing)
703 #else
704     if (m_state != Uninitialized)
705 #endif
706         m_resource->clearLoader();
707     m_resource = nullptr;
708     ResourceLoader::releaseResources();
709 }
710
711 void SubresourceLoader::reportResourceTiming(const NetworkLoadMetrics& networkLoadMetrics)
712 {
713     if (!RuntimeEnabledFeatures::sharedFeatures().resourceTimingEnabled())
714         return;
715
716     if (!ResourceTimingInformation::shouldAddResourceTiming(*m_resource))
717         return;
718
719     Document* document = m_documentLoader->cachedResourceLoader().document();
720     if (!document)
721         return;
722
723     SecurityOrigin& origin = m_origin ? *m_origin : document->securityOrigin();
724     auto resourceTiming = ResourceTiming::fromLoad(*m_resource, m_resource->initiatorName(), m_loadTiming, networkLoadMetrics, origin);
725
726     // Worker resources loaded here are all CachedRawResources loaded through WorkerThreadableLoader.
727     // Pass the ResourceTiming information on so that WorkerThreadableLoader may add them to the
728     // Worker's Performance object.
729     if (options().initiatorContext == InitiatorContext::Worker) {
730         ASSERT(m_origin);
731         ASSERT(is<CachedRawResource>(m_resource));
732         downcast<CachedRawResource>(*m_resource).finishedTimingForWorkerLoad(WTFMove(resourceTiming));
733         return;
734     }
735
736     ASSERT(options().initiatorContext == InitiatorContext::Document);
737     m_documentLoader->cachedResourceLoader().resourceTimingInformation().addResourceTiming(*m_resource, *document, WTFMove(resourceTiming));
738 }
739
740 }