2 * Copyright (C) 2012-2019 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23 * THE POSSIBILITY OF SUCH DAMAGE.
27 #include "NetworkResourceLoader.h"
29 #include "DataReference.h"
30 #include "FormDataReference.h"
32 #include "NetworkCache.h"
33 #include "NetworkConnectionToWebProcess.h"
34 #include "NetworkLoad.h"
35 #include "NetworkLoadChecker.h"
36 #include "NetworkProcess.h"
37 #include "NetworkProcessConnectionMessages.h"
38 #include "NetworkSession.h"
39 #include "SharedBufferDataReference.h"
40 #include "WebCoreArgumentCoders.h"
41 #include "WebErrors.h"
42 #include "WebPageMessages.h"
43 #include "WebResourceLoaderMessages.h"
44 #include "WebsiteDataStoreParameters.h"
45 #include <WebCore/BlobDataFileReference.h>
46 #include <WebCore/CertificateInfo.h>
47 #include <WebCore/ContentSecurityPolicy.h>
48 #include <WebCore/DiagnosticLoggingKeys.h>
49 #include <WebCore/HTTPParsers.h>
50 #include <WebCore/NetworkLoadMetrics.h>
51 #include <WebCore/NetworkStorageSession.h>
52 #include <WebCore/RegistrableDomain.h>
53 #include <WebCore/SameSiteInfo.h>
54 #include <WebCore/SecurityOrigin.h>
55 #include <WebCore/SharedBuffer.h>
56 #include <wtf/RunLoop.h>
59 #include <WebCore/PreviewConverter.h>
62 #define RELEASE_LOG_IF_ALLOWED(fmt, ...) RELEASE_LOG_IF(isAlwaysOnLoggingAllowed(), Network, "%p - NetworkResourceLoader::" fmt, this, ##__VA_ARGS__)
63 #define RELEASE_LOG_ERROR_IF_ALLOWED(fmt, ...) RELEASE_LOG_ERROR_IF(isAlwaysOnLoggingAllowed(), Network, "%p - NetworkResourceLoader::" fmt, this, ##__VA_ARGS__)
66 using namespace WebCore;
68 struct NetworkResourceLoader::SynchronousLoadData {
69 WTF_MAKE_STRUCT_FAST_ALLOCATED;
71 SynchronousLoadData(Messages::NetworkConnectionToWebProcess::PerformSynchronousLoad::DelayedReply&& reply)
72 : delayedReply(WTFMove(reply))
76 ResourceRequest currentRequest;
77 Messages::NetworkConnectionToWebProcess::PerformSynchronousLoad::DelayedReply delayedReply;
78 ResourceResponse response;
82 static void sendReplyToSynchronousRequest(NetworkResourceLoader::SynchronousLoadData& data, const SharedBuffer* buffer)
84 ASSERT(data.delayedReply);
85 ASSERT(!data.response.isNull() || !data.error.isNull());
87 Vector<char> responseBuffer;
88 if (buffer && buffer->size())
89 responseBuffer.append(buffer->data(), buffer->size());
91 data.delayedReply(data.error, data.response, responseBuffer);
92 data.delayedReply = nullptr;
95 NetworkResourceLoader::NetworkResourceLoader(NetworkResourceLoadParameters&& parameters, NetworkConnectionToWebProcess& connection, Messages::NetworkConnectionToWebProcess::PerformSynchronousLoad::DelayedReply&& synchronousReply)
96 : m_parameters { WTFMove(parameters) }
97 , m_connection { connection }
98 , m_fileReferences(connection.resolveBlobReferences(m_parameters))
99 , m_isAllowedToAskUserForCredentials { m_parameters.clientCredentialPolicy == ClientCredentialPolicy::MayAskClientForCredentials }
100 , m_bufferingTimer { *this, &NetworkResourceLoader::bufferingTimerFired }
101 , m_shouldCaptureExtraNetworkLoadMetrics(m_connection->captureExtraNetworkLoadMetricsEnabled())
103 ASSERT(RunLoop::isMain());
105 if (auto* session = connection.networkProcess().networkSession(sessionID()))
106 m_cache = session->cache();
108 // FIXME: This is necessary because of the existence of EmptyFrameLoaderClient in WebCore.
109 // Once bug 116233 is resolved, this ASSERT can just be "m_webPageID && m_webFrameID"
110 ASSERT((m_parameters.webPageID && m_parameters.webFrameID) || m_parameters.clientCredentialPolicy == ClientCredentialPolicy::CannotAskClientForCredentials);
112 if (synchronousReply || parameters.shouldRestrictHTTPResponseAccess || parameters.options.keepAlive) {
113 NetworkLoadChecker::LoadType requestLoadType = isMainFrameLoad() ? NetworkLoadChecker::LoadType::MainFrame : NetworkLoadChecker::LoadType::Other;
114 m_networkLoadChecker = makeUnique<NetworkLoadChecker>(connection.networkProcess(), FetchOptions { m_parameters.options }, m_parameters.sessionID, m_parameters.webPageID, m_parameters.webFrameID, HTTPHeaderMap { m_parameters.originalRequestHeaders }, URL { m_parameters.request.url() }, m_parameters.sourceOrigin.copyRef(), m_parameters.preflightPolicy, originalRequest().httpReferrer(), m_parameters.isHTTPSUpgradeEnabled, shouldCaptureExtraNetworkLoadMetrics(), requestLoadType);
115 if (m_parameters.cspResponseHeaders)
116 m_networkLoadChecker->setCSPResponseHeaders(ContentSecurityPolicyResponseHeaders { m_parameters.cspResponseHeaders.value() });
117 #if ENABLE(CONTENT_EXTENSIONS)
118 m_networkLoadChecker->setContentExtensionController(URL { m_parameters.mainDocumentURL }, m_parameters.userContentControllerIdentifier);
121 if (synchronousReply)
122 m_synchronousLoadData = makeUnique<SynchronousLoadData>(WTFMove(synchronousReply));
125 NetworkResourceLoader::~NetworkResourceLoader()
127 ASSERT(RunLoop::isMain());
128 ASSERT(!m_networkLoad);
129 ASSERT(!isSynchronous() || !m_synchronousLoadData->delayedReply);
130 ASSERT(m_fileReferences.isEmpty());
131 if (m_responseCompletionHandler)
132 m_responseCompletionHandler(PolicyAction::Ignore);
135 bool NetworkResourceLoader::canUseCache(const ResourceRequest& request) const
139 ASSERT(!sessionID().isEphemeral());
141 if (!request.url().protocolIsInHTTPFamily())
143 if (originalRequest().cachePolicy() == WebCore::ResourceRequestCachePolicy::DoNotUseAnyCache)
149 bool NetworkResourceLoader::canUseCachedRedirect(const ResourceRequest& request) const
151 if (!canUseCache(request) || m_cacheEntryForMaxAgeCapValidation)
153 // Limit cached redirects to avoid cycles and other trouble.
154 // Networking layer follows over 30 redirects but caching that many seems unnecessary.
155 static const unsigned maximumCachedRedirectCount { 5 };
156 if (m_redirectCount > maximumCachedRedirectCount)
162 bool NetworkResourceLoader::isSynchronous() const
164 return !!m_synchronousLoadData;
167 void NetworkResourceLoader::start()
169 ASSERT(RunLoop::isMain());
171 m_networkActivityTracker = m_connection->startTrackingResourceLoad(m_parameters.webPageID, m_parameters.identifier, isMainResource(), sessionID());
173 ASSERT(!m_wasStarted);
176 if (m_networkLoadChecker) {
177 m_networkLoadChecker->check(ResourceRequest { originalRequest() }, this, [this, weakThis = makeWeakPtr(*this)] (auto&& result) {
181 WTF::switchOn(result,
182 [this] (ResourceError& error) {
183 RELEASE_LOG_IF_ALLOWED("start: error checking (pageID = %" PRIu64 ", frameID = %" PRIu64 ", resourceID = %" PRIu64 ", isMainResource = %d, isSynchronous = %d, parentPID = %d, error.domain = %{public}s, error.code = %d)", m_parameters.webPageID.toUInt64(), m_parameters.webFrameID.toUInt64(), m_parameters.identifier, this->isMainResource(), this->isSynchronous(), m_parameters.parentPID, error.domain().utf8().data(), error.errorCode());
184 if (!error.isCancellation())
185 this->didFailLoading(error);
187 [this] (NetworkLoadChecker::RedirectionTriplet& triplet) {
188 this->m_isWaitingContinueWillSendRequestForCachedRedirect = true;
189 this->willSendRedirectedRequest(WTFMove(triplet.request), WTFMove(triplet.redirectRequest), WTFMove(triplet.redirectResponse));
190 RELEASE_LOG_IF_ALLOWED("start: synthetic redirect sent because request URL was modified (pageID = %" PRIu64 ", frameID = %" PRIu64 ", resourceID = %" PRIu64 ", isMainResource = %d, isSynchronous = %d, parentPID = %d)", m_parameters.webPageID.toUInt64(), m_parameters.webFrameID.toUInt64(), m_parameters.identifier, this->isMainResource(), this->isSynchronous(), m_parameters.parentPID);
192 [this] (ResourceRequest& request) {
193 if (this->canUseCache(request)) {
194 RELEASE_LOG_IF_ALLOWED("start: Checking cache for resource (pageID = %" PRIu64 ", frameID = %" PRIu64 ", resourceID = %" PRIu64 ", isMainResource = %d, isSynchronous = %d, parentPID = %d)", m_parameters.webPageID.toUInt64(), m_parameters.webFrameID.toUInt64(), m_parameters.identifier, this->isMainResource(), this->isSynchronous(), m_parameters.parentPID);
195 this->retrieveCacheEntry(request);
199 this->startNetworkLoad(WTFMove(request), FirstLoad::Yes);
205 // FIXME: Remove that code path once m_networkLoadChecker is used for all network loads.
206 if (canUseCache(originalRequest())) {
207 RELEASE_LOG_IF_ALLOWED("start: Checking cache for resource (pageID = %" PRIu64 ", frameID = %" PRIu64 ", resourceID = %" PRIu64 ", isMainResource = %d, isSynchronous = %d, parentPID = %d)", m_parameters.webPageID.toUInt64(), m_parameters.webFrameID.toUInt64(), m_parameters.identifier, isMainResource(), isSynchronous(), m_parameters.parentPID);
208 retrieveCacheEntry(originalRequest());
212 startNetworkLoad(ResourceRequest { originalRequest() }, FirstLoad::Yes);
215 void NetworkResourceLoader::retrieveCacheEntry(const ResourceRequest& request)
217 ASSERT(canUseCache(request));
219 auto protectedThis = makeRef(*this);
220 if (isMainFrameLoad()) {
221 ASSERT(m_parameters.options.mode == FetchOptions::Mode::Navigate);
222 if (auto* session = m_connection->networkProcess().networkSession(sessionID())) {
223 if (auto entry = session->prefetchCache().take(request.url())) {
224 if (!entry->redirectRequest.isNull()) {
225 auto cacheEntry = m_cache->makeRedirectEntry(request, entry->response, entry->redirectRequest);
226 retrieveCacheEntryInternal(WTFMove(cacheEntry), ResourceRequest { request });
227 auto maxAgeCap = validateCacheEntryForMaxAgeCapValidation(request, entry->redirectRequest, entry->response);
228 m_cache->storeRedirect(request, entry->response, entry->redirectRequest, maxAgeCap);
231 auto buffer = entry->releaseBuffer();
232 auto cacheEntry = m_cache->makeEntry(request, entry->response, buffer.copyRef());
233 retrieveCacheEntryInternal(WTFMove(cacheEntry), ResourceRequest { request });
234 m_cache->store(request, entry->response, WTFMove(buffer), nullptr);
239 m_cache->retrieve(request, { m_parameters.webPageID, m_parameters.webFrameID }, [this, weakThis = makeWeakPtr(*this), request = ResourceRequest { request }](auto entry, auto info) mutable {
243 logSlowCacheRetrieveIfNeeded(info);
246 RELEASE_LOG_IF_ALLOWED("retrieveCacheEntry: Resource not in cache (pageID = %" PRIu64 ", frameID = %" PRIu64 ", resourceID = %" PRIu64 ", isMainResource = %d, isSynchronous = %d)", m_parameters.webPageID.toUInt64(), m_parameters.webFrameID.toUInt64(), m_parameters.identifier, isMainResource(), isSynchronous());
247 startNetworkLoad(WTFMove(request), FirstLoad::Yes);
250 retrieveCacheEntryInternal(WTFMove(entry), WTFMove(request));
254 void NetworkResourceLoader::retrieveCacheEntryInternal(std::unique_ptr<NetworkCache::Entry>&& entry, WebCore::ResourceRequest&& request)
256 #if ENABLE(RESOURCE_LOAD_STATISTICS)
257 if (entry->hasReachedPrevalentResourceAgeCap()) {
258 RELEASE_LOG_IF_ALLOWED("retrieveCacheEntry: Resource has reached prevalent resource age cap (pageID = %" PRIu64 ", frameID = %" PRIu64 ", resourceID = %" PRIu64 ", isMainResource = %d, isSynchronous = %d)", m_parameters.webPageID.toUInt64(), m_parameters.webFrameID.toUInt64(), m_parameters.identifier, isMainResource(), isSynchronous());
259 m_cacheEntryForMaxAgeCapValidation = WTFMove(entry);
260 ResourceRequest revalidationRequest = originalRequest();
261 startNetworkLoad(WTFMove(revalidationRequest), FirstLoad::Yes);
265 if (entry->redirectRequest()) {
266 RELEASE_LOG_IF_ALLOWED("retrieveCacheEntry: Handling redirect (pageID = %" PRIu64 ", frameID = %" PRIu64 ", resourceID = %" PRIu64 ", isMainResource = %d, isSynchronous = %d)", m_parameters.webPageID.toUInt64(), m_parameters.webFrameID.toUInt64(), m_parameters.identifier, isMainResource(), isSynchronous());
267 dispatchWillSendRequestForCacheEntry(WTFMove(request), WTFMove(entry));
270 if (m_parameters.needsCertificateInfo && !entry->response().certificateInfo()) {
271 RELEASE_LOG_IF_ALLOWED("retrieveCacheEntry: Resource does not have required certificate (pageID = %" PRIu64 ", frameID = %" PRIu64 ", resourceID = %" PRIu64 ", isMainResource = %d, isSynchronous = %d)", m_parameters.webPageID.toUInt64(), m_parameters.webFrameID.toUInt64(), m_parameters.identifier, isMainResource(), isSynchronous());
272 startNetworkLoad(WTFMove(request), FirstLoad::Yes);
275 if (entry->needsValidation() || request.cachePolicy() == WebCore::ResourceRequestCachePolicy::RefreshAnyCacheData) {
276 RELEASE_LOG_IF_ALLOWED("retrieveCacheEntry: Validating cache entry (pageID = %" PRIu64 ", frameID = %" PRIu64 ", resourceID = %" PRIu64 ", isMainResource = %d, isSynchronous = %d)", m_parameters.webPageID.toUInt64(), m_parameters.webFrameID.toUInt64(), m_parameters.identifier, isMainResource(), isSynchronous());
277 validateCacheEntry(WTFMove(entry));
280 RELEASE_LOG_IF_ALLOWED("retrieveCacheEntry: Retrieved resource from cache (pageID = %" PRIu64 ", frameID = %" PRIu64 ", resourceID = %" PRIu64 ", isMainResource = %d, isSynchronous = %d)", m_parameters.webPageID.toUInt64(), m_parameters.webFrameID.toUInt64(), m_parameters.identifier, isMainResource(), isSynchronous());
281 didRetrieveCacheEntry(WTFMove(entry));
284 void NetworkResourceLoader::startNetworkLoad(ResourceRequest&& request, FirstLoad load)
286 if (load == FirstLoad::Yes) {
287 RELEASE_LOG_IF_ALLOWED("startNetworkLoad: (pageID = %" PRIu64 ", frameID = %" PRIu64 ", resourceID = %" PRIu64 ", isMainResource = %d, isSynchronous = %d)", m_parameters.webPageID.toUInt64(), m_parameters.webFrameID.toUInt64(), m_parameters.identifier, isMainResource(), isSynchronous());
289 consumeSandboxExtensions();
291 if (isSynchronous() || m_parameters.maximumBufferingTime > 0_s)
292 m_bufferedData = SharedBuffer::create();
294 if (canUseCache(request))
295 m_bufferedDataForCache = SharedBuffer::create();
298 NetworkLoadParameters parameters = m_parameters;
299 parameters.networkActivityTracker = m_networkActivityTracker;
300 if (parameters.storedCredentialsPolicy == WebCore::StoredCredentialsPolicy::Use && m_networkLoadChecker)
301 parameters.storedCredentialsPolicy = m_networkLoadChecker->storedCredentialsPolicy();
303 auto* networkSession = m_connection->networkProcess().networkSession(parameters.sessionID);
304 if (!networkSession && parameters.sessionID.isEphemeral()) {
305 m_connection->networkProcess().addWebsiteDataStore(WebsiteDataStoreParameters::privateSessionParameters(parameters.sessionID));
306 networkSession = m_connection->networkProcess().networkSession(parameters.sessionID);
308 if (!networkSession) {
309 WTFLogAlways("Attempted to create a NetworkLoad with a session (id=%" PRIu64 ") that does not exist.", parameters.sessionID.toUInt64());
310 RELEASE_LOG_ERROR_IF_ALLOWED("startNetworkLoad: Attempted to create a NetworkLoad with a session that does not exist (pageID = %" PRIu64 ", frameID = %" PRIu64 ", resourceID = %" PRIu64 ", sessionID=%" PRIu64 ")", m_parameters.webPageID.toUInt64(), m_parameters.webFrameID.toUInt64(), m_parameters.identifier, parameters.sessionID.toUInt64());
311 m_connection->networkProcess().logDiagnosticMessage(m_parameters.webPageID, WebCore::DiagnosticLoggingKeys::internalErrorKey(), WebCore::DiagnosticLoggingKeys::invalidSessionIDKey(), WebCore::ShouldSample::No);
312 didFailLoading(internalError(request.url()));
316 if (request.url().protocolIsBlob())
317 parameters.blobFileReferences = networkSession->blobRegistry().filesInBlob(originalRequest().url());
319 parameters.request = WTFMove(request);
320 m_networkLoad = makeUnique<NetworkLoad>(*this, &networkSession->blobRegistry(), WTFMove(parameters), *networkSession);
322 RELEASE_LOG_IF_ALLOWED("startNetworkLoad: (pageID = %" PRIu64 ", frameID = %" PRIu64 ", resourceID = %" PRIu64 ", description = %{public}s)", m_parameters.webPageID.toUInt64(), m_parameters.webFrameID.toUInt64(), m_parameters.identifier, m_networkLoad->description().utf8().data());
325 void NetworkResourceLoader::cleanup(LoadResult result)
327 ASSERT(RunLoop::isMain());
329 m_connection->stopTrackingResourceLoad(m_parameters.identifier,
330 result == LoadResult::Success ? NetworkActivityTracker::CompletionCode::Success :
331 result == LoadResult::Failure ? NetworkActivityTracker::CompletionCode::Failure :
332 NetworkActivityTracker::CompletionCode::None);
334 m_bufferingTimer.stop();
336 invalidateSandboxExtensions();
338 m_networkLoad = nullptr;
340 // This will cause NetworkResourceLoader to be destroyed and therefore we do it last.
341 m_connection->didCleanupResourceLoader(*this);
344 void NetworkResourceLoader::convertToDownload(DownloadID downloadID, const ResourceRequest& request, const ResourceResponse& response)
346 RELEASE_LOG(Loading, "Converting NetworkResourceLoader %p to download %" PRIu64 " (pageID = %" PRIu64 ", frameID = %" PRIu64 ", resourceID = %" PRIu64 ")", this, downloadID.downloadID(), m_parameters.webPageID.toUInt64(), m_parameters.webFrameID.toUInt64(), m_parameters.identifier);
348 // This can happen if the resource came from the disk cache.
349 if (!m_networkLoad) {
350 m_connection->networkProcess().downloadManager().startDownload(m_parameters.sessionID, downloadID, request);
355 if (m_responseCompletionHandler)
356 m_connection->networkProcess().downloadManager().convertNetworkLoadToDownload(downloadID, std::exchange(m_networkLoad, nullptr), WTFMove(m_responseCompletionHandler), WTFMove(m_fileReferences), request, response);
359 void NetworkResourceLoader::abort()
361 ASSERT(RunLoop::isMain());
363 RELEASE_LOG_IF_ALLOWED("abort: Canceling resource load (pageID = %" PRIu64 ", frameID = %" PRIu64 ", resourceID = %" PRIu64 ")",
364 m_parameters.webPageID.toUInt64(), m_parameters.webFrameID.toUInt64(), m_parameters.identifier);
366 if (m_parameters.options.keepAlive && m_response.isNull() && !m_isKeptAlive) {
367 m_isKeptAlive = true;
368 m_connection->transferKeptAliveLoad(*this);
373 if (canUseCache(m_networkLoad->currentRequest())) {
374 // We might already have used data from this incomplete load. Ensure older versions don't remain in the cache after cancel.
375 if (!m_response.isNull())
376 m_cache->remove(m_networkLoad->currentRequest());
378 m_networkLoad->cancel();
381 cleanup(LoadResult::Cancel);
384 bool NetworkResourceLoader::shouldInterruptLoadForXFrameOptions(const String& xFrameOptions, const URL& url)
386 if (isMainFrameLoad())
389 switch (parseXFrameOptionsHeader(xFrameOptions)) {
390 case XFrameOptionsNone:
391 case XFrameOptionsAllowAll:
393 case XFrameOptionsDeny:
395 case XFrameOptionsSameOrigin: {
396 auto origin = SecurityOrigin::create(url);
397 auto topFrameOrigin = m_parameters.frameAncestorOrigins.last();
398 if (!origin->isSameSchemeHostPort(*topFrameOrigin))
400 for (auto& ancestorOrigin : m_parameters.frameAncestorOrigins) {
401 if (!origin->isSameSchemeHostPort(*ancestorOrigin))
406 case XFrameOptionsConflict: {
407 String errorMessage = "Multiple 'X-Frame-Options' headers with conflicting values ('" + xFrameOptions + "') encountered when loading '" + url.stringCenterEllipsizedToLength() + "'. Falling back to 'DENY'.";
408 send(Messages::WebPage::AddConsoleMessage { m_parameters.webFrameID, MessageSource::JS, MessageLevel::Error, errorMessage, identifier() }, m_parameters.webPageID);
411 case XFrameOptionsInvalid: {
412 String errorMessage = "Invalid 'X-Frame-Options' header encountered when loading '" + url.stringCenterEllipsizedToLength() + "': '" + xFrameOptions + "' is not a recognized directive. The header will be ignored.";
413 send(Messages::WebPage::AddConsoleMessage { m_parameters.webFrameID, MessageSource::JS, MessageLevel::Error, errorMessage, identifier() }, m_parameters.webPageID);
417 ASSERT_NOT_REACHED();
421 bool NetworkResourceLoader::shouldInterruptLoadForCSPFrameAncestorsOrXFrameOptions(const ResourceResponse& response)
423 ASSERT(isMainResource());
426 if (PreviewConverter::supportsMIMEType(response.mimeType()))
430 auto url = response.url();
431 ContentSecurityPolicy contentSecurityPolicy { URL { url }, this };
432 contentSecurityPolicy.didReceiveHeaders(ContentSecurityPolicyResponseHeaders { response }, originalRequest().httpReferrer());
433 if (!contentSecurityPolicy.allowFrameAncestors(m_parameters.frameAncestorOrigins, url))
436 if (!contentSecurityPolicy.overridesXFrameOptions()) {
437 String xFrameOptions = m_response.httpHeaderField(HTTPHeaderName::XFrameOptions);
438 if (!xFrameOptions.isNull() && shouldInterruptLoadForXFrameOptions(xFrameOptions, response.url())) {
439 String errorMessage = "Refused to display '" + response.url().stringCenterEllipsizedToLength() + "' in a frame because it set 'X-Frame-Options' to '" + xFrameOptions + "'.";
440 send(Messages::WebPage::AddConsoleMessage { m_parameters.webFrameID, MessageSource::Security, MessageLevel::Error, errorMessage, identifier() }, m_parameters.webPageID);
447 void NetworkResourceLoader::didReceiveResponse(ResourceResponse&& receivedResponse, ResponseCompletionHandler&& completionHandler)
449 RELEASE_LOG_IF_ALLOWED("didReceiveResponse: (pageID = %" PRIu64 ", frameID = %" PRIu64 ", resourceID = %" PRIu64 ", httpStatusCode = %d, length = %" PRId64 ")", m_parameters.webPageID.toUInt64(), m_parameters.webFrameID.toUInt64(), m_parameters.identifier, receivedResponse.httpStatusCode(), receivedResponse.expectedContentLength());
451 m_response = WTFMove(receivedResponse);
453 if (shouldCaptureExtraNetworkLoadMetrics() && m_networkLoadChecker) {
454 auto information = m_networkLoadChecker->takeNetworkLoadInformation();
455 information.response = m_response;
456 m_connection->addNetworkLoadInformation(identifier(), WTFMove(information));
459 // For multipart/x-mixed-replace didReceiveResponseAsync gets called multiple times and buffering would require special handling.
460 if (!isSynchronous() && m_response.isMultipart())
461 m_bufferedData = nullptr;
463 if (m_response.isMultipart())
464 m_bufferedDataForCache = nullptr;
466 if (m_cacheEntryForValidation) {
467 bool validationSucceeded = m_response.httpStatusCode() == 304; // 304 Not Modified
468 if (validationSucceeded) {
469 m_cacheEntryForValidation = m_cache->update(originalRequest(), { m_parameters.webPageID, m_parameters.webFrameID }, *m_cacheEntryForValidation, m_response);
470 // If the request was conditional then this revalidation was not triggered by the network cache and we pass the 304 response to WebCore.
471 if (originalRequest().isConditional())
472 m_cacheEntryForValidation = nullptr;
474 m_cacheEntryForValidation = nullptr;
476 if (m_cacheEntryForValidation)
477 return completionHandler(PolicyAction::Use);
479 if (isMainResource() && shouldInterruptLoadForCSPFrameAncestorsOrXFrameOptions(m_response)) {
480 auto response = sanitizeResponseIfPossible(ResourceResponse { m_response }, ResourceResponse::SanitizationType::CrossOriginSafe);
481 send(Messages::WebResourceLoader::StopLoadingAfterXFrameOptionsOrContentSecurityPolicyDenied { response });
482 return completionHandler(PolicyAction::Ignore);
485 if (m_networkLoadChecker) {
486 auto error = m_networkLoadChecker->validateResponse(m_response);
487 if (!error.isNull()) {
488 RunLoop::main().dispatch([protectedThis = makeRef(*this), error = WTFMove(error)] {
489 if (protectedThis->m_networkLoad)
490 protectedThis->didFailLoading(error);
492 return completionHandler(PolicyAction::Ignore);
496 auto response = sanitizeResponseIfPossible(ResourceResponse { m_response }, ResourceResponse::SanitizationType::CrossOriginSafe);
497 if (isSynchronous()) {
498 m_synchronousLoadData->response = WTFMove(response);
499 return completionHandler(PolicyAction::Use);
502 if (isCrossOriginPrefetch()) {
503 if (response.httpHeaderField(HTTPHeaderName::Vary).contains("Cookie")) {
505 return completionHandler(PolicyAction::Ignore);
507 return completionHandler(PolicyAction::Use);
510 // We wait to receive message NetworkResourceLoader::ContinueDidReceiveResponse before continuing a load for
511 // a main resource because the embedding client must decide whether to allow the load.
512 bool willWaitForContinueDidReceiveResponse = isMainResource();
513 send(Messages::WebResourceLoader::DidReceiveResponse { response, willWaitForContinueDidReceiveResponse });
515 if (willWaitForContinueDidReceiveResponse) {
516 m_responseCompletionHandler = WTFMove(completionHandler);
521 m_responseCompletionHandler = WTFMove(completionHandler);
522 RunLoop::main().dispatch([protectedThis = makeRef(*this)] {
523 protectedThis->didFinishLoading(NetworkLoadMetrics { });
528 completionHandler(PolicyAction::Use);
531 void NetworkResourceLoader::didReceiveBuffer(Ref<SharedBuffer>&& buffer, int reportedEncodedDataLength)
533 if (!m_numBytesReceived)
534 RELEASE_LOG_IF_ALLOWED("didReceiveBuffer: Started receiving data (pageID = %" PRIu64 ", frameID = %" PRIu64 ", resourceID = %" PRIu64 ")", m_parameters.webPageID.toUInt64(), m_parameters.webFrameID.toUInt64(), m_parameters.identifier);
535 m_numBytesReceived += buffer->size();
537 ASSERT(!m_cacheEntryForValidation);
539 if (m_bufferedDataForCache) {
540 // Prevent memory growth in case of streaming data.
541 const size_t maximumCacheBufferSize = 10 * 1024 * 1024;
542 if (m_bufferedDataForCache->size() + buffer->size() <= maximumCacheBufferSize)
543 m_bufferedDataForCache->append(buffer.get());
545 m_bufferedDataForCache = nullptr;
547 if (isCrossOriginPrefetch())
549 // FIXME: At least on OS X Yosemite we always get -1 from the resource handle.
550 unsigned encodedDataLength = reportedEncodedDataLength >= 0 ? reportedEncodedDataLength : buffer->size();
552 if (m_bufferedData) {
553 m_bufferedData->append(buffer.get());
554 m_bufferedDataEncodedDataLength += encodedDataLength;
555 startBufferingTimerIfNeeded();
558 sendBuffer(buffer, encodedDataLength);
561 void NetworkResourceLoader::didFinishLoading(const NetworkLoadMetrics& networkLoadMetrics)
563 RELEASE_LOG_IF_ALLOWED("didFinishLoading: (pageID = %" PRIu64 ", frameID = %" PRIu64 ", resourceID = %" PRIu64 ", length = %zd)", m_parameters.webPageID.toUInt64(), m_parameters.webFrameID.toUInt64(), m_parameters.identifier, m_numBytesReceived);
565 if (shouldCaptureExtraNetworkLoadMetrics())
566 m_connection->addNetworkLoadInformationMetrics(identifier(), networkLoadMetrics);
568 if (m_cacheEntryForValidation) {
570 ASSERT(m_response.httpStatusCode() == 304);
571 LOG(NetworkCache, "(NetworkProcess) revalidated");
572 didRetrieveCacheEntry(WTFMove(m_cacheEntryForValidation));
576 #if ENABLE(RESOURCE_LOAD_STATISTICS) && !RELEASE_LOG_DISABLED
577 if (shouldLogCookieInformation(m_connection, sessionID()))
578 logCookieInformation();
582 sendReplyToSynchronousRequest(*m_synchronousLoadData, m_bufferedData.get());
584 if (m_bufferedData && !m_bufferedData->isEmpty()) {
585 // FIXME: Pass a real value or remove the encoded data size feature.
586 sendBuffer(*m_bufferedData, -1);
588 send(Messages::WebResourceLoader::DidFinishResourceLoad(networkLoadMetrics));
591 tryStoreAsCacheEntry();
593 cleanup(LoadResult::Success);
596 void NetworkResourceLoader::didFailLoading(const ResourceError& error)
598 RELEASE_LOG_IF_ALLOWED("didFailLoading: (pageID = %" PRIu64 ", frameID = %" PRIu64 ", resourceID = %" PRIu64 ", isTimeout = %d, isCancellation = %d, isAccessControl = %d, errCode = %d)", m_parameters.webPageID.toUInt64(), m_parameters.webFrameID.toUInt64(), m_parameters.identifier, error.isTimeout(), error.isCancellation(), error.isAccessControl(), error.errorCode());
600 if (shouldCaptureExtraNetworkLoadMetrics())
601 m_connection->removeNetworkLoadInformation(identifier());
603 ASSERT(!error.isNull());
605 m_cacheEntryForValidation = nullptr;
607 if (isSynchronous()) {
608 m_synchronousLoadData->error = error;
609 sendReplyToSynchronousRequest(*m_synchronousLoadData, nullptr);
610 } else if (auto* connection = messageSenderConnection())
611 connection->send(Messages::WebResourceLoader::DidFailResourceLoad(error), messageSenderDestinationID());
613 cleanup(LoadResult::Failure);
616 void NetworkResourceLoader::didBlockAuthenticationChallenge()
618 send(Messages::WebResourceLoader::DidBlockAuthenticationChallenge());
621 Optional<Seconds> NetworkResourceLoader::validateCacheEntryForMaxAgeCapValidation(const ResourceRequest& request, const ResourceRequest& redirectRequest, const ResourceResponse& redirectResponse)
623 #if ENABLE(RESOURCE_LOAD_STATISTICS)
624 bool existingCacheEntryMatchesNewResponse = false;
625 if (m_cacheEntryForMaxAgeCapValidation) {
626 ASSERT(redirectResponse.source() == ResourceResponse::Source::Network);
627 ASSERT(redirectResponse.isRedirection());
628 if (redirectResponse.httpHeaderField(WebCore::HTTPHeaderName::Location) == m_cacheEntryForMaxAgeCapValidation->response().httpHeaderField(WebCore::HTTPHeaderName::Location))
629 existingCacheEntryMatchesNewResponse = true;
631 m_cache->remove(m_cacheEntryForMaxAgeCapValidation->key());
632 m_cacheEntryForMaxAgeCapValidation = nullptr;
635 if (!existingCacheEntryMatchesNewResponse) {
636 if (auto* networkStorageSession = m_connection->networkProcess().storageSession(sessionID()))
637 return networkStorageSession->maxAgeCacheCap(request);
643 void NetworkResourceLoader::willSendRedirectedRequest(ResourceRequest&& request, ResourceRequest&& redirectRequest, ResourceResponse&& redirectResponse)
647 Optional<AdClickAttribution::Conversion> adClickConversion;
648 if (!sessionID().isEphemeral())
649 adClickConversion = AdClickAttribution::parseConversionRequest(redirectRequest.url());
651 auto maxAgeCap = validateCacheEntryForMaxAgeCapValidation(request, redirectRequest, redirectResponse);
652 if (redirectResponse.source() == ResourceResponse::Source::Network && canUseCachedRedirect(request))
653 m_cache->storeRedirect(request, redirectResponse, redirectRequest, maxAgeCap);
655 if (m_networkLoadChecker) {
656 if (adClickConversion)
657 m_networkLoadChecker->enableContentExtensionsCheck();
658 m_networkLoadChecker->storeRedirectionIfNeeded(request, redirectResponse);
659 m_networkLoadChecker->checkRedirection(WTFMove(request), WTFMove(redirectRequest), WTFMove(redirectResponse), this, [protectedThis = makeRef(*this), this, storedCredentialsPolicy = m_networkLoadChecker->storedCredentialsPolicy(), adClickConversion = WTFMove(adClickConversion)](auto&& result) mutable {
660 if (!result.has_value()) {
661 if (result.error().isCancellation())
664 this->didFailLoading(result.error());
668 if (m_parameters.options.redirect == FetchOptions::Redirect::Manual) {
669 this->didFinishWithRedirectResponse(WTFMove(result->request), WTFMove(result->redirectRequest), WTFMove(result->redirectResponse));
673 if (this->isSynchronous()) {
674 if (storedCredentialsPolicy != m_networkLoadChecker->storedCredentialsPolicy()) {
675 // We need to restart the load to update the session according the new credential policy.
676 this->restartNetworkLoad(WTFMove(result->redirectRequest));
680 // We do not support prompting for credentials for synchronous loads. If we ever change this policy then
681 // we need to take care to prompt if and only if request and redirectRequest are not mixed content.
682 this->continueWillSendRequest(WTFMove(result->redirectRequest), false);
686 m_shouldRestartLoad = storedCredentialsPolicy != m_networkLoadChecker->storedCredentialsPolicy();
687 this->continueWillSendRedirectedRequest(WTFMove(result->request), WTFMove(result->redirectRequest), WTFMove(result->redirectResponse), WTFMove(adClickConversion));
691 continueWillSendRedirectedRequest(WTFMove(request), WTFMove(redirectRequest), WTFMove(redirectResponse), WTFMove(adClickConversion));
694 void NetworkResourceLoader::continueWillSendRedirectedRequest(ResourceRequest&& request, ResourceRequest&& redirectRequest, ResourceResponse&& redirectResponse, Optional<AdClickAttribution::Conversion>&& adClickConversion)
696 ASSERT(!isSynchronous());
699 continueWillSendRequest(WTFMove(redirectRequest), false);
703 NetworkSession* networkSession = nullptr;
704 if (adClickConversion && (networkSession = m_connection->networkProcess().networkSession(sessionID())))
705 networkSession->handleAdClickAttributionConversion(WTFMove(*adClickConversion), request.url(), redirectRequest);
707 send(Messages::WebResourceLoader::WillSendRequest(redirectRequest, sanitizeResponseIfPossible(WTFMove(redirectResponse), ResourceResponse::SanitizationType::Redirection)));
710 void NetworkResourceLoader::didFinishWithRedirectResponse(WebCore::ResourceRequest&& request, WebCore::ResourceRequest&& redirectRequest, ResourceResponse&& redirectResponse)
712 redirectResponse.setType(ResourceResponse::Type::Opaqueredirect);
713 if (!isCrossOriginPrefetch())
714 didReceiveResponse(WTFMove(redirectResponse), [] (auto) { });
715 else if (auto* session = m_connection->networkProcess().networkSession(sessionID()))
716 session->prefetchCache().storeRedirect(m_networkLoad->currentRequest().url(), WTFMove(redirectResponse), WTFMove(redirectRequest));
718 WebCore::NetworkLoadMetrics networkLoadMetrics;
719 networkLoadMetrics.markComplete();
720 networkLoadMetrics.responseBodyBytesReceived = 0;
721 networkLoadMetrics.responseBodyDecodedSize = 0;
722 send(Messages::WebResourceLoader::DidFinishResourceLoad { networkLoadMetrics });
724 cleanup(LoadResult::Success);
727 ResourceResponse NetworkResourceLoader::sanitizeResponseIfPossible(ResourceResponse&& response, ResourceResponse::SanitizationType type)
729 if (m_parameters.shouldRestrictHTTPResponseAccess)
730 response.sanitizeHTTPHeaderFields(type);
732 return WTFMove(response);
735 void NetworkResourceLoader::restartNetworkLoad(WebCore::ResourceRequest&& newRequest)
738 m_networkLoad->cancel();
740 startNetworkLoad(WTFMove(newRequest), FirstLoad::No);
743 void NetworkResourceLoader::continueWillSendRequest(ResourceRequest&& newRequest, bool isAllowedToAskUserForCredentials)
745 if (m_shouldRestartLoad) {
746 m_shouldRestartLoad = false;
749 m_networkLoad->updateRequestAfterRedirection(newRequest);
751 restartNetworkLoad(WTFMove(newRequest));
755 if (m_networkLoadChecker) {
756 // FIXME: We should be doing this check when receiving the redirection and not allow about protocol as per fetch spec.
757 if (!newRequest.url().protocolIsInHTTPFamily() && !newRequest.url().protocolIsAbout() && m_redirectCount) {
758 didFailLoading(ResourceError { String { }, 0, newRequest.url(), "Redirection to URL with a scheme that is not HTTP(S)"_s, ResourceError::Type::AccessControl });
763 RELEASE_LOG_IF_ALLOWED("continueWillSendRequest: (pageID = %" PRIu64 ", frameID = %" PRIu64 ", resourceID = %" PRIu64 ")", m_parameters.webPageID.toUInt64(), m_parameters.webFrameID.toUInt64(), m_parameters.identifier);
765 m_isAllowedToAskUserForCredentials = isAllowedToAskUserForCredentials;
767 // If there is a match in the network cache, we need to reuse the original cache policy and partition.
768 newRequest.setCachePolicy(originalRequest().cachePolicy());
769 newRequest.setCachePartition(originalRequest().cachePartition());
771 if (m_isWaitingContinueWillSendRequestForCachedRedirect) {
772 m_isWaitingContinueWillSendRequestForCachedRedirect = false;
774 LOG(NetworkCache, "(NetworkProcess) Retrieving cached redirect");
776 if (canUseCachedRedirect(newRequest))
777 retrieveCacheEntry(newRequest);
779 startNetworkLoad(WTFMove(newRequest), FirstLoad::Yes);
785 m_networkLoad->continueWillSendRequest(WTFMove(newRequest));
788 void NetworkResourceLoader::continueDidReceiveResponse()
790 if (m_cacheEntryWaitingForContinueDidReceiveResponse) {
791 sendResultForCacheEntry(WTFMove(m_cacheEntryWaitingForContinueDidReceiveResponse));
792 cleanup(LoadResult::Success);
796 if (m_responseCompletionHandler)
797 m_responseCompletionHandler(PolicyAction::Use);
800 void NetworkResourceLoader::didSendData(unsigned long long bytesSent, unsigned long long totalBytesToBeSent)
802 if (!isSynchronous())
803 send(Messages::WebResourceLoader::DidSendData(bytesSent, totalBytesToBeSent));
806 void NetworkResourceLoader::startBufferingTimerIfNeeded()
810 if (m_bufferingTimer.isActive())
812 m_bufferingTimer.startOneShot(m_parameters.maximumBufferingTime);
815 void NetworkResourceLoader::bufferingTimerFired()
817 ASSERT(m_bufferedData);
818 ASSERT(m_networkLoad);
820 if (m_bufferedData->isEmpty())
823 send(Messages::WebResourceLoader::DidReceiveData({ *m_bufferedData }, m_bufferedDataEncodedDataLength));
825 m_bufferedData = SharedBuffer::create();
826 m_bufferedDataEncodedDataLength = 0;
829 void NetworkResourceLoader::sendBuffer(SharedBuffer& buffer, size_t encodedDataLength)
831 ASSERT(!isSynchronous());
833 send(Messages::WebResourceLoader::DidReceiveData({ buffer }, encodedDataLength));
836 void NetworkResourceLoader::tryStoreAsCacheEntry()
838 if (!canUseCache(m_networkLoad->currentRequest()))
840 if (!m_bufferedDataForCache)
843 if (isCrossOriginPrefetch()) {
844 if (auto* session = m_connection->networkProcess().networkSession(sessionID()))
845 session->prefetchCache().store(m_networkLoad->currentRequest().url(), WTFMove(m_response), WTFMove(m_bufferedDataForCache));
848 m_cache->store(m_networkLoad->currentRequest(), m_response, WTFMove(m_bufferedDataForCache), [loader = makeRef(*this)](auto& mappedBody) mutable {
849 #if ENABLE(SHAREABLE_RESOURCE)
850 if (mappedBody.shareableResourceHandle.isNull())
852 LOG(NetworkCache, "(NetworkProcess) sending DidCacheResource");
853 loader->send(Messages::NetworkProcessConnection::DidCacheResource(loader->originalRequest(), mappedBody.shareableResourceHandle, loader->sessionID()));
858 void NetworkResourceLoader::didRetrieveCacheEntry(std::unique_ptr<NetworkCache::Entry> entry)
860 auto response = entry->response();
862 if (isMainResource() && shouldInterruptLoadForCSPFrameAncestorsOrXFrameOptions(response)) {
863 response = sanitizeResponseIfPossible(WTFMove(response), ResourceResponse::SanitizationType::CrossOriginSafe);
864 send(Messages::WebResourceLoader::StopLoadingAfterXFrameOptionsOrContentSecurityPolicyDenied { response });
867 if (m_networkLoadChecker) {
868 auto error = m_networkLoadChecker->validateResponse(response);
869 if (!error.isNull()) {
870 didFailLoading(error);
875 response = sanitizeResponseIfPossible(WTFMove(response), ResourceResponse::SanitizationType::CrossOriginSafe);
876 if (isSynchronous()) {
877 m_synchronousLoadData->response = WTFMove(response);
878 sendReplyToSynchronousRequest(*m_synchronousLoadData, entry->buffer());
879 cleanup(LoadResult::Success);
883 bool needsContinueDidReceiveResponseMessage = isMainResource();
884 send(Messages::WebResourceLoader::DidReceiveResponse { response, needsContinueDidReceiveResponseMessage });
886 if (needsContinueDidReceiveResponseMessage)
887 m_cacheEntryWaitingForContinueDidReceiveResponse = WTFMove(entry);
889 sendResultForCacheEntry(WTFMove(entry));
890 cleanup(LoadResult::Success);
894 void NetworkResourceLoader::sendResultForCacheEntry(std::unique_ptr<NetworkCache::Entry> entry)
896 #if ENABLE(SHAREABLE_RESOURCE)
897 if (!entry->shareableResourceHandle().isNull()) {
898 send(Messages::WebResourceLoader::DidReceiveResource(entry->shareableResourceHandle()));
903 #if ENABLE(RESOURCE_LOAD_STATISTICS) && !RELEASE_LOG_DISABLED
904 if (shouldLogCookieInformation(m_connection, sessionID()))
905 logCookieInformation();
908 WebCore::NetworkLoadMetrics networkLoadMetrics;
909 networkLoadMetrics.markComplete();
910 networkLoadMetrics.requestHeaderBytesSent = 0;
911 networkLoadMetrics.requestBodyBytesSent = 0;
912 networkLoadMetrics.responseHeaderBytesReceived = 0;
913 networkLoadMetrics.responseBodyBytesReceived = 0;
914 networkLoadMetrics.responseBodyDecodedSize = 0;
916 sendBuffer(*entry->buffer(), entry->buffer()->size());
917 send(Messages::WebResourceLoader::DidFinishResourceLoad(networkLoadMetrics));
920 void NetworkResourceLoader::validateCacheEntry(std::unique_ptr<NetworkCache::Entry> entry)
922 ASSERT(!m_networkLoad);
924 // If the request is already conditional then the revalidation was not triggered by the disk cache
925 // and we should not overwrite the existing conditional headers.
926 ResourceRequest revalidationRequest = originalRequest();
927 if (!revalidationRequest.isConditional()) {
928 String eTag = entry->response().httpHeaderField(HTTPHeaderName::ETag);
929 String lastModified = entry->response().httpHeaderField(HTTPHeaderName::LastModified);
931 revalidationRequest.setHTTPHeaderField(HTTPHeaderName::IfNoneMatch, eTag);
932 if (!lastModified.isEmpty())
933 revalidationRequest.setHTTPHeaderField(HTTPHeaderName::IfModifiedSince, lastModified);
936 m_cacheEntryForValidation = WTFMove(entry);
938 startNetworkLoad(WTFMove(revalidationRequest), FirstLoad::Yes);
941 void NetworkResourceLoader::dispatchWillSendRequestForCacheEntry(ResourceRequest&& request, std::unique_ptr<NetworkCache::Entry>&& entry)
943 ASSERT(entry->redirectRequest());
944 ASSERT(!m_isWaitingContinueWillSendRequestForCachedRedirect);
946 LOG(NetworkCache, "(NetworkProcess) Executing cached redirect");
948 m_isWaitingContinueWillSendRequestForCachedRedirect = true;
949 willSendRedirectedRequest(WTFMove(request), ResourceRequest { *entry->redirectRequest() }, ResourceResponse { entry->response() });
952 IPC::Connection* NetworkResourceLoader::messageSenderConnection() const
954 return &connectionToWebProcess().connection();
957 void NetworkResourceLoader::consumeSandboxExtensions()
959 ASSERT(!m_didConsumeSandboxExtensions);
961 for (auto& extension : m_parameters.requestBodySandboxExtensions)
962 extension->consume();
964 if (auto& extension = m_parameters.resourceSandboxExtension)
965 extension->consume();
967 for (auto& fileReference : m_fileReferences)
968 fileReference->prepareForFileAccess();
970 m_didConsumeSandboxExtensions = true;
973 void NetworkResourceLoader::invalidateSandboxExtensions()
975 if (m_didConsumeSandboxExtensions) {
976 for (auto& extension : m_parameters.requestBodySandboxExtensions)
978 if (auto& extension = m_parameters.resourceSandboxExtension)
980 for (auto& fileReference : m_fileReferences)
981 fileReference->revokeFileAccess();
983 m_didConsumeSandboxExtensions = false;
986 m_fileReferences.clear();
989 bool NetworkResourceLoader::isAlwaysOnLoggingAllowed() const
991 if (m_connection->networkProcess().sessionIsControlledByAutomation(sessionID()))
994 return sessionID().isAlwaysOnLoggingAllowed();
997 bool NetworkResourceLoader::shouldCaptureExtraNetworkLoadMetrics() const
999 return m_shouldCaptureExtraNetworkLoadMetrics;
1002 #if ENABLE(RESOURCE_LOAD_STATISTICS) && !RELEASE_LOG_DISABLED
1003 bool NetworkResourceLoader::shouldLogCookieInformation(NetworkConnectionToWebProcess& connection, const PAL::SessionID& sessionID)
1005 if (auto* session = connection.networkProcess().networkSession(sessionID))
1006 return session->shouldLogCookieInformation();
1010 static String escapeForJSON(String s)
1012 return s.replace('\\', "\\\\").replace('"', "\\\"");
1015 static String escapeIDForJSON(const Optional<uint64_t>& value)
1017 return value ? String::number(value.value()) : String("None"_s);
1020 static String escapeIDForJSON(const Optional<FrameIdentifier>& value)
1022 return value ? String::number(value->toUInt64()) : String("None"_s);
1025 static String escapeIDForJSON(const Optional<PageIdentifier>& value)
1027 return value ? String::number(value->toUInt64()) : String("None"_s);
1030 void NetworkResourceLoader::logCookieInformation() const
1032 ASSERT(shouldLogCookieInformation(m_connection, sessionID()));
1034 auto* networkStorageSession = m_connection->networkProcess().storageSession(sessionID());
1035 ASSERT(networkStorageSession);
1037 logCookieInformation(m_connection, "NetworkResourceLoader", reinterpret_cast<const void*>(this), *networkStorageSession, originalRequest().firstPartyForCookies(), SameSiteInfo::create(originalRequest()), originalRequest().url(), originalRequest().httpReferrer(), frameID(), pageID(), identifier());
1040 static void logBlockedCookieInformation(NetworkConnectionToWebProcess& connection, const String& label, const void* loggedObject, const WebCore::NetworkStorageSession& networkStorageSession, const URL& firstParty, const SameSiteInfo& sameSiteInfo, const URL& url, const String& referrer, Optional<FrameIdentifier> frameID, Optional<PageIdentifier> pageID, Optional<uint64_t> identifier)
1042 ASSERT(NetworkResourceLoader::shouldLogCookieInformation(connection, networkStorageSession.sessionID()));
1044 auto escapedURL = escapeForJSON(url.string());
1045 auto escapedFirstParty = escapeForJSON(firstParty.string());
1046 auto escapedFrameID = escapeIDForJSON(frameID);
1047 auto escapedPageID = escapeIDForJSON(pageID);
1048 auto escapedIdentifier = escapeIDForJSON(identifier);
1049 auto escapedReferrer = escapeForJSON(referrer);
1051 #define LOCAL_LOG_IF_ALLOWED(fmt, ...) RELEASE_LOG_IF(networkStorageSession.sessionID().isAlwaysOnLoggingAllowed(), Network, "%p - %s::" fmt, loggedObject, label.utf8().data(), ##__VA_ARGS__)
1052 #define LOCAL_LOG(str, ...) \
1053 LOCAL_LOG_IF_ALLOWED("logCookieInformation: BLOCKED cookie access for pageID = %s, frameID = %s, resourceID = %s, firstParty = %s: " str, escapedPageID.utf8().data(), escapedFrameID.utf8().data(), escapedIdentifier.utf8().data(), escapedFirstParty.utf8().data(), ##__VA_ARGS__)
1055 LOCAL_LOG(R"({ "url": "%{public}s",)", escapedURL.utf8().data());
1056 LOCAL_LOG(R"( "partition": "%{public}s",)", "BLOCKED");
1057 LOCAL_LOG(R"( "hasStorageAccess": %{public}s,)", "false");
1058 LOCAL_LOG(R"( "referer": "%{public}s",)", escapedReferrer.utf8().data());
1059 LOCAL_LOG(R"( "isSameSite": "%{public}s",)", sameSiteInfo.isSameSite ? "true" : "false");
1060 LOCAL_LOG(R"( "isTopSite": "%{public}s",)", sameSiteInfo.isTopSite ? "true" : "false");
1061 LOCAL_LOG(R"( "cookies": [])");
1064 #undef LOCAL_LOG_IF_ALLOWED
1067 static void logCookieInformationInternal(NetworkConnectionToWebProcess& connection, const String& label, const void* loggedObject, const WebCore::NetworkStorageSession& networkStorageSession, const URL& firstParty, const WebCore::SameSiteInfo& sameSiteInfo, const URL& url, const String& referrer, Optional<FrameIdentifier> frameID, Optional<PageIdentifier> pageID, Optional<uint64_t> identifier)
1069 ASSERT(NetworkResourceLoader::shouldLogCookieInformation(connection, networkStorageSession.sessionID()));
1071 Vector<WebCore::Cookie> cookies;
1072 if (!networkStorageSession.getRawCookies(firstParty, sameSiteInfo, url, frameID, pageID, cookies))
1075 auto escapedURL = escapeForJSON(url.string());
1076 auto escapedPartition = escapeForJSON(emptyString());
1077 auto escapedReferrer = escapeForJSON(referrer);
1078 auto escapedFrameID = escapeIDForJSON(frameID);
1079 auto escapedPageID = escapeIDForJSON(pageID);
1080 auto escapedIdentifier = escapeIDForJSON(identifier);
1081 bool hasStorageAccess = (frameID && pageID) ? networkStorageSession.hasStorageAccess(WebCore::RegistrableDomain { url }, WebCore::RegistrableDomain { firstParty }, frameID.value(), pageID.value()) : false;
1083 #define LOCAL_LOG_IF_ALLOWED(fmt, ...) RELEASE_LOG_IF(networkStorageSession.sessionID().isAlwaysOnLoggingAllowed(), Network, "%p - %s::" fmt, loggedObject, label.utf8().data(), ##__VA_ARGS__)
1084 #define LOCAL_LOG(str, ...) \
1085 LOCAL_LOG_IF_ALLOWED("logCookieInformation: pageID = %s, frameID = %s, resourceID = %s: " str, escapedPageID.utf8().data(), escapedFrameID.utf8().data(), escapedIdentifier.utf8().data(), ##__VA_ARGS__)
1087 LOCAL_LOG(R"({ "url": "%{public}s",)", escapedURL.utf8().data());
1088 LOCAL_LOG(R"( "partition": "%{public}s",)", escapedPartition.utf8().data());
1089 LOCAL_LOG(R"( "hasStorageAccess": %{public}s,)", hasStorageAccess ? "true" : "false");
1090 LOCAL_LOG(R"( "referer": "%{public}s",)", escapedReferrer.utf8().data());
1091 LOCAL_LOG(R"( "isSameSite": "%{public}s",)", sameSiteInfo.isSameSite ? "true" : "false");
1092 LOCAL_LOG(R"( "isTopSite": "%{public}s",)", sameSiteInfo.isTopSite ? "true" : "false");
1093 LOCAL_LOG(R"( "cookies": [)");
1095 auto size = cookies.size();
1096 decltype(size) count = 0;
1097 for (const auto& cookie : cookies) {
1098 const char* trailingComma = ",";
1099 if (++count == size)
1102 auto escapedName = escapeForJSON(cookie.name);
1103 auto escapedValue = escapeForJSON(cookie.value);
1104 auto escapedDomain = escapeForJSON(cookie.domain);
1105 auto escapedPath = escapeForJSON(cookie.path);
1106 auto escapedComment = escapeForJSON(cookie.comment);
1107 auto escapedCommentURL = escapeForJSON(cookie.commentURL.string());
1108 // FIXME: Log Same-Site policy for each cookie. See <https://bugs.webkit.org/show_bug.cgi?id=184894>.
1110 LOCAL_LOG(R"( { "name": "%{public}s",)", escapedName.utf8().data());
1111 LOCAL_LOG(R"( "value": "%{public}s",)", escapedValue.utf8().data());
1112 LOCAL_LOG(R"( "domain": "%{public}s",)", escapedDomain.utf8().data());
1113 LOCAL_LOG(R"( "path": "%{public}s",)", escapedPath.utf8().data());
1114 LOCAL_LOG(R"( "created": %f,)", cookie.created);
1115 LOCAL_LOG(R"( "expires": %f,)", cookie.expires);
1116 LOCAL_LOG(R"( "httpOnly": %{public}s,)", cookie.httpOnly ? "true" : "false");
1117 LOCAL_LOG(R"( "secure": %{public}s,)", cookie.secure ? "true" : "false");
1118 LOCAL_LOG(R"( "session": %{public}s,)", cookie.session ? "true" : "false");
1119 LOCAL_LOG(R"( "comment": "%{public}s",)", escapedComment.utf8().data());
1120 LOCAL_LOG(R"( "commentURL": "%{public}s")", escapedCommentURL.utf8().data());
1121 LOCAL_LOG(R"( }%{public}s)", trailingComma);
1125 #undef LOCAL_LOG_IF_ALLOWED
1128 void NetworkResourceLoader::logCookieInformation(NetworkConnectionToWebProcess& connection, const String& label, const void* loggedObject, const NetworkStorageSession& networkStorageSession, const URL& firstParty, const SameSiteInfo& sameSiteInfo, const URL& url, const String& referrer, Optional<FrameIdentifier> frameID, Optional<PageIdentifier> pageID, Optional<uint64_t> identifier)
1130 ASSERT(shouldLogCookieInformation(connection, networkStorageSession.sessionID()));
1132 if (networkStorageSession.shouldBlockCookies(firstParty, url, frameID, pageID))
1133 logBlockedCookieInformation(connection, label, loggedObject, networkStorageSession, firstParty, sameSiteInfo, url, referrer, frameID, pageID, identifier);
1135 logCookieInformationInternal(connection, label, loggedObject, networkStorageSession, firstParty, sameSiteInfo, url, referrer, frameID, pageID, identifier);
1139 void NetworkResourceLoader::addConsoleMessage(MessageSource messageSource, MessageLevel messageLevel, const String& message, unsigned long)
1141 send(Messages::WebPage::AddConsoleMessage { m_parameters.webFrameID, messageSource, messageLevel, message, identifier() }, m_parameters.webPageID);
1144 void NetworkResourceLoader::sendCSPViolationReport(URL&& reportURL, Ref<FormData>&& report)
1146 send(Messages::WebPage::SendCSPViolationReport { m_parameters.webFrameID, WTFMove(reportURL), IPC::FormDataReference { WTFMove(report) } }, m_parameters.webPageID);
1149 void NetworkResourceLoader::enqueueSecurityPolicyViolationEvent(WebCore::SecurityPolicyViolationEvent::Init&& eventInit)
1151 send(Messages::WebPage::EnqueueSecurityPolicyViolationEvent { m_parameters.webFrameID, WTFMove(eventInit) }, m_parameters.webPageID);
1154 void NetworkResourceLoader::logSlowCacheRetrieveIfNeeded(const NetworkCache::Cache::RetrieveInfo& info)
1156 #if RELEASE_LOG_DISABLED
1159 if (!isAlwaysOnLoggingAllowed())
1161 auto duration = info.completionTime - info.startTime;
1164 RELEASE_LOG_IF_ALLOWED("logSlowCacheRetrieveIfNeeded: Took %.0fms, priority %d", duration.milliseconds(), info.priority);
1165 if (info.wasSpeculativeLoad)
1166 RELEASE_LOG_IF_ALLOWED("logSlowCacheRetrieveIfNeeded: Was speculative load");
1167 if (!info.storageTimings.startTime)
1169 RELEASE_LOG_IF_ALLOWED("logSlowCacheRetrieveIfNeeded: Storage retrieve time %.0fms", (info.storageTimings.completionTime - info.storageTimings.startTime).milliseconds());
1170 if (info.storageTimings.dispatchTime) {
1171 auto time = (info.storageTimings.dispatchTime - info.storageTimings.startTime).milliseconds();
1172 auto count = info.storageTimings.dispatchCountAtDispatch - info.storageTimings.dispatchCountAtStart;
1173 RELEASE_LOG_IF_ALLOWED("logSlowCacheRetrieveIfNeeded: Dispatch delay %.0fms, dispatched %lu resources first", time, count);
1175 if (info.storageTimings.recordIOStartTime)
1176 RELEASE_LOG_IF_ALLOWED("logSlowCacheRetrieveIfNeeded: Record I/O time %.0fms", (info.storageTimings.recordIOEndTime - info.storageTimings.recordIOStartTime).milliseconds());
1177 if (info.storageTimings.blobIOStartTime)
1178 RELEASE_LOG_IF_ALLOWED("logSlowCacheRetrieveIfNeeded: Blob I/O time %.0fms", (info.storageTimings.blobIOEndTime - info.storageTimings.blobIOStartTime).milliseconds());
1179 if (info.storageTimings.synchronizationInProgressAtDispatch)
1180 RELEASE_LOG_IF_ALLOWED("logSlowCacheRetrieveIfNeeded: Synchronization was in progress");
1181 if (info.storageTimings.shrinkInProgressAtDispatch)
1182 RELEASE_LOG_IF_ALLOWED("logSlowCacheRetrieveIfNeeded: Shrink was in progress");
1183 if (info.storageTimings.wasCanceled)
1184 RELEASE_LOG_IF_ALLOWED("logSlowCacheRetrieveIfNeeded: Retrieve was canceled");
1188 bool NetworkResourceLoader::isCrossOriginPrefetch() const
1190 auto& request = originalRequest();
1191 return request.httpHeaderField(HTTPHeaderName::Purpose) == "prefetch" && !m_parameters.sourceOrigin->canRequest(request.url());
1194 } // namespace WebKit
1196 #undef RELEASE_LOG_IF_ALLOWED
1197 #undef RELEASE_LOG_ERROR_IF_ALLOWED