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