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