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