debf13d87c5672e921f9aeb15794701b3141c61d
[WebKit.git] / Source / WebKit / NetworkProcess / NetworkResourceLoader.cpp
1 /*
2  * Copyright (C) 2012-2018 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 "Logging.h"
31 #include "NetworkBlobRegistry.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 "SessionTracker.h"
39 #include "WebCoreArgumentCoders.h"
40 #include "WebErrors.h"
41 #include "WebResourceLoaderMessages.h"
42 #include "WebsiteDataStoreParameters.h"
43 #include <WebCore/BlobDataFileReference.h>
44 #include <WebCore/CertificateInfo.h>
45 #include <WebCore/DiagnosticLoggingKeys.h>
46 #include <WebCore/HTTPHeaderNames.h>
47 #include <WebCore/NetworkLoadMetrics.h>
48 #include <WebCore/ProtectionSpace.h>
49 #include <WebCore/SameSiteInfo.h>
50 #include <WebCore/SharedBuffer.h>
51 #include <WebCore/SynchronousLoaderClient.h>
52 #include <wtf/RunLoop.h>
53
54 #if HAVE(CFNETWORK_STORAGE_PARTITIONING) && !RELEASE_LOG_DISABLED
55 #include <WebCore/NetworkStorageSession.h>
56 #include <WebCore/PlatformCookieJar.h>
57 #endif
58
59 using namespace WebCore;
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
66 struct NetworkResourceLoader::SynchronousLoadData {
67     SynchronousLoadData(RefPtr<Messages::NetworkConnectionToWebProcess::PerformSynchronousLoad::DelayedReply>&& reply)
68         : delayedReply(WTFMove(reply))
69     {
70         ASSERT(delayedReply);
71     }
72     ResourceRequest currentRequest;
73     RefPtr<Messages::NetworkConnectionToWebProcess::PerformSynchronousLoad::DelayedReply> delayedReply;
74     ResourceResponse response;
75     ResourceError error;
76 };
77
78 static void sendReplyToSynchronousRequest(NetworkResourceLoader::SynchronousLoadData& data, const SharedBuffer* buffer)
79 {
80     ASSERT(data.delayedReply);
81     ASSERT(!data.response.isNull() || !data.error.isNull());
82
83     Vector<char> responseBuffer;
84     if (buffer && buffer->size())
85         responseBuffer.append(buffer->data(), buffer->size());
86
87     data.delayedReply->send(data.error, data.response, responseBuffer);
88     data.delayedReply = nullptr;
89 }
90
91 static inline bool shouldUseNetworkLoadChecker(bool isSynchronous, const NetworkResourceLoadParameters& parameters)
92 {
93     if (isSynchronous)
94         return true;
95
96     if (!parameters.shouldRestrictHTTPResponseAccess)
97         return false;
98
99     // FIXME: Add support for other destinations.
100     switch (parameters.options.destination) {
101     case FetchOptions::Destination::Audio:
102     case FetchOptions::Destination::Video:
103         return true;
104     default:
105         break;
106     }
107     return false;
108 }
109
110 NetworkResourceLoader::NetworkResourceLoader(NetworkResourceLoadParameters&& parameters, NetworkConnectionToWebProcess& connection, RefPtr<Messages::NetworkConnectionToWebProcess::PerformSynchronousLoad::DelayedReply>&& synchronousReply)
111     : m_parameters { WTFMove(parameters) }
112     , m_connection { connection }
113     , m_defersLoading { parameters.defersLoading }
114     , m_isAllowedToAskUserForCredentials { m_parameters.clientCredentialPolicy == ClientCredentialPolicy::MayAskClientForCredentials }
115     , m_bufferingTimer { *this, &NetworkResourceLoader::bufferingTimerFired }
116     , m_cache { sessionID().isEphemeral() ? nullptr : NetworkProcess::singleton().cache() }
117 {
118     ASSERT(RunLoop::isMain());
119     // FIXME: This is necessary because of the existence of EmptyFrameLoaderClient in WebCore.
120     //        Once bug 116233 is resolved, this ASSERT can just be "m_webPageID && m_webFrameID"
121     ASSERT((m_parameters.webPageID && m_parameters.webFrameID) || m_parameters.clientCredentialPolicy == ClientCredentialPolicy::CannotAskClientForCredentials);
122
123     if (originalRequest().httpBody()) {
124         for (const auto& element : originalRequest().httpBody()->elements()) {
125             if (element.m_type == FormDataElement::Type::EncodedBlob)
126                 m_fileReferences.appendVector(NetworkBlobRegistry::singleton().filesInBlob(connection, element.m_url));
127         }
128     }
129
130     if (shouldUseNetworkLoadChecker(!!synchronousReply, m_parameters)) {
131         m_networkLoadChecker = NetworkLoadChecker::create(FetchOptions { m_parameters.options }, m_parameters.sessionID, HTTPHeaderMap { m_parameters.originalRequestHeaders }, URL { m_parameters.request.url() }, m_parameters.sourceOrigin.copyRef(), m_parameters.preflightPolicy);
132         if (m_parameters.cspResponseHeaders)
133             m_networkLoadChecker->setCSPResponseHeaders(ContentSecurityPolicyResponseHeaders { m_parameters.cspResponseHeaders.value() });
134 #if ENABLE(CONTENT_EXTENSIONS)
135         m_networkLoadChecker->setContentExtensionController(URL { m_parameters.mainDocumentURL }, m_parameters.userContentControllerIdentifier);
136 #endif
137     }
138     if (synchronousReply)
139         m_synchronousLoadData = std::make_unique<SynchronousLoadData>(WTFMove(synchronousReply));
140 }
141
142 NetworkResourceLoader::~NetworkResourceLoader()
143 {
144     ASSERT(RunLoop::isMain());
145     ASSERT(!m_networkLoad);
146     ASSERT(!isSynchronous() || !m_synchronousLoadData->delayedReply);
147 }
148
149 bool NetworkResourceLoader::canUseCache(const ResourceRequest& request) const
150 {
151     if (!m_cache)
152         return false;
153     ASSERT(!sessionID().isEphemeral());
154
155     if (!request.url().protocolIsInHTTPFamily())
156         return false;
157     if (originalRequest().cachePolicy() == WebCore::DoNotUseAnyCache)
158         return false;
159
160     return true;
161 }
162
163 bool NetworkResourceLoader::canUseCachedRedirect(const ResourceRequest& request) const
164 {
165     if (!canUseCache(request))
166         return false;
167     // Limit cached redirects to avoid cycles and other trouble.
168     // Networking layer follows over 30 redirects but caching that many seems unnecessary.
169     static const unsigned maximumCachedRedirectCount { 5 };
170     if (m_redirectCount > maximumCachedRedirectCount)
171         return false;
172
173     return true;
174 }
175
176 bool NetworkResourceLoader::isSynchronous() const
177 {
178     return !!m_synchronousLoadData;
179 }
180
181 void NetworkResourceLoader::start()
182 {
183     ASSERT(RunLoop::isMain());
184
185     if (m_defersLoading) {
186         RELEASE_LOG_IF_ALLOWED("start: Loading is deferred (pageID = %" PRIu64 ", frameID = %" PRIu64 ", resourceID = %" PRIu64 ", isMainResource = %d, isSynchronous = %d)", m_parameters.webPageID, m_parameters.webFrameID, m_parameters.identifier, isMainResource(), isSynchronous());
187         return;
188     }
189
190     ASSERT(!m_wasStarted);
191     m_wasStarted = true;
192
193     if (m_networkLoadChecker) {
194         m_networkLoadChecker->check(ResourceRequest { originalRequest() }, [this] (auto&& result) {
195             if (!result.has_value()) {
196                 if (!result.error().isCancellation())
197                     this->didFailLoading(result.error());
198                 return;
199             }
200             if (this->canUseCache(this->originalRequest())) {
201                 RELEASE_LOG_IF_ALLOWED("start: Checking cache for resource (pageID = %" PRIu64 ", frameID = %" PRIu64 ", resourceID = %" PRIu64 ", isMainResource = %d, isSynchronous = %d)", m_parameters.webPageID, m_parameters.webFrameID, m_parameters.identifier, this->isMainResource(), this->isSynchronous());
202                 this->retrieveCacheEntry(this->originalRequest());
203                 return;
204             }
205
206             this->startNetworkLoad(WTFMove(result.value()), FirstLoad::Yes);
207         });
208         return;
209     }
210     // FIXME: Remove that code path once m_networkLoadChecker is used for all network loads.
211     if (canUseCache(originalRequest())) {
212         RELEASE_LOG_IF_ALLOWED("start: Checking cache for resource (pageID = %" PRIu64 ", frameID = %" PRIu64 ", resourceID = %" PRIu64 ", isMainResource = %d, isSynchronous = %d)", m_parameters.webPageID, m_parameters.webFrameID, m_parameters.identifier, isMainResource(), isSynchronous());
213         retrieveCacheEntry(originalRequest());
214         return;
215     }
216
217     startNetworkLoad(ResourceRequest { originalRequest() }, FirstLoad::Yes);
218 }
219
220 void NetworkResourceLoader::retrieveCacheEntry(const ResourceRequest& request)
221 {
222     ASSERT(canUseCache(request));
223
224     RefPtr<NetworkResourceLoader> loader(this);
225     m_cache->retrieve(request, { m_parameters.webPageID, m_parameters.webFrameID }, [this, loader = WTFMove(loader), request = ResourceRequest { request }](auto entry) mutable {
226 #if RELEASE_LOG_DISABLED
227         UNUSED_PARAM(this);
228 #endif
229         if (loader->hasOneRef()) {
230             // The loader has been aborted and is only held alive by this lambda.
231             return;
232         }
233         if (!entry) {
234             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());
235             loader->startNetworkLoad(WTFMove(request), FirstLoad::Yes);
236             return;
237         }
238         if (entry->redirectRequest()) {
239             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());
240             loader->dispatchWillSendRequestForCacheEntry(WTFMove(entry));
241             return;
242         }
243         if (loader->m_parameters.needsCertificateInfo && !entry->response().certificateInfo()) {
244             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());
245             loader->startNetworkLoad(WTFMove(request), FirstLoad::Yes);
246             return;
247         }
248         if (entry->needsValidation() || request.cachePolicy() == WebCore::RefreshAnyCacheData) {
249             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());
250             loader->validateCacheEntry(WTFMove(entry));
251             return;
252         }
253         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());
254         loader->didRetrieveCacheEntry(WTFMove(entry));
255     });
256 }
257
258 void NetworkResourceLoader::startNetworkLoad(ResourceRequest&& request, FirstLoad load)
259 {
260     if (load == FirstLoad::Yes) {
261         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());
262
263         consumeSandboxExtensions();
264
265         if (isSynchronous() || m_parameters.maximumBufferingTime > 0_s)
266             m_bufferedData = SharedBuffer::create();
267
268         if (canUseCache(request))
269             m_bufferedDataForCache = SharedBuffer::create();
270     }
271
272     NetworkLoadParameters parameters = m_parameters;
273     parameters.defersLoading = m_defersLoading;
274     if (m_networkLoadChecker)
275         parameters.storedCredentialsPolicy = m_networkLoadChecker->storedCredentialsPolicy();
276
277     if (request.url().protocolIsBlob())
278         parameters.blobFileReferences = NetworkBlobRegistry::singleton().filesInBlob(m_connection, originalRequest().url());
279
280     auto* networkSession = SessionTracker::networkSession(parameters.sessionID);
281     if (!networkSession && parameters.sessionID.isEphemeral()) {
282         NetworkProcess::singleton().addWebsiteDataStore(WebsiteDataStoreParameters::privateSessionParameters(parameters.sessionID));
283         networkSession = SessionTracker::networkSession(parameters.sessionID);
284     }
285     if (!networkSession) {
286         WTFLogAlways("Attempted to create a NetworkLoad with a session (id=%" PRIu64 ") that does not exist.", parameters.sessionID.sessionID());
287         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());
288         NetworkProcess::singleton().logDiagnosticMessage(m_parameters.webPageID, WebCore::DiagnosticLoggingKeys::internalErrorKey(), WebCore::DiagnosticLoggingKeys::invalidSessionIDKey(), WebCore::ShouldSample::No);
289         didFailLoading(internalError(request.url()));
290         return;
291     }
292
293     parameters.request = WTFMove(request);
294     m_networkLoad = std::make_unique<NetworkLoad>(*this, WTFMove(parameters), *networkSession);
295
296     if (m_defersLoading) {
297         RELEASE_LOG_IF_ALLOWED("startNetworkLoad: Created, but deferred (pageID = %" PRIu64 ", frameID = %" PRIu64 ", resourceID = %" PRIu64 ")",
298             m_parameters.webPageID, m_parameters.webFrameID, m_parameters.identifier);
299     }
300 }
301
302 void NetworkResourceLoader::setDefersLoading(bool defers)
303 {
304     if (m_defersLoading == defers)
305         return;
306     m_defersLoading = defers;
307
308     if (defers)
309         RELEASE_LOG_IF_ALLOWED("setDefersLoading: Deferring resource load (pageID = %" PRIu64 ", frameID = %" PRIu64 ", resourceID = %" PRIu64 ")", m_parameters.webPageID, m_parameters.webFrameID, m_parameters.identifier);
310     else
311         RELEASE_LOG_IF_ALLOWED("setDefersLoading: Resuming deferred resource load (pageID = %" PRIu64 ", frameID = %" PRIu64 ", resourceID = %" PRIu64 ")", m_parameters.webPageID, m_parameters.webFrameID, m_parameters.identifier);
312
313     if (m_networkLoad) {
314         m_networkLoad->setDefersLoading(defers);
315         return;
316     }
317
318     if (!m_defersLoading && !m_wasStarted)
319         start();
320     else
321         RELEASE_LOG_IF_ALLOWED("setDefersLoading: defers = %d, but nothing to do (pageID = %" PRIu64 ", frameID = %" PRIu64 ", resourceID = %" PRIu64 ")", m_defersLoading, m_parameters.webPageID, m_parameters.webFrameID, m_parameters.identifier);
322 }
323
324 void NetworkResourceLoader::cleanup()
325 {
326     ASSERT(RunLoop::isMain());
327
328     m_bufferingTimer.stop();
329
330     invalidateSandboxExtensions();
331
332     m_networkLoad = nullptr;
333
334     // This will cause NetworkResourceLoader to be destroyed and therefore we do it last.
335     m_connection->didCleanupResourceLoader(*this);
336 }
337
338 void NetworkResourceLoader::convertToDownload(DownloadID downloadID, const ResourceRequest& request, const ResourceResponse& response)
339 {
340     ASSERT(m_networkLoad);
341     NetworkProcess::singleton().downloadManager().convertNetworkLoadToDownload(downloadID, std::exchange(m_networkLoad, nullptr), WTFMove(m_fileReferences), request, response);
342 }
343
344 void NetworkResourceLoader::abort()
345 {
346     ASSERT(RunLoop::isMain());
347
348     RELEASE_LOG_IF_ALLOWED("abort: Canceling resource load (pageID = %" PRIu64 ", frameID = %" PRIu64 ", resourceID = %" PRIu64 ")",
349         m_parameters.webPageID, m_parameters.webFrameID, m_parameters.identifier);
350
351     if (m_networkLoad) {
352         if (canUseCache(m_networkLoad->currentRequest())) {
353             // We might already have used data from this incomplete load. Ensure older versions don't remain in the cache after cancel.
354             if (!m_response.isNull())
355                 m_cache->remove(m_networkLoad->currentRequest());
356         }
357         m_networkLoad->cancel();
358     }
359
360     cleanup();
361 }
362
363 auto NetworkResourceLoader::didReceiveResponse(ResourceResponse&& receivedResponse) -> ShouldContinueDidReceiveResponse
364 {
365     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());
366
367     m_response = WTFMove(receivedResponse);
368
369     if (shouldCaptureExtraNetworkLoadMetrics())
370         m_connection->addNetworkLoadInformationResponse(identifier(), m_response);
371
372     // For multipart/x-mixed-replace didReceiveResponseAsync gets called multiple times and buffering would require special handling.
373     if (!isSynchronous() && m_response.isMultipart())
374         m_bufferedData = nullptr;
375
376     if (m_response.isMultipart())
377         m_bufferedDataForCache = nullptr;
378
379     if (m_cacheEntryForValidation) {
380         bool validationSucceeded = m_response.httpStatusCode() == 304; // 304 Not Modified
381         if (validationSucceeded) {
382             m_cacheEntryForValidation = m_cache->update(originalRequest(), { m_parameters.webPageID, m_parameters.webFrameID }, *m_cacheEntryForValidation, m_response);
383             // If the request was conditional then this revalidation was not triggered by the network cache and we pass the 304 response to WebCore.
384             if (originalRequest().isConditional())
385                 m_cacheEntryForValidation = nullptr;
386         } else
387             m_cacheEntryForValidation = nullptr;
388     }
389     bool shouldSendDidReceiveResponse = !m_cacheEntryForValidation;
390
391     bool shouldWaitContinueDidReceiveResponse = isMainResource();
392     if (shouldSendDidReceiveResponse) {
393         if (m_networkLoadChecker) {
394             auto error = m_networkLoadChecker->validateResponse(m_response);
395             if (!error.isNull()) {
396                 RunLoop::main().dispatch([protectedThis = makeRef(*this), error = WTFMove(error)] {
397                     if (protectedThis->m_networkLoad)
398                         protectedThis->didFailLoading(error);
399                 });
400                 return ShouldContinueDidReceiveResponse::No;
401             }
402         }
403
404         auto response = sanitizeResponseIfPossible(ResourceResponse { m_response }, ResourceResponse::SanitizationType::CrossOriginSafe);
405         if (isSynchronous())
406             m_synchronousLoadData->response = WTFMove(response);
407         else
408             send(Messages::WebResourceLoader::DidReceiveResponse { response, shouldWaitContinueDidReceiveResponse });
409     }
410
411     // For main resources, the web process is responsible for sending back a NetworkResourceLoader::ContinueDidReceiveResponse message.
412     bool shouldContinueDidReceiveResponse = !shouldWaitContinueDidReceiveResponse || m_cacheEntryForValidation;
413
414     if (shouldContinueDidReceiveResponse) {
415         RELEASE_LOG_IF_ALLOWED("didReceiveResponse: Should not wait for message from WebContent process before continuing resource load (pageID = %" PRIu64 ", frameID = %" PRIu64 ", resourceID = %" PRIu64 ")", m_parameters.webPageID, m_parameters.webFrameID, m_parameters.identifier);
416         return ShouldContinueDidReceiveResponse::Yes;
417     }
418
419     RELEASE_LOG_IF_ALLOWED("didReceiveResponse: Should wait for message from WebContent process before continuing resource load (pageID = %" PRIu64 ", frameID = %" PRIu64 ", resourceID = %" PRIu64 ")", m_parameters.webPageID, m_parameters.webFrameID, m_parameters.identifier);
420     return ShouldContinueDidReceiveResponse::No;
421 }
422
423 void NetworkResourceLoader::didReceiveBuffer(Ref<SharedBuffer>&& buffer, int reportedEncodedDataLength)
424 {
425     if (!m_numBytesReceived) {
426         RELEASE_LOG_IF_ALLOWED("didReceiveBuffer: Started receiving data (pageID = %" PRIu64 ", frameID = %" PRIu64 ", resourceID = %" PRIu64 ")", m_parameters.webPageID, m_parameters.webFrameID, m_parameters.identifier);
427     }
428     m_numBytesReceived += buffer->size();
429
430     ASSERT(!m_cacheEntryForValidation);
431
432     if (m_bufferedDataForCache) {
433         // Prevent memory growth in case of streaming data.
434         const size_t maximumCacheBufferSize = 10 * 1024 * 1024;
435         if (m_bufferedDataForCache->size() + buffer->size() <= maximumCacheBufferSize)
436             m_bufferedDataForCache->append(buffer.get());
437         else
438             m_bufferedDataForCache = nullptr;
439     }
440     // FIXME: At least on OS X Yosemite we always get -1 from the resource handle.
441     unsigned encodedDataLength = reportedEncodedDataLength >= 0 ? reportedEncodedDataLength : buffer->size();
442
443     m_bytesReceived += buffer->size();
444     if (m_bufferedData) {
445         m_bufferedData->append(buffer.get());
446         m_bufferedDataEncodedDataLength += encodedDataLength;
447         startBufferingTimerIfNeeded();
448         return;
449     }
450     sendBuffer(buffer, encodedDataLength);
451 }
452
453 void NetworkResourceLoader::didFinishLoading(const NetworkLoadMetrics& networkLoadMetrics)
454 {
455     RELEASE_LOG_IF_ALLOWED("didFinishLoading: (pageID = %" PRIu64 ", frameID = %" PRIu64 ", resourceID = %" PRIu64 ", length = %zd)", m_parameters.webPageID, m_parameters.webFrameID, m_parameters.identifier, m_numBytesReceived);
456
457     if (shouldCaptureExtraNetworkLoadMetrics())
458         m_connection->addNetworkLoadInformationMetrics(identifier(), networkLoadMetrics);
459
460     if (m_cacheEntryForValidation) {
461         // 304 Not Modified
462         ASSERT(m_response.httpStatusCode() == 304);
463         LOG(NetworkCache, "(NetworkProcess) revalidated");
464         didRetrieveCacheEntry(WTFMove(m_cacheEntryForValidation));
465         return;
466     }
467
468 #if HAVE(CFNETWORK_STORAGE_PARTITIONING) && !RELEASE_LOG_DISABLED
469     if (shouldLogCookieInformation())
470         logCookieInformation();
471 #endif
472
473     if (isSynchronous())
474         sendReplyToSynchronousRequest(*m_synchronousLoadData, m_bufferedData.get());
475     else {
476         if (m_bufferedData && !m_bufferedData->isEmpty()) {
477             // FIXME: Pass a real value or remove the encoded data size feature.
478             sendBuffer(*m_bufferedData, -1);
479         }
480         send(Messages::WebResourceLoader::DidFinishResourceLoad(networkLoadMetrics));
481     }
482
483     tryStoreAsCacheEntry();
484
485     cleanup();
486 }
487
488 void NetworkResourceLoader::didFailLoading(const ResourceError& error)
489 {
490     RELEASE_LOG_IF_ALLOWED("didFailLoading: (pageID = %" PRIu64 ", frameID = %" PRIu64 ", resourceID = %" PRIu64 ", isTimeout = %d, isCancellation = %d, errCode = %d)", m_parameters.webPageID, m_parameters.webFrameID, m_parameters.identifier, error.isTimeout(), error.isCancellation(), error.errorCode());
491
492     if (shouldCaptureExtraNetworkLoadMetrics())
493         m_connection->removeNetworkLoadInformation(identifier());
494
495     ASSERT(!error.isNull());
496
497     m_cacheEntryForValidation = nullptr;
498
499     if (isSynchronous()) {
500         m_synchronousLoadData->error = error;
501         sendReplyToSynchronousRequest(*m_synchronousLoadData, nullptr);
502     } else if (auto* connection = messageSenderConnection())
503         connection->send(Messages::WebResourceLoader::DidFailResourceLoad(error), messageSenderDestinationID());
504
505     cleanup();
506 }
507
508 void NetworkResourceLoader::didBlockAuthenticationChallenge()
509 {
510     send(Messages::WebResourceLoader::DidBlockAuthenticationChallenge());
511 }
512
513 void NetworkResourceLoader::willSendRedirectedRequest(ResourceRequest&& request, WebCore::ResourceRequest&& redirectRequest, ResourceResponse&& redirectResponse)
514 {
515     ++m_redirectCount;
516
517     if (m_networkLoadChecker) {
518         m_networkLoadChecker->checkRedirection(redirectResponse, WTFMove(redirectRequest), [protectedThis = makeRef(*this), this, storedCredentialsPolicy = m_networkLoadChecker->storedCredentialsPolicy(), request = WTFMove(request), redirectResponse](auto&& result) mutable {
519             if (!result.has_value()) {
520                 if (result.error().isCancellation())
521                     return;
522                 this->didFailLoading(result.error());
523                 return;
524             }
525
526             if (storedCredentialsPolicy != m_networkLoadChecker->storedCredentialsPolicy()) {
527                 // We need to restart the load to update the session according the new credential policy.
528                 m_networkLoad->cancel();
529                 this->startNetworkLoad(WTFMove(result.value()), FirstLoad::No);
530                 return;
531             }
532
533             if (this->isSynchronous()) {
534                 // We do not support prompting for credentials for synchronous loads. If we ever change this policy then
535                 // we need to take care to prompt if and only if request and redirectRequest are not mixed content.
536                 this->continueWillSendRequest(WTFMove(result.value()), false);
537                 return;
538             }
539
540             this->continueWillSendRedirectedRequest(WTFMove(request), WTFMove(result.value()), WTFMove(redirectResponse));
541         });
542         return;
543     }
544     continueWillSendRedirectedRequest(WTFMove(request), WTFMove(redirectRequest), WTFMove(redirectResponse));
545 }
546
547 void NetworkResourceLoader::continueWillSendRedirectedRequest(WebCore::ResourceRequest&& request, WebCore::ResourceRequest&& redirectRequest, ResourceResponse&& redirectResponse)
548 {
549     ASSERT(!isSynchronous());
550
551     if (canUseCachedRedirect(request))
552         m_cache->storeRedirect(request, redirectResponse, redirectRequest);
553
554     send(Messages::WebResourceLoader::WillSendRequest(redirectRequest, sanitizeResponseIfPossible(WTFMove(redirectResponse), ResourceResponse::SanitizationType::Redirection)));
555 }
556
557 ResourceResponse NetworkResourceLoader::sanitizeResponseIfPossible(ResourceResponse&& response, ResourceResponse::SanitizationType type)
558 {
559     if (m_parameters.shouldRestrictHTTPResponseAccess) {
560         if (type == ResourceResponse::SanitizationType::CrossOriginSafe) {
561             // We reduce filtering when it would otherwise be visible to scripts.
562             // FIXME: We should use response tainting once computed in Network Process.
563             bool isSameOrigin = m_parameters.sourceOrigin ? m_parameters.sourceOrigin->canRequest(response.url()) : protocolHostAndPortAreEqual(response.url(), m_parameters.request.url());
564             if (isSameOrigin && m_parameters.options.destination == FetchOptions::Destination::EmptyString)
565                 type = ResourceResponse::SanitizationType::RemoveCookies;
566         }
567         response.sanitizeHTTPHeaderFields(type);
568     }
569     return WTFMove(response);
570 }
571
572 void NetworkResourceLoader::continueWillSendRequest(ResourceRequest&& newRequest, bool isAllowedToAskUserForCredentials)
573 {
574     RELEASE_LOG_IF_ALLOWED("continueWillSendRequest: (pageID = %" PRIu64 ", frameID = %" PRIu64 ", resourceID = %" PRIu64 ")", m_parameters.webPageID, m_parameters.webFrameID, m_parameters.identifier);
575
576     m_isAllowedToAskUserForCredentials = isAllowedToAskUserForCredentials;
577
578     // If there is a match in the network cache, we need to reuse the original cache policy and partition.
579     newRequest.setCachePolicy(originalRequest().cachePolicy());
580     newRequest.setCachePartition(originalRequest().cachePartition());
581
582     if (m_isWaitingContinueWillSendRequestForCachedRedirect) {
583         m_isWaitingContinueWillSendRequestForCachedRedirect = false;
584
585         LOG(NetworkCache, "(NetworkProcess) Retrieving cached redirect");
586
587         if (canUseCachedRedirect(newRequest))
588             retrieveCacheEntry(newRequest);
589         else
590             startNetworkLoad(WTFMove(newRequest), FirstLoad::Yes);
591
592         return;
593     }
594
595     if (m_networkLoad)
596         m_networkLoad->continueWillSendRequest(WTFMove(newRequest));
597 }
598
599 void NetworkResourceLoader::continueDidReceiveResponse()
600 {
601     if (m_cacheEntryWaitingForContinueDidReceiveResponse) {
602         continueProcessingCachedEntryAfterDidReceiveResponse(WTFMove(m_cacheEntryWaitingForContinueDidReceiveResponse));
603         return;
604     }
605
606     // FIXME: Remove this check once BlobResourceHandle implements didReceiveResponseAsync correctly.
607     // Currently, it does not wait for response, so the load is likely to finish before continueDidReceiveResponse.
608     if (m_networkLoad)
609         m_networkLoad->continueDidReceiveResponse();
610 }
611
612 void NetworkResourceLoader::didSendData(unsigned long long bytesSent, unsigned long long totalBytesToBeSent)
613 {
614     if (!isSynchronous())
615         send(Messages::WebResourceLoader::DidSendData(bytesSent, totalBytesToBeSent));
616 }
617
618 void NetworkResourceLoader::startBufferingTimerIfNeeded()
619 {
620     if (isSynchronous())
621         return;
622     if (m_bufferingTimer.isActive())
623         return;
624     m_bufferingTimer.startOneShot(m_parameters.maximumBufferingTime);
625 }
626
627 void NetworkResourceLoader::bufferingTimerFired()
628 {
629     ASSERT(m_bufferedData);
630     ASSERT(m_networkLoad);
631
632     if (m_bufferedData->isEmpty())
633         return;
634
635     IPC::SharedBufferDataReference dataReference(m_bufferedData.get());
636     size_t encodedLength = m_bufferedDataEncodedDataLength;
637
638     m_bufferedData = SharedBuffer::create();
639     m_bufferedDataEncodedDataLength = 0;
640
641     send(Messages::WebResourceLoader::DidReceiveData(dataReference, encodedLength));
642 }
643
644 void NetworkResourceLoader::sendBuffer(SharedBuffer& buffer, size_t encodedDataLength)
645 {
646     ASSERT(!isSynchronous());
647
648     IPC::SharedBufferDataReference dataReference(&buffer);
649     send(Messages::WebResourceLoader::DidReceiveData(dataReference, encodedDataLength));
650 }
651
652 void NetworkResourceLoader::tryStoreAsCacheEntry()
653 {
654     if (!canUseCache(m_networkLoad->currentRequest()))
655         return;
656     if (!m_bufferedDataForCache)
657         return;
658
659     m_cache->store(m_networkLoad->currentRequest(), m_response, WTFMove(m_bufferedDataForCache), [loader = makeRef(*this)](auto& mappedBody) mutable {
660 #if ENABLE(SHAREABLE_RESOURCE)
661         if (mappedBody.shareableResourceHandle.isNull())
662             return;
663         LOG(NetworkCache, "(NetworkProcess) sending DidCacheResource");
664         loader->send(Messages::NetworkProcessConnection::DidCacheResource(loader->originalRequest(), mappedBody.shareableResourceHandle, loader->sessionID()));
665 #endif
666     });
667 }
668
669 void NetworkResourceLoader::didRetrieveCacheEntry(std::unique_ptr<NetworkCache::Entry> entry)
670 {
671     auto response = entry->response();
672     auto error = m_networkLoadChecker ? m_networkLoadChecker->validateResponse(response) : ResourceError { };
673
674     if (!error.isNull()) {
675         didFailLoading(error);
676         return;
677     }
678
679     response = sanitizeResponseIfPossible(WTFMove(response), ResourceResponse::SanitizationType::CrossOriginSafe);
680     if (isSynchronous()) {
681         m_synchronousLoadData->response = WTFMove(response);
682         sendReplyToSynchronousRequest(*m_synchronousLoadData, entry->buffer());
683         cleanup();
684         return;
685     }
686
687     bool needsContinueDidReceiveResponseMessage = isMainResource();
688     send(Messages::WebResourceLoader::DidReceiveResponse { response, needsContinueDidReceiveResponseMessage });
689
690     if (needsContinueDidReceiveResponseMessage)
691         m_cacheEntryWaitingForContinueDidReceiveResponse = WTFMove(entry);
692     else
693         continueProcessingCachedEntryAfterDidReceiveResponse(WTFMove(entry));
694 }
695
696 void NetworkResourceLoader::continueProcessingCachedEntryAfterDidReceiveResponse(std::unique_ptr<NetworkCache::Entry> entry)
697 {
698     if (entry->sourceStorageRecord().bodyHash && !m_parameters.derivedCachedDataTypesToRetrieve.isEmpty()) {
699         auto bodyHash = *entry->sourceStorageRecord().bodyHash;
700         auto* entryPtr = entry.release();
701         auto retrieveCount = m_parameters.derivedCachedDataTypesToRetrieve.size();
702
703         for (auto& type : m_parameters.derivedCachedDataTypesToRetrieve) {
704             NetworkCache::DataKey key { originalRequest().cachePartition(), type, bodyHash };
705             m_cache->retrieveData(key, [loader = makeRef(*this), entryPtr, type, retrieveCount] (const uint8_t* data, size_t size) mutable {
706                 loader->m_retrievedDerivedDataCount++;
707                 bool retrievedAll = loader->m_retrievedDerivedDataCount == retrieveCount;
708                 std::unique_ptr<NetworkCache::Entry> entry(retrievedAll ? entryPtr : nullptr);
709                 if (loader->hasOneRef())
710                     return;
711                 if (data) {
712                     IPC::DataReference dataReference(data, size);
713                     loader->send(Messages::WebResourceLoader::DidRetrieveDerivedData(type, dataReference));
714                 }
715                 if (retrievedAll) {
716                     loader->sendResultForCacheEntry(WTFMove(entry));
717                     loader->cleanup();
718                 }
719             });
720         }
721         return;
722     }
723
724     sendResultForCacheEntry(WTFMove(entry));
725
726     cleanup();
727 }
728
729 void NetworkResourceLoader::sendResultForCacheEntry(std::unique_ptr<NetworkCache::Entry> entry)
730 {
731 #if ENABLE(SHAREABLE_RESOURCE)
732     if (!entry->shareableResourceHandle().isNull()) {
733         send(Messages::WebResourceLoader::DidReceiveResource(entry->shareableResourceHandle()));
734         return;
735     }
736 #endif
737
738 #if HAVE(CFNETWORK_STORAGE_PARTITIONING) && !RELEASE_LOG_DISABLED
739     if (shouldLogCookieInformation())
740         logCookieInformation();
741 #endif
742
743     WebCore::NetworkLoadMetrics networkLoadMetrics;
744     networkLoadMetrics.markComplete();
745     networkLoadMetrics.requestHeaderBytesSent = 0;
746     networkLoadMetrics.requestBodyBytesSent = 0;
747     networkLoadMetrics.responseHeaderBytesReceived = 0;
748     networkLoadMetrics.responseBodyBytesReceived = 0;
749     networkLoadMetrics.responseBodyDecodedSize = 0;
750
751     sendBuffer(*entry->buffer(), entry->buffer()->size());
752     send(Messages::WebResourceLoader::DidFinishResourceLoad(networkLoadMetrics));
753 }
754
755 void NetworkResourceLoader::validateCacheEntry(std::unique_ptr<NetworkCache::Entry> entry)
756 {
757     ASSERT(!m_networkLoad);
758
759     // If the request is already conditional then the revalidation was not triggered by the disk cache
760     // and we should not overwrite the existing conditional headers.
761     ResourceRequest revalidationRequest = originalRequest();
762     if (!revalidationRequest.isConditional()) {
763         String eTag = entry->response().httpHeaderField(HTTPHeaderName::ETag);
764         String lastModified = entry->response().httpHeaderField(HTTPHeaderName::LastModified);
765         if (!eTag.isEmpty())
766             revalidationRequest.setHTTPHeaderField(HTTPHeaderName::IfNoneMatch, eTag);
767         if (!lastModified.isEmpty())
768             revalidationRequest.setHTTPHeaderField(HTTPHeaderName::IfModifiedSince, lastModified);
769     }
770
771     m_cacheEntryForValidation = WTFMove(entry);
772
773     startNetworkLoad(WTFMove(revalidationRequest), FirstLoad::Yes);
774 }
775
776 void NetworkResourceLoader::dispatchWillSendRequestForCacheEntry(std::unique_ptr<NetworkCache::Entry> entry)
777 {
778     ASSERT(entry->redirectRequest());
779     ASSERT(!m_isWaitingContinueWillSendRequestForCachedRedirect);
780
781     LOG(NetworkCache, "(NetworkProcess) Executing cached redirect");
782
783     ++m_redirectCount;
784     send(Messages::WebResourceLoader::WillSendRequest { *entry->redirectRequest(), sanitizeResponseIfPossible(ResourceResponse { entry->response() }, ResourceResponse::SanitizationType::Redirection) });
785     m_isWaitingContinueWillSendRequestForCachedRedirect = true;
786 }
787
788 IPC::Connection* NetworkResourceLoader::messageSenderConnection()
789 {
790     return &connectionToWebProcess().connection();
791 }
792
793 void NetworkResourceLoader::consumeSandboxExtensions()
794 {
795     ASSERT(!m_didConsumeSandboxExtensions);
796
797     for (auto& extension : m_parameters.requestBodySandboxExtensions)
798         extension->consume();
799
800     if (auto& extension = m_parameters.resourceSandboxExtension)
801         extension->consume();
802
803     for (auto& fileReference : m_fileReferences)
804         fileReference->prepareForFileAccess();
805
806     m_didConsumeSandboxExtensions = true;
807 }
808
809 void NetworkResourceLoader::invalidateSandboxExtensions()
810 {
811     if (m_didConsumeSandboxExtensions) {
812         for (auto& extension : m_parameters.requestBodySandboxExtensions)
813             extension->revoke();
814         if (auto& extension = m_parameters.resourceSandboxExtension)
815             extension->revoke();
816         for (auto& fileReference : m_fileReferences)
817             fileReference->revokeFileAccess();
818
819         m_didConsumeSandboxExtensions = false;
820     }
821
822     m_fileReferences.clear();
823 }
824
825 #if USE(PROTECTION_SPACE_AUTH_CALLBACK)
826 void NetworkResourceLoader::canAuthenticateAgainstProtectionSpaceAsync(const ProtectionSpace& protectionSpace)
827 {
828     NetworkProcess::singleton().canAuthenticateAgainstProtectionSpace(*this, protectionSpace);
829 }
830
831 void NetworkResourceLoader::continueCanAuthenticateAgainstProtectionSpace(bool result)
832 {
833     if (m_networkLoad)
834         m_networkLoad->continueCanAuthenticateAgainstProtectionSpace(result);
835 }
836 #endif
837
838 bool NetworkResourceLoader::isAlwaysOnLoggingAllowed() const
839 {
840     if (NetworkProcess::singleton().sessionIsControlledByAutomation(sessionID()))
841         return true;
842
843     return sessionID().isAlwaysOnLoggingAllowed();
844 }
845
846 bool NetworkResourceLoader::shouldCaptureExtraNetworkLoadMetrics() const
847 {
848     return m_connection->captureExtraNetworkLoadMetricsEnabled();
849 }
850
851 #if HAVE(CFNETWORK_STORAGE_PARTITIONING) && !RELEASE_LOG_DISABLED
852 bool NetworkResourceLoader::shouldLogCookieInformation()
853 {
854     return NetworkProcess::singleton().shouldLogCookieInformation();
855 }
856
857 static String escapeForJSON(String s)
858 {
859     return s.replace('\\', "\\\\").replace('"', "\\\"");
860 }
861
862 static String escapeIDForJSON(const std::optional<uint64_t>& value)
863 {
864     return value ? String::number(value.value()) : String("None");
865 };
866
867 void NetworkResourceLoader::logCookieInformation() const
868 {
869     ASSERT(shouldLogCookieInformation());
870
871     auto networkStorageSession = WebCore::NetworkStorageSession::storageSession(sessionID());
872     ASSERT(networkStorageSession);
873
874     logCookieInformation("NetworkResourceLoader", reinterpret_cast<const void*>(this), *networkStorageSession, originalRequest().firstPartyForCookies(), SameSiteInfo::create(originalRequest()), originalRequest().url(), originalRequest().httpReferrer(), frameID(), pageID(), identifier());
875 }
876
877 static void logBlockedCookieInformation(const String& label, const void* loggedObject, const WebCore::NetworkStorageSession& networkStorageSession, const WebCore::URL& firstParty, const SameSiteInfo& sameSiteInfo, const WebCore::URL& url, const String& referrer, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, std::optional<uint64_t> identifier)
878 {
879     ASSERT(NetworkResourceLoader::shouldLogCookieInformation());
880
881     auto escapedURL = escapeForJSON(url.string());
882     auto escapedFirstParty = escapeForJSON(firstParty.string());
883     auto escapedFrameID = escapeIDForJSON(frameID);
884     auto escapedPageID = escapeIDForJSON(pageID);
885     auto escapedIdentifier = escapeIDForJSON(identifier);
886     auto escapedReferrer = escapeForJSON(referrer);
887
888 #define LOCAL_LOG_IF_ALLOWED(fmt, ...) RELEASE_LOG_IF(networkStorageSession.sessionID().isAlwaysOnLoggingAllowed(), Network, "%p - %s::" fmt, loggedObject, label.utf8().data(), ##__VA_ARGS__)
889 #define LOCAL_LOG(str, ...) \
890     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__)
891
892     LOCAL_LOG(R"({ "url": "%{public}s",)", escapedURL.utf8().data());
893     LOCAL_LOG(R"(  "partition": "%{public}s",)", "BLOCKED");
894     LOCAL_LOG(R"(  "hasStorageAccess": %{public}s,)", "false");
895     LOCAL_LOG(R"(  "referer": "%{public}s",)", escapedReferrer.utf8().data());
896     LOCAL_LOG(R"(  "isSameSite": "%{public}s",)", sameSiteInfo.isSameSite ? "true" : "false");
897     LOCAL_LOG(R"(  "isTopSite": "%{public}s",)", sameSiteInfo.isTopSite ? "true" : "false");
898     LOCAL_LOG(R"(  "cookies": [])");
899     LOCAL_LOG(R"(  "})");
900 #undef LOCAL_LOG
901 #undef LOCAL_LOG_IF_ALLOWED
902 }
903
904 static void logCookieInformationInternal(const String& label, const void* loggedObject, const WebCore::NetworkStorageSession& networkStorageSession, const WebCore::URL& partition, const WebCore::SameSiteInfo& sameSiteInfo, const WebCore::URL& url, const String& referrer, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, std::optional<uint64_t> identifier)
905 {
906     ASSERT(NetworkResourceLoader::shouldLogCookieInformation());
907
908     Vector<WebCore::Cookie> cookies;
909     if (!WebCore::getRawCookies(networkStorageSession, partition, sameSiteInfo, url, frameID, pageID, cookies))
910         return;
911
912     auto escapedURL = escapeForJSON(url.string());
913     auto escapedPartition = escapeForJSON(partition.string());
914     auto escapedReferrer = escapeForJSON(referrer);
915     auto escapedFrameID = escapeIDForJSON(frameID);
916     auto escapedPageID = escapeIDForJSON(pageID);
917     auto escapedIdentifier = escapeIDForJSON(identifier);
918     bool hasStorageAccess = (frameID && pageID) ? networkStorageSession.hasStorageAccess(url.string(), partition.string(), frameID.value(), pageID.value()) : false;
919
920 #define LOCAL_LOG_IF_ALLOWED(fmt, ...) RELEASE_LOG_IF(networkStorageSession.sessionID().isAlwaysOnLoggingAllowed(), Network, "%p - %s::" fmt, loggedObject, label.utf8().data(), ##__VA_ARGS__)
921 #define LOCAL_LOG(str, ...) \
922     LOCAL_LOG_IF_ALLOWED("logCookieInformation: pageID = %s, frameID = %s, resourceID = %s: " str, escapedPageID.utf8().data(), escapedFrameID.utf8().data(), escapedIdentifier.utf8().data(), ##__VA_ARGS__)
923
924     LOCAL_LOG(R"({ "url": "%{public}s",)", escapedURL.utf8().data());
925     LOCAL_LOG(R"(  "partition": "%{public}s",)", escapedPartition.utf8().data());
926     LOCAL_LOG(R"(  "hasStorageAccess": %{public}s,)", hasStorageAccess ? "true" : "false");
927     LOCAL_LOG(R"(  "referer": "%{public}s",)", escapedReferrer.utf8().data());
928     LOCAL_LOG(R"(  "isSameSite": "%{public}s",)", sameSiteInfo.isSameSite ? "true" : "false");
929     LOCAL_LOG(R"(  "isTopSite": "%{public}s",)", sameSiteInfo.isTopSite ? "true" : "false");
930     LOCAL_LOG(R"(  "cookies": [)");
931
932     auto size = cookies.size();
933     decltype(size) count = 0;
934     for (const auto& cookie : cookies) {
935         const char* trailingComma = ",";
936         if (++count == size)
937             trailingComma = "";
938
939         auto escapedName = escapeForJSON(cookie.name);
940         auto escapedValue = escapeForJSON(cookie.value);
941         auto escapedDomain = escapeForJSON(cookie.domain);
942         auto escapedPath = escapeForJSON(cookie.path);
943         auto escapedComment = escapeForJSON(cookie.comment);
944         auto escapedCommentURL = escapeForJSON(cookie.commentURL.string());
945         // FIXME: Log Same-Site policy for each cookie. See <https://bugs.webkit.org/show_bug.cgi?id=184894>.
946
947         LOCAL_LOG(R"(  { "name": "%{public}s",)", escapedName.utf8().data());
948         LOCAL_LOG(R"(    "value": "%{public}s",)", escapedValue.utf8().data());
949         LOCAL_LOG(R"(    "domain": "%{public}s",)", escapedDomain.utf8().data());
950         LOCAL_LOG(R"(    "path": "%{public}s",)", escapedPath.utf8().data());
951         LOCAL_LOG(R"(    "created": %f,)", cookie.created);
952         LOCAL_LOG(R"(    "expires": %f,)", cookie.expires);
953         LOCAL_LOG(R"(    "httpOnly": %{public}s,)", cookie.httpOnly ? "true" : "false");
954         LOCAL_LOG(R"(    "secure": %{public}s,)", cookie.secure ? "true" : "false");
955         LOCAL_LOG(R"(    "session": %{public}s,)", cookie.session ? "true" : "false");
956         LOCAL_LOG(R"(    "comment": "%{public}s",)", escapedComment.utf8().data());
957         LOCAL_LOG(R"(    "commentURL": "%{public}s")", escapedCommentURL.utf8().data());
958         LOCAL_LOG(R"(  }%{public}s)", trailingComma);
959     }
960     LOCAL_LOG(R"(]})");
961 #undef LOCAL_LOG
962 #undef LOCAL_LOG_IF_ALLOWED
963 }
964
965 void NetworkResourceLoader::logCookieInformation(const String& label, const void* loggedObject, const NetworkStorageSession& networkStorageSession, const URL& firstParty, const SameSiteInfo& sameSiteInfo, const URL& url, const String& referrer, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, std::optional<uint64_t> identifier)
966 {
967     ASSERT(shouldLogCookieInformation());
968
969     if (networkStorageSession.shouldBlockCookies(firstParty, url))
970         logBlockedCookieInformation(label, loggedObject, networkStorageSession, firstParty, sameSiteInfo, url, referrer, frameID, pageID, identifier);
971     else {
972         auto partition = URL(ParsedURLString, networkStorageSession.cookieStoragePartition(firstParty, url, frameID, pageID));
973         logCookieInformationInternal(label, loggedObject, networkStorageSession, partition, sameSiteInfo, url, referrer, frameID, pageID, identifier);
974     }
975 }
976 #endif
977
978 } // namespace WebKit