6539536ea4b97bb30b457c9d8b2a8dc1c8da7bad
[WebKit-https.git] / Source / WebKit / NetworkProcess / NetworkResourceLoader.cpp
1 /*
2  * Copyright (C) 2012-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  * 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.
12  *
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.
24  */
25
26 #include "config.h"
27 #include "NetworkResourceLoader.h"
28
29 #include "DataReference.h"
30 #include "FormDataReference.h"
31 #include "Logging.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 "SharedBufferDataReference.h"
39 #include "WebCoreArgumentCoders.h"
40 #include "WebErrors.h"
41 #include "WebPageMessages.h"
42 #include "WebResourceLoaderMessages.h"
43 #include "WebsiteDataStoreParameters.h"
44 #include <WebCore/BlobDataFileReference.h>
45 #include <WebCore/CertificateInfo.h>
46 #include <WebCore/ContentSecurityPolicy.h>
47 #include <WebCore/DiagnosticLoggingKeys.h>
48 #include <WebCore/HTTPParsers.h>
49 #include <WebCore/NetworkLoadMetrics.h>
50 #include <WebCore/NetworkStorageSession.h>
51 #include <WebCore/RegistrableDomain.h>
52 #include <WebCore/SameSiteInfo.h>
53 #include <WebCore/SecurityOrigin.h>
54 #include <WebCore/SharedBuffer.h>
55 #include <wtf/RunLoop.h>
56
57 #if USE(QUICK_LOOK)
58 #include <WebCore/PreviewConverter.h>
59 #endif
60
61 #define RELEASE_LOG_IF_ALLOWED(fmt, ...) RELEASE_LOG_IF(isAlwaysOnLoggingAllowed(), Network, "%p - NetworkResourceLoader::" fmt, this, ##__VA_ARGS__)
62 #define RELEASE_LOG_ERROR_IF_ALLOWED(fmt, ...) RELEASE_LOG_ERROR_IF(isAlwaysOnLoggingAllowed(), Network, "%p - NetworkResourceLoader::" fmt, this, ##__VA_ARGS__)
63
64 namespace WebKit {
65 using namespace WebCore;
66
67 struct NetworkResourceLoader::SynchronousLoadData {
68     WTF_MAKE_STRUCT_FAST_ALLOCATED;
69
70     SynchronousLoadData(Messages::NetworkConnectionToWebProcess::PerformSynchronousLoad::DelayedReply&& reply)
71         : delayedReply(WTFMove(reply))
72     {
73         ASSERT(delayedReply);
74     }
75     ResourceRequest currentRequest;
76     Messages::NetworkConnectionToWebProcess::PerformSynchronousLoad::DelayedReply delayedReply;
77     ResourceResponse response;
78     ResourceError error;
79 };
80
81 static void sendReplyToSynchronousRequest(NetworkResourceLoader::SynchronousLoadData& data, const SharedBuffer* buffer)
82 {
83     ASSERT(data.delayedReply);
84     ASSERT(!data.response.isNull() || !data.error.isNull());
85
86     Vector<char> responseBuffer;
87     if (buffer && buffer->size())
88         responseBuffer.append(buffer->data(), buffer->size());
89
90     data.delayedReply(data.error, data.response, responseBuffer);
91     data.delayedReply = nullptr;
92 }
93
94 NetworkResourceLoader::NetworkResourceLoader(NetworkResourceLoadParameters&& parameters, NetworkConnectionToWebProcess& connection, Messages::NetworkConnectionToWebProcess::PerformSynchronousLoad::DelayedReply&& synchronousReply)
95     : m_parameters { WTFMove(parameters) }
96     , m_connection { connection }
97     , m_fileReferences(connection.resolveBlobReferences(m_parameters))
98     , m_isAllowedToAskUserForCredentials { m_parameters.clientCredentialPolicy == ClientCredentialPolicy::MayAskClientForCredentials }
99     , m_bufferingTimer { *this, &NetworkResourceLoader::bufferingTimerFired }
100     , m_cache { sessionID().isEphemeral() ? nullptr : connection.networkProcess().cache() }
101     , m_shouldCaptureExtraNetworkLoadMetrics(m_connection->captureExtraNetworkLoadMetricsEnabled())
102 {
103     ASSERT(RunLoop::isMain());
104     // FIXME: This is necessary because of the existence of EmptyFrameLoaderClient in WebCore.
105     //        Once bug 116233 is resolved, this ASSERT can just be "m_webPageID && m_webFrameID"
106     ASSERT((m_parameters.webPageID && m_parameters.webFrameID) || m_parameters.clientCredentialPolicy == ClientCredentialPolicy::CannotAskClientForCredentials);
107
108     if (synchronousReply || parameters.shouldRestrictHTTPResponseAccess) {
109         NetworkLoadChecker::LoadType requestLoadType = isMainFrameLoad() ? NetworkLoadChecker::LoadType::MainFrame : NetworkLoadChecker::LoadType::Other;
110         m_networkLoadChecker = std::make_unique<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);
111         if (m_parameters.cspResponseHeaders)
112             m_networkLoadChecker->setCSPResponseHeaders(ContentSecurityPolicyResponseHeaders { m_parameters.cspResponseHeaders.value() });
113 #if ENABLE(CONTENT_EXTENSIONS)
114         m_networkLoadChecker->setContentExtensionController(URL { m_parameters.mainDocumentURL }, m_parameters.userContentControllerIdentifier);
115 #endif
116     }
117     if (synchronousReply)
118         m_synchronousLoadData = std::make_unique<SynchronousLoadData>(WTFMove(synchronousReply));
119 }
120
121 NetworkResourceLoader::~NetworkResourceLoader()
122 {
123     ASSERT(RunLoop::isMain());
124     ASSERT(!m_networkLoad);
125     ASSERT(!isSynchronous() || !m_synchronousLoadData->delayedReply);
126     ASSERT(m_fileReferences.isEmpty());
127     if (m_responseCompletionHandler)
128         m_responseCompletionHandler(PolicyAction::Ignore);
129 }
130
131 bool NetworkResourceLoader::canUseCache(const ResourceRequest& request) const
132 {
133     if (!m_cache)
134         return false;
135     ASSERT(!sessionID().isEphemeral());
136
137     if (!request.url().protocolIsInHTTPFamily())
138         return false;
139     if (originalRequest().cachePolicy() == WebCore::ResourceRequestCachePolicy::DoNotUseAnyCache)
140         return false;
141
142     return true;
143 }
144
145 bool NetworkResourceLoader::canUseCachedRedirect(const ResourceRequest& request) const
146 {
147     if (!canUseCache(request) || m_cacheEntryForMaxAgeCapValidation)
148         return false;
149     // Limit cached redirects to avoid cycles and other trouble.
150     // Networking layer follows over 30 redirects but caching that many seems unnecessary.
151     static const unsigned maximumCachedRedirectCount { 5 };
152     if (m_redirectCount > maximumCachedRedirectCount)
153         return false;
154
155     return true;
156 }
157
158 bool NetworkResourceLoader::isSynchronous() const
159 {
160     return !!m_synchronousLoadData;
161 }
162
163 void NetworkResourceLoader::start()
164 {
165     ASSERT(RunLoop::isMain());
166
167     m_networkActivityTracker = m_connection->startTrackingResourceLoad(m_parameters.webPageID, m_parameters.identifier, isMainResource(), sessionID());
168
169     ASSERT(!m_wasStarted);
170     m_wasStarted = true;
171
172     if (m_networkLoadChecker) {
173         m_networkLoadChecker->check(ResourceRequest { originalRequest() }, this, [this] (auto&& result) {
174             WTF::switchOn(result,
175                 [this] (ResourceError& error) {
176                     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, m_parameters.webFrameID, m_parameters.identifier, this->isMainResource(), this->isSynchronous(), m_parameters.parentPID, error.domain().utf8().data(), error.errorCode());
177                     if (!error.isCancellation())
178                         this->didFailLoading(error);
179                 },
180                 [this] (NetworkLoadChecker::RedirectionTriplet& triplet) {
181                     this->m_isWaitingContinueWillSendRequestForCachedRedirect = true;
182                     this->willSendRedirectedRequest(WTFMove(triplet.request), WTFMove(triplet.redirectRequest), WTFMove(triplet.redirectResponse));
183                     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, m_parameters.webFrameID, m_parameters.identifier, this->isMainResource(), this->isSynchronous(), m_parameters.parentPID);
184                 },
185                 [this] (ResourceRequest& request) {
186                     if (this->canUseCache(request)) {
187                         RELEASE_LOG_IF_ALLOWED("start: Checking cache for resource (pageID = %" PRIu64 ", frameID = %" PRIu64 ", resourceID = %" PRIu64 ", isMainResource = %d, isSynchronous = %d, parentPID = %d)", m_parameters.webPageID, m_parameters.webFrameID, m_parameters.identifier, this->isMainResource(), this->isSynchronous(), m_parameters.parentPID);
188                         this->retrieveCacheEntry(request);
189                         return;
190                     }
191
192                     this->startNetworkLoad(WTFMove(request), FirstLoad::Yes);
193                 }
194             );
195         });
196         return;
197     }
198     // FIXME: Remove that code path once m_networkLoadChecker is used for all network loads.
199     if (canUseCache(originalRequest())) {
200         RELEASE_LOG_IF_ALLOWED("start: Checking cache for resource (pageID = %" PRIu64 ", frameID = %" PRIu64 ", resourceID = %" PRIu64 ", isMainResource = %d, isSynchronous = %d, parentPID = %d)", m_parameters.webPageID, m_parameters.webFrameID, m_parameters.identifier, isMainResource(), isSynchronous(), m_parameters.parentPID);
201         retrieveCacheEntry(originalRequest());
202         return;
203     }
204
205     startNetworkLoad(ResourceRequest { originalRequest() }, FirstLoad::Yes);
206 }
207
208 void NetworkResourceLoader::retrieveCacheEntry(const ResourceRequest& request)
209 {
210     ASSERT(canUseCache(request));
211
212     RefPtr<NetworkResourceLoader> loader(this);
213     m_cache->retrieve(request, { m_parameters.webPageID, m_parameters.webFrameID }, [this, loader = WTFMove(loader), request = ResourceRequest { request }](auto entry, auto info) mutable {
214         if (loader->hasOneRef()) {
215             // The loader has been aborted and is only held alive by this lambda.
216             return;
217         }
218
219         loader->logSlowCacheRetrieveIfNeeded(info);
220
221         if (!entry) {
222             RELEASE_LOG_IF_ALLOWED("retrieveCacheEntry: Resource not in cache (pageID = %" PRIu64 ", frameID = %" PRIu64 ", resourceID = %" PRIu64 ", isMainResource = %d, isSynchronous = %d)", m_parameters.webPageID, m_parameters.webFrameID, m_parameters.identifier, isMainResource(), isSynchronous());
223             loader->startNetworkLoad(WTFMove(request), FirstLoad::Yes);
224             return;
225         }
226 #if ENABLE(RESOURCE_LOAD_STATISTICS)
227         if (entry->hasReachedPrevalentResourceAgeCap()) {
228             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, m_parameters.webFrameID, m_parameters.identifier, isMainResource(), isSynchronous());
229             m_cacheEntryForMaxAgeCapValidation = WTFMove(entry);
230             ResourceRequest revalidationRequest = originalRequest();
231             loader->startNetworkLoad(WTFMove(revalidationRequest), FirstLoad::Yes);
232             return;
233         }
234 #endif
235         if (entry->redirectRequest()) {
236             RELEASE_LOG_IF_ALLOWED("retrieveCacheEntry: Handling redirect (pageID = %" PRIu64 ", frameID = %" PRIu64 ", resourceID = %" PRIu64 ", isMainResource = %d, isSynchronous = %d)", m_parameters.webPageID, m_parameters.webFrameID, m_parameters.identifier, isMainResource(), isSynchronous());
237             loader->dispatchWillSendRequestForCacheEntry(WTFMove(request), WTFMove(entry));
238             return;
239         }
240         if (loader->m_parameters.needsCertificateInfo && !entry->response().certificateInfo()) {
241             RELEASE_LOG_IF_ALLOWED("retrieveCacheEntry: Resource does not have required certificate (pageID = %" PRIu64 ", frameID = %" PRIu64 ", resourceID = %" PRIu64 ", isMainResource = %d, isSynchronous = %d)", m_parameters.webPageID, m_parameters.webFrameID, m_parameters.identifier, isMainResource(), isSynchronous());
242             loader->startNetworkLoad(WTFMove(request), FirstLoad::Yes);
243             return;
244         }
245         if (entry->needsValidation() || request.cachePolicy() == WebCore::ResourceRequestCachePolicy::RefreshAnyCacheData) {
246             RELEASE_LOG_IF_ALLOWED("retrieveCacheEntry: Validating cache entry (pageID = %" PRIu64 ", frameID = %" PRIu64 ", resourceID = %" PRIu64 ", isMainResource = %d, isSynchronous = %d)", m_parameters.webPageID, m_parameters.webFrameID, m_parameters.identifier, isMainResource(), isSynchronous());
247             loader->validateCacheEntry(WTFMove(entry));
248             return;
249         }
250         RELEASE_LOG_IF_ALLOWED("retrieveCacheEntry: Retrieved resource from cache (pageID = %" PRIu64 ", frameID = %" PRIu64 ", resourceID = %" PRIu64 ", isMainResource = %d, isSynchronous = %d)", m_parameters.webPageID, m_parameters.webFrameID, m_parameters.identifier, isMainResource(), isSynchronous());
251         loader->didRetrieveCacheEntry(WTFMove(entry));
252     });
253 }
254
255 void NetworkResourceLoader::startNetworkLoad(ResourceRequest&& request, FirstLoad load)
256 {
257     if (load == FirstLoad::Yes) {
258         RELEASE_LOG_IF_ALLOWED("startNetworkLoad: (pageID = %" PRIu64 ", frameID = %" PRIu64 ", resourceID = %" PRIu64 ", isMainResource = %d, isSynchronous = %d)", m_parameters.webPageID, m_parameters.webFrameID, m_parameters.identifier, isMainResource(), isSynchronous());
259
260         consumeSandboxExtensions();
261
262         if (isSynchronous() || m_parameters.maximumBufferingTime > 0_s)
263             m_bufferedData = SharedBuffer::create();
264
265         if (canUseCache(request))
266             m_bufferedDataForCache = SharedBuffer::create();
267     }
268
269     NetworkLoadParameters parameters = m_parameters;
270     parameters.networkActivityTracker = m_networkActivityTracker;
271     if (m_networkLoadChecker)
272         parameters.storedCredentialsPolicy = m_networkLoadChecker->storedCredentialsPolicy();
273
274     if (request.url().protocolIsBlob())
275         parameters.blobFileReferences = m_connection->filesInBlob(originalRequest().url());
276
277     auto* networkSession = m_connection->networkProcess().networkSession(parameters.sessionID);
278     if (!networkSession && parameters.sessionID.isEphemeral()) {
279         m_connection->networkProcess().addWebsiteDataStore(WebsiteDataStoreParameters::privateSessionParameters(parameters.sessionID));
280         networkSession = m_connection->networkProcess().networkSession(parameters.sessionID);
281     }
282     if (!networkSession) {
283         WTFLogAlways("Attempted to create a NetworkLoad with a session (id=%" PRIu64 ") that does not exist.", parameters.sessionID.sessionID());
284         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, m_parameters.webFrameID, m_parameters.identifier, parameters.sessionID.sessionID());
285         m_connection->networkProcess().logDiagnosticMessage(m_parameters.webPageID, WebCore::DiagnosticLoggingKeys::internalErrorKey(), WebCore::DiagnosticLoggingKeys::invalidSessionIDKey(), WebCore::ShouldSample::No);
286         didFailLoading(internalError(request.url()));
287         return;
288     }
289
290     parameters.request = WTFMove(request);
291     m_networkLoad = std::make_unique<NetworkLoad>(*this, &m_connection->blobRegistry(), WTFMove(parameters), *networkSession);
292
293     RELEASE_LOG_IF_ALLOWED("startNetworkLoad: (pageID = %" PRIu64 ", frameID = %" PRIu64 ", resourceID = %" PRIu64 ", description = %{public}s)", m_parameters.webPageID, m_parameters.webFrameID, m_parameters.identifier, m_networkLoad->description().utf8().data());
294 }
295
296 void NetworkResourceLoader::cleanup(LoadResult result)
297 {
298     ASSERT(RunLoop::isMain());
299
300     m_connection->stopTrackingResourceLoad(m_parameters.identifier,
301         result == LoadResult::Success ? NetworkActivityTracker::CompletionCode::Success :
302         result == LoadResult::Failure ? NetworkActivityTracker::CompletionCode::Failure :
303         NetworkActivityTracker::CompletionCode::None);
304
305     m_bufferingTimer.stop();
306
307     invalidateSandboxExtensions();
308
309     m_networkLoad = nullptr;
310
311     // This will cause NetworkResourceLoader to be destroyed and therefore we do it last.
312     m_connection->didCleanupResourceLoader(*this);
313 }
314
315 void NetworkResourceLoader::convertToDownload(DownloadID downloadID, const ResourceRequest& request, const ResourceResponse& response)
316 {
317     // This can happen if the resource came from the disk cache.
318     if (!m_networkLoad) {
319         m_connection->networkProcess().downloadManager().startDownload(m_parameters.sessionID, downloadID, request);
320         abort();
321         return;
322     }
323
324     if (m_responseCompletionHandler)
325         m_connection->networkProcess().downloadManager().convertNetworkLoadToDownload(downloadID, std::exchange(m_networkLoad, nullptr), WTFMove(m_responseCompletionHandler), WTFMove(m_fileReferences), request, response);
326 }
327
328 void NetworkResourceLoader::abort()
329 {
330     ASSERT(RunLoop::isMain());
331
332     RELEASE_LOG_IF_ALLOWED("abort: Canceling resource load (pageID = %" PRIu64 ", frameID = %" PRIu64 ", resourceID = %" PRIu64 ")",
333         m_parameters.webPageID, m_parameters.webFrameID, m_parameters.identifier);
334
335     if (m_networkLoad) {
336         if (canUseCache(m_networkLoad->currentRequest())) {
337             // We might already have used data from this incomplete load. Ensure older versions don't remain in the cache after cancel.
338             if (!m_response.isNull())
339                 m_cache->remove(m_networkLoad->currentRequest());
340         }
341         m_networkLoad->cancel();
342     }
343
344     cleanup(LoadResult::Cancel);
345 }
346
347 bool NetworkResourceLoader::shouldInterruptLoadForXFrameOptions(const String& xFrameOptions, const URL& url)
348 {
349     if (isMainFrameLoad())
350         return false;
351
352     switch (parseXFrameOptionsHeader(xFrameOptions)) {
353     case XFrameOptionsNone:
354     case XFrameOptionsAllowAll:
355         return false;
356     case XFrameOptionsDeny:
357         return true;
358     case XFrameOptionsSameOrigin: {
359         auto origin = SecurityOrigin::create(url);
360         auto topFrameOrigin = m_parameters.frameAncestorOrigins.last();
361         if (!origin->isSameSchemeHostPort(*topFrameOrigin))
362             return true;
363         for (auto& ancestorOrigin : m_parameters.frameAncestorOrigins) {
364             if (!origin->isSameSchemeHostPort(*ancestorOrigin))
365                 return true;
366         }
367         return false;
368     }
369     case XFrameOptionsConflict: {
370         String errorMessage = "Multiple 'X-Frame-Options' headers with conflicting values ('" + xFrameOptions + "') encountered when loading '" + url.stringCenterEllipsizedToLength() + "'. Falling back to 'DENY'.";
371         send(Messages::WebPage::AddConsoleMessage { m_parameters.webFrameID,  MessageSource::JS, MessageLevel::Error, errorMessage, identifier() }, m_parameters.webPageID);
372         return true;
373     }
374     case XFrameOptionsInvalid: {
375         String errorMessage = "Invalid 'X-Frame-Options' header encountered when loading '" + url.stringCenterEllipsizedToLength() + "': '" + xFrameOptions + "' is not a recognized directive. The header will be ignored.";
376         send(Messages::WebPage::AddConsoleMessage { m_parameters.webFrameID,  MessageSource::JS, MessageLevel::Error, errorMessage, identifier() }, m_parameters.webPageID);
377         return false;
378     }
379     }
380     ASSERT_NOT_REACHED();
381     return false;
382 }
383
384 bool NetworkResourceLoader::shouldInterruptLoadForCSPFrameAncestorsOrXFrameOptions(const ResourceResponse& response)
385 {
386     ASSERT(isMainResource());
387
388 #if USE(QUICK_LOOK)
389     if (PreviewConverter::supportsMIMEType(response.mimeType()))
390         return false;
391 #endif
392
393     auto url = response.url();
394     ContentSecurityPolicy contentSecurityPolicy { URL { url }, this };
395     contentSecurityPolicy.didReceiveHeaders(ContentSecurityPolicyResponseHeaders { response }, originalRequest().httpReferrer());
396     if (!contentSecurityPolicy.allowFrameAncestors(m_parameters.frameAncestorOrigins, url))
397         return true;
398     String xFrameOptions = m_response.httpHeaderField(HTTPHeaderName::XFrameOptions);
399     if (!xFrameOptions.isNull() && shouldInterruptLoadForXFrameOptions(xFrameOptions, response.url())) {
400         String errorMessage = "Refused to display '" + response.url().stringCenterEllipsizedToLength() + "' in a frame because it set 'X-Frame-Options' to '" + xFrameOptions + "'.";
401         send(Messages::WebPage::AddConsoleMessage { m_parameters.webFrameID,  MessageSource::Security, MessageLevel::Error, errorMessage, identifier() }, m_parameters.webPageID);
402         return true;
403     }
404     return false;
405 }
406
407 void NetworkResourceLoader::didReceiveResponse(ResourceResponse&& receivedResponse, ResponseCompletionHandler&& completionHandler)
408 {
409     RELEASE_LOG_IF_ALLOWED("didReceiveResponse: (pageID = %" PRIu64 ", frameID = %" PRIu64 ", resourceID = %" PRIu64 ", httpStatusCode = %d, length = %" PRId64 ")", m_parameters.webPageID, m_parameters.webFrameID, m_parameters.identifier, receivedResponse.httpStatusCode(), receivedResponse.expectedContentLength());
410
411     m_response = WTFMove(receivedResponse);
412
413     if (shouldCaptureExtraNetworkLoadMetrics() && m_networkLoadChecker) {
414         auto information = m_networkLoadChecker->takeNetworkLoadInformation();
415         information.response = m_response;
416         m_connection->addNetworkLoadInformation(identifier(), WTFMove(information));
417     }
418
419     // For multipart/x-mixed-replace didReceiveResponseAsync gets called multiple times and buffering would require special handling.
420     if (!isSynchronous() && m_response.isMultipart())
421         m_bufferedData = nullptr;
422
423     if (m_response.isMultipart())
424         m_bufferedDataForCache = nullptr;
425
426     if (m_cacheEntryForValidation) {
427         bool validationSucceeded = m_response.httpStatusCode() == 304; // 304 Not Modified
428         if (validationSucceeded) {
429             m_cacheEntryForValidation = m_cache->update(originalRequest(), { m_parameters.webPageID, m_parameters.webFrameID }, *m_cacheEntryForValidation, m_response);
430             // If the request was conditional then this revalidation was not triggered by the network cache and we pass the 304 response to WebCore.
431             if (originalRequest().isConditional())
432                 m_cacheEntryForValidation = nullptr;
433         } else
434             m_cacheEntryForValidation = nullptr;
435     }
436     if (m_cacheEntryForValidation)
437         return completionHandler(PolicyAction::Use);
438
439     if (isMainResource() && shouldInterruptLoadForCSPFrameAncestorsOrXFrameOptions(m_response)) {
440         auto response = sanitizeResponseIfPossible(ResourceResponse { m_response }, ResourceResponse::SanitizationType::CrossOriginSafe);
441         send(Messages::WebResourceLoader::StopLoadingAfterXFrameOptionsOrContentSecurityPolicyDenied { response });
442         return completionHandler(PolicyAction::Ignore);
443     }
444
445     if (m_networkLoadChecker) {
446         auto error = m_networkLoadChecker->validateResponse(m_response);
447         if (!error.isNull()) {
448             RunLoop::main().dispatch([protectedThis = makeRef(*this), error = WTFMove(error)] {
449                 if (protectedThis->m_networkLoad)
450                     protectedThis->didFailLoading(error);
451             });
452             return completionHandler(PolicyAction::Ignore);
453         }
454     }
455
456     auto response = sanitizeResponseIfPossible(ResourceResponse { m_response }, ResourceResponse::SanitizationType::CrossOriginSafe);
457     if (isSynchronous()) {
458         m_synchronousLoadData->response = WTFMove(response);
459         return completionHandler(PolicyAction::Use);
460     }
461
462     // We wait to receive message NetworkResourceLoader::ContinueDidReceiveResponse before continuing a load for
463     // a main resource because the embedding client must decide whether to allow the load.
464     bool willWaitForContinueDidReceiveResponse = isMainResource();
465     send(Messages::WebResourceLoader::DidReceiveResponse { response, willWaitForContinueDidReceiveResponse });
466     if (willWaitForContinueDidReceiveResponse)
467         m_responseCompletionHandler = WTFMove(completionHandler);
468     else
469         completionHandler(PolicyAction::Use);
470 }
471
472 void NetworkResourceLoader::didReceiveBuffer(Ref<SharedBuffer>&& buffer, int reportedEncodedDataLength)
473 {
474     if (!m_numBytesReceived)
475         RELEASE_LOG_IF_ALLOWED("didReceiveBuffer: Started receiving data (pageID = %" PRIu64 ", frameID = %" PRIu64 ", resourceID = %" PRIu64 ")", m_parameters.webPageID, m_parameters.webFrameID, m_parameters.identifier);
476     m_numBytesReceived += buffer->size();
477
478     ASSERT(!m_cacheEntryForValidation);
479
480     if (m_bufferedDataForCache) {
481         // Prevent memory growth in case of streaming data.
482         const size_t maximumCacheBufferSize = 10 * 1024 * 1024;
483         if (m_bufferedDataForCache->size() + buffer->size() <= maximumCacheBufferSize)
484             m_bufferedDataForCache->append(buffer.get());
485         else
486             m_bufferedDataForCache = nullptr;
487     }
488     // FIXME: At least on OS X Yosemite we always get -1 from the resource handle.
489     unsigned encodedDataLength = reportedEncodedDataLength >= 0 ? reportedEncodedDataLength : buffer->size();
490
491     if (m_bufferedData) {
492         m_bufferedData->append(buffer.get());
493         m_bufferedDataEncodedDataLength += encodedDataLength;
494         startBufferingTimerIfNeeded();
495         return;
496     }
497     sendBuffer(buffer, encodedDataLength);
498 }
499
500 void NetworkResourceLoader::didFinishLoading(const NetworkLoadMetrics& networkLoadMetrics)
501 {
502     RELEASE_LOG_IF_ALLOWED("didFinishLoading: (pageID = %" PRIu64 ", frameID = %" PRIu64 ", resourceID = %" PRIu64 ", length = %zd)", m_parameters.webPageID, m_parameters.webFrameID, m_parameters.identifier, m_numBytesReceived);
503
504     if (shouldCaptureExtraNetworkLoadMetrics())
505         m_connection->addNetworkLoadInformationMetrics(identifier(), networkLoadMetrics);
506
507     if (m_cacheEntryForValidation) {
508         // 304 Not Modified
509         ASSERT(m_response.httpStatusCode() == 304);
510         LOG(NetworkCache, "(NetworkProcess) revalidated");
511         didRetrieveCacheEntry(WTFMove(m_cacheEntryForValidation));
512         return;
513     }
514
515 #if ENABLE(RESOURCE_LOAD_STATISTICS) && !RELEASE_LOG_DISABLED
516     if (shouldLogCookieInformation(m_connection, sessionID()))
517         logCookieInformation();
518 #endif
519
520     if (isSynchronous())
521         sendReplyToSynchronousRequest(*m_synchronousLoadData, m_bufferedData.get());
522     else {
523         if (m_bufferedData && !m_bufferedData->isEmpty()) {
524             // FIXME: Pass a real value or remove the encoded data size feature.
525             sendBuffer(*m_bufferedData, -1);
526         }
527         send(Messages::WebResourceLoader::DidFinishResourceLoad(networkLoadMetrics));
528     }
529
530     tryStoreAsCacheEntry();
531
532     cleanup(LoadResult::Success);
533 }
534
535 void NetworkResourceLoader::didFailLoading(const ResourceError& error)
536 {
537     RELEASE_LOG_IF_ALLOWED("didFailLoading: (pageID = %" PRIu64 ", frameID = %" PRIu64 ", resourceID = %" PRIu64 ", isTimeout = %d, isCancellation = %d, isAccessControl = %d, errCode = %d)", m_parameters.webPageID, m_parameters.webFrameID, m_parameters.identifier, error.isTimeout(), error.isCancellation(), error.isAccessControl(), error.errorCode());
538
539     if (shouldCaptureExtraNetworkLoadMetrics())
540         m_connection->removeNetworkLoadInformation(identifier());
541
542     ASSERT(!error.isNull());
543
544     m_cacheEntryForValidation = nullptr;
545
546     if (isSynchronous()) {
547         m_synchronousLoadData->error = error;
548         sendReplyToSynchronousRequest(*m_synchronousLoadData, nullptr);
549     } else if (auto* connection = messageSenderConnection())
550         connection->send(Messages::WebResourceLoader::DidFailResourceLoad(error), messageSenderDestinationID());
551
552     cleanup(LoadResult::Failure);
553 }
554
555 void NetworkResourceLoader::didBlockAuthenticationChallenge()
556 {
557     send(Messages::WebResourceLoader::DidBlockAuthenticationChallenge());
558 }
559
560 Optional<Seconds> NetworkResourceLoader::validateCacheEntryForMaxAgeCapValidation(const ResourceRequest& request, const ResourceRequest& redirectRequest, const ResourceResponse& redirectResponse)
561 {
562 #if ENABLE(RESOURCE_LOAD_STATISTICS)
563     bool existingCacheEntryMatchesNewResponse = false;
564     if (m_cacheEntryForMaxAgeCapValidation) {
565         ASSERT(redirectResponse.source() == ResourceResponse::Source::Network);
566         ASSERT(redirectResponse.isRedirection());
567         if (redirectResponse.httpHeaderField(WebCore::HTTPHeaderName::Location) == m_cacheEntryForMaxAgeCapValidation->response().httpHeaderField(WebCore::HTTPHeaderName::Location))
568             existingCacheEntryMatchesNewResponse = true;
569
570         m_cache->remove(m_cacheEntryForMaxAgeCapValidation->key());
571         m_cacheEntryForMaxAgeCapValidation = nullptr;
572     }
573     
574     if (!existingCacheEntryMatchesNewResponse) {
575         if (auto* networkStorageSession = m_connection->networkProcess().storageSession(sessionID()))
576             return networkStorageSession->maxAgeCacheCap(request);
577     }
578 #endif
579     return WTF::nullopt;
580 }
581
582 void NetworkResourceLoader::willSendRedirectedRequest(ResourceRequest&& request, ResourceRequest&& redirectRequest, ResourceResponse&& redirectResponse)
583 {
584     ++m_redirectCount;
585
586     auto maxAgeCap = validateCacheEntryForMaxAgeCapValidation(request, redirectRequest, redirectResponse);
587     if (redirectResponse.source() == ResourceResponse::Source::Network && canUseCachedRedirect(request))
588         m_cache->storeRedirect(request, redirectResponse, redirectRequest, maxAgeCap);
589
590     if (m_networkLoadChecker) {
591         m_networkLoadChecker->storeRedirectionIfNeeded(request, redirectResponse);
592         m_networkLoadChecker->checkRedirection(WTFMove(request), WTFMove(redirectRequest), WTFMove(redirectResponse), this, [protectedThis = makeRef(*this), this, storedCredentialsPolicy = m_networkLoadChecker->storedCredentialsPolicy()](auto&& result) mutable {
593             if (!result.has_value()) {
594                 if (result.error().isCancellation())
595                     return;
596
597                 this->didFailLoading(result.error());
598                 return;
599             }
600
601             if (m_parameters.options.redirect == FetchOptions::Redirect::Manual) {
602                 this->didFinishWithRedirectResponse(WTFMove(result->redirectResponse));
603                 return;
604             }
605
606             if (this->isSynchronous()) {
607                 if (storedCredentialsPolicy != m_networkLoadChecker->storedCredentialsPolicy()) {
608                     // We need to restart the load to update the session according the new credential policy.
609                     this->restartNetworkLoad(WTFMove(result->redirectRequest));
610                     return;
611                 }
612
613                 // We do not support prompting for credentials for synchronous loads. If we ever change this policy then
614                 // we need to take care to prompt if and only if request and redirectRequest are not mixed content.
615                 this->continueWillSendRequest(WTFMove(result->redirectRequest), false);
616                 return;
617             }
618
619             m_shouldRestartLoad = storedCredentialsPolicy != m_networkLoadChecker->storedCredentialsPolicy();
620             this->continueWillSendRedirectedRequest(WTFMove(result->request), WTFMove(result->redirectRequest), WTFMove(result->redirectResponse));
621         });
622         return;
623     }
624     continueWillSendRedirectedRequest(WTFMove(request), WTFMove(redirectRequest), WTFMove(redirectResponse));
625 }
626
627 void NetworkResourceLoader::continueWillSendRedirectedRequest(ResourceRequest&& request, ResourceRequest&& redirectRequest, ResourceResponse&& redirectResponse)
628 {
629     ASSERT(!isSynchronous());
630
631     send(Messages::WebResourceLoader::WillSendRequest(redirectRequest, sanitizeResponseIfPossible(WTFMove(redirectResponse), ResourceResponse::SanitizationType::Redirection)));
632 }
633
634 void NetworkResourceLoader::didFinishWithRedirectResponse(ResourceResponse&& redirectResponse)
635 {
636     redirectResponse.setType(ResourceResponse::Type::Opaqueredirect);
637     didReceiveResponse(WTFMove(redirectResponse), [] (auto) { });
638
639     WebCore::NetworkLoadMetrics networkLoadMetrics;
640     networkLoadMetrics.markComplete();
641     networkLoadMetrics.responseBodyBytesReceived = 0;
642     networkLoadMetrics.responseBodyDecodedSize = 0;
643     send(Messages::WebResourceLoader::DidFinishResourceLoad { networkLoadMetrics });
644
645     cleanup(LoadResult::Success);
646 }
647
648 ResourceResponse NetworkResourceLoader::sanitizeResponseIfPossible(ResourceResponse&& response, ResourceResponse::SanitizationType type)
649 {
650     if (m_parameters.shouldRestrictHTTPResponseAccess)
651         response.sanitizeHTTPHeaderFields(type);
652
653     return WTFMove(response);
654 }
655
656 void NetworkResourceLoader::restartNetworkLoad(WebCore::ResourceRequest&& newRequest)
657 {
658     if (m_networkLoad)
659         m_networkLoad->cancel();
660
661     startNetworkLoad(WTFMove(newRequest), FirstLoad::No);
662 }
663
664 void NetworkResourceLoader::continueWillSendRequest(ResourceRequest&& newRequest, bool isAllowedToAskUserForCredentials)
665 {
666     if (m_shouldRestartLoad) {
667         m_shouldRestartLoad = false;
668         restartNetworkLoad(WTFMove(newRequest));
669         return;
670     }
671
672     if (m_networkLoadChecker) {
673         // FIXME: We should be doing this check when receiving the redirection and not allow about protocol as per fetch spec.
674         if (!newRequest.url().protocolIsInHTTPFamily() && !newRequest.url().protocolIsAbout() && m_redirectCount) {
675             didFailLoading(ResourceError { String { }, 0, newRequest.url(), "Redirection to URL with a scheme that is not HTTP(S)"_s, ResourceError::Type::AccessControl });
676             return;
677         }
678     }
679
680     RELEASE_LOG_IF_ALLOWED("continueWillSendRequest: (pageID = %" PRIu64 ", frameID = %" PRIu64 ", resourceID = %" PRIu64 ")", m_parameters.webPageID, m_parameters.webFrameID, m_parameters.identifier);
681
682     m_isAllowedToAskUserForCredentials = isAllowedToAskUserForCredentials;
683
684     // If there is a match in the network cache, we need to reuse the original cache policy and partition.
685     newRequest.setCachePolicy(originalRequest().cachePolicy());
686     newRequest.setCachePartition(originalRequest().cachePartition());
687
688     if (m_isWaitingContinueWillSendRequestForCachedRedirect) {
689         m_isWaitingContinueWillSendRequestForCachedRedirect = false;
690
691         LOG(NetworkCache, "(NetworkProcess) Retrieving cached redirect");
692
693         if (canUseCachedRedirect(newRequest))
694             retrieveCacheEntry(newRequest);
695         else
696             startNetworkLoad(WTFMove(newRequest), FirstLoad::Yes);
697
698         return;
699     }
700
701     if (m_networkLoad)
702         m_networkLoad->continueWillSendRequest(WTFMove(newRequest));
703 }
704
705 void NetworkResourceLoader::continueDidReceiveResponse()
706 {
707     if (m_cacheEntryWaitingForContinueDidReceiveResponse) {
708         sendResultForCacheEntry(WTFMove(m_cacheEntryWaitingForContinueDidReceiveResponse));
709         cleanup(LoadResult::Success);
710         return;
711     }
712
713     if (m_responseCompletionHandler)
714         m_responseCompletionHandler(PolicyAction::Use);
715 }
716
717 void NetworkResourceLoader::didSendData(unsigned long long bytesSent, unsigned long long totalBytesToBeSent)
718 {
719     if (!isSynchronous())
720         send(Messages::WebResourceLoader::DidSendData(bytesSent, totalBytesToBeSent));
721 }
722
723 void NetworkResourceLoader::startBufferingTimerIfNeeded()
724 {
725     if (isSynchronous())
726         return;
727     if (m_bufferingTimer.isActive())
728         return;
729     m_bufferingTimer.startOneShot(m_parameters.maximumBufferingTime);
730 }
731
732 void NetworkResourceLoader::bufferingTimerFired()
733 {
734     ASSERT(m_bufferedData);
735     ASSERT(m_networkLoad);
736
737     if (m_bufferedData->isEmpty())
738         return;
739
740     send(Messages::WebResourceLoader::DidReceiveData({ *m_bufferedData }, m_bufferedDataEncodedDataLength));
741
742     m_bufferedData = SharedBuffer::create();
743     m_bufferedDataEncodedDataLength = 0;
744 }
745
746 void NetworkResourceLoader::sendBuffer(SharedBuffer& buffer, size_t encodedDataLength)
747 {
748     ASSERT(!isSynchronous());
749
750     send(Messages::WebResourceLoader::DidReceiveData({ buffer }, encodedDataLength));
751 }
752
753 void NetworkResourceLoader::tryStoreAsCacheEntry()
754 {
755     if (!canUseCache(m_networkLoad->currentRequest()))
756         return;
757     if (!m_bufferedDataForCache)
758         return;
759
760     m_cache->store(m_networkLoad->currentRequest(), m_response, WTFMove(m_bufferedDataForCache), [loader = makeRef(*this)](auto& mappedBody) mutable {
761 #if ENABLE(SHAREABLE_RESOURCE)
762         if (mappedBody.shareableResourceHandle.isNull())
763             return;
764         LOG(NetworkCache, "(NetworkProcess) sending DidCacheResource");
765         loader->send(Messages::NetworkProcessConnection::DidCacheResource(loader->originalRequest(), mappedBody.shareableResourceHandle, loader->sessionID()));
766 #endif
767     });
768 }
769
770 void NetworkResourceLoader::didRetrieveCacheEntry(std::unique_ptr<NetworkCache::Entry> entry)
771 {
772     auto response = entry->response();
773
774     if (isMainResource() && shouldInterruptLoadForCSPFrameAncestorsOrXFrameOptions(response)) {
775         response = sanitizeResponseIfPossible(WTFMove(response), ResourceResponse::SanitizationType::CrossOriginSafe);
776         send(Messages::WebResourceLoader::StopLoadingAfterXFrameOptionsOrContentSecurityPolicyDenied { response });
777         return;
778     }
779     if (m_networkLoadChecker) {
780         auto error = m_networkLoadChecker->validateResponse(response);
781         if (!error.isNull()) {
782             didFailLoading(error);
783             return;
784         }
785     }
786
787     response = sanitizeResponseIfPossible(WTFMove(response), ResourceResponse::SanitizationType::CrossOriginSafe);
788     if (isSynchronous()) {
789         m_synchronousLoadData->response = WTFMove(response);
790         sendReplyToSynchronousRequest(*m_synchronousLoadData, entry->buffer());
791         cleanup(LoadResult::Success);
792         return;
793     }
794
795     bool needsContinueDidReceiveResponseMessage = isMainResource();
796     send(Messages::WebResourceLoader::DidReceiveResponse { response, needsContinueDidReceiveResponseMessage });
797
798     if (needsContinueDidReceiveResponseMessage)
799         m_cacheEntryWaitingForContinueDidReceiveResponse = WTFMove(entry);
800     else {
801         sendResultForCacheEntry(WTFMove(entry));
802         cleanup(LoadResult::Success);
803     }
804 }
805
806 void NetworkResourceLoader::sendResultForCacheEntry(std::unique_ptr<NetworkCache::Entry> entry)
807 {
808 #if ENABLE(SHAREABLE_RESOURCE)
809     if (!entry->shareableResourceHandle().isNull()) {
810         send(Messages::WebResourceLoader::DidReceiveResource(entry->shareableResourceHandle()));
811         return;
812     }
813 #endif
814
815 #if ENABLE(RESOURCE_LOAD_STATISTICS) && !RELEASE_LOG_DISABLED
816     if (shouldLogCookieInformation(m_connection, sessionID()))
817         logCookieInformation();
818 #endif
819
820     WebCore::NetworkLoadMetrics networkLoadMetrics;
821     networkLoadMetrics.markComplete();
822     networkLoadMetrics.requestHeaderBytesSent = 0;
823     networkLoadMetrics.requestBodyBytesSent = 0;
824     networkLoadMetrics.responseHeaderBytesReceived = 0;
825     networkLoadMetrics.responseBodyBytesReceived = 0;
826     networkLoadMetrics.responseBodyDecodedSize = 0;
827
828     sendBuffer(*entry->buffer(), entry->buffer()->size());
829     send(Messages::WebResourceLoader::DidFinishResourceLoad(networkLoadMetrics));
830 }
831
832 void NetworkResourceLoader::validateCacheEntry(std::unique_ptr<NetworkCache::Entry> entry)
833 {
834     ASSERT(!m_networkLoad);
835
836     // If the request is already conditional then the revalidation was not triggered by the disk cache
837     // and we should not overwrite the existing conditional headers.
838     ResourceRequest revalidationRequest = originalRequest();
839     if (!revalidationRequest.isConditional()) {
840         String eTag = entry->response().httpHeaderField(HTTPHeaderName::ETag);
841         String lastModified = entry->response().httpHeaderField(HTTPHeaderName::LastModified);
842         if (!eTag.isEmpty())
843             revalidationRequest.setHTTPHeaderField(HTTPHeaderName::IfNoneMatch, eTag);
844         if (!lastModified.isEmpty())
845             revalidationRequest.setHTTPHeaderField(HTTPHeaderName::IfModifiedSince, lastModified);
846     }
847
848     m_cacheEntryForValidation = WTFMove(entry);
849
850     startNetworkLoad(WTFMove(revalidationRequest), FirstLoad::Yes);
851 }
852
853 void NetworkResourceLoader::dispatchWillSendRequestForCacheEntry(ResourceRequest&& request, std::unique_ptr<NetworkCache::Entry>&& entry)
854 {
855     ASSERT(entry->redirectRequest());
856     ASSERT(!m_isWaitingContinueWillSendRequestForCachedRedirect);
857
858     LOG(NetworkCache, "(NetworkProcess) Executing cached redirect");
859
860     m_isWaitingContinueWillSendRequestForCachedRedirect = true;
861     willSendRedirectedRequest(WTFMove(request), ResourceRequest { *entry->redirectRequest() }, ResourceResponse { entry->response() });
862 }
863
864 IPC::Connection* NetworkResourceLoader::messageSenderConnection() const
865 {
866     return &connectionToWebProcess().connection();
867 }
868
869 void NetworkResourceLoader::consumeSandboxExtensions()
870 {
871     ASSERT(!m_didConsumeSandboxExtensions);
872
873     for (auto& extension : m_parameters.requestBodySandboxExtensions)
874         extension->consume();
875
876     if (auto& extension = m_parameters.resourceSandboxExtension)
877         extension->consume();
878
879     for (auto& fileReference : m_fileReferences)
880         fileReference->prepareForFileAccess();
881
882     m_didConsumeSandboxExtensions = true;
883 }
884
885 void NetworkResourceLoader::invalidateSandboxExtensions()
886 {
887     if (m_didConsumeSandboxExtensions) {
888         for (auto& extension : m_parameters.requestBodySandboxExtensions)
889             extension->revoke();
890         if (auto& extension = m_parameters.resourceSandboxExtension)
891             extension->revoke();
892         for (auto& fileReference : m_fileReferences)
893             fileReference->revokeFileAccess();
894
895         m_didConsumeSandboxExtensions = false;
896     }
897
898     m_fileReferences.clear();
899 }
900
901 bool NetworkResourceLoader::isAlwaysOnLoggingAllowed() const
902 {
903     if (m_connection->networkProcess().sessionIsControlledByAutomation(sessionID()))
904         return true;
905
906     return sessionID().isAlwaysOnLoggingAllowed();
907 }
908
909 bool NetworkResourceLoader::shouldCaptureExtraNetworkLoadMetrics() const
910 {
911     return m_shouldCaptureExtraNetworkLoadMetrics;
912 }
913
914 #if ENABLE(RESOURCE_LOAD_STATISTICS) && !RELEASE_LOG_DISABLED
915 bool NetworkResourceLoader::shouldLogCookieInformation(NetworkConnectionToWebProcess& connection, const PAL::SessionID& sessionID)
916 {
917     if (auto session = connection.networkProcess().networkSession(sessionID))
918         return session->shouldLogCookieInformation();
919     return false;
920 }
921
922 static String escapeForJSON(String s)
923 {
924     return s.replace('\\', "\\\\").replace('"', "\\\"");
925 }
926
927 static String escapeIDForJSON(const Optional<uint64_t>& value)
928 {
929     return value ? String::number(value.value()) : String("None"_s);
930 };
931
932 void NetworkResourceLoader::logCookieInformation() const
933 {
934     ASSERT(shouldLogCookieInformation(m_connection, sessionID()));
935
936     auto* networkStorageSession = m_connection->networkProcess().storageSession(sessionID());
937     ASSERT(networkStorageSession);
938
939     logCookieInformation(m_connection, "NetworkResourceLoader", reinterpret_cast<const void*>(this), *networkStorageSession, originalRequest().firstPartyForCookies(), SameSiteInfo::create(originalRequest()), originalRequest().url(), originalRequest().httpReferrer(), frameID(), pageID(), identifier());
940 }
941
942 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<uint64_t> frameID, Optional<uint64_t> pageID, Optional<uint64_t> identifier)
943 {
944     ASSERT(NetworkResourceLoader::shouldLogCookieInformation(connection, networkStorageSession.sessionID()));
945
946     auto escapedURL = escapeForJSON(url.string());
947     auto escapedFirstParty = escapeForJSON(firstParty.string());
948     auto escapedFrameID = escapeIDForJSON(frameID);
949     auto escapedPageID = escapeIDForJSON(pageID);
950     auto escapedIdentifier = escapeIDForJSON(identifier);
951     auto escapedReferrer = escapeForJSON(referrer);
952
953 #define LOCAL_LOG_IF_ALLOWED(fmt, ...) RELEASE_LOG_IF(networkStorageSession.sessionID().isAlwaysOnLoggingAllowed(), Network, "%p - %s::" fmt, loggedObject, label.utf8().data(), ##__VA_ARGS__)
954 #define LOCAL_LOG(str, ...) \
955     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__)
956
957     LOCAL_LOG(R"({ "url": "%{public}s",)", escapedURL.utf8().data());
958     LOCAL_LOG(R"(  "partition": "%{public}s",)", "BLOCKED");
959     LOCAL_LOG(R"(  "hasStorageAccess": %{public}s,)", "false");
960     LOCAL_LOG(R"(  "referer": "%{public}s",)", escapedReferrer.utf8().data());
961     LOCAL_LOG(R"(  "isSameSite": "%{public}s",)", sameSiteInfo.isSameSite ? "true" : "false");
962     LOCAL_LOG(R"(  "isTopSite": "%{public}s",)", sameSiteInfo.isTopSite ? "true" : "false");
963     LOCAL_LOG(R"(  "cookies": [])");
964     LOCAL_LOG(R"(  })");
965 #undef LOCAL_LOG
966 #undef LOCAL_LOG_IF_ALLOWED
967 }
968
969 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<uint64_t> frameID, Optional<uint64_t> pageID, Optional<uint64_t> identifier)
970 {
971     ASSERT(NetworkResourceLoader::shouldLogCookieInformation(connection, networkStorageSession.sessionID()));
972
973     Vector<WebCore::Cookie> cookies;
974     if (!networkStorageSession.getRawCookies(firstParty, sameSiteInfo, url, frameID, pageID, cookies))
975         return;
976
977     auto escapedURL = escapeForJSON(url.string());
978     auto escapedPartition = escapeForJSON(emptyString());
979     auto escapedReferrer = escapeForJSON(referrer);
980     auto escapedFrameID = escapeIDForJSON(frameID);
981     auto escapedPageID = escapeIDForJSON(pageID);
982     auto escapedIdentifier = escapeIDForJSON(identifier);
983     bool hasStorageAccess = (frameID && pageID) ? networkStorageSession.hasStorageAccess(WebCore::RegistrableDomain { url }, WebCore::RegistrableDomain { firstParty }, frameID.value(), pageID.value()) : false;
984
985 #define LOCAL_LOG_IF_ALLOWED(fmt, ...) RELEASE_LOG_IF(networkStorageSession.sessionID().isAlwaysOnLoggingAllowed(), Network, "%p - %s::" fmt, loggedObject, label.utf8().data(), ##__VA_ARGS__)
986 #define LOCAL_LOG(str, ...) \
987     LOCAL_LOG_IF_ALLOWED("logCookieInformation: pageID = %s, frameID = %s, resourceID = %s: " str, escapedPageID.utf8().data(), escapedFrameID.utf8().data(), escapedIdentifier.utf8().data(), ##__VA_ARGS__)
988
989     LOCAL_LOG(R"({ "url": "%{public}s",)", escapedURL.utf8().data());
990     LOCAL_LOG(R"(  "partition": "%{public}s",)", escapedPartition.utf8().data());
991     LOCAL_LOG(R"(  "hasStorageAccess": %{public}s,)", hasStorageAccess ? "true" : "false");
992     LOCAL_LOG(R"(  "referer": "%{public}s",)", escapedReferrer.utf8().data());
993     LOCAL_LOG(R"(  "isSameSite": "%{public}s",)", sameSiteInfo.isSameSite ? "true" : "false");
994     LOCAL_LOG(R"(  "isTopSite": "%{public}s",)", sameSiteInfo.isTopSite ? "true" : "false");
995     LOCAL_LOG(R"(  "cookies": [)");
996
997     auto size = cookies.size();
998     decltype(size) count = 0;
999     for (const auto& cookie : cookies) {
1000         const char* trailingComma = ",";
1001         if (++count == size)
1002             trailingComma = "";
1003
1004         auto escapedName = escapeForJSON(cookie.name);
1005         auto escapedValue = escapeForJSON(cookie.value);
1006         auto escapedDomain = escapeForJSON(cookie.domain);
1007         auto escapedPath = escapeForJSON(cookie.path);
1008         auto escapedComment = escapeForJSON(cookie.comment);
1009         auto escapedCommentURL = escapeForJSON(cookie.commentURL.string());
1010         // FIXME: Log Same-Site policy for each cookie. See <https://bugs.webkit.org/show_bug.cgi?id=184894>.
1011
1012         LOCAL_LOG(R"(  { "name": "%{public}s",)", escapedName.utf8().data());
1013         LOCAL_LOG(R"(    "value": "%{public}s",)", escapedValue.utf8().data());
1014         LOCAL_LOG(R"(    "domain": "%{public}s",)", escapedDomain.utf8().data());
1015         LOCAL_LOG(R"(    "path": "%{public}s",)", escapedPath.utf8().data());
1016         LOCAL_LOG(R"(    "created": %f,)", cookie.created);
1017         LOCAL_LOG(R"(    "expires": %f,)", cookie.expires);
1018         LOCAL_LOG(R"(    "httpOnly": %{public}s,)", cookie.httpOnly ? "true" : "false");
1019         LOCAL_LOG(R"(    "secure": %{public}s,)", cookie.secure ? "true" : "false");
1020         LOCAL_LOG(R"(    "session": %{public}s,)", cookie.session ? "true" : "false");
1021         LOCAL_LOG(R"(    "comment": "%{public}s",)", escapedComment.utf8().data());
1022         LOCAL_LOG(R"(    "commentURL": "%{public}s")", escapedCommentURL.utf8().data());
1023         LOCAL_LOG(R"(  }%{public}s)", trailingComma);
1024     }
1025     LOCAL_LOG(R"(]})");
1026 #undef LOCAL_LOG
1027 #undef LOCAL_LOG_IF_ALLOWED
1028 }
1029
1030 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<uint64_t> frameID, Optional<uint64_t> pageID, Optional<uint64_t> identifier)
1031 {
1032     ASSERT(shouldLogCookieInformation(connection, networkStorageSession.sessionID()));
1033
1034     if (networkStorageSession.shouldBlockCookies(firstParty, url, frameID, pageID))
1035         logBlockedCookieInformation(connection, label, loggedObject, networkStorageSession, firstParty, sameSiteInfo, url, referrer, frameID, pageID, identifier);
1036     else
1037         logCookieInformationInternal(connection, label, loggedObject, networkStorageSession, firstParty, sameSiteInfo, url, referrer, frameID, pageID, identifier);
1038 }
1039 #endif
1040
1041 void NetworkResourceLoader::addConsoleMessage(MessageSource messageSource, MessageLevel messageLevel, const String& message, unsigned long)
1042 {
1043     send(Messages::WebPage::AddConsoleMessage { m_parameters.webFrameID,  messageSource, messageLevel, message, identifier() }, m_parameters.webPageID);
1044 }
1045
1046 void NetworkResourceLoader::sendCSPViolationReport(URL&& reportURL, Ref<FormData>&& report)
1047 {
1048     send(Messages::WebPage::SendCSPViolationReport { m_parameters.webFrameID, WTFMove(reportURL), IPC::FormDataReference { WTFMove(report) } }, m_parameters.webPageID);
1049 }
1050
1051 void NetworkResourceLoader::enqueueSecurityPolicyViolationEvent(WebCore::SecurityPolicyViolationEvent::Init&& eventInit)
1052 {
1053     send(Messages::WebPage::EnqueueSecurityPolicyViolationEvent { m_parameters.webFrameID, WTFMove(eventInit) }, m_parameters.webPageID);
1054 }
1055
1056 void NetworkResourceLoader::logSlowCacheRetrieveIfNeeded(const NetworkCache::Cache::RetrieveInfo& info)
1057 {
1058 #if RELEASE_LOG_DISABLED
1059     UNUSED_PARAM(info);
1060 #else
1061     if (!isAlwaysOnLoggingAllowed())
1062         return;
1063     auto duration = info.completionTime - info.startTime;
1064     if (duration < 1_s)
1065         return;
1066     RELEASE_LOG_IF_ALLOWED("logSlowCacheRetrieveIfNeeded: Took %.0fms, priority %d", duration.milliseconds(), info.priority);
1067     if (info.wasSpeculativeLoad)
1068         RELEASE_LOG_IF_ALLOWED("logSlowCacheRetrieveIfNeeded: Was speculative load");
1069     if (!info.storageTimings.startTime)
1070         return;
1071     RELEASE_LOG_IF_ALLOWED("logSlowCacheRetrieveIfNeeded: Storage retrieve time %.0fms", (info.storageTimings.completionTime - info.storageTimings.startTime).milliseconds());
1072     if (info.storageTimings.dispatchTime) {
1073         auto time = (info.storageTimings.dispatchTime - info.storageTimings.startTime).milliseconds();
1074         auto count = info.storageTimings.dispatchCountAtDispatch - info.storageTimings.dispatchCountAtStart;
1075         RELEASE_LOG_IF_ALLOWED("logSlowCacheRetrieveIfNeeded: Dispatch delay %.0fms, dispatched %lu resources first", time, count);
1076     }
1077     if (info.storageTimings.recordIOStartTime)
1078         RELEASE_LOG_IF_ALLOWED("logSlowCacheRetrieveIfNeeded: Record I/O time %.0fms", (info.storageTimings.recordIOEndTime - info.storageTimings.recordIOStartTime).milliseconds());
1079     if (info.storageTimings.blobIOStartTime)
1080         RELEASE_LOG_IF_ALLOWED("logSlowCacheRetrieveIfNeeded: Blob I/O time %.0fms", (info.storageTimings.blobIOEndTime - info.storageTimings.blobIOStartTime).milliseconds());
1081     if (info.storageTimings.synchronizationInProgressAtDispatch)
1082         RELEASE_LOG_IF_ALLOWED("logSlowCacheRetrieveIfNeeded: Synchronization was in progress");
1083     if (info.storageTimings.shrinkInProgressAtDispatch)
1084         RELEASE_LOG_IF_ALLOWED("logSlowCacheRetrieveIfNeeded: Shrink was in progress");
1085     if (info.storageTimings.wasCanceled)
1086         RELEASE_LOG_IF_ALLOWED("logSlowCacheRetrieveIfNeeded: Retrieve was canceled");
1087 #endif
1088 }
1089
1090 } // namespace WebKit
1091
1092 #undef RELEASE_LOG_IF_ALLOWED
1093 #undef RELEASE_LOG_ERROR_IF_ALLOWED