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