blob: 4fadfe775fd598e6e43301e86d911cf4dad83c15 [file] [log] [blame]
/*
* Copyright (C) 2012-2018 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "config.h"
#include "NetworkResourceLoader.h"
#include "DataReference.h"
#include "Logging.h"
#include "NetworkBlobRegistry.h"
#include "NetworkCache.h"
#include "NetworkConnectionToWebProcess.h"
#include "NetworkLoad.h"
#include "NetworkLoadChecker.h"
#include "NetworkProcess.h"
#include "NetworkProcessConnectionMessages.h"
#include "SessionTracker.h"
#include "WebCoreArgumentCoders.h"
#include "WebErrors.h"
#include "WebResourceLoaderMessages.h"
#include "WebsiteDataStoreParameters.h"
#include <WebCore/BlobDataFileReference.h>
#include <WebCore/CertificateInfo.h>
#include <WebCore/DiagnosticLoggingKeys.h>
#include <WebCore/HTTPHeaderNames.h>
#include <WebCore/HTTPParsers.h>
#include <WebCore/NetworkLoadMetrics.h>
#include <WebCore/ProtectionSpace.h>
#include <WebCore/SameSiteInfo.h>
#include <WebCore/SecurityOrigin.h>
#include <WebCore/SharedBuffer.h>
#include <WebCore/SynchronousLoaderClient.h>
#include <wtf/RunLoop.h>
#if HAVE(CFNETWORK_STORAGE_PARTITIONING) && !RELEASE_LOG_DISABLED
#include <WebCore/NetworkStorageSession.h>
#include <WebCore/PlatformCookieJar.h>
#endif
using namespace WebCore;
#define RELEASE_LOG_IF_ALLOWED(fmt, ...) RELEASE_LOG_IF(isAlwaysOnLoggingAllowed(), Network, "%p - NetworkResourceLoader::" fmt, this, ##__VA_ARGS__)
#define RELEASE_LOG_ERROR_IF_ALLOWED(fmt, ...) RELEASE_LOG_ERROR_IF(isAlwaysOnLoggingAllowed(), Network, "%p - NetworkResourceLoader::" fmt, this, ##__VA_ARGS__)
namespace WebKit {
struct NetworkResourceLoader::SynchronousLoadData {
SynchronousLoadData(RefPtr<Messages::NetworkConnectionToWebProcess::PerformSynchronousLoad::DelayedReply>&& reply)
: delayedReply(WTFMove(reply))
{
ASSERT(delayedReply);
}
ResourceRequest currentRequest;
RefPtr<Messages::NetworkConnectionToWebProcess::PerformSynchronousLoad::DelayedReply> delayedReply;
ResourceResponse response;
ResourceError error;
};
static void sendReplyToSynchronousRequest(NetworkResourceLoader::SynchronousLoadData& data, const SharedBuffer* buffer)
{
ASSERT(data.delayedReply);
ASSERT(!data.response.isNull() || !data.error.isNull());
Vector<char> responseBuffer;
if (buffer && buffer->size())
responseBuffer.append(buffer->data(), buffer->size());
data.delayedReply->send(data.error, data.response, responseBuffer);
data.delayedReply = nullptr;
}
NetworkResourceLoader::NetworkResourceLoader(NetworkResourceLoadParameters&& parameters, NetworkConnectionToWebProcess& connection, RefPtr<Messages::NetworkConnectionToWebProcess::PerformSynchronousLoad::DelayedReply>&& synchronousReply)
: m_parameters { WTFMove(parameters) }
, m_connection { connection }
, m_defersLoading { parameters.defersLoading }
, m_isAllowedToAskUserForCredentials { m_parameters.clientCredentialPolicy == ClientCredentialPolicy::MayAskClientForCredentials }
, m_bufferingTimer { *this, &NetworkResourceLoader::bufferingTimerFired }
, m_cache { sessionID().isEphemeral() ? nullptr : NetworkProcess::singleton().cache() }
{
ASSERT(RunLoop::isMain());
// FIXME: This is necessary because of the existence of EmptyFrameLoaderClient in WebCore.
// Once bug 116233 is resolved, this ASSERT can just be "m_webPageID && m_webFrameID"
ASSERT((m_parameters.webPageID && m_parameters.webFrameID) || m_parameters.clientCredentialPolicy == ClientCredentialPolicy::CannotAskClientForCredentials);
if (originalRequest().httpBody()) {
for (const auto& element : originalRequest().httpBody()->elements()) {
if (element.m_type == FormDataElement::Type::EncodedBlob)
m_fileReferences.appendVector(NetworkBlobRegistry::singleton().filesInBlob(connection, element.m_url));
}
}
if (synchronousReply || parameters.shouldRestrictHTTPResponseAccess) {
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);
if (m_parameters.cspResponseHeaders)
m_networkLoadChecker->setCSPResponseHeaders(ContentSecurityPolicyResponseHeaders { m_parameters.cspResponseHeaders.value() });
#if ENABLE(CONTENT_EXTENSIONS)
m_networkLoadChecker->setContentExtensionController(URL { m_parameters.mainDocumentURL }, m_parameters.userContentControllerIdentifier);
#endif
}
if (synchronousReply)
m_synchronousLoadData = std::make_unique<SynchronousLoadData>(WTFMove(synchronousReply));
}
NetworkResourceLoader::~NetworkResourceLoader()
{
ASSERT(RunLoop::isMain());
ASSERT(!m_networkLoad);
ASSERT(!isSynchronous() || !m_synchronousLoadData->delayedReply);
}
bool NetworkResourceLoader::canUseCache(const ResourceRequest& request) const
{
if (!m_cache)
return false;
ASSERT(!sessionID().isEphemeral());
if (!request.url().protocolIsInHTTPFamily())
return false;
if (originalRequest().cachePolicy() == WebCore::DoNotUseAnyCache)
return false;
return true;
}
bool NetworkResourceLoader::canUseCachedRedirect(const ResourceRequest& request) const
{
if (!canUseCache(request))
return false;
// Limit cached redirects to avoid cycles and other trouble.
// Networking layer follows over 30 redirects but caching that many seems unnecessary.
static const unsigned maximumCachedRedirectCount { 5 };
if (m_redirectCount > maximumCachedRedirectCount)
return false;
return true;
}
bool NetworkResourceLoader::isSynchronous() const
{
return !!m_synchronousLoadData;
}
void NetworkResourceLoader::start()
{
ASSERT(RunLoop::isMain());
if (m_defersLoading) {
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());
return;
}
ASSERT(!m_wasStarted);
m_wasStarted = true;
if (m_networkLoadChecker) {
m_networkLoadChecker->check(ResourceRequest { originalRequest() }, [this] (auto&& result) {
if (!result.has_value()) {
if (!result.error().isCancellation())
this->didFailLoading(result.error());
return;
}
if (this->canUseCache(this->originalRequest())) {
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());
this->retrieveCacheEntry(this->originalRequest());
return;
}
this->startNetworkLoad(WTFMove(result.value()), FirstLoad::Yes);
});
return;
}
// FIXME: Remove that code path once m_networkLoadChecker is used for all network loads.
if (canUseCache(originalRequest())) {
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());
retrieveCacheEntry(originalRequest());
return;
}
startNetworkLoad(ResourceRequest { originalRequest() }, FirstLoad::Yes);
}
void NetworkResourceLoader::retrieveCacheEntry(const ResourceRequest& request)
{
ASSERT(canUseCache(request));
RefPtr<NetworkResourceLoader> loader(this);
m_cache->retrieve(request, { m_parameters.webPageID, m_parameters.webFrameID }, [this, loader = WTFMove(loader), request = ResourceRequest { request }](auto entry) mutable {
#if RELEASE_LOG_DISABLED
UNUSED_PARAM(this);
#endif
if (loader->hasOneRef()) {
// The loader has been aborted and is only held alive by this lambda.
return;
}
if (!entry) {
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());
loader->startNetworkLoad(WTFMove(request), FirstLoad::Yes);
return;
}
if (entry->redirectRequest()) {
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());
loader->dispatchWillSendRequestForCacheEntry(WTFMove(entry));
return;
}
if (loader->m_parameters.needsCertificateInfo && !entry->response().certificateInfo()) {
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());
loader->startNetworkLoad(WTFMove(request), FirstLoad::Yes);
return;
}
if (entry->needsValidation() || request.cachePolicy() == WebCore::RefreshAnyCacheData) {
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());
loader->validateCacheEntry(WTFMove(entry));
return;
}
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());
loader->didRetrieveCacheEntry(WTFMove(entry));
});
}
void NetworkResourceLoader::startNetworkLoad(ResourceRequest&& request, FirstLoad load)
{
if (load == FirstLoad::Yes) {
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());
consumeSandboxExtensions();
if (isSynchronous() || m_parameters.maximumBufferingTime > 0_s)
m_bufferedData = SharedBuffer::create();
if (canUseCache(request))
m_bufferedDataForCache = SharedBuffer::create();
}
NetworkLoadParameters parameters = m_parameters;
parameters.defersLoading = m_defersLoading;
if (m_networkLoadChecker)
parameters.storedCredentialsPolicy = m_networkLoadChecker->storedCredentialsPolicy();
if (request.url().protocolIsBlob())
parameters.blobFileReferences = NetworkBlobRegistry::singleton().filesInBlob(m_connection, originalRequest().url());
auto* networkSession = SessionTracker::networkSession(parameters.sessionID);
if (!networkSession && parameters.sessionID.isEphemeral()) {
NetworkProcess::singleton().addWebsiteDataStore(WebsiteDataStoreParameters::privateSessionParameters(parameters.sessionID));
networkSession = SessionTracker::networkSession(parameters.sessionID);
}
if (!networkSession) {
WTFLogAlways("Attempted to create a NetworkLoad with a session (id=%" PRIu64 ") that does not exist.", parameters.sessionID.sessionID());
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());
NetworkProcess::singleton().logDiagnosticMessage(m_parameters.webPageID, WebCore::DiagnosticLoggingKeys::internalErrorKey(), WebCore::DiagnosticLoggingKeys::invalidSessionIDKey(), WebCore::ShouldSample::No);
didFailLoading(internalError(request.url()));
return;
}
parameters.request = WTFMove(request);
m_networkLoad = std::make_unique<NetworkLoad>(*this, WTFMove(parameters), *networkSession);
if (m_defersLoading) {
RELEASE_LOG_IF_ALLOWED("startNetworkLoad: Created, but deferred (pageID = %" PRIu64 ", frameID = %" PRIu64 ", resourceID = %" PRIu64 ")",
m_parameters.webPageID, m_parameters.webFrameID, m_parameters.identifier);
}
}
void NetworkResourceLoader::setDefersLoading(bool defers)
{
if (m_defersLoading == defers)
return;
m_defersLoading = defers;
if (defers)
RELEASE_LOG_IF_ALLOWED("setDefersLoading: Deferring resource load (pageID = %" PRIu64 ", frameID = %" PRIu64 ", resourceID = %" PRIu64 ")", m_parameters.webPageID, m_parameters.webFrameID, m_parameters.identifier);
else
RELEASE_LOG_IF_ALLOWED("setDefersLoading: Resuming deferred resource load (pageID = %" PRIu64 ", frameID = %" PRIu64 ", resourceID = %" PRIu64 ")", m_parameters.webPageID, m_parameters.webFrameID, m_parameters.identifier);
if (m_networkLoad) {
m_networkLoad->setDefersLoading(defers);
return;
}
if (!m_defersLoading && !m_wasStarted)
start();
else
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);
}
void NetworkResourceLoader::cleanup()
{
ASSERT(RunLoop::isMain());
m_bufferingTimer.stop();
invalidateSandboxExtensions();
m_networkLoad = nullptr;
// This will cause NetworkResourceLoader to be destroyed and therefore we do it last.
m_connection->didCleanupResourceLoader(*this);
}
void NetworkResourceLoader::convertToDownload(DownloadID downloadID, const ResourceRequest& request, const ResourceResponse& response)
{
ASSERT(m_networkLoad);
NetworkProcess::singleton().downloadManager().convertNetworkLoadToDownload(downloadID, std::exchange(m_networkLoad, nullptr), WTFMove(m_fileReferences), request, response);
}
void NetworkResourceLoader::abort()
{
ASSERT(RunLoop::isMain());
RELEASE_LOG_IF_ALLOWED("abort: Canceling resource load (pageID = %" PRIu64 ", frameID = %" PRIu64 ", resourceID = %" PRIu64 ")",
m_parameters.webPageID, m_parameters.webFrameID, m_parameters.identifier);
if (m_networkLoad) {
if (canUseCache(m_networkLoad->currentRequest())) {
// We might already have used data from this incomplete load. Ensure older versions don't remain in the cache after cancel.
if (!m_response.isNull())
m_cache->remove(m_networkLoad->currentRequest());
}
m_networkLoad->cancel();
}
cleanup();
}
static bool areFrameAncestorsSameSite(const ResourceResponse& response, const Vector<RefPtr<SecurityOrigin>>& frameAncestorOrigins)
{
#if ENABLE(PUBLIC_SUFFIX_LIST)
auto responsePartition = ResourceRequest::partitionName(response.url().host());
return frameAncestorOrigins.findMatching([&](const auto& item) {
return item->isUnique() || ResourceRequest::partitionName(item->host()) != responsePartition;
}) == notFound;
#else
UNUSED_PARAM(response);
UNUSED_PARAM(frameAncestorOrigins);
return false;
#endif
}
static bool areFrameAncestorsSameOrigin(const ResourceResponse& response, const Vector<RefPtr<SecurityOrigin>>& frameAncestorOrigins)
{
return frameAncestorOrigins.findMatching([responseOrigin = SecurityOrigin::create(response.url())](const auto& item) {
return !item->isSameOriginAs(responseOrigin);
}) == notFound;
}
static bool shouldCancelCrossOriginLoad(const ResourceResponse& response, const Vector<RefPtr<SecurityOrigin>>& frameAncestorOrigins)
{
auto fromOriginDirective = WebCore::parseFromOriginHeader(response.httpHeaderField(WebCore::HTTPHeaderName::FromOrigin));
switch (fromOriginDirective) {
case WebCore::FromOriginDisposition::None:
case WebCore::FromOriginDisposition::Invalid:
return false;
case WebCore::FromOriginDisposition::Same:
return !areFrameAncestorsSameOrigin(response, frameAncestorOrigins);
case WebCore::FromOriginDisposition::SameSite:
return !areFrameAncestorsSameSite(response, frameAncestorOrigins);
}
RELEASE_ASSERT_NOT_REACHED();
}
static ResourceError fromOriginResourceError(const URL& url)
{
return { errorDomainWebKitInternal, 0, url, ASCIILiteral { "Cancelled load because it violates the resource's From-Origin response header." }, ResourceError::Type::AccessControl };
}
auto NetworkResourceLoader::didReceiveResponse(ResourceResponse&& receivedResponse) -> ShouldContinueDidReceiveResponse
{
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());
m_response = WTFMove(receivedResponse);
if (shouldCaptureExtraNetworkLoadMetrics())
m_connection->addNetworkLoadInformationResponse(identifier(), m_response);
// For multipart/x-mixed-replace didReceiveResponseAsync gets called multiple times and buffering would require special handling.
if (!isSynchronous() && m_response.isMultipart())
m_bufferedData = nullptr;
if (m_response.isMultipart())
m_bufferedDataForCache = nullptr;
if (m_cacheEntryForValidation) {
bool validationSucceeded = m_response.httpStatusCode() == 304; // 304 Not Modified
if (validationSucceeded) {
m_cacheEntryForValidation = m_cache->update(originalRequest(), { m_parameters.webPageID, m_parameters.webFrameID }, *m_cacheEntryForValidation, m_response);
// If the request was conditional then this revalidation was not triggered by the network cache and we pass the 304 response to WebCore.
if (originalRequest().isConditional())
m_cacheEntryForValidation = nullptr;
} else
m_cacheEntryForValidation = nullptr;
}
bool shouldSendDidReceiveResponse = !m_cacheEntryForValidation;
bool shouldWaitContinueDidReceiveResponse = isMainResource();
if (shouldSendDidReceiveResponse) {
ResourceError error;
if (m_parameters.shouldEnableFromOriginResponseHeader && shouldCancelCrossOriginLoad(m_response, m_parameters.frameAncestorOrigins))
error = fromOriginResourceError(m_response.url());
if (error.isNull() && m_networkLoadChecker)
error = m_networkLoadChecker->validateResponse(m_response);
if (!error.isNull()) {
RunLoop::main().dispatch([protectedThis = makeRef(*this), error = WTFMove(error)] {
if (protectedThis->m_networkLoad)
protectedThis->didFailLoading(error);
});
return ShouldContinueDidReceiveResponse::No;
}
auto response = sanitizeResponseIfPossible(ResourceResponse { m_response }, ResourceResponse::SanitizationType::CrossOriginSafe);
if (isSynchronous())
m_synchronousLoadData->response = WTFMove(response);
else
send(Messages::WebResourceLoader::DidReceiveResponse { response, shouldWaitContinueDidReceiveResponse });
}
// For main resources, the web process is responsible for sending back a NetworkResourceLoader::ContinueDidReceiveResponse message.
bool shouldContinueDidReceiveResponse = !shouldWaitContinueDidReceiveResponse || m_cacheEntryForValidation;
if (shouldContinueDidReceiveResponse) {
RELEASE_LOG_IF_ALLOWED("didReceiveResponse: Should not wait for message from WebContent process before continuing resource load (pageID = %" PRIu64 ", frameID = %" PRIu64 ", resourceID = %" PRIu64 ")", m_parameters.webPageID, m_parameters.webFrameID, m_parameters.identifier);
return ShouldContinueDidReceiveResponse::Yes;
}
RELEASE_LOG_IF_ALLOWED("didReceiveResponse: Should wait for message from WebContent process before continuing resource load (pageID = %" PRIu64 ", frameID = %" PRIu64 ", resourceID = %" PRIu64 ")", m_parameters.webPageID, m_parameters.webFrameID, m_parameters.identifier);
return ShouldContinueDidReceiveResponse::No;
}
void NetworkResourceLoader::didReceiveBuffer(Ref<SharedBuffer>&& buffer, int reportedEncodedDataLength)
{
if (!m_numBytesReceived) {
RELEASE_LOG_IF_ALLOWED("didReceiveBuffer: Started receiving data (pageID = %" PRIu64 ", frameID = %" PRIu64 ", resourceID = %" PRIu64 ")", m_parameters.webPageID, m_parameters.webFrameID, m_parameters.identifier);
}
m_numBytesReceived += buffer->size();
ASSERT(!m_cacheEntryForValidation);
if (m_bufferedDataForCache) {
// Prevent memory growth in case of streaming data.
const size_t maximumCacheBufferSize = 10 * 1024 * 1024;
if (m_bufferedDataForCache->size() + buffer->size() <= maximumCacheBufferSize)
m_bufferedDataForCache->append(buffer.get());
else
m_bufferedDataForCache = nullptr;
}
// FIXME: At least on OS X Yosemite we always get -1 from the resource handle.
unsigned encodedDataLength = reportedEncodedDataLength >= 0 ? reportedEncodedDataLength : buffer->size();
m_bytesReceived += buffer->size();
if (m_bufferedData) {
m_bufferedData->append(buffer.get());
m_bufferedDataEncodedDataLength += encodedDataLength;
startBufferingTimerIfNeeded();
return;
}
sendBuffer(buffer, encodedDataLength);
}
void NetworkResourceLoader::didFinishLoading(const NetworkLoadMetrics& networkLoadMetrics)
{
RELEASE_LOG_IF_ALLOWED("didFinishLoading: (pageID = %" PRIu64 ", frameID = %" PRIu64 ", resourceID = %" PRIu64 ", length = %zd)", m_parameters.webPageID, m_parameters.webFrameID, m_parameters.identifier, m_numBytesReceived);
if (shouldCaptureExtraNetworkLoadMetrics())
m_connection->addNetworkLoadInformationMetrics(identifier(), networkLoadMetrics);
if (m_cacheEntryForValidation) {
// 304 Not Modified
ASSERT(m_response.httpStatusCode() == 304);
LOG(NetworkCache, "(NetworkProcess) revalidated");
didRetrieveCacheEntry(WTFMove(m_cacheEntryForValidation));
return;
}
#if HAVE(CFNETWORK_STORAGE_PARTITIONING) && !RELEASE_LOG_DISABLED
if (shouldLogCookieInformation())
logCookieInformation();
#endif
if (isSynchronous())
sendReplyToSynchronousRequest(*m_synchronousLoadData, m_bufferedData.get());
else {
if (m_bufferedData && !m_bufferedData->isEmpty()) {
// FIXME: Pass a real value or remove the encoded data size feature.
sendBuffer(*m_bufferedData, -1);
}
send(Messages::WebResourceLoader::DidFinishResourceLoad(networkLoadMetrics));
}
tryStoreAsCacheEntry();
cleanup();
}
void NetworkResourceLoader::didFailLoading(const ResourceError& error)
{
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());
if (shouldCaptureExtraNetworkLoadMetrics())
m_connection->removeNetworkLoadInformation(identifier());
ASSERT(!error.isNull());
m_cacheEntryForValidation = nullptr;
if (isSynchronous()) {
m_synchronousLoadData->error = error;
sendReplyToSynchronousRequest(*m_synchronousLoadData, nullptr);
} else if (auto* connection = messageSenderConnection())
connection->send(Messages::WebResourceLoader::DidFailResourceLoad(error), messageSenderDestinationID());
cleanup();
}
void NetworkResourceLoader::didBlockAuthenticationChallenge()
{
send(Messages::WebResourceLoader::DidBlockAuthenticationChallenge());
}
void NetworkResourceLoader::willSendRedirectedRequest(ResourceRequest&& request, WebCore::ResourceRequest&& redirectRequest, ResourceResponse&& redirectResponse)
{
++m_redirectCount;
if (m_networkLoadChecker) {
m_networkLoadChecker->checkRedirection(redirectResponse, WTFMove(redirectRequest), [protectedThis = makeRef(*this), this, storedCredentialsPolicy = m_networkLoadChecker->storedCredentialsPolicy(), request = WTFMove(request), redirectResponse](auto&& result) mutable {
if (!result.has_value()) {
if (result.error().isCancellation())
return;
if (m_parameters.options.redirect == FetchOptions::Redirect::Manual) {
redirectResponse.setType(ResourceResponse::Type::Opaqueredirect);
this->didReceiveResponse(WTFMove(redirectResponse));
this->didFinishLoading({ });
return;
}
this->didFailLoading(result.error());
return;
}
if (storedCredentialsPolicy != m_networkLoadChecker->storedCredentialsPolicy()) {
// We need to restart the load to update the session according the new credential policy.
m_networkLoad->cancel();
auto request = WTFMove(result.value());
m_networkLoadChecker->prepareRedirectedRequest(request);
this->startNetworkLoad(WTFMove(request), FirstLoad::No);
return;
}
if (this->isSynchronous()) {
// We do not support prompting for credentials for synchronous loads. If we ever change this policy then
// we need to take care to prompt if and only if request and redirectRequest are not mixed content.
this->continueWillSendRequest(WTFMove(result.value()), false);
return;
}
this->continueWillSendRedirectedRequest(WTFMove(request), WTFMove(result.value()), WTFMove(redirectResponse));
});
return;
}
continueWillSendRedirectedRequest(WTFMove(request), WTFMove(redirectRequest), WTFMove(redirectResponse));
}
void NetworkResourceLoader::continueWillSendRedirectedRequest(WebCore::ResourceRequest&& request, WebCore::ResourceRequest&& redirectRequest, ResourceResponse&& redirectResponse)
{
ASSERT(!isSynchronous());
if (canUseCachedRedirect(request))
m_cache->storeRedirect(request, redirectResponse, redirectRequest);
if (m_parameters.shouldEnableFromOriginResponseHeader && shouldCancelCrossOriginLoad(redirectResponse, m_parameters.frameAncestorOrigins) && m_networkLoad) {
didFailLoading(fromOriginResourceError(redirectResponse.url()));
return;
}
send(Messages::WebResourceLoader::WillSendRequest(redirectRequest, sanitizeResponseIfPossible(WTFMove(redirectResponse), ResourceResponse::SanitizationType::Redirection)));
}
ResourceResponse NetworkResourceLoader::sanitizeResponseIfPossible(ResourceResponse&& response, ResourceResponse::SanitizationType type)
{
if (m_parameters.shouldRestrictHTTPResponseAccess) {
if (type == ResourceResponse::SanitizationType::CrossOriginSafe) {
// We reduce filtering when it would otherwise be visible to scripts.
// FIXME: We should use response tainting once computed in Network Process.
bool isSameOrigin = m_parameters.sourceOrigin ? m_parameters.sourceOrigin->canRequest(response.url()) : protocolHostAndPortAreEqual(response.url(), m_parameters.request.url());
if (isSameOrigin && m_parameters.options.destination == FetchOptions::Destination::EmptyString)
type = ResourceResponse::SanitizationType::RemoveCookies;
}
response.sanitizeHTTPHeaderFields(type);
}
return WTFMove(response);
}
void NetworkResourceLoader::continueWillSendRequest(ResourceRequest&& newRequest, bool isAllowedToAskUserForCredentials)
{
if (m_networkLoadChecker) {
// FIXME: We should be doing this check when receiving the redirection.
if (!newRequest.url().protocolIsInHTTPFamily() && m_redirectCount) {
didFailLoading(ResourceError { String { }, 0, newRequest.url(), ASCIILiteral("Redirection to URL with a scheme that is not HTTP(S)"), ResourceError::Type::AccessControl });
return;
}
}
RELEASE_LOG_IF_ALLOWED("continueWillSendRequest: (pageID = %" PRIu64 ", frameID = %" PRIu64 ", resourceID = %" PRIu64 ")", m_parameters.webPageID, m_parameters.webFrameID, m_parameters.identifier);
if (m_networkLoadChecker)
m_networkLoadChecker->prepareRedirectedRequest(newRequest);
m_isAllowedToAskUserForCredentials = isAllowedToAskUserForCredentials;
// If there is a match in the network cache, we need to reuse the original cache policy and partition.
newRequest.setCachePolicy(originalRequest().cachePolicy());
newRequest.setCachePartition(originalRequest().cachePartition());
if (m_isWaitingContinueWillSendRequestForCachedRedirect) {
m_isWaitingContinueWillSendRequestForCachedRedirect = false;
LOG(NetworkCache, "(NetworkProcess) Retrieving cached redirect");
if (canUseCachedRedirect(newRequest))
retrieveCacheEntry(newRequest);
else
startNetworkLoad(WTFMove(newRequest), FirstLoad::Yes);
return;
}
if (m_networkLoad)
m_networkLoad->continueWillSendRequest(WTFMove(newRequest));
}
void NetworkResourceLoader::continueDidReceiveResponse()
{
if (m_cacheEntryWaitingForContinueDidReceiveResponse) {
continueProcessingCachedEntryAfterDidReceiveResponse(WTFMove(m_cacheEntryWaitingForContinueDidReceiveResponse));
return;
}
// FIXME: Remove this check once BlobResourceHandle implements didReceiveResponseAsync correctly.
// Currently, it does not wait for response, so the load is likely to finish before continueDidReceiveResponse.
if (m_networkLoad)
m_networkLoad->continueDidReceiveResponse();
}
void NetworkResourceLoader::didSendData(unsigned long long bytesSent, unsigned long long totalBytesToBeSent)
{
if (!isSynchronous())
send(Messages::WebResourceLoader::DidSendData(bytesSent, totalBytesToBeSent));
}
void NetworkResourceLoader::startBufferingTimerIfNeeded()
{
if (isSynchronous())
return;
if (m_bufferingTimer.isActive())
return;
m_bufferingTimer.startOneShot(m_parameters.maximumBufferingTime);
}
void NetworkResourceLoader::bufferingTimerFired()
{
ASSERT(m_bufferedData);
ASSERT(m_networkLoad);
if (m_bufferedData->isEmpty())
return;
IPC::SharedBufferDataReference dataReference(m_bufferedData.get());
size_t encodedLength = m_bufferedDataEncodedDataLength;
m_bufferedData = SharedBuffer::create();
m_bufferedDataEncodedDataLength = 0;
send(Messages::WebResourceLoader::DidReceiveData(dataReference, encodedLength));
}
void NetworkResourceLoader::sendBuffer(SharedBuffer& buffer, size_t encodedDataLength)
{
ASSERT(!isSynchronous());
IPC::SharedBufferDataReference dataReference(&buffer);
send(Messages::WebResourceLoader::DidReceiveData(dataReference, encodedDataLength));
}
void NetworkResourceLoader::tryStoreAsCacheEntry()
{
if (!canUseCache(m_networkLoad->currentRequest()))
return;
if (!m_bufferedDataForCache)
return;
m_cache->store(m_networkLoad->currentRequest(), m_response, WTFMove(m_bufferedDataForCache), [loader = makeRef(*this)](auto& mappedBody) mutable {
#if ENABLE(SHAREABLE_RESOURCE)
if (mappedBody.shareableResourceHandle.isNull())
return;
LOG(NetworkCache, "(NetworkProcess) sending DidCacheResource");
loader->send(Messages::NetworkProcessConnection::DidCacheResource(loader->originalRequest(), mappedBody.shareableResourceHandle, loader->sessionID()));
#endif
});
}
void NetworkResourceLoader::didRetrieveCacheEntry(std::unique_ptr<NetworkCache::Entry> entry)
{
auto response = entry->response();
ResourceError error;
if (m_parameters.shouldEnableFromOriginResponseHeader && shouldCancelCrossOriginLoad(response, m_parameters.frameAncestorOrigins))
error = fromOriginResourceError(response.url());
if (error.isNull() && m_networkLoadChecker)
error = m_networkLoadChecker->validateResponse(response);
if (!error.isNull()) {
didFailLoading(error);
return;
}
response = sanitizeResponseIfPossible(WTFMove(response), ResourceResponse::SanitizationType::CrossOriginSafe);
if (isSynchronous()) {
m_synchronousLoadData->response = WTFMove(response);
sendReplyToSynchronousRequest(*m_synchronousLoadData, entry->buffer());
cleanup();
return;
}
bool needsContinueDidReceiveResponseMessage = isMainResource();
send(Messages::WebResourceLoader::DidReceiveResponse { response, needsContinueDidReceiveResponseMessage });
if (needsContinueDidReceiveResponseMessage)
m_cacheEntryWaitingForContinueDidReceiveResponse = WTFMove(entry);
else
continueProcessingCachedEntryAfterDidReceiveResponse(WTFMove(entry));
}
void NetworkResourceLoader::continueProcessingCachedEntryAfterDidReceiveResponse(std::unique_ptr<NetworkCache::Entry> entry)
{
if (entry->sourceStorageRecord().bodyHash && !m_parameters.derivedCachedDataTypesToRetrieve.isEmpty()) {
auto bodyHash = *entry->sourceStorageRecord().bodyHash;
auto* entryPtr = entry.release();
auto retrieveCount = m_parameters.derivedCachedDataTypesToRetrieve.size();
for (auto& type : m_parameters.derivedCachedDataTypesToRetrieve) {
NetworkCache::DataKey key { originalRequest().cachePartition(), type, bodyHash };
m_cache->retrieveData(key, [loader = makeRef(*this), entryPtr, type, retrieveCount] (const uint8_t* data, size_t size) mutable {
loader->m_retrievedDerivedDataCount++;
bool retrievedAll = loader->m_retrievedDerivedDataCount == retrieveCount;
std::unique_ptr<NetworkCache::Entry> entry(retrievedAll ? entryPtr : nullptr);
if (loader->hasOneRef())
return;
if (data) {
IPC::DataReference dataReference(data, size);
loader->send(Messages::WebResourceLoader::DidRetrieveDerivedData(type, dataReference));
}
if (retrievedAll) {
loader->sendResultForCacheEntry(WTFMove(entry));
loader->cleanup();
}
});
}
return;
}
sendResultForCacheEntry(WTFMove(entry));
cleanup();
}
void NetworkResourceLoader::sendResultForCacheEntry(std::unique_ptr<NetworkCache::Entry> entry)
{
#if ENABLE(SHAREABLE_RESOURCE)
if (!entry->shareableResourceHandle().isNull()) {
send(Messages::WebResourceLoader::DidReceiveResource(entry->shareableResourceHandle()));
return;
}
#endif
#if HAVE(CFNETWORK_STORAGE_PARTITIONING) && !RELEASE_LOG_DISABLED
if (shouldLogCookieInformation())
logCookieInformation();
#endif
WebCore::NetworkLoadMetrics networkLoadMetrics;
networkLoadMetrics.markComplete();
networkLoadMetrics.requestHeaderBytesSent = 0;
networkLoadMetrics.requestBodyBytesSent = 0;
networkLoadMetrics.responseHeaderBytesReceived = 0;
networkLoadMetrics.responseBodyBytesReceived = 0;
networkLoadMetrics.responseBodyDecodedSize = 0;
sendBuffer(*entry->buffer(), entry->buffer()->size());
send(Messages::WebResourceLoader::DidFinishResourceLoad(networkLoadMetrics));
}
void NetworkResourceLoader::validateCacheEntry(std::unique_ptr<NetworkCache::Entry> entry)
{
ASSERT(!m_networkLoad);
// If the request is already conditional then the revalidation was not triggered by the disk cache
// and we should not overwrite the existing conditional headers.
ResourceRequest revalidationRequest = originalRequest();
if (!revalidationRequest.isConditional()) {
String eTag = entry->response().httpHeaderField(HTTPHeaderName::ETag);
String lastModified = entry->response().httpHeaderField(HTTPHeaderName::LastModified);
if (!eTag.isEmpty())
revalidationRequest.setHTTPHeaderField(HTTPHeaderName::IfNoneMatch, eTag);
if (!lastModified.isEmpty())
revalidationRequest.setHTTPHeaderField(HTTPHeaderName::IfModifiedSince, lastModified);
}
m_cacheEntryForValidation = WTFMove(entry);
startNetworkLoad(WTFMove(revalidationRequest), FirstLoad::Yes);
}
void NetworkResourceLoader::dispatchWillSendRequestForCacheEntry(std::unique_ptr<NetworkCache::Entry> entry)
{
ASSERT(entry->redirectRequest());
ASSERT(!m_isWaitingContinueWillSendRequestForCachedRedirect);
LOG(NetworkCache, "(NetworkProcess) Executing cached redirect");
auto& response = entry->response();
if (m_parameters.shouldEnableFromOriginResponseHeader && shouldCancelCrossOriginLoad(response, m_parameters.frameAncestorOrigins) && m_networkLoad) {
didFailLoading(fromOriginResourceError(response.url()));
return;
}
++m_redirectCount;
send(Messages::WebResourceLoader::WillSendRequest { *entry->redirectRequest(), sanitizeResponseIfPossible(ResourceResponse { response }, ResourceResponse::SanitizationType::Redirection) });
m_isWaitingContinueWillSendRequestForCachedRedirect = true;
}
IPC::Connection* NetworkResourceLoader::messageSenderConnection()
{
return &connectionToWebProcess().connection();
}
void NetworkResourceLoader::consumeSandboxExtensions()
{
ASSERT(!m_didConsumeSandboxExtensions);
for (auto& extension : m_parameters.requestBodySandboxExtensions)
extension->consume();
if (auto& extension = m_parameters.resourceSandboxExtension)
extension->consume();
for (auto& fileReference : m_fileReferences)
fileReference->prepareForFileAccess();
m_didConsumeSandboxExtensions = true;
}
void NetworkResourceLoader::invalidateSandboxExtensions()
{
if (m_didConsumeSandboxExtensions) {
for (auto& extension : m_parameters.requestBodySandboxExtensions)
extension->revoke();
if (auto& extension = m_parameters.resourceSandboxExtension)
extension->revoke();
for (auto& fileReference : m_fileReferences)
fileReference->revokeFileAccess();
m_didConsumeSandboxExtensions = false;
}
m_fileReferences.clear();
}
#if USE(PROTECTION_SPACE_AUTH_CALLBACK)
void NetworkResourceLoader::canAuthenticateAgainstProtectionSpaceAsync(const ProtectionSpace& protectionSpace)
{
NetworkProcess::singleton().canAuthenticateAgainstProtectionSpace(*this, protectionSpace);
}
void NetworkResourceLoader::continueCanAuthenticateAgainstProtectionSpace(bool result)
{
if (m_networkLoad)
m_networkLoad->continueCanAuthenticateAgainstProtectionSpace(result);
}
#endif
bool NetworkResourceLoader::isAlwaysOnLoggingAllowed() const
{
if (NetworkProcess::singleton().sessionIsControlledByAutomation(sessionID()))
return true;
return sessionID().isAlwaysOnLoggingAllowed();
}
bool NetworkResourceLoader::shouldCaptureExtraNetworkLoadMetrics() const
{
return m_connection->captureExtraNetworkLoadMetricsEnabled();
}
#if HAVE(CFNETWORK_STORAGE_PARTITIONING) && !RELEASE_LOG_DISABLED
bool NetworkResourceLoader::shouldLogCookieInformation()
{
return NetworkProcess::singleton().shouldLogCookieInformation();
}
static String escapeForJSON(String s)
{
return s.replace('\\', "\\\\").replace('"', "\\\"");
}
static String escapeIDForJSON(const std::optional<uint64_t>& value)
{
return value ? String::number(value.value()) : String("None");
};
void NetworkResourceLoader::logCookieInformation() const
{
ASSERT(shouldLogCookieInformation());
auto networkStorageSession = WebCore::NetworkStorageSession::storageSession(sessionID());
ASSERT(networkStorageSession);
logCookieInformation("NetworkResourceLoader", reinterpret_cast<const void*>(this), *networkStorageSession, originalRequest().firstPartyForCookies(), SameSiteInfo::create(originalRequest()), originalRequest().url(), originalRequest().httpReferrer(), frameID(), pageID(), identifier());
}
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)
{
ASSERT(NetworkResourceLoader::shouldLogCookieInformation());
auto escapedURL = escapeForJSON(url.string());
auto escapedFirstParty = escapeForJSON(firstParty.string());
auto escapedFrameID = escapeIDForJSON(frameID);
auto escapedPageID = escapeIDForJSON(pageID);
auto escapedIdentifier = escapeIDForJSON(identifier);
auto escapedReferrer = escapeForJSON(referrer);
#define LOCAL_LOG_IF_ALLOWED(fmt, ...) RELEASE_LOG_IF(networkStorageSession.sessionID().isAlwaysOnLoggingAllowed(), Network, "%p - %s::" fmt, loggedObject, label.utf8().data(), ##__VA_ARGS__)
#define LOCAL_LOG(str, ...) \
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__)
LOCAL_LOG(R"({ "url": "%{public}s",)", escapedURL.utf8().data());
LOCAL_LOG(R"( "partition": "%{public}s",)", "BLOCKED");
LOCAL_LOG(R"( "hasStorageAccess": %{public}s,)", "false");
LOCAL_LOG(R"( "referer": "%{public}s",)", escapedReferrer.utf8().data());
LOCAL_LOG(R"( "isSameSite": "%{public}s",)", sameSiteInfo.isSameSite ? "true" : "false");
LOCAL_LOG(R"( "isTopSite": "%{public}s",)", sameSiteInfo.isTopSite ? "true" : "false");
LOCAL_LOG(R"( "cookies": [])");
LOCAL_LOG(R"( "})");
#undef LOCAL_LOG
#undef LOCAL_LOG_IF_ALLOWED
}
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)
{
ASSERT(NetworkResourceLoader::shouldLogCookieInformation());
Vector<WebCore::Cookie> cookies;
if (!WebCore::getRawCookies(networkStorageSession, partition, sameSiteInfo, url, frameID, pageID, cookies))
return;
auto escapedURL = escapeForJSON(url.string());
auto escapedPartition = escapeForJSON(partition.string());
auto escapedReferrer = escapeForJSON(referrer);
auto escapedFrameID = escapeIDForJSON(frameID);
auto escapedPageID = escapeIDForJSON(pageID);
auto escapedIdentifier = escapeIDForJSON(identifier);
bool hasStorageAccess = (frameID && pageID) ? networkStorageSession.hasStorageAccess(url.string(), partition.string(), frameID.value(), pageID.value()) : false;
#define LOCAL_LOG_IF_ALLOWED(fmt, ...) RELEASE_LOG_IF(networkStorageSession.sessionID().isAlwaysOnLoggingAllowed(), Network, "%p - %s::" fmt, loggedObject, label.utf8().data(), ##__VA_ARGS__)
#define LOCAL_LOG(str, ...) \
LOCAL_LOG_IF_ALLOWED("logCookieInformation: pageID = %s, frameID = %s, resourceID = %s: " str, escapedPageID.utf8().data(), escapedFrameID.utf8().data(), escapedIdentifier.utf8().data(), ##__VA_ARGS__)
LOCAL_LOG(R"({ "url": "%{public}s",)", escapedURL.utf8().data());
LOCAL_LOG(R"( "partition": "%{public}s",)", escapedPartition.utf8().data());
LOCAL_LOG(R"( "hasStorageAccess": %{public}s,)", hasStorageAccess ? "true" : "false");
LOCAL_LOG(R"( "referer": "%{public}s",)", escapedReferrer.utf8().data());
LOCAL_LOG(R"( "isSameSite": "%{public}s",)", sameSiteInfo.isSameSite ? "true" : "false");
LOCAL_LOG(R"( "isTopSite": "%{public}s",)", sameSiteInfo.isTopSite ? "true" : "false");
LOCAL_LOG(R"( "cookies": [)");
auto size = cookies.size();
decltype(size) count = 0;
for (const auto& cookie : cookies) {
const char* trailingComma = ",";
if (++count == size)
trailingComma = "";
auto escapedName = escapeForJSON(cookie.name);
auto escapedValue = escapeForJSON(cookie.value);
auto escapedDomain = escapeForJSON(cookie.domain);
auto escapedPath = escapeForJSON(cookie.path);
auto escapedComment = escapeForJSON(cookie.comment);
auto escapedCommentURL = escapeForJSON(cookie.commentURL.string());
// FIXME: Log Same-Site policy for each cookie. See <https://bugs.webkit.org/show_bug.cgi?id=184894>.
LOCAL_LOG(R"( { "name": "%{public}s",)", escapedName.utf8().data());
LOCAL_LOG(R"( "value": "%{public}s",)", escapedValue.utf8().data());
LOCAL_LOG(R"( "domain": "%{public}s",)", escapedDomain.utf8().data());
LOCAL_LOG(R"( "path": "%{public}s",)", escapedPath.utf8().data());
LOCAL_LOG(R"( "created": %f,)", cookie.created);
LOCAL_LOG(R"( "expires": %f,)", cookie.expires);
LOCAL_LOG(R"( "httpOnly": %{public}s,)", cookie.httpOnly ? "true" : "false");
LOCAL_LOG(R"( "secure": %{public}s,)", cookie.secure ? "true" : "false");
LOCAL_LOG(R"( "session": %{public}s,)", cookie.session ? "true" : "false");
LOCAL_LOG(R"( "comment": "%{public}s",)", escapedComment.utf8().data());
LOCAL_LOG(R"( "commentURL": "%{public}s")", escapedCommentURL.utf8().data());
LOCAL_LOG(R"( }%{public}s)", trailingComma);
}
LOCAL_LOG(R"(]})");
#undef LOCAL_LOG
#undef LOCAL_LOG_IF_ALLOWED
}
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)
{
ASSERT(shouldLogCookieInformation());
if (networkStorageSession.shouldBlockCookies(firstParty, url))
logBlockedCookieInformation(label, loggedObject, networkStorageSession, firstParty, sameSiteInfo, url, referrer, frameID, pageID, identifier);
else {
auto partition = URL(ParsedURLString, networkStorageSession.cookieStoragePartition(firstParty, url, frameID, pageID));
logCookieInformationInternal(label, loggedObject, networkStorageSession, partition, sameSiteInfo, url, referrer, frameID, pageID, identifier);
}
}
#endif
} // namespace WebKit