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