logo

WebKitGTK

Collection of patches on top of WebKitGTK

SubresourceLoader.cpp (37711B)


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