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