diff options
Diffstat (limited to 'Source/WebKit2/NetworkProcess')
98 files changed, 11291 insertions, 2662 deletions
diff --git a/Source/WebKit2/NetworkProcess/CustomProtocols/CustomProtocolManager.h b/Source/WebKit2/NetworkProcess/CustomProtocols/CustomProtocolManager.h new file mode 100644 index 000000000..bf2bd4720 --- /dev/null +++ b/Source/WebKit2/NetworkProcess/CustomProtocols/CustomProtocolManager.h @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2012, 2013 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. + */ + +#ifndef CustomProtocolManager_h +#define CustomProtocolManager_h + +#include "Connection.h" +#include "NetworkProcessSupplement.h" +#include <wtf/WorkQueue.h> +#include <wtf/text/WTFString.h> + +#if PLATFORM(COCOA) +#include <wtf/HashMap.h> +#include <wtf/HashSet.h> +#include <wtf/RetainPtr.h> +#include <wtf/Threading.h> +#include <wtf/text/StringHash.h> +OBJC_CLASS NSURLSessionConfiguration; +OBJC_CLASS WKCustomProtocol; +#else +#include "CustomProtocolManagerImpl.h" +#endif + + +namespace IPC { +class DataReference; +} // namespace IPC + +namespace WebCore { +class ResourceError; +class ResourceRequest; +class ResourceResponse; +} // namespace WebCore + +namespace WebKit { + +class ChildProcess; +struct NetworkProcessCreationParameters; + +class CustomProtocolManager : public NetworkProcessSupplement, public IPC::Connection::WorkQueueMessageReceiver { + WTF_MAKE_NONCOPYABLE(CustomProtocolManager); +public: + explicit CustomProtocolManager(ChildProcess*); + + static const char* supplementName(); + + ChildProcess* childProcess() const { return m_childProcess; } + + void registerScheme(const String&); + void unregisterScheme(const String&); + bool supportsScheme(const String&); + +#if PLATFORM(COCOA) + void addCustomProtocol(WKCustomProtocol *); + void removeCustomProtocol(WKCustomProtocol *); +#if USE(NETWORK_SESSION) + void registerProtocolClass(NSURLSessionConfiguration *); +#endif +#endif + +private: + // ChildProcessSupplement + void initializeConnection(IPC::Connection*) override; + + // NetworkProcessSupplement + void initialize(const NetworkProcessCreationParameters&) override; + + // IPC::MessageReceiver + virtual void didReceiveMessage(IPC::Connection&, IPC::MessageDecoder&) override; + + void didFailWithError(uint64_t customProtocolID, const WebCore::ResourceError&); + void didLoadData(uint64_t customProtocolID, const IPC::DataReference&); + void didReceiveResponse(uint64_t customProtocolID, const WebCore::ResourceResponse&, uint32_t cacheStoragePolicy); + void didFinishLoading(uint64_t customProtocolID); + void wasRedirectedToRequest(uint64_t customProtocolID, const WebCore::ResourceRequest&, const WebCore::ResourceResponse& redirectResponse); + + ChildProcess* m_childProcess; + RefPtr<WorkQueue> m_messageQueue; + +#if PLATFORM(COCOA) + HashSet<String, ASCIICaseInsensitiveHash> m_registeredSchemes; + Lock m_registeredSchemesMutex; + + typedef HashMap<uint64_t, RetainPtr<WKCustomProtocol>> CustomProtocolMap; + CustomProtocolMap m_customProtocolMap; + Lock m_customProtocolMapMutex; + + // WKCustomProtocol objects can be removed from the m_customProtocolMap from multiple threads. + // We return a RetainPtr here because it is unsafe to return a raw pointer since the object might immediately be destroyed from a different thread. + RetainPtr<WKCustomProtocol> protocolForID(uint64_t customProtocolID); +#else + // FIXME: Move mac specific code to CustomProtocolManagerImpl. + std::unique_ptr<CustomProtocolManagerImpl> m_impl; +#endif +}; + +} // namespace WebKit + +#endif // CustomProtocolManager_h diff --git a/Source/WebKit2/NetworkProcess/CustomProtocols/CustomProtocolManager.messages.in b/Source/WebKit2/NetworkProcess/CustomProtocols/CustomProtocolManager.messages.in new file mode 100644 index 000000000..0f55eceb1 --- /dev/null +++ b/Source/WebKit2/NetworkProcess/CustomProtocols/CustomProtocolManager.messages.in @@ -0,0 +1,32 @@ +# Copyright (C) 2012 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. + +messages -> CustomProtocolManager { + DidFailWithError(uint64_t customProtocolID, WebCore::ResourceError error) + DidLoadData(uint64_t customProtocolID, IPC::DataReference data) + DidReceiveResponse(uint64_t customProtocolID, WebCore::ResourceResponse response, uint32_t cacheStoragePolicy) + DidFinishLoading(uint64_t customProtocolID) + WasRedirectedToRequest(uint64_t customProtocolID, WebCore::ResourceRequest request, WebCore::ResourceResponse redirectResponse); + + RegisterScheme(String name) + UnregisterScheme(String name) +} diff --git a/Source/WebKit2/NetworkProcess/CustomProtocols/qt/CustomProtocolManagerImpl.h b/Source/WebKit2/NetworkProcess/CustomProtocols/qt/CustomProtocolManagerImpl.h new file mode 100644 index 000000000..a115e40eb --- /dev/null +++ b/Source/WebKit2/NetworkProcess/CustomProtocols/qt/CustomProtocolManagerImpl.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2016 Konstantin Tokarev <annulen@yandex.ru> + * + * 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. ``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 + * 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. + */ + +#pragma once + +#include <wtf/Noncopyable.h> + +namespace WebKit { + +class CustomProtocolManagerImpl { + WTF_MAKE_NONCOPYABLE(CustomProtocolManagerImpl); +}; + +} // namespace WebKit diff --git a/Source/WebKit2/NetworkProcess/CustomProtocols/qt/CustomProtocolManagerQt.cpp b/Source/WebKit2/NetworkProcess/CustomProtocols/qt/CustomProtocolManagerQt.cpp new file mode 100644 index 000000000..9e79503c4 --- /dev/null +++ b/Source/WebKit2/NetworkProcess/CustomProtocols/qt/CustomProtocolManagerQt.cpp @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2017 Konstantin Tokarev <annulen@yandex.ru> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "config.h" +#include "CustomProtocolManager.h" + +namespace WebKit { + +const char* CustomProtocolManager::supplementName() +{ + return ""; +} + +CustomProtocolManager::CustomProtocolManager(ChildProcess*) + : m_impl(nullptr) +{ +} + +void CustomProtocolManager::initializeConnection(IPC::Connection*) +{ +} + +void CustomProtocolManager::initialize(const NetworkProcessCreationParameters&) +{ +} + +void CustomProtocolManager::registerScheme(const String&) +{ + ASSERT_NOT_REACHED(); +} + +void CustomProtocolManager::unregisterScheme(const String&) +{ +} + +bool CustomProtocolManager::supportsScheme(const String&) +{ + return false; +} + +void CustomProtocolManager::didFailWithError(uint64_t, const WebCore::ResourceError&) +{ + ASSERT_NOT_REACHED(); +} + +void CustomProtocolManager::didLoadData(uint64_t, const IPC::DataReference&) +{ +} + +void CustomProtocolManager::didReceiveResponse(uint64_t, const WebCore::ResourceResponse&, uint32_t) +{ +} + +void CustomProtocolManager::didFinishLoading(uint64_t) +{ +} + +void CustomProtocolManager::wasRedirectedToRequest(uint64_t, const WebCore::ResourceRequest&, const WebCore::ResourceResponse&) +{ +} + +} // namespace WebKit diff --git a/Source/WebKit2/NetworkProcess/Downloads/Download.cpp b/Source/WebKit2/NetworkProcess/Downloads/Download.cpp new file mode 100644 index 000000000..4d2398a54 --- /dev/null +++ b/Source/WebKit2/NetworkProcess/Downloads/Download.cpp @@ -0,0 +1,172 @@ +/* + * Copyright (C) 2010, 2011 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 "Download.h" + +#include "AuthenticationManager.h" +#include "Connection.h" +#include "DataReference.h" +#include "DownloadManager.h" +#include "DownloadProxyMessages.h" +#include "SandboxExtension.h" +#include "WebCoreArgumentCoders.h" +#include <WebCore/NotImplemented.h> + +#if !USE(NETWORK_SESSION) +#include "DownloadAuthenticationClient.h" +#endif + +using namespace WebCore; + +namespace WebKit { + +#if USE(NETWORK_SESSION) +Download::Download(DownloadManager& downloadManager, DownloadID downloadID) +#else +Download::Download(DownloadManager& downloadManager, DownloadID downloadID, const ResourceRequest& request) +#endif + : m_downloadManager(downloadManager) + , m_downloadID(downloadID) +#if !USE(NETWORK_SESSION) + , m_request(request) +#endif +{ + ASSERT(m_downloadID.downloadID()); + + m_downloadManager.didCreateDownload(); +} + +Download::~Download() +{ + platformInvalidate(); + + m_downloadManager.didDestroyDownload(); +} + +#if USE(NETWORK_SESSION) +void Download::didStart(const ResourceRequest& request) +{ + send(Messages::DownloadProxy::DidStart(request)); +} +#else +void Download::didStart() +{ + send(Messages::DownloadProxy::DidStart(m_request)); +} +#endif + +#if !USE(NETWORK_SESSION) +void Download::didReceiveAuthenticationChallenge(const AuthenticationChallenge& authenticationChallenge) +{ + m_downloadManager.downloadsAuthenticationManager().didReceiveAuthenticationChallenge(*this, authenticationChallenge); +} +#endif + +void Download::didReceiveResponse(const ResourceResponse& response) +{ + send(Messages::DownloadProxy::DidReceiveResponse(response)); +} + +void Download::didReceiveData(uint64_t length) +{ + send(Messages::DownloadProxy::DidReceiveData(length)); +} + +bool Download::shouldDecodeSourceDataOfMIMEType(const String& mimeType) +{ + bool result; + if (!sendSync(Messages::DownloadProxy::ShouldDecodeSourceDataOfMIMEType(mimeType), Messages::DownloadProxy::ShouldDecodeSourceDataOfMIMEType::Reply(result))) + return true; + + return result; +} + +String Download::decideDestinationWithSuggestedFilename(const String& filename, bool& allowOverwrite) +{ + String destination; + SandboxExtension::Handle sandboxExtensionHandle; + if (!sendSync(Messages::DownloadProxy::DecideDestinationWithSuggestedFilename(filename), Messages::DownloadProxy::DecideDestinationWithSuggestedFilename::Reply(destination, allowOverwrite, sandboxExtensionHandle))) + return String(); + + m_sandboxExtension = SandboxExtension::create(sandboxExtensionHandle); + if (m_sandboxExtension) + m_sandboxExtension->consume(); + + return destination; +} + +void Download::didCreateDestination(const String& path) +{ + send(Messages::DownloadProxy::DidCreateDestination(path)); +} + +void Download::didFinish() +{ + platformDidFinish(); + + send(Messages::DownloadProxy::DidFinish()); + + if (m_sandboxExtension) { + m_sandboxExtension->revoke(); + m_sandboxExtension = nullptr; + } + + m_downloadManager.downloadFinished(this); +} + +void Download::didFail(const ResourceError& error, const IPC::DataReference& resumeData) +{ + send(Messages::DownloadProxy::DidFail(error, resumeData)); + + if (m_sandboxExtension) { + m_sandboxExtension->revoke(); + m_sandboxExtension = nullptr; + } + m_downloadManager.downloadFinished(this); +} + +void Download::didCancel(const IPC::DataReference& resumeData) +{ + send(Messages::DownloadProxy::DidCancel(resumeData)); + + if (m_sandboxExtension) { + m_sandboxExtension->revoke(); + m_sandboxExtension = nullptr; + } + m_downloadManager.downloadFinished(this); +} + +IPC::Connection* Download::messageSenderConnection() +{ + return m_downloadManager.downloadProxyConnection(); +} + +uint64_t Download::messageSenderDestinationID() +{ + return m_downloadID.downloadID(); +} + +} // namespace WebKit diff --git a/Source/WebKit2/NetworkProcess/Downloads/Download.h b/Source/WebKit2/NetworkProcess/Downloads/Download.h new file mode 100644 index 000000000..2c5a3a83d --- /dev/null +++ b/Source/WebKit2/NetworkProcess/Downloads/Download.h @@ -0,0 +1,162 @@ +/* + * Copyright (C) 2010, 2011 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. + */ + +#ifndef Download_h +#define Download_h + +#include "DownloadID.h" +#include "MessageSender.h" +#include "SandboxExtension.h" +#include <WebCore/ResourceRequest.h> +#include <wtf/Noncopyable.h> + +#if PLATFORM(COCOA) +#include <wtf/RetainPtr.h> + +#if USE(NETWORK_SESSION) +OBJC_CLASS NSURLSessionDownloadTask; +#else +OBJC_CLASS NSURLDownload; +OBJC_CLASS WKDownloadAsDelegate; +#endif +#endif + +#if PLATFORM(GTK) || PLATFORM(EFL) +#include <WebCore/ResourceHandle.h> +#include <WebCore/ResourceHandleClient.h> +#include <memory> +#endif + +#if USE(CFNETWORK) +#include <CFNetwork/CFURLDownloadPriv.h> +#endif + +namespace IPC { +class DataReference; +} + +namespace WebCore { +class AuthenticationChallenge; +class Credential; +class ResourceError; +class ResourceHandle; +class ResourceResponse; +} + +namespace WebKit { + +class DownloadAuthenticationClient; +class DownloadManager; +class NetworkSession; +class WebPage; + +#if PLATFORM(QT) +class QtFileDownloader; +#endif + +class Download : public IPC::MessageSender { + WTF_MAKE_NONCOPYABLE(Download); +public: +#if USE(NETWORK_SESSION) + Download(DownloadManager&, DownloadID); +#else + Download(DownloadManager&, DownloadID, const WebCore::ResourceRequest&); +#endif + ~Download(); + +#if USE(NETWORK_SESSION) && PLATFORM(COCOA) + void dataTaskDidBecomeDownloadTask(const NetworkSession&, RetainPtr<NSURLSessionDownloadTask>&&); +#else + void start(); + void startWithHandle(WebCore::ResourceHandle*, const WebCore::ResourceResponse&); +#endif + void resume(const IPC::DataReference& resumeData, const String& path, const SandboxExtension::Handle&); + void cancel(); + + DownloadID downloadID() const { return m_downloadID; } + +#if USE(NETWORK_SESSION) + void didStart(const WebCore::ResourceRequest&); +#else + void didStart(); + void didReceiveAuthenticationChallenge(const WebCore::AuthenticationChallenge&); +#endif + void didReceiveResponse(const WebCore::ResourceResponse&); + void didReceiveData(uint64_t length); + bool shouldDecodeSourceDataOfMIMEType(const String& mimeType); + String decideDestinationWithSuggestedFilename(const String& filename, bool& allowOverwrite); + void didCreateDestination(const String& path); + void didFinish(); + void platformDidFinish(); + void didFail(const WebCore::ResourceError&, const IPC::DataReference& resumeData); + void didCancel(const IPC::DataReference& resumeData); + +#if PLATFORM(QT) + void startTransfer(const String& destination); +#endif + +#if USE(CFNETWORK) + DownloadAuthenticationClient* authenticationClient(); +#endif + +private: + // IPC::MessageSender + virtual IPC::Connection* messageSenderConnection() override; + virtual uint64_t messageSenderDestinationID() override; + + void platformInvalidate(); + + DownloadManager& m_downloadManager; + DownloadID m_downloadID; +#if !USE(NETWORK_SESSION) + WebCore::ResourceRequest m_request; +#endif + + RefPtr<SandboxExtension> m_sandboxExtension; + +#if PLATFORM(COCOA) +#if USE(NETWORK_SESSION) + RetainPtr<NSURLSessionDownloadTask> m_download; +#else + RetainPtr<NSURLDownload> m_nsURLDownload; + RetainPtr<WKDownloadAsDelegate> m_delegate; +#endif +#endif +#if USE(CFNETWORK) + RetainPtr<CFURLDownloadRef> m_download; + RefPtr<DownloadAuthenticationClient> m_authenticationClient; +#endif +#if PLATFORM(QT) + QtFileDownloader* m_qtDownloader { nullptr }; +#endif +#if PLATFORM(GTK) || PLATFORM(EFL) + std::unique_ptr<WebCore::ResourceHandleClient> m_downloadClient; + RefPtr<WebCore::ResourceHandle> m_resourceHandle; +#endif +}; + +} // namespace WebKit + +#endif // Download_h diff --git a/Source/WebKit2/NetworkProcess/SyncNetworkResourceLoader.h b/Source/WebKit2/NetworkProcess/Downloads/DownloadAuthenticationClient.cpp index a60929b76..ba122642c 100644 --- a/Source/WebKit2/NetworkProcess/SyncNetworkResourceLoader.h +++ b/Source/WebKit2/NetworkProcess/Downloads/DownloadAuthenticationClient.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013 Apple Inc. All rights reserved. + * Copyright (C) 2011 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -23,39 +23,48 @@ * THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef SyncNetworkResourceLoader_h -#define SyncNetworkResourceLoader_h +#include "config.h" +#include "DownloadAuthenticationClient.h" -#include "NetworkConnectionToWebProcessMessages.h" -#include "SchedulableLoader.h" -#include <wtf/RefCounted.h> +#if !USE(NETWORK_SESSION) -#if ENABLE(NETWORK_PROCESS) +#include "Download.h" + +using namespace WebCore; namespace WebKit { -class SyncNetworkResourceLoader : public SchedulableLoader { -public: - static PassRefPtr<SyncNetworkResourceLoader> create(const NetworkResourceLoadParameters& parameters, NetworkConnectionToWebProcess* connection, PassRefPtr<Messages::NetworkConnectionToWebProcess::PerformSynchronousLoad::DelayedReply> reply) - { - return adoptRef(new SyncNetworkResourceLoader(parameters, connection, reply)); - } +DownloadAuthenticationClient::DownloadAuthenticationClient(Download* download) + : m_download(download) +{ + ASSERT_ARG(download, download); +} - virtual void start() OVERRIDE; - virtual void abort() OVERRIDE; +void DownloadAuthenticationClient::receivedCredential(const AuthenticationChallenge&, const Credential&) +{ + // FIXME (119667): This can probably be just removed. +} - virtual bool isSynchronous() OVERRIDE { return true; } +void DownloadAuthenticationClient::receivedRequestToContinueWithoutCredential(const AuthenticationChallenge&) +{ + // FIXME (119667): This can probably be just removed. +} -private: - SyncNetworkResourceLoader(const NetworkResourceLoadParameters&, NetworkConnectionToWebProcess*, PassRefPtr<Messages::NetworkConnectionToWebProcess::PerformSynchronousLoad::DelayedReply>); - - void cleanup(); - - RefPtr<Messages::NetworkConnectionToWebProcess::PerformSynchronousLoad::DelayedReply> m_delayedReply; -}; +void DownloadAuthenticationClient::receivedCancellation(const AuthenticationChallenge&) +{ + // FIXME (119667): This can probably be just removed. +} -} // namespace WebKit +void DownloadAuthenticationClient::receivedRequestToPerformDefaultHandling(const AuthenticationChallenge&) +{ + // FIXME (119667): This can probably be just removed. +} -#endif // ENABLE(NETWORK_PROCESS) +void DownloadAuthenticationClient::receivedChallengeRejection(const AuthenticationChallenge&) +{ + // FIXME (119667): This can probably be just removed. +} + +} // namespace WebKit -#endif // SyncNetworkResourceLoader_h +#endif diff --git a/Source/WebKit2/NetworkProcess/Downloads/DownloadAuthenticationClient.h b/Source/WebKit2/NetworkProcess/Downloads/DownloadAuthenticationClient.h new file mode 100644 index 000000000..9cfc7c693 --- /dev/null +++ b/Source/WebKit2/NetworkProcess/Downloads/DownloadAuthenticationClient.h @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2011 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. + */ + +#ifndef DownloadAuthenticationClient_h +#define DownloadAuthenticationClient_h + +#if !USE(NETWORK_SESSION) + +#include <WebCore/AuthenticationClient.h> +#include <wtf/PassRefPtr.h> +#include <wtf/RefCounted.h> + +namespace WebCore { + class AuthenticationChallenge; + class Credential; +} + +namespace WebKit { + +class Download; + +class DownloadAuthenticationClient : public RefCounted<DownloadAuthenticationClient>, public WebCore::AuthenticationClient { +public: + static Ref<DownloadAuthenticationClient> create(Download* download) { return adoptRef(*new DownloadAuthenticationClient(download)); } + + void detach() { m_download = 0; } + + using RefCounted<DownloadAuthenticationClient>::ref; + using RefCounted<DownloadAuthenticationClient>::deref; + +private: + DownloadAuthenticationClient(Download*); + + virtual void receivedCredential(const WebCore::AuthenticationChallenge&, const WebCore::Credential&) override; + virtual void receivedRequestToContinueWithoutCredential(const WebCore::AuthenticationChallenge&) override; + virtual void receivedCancellation(const WebCore::AuthenticationChallenge&) override; + virtual void receivedRequestToPerformDefaultHandling(const WebCore::AuthenticationChallenge&) override; + virtual void receivedChallengeRejection(const WebCore::AuthenticationChallenge&) override; + + virtual void refAuthenticationClient() override { ref(); } + virtual void derefAuthenticationClient() override { deref(); } + + Download* m_download; +}; + +} // namespace WebKit + +#endif // !USE(NETWORK_SESSION) + +#endif // DownloadAuthenticationClient_h diff --git a/Source/WebKit2/NetworkProcess/Downloads/DownloadID.h b/Source/WebKit2/NetworkProcess/Downloads/DownloadID.h new file mode 100644 index 000000000..37e68c3e9 --- /dev/null +++ b/Source/WebKit2/NetworkProcess/Downloads/DownloadID.h @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2015 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. + */ + +#ifndef DownloadID_h +#define DownloadID_h + +#include "ArgumentCoder.h" +#include "ArgumentDecoder.h" +#include "ArgumentEncoder.h" +#include <wtf/HashTraits.h> + +namespace WebKit { + +class DownloadID { +public: + DownloadID() + { + } + + explicit DownloadID(uint64_t downloadID) + : m_downloadID(downloadID) + { + } + + bool operator==(DownloadID other) const { return m_downloadID == other.m_downloadID; } + bool operator!=(DownloadID other) const { return m_downloadID != other.m_downloadID; } + + uint64_t downloadID() const { return m_downloadID; } +private: + uint64_t m_downloadID { 0 }; +}; + +} + +namespace IPC { + +template<> struct ArgumentCoder<WebKit::DownloadID> { + static void encode(ArgumentEncoder& encoder, const WebKit::DownloadID& downloadID) + { + encoder << downloadID.downloadID(); + } + static bool decode(ArgumentDecoder& decoder, WebKit::DownloadID& downloadID) + { + uint64_t id; + if (!decoder.decode(id)) + return false; + + downloadID = WebKit::DownloadID(id); + + return true; + } +}; + +} + +namespace WTF { + +struct DownloadIDHash { + static unsigned hash(const WebKit::DownloadID& d) { return intHash(d.downloadID()); } + static bool equal(const WebKit::DownloadID& a, const WebKit::DownloadID& b) { return a.downloadID() == b.downloadID(); } + static const bool safeToCompareToEmptyOrDeleted = true; +}; +template<> struct HashTraits<WebKit::DownloadID> : GenericHashTraits<WebKit::DownloadID> { + static WebKit::DownloadID emptyValue() { return { }; } + + static void constructDeletedValue(WebKit::DownloadID& slot) { slot = WebKit::DownloadID(std::numeric_limits<uint64_t>::max()); } + static bool isDeletedValue(const WebKit::DownloadID& slot) { return slot.downloadID() == std::numeric_limits<uint64_t>::max(); } +}; +template<> struct DefaultHash<WebKit::DownloadID> { + typedef DownloadIDHash Hash; +}; + +} +#endif /* DownloadID_h */ diff --git a/Source/WebKit2/NetworkProcess/Downloads/DownloadManager.cpp b/Source/WebKit2/NetworkProcess/Downloads/DownloadManager.cpp new file mode 100644 index 000000000..f05aa645c --- /dev/null +++ b/Source/WebKit2/NetworkProcess/Downloads/DownloadManager.cpp @@ -0,0 +1,161 @@ +/* + * Copyright (C) 2010 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 "DownloadManager.h" + +#include "Download.h" +#include "NetworkLoad.h" +#include "NetworkSession.h" +#include "PendingDownload.h" +#include "SessionTracker.h" +#include <WebCore/NotImplemented.h> +#include <WebCore/SessionID.h> +#include <wtf/StdLibExtras.h> + +using namespace WebCore; + +namespace WebKit { + +DownloadManager::DownloadManager(Client& client) + : m_client(client) +{ +} + +void DownloadManager::startDownload(SessionID sessionID, DownloadID downloadID, const ResourceRequest& request) +{ +#if USE(NETWORK_SESSION) + auto* networkSession = SessionTracker::networkSession(sessionID); + if (!networkSession) + return; + NetworkLoadParameters parameters; + parameters.sessionID = sessionID; + parameters.request = request; + parameters.clientCredentialPolicy = AskClientForAllCredentials; + m_pendingDownloads.add(downloadID, std::make_unique<PendingDownload>(parameters, downloadID)); +#else + auto download = std::make_unique<Download>(*this, downloadID, request); + download->start(); + + ASSERT(!m_downloads.contains(downloadID)); + m_downloads.add(downloadID, WTFMove(download)); +#endif +} + +#if USE(NETWORK_SESSION) +std::unique_ptr<PendingDownload> DownloadManager::dataTaskBecameDownloadTask(DownloadID downloadID, std::unique_ptr<Download>&& download) +{ + // This is needed for downloads started with startDownload, otherwise it will return nullptr. + auto pendingDownload = m_pendingDownloads.take(downloadID); + + m_downloads.add(downloadID, WTFMove(download)); + return pendingDownload; +} + +void DownloadManager::continueCanAuthenticateAgainstProtectionSpace(DownloadID downloadID, bool canAuthenticate) +{ + auto* pendingDownload = m_pendingDownloads.get(downloadID); + ASSERT(pendingDownload); + if (pendingDownload) + pendingDownload->continueCanAuthenticateAgainstProtectionSpace(canAuthenticate); +} + +void DownloadManager::continueWillSendRequest(DownloadID downloadID, const WebCore::ResourceRequest& request) +{ + auto* pendingDownload = m_pendingDownloads.get(downloadID); + ASSERT(pendingDownload); + if (pendingDownload) + pendingDownload->continueWillSendRequest(request); +} +#else +void DownloadManager::convertHandleToDownload(DownloadID downloadID, ResourceHandle* handle, const ResourceRequest& request, const ResourceResponse& response) +{ + auto download = std::make_unique<Download>(*this, downloadID, request); + + download->startWithHandle(handle, response); + ASSERT(!m_downloads.contains(downloadID)); + m_downloads.add(downloadID, WTFMove(download)); +} +#endif + +void DownloadManager::resumeDownload(SessionID, DownloadID downloadID, const IPC::DataReference& resumeData, const String& path, const SandboxExtension::Handle& sandboxExtensionHandle) +{ +#if USE(NETWORK_SESSION) + notImplemented(); +#else + // Download::resume() is responsible for setting the Download's resource request. + auto download = std::make_unique<Download>(*this, downloadID, ResourceRequest()); + + download->resume(resumeData, path, sandboxExtensionHandle); + ASSERT(!m_downloads.contains(downloadID)); + m_downloads.add(downloadID, WTFMove(download)); +#endif +} + +void DownloadManager::cancelDownload(DownloadID downloadID) +{ + Download* download = m_downloads.get(downloadID); + if (!download) + return; + + download->cancel(); +} + +void DownloadManager::downloadFinished(Download* download) +{ + ASSERT(m_downloads.contains(download->downloadID())); + m_downloads.remove(download->downloadID()); +} + +void DownloadManager::didCreateDownload() +{ + m_client.didCreateDownload(); +} + +void DownloadManager::didDestroyDownload() +{ + m_client.didDestroyDownload(); +} + +IPC::Connection* DownloadManager::downloadProxyConnection() +{ + return m_client.downloadProxyConnection(); +} + +AuthenticationManager& DownloadManager::downloadsAuthenticationManager() +{ + return m_client.downloadsAuthenticationManager(); +} + +#if PLATFORM(QT) +void DownloadManager::startTransfer(DownloadID downloadID, const String& destination) +{ + ASSERT(m_downloads.contains(downloadID)); + Download* download = m_downloads.get(downloadID); + download->startTransfer(destination); +} +#endif + +} // namespace WebKit diff --git a/Source/WebKit2/NetworkProcess/Downloads/DownloadManager.h b/Source/WebKit2/NetworkProcess/Downloads/DownloadManager.h new file mode 100644 index 000000000..53c8d3bb9 --- /dev/null +++ b/Source/WebKit2/NetworkProcess/Downloads/DownloadManager.h @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2010 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. + */ + +#ifndef DownloadManager_h +#define DownloadManager_h + +#include "DownloadID.h" +#include "PendingDownload.h" +#include "SandboxExtension.h" +#include <WebCore/NotImplemented.h> +#include <wtf/Forward.h> +#include <wtf/HashMap.h> +#include <wtf/Noncopyable.h> + +namespace WebCore { +#if !USE(NETWORK_SESSION) +class ResourceHandle; +#endif +class ResourceRequest; +class ResourceResponse; +class SessionID; +} + +namespace IPC { +class Connection; +class DataReference; +} + +namespace WebKit { + +class AuthenticationManager; +class Download; +class PendingDownload; + +class DownloadManager { + WTF_MAKE_NONCOPYABLE(DownloadManager); + +public: + class Client { + public: + virtual ~Client() { } + + virtual void didCreateDownload() = 0; + virtual void didDestroyDownload() = 0; + virtual IPC::Connection* downloadProxyConnection() = 0; + virtual AuthenticationManager& downloadsAuthenticationManager() = 0; + }; + + explicit DownloadManager(Client&); + + void startDownload(WebCore::SessionID, DownloadID, const WebCore::ResourceRequest&); +#if USE(NETWORK_SESSION) + std::unique_ptr<PendingDownload> dataTaskBecameDownloadTask(DownloadID, std::unique_ptr<Download>&&); + void continueCanAuthenticateAgainstProtectionSpace(DownloadID, bool canAuthenticate); + void continueWillSendRequest(DownloadID, const WebCore::ResourceRequest&); +#else + void convertHandleToDownload(DownloadID, WebCore::ResourceHandle*, const WebCore::ResourceRequest&, const WebCore::ResourceResponse&); +#endif + + void resumeDownload(WebCore::SessionID, DownloadID, const IPC::DataReference& resumeData, const String& path, const SandboxExtension::Handle&); + + void cancelDownload(DownloadID); + + Download* download(DownloadID downloadID) { return m_downloads.get(downloadID); } + + void downloadFinished(Download*); + bool isDownloading() const { return !m_downloads.isEmpty(); } + uint64_t activeDownloadCount() const { return m_downloads.size(); } + + void didCreateDownload(); + void didDestroyDownload(); + + IPC::Connection* downloadProxyConnection(); + AuthenticationManager& downloadsAuthenticationManager(); + +#if PLATFORM(QT) + void startTransfer(DownloadID, const String& destination); +#endif + +private: + Client& m_client; +#if USE(NETWORK_SESSION) + HashMap<DownloadID, std::unique_ptr<PendingDownload>> m_pendingDownloads; +#endif + HashMap<DownloadID, std::unique_ptr<Download>> m_downloads; +}; + +} // namespace WebKit + +#endif // DownloadManager_h diff --git a/Source/WebKit2/NetworkProcess/Downloads/PendingDownload.cpp b/Source/WebKit2/NetworkProcess/Downloads/PendingDownload.cpp new file mode 100644 index 000000000..62e3799d2 --- /dev/null +++ b/Source/WebKit2/NetworkProcess/Downloads/PendingDownload.cpp @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2016 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 "PendingDownload.h" + +#if USE(NETWORK_SESSION) + +#include "DownloadProxyMessages.h" +#include "NetworkLoad.h" +#include "NetworkProcess.h" +#include "WebCoreArgumentCoders.h" + +using namespace WebCore; + +namespace WebKit { + +PendingDownload::PendingDownload(const NetworkLoadParameters& parameters, DownloadID downloadID) + : m_networkLoad(std::make_unique<NetworkLoad>(*this, parameters)) +{ + m_networkLoad->setPendingDownloadID(downloadID); + m_networkLoad->setPendingDownload(*this); +} + +void PendingDownload::willSendRedirectedRequest(const WebCore::ResourceRequest&, const WebCore::ResourceRequest& redirectRequest, const WebCore::ResourceResponse& redirectResponse) +{ + send(Messages::DownloadProxy::WillSendRequest(redirectRequest, redirectResponse)); +}; + +void PendingDownload::continueWillSendRequest(const WebCore::ResourceRequest& newRequest) +{ + m_networkLoad->continueWillSendRequest(newRequest); +} + +void PendingDownload::canAuthenticateAgainstProtectionSpaceAsync(const WebCore::ProtectionSpace& protectionSpace) +{ + send(Messages::DownloadProxy::CanAuthenticateAgainstProtectionSpace(protectionSpace)); +} + +void PendingDownload::continueCanAuthenticateAgainstProtectionSpace(bool canAuthenticate) +{ + m_networkLoad->continueCanAuthenticateAgainstProtectionSpace(canAuthenticate); +} + +void PendingDownload::didConvertToDownload() +{ + m_networkLoad = nullptr; +} + +IPC::Connection* PendingDownload::messageSenderConnection() +{ + return NetworkProcess::singleton().parentProcessConnection(); +} + +uint64_t PendingDownload::messageSenderDestinationID() +{ + return m_networkLoad->pendingDownloadID().downloadID(); +} + +} + +#endif diff --git a/Source/WebKit2/NetworkProcess/Downloads/PendingDownload.h b/Source/WebKit2/NetworkProcess/Downloads/PendingDownload.h new file mode 100644 index 000000000..72a744cb2 --- /dev/null +++ b/Source/WebKit2/NetworkProcess/Downloads/PendingDownload.h @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2016 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. + */ + +#ifndef PendingDownload_h +#define PendingDownload_h + +#if USE(NETWORK_SESSION) + +#include "MessageSender.h" +#include "NetworkLoadClient.h" + +namespace WebCore { +class ResourceResponse; +} + +namespace WebKit { + +class DownloadID; +class NetworkLoad; +class NetworkLoadParameters; + +class PendingDownload : public NetworkLoadClient, public IPC::MessageSender { +public: + PendingDownload(const NetworkLoadParameters&, DownloadID); + + void continueWillSendRequest(const WebCore::ResourceRequest&); + void continueCanAuthenticateAgainstProtectionSpace(bool canAuthenticate); + +private: + // NetworkLoadClient. + virtual void didSendData(unsigned long long bytesSent, unsigned long long totalBytesToBeSent) override { } + virtual void canAuthenticateAgainstProtectionSpaceAsync(const WebCore::ProtectionSpace&) override; + virtual bool isSynchronous() const override { return false; } + virtual void willSendRedirectedRequest(const WebCore::ResourceRequest&, const WebCore::ResourceRequest& redirectRequest, const WebCore::ResourceResponse& redirectResponse) override; + virtual ShouldContinueDidReceiveResponse didReceiveResponse(const WebCore::ResourceResponse&) override { return ShouldContinueDidReceiveResponse::No; }; + virtual void didReceiveBuffer(RefPtr<WebCore::SharedBuffer>&&, int reportedEncodedDataLength) override { }; + virtual void didFinishLoading(double finishTime) override { }; + virtual void didFailLoading(const WebCore::ResourceError&) override { }; + virtual void didConvertToDownload() override; +#if PLATFORM(COCOA) + virtual void willCacheResponseAsync(CFCachedURLResponseRef) override { } +#endif + + // MessageSender. + virtual IPC::Connection* messageSenderConnection() override; + virtual uint64_t messageSenderDestinationID() override; + +private: + std::unique_ptr<NetworkLoad> m_networkLoad; +}; + +} + +#endif + +#endif diff --git a/Source/WebKit2/NetworkProcess/Downloads/qt/DownloadQt.cpp b/Source/WebKit2/NetworkProcess/Downloads/qt/DownloadQt.cpp new file mode 100644 index 000000000..3db5ecdfb --- /dev/null +++ b/Source/WebKit2/NetworkProcess/Downloads/qt/DownloadQt.cpp @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2010, 2011 Apple Inc. All rights reserved. + * Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies) + * + * 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 "Download.h" + +#include "QtFileDownloader.h" +#include <WebCore/NotImplemented.h> +#include <WebCore/QNetworkReplyHandler.h> +#include <WebCore/ResourceHandle.h> +#include <WebCore/ResourceHandleInternal.h> +#include <WebCore/ResourceResponse.h> + +using namespace WebCore; + +namespace WebKit { + +void Download::start() +{ + ASSERT(!m_qtDownloader); + m_qtDownloader = new QtFileDownloader(*this, m_request.toNetworkRequest()); +} + +void Download::startWithHandle(ResourceHandle* handle, const ResourceResponse& resp) +{ + ASSERT(!m_qtDownloader); + m_qtDownloader = new QtFileDownloader(*this, handle->getInternal()->m_job->release()); +} + +void Download::resume(const IPC::DataReference&, const WTF::String&, const SandboxExtension::Handle&) +{ + notImplemented(); +} + +void Download::cancel() +{ + ASSERT(m_qtDownloader); + m_qtDownloader->cancel(); +} + +void Download::platformInvalidate() +{ + ASSERT(m_qtDownloader); + m_qtDownloader->deleteLater(); + m_qtDownloader = nullptr; +} + +void Download::startTransfer(const String& destination) +{ + m_qtDownloader->startTransfer(destination); +} + +void Download::platformDidFinish() +{ + notImplemented(); +} + +} // namespace WebKit diff --git a/Source/WebKit2/NetworkProcess/Downloads/qt/QtFileDownloader.cpp b/Source/WebKit2/NetworkProcess/Downloads/qt/QtFileDownloader.cpp new file mode 100644 index 000000000..2eaed702d --- /dev/null +++ b/Source/WebKit2/NetworkProcess/Downloads/qt/QtFileDownloader.cpp @@ -0,0 +1,231 @@ +/* + * Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this program; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ +#include "config.h" +#include "QtFileDownloader.h" + +#include "DataReference.h" +#include "Download.h" +#include "HTTPParsers.h" +#include "MIMETypeRegistry.h" +#include "NetworkProcess.h" +#include <QCoreApplication> +#include <QFile> +#include <QFileInfo> +#include <QNetworkAccessManager> +#include <WebCore/QNetworkReplyHandler.h> +#include <WebCore/ResourceError.h> +#include <WebCore/ResourceResponse.h> + +using namespace WebCore; +using namespace WTF; + +namespace WebKit { + +QtFileDownloader::QtFileDownloader(Download& download, const QNetworkRequest& request) + : m_download(download) + , m_reply(NetworkProcess::singleton().networkAccessManager().get(request)) + , m_error(QNetworkReply::NoError) +{ + makeConnections(); +} + +QtFileDownloader::QtFileDownloader(Download& download, QNetworkReply* reply) + : m_download(download) + , m_reply(reply) + , m_error(reply->error()) +{ + makeConnections(); + + if (reply->isFinished()) + onFinished(); + else if (reply->isReadable()) + onReadyRead(); +} + +QtFileDownloader::~QtFileDownloader() +{ + if (!m_destinationFile) + return; + + abortDownloadWritingAndEmitError(QtFileDownloader::DownloadErrorAborted); +} + +void QtFileDownloader::makeConnections() +{ + connect(m_reply.get(), SIGNAL(readyRead()), SLOT(onReadyRead())); + connect(m_reply.get(), SIGNAL(finished()), SLOT(onFinished())); + connect(m_reply.get(), SIGNAL(error(QNetworkReply::NetworkError)), SLOT(onError(QNetworkReply::NetworkError))); +} + +void QtFileDownloader::startTransfer(const QString& decidedFilePath) +{ + ASSERT(!m_destinationFile); + + // Error might have occured during destination query. + if (m_error != QNetworkReply::NoError) { + abortDownloadWritingAndEmitError(QtFileDownloader::DownloadErrorNetworkFailure); + return; + } + + if (decidedFilePath.isEmpty()) { + abortDownloadWritingAndEmitError(QtFileDownloader::DownloadErrorCancelled); + return; + } + + auto downloadFile = std::make_unique<QFile>(decidedFilePath); + + if (!downloadFile->open(QIODevice::WriteOnly | QIODevice::Truncate)) { + abortDownloadWritingAndEmitError(QtFileDownloader::DownloadErrorCannotOpenFile); + return; + } + + // Assigning to m_destinationFile flags that either error or + // finished shall be called in the end. + m_destinationFile = WTFMove(downloadFile); + + m_download.didCreateDestination(m_destinationFile->fileName()); + + // We might have gotten readyRead already even before this function + // was called. + if (m_reply->bytesAvailable()) + onReadyRead(); + + // We might have gotten finished already even before this + // function was called. + if (m_reply->isFinished()) + onFinished(); +} + +void QtFileDownloader::abortDownloadWritingAndEmitError(QtFileDownloader::DownloadError errorCode) +{ + m_reply->abort(); + + // On network failures it's QNetworkReplyHandler::errorForReply who will handle errors. + if (errorCode == QtFileDownloader::DownloadErrorNetworkFailure) { + m_download.didFail(QNetworkReplyHandler::errorForReply(m_reply.get()), IPC::DataReference(0, 0)); + return; + } + + QString translatedErrorMessage; + switch (errorCode) { + case QtFileDownloader::DownloadErrorAborted: + translatedErrorMessage = QCoreApplication::translate("QtFileDownloader", "Download aborted"); + break; + case QtFileDownloader::DownloadErrorCannotWriteToFile: + translatedErrorMessage = QCoreApplication::translate("QtFileDownloader", "Cannot write to file"); + break; + case QtFileDownloader::DownloadErrorCannotOpenFile: + translatedErrorMessage = QCoreApplication::translate("QtFileDownloader", "Cannot open file for writing"); + break; + case QtFileDownloader::DownloadErrorDestinationAlreadyExists: + translatedErrorMessage = QCoreApplication::translate("QtFileDownloader", "Destination already exists"); + break; + case QtFileDownloader::DownloadErrorCancelled: + translatedErrorMessage = QCoreApplication::translate("QtFileDownloader", "Download cancelled by caller"); + break; + case QtFileDownloader::DownloadErrorCannotDetermineFilename: + translatedErrorMessage = QCoreApplication::translate("QtFileDownloader", "Cannot determine filename"); + break; + default: + ASSERT_NOT_REACHED(); + } + + ResourceError downloadError("Download", errorCode, m_reply->url(), translatedErrorMessage); + + m_download.didFail(downloadError, IPC::DataReference(0, 0)); +} + +void QtFileDownloader::handleDownloadResponse() +{ + // By API contract, QNetworkReply::metaDataChanged cannot really be trusted. + // Thus we need to call this upon receiving first data. + String contentType = m_reply->header(QNetworkRequest::ContentTypeHeader).toString(); + String encoding = extractCharsetFromMediaType(contentType); + String mimeType = extractMIMETypeFromMediaType(contentType); + + // Let's try to guess from the extension. + if (mimeType.isEmpty()) + mimeType = MIMETypeRegistry::getMIMETypeForPath(m_reply->url().path()); + + ResourceResponse response(m_reply->url(), mimeType, m_reply->header(QNetworkRequest::ContentLengthHeader).toLongLong(), encoding); + m_download.didReceiveResponse(response); +} + +void QtFileDownloader::onReadyRead() +{ + if (m_destinationFile) { + QByteArray content = m_reply->readAll(); + if (content.size() <= 0) + return; + + qint64 bytesWritten = m_destinationFile->write(content); + + if (bytesWritten == -1) { + abortDownloadWritingAndEmitError(QtFileDownloader::DownloadErrorCannotWriteToFile); + return; + } + + // There might a corner case to be fixed here if bytesWritten != content.size() + // does not actually represent an error. + ASSERT(bytesWritten == content.size()); + + m_download.didReceiveData(bytesWritten); + } else if (!m_headersRead) { + handleDownloadResponse(); + m_headersRead = true; + } +} + +void QtFileDownloader::onFinished() +{ + // If it's finished and we haven't even read the headers, it means we never got to onReadyRead and that we are + // probably dealing with the download of a local file or of a small file that was started with a handle. + if (!m_headersRead) { + handleDownloadResponse(); + m_headersRead = true; + return; + } + + if (!m_destinationFile) + return; + + m_destinationFile = nullptr; + + if (m_error == QNetworkReply::NoError) + m_download.didFinish(); + else if (m_error == QNetworkReply::OperationCanceledError) + abortDownloadWritingAndEmitError(QtFileDownloader::DownloadErrorCancelled); + else + abortDownloadWritingAndEmitError(QtFileDownloader::DownloadErrorNetworkFailure); +} + +void QtFileDownloader::onError(QNetworkReply::NetworkError code) +{ + m_error = code; +} + +void QtFileDownloader::cancel() +{ + m_reply->abort(); + // QtFileDownloader::onFinished() will be called and will raise a DownloadErrorCancelled. +} + +} // namespace WebKit +#include "moc_QtFileDownloader.cpp" diff --git a/Source/WebKit2/NetworkProcess/Downloads/qt/QtFileDownloader.h b/Source/WebKit2/NetworkProcess/Downloads/qt/QtFileDownloader.h new file mode 100644 index 000000000..58f2d5f1f --- /dev/null +++ b/Source/WebKit2/NetworkProcess/Downloads/qt/QtFileDownloader.h @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this program; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef QtFileDownloader_h +#define QtFileDownloader_h + +#include <QNetworkReply> +#include <QNetworkRequest> + +QT_BEGIN_NAMESPACE +class QFile; +class QNetworkAccessManager; +class QNetworkRequest; +QT_END_NAMESPACE + +namespace WebCore { +class ResourceError; +} + +namespace WebKit { +class Download; + +class QtFileDownloader : public QObject { + Q_OBJECT +public: + QtFileDownloader(Download&, const QNetworkRequest&); + QtFileDownloader(Download&, QNetworkReply*); + virtual ~QtFileDownloader(); + + void cancel(); + void startTransfer(const QString& destination); + + enum DownloadError { + DownloadErrorAborted = 0, + DownloadErrorCannotWriteToFile, + DownloadErrorCannotOpenFile, + DownloadErrorDestinationAlreadyExists, + DownloadErrorCancelled, + DownloadErrorCannotDetermineFilename, + DownloadErrorNetworkFailure + }; + +private Q_SLOTS: + void onReadyRead(); + void onFinished(); + void onError(QNetworkReply::NetworkError); + +private: + void makeConnections(); + void abortDownloadWritingAndEmitError(QtFileDownloader::DownloadError); + void handleDownloadResponse(); + + Download& m_download; + std::unique_ptr<QNetworkReply> m_reply; + std::unique_ptr<QFile> m_destinationFile; + QNetworkReply::NetworkError m_error; + bool m_headersRead { false }; +}; + +} // namespace WebKit + +#endif diff --git a/Source/WebKit2/NetworkProcess/EntryPoint/mac/LegacyProcess/Info.plist b/Source/WebKit2/NetworkProcess/EntryPoint/mac/LegacyProcess/Info.plist deleted file mode 100644 index 45905e5a6..000000000 --- a/Source/WebKit2/NetworkProcess/EntryPoint/mac/LegacyProcess/Info.plist +++ /dev/null @@ -1,34 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> -<plist version="1.0"> -<dict> - <key>CFBundleExecutable</key> - <string>${EXECUTABLE_NAME}</string> - <key>CFBundleGetInfoString</key> - <string>${BUNDLE_VERSION}, Copyright 2003-2013 Apple Inc.</string> - <key>CFBundleIdentifier</key> - <string>com.apple.WebKit.${PRODUCT_NAME}</string> - <key>CFBundleInfoDictionaryVersion</key> - <string>6.0</string> - <key>CFBundleName</key> - <string>${PRODUCT_NAME}</string> - <key>CFBundlePackageType</key> - <string>APPL</string> - <key>CFBundleShortVersionString</key> - <string>${SHORT_VERSION_STRING}</string> - <key>CFBundleVersion</key> - <string>${BUNDLE_VERSION}</string> - <key>LSFileQuarantineEnabled</key> - <true/> - <key>LSMinimumSystemVersion</key> - <string>${MACOSX_DEPLOYMENT_TARGET}</string> - <key>LSUIElement</key> - <true/> - <key>NSPrincipalClass</key> - <string>NSApplication</string> - <key>CFBundleIconFile</key> - <string>${APP_ICON}</string> - <key>WebKitEntryPoint</key> - <string>NetworkProcessMain</string> -</dict> -</plist> diff --git a/Source/WebKit2/NetworkProcess/EntryPoint/mac/XPCService/NetworkService.Development/Info.plist b/Source/WebKit2/NetworkProcess/EntryPoint/mac/XPCService/NetworkService.Development/Info.plist deleted file mode 100644 index c73c3d3bb..000000000 --- a/Source/WebKit2/NetworkProcess/EntryPoint/mac/XPCService/NetworkService.Development/Info.plist +++ /dev/null @@ -1,45 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> -<plist version="1.0"> -<dict> - <key>CFBundleGetInfoString</key> - <string>${BUNDLE_VERSION}, Copyright 2003-2013 Apple Inc.</string> - <key>CFBundleDevelopmentRegion</key> - <string>English</string> - <key>CFBundleExecutable</key> - <string>${EXECUTABLE_NAME}</string> - <key>CFBundleIdentifier</key> - <string>com.apple.WebKit.Networking.Development</string> - <key>CFBundleInfoDictionaryVersion</key> - <string>6.0</string> - <key>CFBundleName</key> - <string>${PRODUCT_NAME}</string> - <key>CFBundlePackageType</key> - <string>XPC!</string> - <key>CFBundleShortVersionString</key> - <string>${SHORT_VERSION_STRING}</string> - <key>CFBundleSignature</key> - <string>????</string> - <key>CFBundleVersion</key> - <string>${BUNDLE_VERSION}</string> - <key>LSFileQuarantineEnabled</key> - <true/> - <key>NSPrincipalClass</key> - <string>NSApplication</string> - <key>WebKitEntryPoint</key> - <string>NetworkServiceInitializer</string> - <key>LSUIElement</key> - <true/> - <key>XPCService</key> - <dict> - <key>ServiceType</key> - <string>Application</string> - <key>JoinExistingSession</key> - <true/> - <key>RunLoopType</key> - <string>NSRunLoop</string> - <key>_MultipleInstances</key> - <true/> - </dict> -</dict> -</plist> diff --git a/Source/WebKit2/NetworkProcess/EntryPoint/mac/XPCService/NetworkService/Info.plist b/Source/WebKit2/NetworkProcess/EntryPoint/mac/XPCService/NetworkService/Info.plist deleted file mode 100644 index ac5e4e522..000000000 --- a/Source/WebKit2/NetworkProcess/EntryPoint/mac/XPCService/NetworkService/Info.plist +++ /dev/null @@ -1,50 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> -<plist version="1.0"> -<dict> - <key>CFBundleGetInfoString</key> - <string>${BUNDLE_VERSION}, Copyright 2003-2013 Apple Inc.</string> - <key>CFBundleDevelopmentRegion</key> - <string>English</string> - <key>CFBundleExecutable</key> - <string>${EXECUTABLE_NAME}</string> - <key>CFBundleIdentifier</key> - <string>com.apple.WebKit.Networking</string> - <key>CFBundleInfoDictionaryVersion</key> - <string>6.0</string> - <key>CFBundleName</key> - <string>${PRODUCT_NAME}</string> - <key>CFBundlePackageType</key> - <string>XPC!</string> - <key>CFBundleShortVersionString</key> - <string>${SHORT_VERSION_STRING}</string> - <key>CFBundleSignature</key> - <string>????</string> - <key>CFBundleVersion</key> - <string>${BUNDLE_VERSION}</string> - <key>LSFileQuarantineEnabled</key> - <true/> - <key>NSPrincipalClass</key> - <string>NSApplication</string> - <key>WebKitEntryPoint</key> - <string>NetworkServiceInitializer</string> - <key>LSUIElement</key> - <true/> - <key>XPCService</key> - <dict> - <key>ServiceType</key> - <string>Application</string> - <key>JoinExistingSession</key> - <true/> - <key>RunLoopType</key> - <string>NSRunLoop</string> - <key>_MultipleInstances</key> - <true/> - <key>EnvironmentVariables</key> - <dict> - <key>DYLD_INSERT_LIBRARIES</key> - <string>$(WEBKIT2_FRAMEWORKS_DIR)/WebKit2.framework/NetworkProcess.app/Contents/MacOS/SecItemShim.dylib</string> - </dict> - </dict> -</dict> -</plist> diff --git a/Source/WebKit2/NetworkProcess/EntryPoint/qt/NetworkProcessMain.cpp b/Source/WebKit2/NetworkProcess/EntryPoint/qt/NetworkProcessMain.cpp new file mode 100644 index 000000000..b102fc566 --- /dev/null +++ b/Source/WebKit2/NetworkProcess/EntryPoint/qt/NetworkProcessMain.cpp @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2016 Konstantin Tokarev <annulen@yandex.ru> + * + * 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" + +namespace WebKit { +Q_DECL_IMPORT int NetworkProcessMainQt(int argc, char** argv); +} + +int main(int argc, char** argv) +{ + return WebKit::NetworkProcessMainQt(argc, argv); +} diff --git a/Source/WebKit2/NetworkProcess/EntryPoint/unix/NetworkProcessMain.cpp b/Source/WebKit2/NetworkProcess/EntryPoint/unix/NetworkProcessMain.cpp new file mode 100644 index 000000000..b282e16a3 --- /dev/null +++ b/Source/WebKit2/NetworkProcess/EntryPoint/unix/NetworkProcessMain.cpp @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2014 Igalia S.L. + * + * 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 "NetworkProcessMainUnix.h" + +#include <cstdlib> + +using namespace WebKit; + +int main(int argc, char** argv) +{ + // Disable SSLv3 very early because it is practically impossible to safely + // use setenv() when multiple threads are running, as another thread calling + // getenv() could cause a crash, and many functions use getenv() internally. + // This workaround will stop working if glib-networking switches away from + // GnuTLS or simply stops parsing this variable. We intentionally do not + // overwrite this priority string if it's already set by the user. + // https://bugzilla.gnome.org/show_bug.cgi?id=738633 + // WARNING: This needs to be KEPT IN SYNC with WebProcessMain.cpp. + setenv("G_TLS_GNUTLS_PRIORITY", "NORMAL:%COMPAT:%LATEST_RECORD_VERSION:!VERS-SSL3.0:!ARCFOUR-128", 0); + + return NetworkProcessMainUnix(argc, argv); +} diff --git a/Source/WebKit2/NetworkProcess/FileAPI/NetworkBlobRegistry.cpp b/Source/WebKit2/NetworkProcess/FileAPI/NetworkBlobRegistry.cpp index 1e60d401b..025e2fc65 100644 --- a/Source/WebKit2/NetworkProcess/FileAPI/NetworkBlobRegistry.cpp +++ b/Source/WebKit2/NetworkProcess/FileAPI/NetworkBlobRegistry.cpp @@ -26,20 +26,21 @@ #include "config.h" #include "NetworkBlobRegistry.h" -#if ENABLE(BLOB) && ENABLE(NETWORK_PROCESS) - +#include "BlobDataFileReferenceWithSandboxExtension.h" #include "SandboxExtension.h" +#include <WebCore/BlobPart.h> #include <WebCore/BlobRegistryImpl.h> -#include <wtf/MainThread.h> +#include <wtf/NeverDestroyed.h> +#include <wtf/RunLoop.h> using namespace WebCore; namespace WebKit { -NetworkBlobRegistry& NetworkBlobRegistry::shared() +NetworkBlobRegistry& NetworkBlobRegistry::singleton() { - ASSERT(isMainThread()); - DEFINE_STATIC_LOCAL(NetworkBlobRegistry, registry, ()); + ASSERT(RunLoop::isMain()); + static NeverDestroyed<NetworkBlobRegistry> registry; return registry; } @@ -47,50 +48,73 @@ NetworkBlobRegistry::NetworkBlobRegistry() { } -void NetworkBlobRegistry::registerBlobURL(NetworkConnectionToWebProcess* connection, const KURL& url, PassOwnPtr<BlobData> data, const Vector<RefPtr<SandboxExtension>>& newSandboxExtensions) +void NetworkBlobRegistry::registerFileBlobURL(NetworkConnectionToWebProcess* connection, const URL& url, const String& path, RefPtr<SandboxExtension>&& sandboxExtension, const String& contentType) { - ASSERT(!m_sandboxExtensions.contains(url.string())); - - // Combine new extensions for File items and existing extensions for inner Blob items. - Vector<RefPtr<SandboxExtension>> sandboxExtensions = newSandboxExtensions; - const BlobDataItemList& items = data->items(); - for (size_t i = 0, count = items.size(); i < count; ++i) { - if (items[i].type == BlobDataItem::Blob) - sandboxExtensions.appendVector(m_sandboxExtensions.get(items[i].url.string())); - } + blobRegistry().registerFileBlobURL(url, BlobDataFileReferenceWithSandboxExtension::create(path, sandboxExtension), contentType); - blobRegistry().registerBlobURL(url, data); + ASSERT(!m_blobsForConnection.get(connection).contains(url)); + BlobForConnectionMap::iterator mapIterator = m_blobsForConnection.find(connection); + if (mapIterator == m_blobsForConnection.end()) + mapIterator = m_blobsForConnection.add(connection, HashSet<URL>()).iterator; + mapIterator->value.add(url); +} - if (!sandboxExtensions.isEmpty()) - m_sandboxExtensions.add(url.string(), sandboxExtensions); +void NetworkBlobRegistry::registerBlobURL(NetworkConnectionToWebProcess* connection, const URL& url, Vector<WebCore::BlobPart> blobParts, const String& contentType) +{ + blobRegistry().registerBlobURL(url, WTFMove(blobParts), contentType); ASSERT(!m_blobsForConnection.get(connection).contains(url)); BlobForConnectionMap::iterator mapIterator = m_blobsForConnection.find(connection); if (mapIterator == m_blobsForConnection.end()) - mapIterator = m_blobsForConnection.add(connection, HashSet<KURL>()).iterator; + mapIterator = m_blobsForConnection.add(connection, HashSet<URL>()).iterator; mapIterator->value.add(url); } -void NetworkBlobRegistry::registerBlobURL(NetworkConnectionToWebProcess* connection, const WebCore::KURL& url, const WebCore::KURL& srcURL) +void NetworkBlobRegistry::registerBlobURL(NetworkConnectionToWebProcess* connection, const WebCore::URL& url, const WebCore::URL& srcURL) { + // The connection may not be registered if NetworkProcess prevously crashed for any reason. + BlobForConnectionMap::iterator mapIterator = m_blobsForConnection.find(connection); + if (mapIterator == m_blobsForConnection.end()) + return; + blobRegistry().registerBlobURL(url, srcURL); - SandboxExtensionMap::iterator iter = m_sandboxExtensions.find(srcURL.string()); - if (iter != m_sandboxExtensions.end()) - m_sandboxExtensions.add(url.string(), iter->value); - ASSERT(m_blobsForConnection.contains(connection)); - ASSERT(m_blobsForConnection.find(connection)->value.contains(srcURL)); - m_blobsForConnection.find(connection)->value.add(url); + ASSERT(mapIterator->value.contains(srcURL)); + mapIterator->value.add(url); +} + +void NetworkBlobRegistry::registerBlobURLForSlice(NetworkConnectionToWebProcess* connection, const WebCore::URL& url, const WebCore::URL& srcURL, int64_t start, int64_t end) +{ + // The connection may not be registered if NetworkProcess prevously crashed for any reason. + BlobForConnectionMap::iterator mapIterator = m_blobsForConnection.find(connection); + if (mapIterator == m_blobsForConnection.end()) + return; + + blobRegistry().registerBlobURLForSlice(url, srcURL, start, end); + + ASSERT(mapIterator->value.contains(srcURL)); + mapIterator->value.add(url); } -void NetworkBlobRegistry::unregisterBlobURL(NetworkConnectionToWebProcess* connection, const WebCore::KURL& url) +void NetworkBlobRegistry::unregisterBlobURL(NetworkConnectionToWebProcess* connection, const WebCore::URL& url) { + // The connection may not be registered if NetworkProcess prevously crashed for any reason. + BlobForConnectionMap::iterator mapIterator = m_blobsForConnection.find(connection); + if (mapIterator == m_blobsForConnection.end()) + return; + blobRegistry().unregisterBlobURL(url); - m_sandboxExtensions.remove(url.string()); - ASSERT(m_blobsForConnection.contains(connection)); - ASSERT(m_blobsForConnection.find(connection)->value.contains(url)); - m_blobsForConnection.find(connection)->value.remove(url); + ASSERT(mapIterator->value.contains(url)); + mapIterator->value.remove(url); +} + +uint64_t NetworkBlobRegistry::blobSize(NetworkConnectionToWebProcess* connection, const WebCore::URL& url) +{ + if (!m_blobsForConnection.contains(connection) || !m_blobsForConnection.find(connection)->value.contains(url)) + return 0; + + return blobRegistry().blobSize(url); } void NetworkBlobRegistry::connectionToWebProcessDidClose(NetworkConnectionToWebProcess* connection) @@ -98,20 +122,30 @@ void NetworkBlobRegistry::connectionToWebProcessDidClose(NetworkConnectionToWebP if (!m_blobsForConnection.contains(connection)) return; - HashSet<KURL>& blobsForConnection = m_blobsForConnection.find(connection)->value; - for (HashSet<KURL>::iterator iter = blobsForConnection.begin(), end = blobsForConnection.end(); iter != end; ++iter) { + HashSet<URL>& blobsForConnection = m_blobsForConnection.find(connection)->value; + for (HashSet<URL>::iterator iter = blobsForConnection.begin(), end = blobsForConnection.end(); iter != end; ++iter) blobRegistry().unregisterBlobURL(*iter); - m_sandboxExtensions.remove(*iter); - } m_blobsForConnection.remove(connection); } -const Vector<RefPtr<SandboxExtension>> NetworkBlobRegistry::sandboxExtensions(const WebCore::KURL& url) +Vector<RefPtr<BlobDataFileReference>> NetworkBlobRegistry::filesInBlob(NetworkConnectionToWebProcess& connection, const WebCore::URL& url) { - return m_sandboxExtensions.get(url.string()); -} + if (!m_blobsForConnection.contains(&connection) || !m_blobsForConnection.find(&connection)->value.contains(url)) + return Vector<RefPtr<BlobDataFileReference>>(); + + ASSERT(blobRegistry().isBlobRegistryImpl()); + BlobData* blobData = static_cast<BlobRegistryImpl&>(blobRegistry()).getBlobDataFromURL(url); + if (!blobData) + return Vector<RefPtr<BlobDataFileReference>>(); + + Vector<RefPtr<BlobDataFileReference>> result; + for (const BlobDataItem& item : blobData->items()) { + if (item.type() == BlobDataItem::Type::File) + result.append(item.file()); + } + return result; } -#endif +} diff --git a/Source/WebKit2/NetworkProcess/FileAPI/NetworkBlobRegistry.h b/Source/WebKit2/NetworkProcess/FileAPI/NetworkBlobRegistry.h index 6d71bbfc9..4adde415d 100644 --- a/Source/WebKit2/NetworkProcess/FileAPI/NetworkBlobRegistry.h +++ b/Source/WebKit2/NetworkProcess/FileAPI/NetworkBlobRegistry.h @@ -26,14 +26,13 @@ #ifndef NetworkBlobRegistry_h #define NetworkBlobRegistry_h -#if ENABLE(BLOB) && ENABLE(NETWORK_PROCESS) - -#include <WebCore/KURLHash.h> +#include <WebCore/URLHash.h> #include <wtf/HashMap.h> #include <wtf/HashSet.h> namespace WebCore { -class BlobData; +class BlobDataFileReference; +class BlobPart; } namespace WebKit { @@ -45,28 +44,26 @@ class NetworkBlobRegistry { WTF_MAKE_NONCOPYABLE(NetworkBlobRegistry); public: NetworkBlobRegistry(); - static NetworkBlobRegistry& shared(); + static NetworkBlobRegistry& singleton(); - void registerBlobURL(NetworkConnectionToWebProcess*, const WebCore::KURL&, PassOwnPtr<WebCore::BlobData>, const Vector<RefPtr<SandboxExtension>>&); - void registerBlobURL(NetworkConnectionToWebProcess*, const WebCore::KURL&, const WebCore::KURL& srcURL); - void unregisterBlobURL(NetworkConnectionToWebProcess*, const WebCore::KURL&); + void registerFileBlobURL(NetworkConnectionToWebProcess*, const WebCore::URL&, const String& path, RefPtr<SandboxExtension>&&, const String& contentType); + void registerBlobURL(NetworkConnectionToWebProcess*, const WebCore::URL&, Vector<WebCore::BlobPart>, const String& contentType); + void registerBlobURL(NetworkConnectionToWebProcess*, const WebCore::URL&, const WebCore::URL& srcURL); + void registerBlobURLForSlice(NetworkConnectionToWebProcess*, const WebCore::URL&, const WebCore::URL& srcURL, int64_t start, int64_t end); + void unregisterBlobURL(NetworkConnectionToWebProcess*, const WebCore::URL&); + uint64_t blobSize(NetworkConnectionToWebProcess*, const WebCore::URL&); void connectionToWebProcessDidClose(NetworkConnectionToWebProcess*); - const Vector<RefPtr<SandboxExtension>> sandboxExtensions(const WebCore::KURL&); + Vector<RefPtr<WebCore::BlobDataFileReference>> filesInBlob(NetworkConnectionToWebProcess&, const WebCore::URL&); private: ~NetworkBlobRegistry(); - typedef HashMap<String, Vector<RefPtr<SandboxExtension>>> SandboxExtensionMap; - SandboxExtensionMap m_sandboxExtensions; - - typedef HashMap<NetworkConnectionToWebProcess*, HashSet<WebCore::KURL>> BlobForConnectionMap; + typedef HashMap<NetworkConnectionToWebProcess*, HashSet<WebCore::URL>> BlobForConnectionMap; BlobForConnectionMap m_blobsForConnection; }; } -#endif // ENABLE(BLOB) && ENABLE(NETWORK_PROCESS) - #endif // NetworkBlobRegistry_h diff --git a/Source/WebKit2/NetworkProcess/HostRecord.cpp b/Source/WebKit2/NetworkProcess/HostRecord.cpp deleted file mode 100644 index e7ce34b0b..000000000 --- a/Source/WebKit2/NetworkProcess/HostRecord.cpp +++ /dev/null @@ -1,231 +0,0 @@ -/* - * Copyright (C) 2012 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 "HostRecord.h" - -#include "Logging.h" -#include "NetworkConnectionToWebProcess.h" -#include "NetworkProcess.h" -#include "NetworkResourceLoadParameters.h" -#include "NetworkResourceLoadScheduler.h" -#include "NetworkResourceLoader.h" -#include "SyncNetworkResourceLoader.h" -#include <wtf/MainThread.h> - -#if ENABLE(NETWORK_PROCESS) - -using namespace WebCore; - -namespace WebKit { - -HostRecord::HostRecord(const String& name, int maxRequestsInFlight) - : m_name(name) - , m_maxRequestsInFlight(maxRequestsInFlight) -{ -} - -HostRecord::~HostRecord() -{ -#ifndef NDEBUG - ASSERT(m_loadersInProgress.isEmpty()); - for (unsigned p = 0; p <= ResourceLoadPriorityHighest; p++) - ASSERT(m_loadersPending[p].isEmpty()); -#endif -} - -void HostRecord::scheduleResourceLoader(PassRefPtr<SchedulableLoader> loader) -{ - ASSERT(isMainThread()); - - loader->setHostRecord(this); - - if (loader->isSynchronous()) - m_syncLoadersPending.append(loader); - else - m_loadersPending[loader->priority()].append(loader); -} - -void HostRecord::addLoaderInProgress(SchedulableLoader* loader) -{ - ASSERT(isMainThread()); - - m_loadersInProgress.add(loader); - loader->setHostRecord(this); -} - -inline bool removeLoaderFromQueue(SchedulableLoader* loader, LoaderQueue& queue) -{ - LoaderQueue::iterator end = queue.end(); - for (LoaderQueue::iterator it = queue.begin(); it != end; ++it) { - if (it->get() == loader) { - loader->setHostRecord(0); - queue.remove(it); - return true; - } - } - return false; -} - -void HostRecord::removeLoader(SchedulableLoader* loader) -{ - ASSERT(isMainThread()); - - // FIXME (NetworkProcess): Due to IPC race conditions, it's possible this HostRecord will be asked to remove the same loader twice. - // It would be nice to know the loader has already been removed and treat it as a no-op. - - SchedulableLoaderSet::iterator i = m_loadersInProgress.find(loader); - if (i != m_loadersInProgress.end()) { - i->get()->setHostRecord(0); - m_loadersInProgress.remove(i); - return; - } - - if (removeLoaderFromQueue(loader, m_syncLoadersPending)) - return; - - for (int priority = ResourceLoadPriorityHighest; priority >= ResourceLoadPriorityLowest; --priority) { - if (removeLoaderFromQueue(loader, m_loadersPending[priority])) - return; - } -} - -bool HostRecord::hasRequests() const -{ - if (!m_loadersInProgress.isEmpty()) - return true; - - for (unsigned p = 0; p <= ResourceLoadPriorityHighest; p++) { - if (!m_loadersPending[p].isEmpty()) - return true; - } - - return false; -} - -uint64_t HostRecord::pendingRequestCount() const -{ - uint64_t count = 0; - - for (unsigned p = 0; p <= ResourceLoadPriorityHighest; p++) - count += m_loadersPending[p].size(); - - return count; -} - -uint64_t HostRecord::activeLoadCount() const -{ - return m_loadersInProgress.size(); -} - -void HostRecord::servePendingRequestsForQueue(LoaderQueue& queue, ResourceLoadPriority priority) -{ - // We only enforce the connection limit for http(s) hosts, which are the only ones with names. - bool shouldLimitRequests = !name().isNull(); - - // For non-named hosts - everything but http(s) - we should only enforce the limit if the document - // isn't done parsing and we don't know all stylesheets yet. - - // FIXME (NetworkProcess): The above comment about document parsing and stylesheets is a holdover - // from the WebCore::ResourceLoadScheduler. - // The behavior described was at one time important for WebCore's single threadedness. - // It's possible that we don't care about it with the NetworkProcess. - // We should either decide it's not important and change the above comment, or decide it is - // still important and somehow account for it. - - // Very low priority loaders are only handled when no other loaders are in progress. - if (shouldLimitRequests && priority == ResourceLoadPriorityVeryLow && !m_loadersInProgress.isEmpty()) - return; - - while (!queue.isEmpty()) { - RefPtr<SchedulableLoader> loader = queue.first(); - ASSERT(loader->hostRecord() == this); - - // This request might be from WebProcess we've lost our connection to. - // If so we should just skip it. - if (!loader->connectionToWebProcess()) { - removeLoader(loader.get()); - continue; - } - - if (shouldLimitRequests && limitsRequests(priority, loader.get())) - return; - - m_loadersInProgress.add(loader); - queue.removeFirst(); - - LOG(NetworkScheduling, "(NetworkProcess) HostRecord::servePendingRequestsForQueue - Starting load of %s\n", loader->request().url().string().utf8().data()); - loader->start(); - } -} - -void HostRecord::servePendingRequests(ResourceLoadPriority minimumPriority) -{ - LOG(NetworkScheduling, "(NetworkProcess) HostRecord::servePendingRequests Host name='%s'", name().utf8().data()); - - // We serve synchronous requests before any other requests to improve responsiveness in any - // WebProcess that is waiting on a synchronous load. - servePendingRequestsForQueue(m_syncLoadersPending, ResourceLoadPriorityHighest); - - for (int priority = ResourceLoadPriorityHighest; priority >= minimumPriority; --priority) - servePendingRequestsForQueue(m_loadersPending[priority], (ResourceLoadPriority)priority); -} - -bool HostRecord::limitsRequests(ResourceLoadPriority priority, SchedulableLoader* loader) const -{ - ASSERT(loader); - ASSERT(loader->connectionToWebProcess()); - - if (priority == ResourceLoadPriorityVeryLow && !m_loadersInProgress.isEmpty()) - return true; - - if (loader->connectionToWebProcess()->isSerialLoadingEnabled() && m_loadersInProgress.size() >= 1) - return true; - - // If we're exactly at the limit for requests in flight, and this loader is asynchronous, then we're done serving new requests. - // The synchronous loader exception handles the case where a sync XHR is made while 6 other requests are already in flight. - if (m_loadersInProgress.size() == m_maxRequestsInFlight && !loader->isSynchronous()) - return true; - - // If we're already past the limit of the number of loaders in flight, we won't even serve new synchronous requests right now. - if (m_loadersInProgress.size() > m_maxRequestsInFlight) { -#ifndef NDEBUG - // If we have more loaders in progress than we should, at least one of them had better be synchronous. - SchedulableLoaderSet::iterator i = m_loadersInProgress.begin(); - SchedulableLoaderSet::iterator end = m_loadersInProgress.end(); - for (; i != end; ++i) { - if (i->get()->isSynchronous()) - break; - } - ASSERT(i != end); -#endif - return true; - } - return false; -} - -} // namespace WebKit - -#endif // ENABLE(NETWORK_PROCESS) diff --git a/Source/WebKit2/NetworkProcess/HostRecord.h b/Source/WebKit2/NetworkProcess/HostRecord.h deleted file mode 100644 index 058208975..000000000 --- a/Source/WebKit2/NetworkProcess/HostRecord.h +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright (C) 2012 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. - */ - -#ifndef HostRecord_h -#define HostRecord_h - -#if ENABLE(NETWORK_PROCESS) - -#include <WebCore/ResourceLoadPriority.h> -#include <wtf/Deque.h> -#include <wtf/HashSet.h> -#include <wtf/RefCounted.h> -#include <wtf/text/WTFString.h> - -namespace WebKit { - -class NetworkResourceLoader; -class SchedulableLoader; -class SyncNetworkResourceLoader; - -typedef Deque<RefPtr<SchedulableLoader>> LoaderQueue; -typedef uint64_t ResourceLoadIdentifier; - -class HostRecord : public RefCounted<HostRecord> { -public: - static PassRefPtr<HostRecord> create(const String& name, int maxRequestsInFlight) - { - return adoptRef(new HostRecord(name, maxRequestsInFlight)); - } - - ~HostRecord(); - - const String& name() const { return m_name; } - - void scheduleResourceLoader(PassRefPtr<SchedulableLoader>); - void addLoaderInProgress(SchedulableLoader*); - void removeLoader(SchedulableLoader*); - bool hasRequests() const; - void servePendingRequests(WebCore::ResourceLoadPriority); - - uint64_t pendingRequestCount() const; - uint64_t activeLoadCount() const; - -private: - HostRecord(const String& name, int maxRequestsInFlight); - - void servePendingRequestsForQueue(LoaderQueue&, WebCore::ResourceLoadPriority); - bool limitsRequests(WebCore::ResourceLoadPriority, SchedulableLoader*) const; - - LoaderQueue m_loadersPending[WebCore::ResourceLoadPriorityHighest + 1]; - LoaderQueue m_syncLoadersPending; - - typedef HashSet<RefPtr<SchedulableLoader>> SchedulableLoaderSet; - SchedulableLoaderSet m_loadersInProgress; - - const String m_name; - int m_maxRequestsInFlight; -}; - -} // namespace WebKit - -#endif // ENABLE(NETWORK_PROCESS) - -#endif // #ifndef HostRecord_h diff --git a/Source/WebKit2/NetworkProcess/NetworkConnectionToWebProcess.cpp b/Source/WebKit2/NetworkProcess/NetworkConnectionToWebProcess.cpp index b21815418..812919890 100644 --- a/Source/WebKit2/NetworkProcess/NetworkConnectionToWebProcess.cpp +++ b/Source/WebKit2/NetworkProcess/NetworkConnectionToWebProcess.cpp @@ -26,46 +26,50 @@ #include "config.h" #include "NetworkConnectionToWebProcess.h" -#include "BlobRegistrationData.h" -#include "ConnectionStack.h" #include "NetworkBlobRegistry.h" #include "NetworkConnectionToWebProcessMessages.h" +#include "NetworkLoad.h" #include "NetworkProcess.h" #include "NetworkResourceLoadParameters.h" #include "NetworkResourceLoader.h" #include "NetworkResourceLoaderMessages.h" #include "RemoteNetworkingContext.h" -#include "SyncNetworkResourceLoader.h" -#include <WebCore/BlobData.h> +#include "SessionTracker.h" +#include <WebCore/NotImplemented.h> +#include <WebCore/PingHandle.h> #include <WebCore/PlatformCookieJar.h> #include <WebCore/ResourceLoaderOptions.h> #include <WebCore/ResourceRequest.h> -#include <WebCore/RunLoop.h> - -#if ENABLE(NETWORK_PROCESS) +#include <WebCore/SessionID.h> +#include <wtf/RunLoop.h> using namespace WebCore; namespace WebKit { -PassRefPtr<NetworkConnectionToWebProcess> NetworkConnectionToWebProcess::create(CoreIPC::Connection::Identifier connectionIdentifier) +Ref<NetworkConnectionToWebProcess> NetworkConnectionToWebProcess::create(IPC::Connection::Identifier connectionIdentifier) { - return adoptRef(new NetworkConnectionToWebProcess(connectionIdentifier)); + return adoptRef(*new NetworkConnectionToWebProcess(connectionIdentifier)); } -NetworkConnectionToWebProcess::NetworkConnectionToWebProcess(CoreIPC::Connection::Identifier connectionIdentifier) - : m_serialLoadingEnabled(false) +NetworkConnectionToWebProcess::NetworkConnectionToWebProcess(IPC::Connection::Identifier connectionIdentifier) { - m_connection = CoreIPC::Connection::createServerConnection(connectionIdentifier, this, RunLoop::main()); - m_connection->setOnlySendMessagesAsDispatchWhenWaitingForSyncReplyWhenProcessingSuchAMessage(true); + m_connection = IPC::Connection::createServerConnection(connectionIdentifier, *this); m_connection->open(); } NetworkConnectionToWebProcess::~NetworkConnectionToWebProcess() { } + +void NetworkConnectionToWebProcess::didCleanupResourceLoader(NetworkResourceLoader& loader) +{ + ASSERT(m_networkResourceLoaders.get(loader.identifier()) == &loader); + + m_networkResourceLoaders.remove(loader.identifier()); +} -void NetworkConnectionToWebProcess::didReceiveMessage(CoreIPC::Connection* connection, CoreIPC::MessageDecoder& decoder) +void NetworkConnectionToWebProcess::didReceiveMessage(IPC::Connection& connection, IPC::MessageDecoder& decoder) { if (decoder.messageReceiverName() == Messages::NetworkConnectionToWebProcess::messageReceiverName()) { didReceiveNetworkConnectionToWebProcessMessage(connection, decoder); @@ -82,7 +86,7 @@ void NetworkConnectionToWebProcess::didReceiveMessage(CoreIPC::Connection* conne ASSERT_NOT_REACHED(); } -void NetworkConnectionToWebProcess::didReceiveSyncMessage(CoreIPC::Connection* connection, CoreIPC::MessageDecoder& decoder, OwnPtr<CoreIPC::MessageEncoder>& reply) +void NetworkConnectionToWebProcess::didReceiveSyncMessage(IPC::Connection& connection, IPC::MessageDecoder& decoder, std::unique_ptr<IPC::MessageEncoder>& reply) { if (decoder.messageReceiverName() == Messages::NetworkConnectionToWebProcess::messageReceiverName()) { didReceiveSyncNetworkConnectionToWebProcessMessage(connection, decoder, reply); @@ -91,49 +95,50 @@ void NetworkConnectionToWebProcess::didReceiveSyncMessage(CoreIPC::Connection* c ASSERT_NOT_REACHED(); } -void NetworkConnectionToWebProcess::didClose(CoreIPC::Connection*) +void NetworkConnectionToWebProcess::didClose(IPC::Connection&) { // Protect ourself as we might be otherwise be deleted during this function. - RefPtr<NetworkConnectionToWebProcess> protector(this); - - HashMap<ResourceLoadIdentifier, RefPtr<NetworkResourceLoader>>::iterator end = m_networkResourceLoaders.end(); - for (HashMap<ResourceLoadIdentifier, RefPtr<NetworkResourceLoader>>::iterator i = m_networkResourceLoaders.begin(); i != end; ++i) - i->value->abort(); + Ref<NetworkConnectionToWebProcess> protector(*this); - HashMap<ResourceLoadIdentifier, RefPtr<SyncNetworkResourceLoader>>::iterator syncEnd = m_syncNetworkResourceLoaders.end(); - for (HashMap<ResourceLoadIdentifier, RefPtr<SyncNetworkResourceLoader>>::iterator i = m_syncNetworkResourceLoaders.begin(); i != syncEnd; ++i) - i->value->abort(); + Vector<RefPtr<NetworkResourceLoader>> loaders; + copyValuesToVector(m_networkResourceLoaders, loaders); + for (auto& loader : loaders) + loader->abort(); + ASSERT(m_networkResourceLoaders.isEmpty()); - NetworkBlobRegistry::shared().connectionToWebProcessDidClose(this); - - m_networkResourceLoaders.clear(); - - NetworkProcess::shared().removeNetworkConnectionToWebProcess(this); + NetworkBlobRegistry::singleton().connectionToWebProcessDidClose(this); + NetworkProcess::singleton().removeNetworkConnectionToWebProcess(this); } -void NetworkConnectionToWebProcess::didReceiveInvalidMessage(CoreIPC::Connection*, CoreIPC::StringReference, CoreIPC::StringReference) +void NetworkConnectionToWebProcess::didReceiveInvalidMessage(IPC::Connection&, IPC::StringReference, IPC::StringReference) { } void NetworkConnectionToWebProcess::scheduleResourceLoad(const NetworkResourceLoadParameters& loadParameters) { - RefPtr<NetworkResourceLoader> loader = NetworkResourceLoader::create(loadParameters, this); - m_networkResourceLoaders.add(loadParameters.identifier, loader); - NetworkProcess::shared().networkResourceLoadScheduler().scheduleLoader(loader.get()); + auto loader = NetworkResourceLoader::create(loadParameters, *this); + m_networkResourceLoaders.add(loadParameters.identifier, loader.ptr()); + loader->start(); } -void NetworkConnectionToWebProcess::performSynchronousLoad(const NetworkResourceLoadParameters& loadParameters, PassRefPtr<Messages::NetworkConnectionToWebProcess::PerformSynchronousLoad::DelayedReply> reply) +void NetworkConnectionToWebProcess::performSynchronousLoad(const NetworkResourceLoadParameters& loadParameters, RefPtr<Messages::NetworkConnectionToWebProcess::PerformSynchronousLoad::DelayedReply>&& reply) { - RefPtr<SyncNetworkResourceLoader> loader = SyncNetworkResourceLoader::create(loadParameters, this, reply); - m_syncNetworkResourceLoaders.add(loadParameters.identifier, loader); - NetworkProcess::shared().networkResourceLoadScheduler().scheduleLoader(loader.get()); + auto loader = NetworkResourceLoader::create(loadParameters, *this, WTFMove(reply)); + m_networkResourceLoaders.add(loadParameters.identifier, loader.ptr()); + loader->start(); +} + +void NetworkConnectionToWebProcess::loadPing(const NetworkResourceLoadParameters& loadParameters) +{ + RefPtr<NetworkingContext> context = RemoteNetworkingContext::create(loadParameters.sessionID, loadParameters.shouldClearReferrerOnHTTPSToHTTPRedirect); + + // PingHandle manages its own lifetime, deleting itself when its purpose has been fulfilled. + new PingHandle(context.get(), loadParameters.request, loadParameters.allowStoredCredentials == AllowStoredCredentials, PingHandle::UsesAsyncCallbacks::Yes); } void NetworkConnectionToWebProcess::removeLoadIdentifier(ResourceLoadIdentifier identifier) { - RefPtr<SchedulableLoader> loader = m_networkResourceLoaders.take(identifier); - if (!loader) - loader = m_syncNetworkResourceLoaders.take(identifier); + RefPtr<NetworkResourceLoader> loader = m_networkResourceLoaders.get(identifier); // It's possible we have no loader for this identifier if the NetworkProcess crashed and this was a respawned NetworkProcess. if (!loader) @@ -142,24 +147,29 @@ void NetworkConnectionToWebProcess::removeLoadIdentifier(ResourceLoadIdentifier // Abort the load now, as the WebProcess won't be able to respond to messages any more which might lead // to leaked loader resources (connections, threads, etc). loader->abort(); + ASSERT(!m_networkResourceLoaders.contains(identifier)); } -void NetworkConnectionToWebProcess::servePendingRequests(uint32_t resourceLoadPriority) +void NetworkConnectionToWebProcess::setDefersLoading(ResourceLoadIdentifier identifier, bool defers) { - NetworkProcess::shared().networkResourceLoadScheduler().servePendingRequests(static_cast<ResourceLoadPriority>(resourceLoadPriority)); + RefPtr<NetworkResourceLoader> loader = m_networkResourceLoaders.get(identifier); + if (!loader) + return; + + loader->setDefersLoading(defers); } -void NetworkConnectionToWebProcess::setSerialLoadingEnabled(bool enabled) +void NetworkConnectionToWebProcess::prefetchDNS(const String& hostname) { - m_serialLoadingEnabled = enabled; + NetworkProcess::singleton().prefetchDNS(hostname); } -static NetworkStorageSession& storageSession(bool privateBrowsingEnabled) +static NetworkStorageSession& storageSession(SessionID sessionID) { - if (privateBrowsingEnabled) { - NetworkStorageSession* privateSession = RemoteNetworkingContext::privateBrowsingSession(); - if (privateSession) - return *privateSession; + if (sessionID.isEphemeral()) { + NetworkStorageSession* privateStorageSession = SessionTracker::storageSession(sessionID); + if (privateStorageSession) + return *privateStorageSession; // Some requests with private browsing mode requested may still be coming shortly after NetworkProcess was told to destroy its session. // FIXME: Find a way to track private browsing sessions more rigorously. LOG_ERROR("Private browsing was requested, but there was no session for it. Please file a bug unless you just disabled private browsing, in which case it's an expected race."); @@ -167,79 +177,97 @@ static NetworkStorageSession& storageSession(bool privateBrowsingEnabled) return NetworkStorageSession::defaultStorageSession(); } -void NetworkConnectionToWebProcess::startDownload(bool privateBrowsingEnabled, uint64_t downloadID, const ResourceRequest& request) +void NetworkConnectionToWebProcess::startDownload(SessionID sessionID, DownloadID downloadID, const ResourceRequest& request) { - // FIXME: Do something with the private browsing flag. - NetworkProcess::shared().downloadManager().startDownload(downloadID, request); + NetworkProcess::singleton().downloadManager().startDownload(sessionID, downloadID, request); } -void NetworkConnectionToWebProcess::convertMainResourceLoadToDownload(uint64_t mainResourceLoadIdentifier, uint64_t downloadID, const ResourceRequest& request, const ResourceResponse& response) +void NetworkConnectionToWebProcess::convertMainResourceLoadToDownload(SessionID sessionID, uint64_t mainResourceLoadIdentifier, DownloadID downloadID, const ResourceRequest& request, const ResourceResponse& response) { + auto& networkProcess = NetworkProcess::singleton(); if (!mainResourceLoadIdentifier) { - NetworkProcess::shared().downloadManager().startDownload(downloadID, request); + networkProcess.downloadManager().startDownload(sessionID, downloadID, request); return; } NetworkResourceLoader* loader = m_networkResourceLoaders.get(mainResourceLoadIdentifier); - NetworkProcess::shared().downloadManager().convertHandleToDownload(downloadID, loader->handle(), request, response); + if (!loader) { + // If we're trying to download a blob here loader can be null. + return; + } + +#if USE(NETWORK_SESSION) + loader->networkLoad()->convertTaskToDownload(downloadID); +#else + networkProcess.downloadManager().convertHandleToDownload(downloadID, loader->networkLoad()->handle(), request, response); // Unblock the URL connection operation queue. - loader->handle()->continueDidReceiveResponse(); + loader->networkLoad()->handle()->continueDidReceiveResponse(); - loader->didConvertHandleToDownload(); + loader->didConvertToDownload(); +#endif } -void NetworkConnectionToWebProcess::cookiesForDOM(bool privateBrowsingEnabled, const KURL& firstParty, const KURL& url, String& result) +void NetworkConnectionToWebProcess::cookiesForDOM(SessionID sessionID, const URL& firstParty, const URL& url, String& result) { - result = WebCore::cookiesForDOM(storageSession(privateBrowsingEnabled), firstParty, url); + result = WebCore::cookiesForDOM(storageSession(sessionID), firstParty, url); } -void NetworkConnectionToWebProcess::setCookiesFromDOM(bool privateBrowsingEnabled, const KURL& firstParty, const KURL& url, const String& cookieString) +void NetworkConnectionToWebProcess::setCookiesFromDOM(SessionID sessionID, const URL& firstParty, const URL& url, const String& cookieString) { - WebCore::setCookiesFromDOM(storageSession(privateBrowsingEnabled), firstParty, url, cookieString); + WebCore::setCookiesFromDOM(storageSession(sessionID), firstParty, url, cookieString); } -void NetworkConnectionToWebProcess::cookiesEnabled(bool privateBrowsingEnabled, const KURL& firstParty, const KURL& url, bool& result) +void NetworkConnectionToWebProcess::cookiesEnabled(SessionID sessionID, const URL& firstParty, const URL& url, bool& result) { - result = WebCore::cookiesEnabled(storageSession(privateBrowsingEnabled), firstParty, url); + result = WebCore::cookiesEnabled(storageSession(sessionID), firstParty, url); } -void NetworkConnectionToWebProcess::cookieRequestHeaderFieldValue(bool privateBrowsingEnabled, const KURL& firstParty, const KURL& url, String& result) +void NetworkConnectionToWebProcess::cookieRequestHeaderFieldValue(SessionID sessionID, const URL& firstParty, const URL& url, String& result) { - result = WebCore::cookieRequestHeaderFieldValue(storageSession(privateBrowsingEnabled), firstParty, url); + result = WebCore::cookieRequestHeaderFieldValue(storageSession(sessionID), firstParty, url); } -void NetworkConnectionToWebProcess::getRawCookies(bool privateBrowsingEnabled, const KURL& firstParty, const KURL& url, Vector<Cookie>& result) +void NetworkConnectionToWebProcess::getRawCookies(SessionID sessionID, const URL& firstParty, const URL& url, Vector<Cookie>& result) { - WebCore::getRawCookies(storageSession(privateBrowsingEnabled), firstParty, url, result); + WebCore::getRawCookies(storageSession(sessionID), firstParty, url, result); } -void NetworkConnectionToWebProcess::deleteCookie(bool privateBrowsingEnabled, const KURL& url, const String& cookieName) +void NetworkConnectionToWebProcess::deleteCookie(SessionID sessionID, const URL& url, const String& cookieName) { - WebCore::deleteCookie(storageSession(privateBrowsingEnabled), url, cookieName); + WebCore::deleteCookie(storageSession(sessionID), url, cookieName); } -void NetworkConnectionToWebProcess::registerBlobURL(const KURL& url, const BlobRegistrationData& data) +void NetworkConnectionToWebProcess::registerFileBlobURL(const URL& url, const String& path, const SandboxExtension::Handle& extensionHandle, const String& contentType) { - Vector<RefPtr<SandboxExtension>> extensions; - for (size_t i = 0, count = data.sandboxExtensions().size(); i < count; ++i) { - if (RefPtr<SandboxExtension> extension = SandboxExtension::create(data.sandboxExtensions()[i])) - extensions.append(extension); - } + RefPtr<SandboxExtension> extension = SandboxExtension::create(extensionHandle); - NetworkBlobRegistry::shared().registerBlobURL(this, url, data.releaseData(), extensions); + NetworkBlobRegistry::singleton().registerFileBlobURL(this, url, path, WTFMove(extension), contentType); } -void NetworkConnectionToWebProcess::registerBlobURLFromURL(const KURL& url, const KURL& srcURL) +void NetworkConnectionToWebProcess::registerBlobURL(const URL& url, Vector<BlobPart> blobParts, const String& contentType) { - NetworkBlobRegistry::shared().registerBlobURL(this, url, srcURL); + NetworkBlobRegistry::singleton().registerBlobURL(this, url, WTFMove(blobParts), contentType); } -void NetworkConnectionToWebProcess::unregisterBlobURL(const KURL& url) +void NetworkConnectionToWebProcess::registerBlobURLFromURL(const URL& url, const URL& srcURL) { - NetworkBlobRegistry::shared().unregisterBlobURL(this, url); + NetworkBlobRegistry::singleton().registerBlobURL(this, url, srcURL); } -} // namespace WebKit +void NetworkConnectionToWebProcess::registerBlobURLForSlice(const URL& url, const URL& srcURL, int64_t start, int64_t end) +{ + NetworkBlobRegistry::singleton().registerBlobURLForSlice(this, url, srcURL, start, end); +} -#endif // ENABLE(NETWORK_PROCESS) +void NetworkConnectionToWebProcess::unregisterBlobURL(const URL& url) +{ + NetworkBlobRegistry::singleton().unregisterBlobURL(this, url); +} + +void NetworkConnectionToWebProcess::blobSize(const URL& url, uint64_t& resultSize) +{ + resultSize = NetworkBlobRegistry::singleton().blobSize(this, url); +} + +} // namespace WebKit diff --git a/Source/WebKit2/NetworkProcess/NetworkConnectionToWebProcess.h b/Source/WebKit2/NetworkProcess/NetworkConnectionToWebProcess.h index e63215812..c80f08a58 100644 --- a/Source/WebKit2/NetworkProcess/NetworkConnectionToWebProcess.h +++ b/Source/WebKit2/NetworkProcess/NetworkConnectionToWebProcess.h @@ -26,10 +26,9 @@ #ifndef NetworkConnectionToWebProcess_h #define NetworkConnectionToWebProcess_h -#if ENABLE(NETWORK_PROCESS) - #include "BlockingResponseMap.h" #include "Connection.h" +#include "DownloadID.h" #include "NetworkConnectionToWebProcessMessages.h" #include <WebCore/ResourceLoadPriority.h> #include <wtf/HashSet.h> @@ -41,65 +40,65 @@ class ResourceRequest; namespace WebKit { -class BlobRegistrationData; class NetworkConnectionToWebProcess; class NetworkResourceLoader; class SyncNetworkResourceLoader; typedef uint64_t ResourceLoadIdentifier; -class NetworkConnectionToWebProcess : public RefCounted<NetworkConnectionToWebProcess>, CoreIPC::Connection::Client { +class NetworkConnectionToWebProcess : public RefCounted<NetworkConnectionToWebProcess>, IPC::Connection::Client { public: - static PassRefPtr<NetworkConnectionToWebProcess> create(CoreIPC::Connection::Identifier); + static Ref<NetworkConnectionToWebProcess> create(IPC::Connection::Identifier); virtual ~NetworkConnectionToWebProcess(); - CoreIPC::Connection* connection() const { return m_connection.get(); } + IPC::Connection* connection() const { return m_connection.get(); } - bool isSerialLoadingEnabled() const { return m_serialLoadingEnabled; } + void didCleanupResourceLoader(NetworkResourceLoader&); private: - NetworkConnectionToWebProcess(CoreIPC::Connection::Identifier); + NetworkConnectionToWebProcess(IPC::Connection::Identifier); - // CoreIPC::Connection::Client - virtual void didReceiveMessage(CoreIPC::Connection*, CoreIPC::MessageDecoder&); - virtual void didReceiveSyncMessage(CoreIPC::Connection*, CoreIPC::MessageDecoder&, OwnPtr<CoreIPC::MessageEncoder>&); - virtual void didClose(CoreIPC::Connection*); - virtual void didReceiveInvalidMessage(CoreIPC::Connection*, CoreIPC::StringReference messageReceiverName, CoreIPC::StringReference messageName); + // IPC::Connection::Client + virtual void didReceiveMessage(IPC::Connection&, IPC::MessageDecoder&) override; + virtual void didReceiveSyncMessage(IPC::Connection&, IPC::MessageDecoder&, std::unique_ptr<IPC::MessageEncoder>&) override; + virtual void didClose(IPC::Connection&) override; + virtual void didReceiveInvalidMessage(IPC::Connection&, IPC::StringReference messageReceiverName, IPC::StringReference messageName) override; + virtual IPC::ProcessType localProcessType() override { return IPC::ProcessType::Network; } + virtual IPC::ProcessType remoteProcessType() override { return IPC::ProcessType::Web; } // Message handlers. - void didReceiveNetworkConnectionToWebProcessMessage(CoreIPC::Connection*, CoreIPC::MessageDecoder&); - void didReceiveSyncNetworkConnectionToWebProcessMessage(CoreIPC::Connection*, CoreIPC::MessageDecoder&, OwnPtr<CoreIPC::MessageEncoder>&); + void didReceiveNetworkConnectionToWebProcessMessage(IPC::Connection&, IPC::MessageDecoder&); + void didReceiveSyncNetworkConnectionToWebProcessMessage(IPC::Connection&, IPC::MessageDecoder&, std::unique_ptr<IPC::MessageEncoder>&); void scheduleResourceLoad(const NetworkResourceLoadParameters&); - void performSynchronousLoad(const NetworkResourceLoadParameters&, PassRefPtr<Messages::NetworkConnectionToWebProcess::PerformSynchronousLoad::DelayedReply>); + void performSynchronousLoad(const NetworkResourceLoadParameters&, RefPtr<Messages::NetworkConnectionToWebProcess::PerformSynchronousLoad::DelayedReply>&&); + void loadPing(const NetworkResourceLoadParameters&); + void prefetchDNS(const String&); void removeLoadIdentifier(ResourceLoadIdentifier); - void crossOriginRedirectReceived(ResourceLoadIdentifier, const WebCore::KURL& redirectURL); - void servePendingRequests(uint32_t resourceLoadPriority); - void setSerialLoadingEnabled(bool); - void startDownload(bool privateBrowsingEnabled, uint64_t downloadID, const WebCore::ResourceRequest&); - void convertMainResourceLoadToDownload(uint64_t mainResourceLoadIdentifier, uint64_t downloadID, const WebCore::ResourceRequest&, const WebCore::ResourceResponse&); - - void cookiesForDOM(bool privateBrowsingEnabled, const WebCore::KURL& firstParty, const WebCore::KURL&, String& result); - void setCookiesFromDOM(bool privateBrowsingEnabled, const WebCore::KURL& firstParty, const WebCore::KURL&, const String&); - void cookiesEnabled(bool privateBrowsingEnabled, const WebCore::KURL& firstParty, const WebCore::KURL&, bool& result); - void cookieRequestHeaderFieldValue(bool privateBrowsingEnabled, const WebCore::KURL& firstParty, const WebCore::KURL&, String& result); - void getRawCookies(bool privateBrowsingEnabled, const WebCore::KURL& firstParty, const WebCore::KURL&, Vector<WebCore::Cookie>&); - void deleteCookie(bool privateBrowsingEnabled, const WebCore::KURL&, const String& cookieName); - - void registerBlobURL(const WebCore::KURL&, const BlobRegistrationData&); - void registerBlobURLFromURL(const WebCore::KURL&, const WebCore::KURL& srcURL); - void unregisterBlobURL(const WebCore::KURL&); - - RefPtr<CoreIPC::Connection> m_connection; + void setDefersLoading(ResourceLoadIdentifier, bool); + void crossOriginRedirectReceived(ResourceLoadIdentifier, const WebCore::URL& redirectURL); + void startDownload(WebCore::SessionID, DownloadID, const WebCore::ResourceRequest&); + void convertMainResourceLoadToDownload(WebCore::SessionID, uint64_t mainResourceLoadIdentifier, DownloadID, const WebCore::ResourceRequest&, const WebCore::ResourceResponse&); + + void cookiesForDOM(WebCore::SessionID, const WebCore::URL& firstParty, const WebCore::URL&, String& result); + void setCookiesFromDOM(WebCore::SessionID, const WebCore::URL& firstParty, const WebCore::URL&, const String&); + void cookiesEnabled(WebCore::SessionID, const WebCore::URL& firstParty, const WebCore::URL&, bool& result); + void cookieRequestHeaderFieldValue(WebCore::SessionID, const WebCore::URL& firstParty, const WebCore::URL&, String& result); + void getRawCookies(WebCore::SessionID, const WebCore::URL& firstParty, const WebCore::URL&, Vector<WebCore::Cookie>&); + void deleteCookie(WebCore::SessionID, const WebCore::URL&, const String& cookieName); + + void registerFileBlobURL(const WebCore::URL&, const String& path, const SandboxExtension::Handle&, const String& contentType); + void registerBlobURL(const WebCore::URL&, Vector<WebCore::BlobPart>, const String& contentType); + void registerBlobURLFromURL(const WebCore::URL&, const WebCore::URL& srcURL); + void registerBlobURLForSlice(const WebCore::URL&, const WebCore::URL& srcURL, int64_t start, int64_t end); + void blobSize(const WebCore::URL&, uint64_t& resultSize); + void unregisterBlobURL(const WebCore::URL&); + + RefPtr<IPC::Connection> m_connection; HashMap<ResourceLoadIdentifier, RefPtr<NetworkResourceLoader>> m_networkResourceLoaders; - HashMap<ResourceLoadIdentifier, RefPtr<SyncNetworkResourceLoader>> m_syncNetworkResourceLoaders; - - bool m_serialLoadingEnabled; }; } // namespace WebKit -#endif // ENABLE(NETWORK_PROCESS) - #endif // NetworkConnectionToWebProcess_h diff --git a/Source/WebKit2/NetworkProcess/NetworkConnectionToWebProcess.messages.in b/Source/WebKit2/NetworkProcess/NetworkConnectionToWebProcess.messages.in index afae1a230..03fdd92fd 100644 --- a/Source/WebKit2/NetworkProcess/NetworkConnectionToWebProcess.messages.in +++ b/Source/WebKit2/NetworkProcess/NetworkConnectionToWebProcess.messages.in @@ -20,31 +20,29 @@ # 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. -#if ENABLE(NETWORK_PROCESS) - messages -> NetworkConnectionToWebProcess LegacyReceiver { ScheduleResourceLoad(WebKit::NetworkResourceLoadParameters resourceLoadParameters) - PerformSynchronousLoad(WebKit::NetworkResourceLoadParameters resourceLoadParameters) -> (WebCore::ResourceError error, WebCore::ResourceResponse response, CoreIPC::DataReference data) Delayed + PerformSynchronousLoad(WebKit::NetworkResourceLoadParameters resourceLoadParameters) -> (WebCore::ResourceError error, WebCore::ResourceResponse response, Vector<char> data) Delayed + LoadPing(WebKit::NetworkResourceLoadParameters resourceLoadParameters) RemoveLoadIdentifier(uint64_t resourceLoadIdentifier) - - ServePendingRequests(uint32_t resourceLoadPriority) - - SetSerialLoadingEnabled(bool enabled) -> () + SetDefersLoading(uint64_t resourceLoadIdentifier, bool defers) + PrefetchDNS(String hostname) - StartDownload(bool privateBrowsingEnabled, uint64_t downloadID, WebCore::ResourceRequest request) - ConvertMainResourceLoadToDownload(uint64_t mainResourceLoadIdentifier, uint64_t downloadID, WebCore::ResourceRequest request, WebCore::ResourceResponse response) + StartDownload(WebCore::SessionID sessionID, WebKit::DownloadID downloadID, WebCore::ResourceRequest request) + ConvertMainResourceLoadToDownload(WebCore::SessionID sessionID, uint64_t mainResourceLoadIdentifier, WebKit::DownloadID downloadID, WebCore::ResourceRequest request, WebCore::ResourceResponse response) - CookiesForDOM(bool privateBrowsingEnabled, WebCore::KURL firstParty, WebCore::KURL url) -> (WTF::String result) - SetCookiesFromDOM(bool privateBrowsingEnabled, WebCore::KURL firstParty, WebCore::KURL url, WTF::String cookieString) - CookiesEnabled(bool privateBrowsingEnabled, WebCore::KURL firstParty, WebCore::KURL url) -> (bool enabled) - CookieRequestHeaderFieldValue(bool privateBrowsingEnabled, WebCore::KURL firstParty, WebCore::KURL url) -> (WTF::String result) - GetRawCookies(bool privateBrowsingEnabled, WebCore::KURL firstParty, WebCore::KURL url) -> (WTF::Vector<WebCore::Cookie> cookies) - DeleteCookie(bool privateBrowsingEnabled, WebCore::KURL url, WTF::String cookieName) + CookiesForDOM(WebCore::SessionID sessionID, WebCore::URL firstParty, WebCore::URL url) -> (String result) + SetCookiesFromDOM(WebCore::SessionID sessionID, WebCore::URL firstParty, WebCore::URL url, String cookieString) + CookiesEnabled(WebCore::SessionID sessionID, WebCore::URL firstParty, WebCore::URL url) -> (bool enabled) + CookieRequestHeaderFieldValue(WebCore::SessionID sessionID, WebCore::URL firstParty, WebCore::URL url) -> (String result) + GetRawCookies(WebCore::SessionID sessionID, WebCore::URL firstParty, WebCore::URL url) -> (Vector<WebCore::Cookie> cookies) + DeleteCookie(WebCore::SessionID sessionID, WebCore::URL url, String cookieName) - RegisterBlobURL(WebCore::KURL url, WebKit::BlobRegistrationData data) - RegisterBlobURLFromURL(WebCore::KURL url, WebCore::KURL srcURL) - UnregisterBlobURL(WebCore::KURL url) + RegisterFileBlobURL(WebCore::URL url, String path, WebKit::SandboxExtension::Handle extensionHandle, String contentType) + RegisterBlobURL(WebCore::URL url, Vector<WebCore::BlobPart> blobParts, String contentType) + RegisterBlobURLFromURL(WebCore::URL url, WebCore::URL srcURL) + RegisterBlobURLForSlice(WebCore::URL url, WebCore::URL srcURL, int64_t start, int64_t end) + UnregisterBlobURL(WebCore::URL url) + BlobSize(WebCore::URL url) -> (uint64_t resultSize) } - -#endif // ENABLE(NETWORK_PROCESS) diff --git a/Source/WebKit2/NetworkProcess/NetworkDataTask.h b/Source/WebKit2/NetworkProcess/NetworkDataTask.h new file mode 100644 index 000000000..86482cb7a --- /dev/null +++ b/Source/WebKit2/NetworkProcess/NetworkDataTask.h @@ -0,0 +1,149 @@ +/* + * Copyright (C) 2016 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. + */ + +#ifndef NetworkDataTask_h +#define NetworkDataTask_h + +#include <WebCore/FrameLoaderTypes.h> +#include <WebCore/ResourceHandleTypes.h> +#include <WebCore/ResourceLoaderOptions.h> +#include <WebCore/ResourceRequest.h> +#include <WebCore/Timer.h> +#include <wtf/RetainPtr.h> +#include <wtf/text/WTFString.h> + +#if PLATFORM(COCOA) +OBJC_CLASS NSURLSessionDataTask; +#endif + +namespace WebCore { +class AuthenticationChallenge; +class Credential; +class ResourceError; +class ResourceRequest; +class ResourceResponse; +class SharedBuffer; +} + +namespace WebKit { + +class NetworkSession; +class PendingDownload; + +enum class AuthenticationChallengeDisposition { + UseCredential, + PerformDefaultHandling, + Cancel, + RejectProtectionSpace +}; + +typedef std::function<void(const WebCore::ResourceRequest&)> RedirectCompletionHandler; +typedef std::function<void(AuthenticationChallengeDisposition, const WebCore::Credential&)> ChallengeCompletionHandler; +typedef std::function<void(WebCore::PolicyAction)> ResponseCompletionHandler; + +class NetworkDataTaskClient { +public: + virtual void willPerformHTTPRedirection(const WebCore::ResourceResponse&, const WebCore::ResourceRequest&, RedirectCompletionHandler) = 0; + virtual void didReceiveChallenge(const WebCore::AuthenticationChallenge&, ChallengeCompletionHandler) = 0; + virtual void didReceiveResponseNetworkSession(const WebCore::ResourceResponse&, ResponseCompletionHandler) = 0; + virtual void didReceiveData(RefPtr<WebCore::SharedBuffer>&&) = 0; + virtual void didCompleteWithError(const WebCore::ResourceError&) = 0; + virtual void didBecomeDownload() = 0; + virtual void didSendData(uint64_t totalBytesSent, uint64_t totalBytesExpectedToSend) = 0; + virtual void wasBlocked() = 0; + virtual void cannotShowURL() = 0; + + virtual ~NetworkDataTaskClient() { } +}; + +class NetworkDataTask : public RefCounted<NetworkDataTask> { + friend class NetworkSession; +public: + static Ref<NetworkDataTask> create(NetworkSession& session, NetworkDataTaskClient& client, const WebCore::ResourceRequest& request, WebCore::StoredCredentials storedCredentials, WebCore::ContentSniffingPolicy shouldContentSniff, bool shouldClearReferrerOnHTTPSToHTTPRedirect) + { + return adoptRef(*new NetworkDataTask(session, client, request, storedCredentials, shouldContentSniff, shouldClearReferrerOnHTTPSToHTTPRedirect)); + } + + void suspend(); + void cancel(); + void resume(); + + typedef uint64_t TaskIdentifier; + TaskIdentifier taskIdentifier(); + + ~NetworkDataTask(); + + NetworkDataTaskClient& client() { return m_client; } + + DownloadID pendingDownloadID() { return m_pendingDownloadID; } + PendingDownload* pendingDownload() { return m_pendingDownload; } + void setPendingDownloadID(DownloadID downloadID) + { + ASSERT(!m_pendingDownloadID.downloadID()); + ASSERT(downloadID.downloadID()); + m_pendingDownloadID = downloadID; + } + void setPendingDownload(PendingDownload& pendingDownload) + { + ASSERT(!m_pendingDownload); + m_pendingDownload = &pendingDownload; + } + bool tryPasswordBasedAuthentication(const WebCore::AuthenticationChallenge&, ChallengeCompletionHandler); + void willPerformHTTPRedirection(const WebCore::ResourceResponse&, WebCore::ResourceRequest&&, RedirectCompletionHandler); + +private: + NetworkDataTask(NetworkSession&, NetworkDataTaskClient&, const WebCore::ResourceRequest&, WebCore::StoredCredentials, WebCore::ContentSniffingPolicy, bool shouldClearReferrerOnHTTPSToHTTPRedirect); + + enum FailureType { + NoFailure, + BlockedFailure, + InvalidURLFailure + }; + FailureType m_scheduledFailureType { NoFailure }; + WebCore::Timer m_failureTimer; + void failureTimerFired(); + void scheduleFailure(FailureType); + + NetworkSession& m_session; + NetworkDataTaskClient& m_client; + PendingDownload* m_pendingDownload { nullptr }; + DownloadID m_pendingDownloadID; + String m_user; + String m_password; + String m_lastHTTPMethod; + WebCore::ResourceRequest m_firstRequest; + bool m_shouldClearReferrerOnHTTPSToHTTPRedirect; +#if PLATFORM(COCOA) + RetainPtr<NSURLSessionDataTask> m_task; +#endif +}; + +#if PLATFORM(COCOA) +WebCore::Credential serverTrustCredential(const WebCore::AuthenticationChallenge&); +#endif + +} + +#endif diff --git a/Source/WebKit2/NetworkProcess/NetworkLoad.cpp b/Source/WebKit2/NetworkProcess/NetworkLoad.cpp new file mode 100644 index 000000000..bf27fd306 --- /dev/null +++ b/Source/WebKit2/NetworkProcess/NetworkLoad.cpp @@ -0,0 +1,440 @@ +/* + * Copyright (C) 2015 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 "NetworkLoad.h" + +#include "AuthenticationManager.h" +#include "NetworkProcess.h" +#include "SessionTracker.h" +#include "WebErrors.h" +#include <WebCore/NotImplemented.h> +#include <WebCore/ResourceHandle.h> +#include <WebCore/SessionID.h> +#include <wtf/MainThread.h> + +namespace WebKit { + +using namespace WebCore; + +NetworkLoad::NetworkLoad(NetworkLoadClient& client, const NetworkLoadParameters& parameters) + : m_client(client) + , m_parameters(parameters) +#if !USE(NETWORK_SESSION) + , m_networkingContext(RemoteNetworkingContext::create(parameters.sessionID, parameters.shouldClearReferrerOnHTTPSToHTTPRedirect)) +#endif + , m_currentRequest(parameters.request) +{ +#if USE(NETWORK_SESSION) + if (parameters.request.url().protocolIsBlob()) { + m_handle = ResourceHandle::create(nullptr, parameters.request, this, parameters.defersLoading, parameters.contentSniffingPolicy == SniffContent); + return; + } + if (auto* networkSession = SessionTracker::networkSession(parameters.sessionID)) { + m_task = NetworkDataTask::create(*networkSession, *this, parameters.request, parameters.allowStoredCredentials, parameters.contentSniffingPolicy, parameters.shouldClearReferrerOnHTTPSToHTTPRedirect); + if (!parameters.defersLoading) + m_task->resume(); + } else + ASSERT_NOT_REACHED(); +#else + m_handle = ResourceHandle::create(m_networkingContext.get(), parameters.request, this, parameters.defersLoading, parameters.contentSniffingPolicy == SniffContent); +#endif +} + +NetworkLoad::~NetworkLoad() +{ + ASSERT(RunLoop::isMain()); +#if USE(NETWORK_SESSION) + if (m_responseCompletionHandler) + m_responseCompletionHandler(PolicyIgnore); +#endif + if (m_handle) + m_handle->clearClient(); +} + +void NetworkLoad::setDefersLoading(bool defers) +{ +#if USE(NETWORK_SESSION) + if (m_task) { + if (defers) + m_task->suspend(); + else + m_task->resume(); + } +#endif + if (m_handle) + m_handle->setDefersLoading(defers); +} + +void NetworkLoad::cancel() +{ +#if USE(NETWORK_SESSION) + if (m_task) + m_task->cancel(); +#endif + if (m_handle) + m_handle->cancel(); +} + +void NetworkLoad::continueWillSendRequest(const WebCore::ResourceRequest& newRequest) +{ +#if PLATFORM(COCOA) + m_currentRequest.updateFromDelegatePreservingOldProperties(newRequest.nsURLRequest(DoNotUpdateHTTPBody)); +#elif USE(SOUP) + // FIXME: Implement ResourceRequest::updateFromDelegatePreservingOldProperties. See https://bugs.webkit.org/show_bug.cgi?id=126127. + m_currentRequest.updateFromDelegatePreservingOldProperties(newRequest); +#endif + + if (m_currentRequest.isNull()) { + if (m_handle) + m_handle->cancel(); + didFail(m_handle.get(), cancelledError(m_currentRequest)); + } else if (m_handle) + m_handle->continueWillSendRequest(m_currentRequest); + +#if USE(NETWORK_SESSION) + ASSERT(m_redirectCompletionHandler); + if (m_redirectCompletionHandler) { + m_redirectCompletionHandler(m_currentRequest); + m_redirectCompletionHandler = nullptr; + } +#endif +} + +void NetworkLoad::continueDidReceiveResponse() +{ +#if USE(NETWORK_SESSION) + ASSERT(m_responseCompletionHandler); + if (m_responseCompletionHandler) { + m_responseCompletionHandler(PolicyUse); + m_responseCompletionHandler = nullptr; + } +#endif + if (m_handle) + m_handle->continueDidReceiveResponse(); +} + +NetworkLoadClient::ShouldContinueDidReceiveResponse NetworkLoad::sharedDidReceiveResponse(const ResourceResponse& receivedResponse) +{ + ResourceResponse response = receivedResponse; + response.setSource(ResourceResponse::Source::Network); + if (m_parameters.needsCertificateInfo) + response.includeCertificateInfo(); + + return m_client.didReceiveResponse(response); +} + +void NetworkLoad::sharedWillSendRedirectedRequest(const ResourceRequest& request, const ResourceResponse& redirectResponse) +{ + // We only expect to get the willSendRequest callback from ResourceHandle as the result of a redirect. + ASSERT(!redirectResponse.isNull()); + ASSERT(RunLoop::isMain()); + + auto oldRequest = m_currentRequest; + m_currentRequest = request; + m_client.willSendRedirectedRequest(oldRequest, request, redirectResponse); +} + +#if USE(NETWORK_SESSION) + +void NetworkLoad::convertTaskToDownload(DownloadID downloadID) +{ + m_task->setPendingDownloadID(downloadID); + + ASSERT(m_responseCompletionHandler); + if (m_responseCompletionHandler) { + m_responseCompletionHandler(PolicyDownload); + m_responseCompletionHandler = nullptr; + } +} + +void NetworkLoad::setPendingDownloadID(DownloadID downloadID) +{ + m_task->setPendingDownloadID(downloadID); +} + +void NetworkLoad::setPendingDownload(PendingDownload& pendingDownload) +{ + m_task->setPendingDownload(pendingDownload); +} + +void NetworkLoad::willPerformHTTPRedirection(const ResourceResponse& response, const ResourceRequest& request, RedirectCompletionHandler completionHandler) +{ + ASSERT(!m_redirectCompletionHandler); + m_redirectCompletionHandler = completionHandler; + sharedWillSendRedirectedRequest(request, response); +} + +void NetworkLoad::didReceiveChallenge(const AuthenticationChallenge& challenge, std::function<void(AuthenticationChallengeDisposition, const Credential&)> completionHandler) +{ + // NetworkResourceLoader does not know whether the request is cross origin, so Web process computes an applicable credential policy for it. + ASSERT(m_parameters.clientCredentialPolicy != DoNotAskClientForCrossOriginCredentials); + + // Handle server trust evaluation at platform-level if requested, for performance reasons. + if (challenge.protectionSpace().authenticationScheme() == ProtectionSpaceAuthenticationSchemeServerTrustEvaluationRequested + && !NetworkProcess::singleton().canHandleHTTPSServerTrustEvaluation()) { + completionHandler(AuthenticationChallengeDisposition::RejectProtectionSpace, Credential()); + return; + } + + m_challengeCompletionHandler = completionHandler; + m_challenge = challenge; + + if (m_client.isSynchronous()) { + // FIXME: We should ask the WebProcess like the asynchronous case below does. + // This is currently impossible as the WebProcess is blocked waiting on this synchronous load. + // It's possible that we can jump straight to the UI process to resolve this. + continueCanAuthenticateAgainstProtectionSpace(true); + return; + } else + m_client.canAuthenticateAgainstProtectionSpaceAsync(challenge.protectionSpace()); +} + +void NetworkLoad::didReceiveResponseNetworkSession(const ResourceResponse& response, ResponseCompletionHandler completionHandler) +{ + ASSERT(isMainThread()); + if (m_task && m_task->pendingDownloadID().downloadID()) + completionHandler(PolicyDownload); + else if (sharedDidReceiveResponse(response) == NetworkLoadClient::ShouldContinueDidReceiveResponse::Yes) + completionHandler(PolicyUse); + else + m_responseCompletionHandler = completionHandler; +} + +void NetworkLoad::didReceiveData(RefPtr<SharedBuffer>&& buffer) +{ + ASSERT(buffer); + auto size = buffer->size(); + m_client.didReceiveBuffer(WTFMove(buffer), size); +} + +void NetworkLoad::didCompleteWithError(const ResourceError& error) +{ + if (error.isNull()) + m_client.didFinishLoading(WTF::monotonicallyIncreasingTime()); + else + m_client.didFailLoading(error); +} + +void NetworkLoad::didBecomeDownload() +{ + m_client.didConvertToDownload(); +} + +void NetworkLoad::didSendData(uint64_t totalBytesSent, uint64_t totalBytesExpectedToSend) +{ + m_client.didSendData(totalBytesSent, totalBytesExpectedToSend); +} + +void NetworkLoad::wasBlocked() +{ + m_client.didFailLoading(blockedError(m_currentRequest)); +} + +void NetworkLoad::cannotShowURL() +{ + m_client.didFailLoading(cannotShowURLError(m_currentRequest)); +} + +#endif + +void NetworkLoad::didReceiveResponseAsync(ResourceHandle* handle, const ResourceResponse& receivedResponse) +{ + ASSERT_UNUSED(handle, handle == m_handle); + if (sharedDidReceiveResponse(receivedResponse) == NetworkLoadClient::ShouldContinueDidReceiveResponse::Yes) + m_handle->continueDidReceiveResponse(); +} + +void NetworkLoad::didReceiveData(ResourceHandle*, const char* /* data */, unsigned /* length */, int /* encodedDataLength */) +{ + // The NetworkProcess should never get a didReceiveData callback. + // We should always be using didReceiveBuffer. + ASSERT_NOT_REACHED(); +} + +void NetworkLoad::didReceiveBuffer(ResourceHandle* handle, PassRefPtr<SharedBuffer> buffer, int reportedEncodedDataLength) +{ + ASSERT_UNUSED(handle, handle == m_handle); + m_client.didReceiveBuffer(WTFMove(buffer), reportedEncodedDataLength); +} + +void NetworkLoad::didFinishLoading(ResourceHandle* handle, double finishTime) +{ + ASSERT_UNUSED(handle, handle == m_handle); + m_client.didFinishLoading(finishTime); +} + +void NetworkLoad::didFail(ResourceHandle* handle, const ResourceError& error) +{ + ASSERT_UNUSED(handle, !handle || handle == m_handle); + ASSERT(!error.isNull()); + + m_client.didFailLoading(error); +} + +void NetworkLoad::willSendRequestAsync(ResourceHandle* handle, const ResourceRequest& request, const ResourceResponse& redirectResponse) +{ + ASSERT_UNUSED(handle, handle == m_handle); + sharedWillSendRedirectedRequest(request, redirectResponse); +} + +#if USE(PROTECTION_SPACE_AUTH_CALLBACK) +void NetworkLoad::canAuthenticateAgainstProtectionSpaceAsync(ResourceHandle* handle, const ProtectionSpace& protectionSpace) +{ + ASSERT(RunLoop::isMain()); + ASSERT_UNUSED(handle, handle == m_handle); + + // Handle server trust evaluation at platform-level if requested, for performance reasons. + if (protectionSpace.authenticationScheme() == ProtectionSpaceAuthenticationSchemeServerTrustEvaluationRequested + && !NetworkProcess::singleton().canHandleHTTPSServerTrustEvaluation()) { + continueCanAuthenticateAgainstProtectionSpace(false); + return; + } + + if (m_client.isSynchronous()) { + // FIXME: We should ask the WebProcess like the asynchronous case below does. + // This is currently impossible as the WebProcess is blocked waiting on this synchronous load. + // It's possible that we can jump straight to the UI process to resolve this. + continueCanAuthenticateAgainstProtectionSpace(true); + return; + } + + m_client.canAuthenticateAgainstProtectionSpaceAsync(protectionSpace); +} +#endif + +#if USE(PROTECTION_SPACE_AUTH_CALLBACK) +void NetworkLoad::continueCanAuthenticateAgainstProtectionSpace(bool result) +{ +#if USE(NETWORK_SESSION) + ASSERT_WITH_MESSAGE(!m_handle, "Blobs should never give authentication challenges"); + ASSERT(m_challengeCompletionHandler); + auto completionHandler = WTFMove(m_challengeCompletionHandler); + if (!result) { + completionHandler(AuthenticationChallengeDisposition::RejectProtectionSpace, Credential()); + return; + } + + if (!m_challenge.protectionSpace().isPasswordBased()) { + completionHandler(AuthenticationChallengeDisposition::UseCredential, serverTrustCredential(m_challenge)); + return; + } + + if (m_parameters.clientCredentialPolicy == DoNotAskClientForAnyCredentials) { + completionHandler(AuthenticationChallengeDisposition::UseCredential, Credential()); + return; + } + + if (m_task) { + if (auto* pendingDownload = m_task->pendingDownload()) + NetworkProcess::singleton().authenticationManager().didReceiveAuthenticationChallenge(*pendingDownload, m_challenge, completionHandler); + else + NetworkProcess::singleton().authenticationManager().didReceiveAuthenticationChallenge(m_parameters.webPageID, m_parameters.webFrameID, m_challenge, completionHandler); + } +#endif + if (m_handle) + m_handle->continueCanAuthenticateAgainstProtectionSpace(result); +} +#endif + +#if USE(NETWORK_CFDATA_ARRAY_CALLBACK) +bool NetworkLoad::supportsDataArray() +{ + notImplemented(); + return false; +} + +void NetworkLoad::didReceiveDataArray(ResourceHandle*, CFArrayRef) +{ + ASSERT_NOT_REACHED(); + notImplemented(); +} +#endif + +void NetworkLoad::didSendData(ResourceHandle* handle, unsigned long long bytesSent, unsigned long long totalBytesToBeSent) +{ + ASSERT_UNUSED(handle, handle == m_handle); + + m_client.didSendData(bytesSent, totalBytesToBeSent); +} + +void NetworkLoad::wasBlocked(ResourceHandle* handle) +{ + ASSERT_UNUSED(handle, handle == m_handle); + + didFail(handle, WebKit::blockedError(m_currentRequest)); +} + +void NetworkLoad::cannotShowURL(ResourceHandle* handle) +{ + ASSERT_UNUSED(handle, handle == m_handle); + + didFail(handle, WebKit::cannotShowURLError(m_currentRequest)); +} + +bool NetworkLoad::shouldUseCredentialStorage(ResourceHandle* handle) +{ + ASSERT_UNUSED(handle, handle == m_handle || !m_handle); // m_handle will be 0 if called from ResourceHandle::start(). + + // When the WebProcess is handling loading a client is consulted each time this shouldUseCredentialStorage question is asked. + // In NetworkProcess mode we ask the WebProcess client up front once and then reuse the cached answer. + + // We still need this sync version, because ResourceHandle itself uses it internally, even when the delegate uses an async one. + + return m_parameters.allowStoredCredentials == AllowStoredCredentials; +} + +void NetworkLoad::didReceiveAuthenticationChallenge(ResourceHandle* handle, const AuthenticationChallenge& challenge) +{ + ASSERT_UNUSED(handle, handle == m_handle); + // NetworkResourceLoader does not know whether the request is cross origin, so Web process computes an applicable credential policy for it. + ASSERT(m_parameters.clientCredentialPolicy != DoNotAskClientForCrossOriginCredentials); + + if (m_parameters.clientCredentialPolicy == DoNotAskClientForAnyCredentials) { + challenge.authenticationClient()->receivedRequestToContinueWithoutCredential(challenge); + return; + } + + NetworkProcess::singleton().authenticationManager().didReceiveAuthenticationChallenge(m_parameters.webPageID, m_parameters.webFrameID, challenge); +} + +void NetworkLoad::didCancelAuthenticationChallenge(ResourceHandle* handle, const AuthenticationChallenge&) +{ + ASSERT_UNUSED(handle, handle == m_handle); + + // This function is probably not needed (see <rdar://problem/8960124>). + notImplemented(); +} + +void NetworkLoad::receivedCancellation(ResourceHandle* handle, const AuthenticationChallenge&) +{ + ASSERT_UNUSED(handle, handle == m_handle); + + m_handle->cancel(); + didFail(m_handle.get(), cancelledError(m_currentRequest)); +} + +} // namespace WebKit diff --git a/Source/WebKit2/NetworkProcess/NetworkLoad.h b/Source/WebKit2/NetworkProcess/NetworkLoad.h new file mode 100644 index 000000000..77164c2f2 --- /dev/null +++ b/Source/WebKit2/NetworkProcess/NetworkLoad.h @@ -0,0 +1,140 @@ +/* + * Copyright (C) 2015 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. + */ + +#ifndef NetworkLoad_h +#define NetworkLoad_h + +#include "NetworkLoadClient.h" +#include "NetworkLoadParameters.h" +#include "RemoteNetworkingContext.h" +#include <WebCore/ResourceHandleClient.h> + +#if USE(NETWORK_SESSION) +#include "DownloadID.h" +#include "NetworkSession.h" +#include <WebCore/AuthenticationChallenge.h> +#endif + +namespace WebKit { + +class NetworkLoad : public WebCore::ResourceHandleClient +#if USE(NETWORK_SESSION) + , public NetworkDataTaskClient +#endif +{ + WTF_MAKE_FAST_ALLOCATED; +public: + NetworkLoad(NetworkLoadClient&, const NetworkLoadParameters&); + ~NetworkLoad(); + + void setDefersLoading(bool); + void cancel(); + + const WebCore::ResourceRequest& currentRequest() const { return m_currentRequest; } + void clearCurrentRequest() { m_currentRequest = WebCore::ResourceRequest(); } + + void continueWillSendRequest(const WebCore::ResourceRequest&); + void continueDidReceiveResponse(); + +#if USE(NETWORK_SESSION) + void convertTaskToDownload(DownloadID); + void setPendingDownloadID(DownloadID); + void setPendingDownload(PendingDownload&); + DownloadID pendingDownloadID() { return m_task->pendingDownloadID(); } + + // NetworkDataTaskClient + virtual void willPerformHTTPRedirection(const WebCore::ResourceResponse&, const WebCore::ResourceRequest&, RedirectCompletionHandler) final override; + virtual void didReceiveChallenge(const WebCore::AuthenticationChallenge&, ChallengeCompletionHandler) final override; + virtual void didReceiveResponseNetworkSession(const WebCore::ResourceResponse&, ResponseCompletionHandler) final override; + virtual void didReceiveData(RefPtr<WebCore::SharedBuffer>&&) final override; + virtual void didCompleteWithError(const WebCore::ResourceError&) final override; + virtual void didBecomeDownload() final override; + virtual void didSendData(uint64_t totalBytesSent, uint64_t totalBytesExpectedToSend) override; + virtual void wasBlocked() override; + virtual void cannotShowURL() override; +#endif + // ResourceHandleClient + virtual void willSendRequestAsync(WebCore::ResourceHandle*, const WebCore::ResourceRequest&, const WebCore::ResourceResponse& redirectResponse) override; + virtual void didSendData(WebCore::ResourceHandle*, unsigned long long bytesSent, unsigned long long totalBytesToBeSent) override; + virtual void didReceiveResponseAsync(WebCore::ResourceHandle*, const WebCore::ResourceResponse&) override; + virtual void didReceiveData(WebCore::ResourceHandle*, const char*, unsigned, int encodedDataLength) override; + virtual void didReceiveBuffer(WebCore::ResourceHandle*, PassRefPtr<WebCore::SharedBuffer>, int encodedDataLength) override; + virtual void didFinishLoading(WebCore::ResourceHandle*, double finishTime) override; + virtual void didFail(WebCore::ResourceHandle*, const WebCore::ResourceError&) override; + virtual void wasBlocked(WebCore::ResourceHandle*) override; + virtual void cannotShowURL(WebCore::ResourceHandle*) override; + virtual bool shouldUseCredentialStorage(WebCore::ResourceHandle*) override; + virtual void didReceiveAuthenticationChallenge(WebCore::ResourceHandle*, const WebCore::AuthenticationChallenge&) override; + virtual void didCancelAuthenticationChallenge(WebCore::ResourceHandle*, const WebCore::AuthenticationChallenge&) override; + virtual void receivedCancellation(WebCore::ResourceHandle*, const WebCore::AuthenticationChallenge&) override; + virtual bool usesAsyncCallbacks() override { return true; } + virtual bool loadingSynchronousXHR() override { return m_client.isSynchronous(); } + +#if USE(PROTECTION_SPACE_AUTH_CALLBACK) + virtual void canAuthenticateAgainstProtectionSpaceAsync(WebCore::ResourceHandle*, const WebCore::ProtectionSpace&) override; +#endif +#if USE(NETWORK_CFDATA_ARRAY_CALLBACK) + virtual bool supportsDataArray() override; + virtual void didReceiveDataArray(WebCore::ResourceHandle*, CFArrayRef) override; +#endif +#if PLATFORM(COCOA) +#if USE(CFNETWORK) + virtual void willCacheResponseAsync(WebCore::ResourceHandle*, CFCachedURLResponseRef) override; +#else + virtual void willCacheResponseAsync(WebCore::ResourceHandle*, NSCachedURLResponse *) override; +#endif +#endif + +#if USE(PROTECTION_SPACE_AUTH_CALLBACK) + void continueCanAuthenticateAgainstProtectionSpace(bool); +#endif + +#if !USE(NETWORK_SESSION) + WebCore::ResourceHandle* handle() const { return m_handle.get(); } +#endif + +private: + NetworkLoadClient::ShouldContinueDidReceiveResponse sharedDidReceiveResponse(const WebCore::ResourceResponse&); + void sharedWillSendRedirectedRequest(const WebCore::ResourceRequest&, const WebCore::ResourceResponse&); + + NetworkLoadClient& m_client; + const NetworkLoadParameters m_parameters; +#if USE(NETWORK_SESSION) + RefPtr<NetworkDataTask> m_task; + WebCore::AuthenticationChallenge m_challenge; + ChallengeCompletionHandler m_challengeCompletionHandler; + ResponseCompletionHandler m_responseCompletionHandler; + RedirectCompletionHandler m_redirectCompletionHandler; +#else + RefPtr<RemoteNetworkingContext> m_networkingContext; +#endif + RefPtr<WebCore::ResourceHandle> m_handle; + + WebCore::ResourceRequest m_currentRequest; // Updated on redirects. +}; + +} // namespace WebKit + +#endif // NetworkLoad_h diff --git a/Source/WebKit2/NetworkProcess/NetworkLoadClient.h b/Source/WebKit2/NetworkProcess/NetworkLoadClient.h new file mode 100644 index 000000000..e454738a8 --- /dev/null +++ b/Source/WebKit2/NetworkProcess/NetworkLoadClient.h @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2015 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. + */ + +#ifndef NetworkLoadClient_h +#define NetworkLoadClient_h + +#include <WebCore/ResourceError.h> +#include <WebCore/ResourceRequest.h> +#include <WebCore/ResourceResponse.h> +#include <WebCore/SharedBuffer.h> +#include <wtf/Forward.h> + +#if PLATFORM(COCOA) +typedef const struct _CFCachedURLResponse* CFCachedURLResponseRef; +#endif + +namespace WebCore { +class ProtectionSpace; +} + +namespace WebKit { + +class NetworkLoadClient { +public: + virtual ~NetworkLoadClient() { } + + virtual bool isSynchronous() const = 0; + + virtual void didSendData(unsigned long long bytesSent, unsigned long long totalBytesToBeSent) = 0; + virtual void canAuthenticateAgainstProtectionSpaceAsync(const WebCore::ProtectionSpace&) = 0; + virtual void willSendRedirectedRequest(const WebCore::ResourceRequest&, const WebCore::ResourceRequest& redirectRequest, const WebCore::ResourceResponse& redirectResponse) = 0; + enum class ShouldContinueDidReceiveResponse { No, Yes }; + virtual ShouldContinueDidReceiveResponse didReceiveResponse(const WebCore::ResourceResponse&) = 0; + virtual void didReceiveBuffer(RefPtr<WebCore::SharedBuffer>&&, int reportedEncodedDataLength) = 0; + virtual void didFinishLoading(double finishTime) = 0; + virtual void didFailLoading(const WebCore::ResourceError&) = 0; + virtual void didConvertToDownload() = 0; + +#if PLATFORM(COCOA) + virtual void willCacheResponseAsync(CFCachedURLResponseRef) = 0; +#endif +}; + +} // namespace WebKit + +#endif // NetworkLoadClient_h + diff --git a/Source/WebKit2/NetworkProcess/unix/NetworkProcessMainUnix.cpp b/Source/WebKit2/NetworkProcess/NetworkLoadParameters.cpp index c82af840e..cc8487555 100644 --- a/Source/WebKit2/NetworkProcess/unix/NetworkProcessMainUnix.cpp +++ b/Source/WebKit2/NetworkProcess/NetworkLoadParameters.cpp @@ -1,6 +1,5 @@ /* - * Copyright (C) 2013 Apple Inc. All rights reserved. - * Copyright (C) 2013 Company 100 Inc. + * Copyright (C) 2015 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -11,7 +10,7 @@ * 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 THE COPYRIGHT HOLDERS ``AS IS'' + * 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 @@ -25,56 +24,24 @@ */ #include "config.h" -#include "NetworkProcessMainUnix.h" +#include "NetworkLoadParameters.h" -#if ENABLE(NETWORK_PROCESS) - -#include "WKBase.h" -#include "WebKit2Initialize.h" -#include <WebCore/RunLoop.h> -#include <WebKit2/NetworkProcess.h> -#include <error.h> -#include <runtime/InitializeThreading.h> -#include <stdlib.h> -#include <wtf/MainThread.h> - -#if PLATFORM(EFL) -#include <Ecore.h> -#endif - -using namespace WebCore; +#include "NetworkResourceLoadParameters.h" namespace WebKit { -WK_EXPORT int NetworkProcessMain(int argc, char* argv[]) +NetworkLoadParameters::NetworkLoadParameters(const NetworkResourceLoadParameters& parameters) + : webPageID(parameters.webPageID) + , webFrameID(parameters.webFrameID) + , sessionID(parameters.sessionID) + , request(parameters.request) + , contentSniffingPolicy(parameters.contentSniffingPolicy) + , allowStoredCredentials(parameters.allowStoredCredentials) + , clientCredentialPolicy(parameters.clientCredentialPolicy) + , shouldClearReferrerOnHTTPSToHTTPRedirect(parameters.shouldClearReferrerOnHTTPSToHTTPRedirect) + , defersLoading(parameters.defersLoading) + , needsCertificateInfo(parameters.needsCertificateInfo) { - if (argc != 2) - return 1; - -#if PLATFORM(EFL) - if (!ecore_init()) - return 1; - - if (!ecore_main_loop_glib_integrate()) - return 1; -#endif - - InitializeWebKit2(); - - // FIXME: handle proxy settings. - - int socket = atoi(argv[1]); - - WebKit::ChildProcessInitializationParameters parameters; - parameters.connectionIdentifier = int(socket); - - NetworkProcess::shared().initialize(parameters); - - RunLoop::run(); - - return 0; } } // namespace WebKit - -#endif // ENABLE(NETWORK_PROCESS) diff --git a/Source/WebKit2/NetworkProcess/NetworkLoadParameters.h b/Source/WebKit2/NetworkProcess/NetworkLoadParameters.h new file mode 100644 index 000000000..130aaa348 --- /dev/null +++ b/Source/WebKit2/NetworkProcess/NetworkLoadParameters.h @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2015 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. + */ + +#ifndef NetworkLoadParameters_h +#define NetworkLoadParameters_h + +#include <WebCore/ResourceLoaderOptions.h> +#include <WebCore/ResourceRequest.h> +#include <WebCore/SessionID.h> + +namespace WebKit { + +class NetworkResourceLoadParameters; + +class NetworkLoadParameters { +public: + NetworkLoadParameters() = default; + NetworkLoadParameters(const NetworkResourceLoadParameters&); + + uint64_t webPageID { 0 }; + uint64_t webFrameID { 0 }; + WebCore::SessionID sessionID { WebCore::SessionID::emptySessionID() }; + WebCore::ResourceRequest request; + WebCore::ContentSniffingPolicy contentSniffingPolicy { WebCore::SniffContent }; + WebCore::StoredCredentials allowStoredCredentials { WebCore::DoNotAllowStoredCredentials }; + WebCore::ClientCredentialPolicy clientCredentialPolicy { WebCore::DoNotAskClientForAnyCredentials }; + bool shouldClearReferrerOnHTTPSToHTTPRedirect { true }; + bool defersLoading { false }; + bool needsCertificateInfo { false }; +}; + +} // namespace WebKit + +#endif // NetworkLoadParameters_h diff --git a/Source/WebKit2/NetworkProcess/NetworkProcess.cpp b/Source/WebKit2/NetworkProcess/NetworkProcess.cpp index 917ddc5c1..75ce5d4a2 100644 --- a/Source/WebKit2/NetworkProcess/NetworkProcess.cpp +++ b/Source/WebKit2/NetworkProcess/NetworkProcess.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 Apple Inc. All rights reserved. + * Copyright (C) 2012-2015 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -26,47 +26,64 @@ #include "config.h" #include "NetworkProcess.h" -#if ENABLE(NETWORK_PROCESS) - #include "ArgumentCoders.h" #include "Attachment.h" #include "AuthenticationManager.h" +#include "ChildProcessMessages.h" #include "CustomProtocolManager.h" #include "Logging.h" #include "NetworkConnectionToWebProcess.h" #include "NetworkProcessCreationParameters.h" #include "NetworkProcessPlatformStrategies.h" #include "NetworkProcessProxyMessages.h" +#include "NetworkResourceLoader.h" #include "RemoteNetworkingContext.h" -#include "SchedulableLoader.h" +#include "SessionTracker.h" #include "StatisticsData.h" -#include "WebContextMessages.h" #include "WebCookieManager.h" -#include <WebCore/InitializeLogging.h> +#include "WebProcessPoolMessages.h" +#include "WebsiteData.h" +#include <WebCore/DNS.h> +#include <WebCore/DiagnosticLoggingClient.h> +#include <WebCore/Logging.h> +#include <WebCore/PlatformCookieJar.h> #include <WebCore/ResourceRequest.h> -#include <WebCore/RunLoop.h> +#include <WebCore/SecurityOriginData.h> +#include <WebCore/SecurityOriginHash.h> +#include <WebCore/SessionID.h> +#include <wtf/RunLoop.h> #include <wtf/text/CString.h> -#if USE(SECURITY_FRAMEWORK) +#if ENABLE(SEC_ITEM_SHIM) #include "SecItemShim.h" #endif +#if ENABLE(NETWORK_CACHE) +#include "NetworkCache.h" +#include "NetworkCacheCoders.h" +#endif + using namespace WebCore; namespace WebKit { -NetworkProcess& NetworkProcess::shared() +NetworkProcess& NetworkProcess::singleton() { - DEFINE_STATIC_LOCAL(NetworkProcess, networkProcess, ()); + static NeverDestroyed<NetworkProcess> networkProcess; return networkProcess; } NetworkProcess::NetworkProcess() : m_hasSetCacheModel(false) , m_cacheModel(CacheModelDocumentViewer) -#if PLATFORM(MAC) + , m_diskCacheIsDisabledForTesting(false) + , m_canHandleHTTPSServerTrustEvaluation(true) +#if PLATFORM(COCOA) , m_clearCacheDispatchGroup(0) #endif +#if PLATFORM(IOS) + , m_webSQLiteDatabaseTracker(*this) +#endif { NetworkProcessPlatformStrategies::initialize(); @@ -86,7 +103,7 @@ AuthenticationManager& NetworkProcess::authenticationManager() DownloadManager& NetworkProcess::downloadManager() { - DEFINE_STATIC_LOCAL(DownloadManager, downloadManager, (this)); + static NeverDestroyed<DownloadManager> downloadManager(*this); return downloadManager; } @@ -104,28 +121,36 @@ bool NetworkProcess::shouldTerminate() return false; } -void NetworkProcess::didReceiveMessage(CoreIPC::Connection* connection, CoreIPC::MessageDecoder& decoder) +void NetworkProcess::didReceiveMessage(IPC::Connection& connection, IPC::MessageDecoder& decoder) { if (messageReceiverMap().dispatchMessage(connection, decoder)) return; + if (decoder.messageReceiverName() == Messages::ChildProcess::messageReceiverName()) { + ChildProcess::didReceiveMessage(connection, decoder); + return; + } + didReceiveNetworkProcessMessage(connection, decoder); } -void NetworkProcess::didReceiveSyncMessage(CoreIPC::Connection* connection, CoreIPC::MessageDecoder& decoder, OwnPtr<CoreIPC::MessageEncoder>& replyEncoder) +void NetworkProcess::didReceiveSyncMessage(IPC::Connection& connection, IPC::MessageDecoder& decoder, std::unique_ptr<IPC::MessageEncoder>& replyEncoder) { - messageReceiverMap().dispatchSyncMessage(connection, decoder, replyEncoder); + if (messageReceiverMap().dispatchSyncMessage(connection, decoder, replyEncoder)) + return; + + didReceiveSyncNetworkProcessMessage(connection, decoder, replyEncoder); } -void NetworkProcess::didClose(CoreIPC::Connection*) +void NetworkProcess::didClose(IPC::Connection&) { // The UIProcess just exited. - RunLoop::current()->stop(); + RunLoop::current().stop(); } -void NetworkProcess::didReceiveInvalidMessage(CoreIPC::Connection*, CoreIPC::StringReference, CoreIPC::StringReference) +void NetworkProcess::didReceiveInvalidMessage(IPC::Connection&, IPC::StringReference, IPC::StringReference) { - RunLoop::current()->stop(); + RunLoop::current().stop(); } void NetworkProcess::didCreateDownload() @@ -138,7 +163,7 @@ void NetworkProcess::didDestroyDownload() enableTermination(); } -CoreIPC::Connection* NetworkProcess::downloadProxyConnection() +IPC::Connection* NetworkProcess::downloadProxyConnection() { return parentProcessConnection(); } @@ -148,18 +173,41 @@ AuthenticationManager& NetworkProcess::downloadsAuthenticationManager() return authenticationManager(); } +void NetworkProcess::lowMemoryHandler(Critical critical) +{ + platformLowMemoryHandler(critical); + WTF::releaseFastMallocFreeMemory(); +} + void NetworkProcess::initializeNetworkProcess(const NetworkProcessCreationParameters& parameters) { platformInitializeNetworkProcess(parameters); + WTF::setCurrentThreadIsUserInitiated(); + + auto& memoryPressureHandler = MemoryPressureHandler::singleton(); + memoryPressureHandler.setLowMemoryHandler([this] (Critical critical, Synchronous) { + lowMemoryHandler(critical); + }); + memoryPressureHandler.install(); + + m_diskCacheIsDisabledForTesting = parameters.shouldUseTestingNetworkSession; + + m_diskCacheSizeOverride = parameters.diskCacheSizeOverride; setCacheModel(static_cast<uint32_t>(parameters.cacheModel)); -#if PLATFORM(MAC) || USE(CFNETWORK) - RemoteNetworkingContext::setPrivateBrowsingStorageSessionIdentifierBase(parameters.uiProcessBundleIdentifier); + setCanHandleHTTPSServerTrustEvaluation(parameters.canHandleHTTPSServerTrustEvaluation); + +#if PLATFORM(COCOA) || USE(CFNETWORK) + SessionTracker::setIdentifierBase(parameters.uiProcessBundleIdentifier); #endif + // FIXME: instead of handling this here, a message should be sent later (scales to multiple sessions) if (parameters.privateBrowsingEnabled) - RemoteNetworkingContext::ensurePrivateBrowsingSession(); + RemoteNetworkingContext::ensurePrivateBrowsingSession(SessionID::legacyPrivateSessionID()); + + if (parameters.shouldUseTestingNetworkSession) + NetworkStorageSession::switchToNewTestingSession(); NetworkProcessSupplementMap::const_iterator it = m_supplements.begin(); NetworkProcessSupplementMap::const_iterator end = m_supplements.end(); @@ -167,12 +215,12 @@ void NetworkProcess::initializeNetworkProcess(const NetworkProcessCreationParame it->value->initialize(parameters); } -void NetworkProcess::initializeConnection(CoreIPC::Connection* connection) +void NetworkProcess::initializeConnection(IPC::Connection* connection) { ChildProcess::initializeConnection(connection); -#if USE(SECURITY_FRAMEWORK) - SecItemShim::shared().initializeConnection(connection); +#if ENABLE(SEC_ITEM_SHIM) + SecItemShim::singleton().initializeConnection(connection); #endif NetworkProcessSupplementMap::const_iterator it = m_supplements.begin(); @@ -183,42 +231,246 @@ void NetworkProcess::initializeConnection(CoreIPC::Connection* connection) void NetworkProcess::createNetworkConnectionToWebProcess() { -#if PLATFORM(MAC) +#if USE(UNIX_DOMAIN_SOCKETS) + IPC::Connection::SocketPair socketPair = IPC::Connection::createPlatformConnection(); + + RefPtr<NetworkConnectionToWebProcess> connection = NetworkConnectionToWebProcess::create(socketPair.server); + m_webProcessConnections.append(connection.release()); + + IPC::Attachment clientSocket(socketPair.client); + parentProcessConnection()->send(Messages::NetworkProcessProxy::DidCreateNetworkConnectionToWebProcess(clientSocket), 0); +#elif OS(DARWIN) // Create the listening port. mach_port_t listeningPort; mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &listeningPort); // Create a listening connection. - RefPtr<NetworkConnectionToWebProcess> connection = NetworkConnectionToWebProcess::create(CoreIPC::Connection::Identifier(listeningPort)); + RefPtr<NetworkConnectionToWebProcess> connection = NetworkConnectionToWebProcess::create(IPC::Connection::Identifier(listeningPort)); m_webProcessConnections.append(connection.release()); - CoreIPC::Attachment clientPort(listeningPort, MACH_MSG_TYPE_MAKE_SEND); + IPC::Attachment clientPort(listeningPort, MACH_MSG_TYPE_MAKE_SEND); parentProcessConnection()->send(Messages::NetworkProcessProxy::DidCreateNetworkConnectionToWebProcess(clientPort), 0); #else notImplemented(); #endif } -void NetworkProcess::ensurePrivateBrowsingSession() +void NetworkProcess::clearCachedCredentials() +{ + NetworkStorageSession::defaultStorageSession().credentialStorage().clearCredentials(); +} + +void NetworkProcess::ensurePrivateBrowsingSession(SessionID sessionID) +{ + RemoteNetworkingContext::ensurePrivateBrowsingSession(sessionID); +} + +void NetworkProcess::destroyPrivateBrowsingSession(SessionID sessionID) +{ + SessionTracker::destroySession(sessionID); +} + +static void fetchDiskCacheEntries(SessionID sessionID, std::function<void (Vector<WebsiteData::Entry>)> completionHandler) +{ +#if ENABLE(NETWORK_CACHE) + if (NetworkCache::singleton().isEnabled()) { + auto* origins = new HashSet<RefPtr<SecurityOrigin>>(); + + NetworkCache::singleton().traverse([completionHandler, origins](const NetworkCache::Cache::TraversalEntry *traversalEntry) { + if (!traversalEntry) { + Vector<WebsiteData::Entry> entries; + + for (auto& origin : *origins) + entries.append(WebsiteData::Entry { origin, WebsiteDataTypeDiskCache }); + + delete origins; + + RunLoop::main().dispatch([completionHandler, entries] { + completionHandler(entries); + }); + + return; + } + + origins->add(SecurityOrigin::create(traversalEntry->entry.response().url())); + }); + + return; + } +#endif + + Vector<WebsiteData::Entry> entries; + +#if USE(CFURLCACHE) + for (auto& origin : NetworkProcess::cfURLCacheOrigins()) + entries.append(WebsiteData::Entry { WTFMove(origin), WebsiteDataTypeDiskCache }); +#endif + + RunLoop::main().dispatch([completionHandler, entries] { + completionHandler(entries); + }); +} + +void NetworkProcess::fetchWebsiteData(SessionID sessionID, uint64_t websiteDataTypes, uint64_t callbackID) +{ + struct CallbackAggregator final : public RefCounted<CallbackAggregator> { + explicit CallbackAggregator(std::function<void (WebsiteData)> completionHandler) + : m_completionHandler(WTFMove(completionHandler)) + { + } + + ~CallbackAggregator() + { + ASSERT(RunLoop::isMain()); + + auto completionHandler = WTFMove(m_completionHandler); + auto websiteData = WTFMove(m_websiteData); + + RunLoop::main().dispatch([completionHandler, websiteData] { + completionHandler(websiteData); + }); + } + + std::function<void (WebsiteData)> m_completionHandler; + WebsiteData m_websiteData; + }; + + RefPtr<CallbackAggregator> callbackAggregator = adoptRef(new CallbackAggregator([this, callbackID](WebsiteData websiteData) { + parentProcessConnection()->send(Messages::NetworkProcessProxy::DidFetchWebsiteData(callbackID, websiteData), 0); + })); + + if (websiteDataTypes & WebsiteDataTypeCookies) { + if (auto* networkStorageSession = SessionTracker::storageSession(sessionID)) + getHostnamesWithCookies(*networkStorageSession, callbackAggregator->m_websiteData.hostNamesWithCookies); + } + + if (websiteDataTypes & WebsiteDataTypeDiskCache) { + fetchDiskCacheEntries(sessionID, [callbackAggregator](Vector<WebsiteData::Entry> entries) { + callbackAggregator->m_websiteData.entries.appendVector(entries); + }); + } +} + +void NetworkProcess::deleteWebsiteData(SessionID sessionID, uint64_t websiteDataTypes, std::chrono::system_clock::time_point modifiedSince, uint64_t callbackID) +{ +#if PLATFORM(COCOA) + if (websiteDataTypes & WebsiteDataTypeHSTSCache) { + if (auto* networkStorageSession = SessionTracker::storageSession(sessionID)) + clearHSTSCache(*networkStorageSession, modifiedSince); + } +#endif + + if (websiteDataTypes & WebsiteDataTypeCookies) { + if (auto* networkStorageSession = SessionTracker::storageSession(sessionID)) + deleteAllCookiesModifiedSince(*networkStorageSession, modifiedSince); + } + + auto completionHandler = [this, callbackID] { + parentProcessConnection()->send(Messages::NetworkProcessProxy::DidDeleteWebsiteData(callbackID), 0); + }; + + if ((websiteDataTypes & WebsiteDataTypeDiskCache) && !sessionID.isEphemeral()) { + clearDiskCache(modifiedSince, WTFMove(completionHandler)); + return; + } + + completionHandler(); +} + +static void clearDiskCacheEntries(const Vector<SecurityOriginData>& origins, std::function<void ()> completionHandler) +{ +#if ENABLE(NETWORK_CACHE) + if (NetworkCache::singleton().isEnabled()) { + auto* originsToDelete = new HashSet<RefPtr<SecurityOrigin>>(); + + for (auto& origin : origins) + originsToDelete->add(origin.securityOrigin()); + + auto* cacheKeysToDelete = new Vector<NetworkCache::Key>; + + NetworkCache::singleton().traverse([completionHandler, originsToDelete, cacheKeysToDelete](const NetworkCache::Cache::TraversalEntry *traversalEntry) { + + if (traversalEntry) { + if (originsToDelete->contains(SecurityOrigin::create(traversalEntry->entry.response().url()))) + cacheKeysToDelete->append(traversalEntry->entry.key()); + return; + } + + delete originsToDelete; + + for (auto& key : *cacheKeysToDelete) + NetworkCache::singleton().remove(key); + + delete cacheKeysToDelete; + + RunLoop::main().dispatch(completionHandler); + return; + }); + + return; + } +#endif + +#if USE(CFURLCACHE) + NetworkProcess::clearCFURLCacheForOrigins(origins); +#endif + + RunLoop::main().dispatch(WTFMove(completionHandler)); +} + +void NetworkProcess::deleteWebsiteDataForOrigins(SessionID sessionID, uint64_t websiteDataTypes, const Vector<SecurityOriginData>& origins, const Vector<String>& cookieHostNames, uint64_t callbackID) { - RemoteNetworkingContext::ensurePrivateBrowsingSession(); + if (websiteDataTypes & WebsiteDataTypeCookies) { + if (auto* networkStorageSession = SessionTracker::storageSession(sessionID)) + deleteCookiesForHostnames(*networkStorageSession, cookieHostNames); + } + + auto completionHandler = [this, callbackID] { + parentProcessConnection()->send(Messages::NetworkProcessProxy::DidDeleteWebsiteDataForOrigins(callbackID), 0); + }; + + if ((websiteDataTypes & WebsiteDataTypeDiskCache) && !sessionID.isEphemeral()) { + clearDiskCacheEntries(origins, WTFMove(completionHandler)); + return; + } + + completionHandler(); } -void NetworkProcess::destroyPrivateBrowsingSession() +void NetworkProcess::downloadRequest(SessionID sessionID, DownloadID downloadID, const ResourceRequest& request) { - RemoteNetworkingContext::destroyPrivateBrowsingSession(); + downloadManager().startDownload(sessionID, downloadID, request); } -void NetworkProcess::downloadRequest(uint64_t downloadID, const ResourceRequest& request) +void NetworkProcess::resumeDownload(SessionID sessionID, DownloadID downloadID, const IPC::DataReference& resumeData, const String& path, const WebKit::SandboxExtension::Handle& sandboxExtensionHandle) { - downloadManager().startDownload(downloadID, request); + downloadManager().resumeDownload(sessionID, downloadID, resumeData, path, sandboxExtensionHandle); } -void NetworkProcess::cancelDownload(uint64_t downloadID) +void NetworkProcess::cancelDownload(DownloadID downloadID) { downloadManager().cancelDownload(downloadID); } +#if PLATFORM(QT) +void NetworkProcess::startTransfer(DownloadID downloadID, const String& destination) +{ + downloadManager().startTransfer(downloadID, destination); +} +#endif + +#if USE(NETWORK_SESSION) +void NetworkProcess::continueCanAuthenticateAgainstProtectionSpace(DownloadID downloadID, bool canAuthenticate) +{ + downloadManager().continueCanAuthenticateAgainstProtectionSpace(downloadID, canAuthenticate); +} + +void NetworkProcess::continueWillSendRequest(DownloadID downloadID, const WebCore::ResourceRequest& request) +{ + downloadManager().continueWillSendRequest(downloadID, request); +} +#endif + void NetworkProcess::setCacheModel(uint32_t cm) { CacheModel cacheModel = static_cast<CacheModel>(cm); @@ -230,20 +482,44 @@ void NetworkProcess::setCacheModel(uint32_t cm) } } -void NetworkProcess::getNetworkProcessStatistics(uint64_t callbackID) +void NetworkProcess::setCanHandleHTTPSServerTrustEvaluation(bool value) { - NetworkResourceLoadScheduler& scheduler = NetworkProcess::shared().networkResourceLoadScheduler(); + m_canHandleHTTPSServerTrustEvaluation = value; +} +void NetworkProcess::getNetworkProcessStatistics(uint64_t callbackID) +{ StatisticsData data; - data.statisticsNumbers.set("HostsPendingCount", scheduler.hostsPendingCount()); - data.statisticsNumbers.set("HostsActiveCount", scheduler.hostsActiveCount()); - data.statisticsNumbers.set("LoadsPendingCount", scheduler.loadsPendingCount()); - data.statisticsNumbers.set("LoadsActiveCount", scheduler.loadsActiveCount()); - data.statisticsNumbers.set("DownloadsActiveCount", shared().downloadManager().activeDownloadCount()); - data.statisticsNumbers.set("OutstandingAuthenticationChallengesCount", shared().authenticationManager().outstandingAuthenticationChallengeCount()); + auto& networkProcess = NetworkProcess::singleton(); + data.statisticsNumbers.set("DownloadsActiveCount", networkProcess.downloadManager().activeDownloadCount()); + data.statisticsNumbers.set("OutstandingAuthenticationChallengesCount", networkProcess.authenticationManager().outstandingAuthenticationChallengeCount()); - parentProcessConnection()->send(Messages::WebContext::DidGetStatistics(data, callbackID), 0); + parentProcessConnection()->send(Messages::WebProcessPool::DidGetStatistics(data, callbackID), 0); +} + +void NetworkProcess::logDiagnosticMessage(uint64_t webPageID, const String& message, const String& description, ShouldSample shouldSample) +{ + if (!DiagnosticLoggingClient::shouldLogAfterSampling(shouldSample)) + return; + + parentProcessConnection()->send(Messages::NetworkProcessProxy::LogSampledDiagnosticMessage(webPageID, message, description), 0); +} + +void NetworkProcess::logDiagnosticMessageWithResult(uint64_t webPageID, const String& message, const String& description, DiagnosticLoggingResultType result, ShouldSample shouldSample) +{ + if (!DiagnosticLoggingClient::shouldLogAfterSampling(shouldSample)) + return; + + parentProcessConnection()->send(Messages::NetworkProcessProxy::LogSampledDiagnosticMessageWithResult(webPageID, message, description, result), 0); +} + +void NetworkProcess::logDiagnosticMessageWithValue(uint64_t webPageID, const String& message, const String& description, const String& value, ShouldSample shouldSample) +{ + if (!DiagnosticLoggingClient::shouldLogAfterSampling(shouldSample)) + return; + + parentProcessConnection()->send(Messages::NetworkProcessProxy::LogSampledDiagnosticMessageWithValue(webPageID, message, description, value), 0); } void NetworkProcess::terminate() @@ -252,7 +528,36 @@ void NetworkProcess::terminate() ChildProcess::terminate(); } -#if !PLATFORM(MAC) +void NetworkProcess::processWillSuspendImminently(bool& handled) +{ + lowMemoryHandler(Critical::Yes); + handled = true; +} + +void NetworkProcess::prepareToSuspend() +{ + lowMemoryHandler(Critical::Yes); + parentProcessConnection()->send(Messages::NetworkProcessProxy::ProcessReadyToSuspend(), 0); +} + +void NetworkProcess::cancelPrepareToSuspend() +{ + // Although it is tempting to send a NetworkProcessProxy::DidCancelProcessSuspension message from here + // we do not because prepareToSuspend() already replied with a NetworkProcessProxy::ProcessReadyToSuspend + // message. And NetworkProcessProxy expects to receive either a NetworkProcessProxy::ProcessReadyToSuspend- + // or NetworkProcessProxy::DidCancelProcessSuspension- message, but not both. +} + +void NetworkProcess::processDidResume() +{ +} + +void NetworkProcess::prefetchDNS(const String& hostname) +{ + WebCore::prefetchDNS(hostname); +} + +#if !PLATFORM(COCOA) void NetworkProcess::initializeProcess(const ChildProcessInitializationParameters&) { } @@ -264,8 +569,10 @@ void NetworkProcess::initializeProcessName(const ChildProcessInitializationParam void NetworkProcess::initializeSandbox(const ChildProcessInitializationParameters&, SandboxInitializationParameters&) { } + +void NetworkProcess::platformLowMemoryHandler(Critical) +{ +} #endif } // namespace WebKit - -#endif // ENABLE(NETWORK_PROCESS) diff --git a/Source/WebKit2/NetworkProcess/NetworkProcess.h b/Source/WebKit2/NetworkProcess/NetworkProcess.h index 00f4068cf..4b573b9b5 100644 --- a/Source/WebKit2/NetworkProcess/NetworkProcess.h +++ b/Source/WebKit2/NetworkProcess/NetworkProcess.h @@ -26,31 +26,45 @@ #ifndef NetworkProcess_h #define NetworkProcess_h -#if ENABLE(NETWORK_PROCESS) - #include "CacheModel.h" #include "ChildProcess.h" #include "DownloadManager.h" #include "MessageReceiverMap.h" -#include "NetworkResourceLoadScheduler.h" +#include <WebCore/DiagnosticLoggingClient.h> +#include <WebCore/MemoryPressureHandler.h> +#include <WebCore/SessionID.h> +#include <memory> #include <wtf/Forward.h> +#include <wtf/NeverDestroyed.h> + +#if PLATFORM(IOS) +#include "WebSQLiteDatabaseTracker.h" +#endif + +#if PLATFORM(QT) +#include "QtNetworkAccessManager.h" +#endif namespace WebCore { - class RunLoop; +class CertificateInfo; +class NetworkStorageSession; +class SecurityOrigin; +class SessionID; +struct SecurityOriginData; } namespace WebKit { - class AuthenticationManager; class NetworkConnectionToWebProcess; class NetworkProcessSupplement; -class PlatformCertificateInfo; struct NetworkProcessCreationParameters; class NetworkProcess : public ChildProcess, private DownloadManager::Client { WTF_MAKE_NONCOPYABLE(NetworkProcess); + friend class NeverDestroyed<NetworkProcess>; + friend class NeverDestroyed<DownloadManager>; public: - static NetworkProcess& shared(); + static NetworkProcess& singleton(); template <typename T> T* supplement() @@ -61,15 +75,39 @@ public: template <typename T> void addSupplement() { - m_supplements.add(T::supplementName(), adoptPtr<NetworkProcessSupplement>(new T(this))); + m_supplements.add(T::supplementName(), std::make_unique<T>(this)); } void removeNetworkConnectionToWebProcess(NetworkConnectionToWebProcess*); - NetworkResourceLoadScheduler& networkResourceLoadScheduler() { return m_networkResourceLoadScheduler; } - AuthenticationManager& authenticationManager(); DownloadManager& downloadManager(); + bool canHandleHTTPSServerTrustEvaluation() const { return m_canHandleHTTPSServerTrustEvaluation; } + + void processWillSuspendImminently(bool& handled); + void prepareToSuspend(); + void cancelPrepareToSuspend(); + void processDidResume(); + + // Diagnostic messages logging. + void logDiagnosticMessage(uint64_t webPageID, const String& message, const String& description, WebCore::ShouldSample); + void logDiagnosticMessageWithResult(uint64_t webPageID, const String& message, const String& description, WebCore::DiagnosticLoggingResultType, WebCore::ShouldSample); + void logDiagnosticMessageWithValue(uint64_t webPageID, const String& message, const String& description, const String& value, WebCore::ShouldSample); + +#if USE(CFURLCACHE) + static Vector<Ref<WebCore::SecurityOrigin>> cfURLCacheOrigins(); + static void clearCFURLCacheForOrigins(const Vector<WebCore::SecurityOriginData>&); +#endif + +#if PLATFORM(COCOA) + void clearHSTSCache(WebCore::NetworkStorageSession&, std::chrono::system_clock::time_point modifiedSince); +#endif + +#if PLATFORM(QT) + QNetworkAccessManager& networkAccessManager() { return m_networkAccessManager; } +#endif + + void prefetchDNS(const String&); private: NetworkProcess(); @@ -77,66 +115,105 @@ private: void platformInitializeNetworkProcess(const NetworkProcessCreationParameters&); - virtual void terminate() OVERRIDE; + virtual void terminate() override; void platformTerminate(); + void lowMemoryHandler(WebCore::Critical); + void platformLowMemoryHandler(WebCore::Critical); + // ChildProcess - virtual void initializeProcess(const ChildProcessInitializationParameters&) OVERRIDE; - virtual void initializeProcessName(const ChildProcessInitializationParameters&) OVERRIDE; - virtual void initializeSandbox(const ChildProcessInitializationParameters&, SandboxInitializationParameters&) OVERRIDE; - virtual void initializeConnection(CoreIPC::Connection*) OVERRIDE; - virtual bool shouldTerminate() OVERRIDE; - - // CoreIPC::Connection::Client - virtual void didReceiveMessage(CoreIPC::Connection*, CoreIPC::MessageDecoder&) OVERRIDE; - virtual void didReceiveSyncMessage(CoreIPC::Connection*, CoreIPC::MessageDecoder&, OwnPtr<CoreIPC::MessageEncoder>&); - virtual void didClose(CoreIPC::Connection*) OVERRIDE; - virtual void didReceiveInvalidMessage(CoreIPC::Connection*, CoreIPC::StringReference messageReceiverName, CoreIPC::StringReference messageName) OVERRIDE; + virtual void initializeProcess(const ChildProcessInitializationParameters&) override; + virtual void initializeProcessName(const ChildProcessInitializationParameters&) override; + virtual void initializeSandbox(const ChildProcessInitializationParameters&, SandboxInitializationParameters&) override; + virtual void initializeConnection(IPC::Connection*) override; + virtual bool shouldTerminate() override; + + // IPC::Connection::Client + virtual void didReceiveMessage(IPC::Connection&, IPC::MessageDecoder&) override; + virtual void didReceiveSyncMessage(IPC::Connection&, IPC::MessageDecoder&, std::unique_ptr<IPC::MessageEncoder>&) override; + virtual void didClose(IPC::Connection&) override; + virtual void didReceiveInvalidMessage(IPC::Connection&, IPC::StringReference messageReceiverName, IPC::StringReference messageName) override; + virtual IPC::ProcessType localProcessType() override { return IPC::ProcessType::Network; } + virtual IPC::ProcessType remoteProcessType() override { return IPC::ProcessType::UI; } // DownloadManager::Client - virtual void didCreateDownload() OVERRIDE; - virtual void didDestroyDownload() OVERRIDE; - virtual CoreIPC::Connection* downloadProxyConnection() OVERRIDE; - virtual AuthenticationManager& downloadsAuthenticationManager() OVERRIDE; + virtual void didCreateDownload() override; + virtual void didDestroyDownload() override; + virtual IPC::Connection* downloadProxyConnection() override; + virtual AuthenticationManager& downloadsAuthenticationManager() override; // Message Handlers - void didReceiveNetworkProcessMessage(CoreIPC::Connection*, CoreIPC::MessageDecoder&); + void didReceiveNetworkProcessMessage(IPC::Connection&, IPC::MessageDecoder&); + void didReceiveSyncNetworkProcessMessage(IPC::Connection&, IPC::MessageDecoder&, std::unique_ptr<IPC::MessageEncoder>&); void initializeNetworkProcess(const NetworkProcessCreationParameters&); void createNetworkConnectionToWebProcess(); - void ensurePrivateBrowsingSession(); - void destroyPrivateBrowsingSession(); - void downloadRequest(uint64_t downloadID, const WebCore::ResourceRequest&); - void cancelDownload(uint64_t downloadID); + void ensurePrivateBrowsingSession(WebCore::SessionID); + void destroyPrivateBrowsingSession(WebCore::SessionID); + + void fetchWebsiteData(WebCore::SessionID, uint64_t websiteDataTypes, uint64_t callbackID); + void deleteWebsiteData(WebCore::SessionID, uint64_t websiteDataTypes, std::chrono::system_clock::time_point modifiedSince, uint64_t callbackID); + void deleteWebsiteDataForOrigins(WebCore::SessionID, uint64_t websiteDataTypes, const Vector<WebCore::SecurityOriginData>& origins, const Vector<String>& cookieHostNames, uint64_t callbackID); + + void clearCachedCredentials(); + + // FIXME: This should take a session ID so we can identify which disk cache to delete. + void clearDiskCache(std::chrono::system_clock::time_point modifiedSince, std::function<void ()> completionHandler); + + void downloadRequest(WebCore::SessionID, DownloadID, const WebCore::ResourceRequest&); + void resumeDownload(WebCore::SessionID, DownloadID, const IPC::DataReference& resumeData, const String& path, const SandboxExtension::Handle&); + void cancelDownload(DownloadID); +#if PLATFORM(QT) + void startTransfer(DownloadID, const String& destination); +#endif +#if USE(NETWORK_SESSION) + void continueCanAuthenticateAgainstProtectionSpace(DownloadID, bool canAuthenticate); + void continueWillSendRequest(DownloadID, const WebCore::ResourceRequest&); +#endif void setCacheModel(uint32_t); - void allowSpecificHTTPSCertificateForHost(const PlatformCertificateInfo&, const String& host); + void allowSpecificHTTPSCertificateForHost(const WebCore::CertificateInfo&, const String& host); + void setCanHandleHTTPSServerTrustEvaluation(bool); void getNetworkProcessStatistics(uint64_t callbackID); void clearCacheForAllOrigins(uint32_t cachesToClear); +#if USE(SOUP) + void setIgnoreTLSErrors(bool); + void userPreferredLanguagesChanged(const Vector<String>&); +#endif + // Platform Helpers void platformSetCacheModel(CacheModel); // Connections to WebProcesses. Vector<RefPtr<NetworkConnectionToWebProcess>> m_webProcessConnections; - NetworkResourceLoadScheduler m_networkResourceLoadScheduler; - String m_diskCacheDirectory; bool m_hasSetCacheModel; CacheModel m_cacheModel; + int64_t m_diskCacheSizeOverride { -1 }; + bool m_diskCacheIsDisabledForTesting; + bool m_canHandleHTTPSServerTrustEvaluation; - typedef HashMap<const char*, OwnPtr<NetworkProcessSupplement>, PtrHash<const char*>> NetworkProcessSupplementMap; + typedef HashMap<const char*, std::unique_ptr<NetworkProcessSupplement>, PtrHash<const char*>> NetworkProcessSupplementMap; NetworkProcessSupplementMap m_supplements; -#if PLATFORM(MAC) +#if PLATFORM(COCOA) + void platformInitializeNetworkProcessCocoa(const NetworkProcessCreationParameters&); + // FIXME: We'd like to be able to do this without the #ifdef, but WorkQueue + BinarySemaphore isn't good enough since // multiple requests to clear the cache can come in before previous requests complete, and we need to wait for all of them. // In the future using WorkQueue and a counting semaphore would work, as would WorkQueue supporting the libdispatch concept of "work groups". dispatch_group_t m_clearCacheDispatchGroup; #endif + +#if PLATFORM(IOS) + WebSQLiteDatabaseTracker m_webSQLiteDatabaseTracker; +#endif + +#if PLATFORM(QT) + QtNetworkAccessManager m_networkAccessManager; +#endif }; } // namespace WebKit -#endif // ENABLE(NETWORK_PROCESS) - #endif // NetworkProcess_h diff --git a/Source/WebKit2/NetworkProcess/NetworkProcess.messages.in b/Source/WebKit2/NetworkProcess/NetworkProcess.messages.in index a5fcbc7be..3c6f89337 100644 --- a/Source/WebKit2/NetworkProcess/NetworkProcess.messages.in +++ b/Source/WebKit2/NetworkProcess/NetworkProcess.messages.in @@ -20,30 +20,53 @@ # 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. -#if ENABLE(NETWORK_PROCESS) - messages -> NetworkProcess LegacyReceiver { # Initializes the network process. - InitializeNetworkProcess(WebKit::NetworkProcessCreationParameters processCreationParameters) + InitializeNetworkProcess(struct WebKit::NetworkProcessCreationParameters processCreationParameters) # Creates a connection for communication with a WebProcess CreateNetworkConnectionToWebProcess() - EnsurePrivateBrowsingSession() - DestroyPrivateBrowsingSession() +#if USE(SOUP) + SetIgnoreTLSErrors(bool ignoreTLSErrors) + UserPreferredLanguagesChanged(Vector<String> languages) +#endif + + ClearCachedCredentials() - DownloadRequest(uint64_t downloadID, WebCore::ResourceRequest request) - CancelDownload(uint64_t downloadID) + EnsurePrivateBrowsingSession(WebCore::SessionID sessionID) + DestroyPrivateBrowsingSession(WebCore::SessionID sessionID) + + FetchWebsiteData(WebCore::SessionID sessionID, uint64_t websiteDataTypes, uint64_t callbackID) + DeleteWebsiteData(WebCore::SessionID sessionID, uint64_t websiteDataTypes, std::chrono::system_clock::time_point modifiedSince, uint64_t callbackID) + DeleteWebsiteDataForOrigins(WebCore::SessionID sessionID, uint64_t websiteDataTypes, Vector<WebCore::SecurityOriginData> origins, Vector<String> cookieHostNames, uint64_t callbackID) + + DownloadRequest(WebCore::SessionID sessionID, WebKit::DownloadID downloadID, WebCore::ResourceRequest request) + ResumeDownload(WebCore::SessionID sessionID, WebKit::DownloadID downloadID, IPC::DataReference resumeData, String path, WebKit::SandboxExtension::Handle sandboxExtensionHandle) + CancelDownload(WebKit::DownloadID downloadID) +#if PLATFORM(QT) + StartTransfer(WebKit::DownloadID downloadID, String destination) +#endif +#if USE(NETWORK_SESSION) + ContinueCanAuthenticateAgainstProtectionSpace(WebKit::DownloadID downloadID, bool canAuthenticate) + ContinueWillSendRequest(WebKit::DownloadID downloadID, WebCore::ResourceRequest request) +#endif -#if PLATFORM(MAC) SetProcessSuppressionEnabled(bool flag) +#if PLATFORM(COCOA) + SetQOS(int latencyQOS, int throughputQOS) #endif - AllowSpecificHTTPSCertificateForHost(WebKit::PlatformCertificateInfo certificate, WTF::String host) + AllowSpecificHTTPSCertificateForHost(WebCore::CertificateInfo certificate, String host) + SetCanHandleHTTPSServerTrustEvaluation(bool value) GetNetworkProcessStatistics(uint64_t callbackID) ClearCacheForAllOrigins(uint32_t cachesToClear) -} + SetCacheModel(uint32_t cacheModel); -#endif // ENABLE(NETWORK_PROCESS) + ProcessWillSuspendImminently() -> (bool handled) + PrepareToSuspend() + CancelPrepareToSuspend() + ProcessDidResume() +} diff --git a/Source/WebKit2/NetworkProcess/NetworkProcessCreationParameters.cpp b/Source/WebKit2/NetworkProcess/NetworkProcessCreationParameters.cpp new file mode 100644 index 000000000..da6fb1902 --- /dev/null +++ b/Source/WebKit2/NetworkProcess/NetworkProcessCreationParameters.cpp @@ -0,0 +1,168 @@ +/* + * Copyright (C) 2012 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 "NetworkProcessCreationParameters.h" + +#include "ArgumentCoders.h" + +#if PLATFORM(COCOA) +#include "ArgumentCodersCF.h" +#endif + +namespace WebKit { + +NetworkProcessCreationParameters::NetworkProcessCreationParameters() +{ +} + +void NetworkProcessCreationParameters::encode(IPC::ArgumentEncoder& encoder) const +{ + encoder << privateBrowsingEnabled; + encoder.encodeEnum(cacheModel); + encoder << diskCacheSizeOverride; + encoder << canHandleHTTPSServerTrustEvaluation; + encoder << diskCacheDirectory; + encoder << diskCacheDirectoryExtensionHandle; +#if ENABLE(NETWORK_CACHE) + encoder << shouldEnableNetworkCache; + encoder << shouldEnableNetworkCacheEfficacyLogging; +#if ENABLE(NETWORK_CACHE_SPECULATIVE_REVALIDATION) + encoder << shouldEnableNetworkCacheSpeculativeRevalidation; +#endif +#endif +#if ENABLE(SECCOMP_FILTERS) + encoder << cookieStorageDirectory; +#endif +#if PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101100 + encoder << uiProcessCookieStorageIdentifier; +#endif +#if PLATFORM(IOS) + encoder << cookieStorageDirectoryExtensionHandle; + encoder << containerCachesDirectoryExtensionHandle; + encoder << parentBundleDirectoryExtensionHandle; +#endif + encoder << shouldUseTestingNetworkSession; + encoder << urlSchemesRegisteredForCustomProtocols; +#if PLATFORM(COCOA) + encoder << parentProcessName; + encoder << uiProcessBundleIdentifier; + encoder << nsURLCacheMemoryCapacity; + encoder << nsURLCacheDiskCapacity; + encoder << httpProxy; + encoder << httpsProxy; +#if TARGET_OS_IPHONE || (PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101100) + IPC::encode(encoder, networkATSContext.get()); +#endif +#endif +#if USE(SOUP) || PLATFORM(QT) + encoder << cookiePersistentStoragePath; + encoder << cookiePersistentStorageType; + encoder.encodeEnum(cookieAcceptPolicy); + encoder << ignoreTLSErrors; + encoder << languages; +#endif +} + +bool NetworkProcessCreationParameters::decode(IPC::ArgumentDecoder& decoder, NetworkProcessCreationParameters& result) +{ + if (!decoder.decode(result.privateBrowsingEnabled)) + return false; + if (!decoder.decodeEnum(result.cacheModel)) + return false; + if (!decoder.decode(result.diskCacheSizeOverride)) + return false; + if (!decoder.decode(result.canHandleHTTPSServerTrustEvaluation)) + return false; + if (!decoder.decode(result.diskCacheDirectory)) + return false; + if (!decoder.decode(result.diskCacheDirectoryExtensionHandle)) + return false; +#if ENABLE(NETWORK_CACHE) + if (!decoder.decode(result.shouldEnableNetworkCache)) + return false; + if (!decoder.decode(result.shouldEnableNetworkCacheEfficacyLogging)) + return false; +#if ENABLE(NETWORK_CACHE_SPECULATIVE_REVALIDATION) + if (!decoder.decode(result.shouldEnableNetworkCacheSpeculativeRevalidation)) + return false; +#endif +#endif +#if ENABLE(SECCOMP_FILTERS) + if (!decoder.decode(result.cookieStorageDirectory)) + return false; +#endif +#if PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101100 + if (!decoder.decode(result.uiProcessCookieStorageIdentifier)) + return false; +#endif +#if PLATFORM(IOS) + if (!decoder.decode(result.cookieStorageDirectoryExtensionHandle)) + return false; + if (!decoder.decode(result.containerCachesDirectoryExtensionHandle)) + return false; + if (!decoder.decode(result.parentBundleDirectoryExtensionHandle)) + return false; +#endif + if (!decoder.decode(result.shouldUseTestingNetworkSession)) + return false; + if (!decoder.decode(result.urlSchemesRegisteredForCustomProtocols)) + return false; +#if PLATFORM(COCOA) + if (!decoder.decode(result.parentProcessName)) + return false; + if (!decoder.decode(result.uiProcessBundleIdentifier)) + return false; + if (!decoder.decode(result.nsURLCacheMemoryCapacity)) + return false; + if (!decoder.decode(result.nsURLCacheDiskCapacity)) + return false; + if (!decoder.decode(result.httpProxy)) + return false; + if (!decoder.decode(result.httpsProxy)) + return false; +#if TARGET_OS_IPHONE || (PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101100) + if (!IPC::decode(decoder, result.networkATSContext)) + return false; +#endif +#endif + +#if USE(SOUP) || PLATFORM(QT) + if (!decoder.decode(result.cookiePersistentStoragePath)) + return false; + if (!decoder.decode(result.cookiePersistentStorageType)) + return false; + if (!decoder.decodeEnum(result.cookieAcceptPolicy)) + return false; + if (!decoder.decode(result.ignoreTLSErrors)) + return false; + if (!decoder.decode(result.languages)) + return false; +#endif + + return true; +} + +} // namespace WebKit diff --git a/Source/WebKit2/NetworkProcess/NetworkProcessCreationParameters.h b/Source/WebKit2/NetworkProcess/NetworkProcessCreationParameters.h new file mode 100644 index 000000000..dfd188a47 --- /dev/null +++ b/Source/WebKit2/NetworkProcess/NetworkProcessCreationParameters.h @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2012 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. + */ + +#ifndef NetworkProcessCreationParameters_h +#define NetworkProcessCreationParameters_h + +#include "CacheModel.h" +#include "SandboxExtension.h" +#include <wtf/Vector.h> +#include <wtf/text/WTFString.h> + +#if USE(SOUP) || PLATFORM(QT) +#include "HTTPCookieAcceptPolicy.h" +#endif + +namespace IPC { +class ArgumentDecoder; +class ArgumentEncoder; +} + +namespace WebKit { + +struct NetworkProcessCreationParameters { + NetworkProcessCreationParameters(); + + void encode(IPC::ArgumentEncoder&) const; + static bool decode(IPC::ArgumentDecoder&, NetworkProcessCreationParameters&); + + bool privateBrowsingEnabled; + CacheModel cacheModel; + int64_t diskCacheSizeOverride { -1 }; + bool canHandleHTTPSServerTrustEvaluation; + + String diskCacheDirectory; + SandboxExtension::Handle diskCacheDirectoryExtensionHandle; +#if ENABLE(NETWORK_CACHE) + bool shouldEnableNetworkCache; + bool shouldEnableNetworkCacheEfficacyLogging; +#if ENABLE(NETWORK_CACHE_SPECULATIVE_REVALIDATION) + bool shouldEnableNetworkCacheSpeculativeRevalidation; +#endif +#endif +#if ENABLE(SECCOMP_FILTERS) + String cookieStorageDirectory; +#endif +#if PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101100 + Vector<uint8_t> uiProcessCookieStorageIdentifier; +#endif +#if PLATFORM(IOS) + SandboxExtension::Handle cookieStorageDirectoryExtensionHandle; + SandboxExtension::Handle containerCachesDirectoryExtensionHandle; + SandboxExtension::Handle parentBundleDirectoryExtensionHandle; +#endif + bool shouldUseTestingNetworkSession; + + Vector<String> urlSchemesRegisteredForCustomProtocols; + +#if PLATFORM(COCOA) + String parentProcessName; + String uiProcessBundleIdentifier; + uint64_t nsURLCacheMemoryCapacity; + uint64_t nsURLCacheDiskCapacity; + + String httpProxy; + String httpsProxy; +#if TARGET_OS_IPHONE || (PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101100) + RetainPtr<CFDataRef> networkATSContext; +#endif +#endif + +#if USE(SOUP) || PLATFORM(QT) + String cookiePersistentStoragePath; + uint32_t cookiePersistentStorageType; + HTTPCookieAcceptPolicy cookieAcceptPolicy; + bool ignoreTLSErrors; + Vector<String> languages; +#endif +}; + +} // namespace WebKit + +#endif // NetworkProcessCreationParameters_h diff --git a/Source/WebKit2/NetworkProcess/NetworkProcessPlatformStrategies.cpp b/Source/WebKit2/NetworkProcess/NetworkProcessPlatformStrategies.cpp index dcf758877..2f038ebc7 100644 --- a/Source/WebKit2/NetworkProcess/NetworkProcessPlatformStrategies.cpp +++ b/Source/WebKit2/NetworkProcess/NetworkProcessPlatformStrategies.cpp @@ -27,6 +27,7 @@ #include "NetworkProcessPlatformStrategies.h" #include <WebCore/BlobRegistryImpl.h> +#include <wtf/NeverDestroyed.h> using namespace WebCore; @@ -34,68 +35,33 @@ namespace WebKit { void NetworkProcessPlatformStrategies::initialize() { - DEFINE_STATIC_LOCAL(NetworkProcessPlatformStrategies, platformStrategies, ()); - setPlatformStrategies(&platformStrategies); + static NeverDestroyed<NetworkProcessPlatformStrategies> platformStrategies; + setPlatformStrategies(&platformStrategies.get()); } CookiesStrategy* NetworkProcessPlatformStrategies::createCookiesStrategy() { - return 0; -} - -DatabaseStrategy* NetworkProcessPlatformStrategies::createDatabaseStrategy() -{ - return 0; + return nullptr; } LoaderStrategy* NetworkProcessPlatformStrategies::createLoaderStrategy() { - return this; + return nullptr; } PasteboardStrategy* NetworkProcessPlatformStrategies::createPasteboardStrategy() { - return 0; + return nullptr; } PluginStrategy* NetworkProcessPlatformStrategies::createPluginStrategy() { - return 0; -} - -SharedWorkerStrategy* NetworkProcessPlatformStrategies::createSharedWorkerStrategy() -{ - return 0; -} - -StorageStrategy* NetworkProcessPlatformStrategies::createStorageStrategy() -{ - return 0; -} - -VisitedLinkStrategy* NetworkProcessPlatformStrategies::createVisitedLinkStrategy() -{ - return 0; -} - -ResourceLoadScheduler* NetworkProcessPlatformStrategies::resourceLoadScheduler() -{ - ASSERT_NOT_REACHED(); - return 0; + return nullptr; } -void NetworkProcessPlatformStrategies::loadResourceSynchronously(NetworkingContext*, unsigned long resourceLoadIdentifier, const ResourceRequest&, StoredCredentials, ClientCredentialPolicy, ResourceError&, ResourceResponse&, Vector<char>& data) -{ - ASSERT_NOT_REACHED(); -} - -#if ENABLE(BLOB) BlobRegistry* NetworkProcessPlatformStrategies::createBlobRegistry() { return new BlobRegistryImpl; } -#endif - - } diff --git a/Source/WebKit2/NetworkProcess/NetworkProcessPlatformStrategies.h b/Source/WebKit2/NetworkProcess/NetworkProcessPlatformStrategies.h index 818ccebb1..b6c0b6102 100644 --- a/Source/WebKit2/NetworkProcess/NetworkProcessPlatformStrategies.h +++ b/Source/WebKit2/NetworkProcess/NetworkProcessPlatformStrategies.h @@ -31,27 +31,17 @@ namespace WebKit { -class NetworkProcessPlatformStrategies : public WebCore::PlatformStrategies, private WebCore::LoaderStrategy { +class NetworkProcessPlatformStrategies : public WebCore::PlatformStrategies { public: static void initialize(); private: // WebCore::PlatformStrategies - virtual WebCore::CookiesStrategy* createCookiesStrategy() OVERRIDE; - virtual WebCore::DatabaseStrategy* createDatabaseStrategy() OVERRIDE; - virtual WebCore::LoaderStrategy* createLoaderStrategy() OVERRIDE; - virtual WebCore::PasteboardStrategy* createPasteboardStrategy() OVERRIDE; - virtual WebCore::PluginStrategy* createPluginStrategy() OVERRIDE; - virtual WebCore::SharedWorkerStrategy* createSharedWorkerStrategy() OVERRIDE; - virtual WebCore::StorageStrategy* createStorageStrategy() OVERRIDE; - virtual WebCore::VisitedLinkStrategy* createVisitedLinkStrategy() OVERRIDE; - - // WebCore::LoaderStrategy - virtual WebCore::ResourceLoadScheduler* resourceLoadScheduler() OVERRIDE; - virtual void loadResourceSynchronously(WebCore::NetworkingContext*, unsigned long resourceLoadIdentifier, const WebCore::ResourceRequest&, WebCore::StoredCredentials, WebCore::ClientCredentialPolicy, WebCore::ResourceError&, WebCore::ResourceResponse&, Vector<char>& data) OVERRIDE; -#if ENABLE(BLOB) - virtual WebCore::BlobRegistry* createBlobRegistry() OVERRIDE; -#endif + WebCore::CookiesStrategy* createCookiesStrategy() override; + WebCore::LoaderStrategy* createLoaderStrategy() override; + WebCore::PasteboardStrategy* createPasteboardStrategy() override; + WebCore::PluginStrategy* createPluginStrategy() override; + WebCore::BlobRegistry* createBlobRegistry() override; }; } // namespace WebKit diff --git a/Source/WebKit2/NetworkProcess/EntryPoint/mac/LegacyProcess/NetworkProcessMain.mm b/Source/WebKit2/NetworkProcess/NetworkProcessSupplement.h index 87b9e4000..e2b46b391 100644 --- a/Source/WebKit2/NetworkProcess/EntryPoint/mac/LegacyProcess/NetworkProcessMain.mm +++ b/Source/WebKit2/NetworkProcess/NetworkProcessSupplement.h @@ -23,21 +23,22 @@ * THE POSSIBILITY OF SUCH DAMAGE. */ -#import "config.h" +#ifndef NetworkProcessSupplement_h +#define NetworkProcessSupplement_h -#if ENABLE(NETWORK_PROCESS) +#include "ChildProcessSupplement.h" -#import "ChildProcessEntryPoint.h" -#import "NetworkProcess.h" -#import "WKBase.h" +namespace WebKit { -using namespace WebKit; +struct NetworkProcessCreationParameters; -extern "C" WK_EXPORT int NetworkProcessMain(int argc, char** argv); +class NetworkProcessSupplement : public ChildProcessSupplement { +public: + virtual void initialize(const NetworkProcessCreationParameters&) + { + } +}; -int NetworkProcessMain(int argc, char** argv) -{ - return ChildProcessMain<NetworkProcess, ChildProcessMainDelegate>(argc, argv); -} +} // namespace WebKit -#endif // ENABLE(NETWORK_PROCESS) +#endif // NetworkProcessSupplement_h diff --git a/Source/WebKit2/NetworkProcess/NetworkResourceLoadParameters.cpp b/Source/WebKit2/NetworkProcess/NetworkResourceLoadParameters.cpp new file mode 100644 index 000000000..b56b2f7d8 --- /dev/null +++ b/Source/WebKit2/NetworkProcess/NetworkResourceLoadParameters.cpp @@ -0,0 +1,160 @@ +/* + * Copyright (C) 2012 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 "NetworkResourceLoadParameters.h" + +#include "ArgumentCoders.h" +#include "DataReference.h" +#include "WebCoreArgumentCoders.h" + +using namespace WebCore; + +namespace WebKit { + +NetworkResourceLoadParameters::NetworkResourceLoadParameters() + : identifier(0) + , webPageID(0) + , webFrameID(0) + , sessionID(SessionID::emptySessionID()) + , contentSniffingPolicy(SniffContent) + , allowStoredCredentials(DoNotAllowStoredCredentials) + , clientCredentialPolicy(DoNotAskClientForAnyCredentials) + , shouldClearReferrerOnHTTPSToHTTPRedirect(true) + , defersLoading(false) + , needsCertificateInfo(false) + , maximumBufferingTime(0_ms) +{ +} + +void NetworkResourceLoadParameters::encode(IPC::ArgumentEncoder& encoder) const +{ + encoder << identifier; + encoder << webPageID; + encoder << webFrameID; + encoder << sessionID; + encoder << request; + + encoder << static_cast<bool>(request.httpBody()); + if (request.httpBody()) { + request.httpBody()->encode(encoder); + + const Vector<FormDataElement>& elements = request.httpBody()->elements(); + size_t fileCount = 0; + for (size_t i = 0, count = elements.size(); i < count; ++i) { + if (elements[i].m_type == FormDataElement::Type::EncodedFile) + ++fileCount; + } + + SandboxExtension::HandleArray requestBodySandboxExtensions; + requestBodySandboxExtensions.allocate(fileCount); + size_t extensionIndex = 0; + for (size_t i = 0, count = elements.size(); i < count; ++i) { + const FormDataElement& element = elements[i]; + if (element.m_type == FormDataElement::Type::EncodedFile) { + const String& path = element.m_shouldGenerateFile ? element.m_generatedFilename : element.m_filename; + SandboxExtension::createHandle(path, SandboxExtension::ReadOnly, requestBodySandboxExtensions[extensionIndex++]); + } + } + encoder << requestBodySandboxExtensions; + } + + if (request.url().isLocalFile()) { + SandboxExtension::Handle requestSandboxExtension; + SandboxExtension::createHandle(request.url().fileSystemPath(), SandboxExtension::ReadOnly, requestSandboxExtension); + encoder << requestSandboxExtension; + } + + encoder.encodeEnum(contentSniffingPolicy); + encoder.encodeEnum(allowStoredCredentials); + encoder.encodeEnum(clientCredentialPolicy); + encoder << shouldClearReferrerOnHTTPSToHTTPRedirect; + encoder << defersLoading; + encoder << needsCertificateInfo; + encoder << maximumBufferingTime; +} + +bool NetworkResourceLoadParameters::decode(IPC::ArgumentDecoder& decoder, NetworkResourceLoadParameters& result) +{ + if (!decoder.decode(result.identifier)) + return false; + + if (!decoder.decode(result.webPageID)) + return false; + + if (!decoder.decode(result.webFrameID)) + return false; + + if (!decoder.decode(result.sessionID)) + return false; + + if (!decoder.decode(result.request)) + return false; + + bool hasHTTPBody; + if (!decoder.decode(hasHTTPBody)) + return false; + + if (hasHTTPBody) { + RefPtr<FormData> formData = FormData::decode(decoder); + if (!formData) + return false; + result.request.setHTTPBody(WTFMove(formData)); + + SandboxExtension::HandleArray requestBodySandboxExtensionHandles; + if (!decoder.decode(requestBodySandboxExtensionHandles)) + return false; + for (size_t i = 0; i < requestBodySandboxExtensionHandles.size(); ++i) { + if (RefPtr<SandboxExtension> extension = SandboxExtension::create(requestBodySandboxExtensionHandles[i])) + result.requestBodySandboxExtensions.append(extension.release()); + } + } + + if (result.request.url().isLocalFile()) { + SandboxExtension::Handle resourceSandboxExtensionHandle; + if (!decoder.decode(resourceSandboxExtensionHandle)) + return false; + result.resourceSandboxExtension = SandboxExtension::create(resourceSandboxExtensionHandle); + } + + if (!decoder.decodeEnum(result.contentSniffingPolicy)) + return false; + if (!decoder.decodeEnum(result.allowStoredCredentials)) + return false; + if (!decoder.decodeEnum(result.clientCredentialPolicy)) + return false; + if (!decoder.decode(result.shouldClearReferrerOnHTTPSToHTTPRedirect)) + return false; + if (!decoder.decode(result.defersLoading)) + return false; + if (!decoder.decode(result.needsCertificateInfo)) + return false; + if (!decoder.decode(result.maximumBufferingTime)) + return false; + + return true; +} + +} // namespace WebKit diff --git a/Source/WebKit2/NetworkProcess/NetworkResourceLoadParameters.h b/Source/WebKit2/NetworkProcess/NetworkResourceLoadParameters.h new file mode 100644 index 000000000..22e10c6af --- /dev/null +++ b/Source/WebKit2/NetworkProcess/NetworkResourceLoadParameters.h @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2012, 2013 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. + */ + +#ifndef NetworkResourceLoadParameters_h +#define NetworkResourceLoadParameters_h + +#include "SandboxExtension.h" +#include <WebCore/ResourceHandle.h> +#include <WebCore/ResourceLoaderOptions.h> +#include <WebCore/ResourceRequest.h> +#include <WebCore/SessionID.h> + +namespace IPC { +class ArgumentDecoder; +class ArgumentEncoder; +} + +namespace WebKit { + +typedef uint64_t ResourceLoadIdentifier; + +class NetworkResourceLoadParameters { +public: + NetworkResourceLoadParameters(); + + void encode(IPC::ArgumentEncoder&) const; + static bool decode(IPC::ArgumentDecoder&, NetworkResourceLoadParameters&); + + ResourceLoadIdentifier identifier; + uint64_t webPageID; + uint64_t webFrameID; + WebCore::SessionID sessionID; + WebCore::ResourceRequest request; + Vector<RefPtr<SandboxExtension>> requestBodySandboxExtensions; // Created automatically for the sender. + RefPtr<SandboxExtension> resourceSandboxExtension; // Created automatically for the sender. + WebCore::ContentSniffingPolicy contentSniffingPolicy; + WebCore::StoredCredentials allowStoredCredentials; + WebCore::ClientCredentialPolicy clientCredentialPolicy; + bool shouldClearReferrerOnHTTPSToHTTPRedirect; + bool defersLoading; + bool needsCertificateInfo; + std::chrono::milliseconds maximumBufferingTime; +}; + +} // namespace WebKit + +#endif // NetworkResourceLoadParameters_h diff --git a/Source/WebKit2/NetworkProcess/NetworkResourceLoadScheduler.cpp b/Source/WebKit2/NetworkProcess/NetworkResourceLoadScheduler.cpp deleted file mode 100644 index 9aebcc699..000000000 --- a/Source/WebKit2/NetworkProcess/NetworkResourceLoadScheduler.cpp +++ /dev/null @@ -1,228 +0,0 @@ -#include "config.h" -#include "NetworkResourceLoadScheduler.h" - -#include "HostRecord.h" -#include "Logging.h" -#include "NetworkProcess.h" -#include "NetworkResourceLoadParameters.h" -#include "NetworkResourceLoader.h" -#include "SyncNetworkResourceLoader.h" -#include <wtf/MainThread.h> -#include <wtf/text/CString.h> - -#if ENABLE(NETWORK_PROCESS) - -using namespace WebCore; - -namespace WebKit { - -static const unsigned maxRequestsInFlightForNonHTTPProtocols = 20; - -NetworkResourceLoadScheduler::NetworkResourceLoadScheduler() - : m_nonHTTPProtocolHost(HostRecord::create(String(), maxRequestsInFlightForNonHTTPProtocols)) - , m_requestTimer(this, &NetworkResourceLoadScheduler::requestTimerFired) - -{ - platformInitializeMaximumHTTPConnectionCountPerHost(); -} - -void NetworkResourceLoadScheduler::scheduleServePendingRequests() -{ - if (!m_requestTimer.isActive()) - m_requestTimer.startOneShot(0); -} - -void NetworkResourceLoadScheduler::requestTimerFired(WebCore::Timer<NetworkResourceLoadScheduler>*) -{ - servePendingRequests(); -} - -void NetworkResourceLoadScheduler::scheduleLoader(PassRefPtr<SchedulableLoader> loader) -{ - ResourceLoadPriority priority = loader->priority(); - const ResourceRequest& resourceRequest = loader->request(); - - LOG(NetworkScheduling, "(NetworkProcess) NetworkResourceLoadScheduler::scheduleLoader resource '%s'", resourceRequest.url().string().utf8().data()); - - HostRecord* host = hostForURL(resourceRequest.url(), CreateIfNotFound); - bool hadRequests = host->hasRequests(); - host->scheduleResourceLoader(loader); - - if (priority > ResourceLoadPriorityLow || !resourceRequest.url().protocolIsInHTTPFamily() || (priority == ResourceLoadPriorityLow && !hadRequests)) { - // Try to request important resources immediately. - host->servePendingRequests(priority); - return; - } - - // Handle asynchronously so early low priority requests don't get scheduled before later high priority ones. - scheduleServePendingRequests(); -} - -HostRecord* NetworkResourceLoadScheduler::hostForURL(const WebCore::KURL& url, CreateHostPolicy createHostPolicy) -{ - if (!url.protocolIsInHTTPFamily()) - return m_nonHTTPProtocolHost.get(); - - m_hosts.checkConsistency(); - String hostName = url.host(); - HostRecord* host = m_hosts.get(hostName); - if (!host && createHostPolicy == CreateIfNotFound) { - RefPtr<HostRecord> newHost = HostRecord::create(hostName, m_maxRequestsInFlightPerHost); - host = newHost.get(); - m_hosts.add(hostName, newHost.release()); - } - - return host; -} - -void NetworkResourceLoadScheduler::removeLoader(SchedulableLoader* loader) -{ - ASSERT(isMainThread()); - ASSERT(loader); - - LOG(NetworkScheduling, "(NetworkProcess) NetworkResourceLoadScheduler::removeLoadIdentifier removing loader %s", loader->request().url().string().utf8().data()); - - HostRecord* host = loader->hostRecord(); - - // Due to a race condition the WebProcess might have messaged the NetworkProcess to remove this identifier - // after the NetworkProcess has already removed it internally. - // In this situation we might not have a HostRecord to clean up. - if (host) - host->removeLoader(loader); - - scheduleServePendingRequests(); -} - -void NetworkResourceLoadScheduler::receivedRedirect(SchedulableLoader* loader, const WebCore::KURL& redirectURL) -{ - ASSERT(isMainThread()); - LOG(NetworkScheduling, "(NetworkProcess) NetworkResourceLoadScheduler::receivedRedirect loader originally for '%s' redirected to '%s'", loader->request().url().string().utf8().data(), redirectURL.string().utf8().data()); - - HostRecord* oldHost = loader->hostRecord(); - - // The load may have been cancelled while the message was in flight from network thread to main thread. - if (!oldHost) - return; - - HostRecord* newHost = hostForURL(redirectURL, CreateIfNotFound); - - if (oldHost->name() == newHost->name()) - return; - - oldHost->removeLoader(loader); - newHost->addLoaderInProgress(loader); -} - -void NetworkResourceLoadScheduler::servePendingRequests(ResourceLoadPriority minimumPriority) -{ - LOG(NetworkScheduling, "(NetworkProcess) NetworkResourceLoadScheduler::servePendingRequests Serving requests for up to %i hosts with minimum priority %i", m_hosts.size(), minimumPriority); - - m_requestTimer.stop(); - - m_nonHTTPProtocolHost->servePendingRequests(minimumPriority); - - m_hosts.checkConsistency(); - Vector<RefPtr<HostRecord>> hostsToServe; - copyValuesToVector(m_hosts, hostsToServe); - - size_t size = hostsToServe.size(); - for (size_t i = 0; i < size; ++i) { - HostRecord* host = hostsToServe[i].get(); - if (host->hasRequests()) - host->servePendingRequests(minimumPriority); - else - m_hosts.remove(host->name()); - } -} - -static bool removeScheduledLoadersCalled = false; - -void NetworkResourceLoadScheduler::removeScheduledLoaders(void* context) -{ - ASSERT(isMainThread()); - ASSERT(removeScheduledLoadersCalled); - - NetworkResourceLoadScheduler* scheduler = static_cast<NetworkResourceLoadScheduler*>(context); - scheduler->removeScheduledLoaders(); -} - -void NetworkResourceLoadScheduler::removeScheduledLoaders() -{ - Vector<RefPtr<SchedulableLoader>> loadersToRemove; - { - MutexLocker locker(m_loadersToRemoveMutex); - loadersToRemove = m_loadersToRemove; - m_loadersToRemove.clear(); - removeScheduledLoadersCalled = false; - } - - for (size_t i = 0; i < loadersToRemove.size(); ++i) - removeLoader(loadersToRemove[i].get()); -} - -void NetworkResourceLoadScheduler::scheduleRemoveLoader(SchedulableLoader* loader) -{ - MutexLocker locker(m_loadersToRemoveMutex); - - m_loadersToRemove.append(loader); - - if (!removeScheduledLoadersCalled) { - removeScheduledLoadersCalled = true; - callOnMainThread(NetworkResourceLoadScheduler::removeScheduledLoaders, this); - } -} - -uint64_t NetworkResourceLoadScheduler::hostsPendingCount() const -{ - uint64_t count = m_nonHTTPProtocolHost->pendingRequestCount() ? 1 : 0; - - HostMap::const_iterator end = m_hosts.end(); - for (HostMap::const_iterator i = m_hosts.begin(); i != end; ++i) { - if (i->value->pendingRequestCount()) - ++count; - } - - return count; -} - -uint64_t NetworkResourceLoadScheduler::loadsPendingCount() const -{ - uint64_t count = m_nonHTTPProtocolHost->pendingRequestCount(); - - HostMap::const_iterator end = m_hosts.end(); - for (HostMap::const_iterator i = m_hosts.begin(); i != end; ++i) - count += i->value->pendingRequestCount(); - - return count; -} - -uint64_t NetworkResourceLoadScheduler::hostsActiveCount() const -{ - uint64_t count = 0; - - if (m_nonHTTPProtocolHost->activeLoadCount()) - count = 1; - - HostMap::const_iterator end = m_hosts.end(); - for (HostMap::const_iterator i = m_hosts.begin(); i != end; ++i) { - if (i->value->activeLoadCount()) - ++count; - } - - return count; -} - -uint64_t NetworkResourceLoadScheduler::loadsActiveCount() const -{ - uint64_t count = m_nonHTTPProtocolHost->activeLoadCount(); - - HostMap::const_iterator end = m_hosts.end(); - for (HostMap::const_iterator i = m_hosts.begin(); i != end; ++i) - count += i->value->activeLoadCount(); - - return count; -} - -} // namespace WebKit - -#endif // ENABLE(NETWORK_PROCESS) diff --git a/Source/WebKit2/NetworkProcess/NetworkResourceLoadScheduler.h b/Source/WebKit2/NetworkProcess/NetworkResourceLoadScheduler.h deleted file mode 100644 index dfe710268..000000000 --- a/Source/WebKit2/NetworkProcess/NetworkResourceLoadScheduler.h +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright (C) 2012 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. - */ - -#ifndef NetworkResourceLoadScheduler_h -#define NetworkResourceLoadScheduler_h - -#include <WebCore/ResourceLoadPriority.h> -#include <WebCore/Timer.h> -#include <wtf/HashMap.h> -#include <wtf/HashSet.h> -#include <wtf/text/StringHash.h> - -#if ENABLE(NETWORK_PROCESS) - -namespace WebCore { -class KURL; -} - -namespace WebKit { - -class HostRecord; -class SchedulableLoader; - -class NetworkResourceLoadScheduler { - WTF_MAKE_NONCOPYABLE(NetworkResourceLoadScheduler); WTF_MAKE_FAST_ALLOCATED; - -public: - NetworkResourceLoadScheduler(); - - // Adds the request to the queue for its host. - void scheduleLoader(PassRefPtr<SchedulableLoader>); - - // Called by the WebProcess when a ResourceLoader is being cleaned up. - void removeLoader(SchedulableLoader*); - - // Called within the NetworkProcess on a background thread when a resource load has finished. - void scheduleRemoveLoader(SchedulableLoader*); - - void receivedRedirect(SchedulableLoader*, const WebCore::KURL& redirectURL); - void servePendingRequests(WebCore::ResourceLoadPriority = WebCore::ResourceLoadPriorityVeryLow); - - // For NetworkProcess statistics reporting. - uint64_t hostsPendingCount() const; - uint64_t loadsPendingCount() const; - uint64_t hostsActiveCount() const; - uint64_t loadsActiveCount() const; - -private: - enum CreateHostPolicy { - CreateIfNotFound, - FindOnly - }; - - HostRecord* hostForURL(const WebCore::KURL&, CreateHostPolicy = FindOnly); - - void scheduleServePendingRequests(); - void requestTimerFired(WebCore::Timer<NetworkResourceLoadScheduler>*); - - void platformInitializeMaximumHTTPConnectionCountPerHost(); - - static void removeScheduledLoaders(void* context); - void removeScheduledLoaders(); - - typedef HashMap<String, RefPtr<HostRecord>, StringHash> HostMap; - HostMap m_hosts; - - typedef HashSet<RefPtr<SchedulableLoader>> SchedulableLoaderSet; - SchedulableLoaderSet m_loaders; - - RefPtr<HostRecord> m_nonHTTPProtocolHost; - - bool m_isSerialLoadingEnabled; - - WebCore::Timer<NetworkResourceLoadScheduler> m_requestTimer; - - Mutex m_loadersToRemoveMutex; - Vector<RefPtr<SchedulableLoader>> m_loadersToRemove; - - unsigned m_maxRequestsInFlightPerHost; -}; - -} // namespace WebKit - -#endif // ENABLE(NETWORK_PROCESS) - -#endif // NetworkResourceLoadScheduler_h diff --git a/Source/WebKit2/NetworkProcess/NetworkResourceLoader.cpp b/Source/WebKit2/NetworkProcess/NetworkResourceLoader.cpp index 5ad12d324..cef04f346 100644 --- a/Source/WebKit2/NetworkProcess/NetworkResourceLoader.cpp +++ b/Source/WebKit2/NetworkProcess/NetworkResourceLoader.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 Apple Inc. All rights reserved. + * Copyright (C) 2012-2015 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -26,325 +26,605 @@ #include "config.h" #include "NetworkResourceLoader.h" -#if ENABLE(NETWORK_PROCESS) - -#include "AuthenticationManager.h" #include "DataReference.h" #include "Logging.h" +#include "NetworkBlobRegistry.h" +#include "NetworkCache.h" #include "NetworkConnectionToWebProcess.h" -#include "NetworkProcess.h" +#include "NetworkLoad.h" #include "NetworkProcessConnectionMessages.h" -#include "NetworkResourceLoadParameters.h" -#include "PlatformCertificateInfo.h" -#include "RemoteNetworkingContext.h" -#include "ShareableResource.h" -#include "SharedMemory.h" #include "WebCoreArgumentCoders.h" -#include "WebErrors.h" #include "WebResourceLoaderMessages.h" -#include <WebCore/NotImplemented.h> -#include <WebCore/ResourceBuffer.h> -#include <WebCore/ResourceHandle.h> +#include <WebCore/BlobDataFileReference.h> +#include <WebCore/CertificateInfo.h> +#include <WebCore/HTTPHeaderNames.h> +#include <WebCore/ProtectionSpace.h> +#include <WebCore/SharedBuffer.h> +#include <WebCore/SynchronousLoaderClient.h> #include <wtf/CurrentTime.h> -#include <wtf/MainThread.h> +#include <wtf/RunLoop.h> using namespace WebCore; namespace WebKit { -NetworkResourceLoader::NetworkResourceLoader(const NetworkResourceLoadParameters& loadParameters, NetworkConnectionToWebProcess* connection) - : SchedulableLoader(loadParameters, connection) - , m_bytesReceived(0) - , m_handleConvertedToDownload(false) -{ - ASSERT(isMainThread()); -} +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; +}; -NetworkResourceLoader::~NetworkResourceLoader() +static void sendReplyToSynchronousRequest(NetworkResourceLoader::SynchronousLoadData& data, const SharedBuffer* buffer) { - ASSERT(isMainThread()); - ASSERT(!m_handle); + 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; } -void NetworkResourceLoader::start() +NetworkResourceLoader::NetworkResourceLoader(const NetworkResourceLoadParameters& parameters, NetworkConnectionToWebProcess& connection, RefPtr<Messages::NetworkConnectionToWebProcess::PerformSynchronousLoad::DelayedReply>&& synchronousReply) + : m_parameters(parameters) + , m_connection(connection) + , m_defersLoading(parameters.defersLoading) + , m_bufferingTimer(*this, &NetworkResourceLoader::bufferingTimerFired) { - ASSERT(isMainThread()); + 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 == DoNotAskClientForAnyCredentials); + + 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)); + } + } - // Explicit ref() balanced by a deref() in NetworkResourceLoader::resourceHandleStopped() - ref(); - - // FIXME (NetworkProcess): Create RemoteNetworkingContext with actual settings. - m_networkingContext = RemoteNetworkingContext::create(false, false, inPrivateBrowsingMode(), shouldClearReferrerOnHTTPSToHTTPRedirect()); + if (originalRequest().url().protocolIsBlob()) { + ASSERT(!m_parameters.resourceSandboxExtension); + m_fileReferences.appendVector(NetworkBlobRegistry::singleton().filesInBlob(connection, originalRequest().url())); + } - consumeSandboxExtensions(); + if (synchronousReply) + m_synchronousLoadData = std::make_unique<SynchronousLoadData>(WTFMove(synchronousReply)); +} - // FIXME (NetworkProcess): Pass an actual value for defersLoading - m_handle = ResourceHandle::create(m_networkingContext.get(), request(), this, false /* defersLoading */, contentSniffingPolicy() == SniffContent); +NetworkResourceLoader::~NetworkResourceLoader() +{ + ASSERT(RunLoop::isMain()); + ASSERT(!m_networkLoad); + ASSERT(!isSynchronous() || !m_synchronousLoadData->delayedReply); } -void NetworkResourceLoader::cleanup() +#if ENABLE(NETWORK_CACHE) +bool NetworkResourceLoader::canUseCache(const ResourceRequest& request) const { - ASSERT(isMainThread()); + if (!NetworkCache::singleton().isEnabled()) + return false; + if (sessionID().isEphemeral()) + return false; + if (!request.url().protocolIsInHTTPFamily()) + return false; + + return true; +} - invalidateSandboxExtensions(); +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; +} +#endif - if (FormData* formData = request().httpBody()) - formData->removeGeneratedFilesIfNeeded(); +bool NetworkResourceLoader::isSynchronous() const +{ + return !!m_synchronousLoadData; +} - // Tell the scheduler about this finished loader soon so it can start more network requests. - NetworkProcess::shared().networkResourceLoadScheduler().scheduleRemoveLoader(this); +void NetworkResourceLoader::start() +{ + ASSERT(RunLoop::isMain()); + + if (m_defersLoading) + return; - if (m_handle) { - // Explicit deref() balanced by a ref() in NetworkResourceLoader::start() - // This might cause the NetworkResourceLoader to be destroyed and therefore we do it last. - m_handle = 0; - deref(); +#if ENABLE(NETWORK_CACHE) + if (canUseCache(originalRequest())) { + retrieveCacheEntry(originalRequest()); + return; } +#endif + + startNetworkLoad(originalRequest()); } -template<typename U> bool NetworkResourceLoader::sendAbortingOnFailure(const U& message, unsigned messageSendFlags) +#if ENABLE(NETWORK_CACHE) +void NetworkResourceLoader::retrieveCacheEntry(const ResourceRequest& request) { - bool result = messageSenderConnection()->send(message, messageSenderDestinationID(), messageSendFlags); - if (!result) - abort(); - return result; + ASSERT(canUseCache(request)); + + RefPtr<NetworkResourceLoader> loader(this); + NetworkCache::singleton().retrieve(request, { m_parameters.webPageID, m_parameters.webFrameID }, [loader, request](std::unique_ptr<NetworkCache::Entry> entry) { + if (loader->hasOneRef()) { + // The loader has been aborted and is only held alive by this lambda. + return; + } + if (!entry) { + loader->startNetworkLoad(request); + return; + } + if (entry->redirectRequest()) { + loader->dispatchWillSendRequestForCacheEntry(WTFMove(entry)); + return; + } + if (loader->m_parameters.needsCertificateInfo && !entry->response().containsCertificateInfo()) { + loader->startNetworkLoad(request); + return; + } + if (entry->needsValidation()) { + loader->validateCacheEntry(WTFMove(entry)); + return; + } + loader->didRetrieveCacheEntry(WTFMove(entry)); + }); } +#endif -void NetworkResourceLoader::didConvertHandleToDownload() +void NetworkResourceLoader::startNetworkLoad(const ResourceRequest& request) { - ASSERT(m_handle); - m_handleConvertedToDownload = true; + consumeSandboxExtensions(); + + if (isSynchronous() || m_parameters.maximumBufferingTime > 0_ms) + m_bufferedData = SharedBuffer::create(); + +#if ENABLE(NETWORK_CACHE) + if (canUseCache(request)) + m_bufferedDataForCache = SharedBuffer::create(); +#endif + + NetworkLoadParameters parameters = m_parameters; + parameters.defersLoading = m_defersLoading; + parameters.request = request; + m_networkLoad = std::make_unique<NetworkLoad>(*this, parameters); } -void NetworkResourceLoader::abort() +void NetworkResourceLoader::setDefersLoading(bool defers) { - ASSERT(isMainThread()); + if (m_defersLoading == defers) + return; + m_defersLoading = defers; - if (m_handle && !m_handleConvertedToDownload) - m_handle->cancel(); + if (m_networkLoad) { + m_networkLoad->setDefersLoading(defers); + return; + } - cleanup(); + if (!m_defersLoading) + start(); } -void NetworkResourceLoader::didReceiveResponseAsync(ResourceHandle* handle, const ResourceResponse& response) +void NetworkResourceLoader::cleanup() { - ASSERT_UNUSED(handle, handle == m_handle); + ASSERT(RunLoop::isMain()); - // FIXME (NetworkProcess): Cache the response. - if (FormData* formData = request().httpBody()) - formData->removeGeneratedFilesIfNeeded(); + m_bufferingTimer.stop(); - sendAbortingOnFailure(Messages::WebResourceLoader::DidReceiveResponseWithCertificateInfo(response, PlatformCertificateInfo(response), isLoadingMainResource())); + invalidateSandboxExtensions(); - // m_handle will be 0 if the request got aborted above. - if (!m_handle) - return; + m_networkLoad = nullptr; + + // This will cause NetworkResourceLoader to be destroyed and therefore we do it last. + m_connection->didCleanupResourceLoader(*this); +} + +void NetworkResourceLoader::didConvertToDownload() +{ + ASSERT(m_networkLoad); + m_didConvertToDownload = true; +#if USE(NETWORK_SESSION) + m_networkLoad = nullptr; +#endif +} - if (!isLoadingMainResource()) { - // For main resources, the web process is responsible for sending back a NetworkResourceLoader::ContinueDidReceiveResponse message. - m_handle->continueDidReceiveResponse(); +void NetworkResourceLoader::abort() +{ + ASSERT(RunLoop::isMain()); + + if (m_networkLoad && !m_didConvertToDownload) { +#if ENABLE(NETWORK_CACHE) + 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()) + NetworkCache::singleton().remove(m_networkLoad->currentRequest()); + } +#endif + m_networkLoad->cancel(); } + + cleanup(); } -void NetworkResourceLoader::didReceiveData(ResourceHandle*, const char* data, int length, int encodedDataLength) +auto NetworkResourceLoader::didReceiveResponse(const ResourceResponse& receivedResponse) -> ShouldContinueDidReceiveResponse { - // The NetworkProcess should never get a didReceiveData callback. - // We should always be using didReceiveBuffer. - ASSERT_NOT_REACHED(); + m_response = receivedResponse; + + // For multipart/x-mixed-replace didReceiveResponseAsync gets called multiple times and buffering would require special handling. + if (!isSynchronous() && m_response.isMultipart()) + m_bufferedData = nullptr; + + bool shouldSendDidReceiveResponse = true; +#if ENABLE(NETWORK_CACHE) + if (m_response.isMultipart()) + m_bufferedDataForCache = nullptr; + + if (m_cacheEntryForValidation) { + bool validationSucceeded = m_response.httpStatusCode() == 304; // 304 Not Modified + if (validationSucceeded) { + NetworkCache::singleton().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; + } + shouldSendDidReceiveResponse = !m_cacheEntryForValidation; +#endif + + bool shouldWaitContinueDidReceiveResponse = originalRequest().requester() == ResourceRequest::Requester::Main; + if (shouldSendDidReceiveResponse) { + if (isSynchronous()) + m_synchronousLoadData->response = m_response; + else { + if (!sendAbortingOnFailure(Messages::WebResourceLoader::DidReceiveResponse(m_response, shouldWaitContinueDidReceiveResponse))) + return ShouldContinueDidReceiveResponse::No; + } + } + + // For main resources, the web process is responsible for sending back a NetworkResourceLoader::ContinueDidReceiveResponse message. + bool shouldContinueDidReceiveResponse = !shouldWaitContinueDidReceiveResponse; +#if ENABLE(NETWORK_CACHE) + shouldContinueDidReceiveResponse = shouldContinueDidReceiveResponse || m_cacheEntryForValidation; +#endif + + return shouldContinueDidReceiveResponse ? ShouldContinueDidReceiveResponse::Yes : ShouldContinueDidReceiveResponse::No; } -void NetworkResourceLoader::didReceiveBuffer(ResourceHandle* handle, PassRefPtr<SharedBuffer> buffer, int encodedDataLength) +void NetworkResourceLoader::didReceiveBuffer(RefPtr<SharedBuffer>&& buffer, int reportedEncodedDataLength) { - ASSERT_UNUSED(handle, handle == m_handle); +#if ENABLE(NETWORK_CACHE) + 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; + } +#endif + // FIXME: At least on OS X Yosemite we always get -1 from the resource handle. + unsigned encodedDataLength = reportedEncodedDataLength >= 0 ? reportedEncodedDataLength : buffer->size(); - // FIXME (NetworkProcess): For the memory cache we'll also need to cache the response data here. - // Such buffering will need to be thread safe, as this callback is happening on a background thread. - m_bytesReceived += buffer->size(); - -#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1090 - ShareableResource::Handle shareableResourceHandle; - tryGetShareableHandleFromSharedBuffer(shareableResourceHandle, buffer.get()); - if (!shareableResourceHandle.isNull()) { - // Since we're delivering this resource by ourselves all at once, we'll abort the resource handle since we don't need anymore callbacks from ResourceHandle. - abort(); - send(Messages::WebResourceLoader::DidReceiveResource(shareableResourceHandle, currentTime())); + if (m_bufferedData) { + m_bufferedData->append(buffer.get()); + m_bufferedDataEncodedDataLength += encodedDataLength; + startBufferingTimerIfNeeded(); return; } -#endif // __MAC_OS_X_VERSION_MIN_REQUIRED >= 1090 - - CoreIPC::DataReference dataReference(reinterpret_cast<const uint8_t*>(buffer->data()), buffer->size()); - sendAbortingOnFailure(Messages::WebResourceLoader::DidReceiveData(dataReference, encodedDataLength)); + sendBufferMaybeAborting(*buffer, encodedDataLength); } -void NetworkResourceLoader::didFinishLoading(ResourceHandle* handle, double finishTime) +void NetworkResourceLoader::didFinishLoading(double finishTime) { - ASSERT_UNUSED(handle, handle == m_handle); +#if ENABLE(NETWORK_CACHE) + if (m_cacheEntryForValidation) { + // 304 Not Modified + ASSERT(m_response.httpStatusCode() == 304); + LOG(NetworkCache, "(NetworkProcess) revalidated"); + didRetrieveCacheEntry(WTFMove(m_cacheEntryForValidation)); + return; + } +#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. + bool shouldContinue = sendBufferMaybeAborting(*m_bufferedData, -1); + if (!shouldContinue) + return; + } + send(Messages::WebResourceLoader::DidFinishResourceLoad(finishTime)); + } + +#if ENABLE(NETWORK_CACHE) + tryStoreAsCacheEntry(); +#endif - // FIXME (NetworkProcess): For the memory cache we'll need to update the finished status of the cached resource here. - // Such bookkeeping will need to be thread safe, as this callback is happening on a background thread. - send(Messages::WebResourceLoader::DidFinishResourceLoad(finishTime)); - cleanup(); } -void NetworkResourceLoader::didFail(ResourceHandle* handle, const ResourceError& error) +void NetworkResourceLoader::didFailLoading(const ResourceError& error) { - ASSERT_UNUSED(handle, handle == m_handle); + ASSERT(!error.isNull()); + +#if ENABLE(NETWORK_CACHE) + m_cacheEntryForValidation = nullptr; +#endif + + if (isSynchronous()) { + m_synchronousLoadData->error = error; + sendReplyToSynchronousRequest(*m_synchronousLoadData, nullptr); + } else if (auto* connection = messageSenderConnection()) + connection->send(Messages::WebResourceLoader::DidFailResourceLoad(error), messageSenderDestinationID(), 0); - // FIXME (NetworkProcess): For the memory cache we'll need to update the finished status of the cached resource here. - // Such bookkeeping will need to be thread safe, as this callback is happening on a background thread. - send(Messages::WebResourceLoader::DidFailResourceLoad(error)); cleanup(); } -void NetworkResourceLoader::willSendRequestAsync(ResourceHandle* handle, const ResourceRequest& request, const ResourceResponse& redirectResponse) +void NetworkResourceLoader::willSendRedirectedRequest(const ResourceRequest& request, const WebCore::ResourceRequest& redirectRequest, const ResourceResponse& redirectResponse) { - ASSERT_UNUSED(handle, handle == m_handle); - - // We only expect to get the willSendRequest callback from ResourceHandle as the result of a redirect. - ASSERT(!redirectResponse.isNull()); - ASSERT(isMainThread()); - - m_suggestedRequestForWillSendRequest = request; + ++m_redirectCount; + + if (isSynchronous()) { + ResourceRequest overridenRequest = redirectRequest; + // FIXME: This needs to be fixed to follow the redirect correctly even for cross-domain requests. + // This includes at least updating host records, and comparing the current request instead of the original request here. + if (!protocolHostAndPortAreEqual(originalRequest().url(), redirectRequest.url())) { + ASSERT(m_synchronousLoadData->error.isNull()); + m_synchronousLoadData->error = SynchronousLoaderClient::platformBadResponseError(); + m_networkLoad->clearCurrentRequest(); + overridenRequest = ResourceRequest(); + } + continueWillSendRequest(overridenRequest); + return; + } + sendAbortingOnFailure(Messages::WebResourceLoader::WillSendRequest(redirectRequest, redirectResponse)); - // This message is DispatchMessageEvenWhenWaitingForSyncReply to avoid a situation where the NetworkProcess is deadlocked waiting for 6 connections - // to complete while the WebProcess is waiting for a 7th to complete. - sendAbortingOnFailure(Messages::WebResourceLoader::WillSendRequest(request, redirectResponse), CoreIPC::DispatchMessageEvenWhenWaitingForSyncReply); +#if ENABLE(NETWORK_CACHE) + if (canUseCachedRedirect(request)) + NetworkCache::singleton().storeRedirect(request, redirectResponse, redirectRequest); +#else + UNUSED_PARAM(request); +#endif } void NetworkResourceLoader::continueWillSendRequest(const ResourceRequest& newRequest) { - m_suggestedRequestForWillSendRequest.updateFromDelegatePreservingOldHTTPBody(newRequest.nsURLRequest(DoNotUpdateHTTPBody)); +#if ENABLE(NETWORK_CACHE) + if (m_isWaitingContinueWillSendRequestForCachedRedirect) { + LOG(NetworkCache, "(NetworkProcess) Retrieving cached redirect"); - RunLoop::main()->dispatch(bind(&NetworkResourceLoadScheduler::receivedRedirect, &NetworkProcess::shared().networkResourceLoadScheduler(), this, m_suggestedRequestForWillSendRequest.url())); - m_handle->continueWillSendRequest(m_suggestedRequestForWillSendRequest); + if (canUseCachedRedirect(newRequest)) + retrieveCacheEntry(newRequest); + else + startNetworkLoad(newRequest); - m_suggestedRequestForWillSendRequest = ResourceRequest(); + m_isWaitingContinueWillSendRequestForCachedRedirect = false; + return; + } +#endif + m_networkLoad->continueWillSendRequest(newRequest); } void NetworkResourceLoader::continueDidReceiveResponse() { // 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_handle) - return; - - m_handle->continueDidReceiveResponse(); + if (m_networkLoad) + m_networkLoad->continueDidReceiveResponse(); } -void NetworkResourceLoader::didSendData(ResourceHandle* handle, unsigned long long bytesSent, unsigned long long totalBytesToBeSent) +void NetworkResourceLoader::didSendData(unsigned long long bytesSent, unsigned long long totalBytesToBeSent) { - ASSERT_UNUSED(handle, handle == m_handle); - - send(Messages::WebResourceLoader::DidSendData(bytesSent, totalBytesToBeSent)); + if (!isSynchronous()) + send(Messages::WebResourceLoader::DidSendData(bytesSent, totalBytesToBeSent)); } -void NetworkResourceLoader::wasBlocked(ResourceHandle* handle) +void NetworkResourceLoader::startBufferingTimerIfNeeded() { - ASSERT_UNUSED(handle, handle == m_handle); - - didFail(handle, WebKit::blockedError(request())); + if (isSynchronous()) + return; + if (m_bufferingTimer.isActive()) + return; + m_bufferingTimer.startOneShot(m_parameters.maximumBufferingTime); } -void NetworkResourceLoader::cannotShowURL(ResourceHandle* handle) +void NetworkResourceLoader::bufferingTimerFired() { - ASSERT_UNUSED(handle, handle == m_handle); + ASSERT(m_bufferedData); + ASSERT(m_networkLoad); - didFail(handle, WebKit::cannotShowURLError(request())); + if (m_bufferedData->isEmpty()) + return; + + IPC::SharedBufferDataReference dataReference(m_bufferedData.get()); + size_t encodedLength = m_bufferedDataEncodedDataLength; + + m_bufferedData = SharedBuffer::create(); + m_bufferedDataEncodedDataLength = 0; + + sendAbortingOnFailure(Messages::WebResourceLoader::DidReceiveData(dataReference, encodedLength)); } -bool NetworkResourceLoader::shouldUseCredentialStorage(ResourceHandle* handle) +bool NetworkResourceLoader::sendBufferMaybeAborting(SharedBuffer& buffer, size_t encodedDataLength) { - ASSERT_UNUSED(handle, handle == m_handle || !m_handle); // m_handle will be 0 if called from ResourceHandle::start(). + ASSERT(!isSynchronous()); - // When the WebProcess is handling loading a client is consulted each time this shouldUseCredentialStorage question is asked. - // In NetworkProcess mode we ask the WebProcess client up front once and then reuse the cached answer. - - // We still need this sync version, because ResourceHandle itself uses it internally, even when the delegate uses an async one. +#if PLATFORM(COCOA) + ShareableResource::Handle shareableResourceHandle; + NetworkResourceLoader::tryGetShareableHandleFromSharedBuffer(shareableResourceHandle, buffer); + if (!shareableResourceHandle.isNull()) { + send(Messages::WebResourceLoader::DidReceiveResource(shareableResourceHandle, currentTime())); + abort(); + return false; + } +#endif - return allowStoredCredentials() == AllowStoredCredentials; + IPC::SharedBufferDataReference dataReference(&buffer); + return sendAbortingOnFailure(Messages::WebResourceLoader::DidReceiveData(dataReference, encodedDataLength)); } -void NetworkResourceLoader::shouldUseCredentialStorageAsync(ResourceHandle* handle) +#if ENABLE(NETWORK_CACHE) +void NetworkResourceLoader::tryStoreAsCacheEntry() { - ASSERT_UNUSED(handle, handle == m_handle); + if (!canUseCache(m_networkLoad->currentRequest())) + return; + if (!m_bufferedDataForCache) + return; - handle->continueShouldUseCredentialStorage(shouldUseCredentialStorage(handle)); + // Keep the connection alive. + RefPtr<NetworkConnectionToWebProcess> connection(&connectionToWebProcess()); + RefPtr<NetworkResourceLoader> loader(this); + NetworkCache::singleton().store(m_networkLoad->currentRequest(), m_response, WTFMove(m_bufferedDataForCache), [loader, connection](NetworkCache::MappedBody& mappedBody) { +#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::didReceiveAuthenticationChallenge(ResourceHandle* handle, const AuthenticationChallenge& challenge) +void NetworkResourceLoader::didRetrieveCacheEntry(std::unique_ptr<NetworkCache::Entry> entry) { - ASSERT_UNUSED(handle, handle == m_handle); - - // FIXME (http://webkit.org/b/115291): Since we go straight to the UI process for authentication we don't get WebCore's - // cross-origin check before asking the client for credentials. - // Therefore we are too permissive in the case where the ClientCredentialPolicy is DoNotAskClientForCrossOriginCredentials. - if (clientCredentialPolicy() == DoNotAskClientForAnyCredentials) { - challenge.authenticationClient()->receivedRequestToContinueWithoutCredential(challenge); - return; + if (isSynchronous()) { + m_synchronousLoadData->response = entry->response(); + sendReplyToSynchronousRequest(*m_synchronousLoadData, entry->buffer()); + } else { + bool needsContinueDidReceiveResponseMessage = originalRequest().requester() == ResourceRequest::Requester::Main; + sendAbortingOnFailure(Messages::WebResourceLoader::DidReceiveResponse(entry->response(), needsContinueDidReceiveResponseMessage)); + +#if ENABLE(SHAREABLE_RESOURCE) + if (!entry->shareableResourceHandle().isNull()) + send(Messages::WebResourceLoader::DidReceiveResource(entry->shareableResourceHandle(), currentTime())); + else { +#endif + bool shouldContinue = sendBufferMaybeAborting(*entry->buffer(), entry->buffer()->size()); + if (!shouldContinue) + return; + send(Messages::WebResourceLoader::DidFinishResourceLoad(currentTime())); +#if ENABLE(SHAREABLE_RESOURCE) + } +#endif } - NetworkProcess::shared().authenticationManager().didReceiveAuthenticationChallenge(webPageID(), webFrameID(), challenge); + cleanup(); } -void NetworkResourceLoader::didCancelAuthenticationChallenge(ResourceHandle* handle, const AuthenticationChallenge& challenge) +void NetworkResourceLoader::validateCacheEntry(std::unique_ptr<NetworkCache::Entry> entry) { - ASSERT_UNUSED(handle, handle == m_handle); + 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); - // This function is probably not needed (see <rdar://problem/8960124>). - notImplemented(); + startNetworkLoad(revalidationRequest); } -CoreIPC::Connection* NetworkResourceLoader::messageSenderConnection() +void NetworkResourceLoader::dispatchWillSendRequestForCacheEntry(std::unique_ptr<NetworkCache::Entry> entry) { - return connectionToWebProcess()->connection(); + ASSERT(entry->redirectRequest()); + LOG(NetworkCache, "(NetworkProcess) Executing cached redirect"); + + ++m_redirectCount; + sendAbortingOnFailure(Messages::WebResourceLoader::WillSendRequest(*entry->redirectRequest(), entry->response())); + m_isWaitingContinueWillSendRequestForCachedRedirect = true; } +#endif -uint64_t NetworkResourceLoader::messageSenderDestinationID() +IPC::Connection* NetworkResourceLoader::messageSenderConnection() { - return identifier(); + return connectionToWebProcess().connection(); } -#if USE(PROTECTION_SPACE_AUTH_CALLBACK) -void NetworkResourceLoader::canAuthenticateAgainstProtectionSpaceAsync(ResourceHandle* handle, const ProtectionSpace& protectionSpace) +void NetworkResourceLoader::consumeSandboxExtensions() { - ASSERT(isMainThread()); - ASSERT_UNUSED(handle, handle == m_handle); + ASSERT(!m_didConsumeSandboxExtensions); + + for (auto& extension : m_parameters.requestBodySandboxExtensions) + extension->consume(); - // This message is DispatchMessageEvenWhenWaitingForSyncReply to avoid a situation where the NetworkProcess is deadlocked - // waiting for 6 connections to complete while the WebProcess is waiting for a 7th to complete. - sendAbortingOnFailure(Messages::WebResourceLoader::CanAuthenticateAgainstProtectionSpace(protectionSpace), CoreIPC::DispatchMessageEvenWhenWaitingForSyncReply); + if (auto& extension = m_parameters.resourceSandboxExtension) + extension->consume(); + + for (auto& fileReference : m_fileReferences) + fileReference->prepareForFileAccess(); + + m_didConsumeSandboxExtensions = true; } -void NetworkResourceLoader::continueCanAuthenticateAgainstProtectionSpace(bool result) +void NetworkResourceLoader::invalidateSandboxExtensions() { - m_handle->continueCanAuthenticateAgainstProtectionSpace(result); -} + 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; + } -#endif + m_fileReferences.clear(); +} -#if USE(NETWORK_CFDATA_ARRAY_CALLBACK) -bool NetworkResourceLoader::supportsDataArray() +template<typename T> +bool NetworkResourceLoader::sendAbortingOnFailure(T&& message, unsigned messageSendFlags) { - notImplemented(); - return false; + bool result = messageSenderConnection()->send(std::forward<T>(message), messageSenderDestinationID(), messageSendFlags); + if (!result) + abort(); + return result; } -void NetworkResourceLoader::didReceiveDataArray(ResourceHandle*, CFArrayRef) +void NetworkResourceLoader::canAuthenticateAgainstProtectionSpaceAsync(const ProtectionSpace& protectionSpace) { - ASSERT_NOT_REACHED(); - notImplemented(); -} +#if USE(PROTECTION_SPACE_AUTH_CALLBACK) + sendAbortingOnFailure(Messages::WebResourceLoader::CanAuthenticateAgainstProtectionSpace(protectionSpace)); +#else + UNUSED_PARAM(protectionSpace); #endif +} -#if PLATFORM(MAC) -void NetworkResourceLoader::willStopBufferingData(ResourceHandle*, const char*, int) +#if USE(PROTECTION_SPACE_AUTH_CALLBACK) +void NetworkResourceLoader::continueCanAuthenticateAgainstProtectionSpace(bool result) { - notImplemented(); + m_networkLoad->continueCanAuthenticateAgainstProtectionSpace(result); } -#endif // PLATFORM(MAC) +#endif } // namespace WebKit - -#endif // ENABLE(NETWORK_PROCESS) diff --git a/Source/WebKit2/NetworkProcess/NetworkResourceLoader.h b/Source/WebKit2/NetworkProcess/NetworkResourceLoader.h index 798cccf20..d9810dcb4 100644 --- a/Source/WebKit2/NetworkProcess/NetworkResourceLoader.h +++ b/Source/WebKit2/NetworkProcess/NetworkResourceLoader.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 Apple Inc. All rights reserved. + * Copyright (C) 2012-2015 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -26,116 +26,146 @@ #ifndef NetworkResourceLoader_h #define NetworkResourceLoader_h -#if ENABLE(NETWORK_PROCESS) - #include "MessageSender.h" -#include "SchedulableLoader.h" +#include "NetworkConnectionToWebProcessMessages.h" +#include "NetworkLoadClient.h" +#include "NetworkResourceLoadParameters.h" #include "ShareableResource.h" -#include <WebCore/ResourceHandleClient.h> -#include <WebCore/RunLoop.h> - -typedef const struct _CFCachedURLResponse* CFCachedURLResponseRef; +#include <WebCore/Timer.h> +#include <wtf/Optional.h> namespace WebCore { -class ResourceBuffer; -class ResourceHandle; +class BlobDataFileReference; class ResourceRequest; } namespace WebKit { class NetworkConnectionToWebProcess; -class RemoteNetworkingContext; +class NetworkLoad; +class SandboxExtension; -class NetworkResourceLoader : public SchedulableLoader, public WebCore::ResourceHandleClient, public CoreIPC::MessageSender { +namespace NetworkCache { +class Entry; +} + +class NetworkResourceLoader final : public RefCounted<NetworkResourceLoader>, public NetworkLoadClient, public IPC::MessageSender { public: - static RefPtr<NetworkResourceLoader> create(const NetworkResourceLoadParameters& parameters, NetworkConnectionToWebProcess* connection) + static Ref<NetworkResourceLoader> create(const NetworkResourceLoadParameters& parameters, NetworkConnectionToWebProcess& connection, RefPtr<Messages::NetworkConnectionToWebProcess::PerformSynchronousLoad::DelayedReply>&& reply = nullptr) { - return adoptRef(new NetworkResourceLoader(parameters, connection)); + return adoptRef(*new NetworkResourceLoader(parameters, connection, WTFMove(reply))); } - - ~NetworkResourceLoader(); - - WebCore::ResourceHandle* handle() const { return m_handle.get(); } - void didConvertHandleToDownload(); - - virtual void start() OVERRIDE; - virtual void abort() OVERRIDE; - - // ResourceHandleClient methods - virtual void willSendRequestAsync(WebCore::ResourceHandle*, const WebCore::ResourceRequest&, const WebCore::ResourceResponse& redirectResponse) OVERRIDE; - virtual void didSendData(WebCore::ResourceHandle*, unsigned long long bytesSent, unsigned long long totalBytesToBeSent) OVERRIDE; - virtual void didReceiveResponseAsync(WebCore::ResourceHandle*, const WebCore::ResourceResponse&) OVERRIDE; - virtual void didReceiveData(WebCore::ResourceHandle*, const char*, int, int encodedDataLength) OVERRIDE; - virtual void didReceiveBuffer(WebCore::ResourceHandle*, PassRefPtr<WebCore::SharedBuffer>, int encodedDataLength) OVERRIDE; - virtual void didFinishLoading(WebCore::ResourceHandle*, double finishTime) OVERRIDE; - virtual void didFail(WebCore::ResourceHandle*, const WebCore::ResourceError&) OVERRIDE; - virtual void wasBlocked(WebCore::ResourceHandle*) OVERRIDE; - virtual void cannotShowURL(WebCore::ResourceHandle*) OVERRIDE; - virtual bool shouldUseCredentialStorage(WebCore::ResourceHandle*) OVERRIDE; - virtual void shouldUseCredentialStorageAsync(WebCore::ResourceHandle*) OVERRIDE; - virtual void didReceiveAuthenticationChallenge(WebCore::ResourceHandle*, const WebCore::AuthenticationChallenge&) OVERRIDE; - virtual void didCancelAuthenticationChallenge(WebCore::ResourceHandle*, const WebCore::AuthenticationChallenge&) OVERRIDE; - virtual bool usesAsyncCallbacks() OVERRIDE { return true; } + virtual ~NetworkResourceLoader(); -#if USE(PROTECTION_SPACE_AUTH_CALLBACK) - virtual void canAuthenticateAgainstProtectionSpaceAsync(WebCore::ResourceHandle*, const WebCore::ProtectionSpace&) OVERRIDE; -#endif + const WebCore::ResourceRequest& originalRequest() const { return m_parameters.request; } -#if USE(NETWORK_CFDATA_ARRAY_CALLBACK) - virtual bool supportsDataArray() OVERRIDE; - virtual void didReceiveDataArray(WebCore::ResourceHandle*, CFArrayRef) OVERRIDE; -#endif + NetworkLoad* networkLoad() const { return m_networkLoad.get(); } + + void start(); + void abort(); -#if PLATFORM(MAC) + void setDefersLoading(bool); + +#if PLATFORM(COCOA) static size_t fileBackedResourceMinimumSize(); - virtual void willCacheResponseAsync(WebCore::ResourceHandle*, NSCachedURLResponse *) OVERRIDE; - virtual void willStopBufferingData(WebCore::ResourceHandle*, const char*, int) OVERRIDE; -#endif // PLATFORM(MAC) + virtual void willCacheResponseAsync(CFCachedURLResponseRef) override; +#endif // Message handlers. - void didReceiveNetworkResourceLoaderMessage(CoreIPC::Connection*, CoreIPC::MessageDecoder&); + void didReceiveNetworkResourceLoaderMessage(IPC::Connection&, IPC::MessageDecoder&); -#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1090 +#if PLATFORM(COCOA) static void tryGetShareableHandleFromCFURLCachedResponse(ShareableResource::Handle&, CFCachedURLResponseRef); + static void tryGetShareableHandleFromSharedBuffer(ShareableResource::Handle&, WebCore::SharedBuffer&); +#endif + +#if USE(PROTECTION_SPACE_AUTH_CALLBACK) + void continueCanAuthenticateAgainstProtectionSpace(bool); #endif + void continueWillSendRequest(const WebCore::ResourceRequest& newRequest); + + WebCore::SharedBuffer* bufferedData() { return m_bufferedData.get(); } + const WebCore::ResourceResponse& response() const { return m_response; } + + NetworkConnectionToWebProcess& connectionToWebProcess() { return m_connection; } + WebCore::SessionID sessionID() const { return m_parameters.sessionID; } + ResourceLoadIdentifier identifier() const { return m_parameters.identifier; } + + struct SynchronousLoadData; + + // NetworkLoadClient. + virtual void didSendData(unsigned long long bytesSent, unsigned long long totalBytesToBeSent) override; + virtual void canAuthenticateAgainstProtectionSpaceAsync(const WebCore::ProtectionSpace&) override; + virtual bool isSynchronous() const override; + virtual void willSendRedirectedRequest(const WebCore::ResourceRequest&, const WebCore::ResourceRequest& redirectRequest, const WebCore::ResourceResponse& redirectResponse) override; + virtual ShouldContinueDidReceiveResponse didReceiveResponse(const WebCore::ResourceResponse&) override; + virtual void didReceiveBuffer(RefPtr<WebCore::SharedBuffer>&&, int reportedEncodedDataLength) override; + virtual void didFinishLoading(double finishTime) override; + virtual void didFailLoading(const WebCore::ResourceError&) override; + virtual void didConvertToDownload() override; private: - NetworkResourceLoader(const NetworkResourceLoadParameters&, NetworkConnectionToWebProcess*); + NetworkResourceLoader(const NetworkResourceLoadParameters&, NetworkConnectionToWebProcess&, RefPtr<Messages::NetworkConnectionToWebProcess::PerformSynchronousLoad::DelayedReply>&&); - // CoreIPC::MessageSender - virtual CoreIPC::Connection* messageSenderConnection() OVERRIDE; - virtual uint64_t messageSenderDestinationID() OVERRIDE; + // IPC::MessageSender + virtual IPC::Connection* messageSenderConnection() override; + virtual uint64_t messageSenderDestinationID() override { return m_parameters.identifier; } - void continueWillSendRequest(const WebCore::ResourceRequest& newRequest); - void continueDidReceiveResponse(); -#if USE(PROTECTION_SPACE_AUTH_CALLBACK) - void continueCanAuthenticateAgainstProtectionSpace(bool); +#if ENABLE(NETWORK_CACHE) + bool canUseCache(const WebCore::ResourceRequest&) const; + bool canUseCachedRedirect(const WebCore::ResourceRequest&) const; + + void tryStoreAsCacheEntry(); + void retrieveCacheEntry(const WebCore::ResourceRequest&); + void didRetrieveCacheEntry(std::unique_ptr<NetworkCache::Entry>); + void validateCacheEntry(std::unique_ptr<NetworkCache::Entry>); + void dispatchWillSendRequestForCacheEntry(std::unique_ptr<NetworkCache::Entry>); #endif + void startNetworkLoad(const WebCore::ResourceRequest&); + void continueDidReceiveResponse(); + void cleanup(); void platformDidReceiveResponse(const WebCore::ResourceResponse&); - template<typename U> bool sendAbortingOnFailure(const U& message, unsigned messageSendFlags = 0); + void startBufferingTimerIfNeeded(); + void bufferingTimerFired(); + bool sendBufferMaybeAborting(WebCore::SharedBuffer&, size_t encodedDataLength); + + void consumeSandboxExtensions(); + void invalidateSandboxExtensions(); + + template<typename T> bool sendAbortingOnFailure(T&& message, unsigned messageSendFlags = 0); - RefPtr<RemoteNetworkingContext> m_networkingContext; - RefPtr<WebCore::ResourceHandle> m_handle; + const NetworkResourceLoadParameters m_parameters; - // Keep the suggested request around while asynchronously asking to update it, because some parts of the request don't survive IPC. - WebCore::ResourceRequest m_suggestedRequestForWillSendRequest; + Ref<NetworkConnectionToWebProcess> m_connection; - uint64_t m_bytesReceived; + std::unique_ptr<NetworkLoad> m_networkLoad; - bool m_handleConvertedToDownload; + WebCore::ResourceResponse m_response; -#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1090 - static void tryGetShareableHandleFromSharedBuffer(ShareableResource::Handle&, WebCore::SharedBuffer*); + size_t m_bytesReceived { 0 }; + size_t m_bufferedDataEncodedDataLength { 0 }; + RefPtr<WebCore::SharedBuffer> m_bufferedData; + unsigned m_redirectCount { 0 }; + + std::unique_ptr<SynchronousLoadData> m_synchronousLoadData; + Vector<RefPtr<WebCore::BlobDataFileReference>> m_fileReferences; + + bool m_didConvertToDownload { false }; + bool m_didConsumeSandboxExtensions { false }; + bool m_defersLoading { false }; + + WebCore::Timer m_bufferingTimer; +#if ENABLE(NETWORK_CACHE) + RefPtr<WebCore::SharedBuffer> m_bufferedDataForCache; + std::unique_ptr<NetworkCache::Entry> m_cacheEntryForValidation; + bool m_isWaitingContinueWillSendRequestForCachedRedirect { false }; #endif }; } // namespace WebKit -#endif // ENABLE(NETWORK_PROCESS) - #endif // NetworkResourceLoader_h diff --git a/Source/WebKit2/NetworkProcess/NetworkResourceLoader.messages.in b/Source/WebKit2/NetworkProcess/NetworkResourceLoader.messages.in index 00706af43..ebb8d7cd3 100644 --- a/Source/WebKit2/NetworkProcess/NetworkResourceLoader.messages.in +++ b/Source/WebKit2/NetworkProcess/NetworkResourceLoader.messages.in @@ -24,5 +24,7 @@ messages -> NetworkResourceLoader LegacyReceiver { ContinueWillSendRequest(WebCore::ResourceRequest request) ContinueDidReceiveResponse() +#if USE(PROTECTION_SPACE_AUTH_CALLBACK) ContinueCanAuthenticateAgainstProtectionSpace(bool canAuthenticate) +#endif } diff --git a/Source/WebKit2/NetworkProcess/NetworkSession.h b/Source/WebKit2/NetworkProcess/NetworkSession.h new file mode 100644 index 000000000..083ed96fe --- /dev/null +++ b/Source/WebKit2/NetworkProcess/NetworkSession.h @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2015 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. + */ + +#ifndef NetworkSession_h +#define NetworkSession_h + +#if PLATFORM(COCOA) +OBJC_CLASS NSURLSession; +OBJC_CLASS NSOperationQueue; +OBJC_CLASS WKNetworkSessionDelegate; +#endif + +#include "DownloadID.h" +#include "NetworkDataTask.h" +#include <WebCore/SessionID.h> +#include <wtf/HashMap.h> +#include <wtf/Ref.h> +#include <wtf/RefCounted.h> + +namespace WebKit { + +class CustomProtocolManager; + +class NetworkSession { + friend class NetworkDataTask; +public: + enum class Type { + Normal, + Ephemeral + }; + NetworkSession(Type, WebCore::SessionID, CustomProtocolManager*); + ~NetworkSession(); + + static NetworkSession& defaultSession(); + + NetworkDataTask* dataTaskForIdentifier(NetworkDataTask::TaskIdentifier); + + void addDownloadID(NetworkDataTask::TaskIdentifier, DownloadID); + DownloadID downloadID(NetworkDataTask::TaskIdentifier); + DownloadID takeDownloadID(NetworkDataTask::TaskIdentifier); + +private: + HashMap<NetworkDataTask::TaskIdentifier, NetworkDataTask*> m_dataTaskMap; + HashMap<NetworkDataTask::TaskIdentifier, DownloadID> m_downloadMap; +#if PLATFORM(COCOA) + RetainPtr<NSURLSession> m_sessionWithCredentialStorage; + RetainPtr<NSURLSession> m_sessionWithoutCredentialStorage; + RetainPtr<WKNetworkSessionDelegate> m_sessionDelegate; +#endif +}; + +} // namespace WebKit + +#endif // NetworkSession_h diff --git a/Source/WebKit2/NetworkProcess/mac/RemoteNetworkingContext.h b/Source/WebKit2/NetworkProcess/RemoteNetworkingContext.h index d0402246b..e44a5d078 100644 --- a/Source/WebKit2/NetworkProcess/mac/RemoteNetworkingContext.h +++ b/Source/WebKit2/NetworkProcess/RemoteNetworkingContext.h @@ -1,5 +1,6 @@ /* * Copyright (C) 2012 Apple Inc. All rights reserved. + * Copyright (C) 2013 University of Szeged. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -27,40 +28,54 @@ #define RemoteNetworkingContext_h #include <WebCore/NetworkingContext.h> +#include <WebCore/SessionID.h> namespace WebKit { -class RemoteNetworkingContext : public WebCore::NetworkingContext { +class RemoteNetworkingContext final : public WebCore::NetworkingContext { public: - static PassRefPtr<RemoteNetworkingContext> create(bool needsSiteSpecificQuirks, bool localFileContentSniffingEnabled, bool privateBrowsingEnabled, bool shouldClearReferrerOnHTTPSToHTTPRedirect) + static Ref<RemoteNetworkingContext> create(WebCore::SessionID sessionID, bool shouldClearReferrerOnHTTPSToHTTPRedirect) { - return adoptRef(new RemoteNetworkingContext(needsSiteSpecificQuirks, localFileContentSniffingEnabled, privateBrowsingEnabled, shouldClearReferrerOnHTTPSToHTTPRedirect)); + return adoptRef(*new RemoteNetworkingContext(sessionID, shouldClearReferrerOnHTTPSToHTTPRedirect)); } virtual ~RemoteNetworkingContext(); - static void setPrivateBrowsingStorageSessionIdentifierBase(const String&); - static void ensurePrivateBrowsingSession(); - static void destroyPrivateBrowsingSession(); + // FIXME: Remove platform-specific code and use SessionTracker. + static void ensurePrivateBrowsingSession(WebCore::SessionID); - static WebCore::NetworkStorageSession* privateBrowsingSession(); - - virtual bool shouldClearReferrerOnHTTPSToHTTPRedirect() const OVERRIDE; + virtual bool shouldClearReferrerOnHTTPSToHTTPRedirect() const override { return m_shouldClearReferrerOnHTTPSToHTTPRedirect; } private: - RemoteNetworkingContext(bool needsSiteSpecificQuirks, bool localFileContentSniffingEnabled, bool privateBrowsingEnabled, bool m_shouldClearReferrerOnHTTPSToHTTPRedirect); + RemoteNetworkingContext(WebCore::SessionID sessionID, bool shouldClearReferrerOnHTTPSToHTTPRedirect) + : m_sessionID(sessionID) + , m_shouldClearReferrerOnHTTPSToHTTPRedirect(shouldClearReferrerOnHTTPSToHTTPRedirect) + { + } - virtual bool isValid() const OVERRIDE; + virtual bool isValid() const override; + virtual WebCore::NetworkStorageSession& storageSession() const override; - virtual bool needsSiteSpecificQuirks() const OVERRIDE; - virtual bool localFileContentSniffingEnabled() const OVERRIDE; - virtual WebCore::NetworkStorageSession& storageSession() const OVERRIDE; - virtual RetainPtr<CFDataRef> sourceApplicationAuditData() const OVERRIDE; - virtual WebCore::ResourceError blockedError(const WebCore::ResourceRequest&) const OVERRIDE; +#if PLATFORM(COCOA) + void setLocalFileContentSniffingEnabled(bool value) { m_localFileContentSniffingEnabled = value; } + virtual bool localFileContentSniffingEnabled() const override; + virtual RetainPtr<CFDataRef> sourceApplicationAuditData() const override; + virtual String sourceApplicationIdentifier() const override; + virtual WebCore::ResourceError blockedError(const WebCore::ResourceRequest&) const override; +#endif - bool m_needsSiteSpecificQuirks; - bool m_localFileContentSniffingEnabled; - bool m_privateBrowsingEnabled; + WebCore::SessionID m_sessionID; bool m_shouldClearReferrerOnHTTPSToHTTPRedirect; + +#if PLATFORM(COCOA) + bool m_localFileContentSniffingEnabled = false; +#endif + +#if PLATFORM(QT) + QObject* originatingObject() const override { return nullptr; } + QNetworkAccessManager* networkAccessManager() const override; + bool mimeSniffingEnabled() const override { return true; } + bool thirdPartyCookiePolicyPermission(const QUrl&) const override { return true; } +#endif }; } diff --git a/Source/WebKit2/NetworkProcess/SchedulableLoader.cpp b/Source/WebKit2/NetworkProcess/SchedulableLoader.cpp deleted file mode 100644 index ae7b20397..000000000 --- a/Source/WebKit2/NetworkProcess/SchedulableLoader.cpp +++ /dev/null @@ -1,118 +0,0 @@ -/* - * Copyright (C) 2013 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 "SchedulableLoader.h" - -#if ENABLE(NETWORK_PROCESS) - -#include "NetworkBlobRegistry.h" -#include "NetworkConnectionToWebProcess.h" -#include "NetworkResourceLoadParameters.h" -#include <WebCore/FormData.h> - -using namespace WebCore; - -namespace WebKit { - -SchedulableLoader::SchedulableLoader(const NetworkResourceLoadParameters& parameters, NetworkConnectionToWebProcess* connection) - : m_identifier(parameters.identifier) - , m_webPageID(parameters.webPageID) - , m_webFrameID(parameters.webFrameID) - , m_request(parameters.request) - , m_priority(parameters.priority) - , m_contentSniffingPolicy(parameters.contentSniffingPolicy) - , m_allowStoredCredentials(parameters.allowStoredCredentials) - , m_clientCredentialPolicy(parameters.clientCredentialPolicy) - , m_inPrivateBrowsingMode(parameters.inPrivateBrowsingMode) - , m_shouldClearReferrerOnHTTPSToHTTPRedirect(parameters.shouldClearReferrerOnHTTPSToHTTPRedirect) - , m_isLoadingMainResource(parameters.isMainResource) - , m_sandboxExtensionsAreConsumed(false) - , m_connection(connection) -{ - // Either this loader has both a webPageID and webFrameID, or it is not allowed to ask the client for authentication credentials. - // 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_webPageID && m_webFrameID) || m_clientCredentialPolicy == DoNotAskClientForAnyCredentials); - - for (size_t i = 0, count = parameters.requestBodySandboxExtensions.size(); i < count; ++i) { - if (RefPtr<SandboxExtension> extension = SandboxExtension::create(parameters.requestBodySandboxExtensions[i])) - m_requestBodySandboxExtensions.append(extension); - } - -#if ENABLE(BLOB) - if (m_request.httpBody()) { - const Vector<FormDataElement>& elements = m_request.httpBody()->elements(); - for (size_t i = 0, count = elements.size(); i < count; ++i) { - if (elements[i].m_type == FormDataElement::encodedBlob) { - Vector<RefPtr<SandboxExtension>> blobElementExtensions = NetworkBlobRegistry::shared().sandboxExtensions(elements[i].m_url); - m_requestBodySandboxExtensions.appendVector(blobElementExtensions); - } - } - } - - if (m_request.url().protocolIs("blob")) { - ASSERT(!SandboxExtension::create(parameters.resourceSandboxExtension)); - m_resourceSandboxExtensions = NetworkBlobRegistry::shared().sandboxExtensions(m_request.url()); - } else -#endif - if (RefPtr<SandboxExtension> resourceSandboxExtension = SandboxExtension::create(parameters.resourceSandboxExtension)) - m_resourceSandboxExtensions.append(resourceSandboxExtension); -} - -SchedulableLoader::~SchedulableLoader() -{ - ASSERT(!m_hostRecord); -} - -void SchedulableLoader::consumeSandboxExtensions() -{ - for (size_t i = 0, count = m_requestBodySandboxExtensions.size(); i < count; ++i) - m_requestBodySandboxExtensions[i]->consume(); - - for (size_t i = 0, count = m_resourceSandboxExtensions.size(); i < count; ++i) - m_resourceSandboxExtensions[i]->consume(); - - m_sandboxExtensionsAreConsumed = true; -} - -void SchedulableLoader::invalidateSandboxExtensions() -{ - if (m_sandboxExtensionsAreConsumed) { - for (size_t i = 0, count = m_requestBodySandboxExtensions.size(); i < count; ++i) - m_requestBodySandboxExtensions[i]->revoke(); - for (size_t i = 0, count = m_resourceSandboxExtensions.size(); i < count; ++i) - m_resourceSandboxExtensions[i]->revoke(); - } - - m_requestBodySandboxExtensions.clear(); - m_resourceSandboxExtensions.clear(); - - m_sandboxExtensionsAreConsumed = false; -} - -} // namespace WebKit - -#endif // ENABLE(NETWORK_PROCESS) diff --git a/Source/WebKit2/NetworkProcess/SchedulableLoader.h b/Source/WebKit2/NetworkProcess/SchedulableLoader.h deleted file mode 100644 index 81ff5e5ce..000000000 --- a/Source/WebKit2/NetworkProcess/SchedulableLoader.h +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright (C) 2013 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. - */ - -#ifndef SchedulableLoader_h -#define SchedulableLoader_h - -#include "HostRecord.h" -#include <WebCore/ResourceLoaderOptions.h> -#include <WebCore/ResourceRequest.h> -#include <wtf/MainThread.h> -#include <wtf/RefCounted.h> - -#if ENABLE(NETWORK_PROCESS) - -namespace WebKit { - -class NetworkConnectionToWebProcess; -class NetworkResourceLoadParameters; -class SandboxExtension; - -class SchedulableLoader : public RefCounted<SchedulableLoader> { -public: - virtual ~SchedulableLoader(); - - ResourceLoadIdentifier identifier() const { return m_identifier; } - uint64_t webPageID() const { return m_webPageID; } - uint64_t webFrameID() const { return m_webFrameID; } - const WebCore::ResourceRequest& request() const { return m_request; } - WebCore::ResourceLoadPriority priority() const { return m_priority; } - WebCore::ContentSniffingPolicy contentSniffingPolicy() const { return m_contentSniffingPolicy; } - WebCore::StoredCredentials allowStoredCredentials() const { return m_allowStoredCredentials; } - WebCore::ClientCredentialPolicy clientCredentialPolicy() const { return m_clientCredentialPolicy; } - bool inPrivateBrowsingMode() const { return m_inPrivateBrowsingMode; } - bool isLoadingMainResource() const { return m_isLoadingMainResource; } - - NetworkConnectionToWebProcess* connectionToWebProcess() const { return m_connection.get(); } - - virtual void start() = 0; - virtual void abort() = 0; - - virtual bool isSynchronous() { return false; } - - void setHostRecord(HostRecord* hostRecord) { ASSERT(isMainThread()); m_hostRecord = hostRecord; } - HostRecord* hostRecord() const { ASSERT(isMainThread()); return m_hostRecord.get(); } - -protected: - SchedulableLoader(const NetworkResourceLoadParameters&, NetworkConnectionToWebProcess*); - - void consumeSandboxExtensions(); - void invalidateSandboxExtensions(); - - bool shouldClearReferrerOnHTTPSToHTTPRedirect() const { return m_shouldClearReferrerOnHTTPSToHTTPRedirect; } - -private: - ResourceLoadIdentifier m_identifier; - uint64_t m_webPageID; - uint64_t m_webFrameID; - WebCore::ResourceRequest m_request; - WebCore::ResourceLoadPriority m_priority; - WebCore::ContentSniffingPolicy m_contentSniffingPolicy; - WebCore::StoredCredentials m_allowStoredCredentials; - WebCore::ClientCredentialPolicy m_clientCredentialPolicy; - bool m_inPrivateBrowsingMode; - bool m_shouldClearReferrerOnHTTPSToHTTPRedirect; - bool m_isLoadingMainResource; - - Vector<RefPtr<SandboxExtension>> m_requestBodySandboxExtensions; - Vector<RefPtr<SandboxExtension>> m_resourceSandboxExtensions; - bool m_sandboxExtensionsAreConsumed; - - RefPtr<NetworkConnectionToWebProcess> m_connection; - - RefPtr<HostRecord> m_hostRecord; -}; - -} // namespace WebKit - -#endif // ENABLE(NETWORK_PROCESS) - -#endif // SchedulableLoader_h diff --git a/Source/WebKit2/NetworkProcess/SyncNetworkResourceLoader.cpp b/Source/WebKit2/NetworkProcess/SyncNetworkResourceLoader.cpp deleted file mode 100644 index 02b05ed7d..000000000 --- a/Source/WebKit2/NetworkProcess/SyncNetworkResourceLoader.cpp +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright (C) 2013 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 "SyncNetworkResourceLoader.h" - -#if ENABLE(NETWORK_PROCESS) - -#include "DataReference.h" -#include "NetworkProcess.h" -#include "RemoteNetworkingContext.h" -#include <WebCore/ResourceError.h> -#include <WebCore/ResourceHandle.h> -#include <WebCore/ResourceResponse.h> -#include <wtf/MainThread.h> - -using namespace WebCore; - -namespace WebKit { - -SyncNetworkResourceLoader::SyncNetworkResourceLoader(const NetworkResourceLoadParameters& parameters, NetworkConnectionToWebProcess* connection, PassRefPtr<Messages::NetworkConnectionToWebProcess::PerformSynchronousLoad::DelayedReply> reply) - : SchedulableLoader(parameters, connection) - , m_delayedReply(reply) -{ -} - -void SyncNetworkResourceLoader::start() -{ - // FIXME (NetworkProcess): This is called on the NetworkProcess main thread, blocking any other requests from being scheduled. - // This should move to a background thread, but we'd either need to be sure that: - // A - ResourceHandle::loadResourceSynchronously is safe to run on a background thread. - // B - Write custom loading logic that is known to be safe on a background thread. - - ASSERT(isMainThread()); - - ResourceError error; - ResourceResponse response; - Vector<char> data; - - // FIXME (NetworkProcess): Create RemoteNetworkingContext with actual settings. - RefPtr<RemoteNetworkingContext> networkingContext = RemoteNetworkingContext::create(false, false, inPrivateBrowsingMode(), shouldClearReferrerOnHTTPSToHTTPRedirect()); - - consumeSandboxExtensions(); - - ResourceHandle::loadResourceSynchronously(networkingContext.get(), request(), allowStoredCredentials(), error, response, data); - - cleanup(); - - m_delayedReply->send(error, response, CoreIPC::DataReference((uint8_t*)data.data(), data.size())); -} - -void SyncNetworkResourceLoader::abort() -{ - cleanup(); -} - -void SyncNetworkResourceLoader::cleanup() -{ - invalidateSandboxExtensions(); - NetworkProcess::shared().networkResourceLoadScheduler().scheduleRemoveLoader(this); -} - -} // namespace WebKit - -#endif // ENABLE(NETWORK_PROCESS) diff --git a/Source/WebKit2/NetworkProcess/cache/NetworkCache.cpp b/Source/WebKit2/NetworkProcess/cache/NetworkCache.cpp new file mode 100644 index 000000000..9ea0d3f41 --- /dev/null +++ b/Source/WebKit2/NetworkProcess/cache/NetworkCache.cpp @@ -0,0 +1,656 @@ +/* + * Copyright (C) 2014-2015 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 "NetworkCache.h" + +#if ENABLE(NETWORK_CACHE) + +#include "Logging.h" +#include "NetworkCacheSpeculativeLoadManager.h" +#include "NetworkCacheStatistics.h" +#include "NetworkCacheStorage.h" +#include <WebCore/CacheValidation.h> +#include <WebCore/FileSystem.h> +#include <WebCore/HTTPHeaderNames.h> +#include <WebCore/NetworkStorageSession.h> +#include <WebCore/PlatformCookieJar.h> +#include <WebCore/ResourceRequest.h> +#include <WebCore/ResourceResponse.h> +#include <WebCore/SharedBuffer.h> +#include <wtf/MainThread.h> +#include <wtf/NeverDestroyed.h> +#include <wtf/RunLoop.h> +#include <wtf/text/StringBuilder.h> + +#if PLATFORM(COCOA) +#include <notify.h> +#endif + +namespace WebKit { +namespace NetworkCache { + +static const AtomicString& resourceType() +{ + ASSERT(WTF::isMainThread()); + static NeverDestroyed<const AtomicString> resource("resource", AtomicString::ConstructFromLiteral); + return resource; +} + +Cache& singleton() +{ + static NeverDestroyed<Cache> instance; + return instance; +} + +#if PLATFORM(GTK) +static void dumpFileChanged(Cache* cache) +{ + cache->dumpContentsToFile(); +} +#endif + +bool Cache::initialize(const String& cachePath, const Parameters& parameters) +{ + m_storage = Storage::open(cachePath); + +#if ENABLE(NETWORK_CACHE_SPECULATIVE_REVALIDATION) + if (parameters.enableNetworkCacheSpeculativeRevalidation) + m_speculativeLoadManager = std::make_unique<SpeculativeLoadManager>(*m_storage); +#endif + + if (parameters.enableEfficacyLogging) + m_statistics = Statistics::open(cachePath); + +#if PLATFORM(COCOA) + // Triggers with "notifyutil -p com.apple.WebKit.Cache.dump". + if (m_storage) { + int token; + notify_register_dispatch("com.apple.WebKit.Cache.dump", &token, dispatch_get_main_queue(), ^(int) { + dumpContentsToFile(); + }); + } +#endif +#if PLATFORM(GTK) + // Triggers with "touch $cachePath/dump". + if (m_storage) { + CString dumpFilePath = WebCore::fileSystemRepresentation(WebCore::pathByAppendingComponent(m_storage->basePath(), "dump")); + GRefPtr<GFile> dumpFile = adoptGRef(g_file_new_for_path(dumpFilePath.data())); + GFileMonitor* monitor = g_file_monitor_file(dumpFile.get(), G_FILE_MONITOR_NONE, nullptr, nullptr); + g_signal_connect_swapped(monitor, "changed", G_CALLBACK(dumpFileChanged), this); + } +#endif + + LOG(NetworkCache, "(NetworkProcess) opened cache storage, success %d", !!m_storage); + return !!m_storage; +} + +void Cache::setCapacity(size_t maximumSize) +{ + if (!m_storage) + return; + m_storage->setCapacity(maximumSize); +} + +static Key makeCacheKey(const WebCore::ResourceRequest& request) +{ +#if ENABLE(CACHE_PARTITIONING) + String partition = request.cachePartition(); +#else + String partition; +#endif + if (partition.isEmpty()) + partition = ASCIILiteral("No partition"); + + // FIXME: This implements minimal Range header disk cache support. We don't parse + // ranges so only the same exact range request will be served from the cache. + String range = request.httpHeaderField(WebCore::HTTPHeaderName::Range); + return { partition, resourceType(), range, request.url().string() }; +} + +static String headerValueForVary(const WebCore::ResourceRequest& request, const String& headerName) +{ + // Explicit handling for cookies is needed because they are added magically by the networking layer. + // FIXME: The value might have changed between making the request and retrieving the cookie here. + // We could fetch the cookie when making the request but that seems overkill as the case is very rare and it + // is a blocking operation. This should be sufficient to cover reasonable cases. + if (headerName == httpHeaderNameString(WebCore::HTTPHeaderName::Cookie)) + return WebCore::cookieRequestHeaderFieldValue(WebCore::NetworkStorageSession::defaultStorageSession(), request.firstPartyForCookies(), request.url()); + return request.httpHeaderField(headerName); +} + +static Vector<std::pair<String, String>> collectVaryingRequestHeaders(const WebCore::ResourceRequest& request, const WebCore::ResourceResponse& response) +{ + String varyValue = response.httpHeaderField(WebCore::HTTPHeaderName::Vary); + if (varyValue.isEmpty()) + return { }; + Vector<String> varyingHeaderNames; + varyValue.split(',', /*allowEmptyEntries*/ false, varyingHeaderNames); + Vector<std::pair<String, String>> varyingRequestHeaders; + varyingRequestHeaders.reserveCapacity(varyingHeaderNames.size()); + for (auto& varyHeaderName : varyingHeaderNames) { + String headerName = varyHeaderName.stripWhiteSpace(); + String headerValue = headerValueForVary(request, headerName); + varyingRequestHeaders.append(std::make_pair(headerName, headerValue)); + } + return varyingRequestHeaders; +} + +static bool verifyVaryingRequestHeaders(const Vector<std::pair<String, String>>& varyingRequestHeaders, const WebCore::ResourceRequest& request) +{ + for (auto& varyingRequestHeader : varyingRequestHeaders) { + // FIXME: Vary: * in response would ideally trigger a cache delete instead of a store. + if (varyingRequestHeader.first == "*") + return false; + String headerValue = headerValueForVary(request, varyingRequestHeader.first); + if (headerValue != varyingRequestHeader.second) + return false; + } + return true; +} + +static bool cachePolicyAllowsExpired(WebCore::ResourceRequestCachePolicy policy) +{ + switch (policy) { + case WebCore::ReturnCacheDataElseLoad: + case WebCore::ReturnCacheDataDontLoad: + return true; + case WebCore::UseProtocolCachePolicy: + case WebCore::ReloadIgnoringCacheData: + return false; + } + ASSERT_NOT_REACHED(); + return false; +} + +static bool responseHasExpired(const WebCore::ResourceResponse& response, std::chrono::system_clock::time_point timestamp, Optional<std::chrono::microseconds> maxStale) +{ + if (response.cacheControlContainsNoCache()) + return true; + + auto age = WebCore::computeCurrentAge(response, timestamp); + auto lifetime = WebCore::computeFreshnessLifetimeForHTTPFamily(response, timestamp); + + auto maximumStaleness = maxStale ? maxStale.value() : 0_ms; + bool hasExpired = age - lifetime > maximumStaleness; + +#ifndef LOG_DISABLED + if (hasExpired) + LOG(NetworkCache, "(NetworkProcess) needsRevalidation hasExpired age=%f lifetime=%f max-stale=%g", age, lifetime, maxStale); +#endif + + return hasExpired; +} + +static bool responseNeedsRevalidation(const WebCore::ResourceResponse& response, const WebCore::ResourceRequest& request, std::chrono::system_clock::time_point timestamp) +{ + auto requestDirectives = WebCore::parseCacheControlDirectives(request.httpHeaderFields()); + if (requestDirectives.noCache) + return true; + // For requests we ignore max-age values other than zero. + if (requestDirectives.maxAge && requestDirectives.maxAge.value() == 0_ms) + return true; + + return responseHasExpired(response, timestamp, requestDirectives.maxStale); +} + +static UseDecision makeUseDecision(const Entry& entry, const WebCore::ResourceRequest& request) +{ + // The request is conditional so we force revalidation from the network. We merely check the disk cache + // so we can update the cache entry. + if (request.isConditional() && !entry.redirectRequest()) + return UseDecision::Validate; + + if (!verifyVaryingRequestHeaders(entry.varyingRequestHeaders(), request)) + return UseDecision::NoDueToVaryingHeaderMismatch; + + // We never revalidate in the case of a history navigation. + if (cachePolicyAllowsExpired(request.cachePolicy())) + return UseDecision::Use; + + if (!responseNeedsRevalidation(entry.response(), request, entry.timeStamp())) + return UseDecision::Use; + + if (!entry.response().hasCacheValidatorFields()) + return UseDecision::NoDueToMissingValidatorFields; + + return entry.redirectRequest() ? UseDecision::NoDueToExpiredRedirect : UseDecision::Validate; +} + +static RetrieveDecision makeRetrieveDecision(const WebCore::ResourceRequest& request) +{ + // FIXME: Support HEAD requests. + if (request.httpMethod() != "GET") + return RetrieveDecision::NoDueToHTTPMethod; + if (request.cachePolicy() == WebCore::ReloadIgnoringCacheData && !request.isConditional()) + return RetrieveDecision::NoDueToReloadIgnoringCache; + + return RetrieveDecision::Yes; +} + +// http://tools.ietf.org/html/rfc7231#page-48 +static bool isStatusCodeCacheableByDefault(int statusCode) +{ + switch (statusCode) { + case 200: // OK + case 203: // Non-Authoritative Information + case 204: // No Content + case 206: // Partial Content + case 300: // Multiple Choices + case 301: // Moved Permanently + case 404: // Not Found + case 405: // Method Not Allowed + case 410: // Gone + case 414: // URI Too Long + case 501: // Not Implemented + return true; + default: + return false; + } +} + +static bool isStatusCodePotentiallyCacheable(int statusCode) +{ + switch (statusCode) { + case 201: // Created + case 202: // Accepted + case 205: // Reset Content + case 302: // Found + case 303: // See Other + case 307: // Temporary redirect + case 403: // Forbidden + case 406: // Not Acceptable + case 415: // Unsupported Media Type + return true; + default: + return false; + } +} + +static bool isMediaMIMEType(const String& mimeType) +{ + if (mimeType.startsWith("video/", /*caseSensitive*/ false)) + return true; + if (mimeType.startsWith("audio/", /*caseSensitive*/ false)) + return true; + return false; +} + +static StoreDecision makeStoreDecision(const WebCore::ResourceRequest& originalRequest, const WebCore::ResourceResponse& response) +{ + if (!originalRequest.url().protocolIsInHTTPFamily() || !response.isHTTP()) + return StoreDecision::NoDueToProtocol; + + if (originalRequest.httpMethod() != "GET") + return StoreDecision::NoDueToHTTPMethod; + + auto requestDirectives = WebCore::parseCacheControlDirectives(originalRequest.httpHeaderFields()); + if (requestDirectives.noStore) + return StoreDecision::NoDueToNoStoreRequest; + + if (response.cacheControlContainsNoStore()) + return StoreDecision::NoDueToNoStoreResponse; + + if (!isStatusCodeCacheableByDefault(response.httpStatusCode())) { + // http://tools.ietf.org/html/rfc7234#section-4.3.2 + bool hasExpirationHeaders = response.expires() || response.cacheControlMaxAge(); + bool expirationHeadersAllowCaching = isStatusCodePotentiallyCacheable(response.httpStatusCode()) && hasExpirationHeaders; + if (!expirationHeadersAllowCaching) + return StoreDecision::NoDueToHTTPStatusCode; + } + + bool isMainResource = originalRequest.requester() == WebCore::ResourceRequest::Requester::Main; + bool storeUnconditionallyForHistoryNavigation = isMainResource || originalRequest.priority() == WebCore::ResourceLoadPriority::VeryHigh; + if (!storeUnconditionallyForHistoryNavigation) { + auto now = std::chrono::system_clock::now(); + bool hasNonZeroLifetime = !response.cacheControlContainsNoCache() && WebCore::computeFreshnessLifetimeForHTTPFamily(response, now) > 0_ms; + + bool possiblyReusable = response.hasCacheValidatorFields() || hasNonZeroLifetime; + if (!possiblyReusable) + return StoreDecision::NoDueToUnlikelyToReuse; + } + + // Media loaded via XHR is likely being used for MSE streaming (YouTube and Netflix for example). + // Streaming media fills the cache quickly and is unlikely to be reused. + // FIXME: We should introduce a separate media cache partition that doesn't affect other resources. + // FIXME: We should also make sure make the MSE paths are copy-free so we can use mapped buffers from disk effectively. + bool isLikelyStreamingMedia = originalRequest.requester() == WebCore::ResourceRequest::Requester::XHR && isMediaMIMEType(response.mimeType()); + if (isLikelyStreamingMedia) + return StoreDecision::NoDueToStreamingMedia; + + return StoreDecision::Yes; +} + +void Cache::retrieve(const WebCore::ResourceRequest& request, const GlobalFrameID& frameID, std::function<void (std::unique_ptr<Entry>)> completionHandler) +{ + ASSERT(isEnabled()); + ASSERT(request.url().protocolIsInHTTPFamily()); + + LOG(NetworkCache, "(NetworkProcess) retrieving %s priority %d", request.url().string().ascii().data(), static_cast<int>(request.priority())); + + if (m_statistics) + m_statistics->recordRetrievalRequest(frameID.first); + + Key storageKey = makeCacheKey(request); + +#if ENABLE(NETWORK_CACHE_SPECULATIVE_REVALIDATION) + if (m_speculativeLoadManager) + m_speculativeLoadManager->registerLoad(frameID, request, storageKey); +#endif + + auto retrieveDecision = makeRetrieveDecision(request); + if (retrieveDecision != RetrieveDecision::Yes) { + if (m_statistics) + m_statistics->recordNotUsingCacheForRequest(frameID.first, storageKey, request, retrieveDecision); + + completionHandler(nullptr); + return; + } + +#if ENABLE(NETWORK_CACHE_SPECULATIVE_REVALIDATION) + if (m_speculativeLoadManager && m_speculativeLoadManager->retrieve(frameID, storageKey, [request, completionHandler](std::unique_ptr<Entry> entry) { + if (entry && verifyVaryingRequestHeaders(entry->varyingRequestHeaders(), request)) + completionHandler(WTFMove(entry)); + else + completionHandler(nullptr); + })) + return; +#endif + + auto startTime = std::chrono::system_clock::now(); + auto priority = static_cast<unsigned>(request.priority()); + + m_storage->retrieve(storageKey, priority, [this, request, completionHandler, startTime, storageKey, frameID](std::unique_ptr<Storage::Record> record) { + if (!record) { + LOG(NetworkCache, "(NetworkProcess) not found in storage"); + + if (m_statistics) + m_statistics->recordRetrievalFailure(frameID.first, storageKey, request); + + completionHandler(nullptr); + return false; + } + + ASSERT(record->key == storageKey); + + auto entry = Entry::decodeStorageRecord(*record); + + auto useDecision = entry ? makeUseDecision(*entry, request) : UseDecision::NoDueToDecodeFailure; + switch (useDecision) { + case UseDecision::Use: + break; + case UseDecision::Validate: + entry->setNeedsValidation(); + break; + default: + entry = nullptr; + }; + +#if !LOG_DISABLED + auto elapsedMS = static_cast<int64_t>(std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now() - startTime).count()); + LOG(NetworkCache, "(NetworkProcess) retrieve complete useDecision=%d priority=%d time=%" PRIi64 "ms", static_cast<int>(useDecision), static_cast<int>(request.priority()), elapsedMS); +#endif + completionHandler(WTFMove(entry)); + + if (m_statistics) + m_statistics->recordRetrievedCachedEntry(frameID.first, storageKey, request, useDecision); + return useDecision != UseDecision::NoDueToDecodeFailure; + }); +} + +std::unique_ptr<Entry> Cache::store(const WebCore::ResourceRequest& request, const WebCore::ResourceResponse& response, RefPtr<WebCore::SharedBuffer>&& responseData, std::function<void (MappedBody&)> completionHandler) +{ + ASSERT(isEnabled()); + ASSERT(responseData); + + LOG(NetworkCache, "(NetworkProcess) storing %s, partition %s", request.url().string().latin1().data(), makeCacheKey(request).partition().latin1().data()); + + StoreDecision storeDecision = makeStoreDecision(request, response); + if (storeDecision != StoreDecision::Yes) { + LOG(NetworkCache, "(NetworkProcess) didn't store, storeDecision=%d", static_cast<int>(storeDecision)); + auto key = makeCacheKey(request); + + auto isSuccessfulRevalidation = response.httpStatusCode() == 304; + if (!isSuccessfulRevalidation) { + // Make sure we don't keep a stale entry in the cache. + remove(key); + } + + if (m_statistics) + m_statistics->recordNotCachingResponse(key, storeDecision); + + return nullptr; + } + + std::unique_ptr<Entry> cacheEntry = std::make_unique<Entry>(makeCacheKey(request), response, WTFMove(responseData), collectVaryingRequestHeaders(request, response)); + + auto record = cacheEntry->encodeAsStorageRecord(); + + m_storage->store(record, [completionHandler](const Data& bodyData) { + MappedBody mappedBody; +#if ENABLE(SHAREABLE_RESOURCE) + if (RefPtr<SharedMemory> sharedMemory = bodyData.tryCreateSharedMemory()) { + mappedBody.shareableResource = ShareableResource::create(WTFMove(sharedMemory), 0, bodyData.size()); + ASSERT(mappedBody.shareableResource); + mappedBody.shareableResource->createHandle(mappedBody.shareableResourceHandle); + } +#endif + completionHandler(mappedBody); + LOG(NetworkCache, "(NetworkProcess) stored"); + }); + + return cacheEntry; +} + +std::unique_ptr<Entry> Cache::storeRedirect(const WebCore::ResourceRequest& request, const WebCore::ResourceResponse& response, const WebCore::ResourceRequest& redirectRequest) +{ + ASSERT(isEnabled()); + + LOG(NetworkCache, "(NetworkProcess) storing redirect %s -> %s", request.url().string().latin1().data(), redirectRequest.url().string().latin1().data()); + + StoreDecision storeDecision = makeStoreDecision(request, response); + if (storeDecision != StoreDecision::Yes) { + LOG(NetworkCache, "(NetworkProcess) didn't store redirect, storeDecision=%d", static_cast<int>(storeDecision)); + auto key = makeCacheKey(request); + if (m_statistics) + m_statistics->recordNotCachingResponse(key, storeDecision); + + return nullptr; + } + + std::unique_ptr<Entry> cacheEntry = std::make_unique<Entry>(makeCacheKey(request), response, redirectRequest, collectVaryingRequestHeaders(request, response)); + + auto record = cacheEntry->encodeAsStorageRecord(); + + m_storage->store(record, nullptr); + + return cacheEntry; +} + +std::unique_ptr<Entry> Cache::update(const WebCore::ResourceRequest& originalRequest, const GlobalFrameID& frameID, const Entry& existingEntry, const WebCore::ResourceResponse& validatingResponse) +{ + LOG(NetworkCache, "(NetworkProcess) updating %s", originalRequest.url().string().latin1().data()); + + WebCore::ResourceResponse response = existingEntry.response(); + WebCore::updateResponseHeadersAfterRevalidation(response, validatingResponse); + response.setSource(WebCore::ResourceResponse::Source::DiskCache); + + auto updateEntry = std::make_unique<Entry>(existingEntry.key(), response, existingEntry.buffer(), collectVaryingRequestHeaders(originalRequest, response)); + auto updateRecord = updateEntry->encodeAsStorageRecord(); + + m_storage->store(updateRecord, { }); + + if (m_statistics) + m_statistics->recordRevalidationSuccess(frameID.first, existingEntry.key(), originalRequest); + + return updateEntry; +} + +void Cache::remove(const Key& key) +{ + ASSERT(isEnabled()); + + m_storage->remove(key); +} + +void Cache::remove(const WebCore::ResourceRequest& request) +{ + remove(makeCacheKey(request)); +} + +void Cache::traverse(const std::function<void (const TraversalEntry*)>& traverseHandler) +{ + ASSERT(isEnabled()); + + // Protect against clients making excessive traversal requests. + const unsigned maximumTraverseCount = 3; + if (m_traverseCount >= maximumTraverseCount) { + WTFLogAlways("Maximum parallel cache traverse count exceeded. Ignoring traversal request."); + + RunLoop::main().dispatch([traverseHandler] { + traverseHandler(nullptr); + }); + return; + } + + ++m_traverseCount; + + m_storage->traverse(resourceType(), 0, [this, traverseHandler](const Storage::Record* record, const Storage::RecordInfo& recordInfo) { + if (!record) { + --m_traverseCount; + traverseHandler(nullptr); + return; + } + + auto entry = Entry::decodeStorageRecord(*record); + if (!entry) + return; + + TraversalEntry traversalEntry { *entry, recordInfo }; + traverseHandler(&traversalEntry); + }); +} + +String Cache::dumpFilePath() const +{ + return WebCore::pathByAppendingComponent(m_storage->versionPath(), "dump.json"); +} + +void Cache::dumpContentsToFile() +{ + if (!m_storage) + return; + auto fd = WebCore::openFile(dumpFilePath(), WebCore::OpenForWrite); + if (!WebCore::isHandleValid(fd)) + return; + auto prologue = String("{\n\"entries\": [\n").utf8(); + WebCore::writeToFile(fd, prologue.data(), prologue.length()); + + struct Totals { + unsigned count { 0 }; + double worth { 0 }; + size_t bodySize { 0 }; + }; + Totals totals; + auto flags = Storage::TraverseFlag::ComputeWorth | Storage::TraverseFlag::ShareCount; + size_t capacity = m_storage->capacity(); + m_storage->traverse(resourceType(), flags, [fd, totals, capacity](const Storage::Record* record, const Storage::RecordInfo& info) mutable { + if (!record) { + StringBuilder epilogue; + epilogue.appendLiteral("{}\n],\n"); + epilogue.appendLiteral("\"totals\": {\n"); + epilogue.appendLiteral("\"capacity\": "); + epilogue.appendNumber(capacity); + epilogue.appendLiteral(",\n"); + epilogue.appendLiteral("\"count\": "); + epilogue.appendNumber(totals.count); + epilogue.appendLiteral(",\n"); + epilogue.appendLiteral("\"bodySize\": "); + epilogue.appendNumber(totals.bodySize); + epilogue.appendLiteral(",\n"); + epilogue.appendLiteral("\"averageWorth\": "); + epilogue.appendNumber(totals.count ? totals.worth / totals.count : 0); + epilogue.appendLiteral("\n"); + epilogue.appendLiteral("}\n}\n"); + auto writeData = epilogue.toString().utf8(); + WebCore::writeToFile(fd, writeData.data(), writeData.length()); + WebCore::closeFile(fd); + return; + } + auto entry = Entry::decodeStorageRecord(*record); + if (!entry) + return; + ++totals.count; + totals.worth += info.worth; + totals.bodySize += info.bodySize; + + StringBuilder json; + entry->asJSON(json, info); + json.appendLiteral(",\n"); + auto writeData = json.toString().utf8(); + WebCore::writeToFile(fd, writeData.data(), writeData.length()); + }); +} + +void Cache::deleteDumpFile() +{ + auto queue = WorkQueue::create("com.apple.WebKit.Cache.delete"); + StringCapture dumpFilePathCapture(dumpFilePath()); + queue->dispatch([dumpFilePathCapture] { + WebCore::deleteFile(dumpFilePathCapture.string()); + }); +} + +void Cache::clear(std::chrono::system_clock::time_point modifiedSince, std::function<void ()>&& completionHandler) +{ + LOG(NetworkCache, "(NetworkProcess) clearing cache"); + + if (m_statistics) + m_statistics->clear(); + + if (!m_storage) { + RunLoop::main().dispatch(completionHandler); + return; + } + String anyType; + m_storage->clear(anyType, modifiedSince, WTFMove(completionHandler)); + + deleteDumpFile(); +} + +void Cache::clear() +{ + clear(std::chrono::system_clock::time_point::min(), nullptr); +} + +String Cache::recordsPath() const +{ + return m_storage ? m_storage->recordsPath() : String(); +} + +} +} + +#endif diff --git a/Source/WebKit2/NetworkProcess/cache/NetworkCache.h b/Source/WebKit2/NetworkProcess/cache/NetworkCache.h new file mode 100644 index 000000000..38db1fab8 --- /dev/null +++ b/Source/WebKit2/NetworkProcess/cache/NetworkCache.h @@ -0,0 +1,145 @@ +/* + * Copyright (C) 2014-2015 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. + */ + +#ifndef NetworkCache_h +#define NetworkCache_h + +#if ENABLE(NETWORK_CACHE) + +#include "NetworkCacheEntry.h" +#include "NetworkCacheStorage.h" +#include "ShareableResource.h" +#include <WebCore/ResourceResponse.h> +#include <wtf/text/WTFString.h> + +namespace WebCore { +class ResourceRequest; +class SharedBuffer; +class URL; +} + +namespace WebKit { +namespace NetworkCache { + +class Cache; +class SpeculativeLoadManager; +class Statistics; + +Cache& singleton(); + +struct MappedBody { +#if ENABLE(SHAREABLE_RESOURCE) + RefPtr<ShareableResource> shareableResource; + ShareableResource::Handle shareableResourceHandle; +#endif +}; + +enum class RetrieveDecision { + Yes, + NoDueToHTTPMethod, + NoDueToConditionalRequest, + NoDueToReloadIgnoringCache +}; + +// FIXME: This enum is used in the Statistics code in a way that prevents removing or reordering anything. +enum class StoreDecision { + Yes, + NoDueToProtocol, + NoDueToHTTPMethod, + NoDueToAttachmentResponse, // Unused. + NoDueToNoStoreResponse, + NoDueToHTTPStatusCode, + NoDueToNoStoreRequest, + NoDueToUnlikelyToReuse, + NoDueToStreamingMedia +}; + +enum class UseDecision { + Use, + Validate, + NoDueToVaryingHeaderMismatch, + NoDueToMissingValidatorFields, + NoDueToDecodeFailure, + NoDueToExpiredRedirect, +}; + +using GlobalFrameID = std::pair<uint64_t /*webPageID*/, uint64_t /*webFrameID*/>; + +class Cache { + WTF_MAKE_NONCOPYABLE(Cache); + friend class WTF::NeverDestroyed<Cache>; +public: + struct Parameters { + bool enableEfficacyLogging; +#if ENABLE(NETWORK_CACHE_SPECULATIVE_REVALIDATION) + bool enableNetworkCacheSpeculativeRevalidation; +#endif + }; + bool initialize(const String& cachePath, const Parameters&); + void setCapacity(size_t); + + bool isEnabled() const { return !!m_storage; } + + // Completion handler may get called back synchronously on failure. + void retrieve(const WebCore::ResourceRequest&, const GlobalFrameID&, std::function<void (std::unique_ptr<Entry>)>); + std::unique_ptr<Entry> store(const WebCore::ResourceRequest&, const WebCore::ResourceResponse&, RefPtr<WebCore::SharedBuffer>&&, std::function<void (MappedBody&)>); + std::unique_ptr<Entry> storeRedirect(const WebCore::ResourceRequest&, const WebCore::ResourceResponse&, const WebCore::ResourceRequest& redirectRequest); + std::unique_ptr<Entry> update(const WebCore::ResourceRequest&, const GlobalFrameID&, const Entry&, const WebCore::ResourceResponse& validatingResponse); + + struct TraversalEntry { + const Entry& entry; + const Storage::RecordInfo& recordInfo; + }; + void traverse(const std::function<void (const TraversalEntry*)>&); + void remove(const Key&); + void remove(const WebCore::ResourceRequest&); + + void clear(); + void clear(std::chrono::system_clock::time_point modifiedSince, std::function<void ()>&& completionHandler); + + void dumpContentsToFile(); + + String recordsPath() const; + +private: + Cache() = default; + ~Cache() = delete; + + String dumpFilePath() const; + void deleteDumpFile(); + + std::unique_ptr<Storage> m_storage; +#if ENABLE(NETWORK_CACHE_SPECULATIVE_REVALIDATION) + std::unique_ptr<SpeculativeLoadManager> m_speculativeLoadManager; +#endif + std::unique_ptr<Statistics> m_statistics; + + unsigned m_traverseCount { 0 }; +}; + +} +} +#endif +#endif diff --git a/Source/WebKit2/NetworkProcess/cache/NetworkCacheBlobStorage.cpp b/Source/WebKit2/NetworkProcess/cache/NetworkCacheBlobStorage.cpp new file mode 100644 index 000000000..32c0a1ec5 --- /dev/null +++ b/Source/WebKit2/NetworkProcess/cache/NetworkCacheBlobStorage.cpp @@ -0,0 +1,151 @@ +/* + * Copyright (C) 2015 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 "NetworkCacheBlobStorage.h" + +#if ENABLE(NETWORK_CACHE) + +#include "Logging.h" +#include "NetworkCacheFileSystem.h" +#include <WebCore/FileSystem.h> +#include <fcntl.h> +#include <sys/mman.h> +#include <sys/stat.h> +#include <wtf/RunLoop.h> +#include <wtf/SHA1.h> +#include <wtf/text/StringBuilder.h> + +namespace WebKit { +namespace NetworkCache { + +BlobStorage::BlobStorage(const String& blobDirectoryPath) + : m_blobDirectoryPath(blobDirectoryPath) +{ +} + +String BlobStorage::blobDirectoryPath() const +{ + return m_blobDirectoryPath.isolatedCopy(); +} + +void BlobStorage::synchronize() +{ + ASSERT(!RunLoop::isMain()); + + WebCore::makeAllDirectories(blobDirectoryPath()); + + m_approximateSize = 0; + auto blobDirectory = blobDirectoryPath(); + traverseDirectory(blobDirectory, [this, &blobDirectory](const String& name, DirectoryEntryType type) { + if (type != DirectoryEntryType::File) + return; + auto path = WebCore::pathByAppendingComponent(blobDirectory, name); + auto filePath = WebCore::fileSystemRepresentation(path); + struct stat stat; + ::stat(filePath.data(), &stat); + // No clients left for this blob. + if (stat.st_nlink == 1) + unlink(filePath.data()); + else + m_approximateSize += stat.st_size; + }); + + LOG(NetworkCacheStorage, "(NetworkProcess) blob synchronization completed approximateSize=%zu", approximateSize()); +} + +String BlobStorage::blobPathForHash(const SHA1::Digest& hash) const +{ + auto hashAsString = SHA1::hexDigest(hash); + return WebCore::pathByAppendingComponent(blobDirectoryPath(), String::fromUTF8(hashAsString)); +} + +BlobStorage::Blob BlobStorage::add(const String& path, const Data& data) +{ + ASSERT(!RunLoop::isMain()); + + auto hash = computeSHA1(data); + if (data.isEmpty()) + return { data, hash }; + + auto blobPath = WebCore::fileSystemRepresentation(blobPathForHash(hash)); + auto linkPath = WebCore::fileSystemRepresentation(path); + unlink(linkPath.data()); + + bool blobExists = access(blobPath.data(), F_OK) != -1; + if (blobExists) { + auto existingData = mapFile(blobPath.data()); + if (bytesEqual(existingData, data)) { + link(blobPath.data(), linkPath.data()); + return { existingData, hash }; + } + unlink(blobPath.data()); + } + + auto mappedData = data.mapToFile(blobPath.data()); + if (mappedData.isNull()) + return { }; + + link(blobPath.data(), linkPath.data()); + + m_approximateSize += mappedData.size(); + + return { mappedData, hash }; +} + +BlobStorage::Blob BlobStorage::get(const String& path) +{ + ASSERT(!RunLoop::isMain()); + + auto linkPath = WebCore::fileSystemRepresentation(path); + auto data = mapFile(linkPath.data()); + + return { data, computeSHA1(data) }; +} + +void BlobStorage::remove(const String& path) +{ + ASSERT(!RunLoop::isMain()); + + auto linkPath = WebCore::fileSystemRepresentation(path); + unlink(linkPath.data()); +} + +unsigned BlobStorage::shareCount(const String& path) +{ + ASSERT(!RunLoop::isMain()); + + auto linkPath = WebCore::fileSystemRepresentation(path); + struct stat stat; + if (::stat(linkPath.data(), &stat) < 0) + return 0; + // Link count is 2 in the single client case (the blob file and a link). + return stat.st_nlink - 1; +} + +} +} + +#endif diff --git a/Source/WebKit2/NetworkProcess/cache/NetworkCacheBlobStorage.h b/Source/WebKit2/NetworkProcess/cache/NetworkCacheBlobStorage.h new file mode 100644 index 000000000..e4b494cf9 --- /dev/null +++ b/Source/WebKit2/NetworkProcess/cache/NetworkCacheBlobStorage.h @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2015 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. + */ + +#ifndef NetworkCacheBlobStorage_h +#define NetworkCacheBlobStorage_h + +#if ENABLE(NETWORK_CACHE) + +#include "NetworkCacheData.h" +#include "NetworkCacheKey.h" +#include <wtf/SHA1.h> + +namespace WebKit { +namespace NetworkCache { + +// BlobStorage deduplicates the data using SHA1 hash computed over the blob bytes. +class BlobStorage { + WTF_MAKE_NONCOPYABLE(BlobStorage); +public: + BlobStorage(const String& blobDirectoryPath); + + struct Blob { + Data data; + SHA1::Digest hash; + }; + // These are all synchronous and should not be used from the main thread. + Blob add(const String& path, const Data&); + Blob get(const String& path); + + // Blob won't be removed until synchronization. + void remove(const String& path); + + unsigned shareCount(const String& path); + + size_t approximateSize() const { return m_approximateSize; } + + void synchronize(); + +private: + String blobDirectoryPath() const; + String blobPathForHash(const SHA1::Digest&) const; + + const String m_blobDirectoryPath; + + std::atomic<size_t> m_approximateSize { 0 }; +}; + +} +} + +#endif +#endif diff --git a/Source/WebKit2/NetworkProcess/cache/NetworkCacheCoder.h b/Source/WebKit2/NetworkProcess/cache/NetworkCacheCoder.h new file mode 100644 index 000000000..4b4be369d --- /dev/null +++ b/Source/WebKit2/NetworkProcess/cache/NetworkCacheCoder.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2010, 2014 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. + */ + +#ifndef NetworkCacheCoder_h +#define NetworkCacheCoder_h + +#if ENABLE(NETWORK_CACHE) + +namespace WebKit { +namespace NetworkCache { + +class Decoder; +class Encoder; + +template<typename T> struct Coder { + static void encode(Encoder& encoder, const T& t) + { + t.encode(encoder); + } + + static bool decode(Decoder& decoder, T& t) + { + return T::decode(decoder, t); + } +}; + +} +} + +#endif +#endif diff --git a/Source/WebKit2/NetworkProcess/cache/NetworkCacheCoders.cpp b/Source/WebKit2/NetworkProcess/cache/NetworkCacheCoders.cpp new file mode 100644 index 000000000..868d7c3ed --- /dev/null +++ b/Source/WebKit2/NetworkProcess/cache/NetworkCacheCoders.cpp @@ -0,0 +1,182 @@ +/* + * Copyright (C) 2011, 2014-2015 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 "NetworkCacheCoders.h" + +#if ENABLE(NETWORK_CACHE) + +#include "WebCoreArgumentCoders.h" +#include <wtf/text/CString.h> +#include <wtf/text/WTFString.h> + +namespace WebKit { +namespace NetworkCache { + +void Coder<AtomicString>::encode(Encoder& encoder, const AtomicString& atomicString) +{ + encoder << atomicString.string(); +} + +bool Coder<AtomicString>::decode(Decoder& decoder, AtomicString& atomicString) +{ + String string; + if (!decoder.decode(string)) + return false; + + atomicString = string; + return true; +} + +void Coder<CString>::encode(Encoder& encoder, const CString& string) +{ + // Special case the null string. + if (string.isNull()) { + encoder << std::numeric_limits<uint32_t>::max(); + return; + } + + uint32_t length = string.length(); + encoder << length; + encoder.encodeFixedLengthData(reinterpret_cast<const uint8_t*>(string.data()), length); +} + +bool Coder<CString>::decode(Decoder& decoder, CString& result) +{ + uint32_t length; + if (!decoder.decode(length)) + return false; + + if (length == std::numeric_limits<uint32_t>::max()) { + // This is the null string. + result = CString(); + return true; + } + + // Before allocating the string, make sure that the decoder buffer is big enough. + if (!decoder.bufferIsLargeEnoughToContain<char>(length)) + return false; + + char* buffer; + CString string = CString::newUninitialized(length, buffer); + if (!decoder.decodeFixedLengthData(reinterpret_cast<uint8_t*>(buffer), length)) + return false; + + result = string; + return true; +} + + +void Coder<String>::encode(Encoder& encoder, const String& string) +{ + // Special case the null string. + if (string.isNull()) { + encoder << std::numeric_limits<uint32_t>::max(); + return; + } + + uint32_t length = string.length(); + bool is8Bit = string.is8Bit(); + + encoder << length << is8Bit; + + if (is8Bit) + encoder.encodeFixedLengthData(reinterpret_cast<const uint8_t*>(string.characters8()), length * sizeof(LChar)); + else + encoder.encodeFixedLengthData(reinterpret_cast<const uint8_t*>(string.characters16()), length * sizeof(UChar)); +} + +template <typename CharacterType> +static inline bool decodeStringText(Decoder& decoder, uint32_t length, String& result) +{ + // Before allocating the string, make sure that the decoder buffer is big enough. + if (!decoder.bufferIsLargeEnoughToContain<CharacterType>(length)) + return false; + + CharacterType* buffer; + String string = String::createUninitialized(length, buffer); + if (!decoder.decodeFixedLengthData(reinterpret_cast<uint8_t*>(buffer), length * sizeof(CharacterType))) + return false; + + result = string; + return true; +} + +bool Coder<String>::decode(Decoder& decoder, String& result) +{ + uint32_t length; + if (!decoder.decode(length)) + return false; + + if (length == std::numeric_limits<uint32_t>::max()) { + // This is the null string. + result = String(); + return true; + } + + bool is8Bit; + if (!decoder.decode(is8Bit)) + return false; + + if (is8Bit) + return decodeStringText<LChar>(decoder, length, result); + return decodeStringText<UChar>(decoder, length, result); +} + +void Coder<WebCore::CertificateInfo>::encode(Encoder& encoder, const WebCore::CertificateInfo& certificateInfo) +{ + // FIXME: Cocoa CertificateInfo is a CF object tree. Generalize CF type coding so we don't need to use ArgumentCoder here. + IPC::ArgumentEncoder argumentEncoder; + argumentEncoder << certificateInfo; + encoder << static_cast<uint64_t>(argumentEncoder.bufferSize()); + encoder.encodeFixedLengthData(argumentEncoder.buffer(), argumentEncoder.bufferSize()); +} + +bool Coder<WebCore::CertificateInfo>::decode(Decoder& decoder, WebCore::CertificateInfo& certificateInfo) +{ + uint64_t certificateSize; + if (!decoder.decode(certificateSize)) + return false; + Vector<uint8_t> data(certificateSize); + if (!decoder.decodeFixedLengthData(data.data(), data.size())) + return false; + IPC::ArgumentDecoder argumentDecoder(data.data(), data.size()); + return argumentDecoder.decode(certificateInfo); +} + +void Coder<SHA1::Digest>::encode(Encoder& encoder, const SHA1::Digest& digest) +{ + encoder.encodeFixedLengthData(digest.data(), sizeof(digest)); +} + +bool Coder<SHA1::Digest>::decode(Decoder& decoder, SHA1::Digest& digest) +{ + return decoder.decodeFixedLengthData(digest.data(), sizeof(digest)); +} + +} +} + +#endif diff --git a/Source/WebKit2/NetworkProcess/cache/NetworkCacheCoders.h b/Source/WebKit2/NetworkProcess/cache/NetworkCacheCoders.h new file mode 100644 index 000000000..13aefbf4f --- /dev/null +++ b/Source/WebKit2/NetworkProcess/cache/NetworkCacheCoders.h @@ -0,0 +1,264 @@ +/* + * Copyright (C) 2010, 2014-2015 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. + */ + +#ifndef NetworkCacheCoders_h +#define NetworkCacheCoders_h + +#if ENABLE(NETWORK_CACHE) + +#include "NetworkCacheDecoder.h" +#include "NetworkCacheEncoder.h" +#include <WebCore/CertificateInfo.h> +#include <utility> +#include <wtf/Forward.h> +#include <wtf/HashMap.h> +#include <wtf/HashSet.h> +#include <wtf/SHA1.h> +#include <wtf/Vector.h> + +namespace WebKit { +namespace NetworkCache { + +template<typename T, typename U> struct Coder<std::pair<T, U>> { + static void encode(Encoder& encoder, const std::pair<T, U>& pair) + { + encoder << pair.first << pair.second; + } + + static bool decode(Decoder& decoder, std::pair<T, U>& pair) + { + T first; + if (!decoder.decode(first)) + return false; + + U second; + if (!decoder.decode(second)) + return false; + + pair.first = first; + pair.second = second; + return true; + } +}; + +template<typename Rep, typename Period> struct Coder<std::chrono::duration<Rep, Period>> { + static void encode(Encoder& encoder, const std::chrono::duration<Rep, Period>& duration) + { + static_assert(std::is_integral<Rep>::value && std::is_signed<Rep>::value && sizeof(Rep) <= sizeof(int64_t), "Serialization of this Rep type is not supported yet. Only signed integer type which can be fit in an int64_t is currently supported."); + encoder << static_cast<int64_t>(duration.count()); + } + + static bool decode(Decoder& decoder, std::chrono::duration<Rep, Period>& result) + { + int64_t count; + if (!decoder.decode(count)) + return false; + result = std::chrono::duration<Rep, Period>(static_cast<Rep>(count)); + return true; + } +}; + +template<typename KeyType, typename ValueType> struct Coder<WTF::KeyValuePair<KeyType, ValueType>> { + static void encode(Encoder& encoder, const WTF::KeyValuePair<KeyType, ValueType>& pair) + { + encoder << pair.key << pair.value; + } + + static bool decode(Decoder& decoder, WTF::KeyValuePair<KeyType, ValueType>& pair) + { + KeyType key; + if (!decoder.decode(key)) + return false; + + ValueType value; + if (!decoder.decode(value)) + return false; + + pair.key = key; + pair.value = value; + return true; + } +}; + +template<bool fixedSizeElements, typename T, size_t inlineCapacity> struct VectorCoder; + +template<typename T, size_t inlineCapacity> struct VectorCoder<false, T, inlineCapacity> { + static void encode(Encoder& encoder, const Vector<T, inlineCapacity>& vector) + { + encoder << static_cast<uint64_t>(vector.size()); + for (size_t i = 0; i < vector.size(); ++i) + encoder << vector[i]; + } + + static bool decode(Decoder& decoder, Vector<T, inlineCapacity>& vector) + { + uint64_t size; + if (!decoder.decode(size)) + return false; + + Vector<T, inlineCapacity> tmp; + for (size_t i = 0; i < size; ++i) { + T element; + if (!decoder.decode(element)) + return false; + + tmp.append(WTFMove(element)); + } + + tmp.shrinkToFit(); + vector.swap(tmp); + return true; + } +}; + +template<typename T, size_t inlineCapacity> struct VectorCoder<true, T, inlineCapacity> { + static void encode(Encoder& encoder, const Vector<T, inlineCapacity>& vector) + { + encoder << static_cast<uint64_t>(vector.size()); + encoder.encodeFixedLengthData(reinterpret_cast<const uint8_t*>(vector.data()), vector.size() * sizeof(T), alignof(T)); + } + + static bool decode(Decoder& decoder, Vector<T, inlineCapacity>& vector) + { + uint64_t size; + if (!decoder.decode(size)) + return false; + + // Since we know the total size of the elements, we can allocate the vector in + // one fell swoop. Before allocating we must however make sure that the decoder buffer + // is big enough. + if (!decoder.bufferIsLargeEnoughToContain<T>(size)) + return false; + + Vector<T, inlineCapacity> temp; + temp.resize(size); + + decoder.decodeFixedLengthData(reinterpret_cast<uint8_t*>(temp.data()), size * sizeof(T)); + + vector.swap(temp); + return true; + } +}; + +template<typename T, size_t inlineCapacity> struct Coder<Vector<T, inlineCapacity>> : VectorCoder<std::is_arithmetic<T>::value, T, inlineCapacity> { }; + +template<typename KeyArg, typename MappedArg, typename HashArg, typename KeyTraitsArg, typename MappedTraitsArg> struct Coder<HashMap<KeyArg, MappedArg, HashArg, KeyTraitsArg, MappedTraitsArg>> { + typedef HashMap<KeyArg, MappedArg, HashArg, KeyTraitsArg, MappedTraitsArg> HashMapType; + + static void encode(Encoder& encoder, const HashMapType& hashMap) + { + encoder << static_cast<uint64_t>(hashMap.size()); + for (typename HashMapType::const_iterator it = hashMap.begin(), end = hashMap.end(); it != end; ++it) + encoder << *it; + } + + static bool decode(Decoder& decoder, HashMapType& hashMap) + { + uint64_t hashMapSize; + if (!decoder.decode(hashMapSize)) + return false; + + HashMapType tempHashMap; + for (uint64_t i = 0; i < hashMapSize; ++i) { + KeyArg key; + MappedArg value; + if (!decoder.decode(key)) + return false; + if (!decoder.decode(value)) + return false; + + if (!tempHashMap.add(key, value).isNewEntry) { + // The hash map already has the specified key, bail. + return false; + } + } + + hashMap.swap(tempHashMap); + return true; + } +}; + +template<typename KeyArg, typename HashArg, typename KeyTraitsArg> struct Coder<HashSet<KeyArg, HashArg, KeyTraitsArg>> { + typedef HashSet<KeyArg, HashArg, KeyTraitsArg> HashSetType; + + static void encode(Encoder& encoder, const HashSetType& hashSet) + { + encoder << static_cast<uint64_t>(hashSet.size()); + for (typename HashSetType::const_iterator it = hashSet.begin(), end = hashSet.end(); it != end; ++it) + encoder << *it; + } + + static bool decode(Decoder& decoder, HashSetType& hashSet) + { + uint64_t hashSetSize; + if (!decoder.decode(hashSetSize)) + return false; + + HashSetType tempHashSet; + for (uint64_t i = 0; i < hashSetSize; ++i) { + KeyArg key; + if (!decoder.decode(key)) + return false; + + if (!tempHashSet.add(key).isNewEntry) { + // The hash map already has the specified key, bail. + return false; + } + } + + hashSet.swap(tempHashSet); + return true; + } +}; + +template<> struct Coder<AtomicString> { + static void encode(Encoder&, const AtomicString&); + static bool decode(Decoder&, AtomicString&); +}; + +template<> struct Coder<CString> { + static void encode(Encoder&, const CString&); + static bool decode(Decoder&, CString&); +}; + +template<> struct Coder<String> { + static void encode(Encoder&, const String&); + static bool decode(Decoder&, String&); +}; + +template<> struct Coder<WebCore::CertificateInfo> { + static void encode(Encoder&, const WebCore::CertificateInfo&); + static bool decode(Decoder&, WebCore::CertificateInfo&); +}; + +template<> struct Coder<SHA1::Digest> { + static void encode(Encoder&, const SHA1::Digest&); + static bool decode(Decoder&, SHA1::Digest&); +}; + +} +} +#endif +#endif diff --git a/Source/WebKit2/NetworkProcess/cache/NetworkCacheData.cpp b/Source/WebKit2/NetworkProcess/cache/NetworkCacheData.cpp new file mode 100644 index 000000000..f950d69ed --- /dev/null +++ b/Source/WebKit2/NetworkProcess/cache/NetworkCacheData.cpp @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2015 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 "NetworkCacheData.h" + +#if ENABLE(NETWORK_CACHE) + +#include <fcntl.h> +#include <sys/mman.h> +#include <sys/stat.h> + +namespace WebKit { +namespace NetworkCache { + +Data Data::mapToFile(const char* path) const +{ + int fd = open(path, O_CREAT | O_EXCL | O_RDWR , S_IRUSR | S_IWUSR); + if (fd < 0) + return { }; + + if (ftruncate(fd, m_size) < 0) { + close(fd); + return { }; + } + + void* map = mmap(nullptr, m_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (map == MAP_FAILED) { + close(fd); + return { }; + } + + uint8_t* mapData = static_cast<uint8_t*>(map); + apply([&mapData](const uint8_t* bytes, size_t bytesSize) { + memcpy(mapData, bytes, bytesSize); + mapData += bytesSize; + return true; + }); + + // Drop the write permission. + mprotect(map, m_size, PROT_READ); + + // Flush (asynchronously) to file, turning this into clean memory. + msync(map, m_size, MS_ASYNC); + + return Data::adoptMap(map, m_size, fd); +} + +Data mapFile(const char* path) +{ + int fd = open(path, O_RDONLY, 0); + if (fd < 0) + return { }; + struct stat stat; + if (fstat(fd, &stat) < 0) { + close(fd); + return { }; + } + size_t size = stat.st_size; + if (!size) { + close(fd); + return Data::empty(); + } + + return adoptAndMapFile(fd, 0, size); +} + +Data adoptAndMapFile(int fd, size_t offset, size_t size) +{ + if (!size) { + close(fd); + return Data::empty(); + } + + void* map = mmap(nullptr, size, PROT_READ, MAP_PRIVATE, fd, offset); + if (map == MAP_FAILED) { + close(fd); + return { }; + } + + return Data::adoptMap(map, size, fd); +} + +SHA1::Digest computeSHA1(const Data& data) +{ + SHA1 sha1; + data.apply([&sha1](const uint8_t* data, size_t size) { + sha1.addBytes(data, size); + return true; + }); + SHA1::Digest digest; + sha1.computeHash(digest); + return digest; +} + +bool bytesEqual(const Data& a, const Data& b) +{ + if (a.isNull() || b.isNull()) + return false; + if (a.size() != b.size()) + return false; + return !memcmp(a.data(), b.data(), a.size()); +} + +} // namespace NetworkCache +} // namespace WebKit + +#endif // #if ENABLE(NETWORK_CACHE) diff --git a/Source/WebKit2/NetworkProcess/cache/NetworkCacheData.h b/Source/WebKit2/NetworkProcess/cache/NetworkCacheData.h new file mode 100644 index 000000000..225589854 --- /dev/null +++ b/Source/WebKit2/NetworkProcess/cache/NetworkCacheData.h @@ -0,0 +1,163 @@ +/* + * Copyright (C) 2015 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. + */ + +#ifndef NetworkCacheData_h +#define NetworkCacheData_h + +#if ENABLE(NETWORK_CACHE) + +#include <functional> +#include <wtf/FunctionDispatcher.h> +#include <wtf/SHA1.h> +#include <wtf/ThreadSafeRefCounted.h> +#include <wtf/text/WTFString.h> + +#if USE(SOUP) +#include <WebCore/GRefPtrSoup.h> +#endif + +namespace WebKit { + +class SharedMemory; + +namespace NetworkCache { + +#if PLATFORM(COCOA) +template <typename T> class DispatchPtr; +template <typename T> DispatchPtr<T> adoptDispatch(T dispatchObject); + +// FIXME: Use OSObjectPtr instead when it works with dispatch_data_t on all platforms. +template<typename T> class DispatchPtr { +public: + DispatchPtr() + : m_ptr(nullptr) + { + } + explicit DispatchPtr(T ptr) + : m_ptr(ptr) + { + if (m_ptr) + dispatch_retain(m_ptr); + } + DispatchPtr(const DispatchPtr& other) + : m_ptr(other.m_ptr) + { + if (m_ptr) + dispatch_retain(m_ptr); + } + ~DispatchPtr() + { + if (m_ptr) + dispatch_release(m_ptr); + } + + DispatchPtr& operator=(const DispatchPtr& other) + { + auto copy = other; + std::swap(m_ptr, copy.m_ptr); + return *this; + } + + T get() const { return m_ptr; } + explicit operator bool() const { return m_ptr; } + + friend DispatchPtr adoptDispatch<T>(T); + +private: + struct Adopt { }; + DispatchPtr(Adopt, T data) + : m_ptr(data) + { + } + + T m_ptr; +}; + +template <typename T> DispatchPtr<T> adoptDispatch(T dispatchObject) +{ + return DispatchPtr<T>(typename DispatchPtr<T>::Adopt { }, dispatchObject); +} +#endif + +class Data { +public: + Data() { } + Data(const uint8_t*, size_t); + + static Data empty(); + static Data adoptMap(void* map, size_t, int fd); + +#if PLATFORM(COCOA) + enum class Backing { Buffer, Map }; + Data(DispatchPtr<dispatch_data_t>, Backing = Backing::Buffer); +#endif +#if USE(SOUP) + Data(GRefPtr<SoupBuffer>&&, int fd = -1); +#endif + bool isNull() const; + bool isEmpty() const { return !m_size; } + + const uint8_t* data() const; + size_t size() const { return m_size; } + bool isMap() const { return m_isMap; } + RefPtr<SharedMemory> tryCreateSharedMemory() const; + + Data subrange(size_t offset, size_t) const; + + bool apply(const std::function<bool (const uint8_t*, size_t)>&&) const; + + Data mapToFile(const char* path) const; + +#if PLATFORM(COCOA) + dispatch_data_t dispatchData() const { return m_dispatchData.get(); } +#endif + +#if USE(SOUP) + SoupBuffer* soupBuffer() const { return m_buffer.get(); } +#endif +private: +#if PLATFORM(COCOA) + mutable DispatchPtr<dispatch_data_t> m_dispatchData; +#endif +#if USE(SOUP) + mutable GRefPtr<SoupBuffer> m_buffer; + int m_fileDescriptor { -1 }; +#endif + mutable const uint8_t* m_data { nullptr }; + size_t m_size { 0 }; + bool m_isMap { false }; +}; + +Data concatenate(const Data&, const Data&); +bool bytesEqual(const Data&, const Data&); +Data adoptAndMapFile(int fd, size_t offset, size_t); +Data mapFile(const char* path); +SHA1::Digest computeSHA1(const Data&); + +} +} + +#endif +#endif diff --git a/Source/WebKit2/NetworkProcess/cache/NetworkCacheDataSoup.cpp b/Source/WebKit2/NetworkProcess/cache/NetworkCacheDataSoup.cpp new file mode 100644 index 000000000..644c38e30 --- /dev/null +++ b/Source/WebKit2/NetworkProcess/cache/NetworkCacheDataSoup.cpp @@ -0,0 +1,142 @@ +/* + * Copyright (C) 2015 Igalia S.L + * + * 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 "NetworkCacheData.h" + +#if ENABLE(NETWORK_CACHE) + +#include "SharedMemory.h" +#include <fcntl.h> +#include <sys/mman.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + +namespace WebKit { +namespace NetworkCache { + +Data::Data(const uint8_t* data, size_t size) + : m_size(size) +{ + uint8_t* copiedData = static_cast<uint8_t*>(fastMalloc(size)); + memcpy(copiedData, data, size); + m_buffer = adoptGRef(soup_buffer_new_with_owner(copiedData, size, copiedData, fastFree)); +} + +Data::Data(GRefPtr<SoupBuffer>&& buffer, int fd) + : m_buffer(buffer) + , m_fileDescriptor(fd) + , m_size(buffer ? buffer->length : 0) + , m_isMap(m_size && fd != -1) +{ +} + +Data Data::empty() +{ + GRefPtr<SoupBuffer> buffer = adoptGRef(soup_buffer_new(SOUP_MEMORY_TAKE, nullptr, 0)); + return { WTFMove(buffer) }; +} + +const uint8_t* Data::data() const +{ + return m_buffer ? reinterpret_cast<const uint8_t*>(m_buffer->data) : nullptr; +} + +bool Data::isNull() const +{ + return !m_buffer; +} + +bool Data::apply(const std::function<bool (const uint8_t*, size_t)>&& applier) const +{ + if (!m_size) + return false; + + return applier(reinterpret_cast<const uint8_t*>(m_buffer->data), m_buffer->length); +} + +Data Data::subrange(size_t offset, size_t size) const +{ + if (!m_buffer) + return { }; + + GRefPtr<SoupBuffer> subBuffer = adoptGRef(soup_buffer_new_subbuffer(m_buffer.get(), offset, size)); + return { WTFMove(subBuffer) }; +} + +Data concatenate(const Data& a, const Data& b) +{ + if (a.isNull()) + return b; + if (b.isNull()) + return a; + + size_t size = a.size() + b.size(); + uint8_t* data = static_cast<uint8_t*>(fastMalloc(size)); + memcpy(data, a.soupBuffer()->data, a.soupBuffer()->length); + memcpy(data + a.soupBuffer()->length, b.soupBuffer()->data, b.soupBuffer()->length); + GRefPtr<SoupBuffer> buffer = adoptGRef(soup_buffer_new_with_owner(data, size, data, fastFree)); + return { WTFMove(buffer) }; +} + +struct MapWrapper { + ~MapWrapper() + { + munmap(map, size); + close(fileDescriptor); + } + + void* map; + size_t size; + int fileDescriptor; +}; + +static void deleteMapWrapper(MapWrapper* wrapper) +{ + delete wrapper; +} + +Data Data::adoptMap(void* map, size_t size, int fd) +{ + ASSERT(map); + ASSERT(map != MAP_FAILED); + MapWrapper* wrapper = new MapWrapper { map, size, fd }; + GRefPtr<SoupBuffer> buffer = adoptGRef(soup_buffer_new_with_owner(map, size, wrapper, reinterpret_cast<GDestroyNotify>(deleteMapWrapper))); + return { WTFMove(buffer), fd }; +} + +RefPtr<SharedMemory> Data::tryCreateSharedMemory() const +{ + if (isNull() || !isMap()) + return nullptr; + + return SharedMemory::wrapMap(const_cast<char*>(m_buffer->data), m_buffer->length, m_fileDescriptor); +} + +} // namespace NetworkCache +} // namespace WebKit + +#endif diff --git a/Source/WebKit2/NetworkProcess/cache/NetworkCacheDecoder.cpp b/Source/WebKit2/NetworkProcess/cache/NetworkCacheDecoder.cpp new file mode 100644 index 000000000..80e657578 --- /dev/null +++ b/Source/WebKit2/NetworkProcess/cache/NetworkCacheDecoder.cpp @@ -0,0 +1,137 @@ +/* + * Copyright (C) 2010, 2011, 2014 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 "NetworkCacheDecoder.h" + +#if ENABLE(NETWORK_CACHE) + +#include "NetworkCacheEncoder.h" + +namespace WebKit { +namespace NetworkCache { + +Decoder::Decoder(const uint8_t* buffer, size_t bufferSize) + : m_buffer(buffer) + , m_bufferPosition(buffer) + , m_bufferEnd(buffer + bufferSize) +{ +} + +Decoder::~Decoder() +{ +} + +bool Decoder::bufferIsLargeEnoughToContain(size_t size) const +{ + return size <= static_cast<size_t>(m_bufferEnd - m_bufferPosition); +} + +bool Decoder::decodeFixedLengthData(uint8_t* data, size_t size) +{ + if (!bufferIsLargeEnoughToContain(size)) + return false; + + memcpy(data, m_bufferPosition, size); + m_bufferPosition += size; + + Encoder::updateChecksumForData(m_sha1, data, size); + return true; +} + +template<typename Type> +bool Decoder::decodeNumber(Type& value) +{ + if (!bufferIsLargeEnoughToContain(sizeof(value))) + return false; + + memcpy(&value, m_bufferPosition, sizeof(value)); + m_bufferPosition += sizeof(Type); + + Encoder::updateChecksumForNumber(m_sha1, value); + return true; +} + +bool Decoder::decode(bool& result) +{ + return decodeNumber(result); +} + +bool Decoder::decode(uint8_t& result) +{ + return decodeNumber(result); +} + +bool Decoder::decode(uint16_t& result) +{ + return decodeNumber(result); +} + +bool Decoder::decode(uint32_t& result) +{ + return decodeNumber(result); +} + +bool Decoder::decode(uint64_t& result) +{ + return decodeNumber(result); +} + +bool Decoder::decode(int32_t& result) +{ + return decodeNumber(result); +} + +bool Decoder::decode(int64_t& result) +{ + return decodeNumber(result); +} + +bool Decoder::decode(float& result) +{ + return decodeNumber(result); +} + +bool Decoder::decode(double& result) +{ + return decodeNumber(result); +} + +bool Decoder::verifyChecksum() +{ + SHA1::Digest computedHash; + m_sha1.computeHash(computedHash); + + SHA1::Digest savedHash; + if (!decodeFixedLengthData(savedHash.data(), sizeof(savedHash))) + return false; + + return computedHash == savedHash; +} + +} +} + +#endif diff --git a/Source/WebKit2/NetworkProcess/cache/NetworkCacheDecoder.h b/Source/WebKit2/NetworkProcess/cache/NetworkCacheDecoder.h new file mode 100644 index 000000000..12e3d03dc --- /dev/null +++ b/Source/WebKit2/NetworkProcess/cache/NetworkCacheDecoder.h @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2014 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. + */ + +#ifndef NetworkCacheDecoder_h +#define NetworkCacheDecoder_h + +#if ENABLE(NETWORK_CACHE) + +#include "NetworkCacheCoder.h" +#include <wtf/SHA1.h> + +namespace WebKit { +namespace NetworkCache { + +class Decoder { + WTF_MAKE_FAST_ALLOCATED; +public: + Decoder(const uint8_t* buffer, size_t bufferSize); + virtual ~Decoder(); + + size_t length() const { return m_bufferEnd - m_buffer; } + size_t currentOffset() const { return m_bufferPosition - m_buffer; } + + bool verifyChecksum(); + + bool decodeFixedLengthData(uint8_t*, size_t); + + bool decode(bool&); + bool decode(uint8_t&); + bool decode(uint16_t&); + bool decode(uint32_t&); + bool decode(uint64_t&); + bool decode(int32_t&); + bool decode(int64_t&); + bool decode(float&); + bool decode(double&); + + template<typename T> bool decodeEnum(T& result) + { + static_assert(sizeof(T) <= 8, "Enum type T must not be larger than 64 bits!"); + + uint64_t value; + if (!decode(value)) + return false; + + result = static_cast<T>(value); + return true; + } + + template<typename T> bool decode(T& t) + { + return Coder<T>::decode(*this, t); + } + + template<typename T> + bool bufferIsLargeEnoughToContain(size_t numElements) const + { + static_assert(std::is_arithmetic<T>::value, "Type T must have a fixed, known encoded size!"); + + if (numElements > std::numeric_limits<size_t>::max() / sizeof(T)) + return false; + + return bufferIsLargeEnoughToContain(numElements * sizeof(T)); + } + +private: + bool bufferIsLargeEnoughToContain(size_t) const; + template<typename Type> bool decodeNumber(Type&); + + const uint8_t* m_buffer; + const uint8_t* m_bufferPosition; + const uint8_t* m_bufferEnd; + + SHA1 m_sha1; +}; + +} +} + +#endif +#endif diff --git a/Source/WebKit2/NetworkProcess/cache/NetworkCacheEncoder.cpp b/Source/WebKit2/NetworkProcess/cache/NetworkCacheEncoder.cpp new file mode 100644 index 000000000..0c2d7191d --- /dev/null +++ b/Source/WebKit2/NetworkProcess/cache/NetworkCacheEncoder.cpp @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2010, 2014 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 "NetworkCacheEncoder.h" + +#if ENABLE(NETWORK_CACHE) + +namespace WebKit { +namespace NetworkCache { + +Encoder::Encoder() +{ +} + +Encoder::~Encoder() +{ +} + +uint8_t* Encoder::grow(size_t size) +{ + size_t newPosition = m_buffer.size(); + m_buffer.grow(m_buffer.size() + size); + return m_buffer.data() + newPosition; +} + +void Encoder::updateChecksumForData(SHA1& sha1, const uint8_t* data, size_t size) +{ + auto typeSalt = Salt<uint8_t*>::value; + sha1.addBytes(reinterpret_cast<uint8_t*>(&typeSalt), sizeof(typeSalt)); + sha1.addBytes(data, size); +} + +void Encoder::encodeFixedLengthData(const uint8_t* data, size_t size) +{ + updateChecksumForData(m_sha1, data, size); + + uint8_t* buffer = grow(size); + memcpy(buffer, data, size); +} + +template<typename Type> +void Encoder::encodeNumber(Type value) +{ + Encoder::updateChecksumForNumber(m_sha1, value); + + uint8_t* buffer = grow(sizeof(Type)); + memcpy(buffer, &value, sizeof(Type)); +} + +void Encoder::encode(bool value) +{ + encodeNumber(value); +} + +void Encoder::encode(uint8_t value) +{ + encodeNumber(value); +} + +void Encoder::encode(uint16_t value) +{ + encodeNumber(value); +} + +void Encoder::encode(uint32_t value) +{ + encodeNumber(value); +} + +void Encoder::encode(uint64_t value) +{ + encodeNumber(value); +} + +void Encoder::encode(int32_t value) +{ + encodeNumber(value); +} + +void Encoder::encode(int64_t value) +{ + encodeNumber(value); +} + +void Encoder::encode(float value) +{ + encodeNumber(value); +} + +void Encoder::encode(double value) +{ + encodeNumber(value); +} + +void Encoder::encodeChecksum() +{ + SHA1::Digest hash; + m_sha1.computeHash(hash); + encodeFixedLengthData(hash.data(), hash.size()); +} + +} +} + +#endif diff --git a/Source/WebKit2/NetworkProcess/cache/NetworkCacheEncoder.h b/Source/WebKit2/NetworkProcess/cache/NetworkCacheEncoder.h new file mode 100644 index 000000000..469c06c59 --- /dev/null +++ b/Source/WebKit2/NetworkProcess/cache/NetworkCacheEncoder.h @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2010 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. + */ + +#ifndef NetworkCacheEncoder_h +#define NetworkCacheEncoder_h + +#if ENABLE(NETWORK_CACHE) + +#include "NetworkCacheCoder.h" +#include <wtf/SHA1.h> +#include <wtf/Vector.h> + +namespace WebKit { +namespace NetworkCache { + +class Encoder; +class DataReference; + +class Encoder { + WTF_MAKE_FAST_ALLOCATED; +public: + Encoder(); + virtual ~Encoder(); + + void encodeChecksum(); + void encodeFixedLengthData(const uint8_t*, size_t); + + template<typename T> void encodeEnum(T t) + { + COMPILE_ASSERT(sizeof(T) <= sizeof(uint64_t), enum_type_must_not_be_larger_than_64_bits); + + encode(static_cast<uint64_t>(t)); + } + + template<typename T> void encode(const T& t) + { + Coder<T>::encode(*this, t); + } + + template<typename T> Encoder& operator<<(const T& t) + { + encode(t); + return *this; + } + + const uint8_t* buffer() const { return m_buffer.data(); } + size_t bufferSize() const { return m_buffer.size(); } + + static void updateChecksumForData(SHA1&, const uint8_t*, size_t); + template <typename Type> static void updateChecksumForNumber(SHA1&, Type); + +private: + void encode(bool); + void encode(uint8_t); + void encode(uint16_t); + void encode(uint32_t); + void encode(uint64_t); + void encode(int32_t); + void encode(int64_t); + void encode(float); + void encode(double); + + template<typename Type> void encodeNumber(Type); + + uint8_t* grow(size_t); + + template <typename Type> struct Salt; + + Vector<uint8_t, 4096> m_buffer; + SHA1 m_sha1; +}; + +template <> struct Encoder::Salt<bool> { static const unsigned value = 3; }; +template <> struct Encoder::Salt<uint8_t> { static const unsigned value = 5; }; +template <> struct Encoder::Salt<uint16_t> { static const unsigned value = 7; }; +template <> struct Encoder::Salt<uint32_t> { static const unsigned value = 11; }; +template <> struct Encoder::Salt<uint64_t> { static const unsigned value = 13; }; +template <> struct Encoder::Salt<int32_t> { static const unsigned value = 17; }; +template <> struct Encoder::Salt<int64_t> { static const unsigned value = 19; }; +template <> struct Encoder::Salt<float> { static const unsigned value = 23; }; +template <> struct Encoder::Salt<double> { static const unsigned value = 29; }; +template <> struct Encoder::Salt<uint8_t*> { static const unsigned value = 101; }; + +template <typename Type> +void Encoder::updateChecksumForNumber(SHA1& sha1, Type value) +{ + auto typeSalt = Salt<Type>::value; + sha1.addBytes(reinterpret_cast<uint8_t*>(&typeSalt), sizeof(typeSalt)); + sha1.addBytes(reinterpret_cast<uint8_t*>(&value), sizeof(value)); +} + +} +} + +#endif +#endif diff --git a/Source/WebKit2/NetworkProcess/cache/NetworkCacheEntry.cpp b/Source/WebKit2/NetworkProcess/cache/NetworkCacheEntry.cpp new file mode 100644 index 000000000..faa4f04d1 --- /dev/null +++ b/Source/WebKit2/NetworkProcess/cache/NetworkCacheEntry.cpp @@ -0,0 +1,242 @@ +/* + * Copyright (C) 2015 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 "NetworkCache.h" + +#include "Logging.h" +#include "NetworkCacheCoders.h" +#include "NetworkCacheDecoder.h" +#include "NetworkCacheEncoder.h" +#include <WebCore/ResourceRequest.h> +#include <WebCore/SharedBuffer.h> +#include <wtf/text/StringBuilder.h> + +#if ENABLE(NETWORK_CACHE) + +namespace WebKit { +namespace NetworkCache { + +Entry::Entry(const Key& key, const WebCore::ResourceResponse& response, RefPtr<WebCore::SharedBuffer>&& buffer, const Vector<std::pair<String, String>>& varyingRequestHeaders) + : m_key(key) + , m_timeStamp(std::chrono::system_clock::now()) + , m_response(response) + , m_varyingRequestHeaders(varyingRequestHeaders) + , m_buffer(WTFMove(buffer)) +{ + ASSERT(m_key.type() == "resource"); +} + +Entry::Entry(const Key& key, const WebCore::ResourceResponse& response, const WebCore::ResourceRequest& redirectRequest, const Vector<std::pair<String, String>>& varyingRequestHeaders) + : m_key(key) + , m_timeStamp(std::chrono::system_clock::now()) + , m_response(response) + , m_varyingRequestHeaders(varyingRequestHeaders) + , m_redirectRequest(WebCore::ResourceRequest::adopt(redirectRequest.copyData())) // Don't include the underlying platform request object. +{ + ASSERT(m_key.type() == "resource"); + // Redirect body is not needed even if exists. + m_redirectRequest->setHTTPBody(nullptr); +} + +Entry::Entry(const Entry& other) + : m_key(other.m_key) + , m_timeStamp(other.m_timeStamp) + , m_response(other.m_response) + , m_varyingRequestHeaders(other.m_varyingRequestHeaders) + , m_buffer(other.m_buffer) + , m_sourceStorageRecord(other.m_sourceStorageRecord) +{ +} + +Entry::Entry(const Storage::Record& storageEntry) + : m_key(storageEntry.key) + , m_timeStamp(storageEntry.timeStamp) + , m_sourceStorageRecord(storageEntry) +{ + ASSERT(m_key.type() == "resource"); +} + +Storage::Record Entry::encodeAsStorageRecord() const +{ + Encoder encoder; + encoder << m_response; + + bool hasVaryingRequestHeaders = !m_varyingRequestHeaders.isEmpty(); + encoder << hasVaryingRequestHeaders; + if (hasVaryingRequestHeaders) + encoder << m_varyingRequestHeaders; + + bool isRedirect = !!m_redirectRequest; + encoder << isRedirect; + if (isRedirect) + m_redirectRequest->encodeWithoutPlatformData(encoder); + + encoder.encodeChecksum(); + + Data header(encoder.buffer(), encoder.bufferSize()); + Data body; + if (m_buffer) + body = { reinterpret_cast<const uint8_t*>(m_buffer->data()), m_buffer->size() }; + + return { m_key, m_timeStamp, header, body }; +} + +std::unique_ptr<Entry> Entry::decodeStorageRecord(const Storage::Record& storageEntry) +{ + auto entry = std::make_unique<Entry>(storageEntry); + + Decoder decoder(storageEntry.header.data(), storageEntry.header.size()); + if (!decoder.decode(entry->m_response)) + return nullptr; + entry->m_response.setSource(WebCore::ResourceResponse::Source::DiskCache); + + bool hasVaryingRequestHeaders; + if (!decoder.decode(hasVaryingRequestHeaders)) + return nullptr; + + if (hasVaryingRequestHeaders) { + if (!decoder.decode(entry->m_varyingRequestHeaders)) + return nullptr; + } + + bool isRedirect; + if (!decoder.decode(isRedirect)) + return nullptr; + + if (isRedirect) { + entry->m_redirectRequest = std::make_unique<WebCore::ResourceRequest>(); + if (!entry->m_redirectRequest->decodeWithoutPlatformData(decoder)) + return nullptr; + } + + if (!decoder.verifyChecksum()) { + LOG(NetworkCache, "(NetworkProcess) checksum verification failure\n"); + return nullptr; + } + + return entry; +} + +#if ENABLE(SHAREABLE_RESOURCE) +void Entry::initializeShareableResourceHandleFromStorageRecord() const +{ + RefPtr<SharedMemory> sharedMemory = m_sourceStorageRecord.body.tryCreateSharedMemory(); + if (!sharedMemory) + return; + + RefPtr<ShareableResource> shareableResource = ShareableResource::create(sharedMemory.release(), 0, m_sourceStorageRecord.body.size()); + ASSERT(shareableResource); + shareableResource->createHandle(m_shareableResourceHandle); +} +#endif + +void Entry::initializeBufferFromStorageRecord() const +{ +#if ENABLE(SHAREABLE_RESOURCE) + if (!shareableResourceHandle().isNull()) { + m_buffer = m_shareableResourceHandle.tryWrapInSharedBuffer(); + if (m_buffer) + return; + } +#endif + m_buffer = WebCore::SharedBuffer::create(m_sourceStorageRecord.body.data(), m_sourceStorageRecord.body.size()); +} + +WebCore::SharedBuffer* Entry::buffer() const +{ + if (!m_buffer) + initializeBufferFromStorageRecord(); + + return m_buffer.get(); +} + +#if ENABLE(SHAREABLE_RESOURCE) +ShareableResource::Handle& Entry::shareableResourceHandle() const +{ + if (m_shareableResourceHandle.isNull()) + initializeShareableResourceHandleFromStorageRecord(); + + return m_shareableResourceHandle; +} +#endif + +bool Entry::needsValidation() const +{ + return m_response.source() == WebCore::ResourceResponse::Source::DiskCacheAfterValidation; +} + +void Entry::setNeedsValidation() +{ + ASSERT(m_response.source() == WebCore::ResourceResponse::Source::DiskCache); + m_response.setSource(WebCore::ResourceResponse::Source::DiskCacheAfterValidation); +} + +void Entry::asJSON(StringBuilder& json, const Storage::RecordInfo& info) const +{ + json.appendLiteral("{\n"); + json.appendLiteral("\"hash\": "); + json.appendQuotedJSONString(m_key.hashAsString()); + json.appendLiteral(",\n"); + json.appendLiteral("\"bodySize\": "); + json.appendNumber(info.bodySize); + json.appendLiteral(",\n"); + json.appendLiteral("\"worth\": "); + json.appendNumber(info.worth); + json.appendLiteral(",\n"); + json.appendLiteral("\"partition\": "); + json.appendQuotedJSONString(m_key.partition()); + json.appendLiteral(",\n"); + json.appendLiteral("\"timestamp\": "); + json.appendNumber(std::chrono::duration_cast<std::chrono::milliseconds>(m_timeStamp.time_since_epoch()).count()); + json.appendLiteral(",\n"); + json.appendLiteral("\"URL\": "); + json.appendQuotedJSONString(m_response.url().string()); + json.appendLiteral(",\n"); + json.appendLiteral("\"bodyHash\": "); + json.appendQuotedJSONString(info.bodyHash); + json.appendLiteral(",\n"); + json.appendLiteral("\"bodyShareCount\": "); + json.appendNumber(info.bodyShareCount); + json.appendLiteral(",\n"); + json.appendLiteral("\"headers\": {\n"); + bool firstHeader = true; + for (auto& header : m_response.httpHeaderFields()) { + if (!firstHeader) + json.appendLiteral(",\n"); + firstHeader = false; + json.appendLiteral(" "); + json.appendQuotedJSONString(header.key); + json.appendLiteral(": "); + json.appendQuotedJSONString(header.value); + } + json.appendLiteral("\n}\n"); + json.appendLiteral("}"); +} + +} +} + +#endif diff --git a/Source/WebKit2/NetworkProcess/cache/NetworkCacheEntry.h b/Source/WebKit2/NetworkProcess/cache/NetworkCacheEntry.h new file mode 100644 index 000000000..0855a4efe --- /dev/null +++ b/Source/WebKit2/NetworkProcess/cache/NetworkCacheEntry.h @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2015 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. + */ + +#ifndef NetworkCacheEntry_h +#define NetworkCacheEntry_h + +#if ENABLE(NETWORK_CACHE) + +#include "NetworkCacheStorage.h" +#include "ShareableResource.h" +#include <WebCore/ResourceRequest.h> +#include <WebCore/ResourceResponse.h> +#include <wtf/Noncopyable.h> +#include <wtf/text/WTFString.h> + +namespace WebCore { +class SharedBuffer; +} + +namespace WebKit { +namespace NetworkCache { + +class Entry { + WTF_MAKE_FAST_ALLOCATED; +public: + Entry(const Key&, const WebCore::ResourceResponse&, RefPtr<WebCore::SharedBuffer>&&, const Vector<std::pair<String, String>>& varyingRequestHeaders); + Entry(const Key&, const WebCore::ResourceResponse&, const WebCore::ResourceRequest& redirectRequest, const Vector<std::pair<String, String>>& varyingRequestHeaders); + explicit Entry(const Storage::Record&); + Entry(const Entry&); + + Storage::Record encodeAsStorageRecord() const; + static std::unique_ptr<Entry> decodeStorageRecord(const Storage::Record&); + + const Key& key() const { return m_key; } + std::chrono::system_clock::time_point timeStamp() const { return m_timeStamp; } + const WebCore::ResourceResponse& response() const { return m_response; } + const Vector<std::pair<String, String>>& varyingRequestHeaders() const { return m_varyingRequestHeaders; } + + WebCore::SharedBuffer* buffer() const; + const WebCore::ResourceRequest* redirectRequest() const { return m_redirectRequest.get(); } + +#if ENABLE(SHAREABLE_RESOURCE) + ShareableResource::Handle& shareableResourceHandle() const; +#endif + + bool needsValidation() const; + void setNeedsValidation(); + + const Storage::Record& sourceStorageRecord() const { return m_sourceStorageRecord; } + + void asJSON(StringBuilder&, const Storage::RecordInfo&) const; + +private: + void initializeBufferFromStorageRecord() const; +#if ENABLE(SHAREABLE_RESOURCE) + void initializeShareableResourceHandleFromStorageRecord() const; +#endif + + Key m_key; + std::chrono::system_clock::time_point m_timeStamp; + WebCore::ResourceResponse m_response; + Vector<std::pair<String, String>> m_varyingRequestHeaders; + + std::unique_ptr<WebCore::ResourceRequest> m_redirectRequest; + mutable RefPtr<WebCore::SharedBuffer> m_buffer; +#if ENABLE(SHAREABLE_RESOURCE) + mutable ShareableResource::Handle m_shareableResourceHandle; +#endif + + Storage::Record m_sourceStorageRecord { }; +}; + +} +} + +#endif +#endif diff --git a/Source/WebKit2/NetworkProcess/cache/NetworkCacheFileSystem.cpp b/Source/WebKit2/NetworkProcess/cache/NetworkCacheFileSystem.cpp new file mode 100644 index 000000000..93d6a58d6 --- /dev/null +++ b/Source/WebKit2/NetworkProcess/cache/NetworkCacheFileSystem.cpp @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2015 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 "NetworkCacheFileSystem.h" + +#if ENABLE(NETWORK_CACHE) + +#include <WebCore/FileSystem.h> +#include <dirent.h> +#include <sys/stat.h> +#include <sys/time.h> +#include <wtf/text/CString.h> + +#if USE(SOUP) +#include <gio/gio.h> +#include <wtf/glib/GRefPtr.h> +#endif + +namespace WebKit { +namespace NetworkCache { + +static DirectoryEntryType directoryEntryType(uint8_t dtype) +{ + switch (dtype) { + case DT_DIR: + return DirectoryEntryType::Directory; + case DT_REG: + return DirectoryEntryType::File; + default: + ASSERT_NOT_REACHED(); + return DirectoryEntryType::File; + } +} + +void traverseDirectory(const String& path, const std::function<void (const String&, DirectoryEntryType)>& function) +{ + DIR* dir = opendir(WebCore::fileSystemRepresentation(path).data()); + if (!dir) + return; + dirent* dp; + while ((dp = readdir(dir))) { + if (dp->d_type != DT_DIR && dp->d_type != DT_REG) + continue; + const char* name = dp->d_name; + if (!strcmp(name, ".") || !strcmp(name, "..")) + continue; + auto nameString = String::fromUTF8(name); + if (nameString.isNull()) + continue; + function(nameString, directoryEntryType(dp->d_type)); + } + closedir(dir); +} + +void deleteDirectoryRecursively(const String& path) +{ + traverseDirectory(path, [&path](const String& name, DirectoryEntryType type) { + String entryPath = WebCore::pathByAppendingComponent(path, name); + switch (type) { + case DirectoryEntryType::File: + WebCore::deleteFile(entryPath); + break; + case DirectoryEntryType::Directory: + deleteDirectoryRecursively(entryPath); + break; + // This doesn't follow symlinks. + } + }); + WebCore::deleteEmptyDirectory(path); +} + +FileTimes fileTimes(const String& path) +{ +#if HAVE(STAT_BIRTHTIME) + struct stat fileInfo; + if (stat(WebCore::fileSystemRepresentation(path).data(), &fileInfo)) + return { }; + return { std::chrono::system_clock::from_time_t(fileInfo.st_birthtime), std::chrono::system_clock::from_time_t(fileInfo.st_mtime) }; +#elif USE(SOUP) + // There's no st_birthtime in some operating systems like Linux, so we use xattrs to set/get the creation time. + GRefPtr<GFile> file = adoptGRef(g_file_new_for_path(WebCore::fileSystemRepresentation(path).data())); + GRefPtr<GFileInfo> fileInfo = adoptGRef(g_file_query_info(file.get(), "xattr::birthtime,time::modified", G_FILE_QUERY_INFO_NONE, nullptr, nullptr)); + if (!fileInfo) + return { }; + const char* birthtimeString = g_file_info_get_attribute_string(fileInfo.get(), "xattr::birthtime"); + if (!birthtimeString) + return { }; + return { std::chrono::system_clock::from_time_t(g_ascii_strtoull(birthtimeString, nullptr, 10)), + std::chrono::system_clock::from_time_t(g_file_info_get_attribute_uint64(fileInfo.get(), "time::modified")) }; +#endif +} + +void updateFileModificationTimeIfNeeded(const String& path) +{ + auto times = fileTimes(path); + if (times.creation != times.modification) { + // Don't update more than once per hour. + if (std::chrono::system_clock::now() - times.modification < std::chrono::hours(1)) + return; + } + // This really updates both the access time and the modification time. + utimes(WebCore::fileSystemRepresentation(path).data(), nullptr); +} + +} +} + +#endif // ENABLE(NETWORK_CACHE) diff --git a/Source/WebKit2/NetworkProcess/EntryPoint/mac/XPCService/NetworkServiceEntryPoint.mm b/Source/WebKit2/NetworkProcess/cache/NetworkCacheFileSystem.h index 1c6ab641a..17407e8ab 100644 --- a/Source/WebKit2/NetworkProcess/EntryPoint/mac/XPCService/NetworkServiceEntryPoint.mm +++ b/Source/WebKit2/NetworkProcess/cache/NetworkCacheFileSystem.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013 Apple Inc. All rights reserved. + * Copyright (C) 2015 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -23,26 +23,33 @@ * THE POSSIBILITY OF SUCH DAMAGE. */ -#import "config.h" +#ifndef NetworkCacheFileSystem_h +#define NetworkCacheFileSystem_h -#if HAVE(XPC) +#if ENABLE(NETWORK_CACHE) -#import "EnvironmentUtilities.h" -#import "NetworkProcess.h" -#import "WKBase.h" -#import "XPCServiceEntryPoint.h" +#include <WebCore/FileSystem.h> +#include <functional> -using namespace WebKit; +namespace WebKit { +namespace NetworkCache { -extern "C" WK_EXPORT void NetworkServiceInitializer(xpc_connection_t connection, xpc_object_t initializerMessage); +enum class DirectoryEntryType { Directory, File }; +void traverseDirectory(const String& path, const std::function<void (const String& fileName, DirectoryEntryType)>&); -void NetworkServiceInitializer(xpc_connection_t connection, xpc_object_t initializerMessage) -{ - // Remove the SecItemShim from the DYLD_INSERT_LIBRARIES environment variable so any processes spawned by - // the this process don't try to insert the shim and crash. - EnvironmentUtilities::stripValuesEndingWithString("DYLD_INSERT_LIBRARIES", "/SecItemShim.dylib"); +void deleteDirectoryRecursively(const String& path); - XPCServiceInitializer<NetworkProcess, XPCServiceInitializerDelegate>(connection, initializerMessage); +struct FileTimes { + std::chrono::system_clock::time_point creation; + std::chrono::system_clock::time_point modification; +}; +FileTimes fileTimes(const String& path); +void updateFileModificationTimeIfNeeded(const String& path); + +} } -#endif // HAVE(XPC) +#endif + +#endif + diff --git a/Source/WebKit2/NetworkProcess/cache/NetworkCacheIOChannel.h b/Source/WebKit2/NetworkProcess/cache/NetworkCacheIOChannel.h new file mode 100644 index 000000000..c5edaa8f2 --- /dev/null +++ b/Source/WebKit2/NetworkProcess/cache/NetworkCacheIOChannel.h @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2015 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. + */ + +#ifndef NetworkCacheIOChannel_h +#define NetworkCacheIOChannel_h + +#if ENABLE(NETWORK_CACHE) + +#include "NetworkCacheData.h" +#include <functional> +#include <wtf/ThreadSafeRefCounted.h> +#include <wtf/WorkQueue.h> +#include <wtf/text/WTFString.h> + +#if USE(SOUP) +#include <wtf/glib/GRefPtr.h> +#endif + +namespace WebKit { +namespace NetworkCache { + +class IOChannel : public ThreadSafeRefCounted<IOChannel> { +public: + enum class Type { Read, Write, Create }; + static Ref<IOChannel> open(const String& file, Type); + + // Using nullptr as queue submits the result to the main queue. + // FIXME: We should add WorkQueue::main() instead. + void read(size_t offset, size_t, WorkQueue*, std::function<void (Data&, int error)>); + void write(size_t offset, const Data&, WorkQueue*, std::function<void (int error)>); + + const String& path() const { return m_path; } + Type type() const { return m_type; } + + int fileDescriptor() const { return m_fileDescriptor; } + +private: + IOChannel(const String& filePath, IOChannel::Type); + +#if USE(SOUP) + void readSyncInThread(size_t offset, size_t, WorkQueue*, std::function<void (Data&, int error)>); +#endif + + String m_path; + Type m_type; + + int m_fileDescriptor { 0 }; +#if PLATFORM(COCOA) + DispatchPtr<dispatch_io_t> m_dispatchIO; +#endif +#if USE(SOUP) + GRefPtr<GInputStream> m_inputStream; + GRefPtr<GOutputStream> m_outputStream; + GRefPtr<GFileIOStream> m_ioStream; +#endif +}; + +} +} + +#endif +#endif diff --git a/Source/WebKit2/NetworkProcess/cache/NetworkCacheIOChannelSoup.cpp b/Source/WebKit2/NetworkProcess/cache/NetworkCacheIOChannelSoup.cpp new file mode 100644 index 000000000..080ce256d --- /dev/null +++ b/Source/WebKit2/NetworkProcess/cache/NetworkCacheIOChannelSoup.cpp @@ -0,0 +1,290 @@ +/* + * Copyright (C) 2015 Igalia S.L. + * + * 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 "NetworkCacheIOChannel.h" + +#if ENABLE(NETWORK_CACHE) + +#include "NetworkCacheFileSystem.h" +#include <wtf/MainThread.h> +#include <wtf/RunLoop.h> +#include <wtf/glib/GUniquePtr.h> + +namespace WebKit { +namespace NetworkCache { + +static const size_t gDefaultReadBufferSize = 4096; + +IOChannel::IOChannel(const String& filePath, Type type) + : m_path(filePath) + , m_type(type) +{ + auto path = WebCore::fileSystemRepresentation(filePath); + GRefPtr<GFile> file = adoptGRef(g_file_new_for_path(path.data())); + switch (m_type) { + case Type::Create: { + g_file_delete(file.get(), nullptr, nullptr); + m_outputStream = adoptGRef(G_OUTPUT_STREAM(g_file_create(file.get(), static_cast<GFileCreateFlags>(G_FILE_CREATE_PRIVATE), nullptr, nullptr))); +#if !HAVE(STAT_BIRTHTIME) + GUniquePtr<char> birthtimeString(g_strdup_printf("%" G_GUINT64_FORMAT, std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()))); + g_file_set_attribute_string(file.get(), "xattr::birthtime", birthtimeString.get(), G_FILE_QUERY_INFO_NONE, nullptr, nullptr); +#endif + break; + } + case Type::Write: { + m_ioStream = adoptGRef(g_file_open_readwrite(file.get(), nullptr, nullptr)); + break; + } + case Type::Read: + m_inputStream = adoptGRef(G_INPUT_STREAM(g_file_read(file.get(), nullptr, nullptr))); + break; + } +} + +Ref<IOChannel> IOChannel::open(const String& filePath, IOChannel::Type type) +{ + return adoptRef(*new IOChannel(filePath, type)); +} + +static inline void runTaskInQueue(std::function<void ()> task, WorkQueue* queue) +{ + if (queue) { + queue->dispatch(task); + return; + } + + // Using nullptr as queue submits the result to the main context. + RunLoop::main().dispatch(WTFMove(task)); +} + +static void fillDataFromReadBuffer(SoupBuffer* readBuffer, size_t size, Data& data) +{ + GRefPtr<SoupBuffer> buffer; + if (size != readBuffer->length) { + // The subbuffer does not copy the data. + buffer = adoptGRef(soup_buffer_new_subbuffer(readBuffer, 0, size)); + } else + buffer = readBuffer; + + if (data.isNull()) { + // First chunk, we need to force the data to be copied. + data = { reinterpret_cast<const uint8_t*>(buffer->data), size }; + } else { + Data dataRead(WTFMove(buffer)); + // Concatenate will copy the data. + data = concatenate(data, dataRead); + } +} + +struct ReadAsyncData { + RefPtr<IOChannel> channel; + GRefPtr<SoupBuffer> buffer; + RefPtr<WorkQueue> queue; + size_t bytesToRead; + std::function<void (Data&, int error)> completionHandler; + Data data; +}; + +static void inputStreamReadReadyCallback(GInputStream* stream, GAsyncResult* result, gpointer userData) +{ + std::unique_ptr<ReadAsyncData> asyncData(static_cast<ReadAsyncData*>(userData)); + gssize bytesRead = g_input_stream_read_finish(stream, result, nullptr); + if (bytesRead == -1) { + WorkQueue* queue = asyncData->queue.get(); + auto* asyncDataPtr = asyncData.release(); + runTaskInQueue([asyncDataPtr] { + std::unique_ptr<ReadAsyncData> asyncData(asyncDataPtr); + asyncData->completionHandler(asyncData->data, -1); + }, queue); + return; + } + + if (!bytesRead) { + WorkQueue* queue = asyncData->queue.get(); + auto* asyncDataPtr = asyncData.release(); + runTaskInQueue([asyncDataPtr] { + std::unique_ptr<ReadAsyncData> asyncData(asyncDataPtr); + asyncData->completionHandler(asyncData->data, 0); + }, queue); + return; + } + + ASSERT(bytesRead > 0); + fillDataFromReadBuffer(asyncData->buffer.get(), static_cast<size_t>(bytesRead), asyncData->data); + + size_t pendingBytesToRead = asyncData->bytesToRead - asyncData->data.size(); + if (!pendingBytesToRead) { + WorkQueue* queue = asyncData->queue.get(); + auto* asyncDataPtr = asyncData.release(); + runTaskInQueue([asyncDataPtr] { + std::unique_ptr<ReadAsyncData> asyncData(asyncDataPtr); + asyncData->completionHandler(asyncData->data, 0); + }, queue); + return; + } + + size_t bytesToRead = std::min(pendingBytesToRead, asyncData->buffer->length); + // Use a local variable for the data buffer to pass it to g_input_stream_read_async(), because ReadAsyncData is released. + auto data = const_cast<char*>(asyncData->buffer->data); + g_input_stream_read_async(stream, data, bytesToRead, G_PRIORITY_DEFAULT, nullptr, + reinterpret_cast<GAsyncReadyCallback>(inputStreamReadReadyCallback), asyncData.release()); +} + +void IOChannel::read(size_t offset, size_t size, WorkQueue* queue, std::function<void (Data&, int error)> completionHandler) +{ + RefPtr<IOChannel> channel(this); + if (!m_inputStream) { + runTaskInQueue([channel, completionHandler] { + Data data; + completionHandler(data, -1); + }, queue); + return; + } + + if (!isMainThread()) { + readSyncInThread(offset, size, queue, completionHandler); + return; + } + + size_t bufferSize = std::min(size, gDefaultReadBufferSize); + uint8_t* bufferData = static_cast<uint8_t*>(fastMalloc(bufferSize)); + GRefPtr<SoupBuffer> buffer = adoptGRef(soup_buffer_new_with_owner(bufferData, bufferSize, bufferData, fastFree)); + ReadAsyncData* asyncData = new ReadAsyncData { this, buffer.get(), queue, size, completionHandler, { } }; + + // FIXME: implement offset. + g_input_stream_read_async(m_inputStream.get(), const_cast<char*>(buffer->data), bufferSize, G_PRIORITY_DEFAULT, nullptr, + reinterpret_cast<GAsyncReadyCallback>(inputStreamReadReadyCallback), asyncData); +} + +void IOChannel::readSyncInThread(size_t offset, size_t size, WorkQueue* queue, std::function<void (Data&, int error)> completionHandler) +{ + ASSERT(!isMainThread()); + + RefPtr<IOChannel> channel(this); + detachThread(createThread("IOChannel::readSync", [channel, size, queue, completionHandler] { + size_t bufferSize = std::min(size, gDefaultReadBufferSize); + uint8_t* bufferData = static_cast<uint8_t*>(fastMalloc(bufferSize)); + GRefPtr<SoupBuffer> readBuffer = adoptGRef(soup_buffer_new_with_owner(bufferData, bufferSize, bufferData, fastFree)); + Data data; + size_t pendingBytesToRead = size; + size_t bytesToRead = bufferSize; + do { + // FIXME: implement offset. + gssize bytesRead = g_input_stream_read(channel->m_inputStream.get(), const_cast<char*>(readBuffer->data), bytesToRead, nullptr, nullptr); + if (bytesRead == -1) { + runTaskInQueue([channel, completionHandler] { + Data data; + completionHandler(data, -1); + }, queue); + return; + } + + if (!bytesRead) + break; + + ASSERT(bytesRead > 0); + fillDataFromReadBuffer(readBuffer.get(), static_cast<size_t>(bytesRead), data); + + pendingBytesToRead = size - data.size(); + bytesToRead = std::min(pendingBytesToRead, readBuffer->length); + } while (pendingBytesToRead); + + GRefPtr<SoupBuffer> bufferCapture = data.soupBuffer(); + runTaskInQueue([channel, bufferCapture, completionHandler] { + GRefPtr<SoupBuffer> buffer = bufferCapture; + Data data = { WTFMove(buffer) }; + completionHandler(data, 0); + }, queue); + })); +} + +struct WriteAsyncData { + RefPtr<IOChannel> channel; + GRefPtr<SoupBuffer> buffer; + RefPtr<WorkQueue> queue; + std::function<void (int error)> completionHandler; +}; + +static void outputStreamWriteReadyCallback(GOutputStream* stream, GAsyncResult* result, gpointer userData) +{ + std::unique_ptr<WriteAsyncData> asyncData(static_cast<WriteAsyncData*>(userData)); + gssize bytesWritten = g_output_stream_write_finish(stream, result, nullptr); + if (bytesWritten == -1) { + WorkQueue* queue = asyncData->queue.get(); + auto* asyncDataPtr = asyncData.release(); + runTaskInQueue([asyncDataPtr] { + std::unique_ptr<WriteAsyncData> asyncData(asyncDataPtr); + asyncData->completionHandler(-1); + }, queue); + return; + } + + gssize pendingBytesToWrite = asyncData->buffer->length - bytesWritten; + if (!pendingBytesToWrite) { + WorkQueue* queue = asyncData->queue.get(); + auto* asyncDataPtr = asyncData.release(); + runTaskInQueue([asyncDataPtr] { + std::unique_ptr<WriteAsyncData> asyncData(asyncDataPtr); + asyncData->completionHandler(0); + }, queue); + return; + } + + asyncData->buffer = adoptGRef(soup_buffer_new_subbuffer(asyncData->buffer.get(), bytesWritten, pendingBytesToWrite)); + // Use a local variable for the data buffer to pass it to g_output_stream_write_async(), because WriteAsyncData is released. + auto data = asyncData->buffer->data; + g_output_stream_write_async(stream, data, pendingBytesToWrite, G_PRIORITY_DEFAULT_IDLE, nullptr, + reinterpret_cast<GAsyncReadyCallback>(outputStreamWriteReadyCallback), asyncData.release()); +} + +void IOChannel::write(size_t offset, const Data& data, WorkQueue* queue, std::function<void (int error)> completionHandler) +{ + RefPtr<IOChannel> channel(this); + if (!m_outputStream && !m_ioStream) { + runTaskInQueue([channel, completionHandler] { + completionHandler(-1); + }, queue); + return; + } + + GOutputStream* stream = m_outputStream ? m_outputStream.get() : g_io_stream_get_output_stream(G_IO_STREAM(m_ioStream.get())); + if (!stream) { + runTaskInQueue([channel, completionHandler] { + completionHandler(-1); + }, queue); + return; + } + + WriteAsyncData* asyncData = new WriteAsyncData { this, data.soupBuffer(), queue, completionHandler }; + // FIXME: implement offset. + g_output_stream_write_async(stream, asyncData->buffer->data, data.size(), G_PRIORITY_DEFAULT_IDLE, nullptr, + reinterpret_cast<GAsyncReadyCallback>(outputStreamWriteReadyCallback), asyncData); +} + +} // namespace NetworkCache +} // namespace WebKit + +#endif diff --git a/Source/WebKit2/NetworkProcess/cache/NetworkCacheKey.cpp b/Source/WebKit2/NetworkProcess/cache/NetworkCacheKey.cpp new file mode 100644 index 000000000..1f06a65bb --- /dev/null +++ b/Source/WebKit2/NetworkProcess/cache/NetworkCacheKey.cpp @@ -0,0 +1,156 @@ +/* + * Copyright (C) 2014-2015 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 "NetworkCacheKey.h" + +#if ENABLE(NETWORK_CACHE) + +#include "NetworkCacheCoders.h" +#include <wtf/ASCIICType.h> +#include <wtf/text/CString.h> +#include <wtf/text/StringBuilder.h> + +namespace WebKit { +namespace NetworkCache { + +Key::Key(const Key& o) + : m_partition(o.m_partition.isolatedCopy()) + , m_type(o.m_type.isolatedCopy()) + , m_identifier(o.m_identifier.isolatedCopy()) + , m_range(o.m_range.isolatedCopy()) + , m_hash(o.m_hash) +{ +} + +Key::Key(const String& partition, const String& type, const String& range, const String& identifier) + : m_partition(partition.isolatedCopy()) + , m_type(type.isolatedCopy()) + , m_identifier(identifier.isolatedCopy()) + , m_range(range.isolatedCopy()) + , m_hash(computeHash()) +{ +} + +Key::Key(WTF::HashTableDeletedValueType) + : m_identifier(WTF::HashTableDeletedValue) +{ +} + +Key& Key::operator=(const Key& other) +{ + m_partition = other.m_partition.isolatedCopy(); + m_type = other.m_type.isolatedCopy(); + m_identifier = other.m_identifier.isolatedCopy(); + m_range = other.m_range.isolatedCopy(); + m_hash = other.m_hash; + return *this; +} + +static void hashString(SHA1& sha1, const String& string) +{ + if (string.isNull()) + return; + + if (string.is8Bit() && string.containsOnlyASCII()) { + const uint8_t nullByte = 0; + sha1.addBytes(string.characters8(), string.length()); + sha1.addBytes(&nullByte, 1); + return; + } + auto cString = string.utf8(); + // Include terminating null byte. + sha1.addBytes(reinterpret_cast<const uint8_t*>(cString.data()), cString.length() + 1); +} + +Key::HashType Key::computeHash() const +{ + // We don't really need a cryptographic hash. The key is always verified against the entry header. + // SHA1 just happens to be suitably sized, fast and available. + SHA1 sha1; + hashString(sha1, m_partition); + hashString(sha1, m_type); + hashString(sha1, m_identifier); + hashString(sha1, m_range); + SHA1::Digest hash; + sha1.computeHash(hash); + return hash; +} + +String Key::hashAsString() const +{ + StringBuilder builder; + builder.reserveCapacity(hashStringLength()); + for (auto byte : m_hash) { + builder.append(upperNibbleToASCIIHexDigit(byte)); + builder.append(lowerNibbleToASCIIHexDigit(byte)); + } + return builder.toString(); +} + +template <typename CharType> bool hexDigitsToHash(CharType* characters, Key::HashType& hash) +{ + for (unsigned i = 0; i < sizeof(hash); ++i) { + auto high = characters[2 * i]; + auto low = characters[2 * i + 1]; + if (!isASCIIHexDigit(high) || !isASCIIHexDigit(low)) + return false; + hash[i] = toASCIIHexValue(high, low); + } + return true; +} + +bool Key::stringToHash(const String& string, HashType& hash) +{ + if (string.length() != hashStringLength()) + return false; + if (string.is8Bit()) + return hexDigitsToHash(string.characters8(), hash); + return hexDigitsToHash(string.characters16(), hash); +} + +bool Key::operator==(const Key& other) const +{ + return m_hash == other.m_hash && m_partition == other.m_partition && m_type == other.m_type && m_identifier == other.m_identifier && m_range == other.m_range; +} + +void Key::encode(Encoder& encoder) const +{ + encoder << m_partition; + encoder << m_type; + encoder << m_identifier; + encoder << m_range; + encoder << m_hash; +} + +bool Key::decode(Decoder& decoder, Key& key) +{ + return decoder.decode(key.m_partition) && decoder.decode(key.m_type) && decoder.decode(key.m_identifier) && decoder.decode(key.m_range) && decoder.decode(key.m_hash); +} + +} +} + +#endif diff --git a/Source/WebKit2/NetworkProcess/cache/NetworkCacheKey.h b/Source/WebKit2/NetworkProcess/cache/NetworkCacheKey.h new file mode 100644 index 000000000..12a7007fd --- /dev/null +++ b/Source/WebKit2/NetworkProcess/cache/NetworkCacheKey.h @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2014-2015 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. + */ + +#ifndef NetworkCacheKey_h +#define NetworkCacheKey_h + +#if ENABLE(NETWORK_CACHE) + +#include <wtf/SHA1.h> +#include <wtf/text/WTFString.h> + +namespace WebKit { +namespace NetworkCache { + +class Encoder; +class Decoder; + +class Key { +public: + typedef SHA1::Digest HashType; + + Key() { } + Key(const Key&); + Key(Key&&) = default; + Key(const String& partition, const String& type, const String& range, const String& identifier); + + Key& operator=(const Key&); + Key& operator=(Key&&) = default; + + Key(WTF::HashTableDeletedValueType); + bool isHashTableDeletedValue() const { return m_identifier.isHashTableDeletedValue(); } + + bool isNull() const { return m_identifier.isNull(); } + + const String& partition() const { return m_partition; } + const String& identifier() const { return m_identifier; } + const String& type() const { return m_type; } + const String& range() const { return m_range; } + + HashType hash() const { return m_hash; } + + static bool stringToHash(const String&, HashType&); + + static size_t hashStringLength() { return 2 * sizeof(m_hash); } + String hashAsString() const; + + void encode(Encoder&) const; + static bool decode(Decoder&, Key&); + + bool operator==(const Key&) const; + bool operator!=(const Key& other) const { return !(*this == other); } + +private: + HashType computeHash() const; + + String m_partition; + String m_type; + String m_identifier; + String m_range; + HashType m_hash; +}; + +} +} + +namespace WTF { + +struct NetworkCacheKeyHash { + static unsigned hash(const WebKit::NetworkCache::Key& key) + { + static_assert(SHA1::hashSize >= sizeof(unsigned), "Hash size must be greater than sizeof(unsigned)"); + return *reinterpret_cast<const unsigned*>(key.hash().data()); + } + + static bool equal(const WebKit::NetworkCache::Key& a, const WebKit::NetworkCache::Key& b) + { + return a == b; + } + + static const bool safeToCompareToEmptyOrDeleted = false; +}; + +template<typename T> struct DefaultHash; +template<> struct DefaultHash<WebKit::NetworkCache::Key> { + typedef NetworkCacheKeyHash Hash; +}; + +template<> struct HashTraits<WebKit::NetworkCache::Key> : SimpleClassHashTraits<WebKit::NetworkCache::Key> { + static const bool emptyValueIsZero = false; + + static const bool hasIsEmptyValueFunction = true; + static bool isEmptyValue(const WebKit::NetworkCache::Key& key) { return key.isNull(); } +}; + +} // namespace WTF + +#endif +#endif diff --git a/Source/WebKit2/NetworkProcess/cache/NetworkCacheSpeculativeLoad.cpp b/Source/WebKit2/NetworkProcess/cache/NetworkCacheSpeculativeLoad.cpp new file mode 100644 index 000000000..f6f594030 --- /dev/null +++ b/Source/WebKit2/NetworkProcess/cache/NetworkCacheSpeculativeLoad.cpp @@ -0,0 +1,148 @@ +/* + * Copyright (C) 2015 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" + +#if ENABLE(NETWORK_CACHE_SPECULATIVE_REVALIDATION) +#include "NetworkCacheSpeculativeLoad.h" + +#include "Logging.h" +#include "NetworkCache.h" +#include "NetworkLoad.h" +#include <WebCore/SessionID.h> +#include <wtf/CurrentTime.h> +#include <wtf/RunLoop.h> + +namespace WebKit { +namespace NetworkCache { + +using namespace WebCore; + +SpeculativeLoad::SpeculativeLoad(const GlobalFrameID& frameID, const ResourceRequest& request, std::unique_ptr<NetworkCache::Entry> cacheEntryForValidation, RevalidationCompletionHandler&& completionHandler) + : m_frameID(frameID) + , m_completionHandler(WTFMove(completionHandler)) + , m_originalRequest(request) + , m_bufferedDataForCache(SharedBuffer::create()) + , m_cacheEntryForValidation(WTFMove(cacheEntryForValidation)) +{ + ASSERT(m_cacheEntryForValidation); + ASSERT(m_cacheEntryForValidation->needsValidation()); + + NetworkLoadParameters parameters; + parameters.sessionID = SessionID::defaultSessionID(); + parameters.allowStoredCredentials = AllowStoredCredentials; + parameters.contentSniffingPolicy = DoNotSniffContent; + parameters.request = m_originalRequest; + m_networkLoad = std::make_unique<NetworkLoad>(*this, parameters); +} + +SpeculativeLoad::~SpeculativeLoad() +{ + ASSERT(!m_networkLoad); +} + +void SpeculativeLoad::willSendRedirectedRequest(const ResourceRequest& request, const ResourceRequest& redirectRequest, const ResourceResponse& redirectResponse) +{ + updateRedirectChainStatus(m_redirectChainCacheStatus, redirectResponse); +} + +auto SpeculativeLoad::didReceiveResponse(const ResourceResponse& receivedResponse) -> ShouldContinueDidReceiveResponse +{ + m_response = receivedResponse; + + if (m_response.isMultipart()) + m_bufferedDataForCache = nullptr; + + ASSERT(m_cacheEntryForValidation); + + bool validationSucceeded = m_response.httpStatusCode() == 304; // 304 Not Modified + if (validationSucceeded) { + m_cacheEntryForValidation = NetworkCache::singleton().update(m_originalRequest, m_frameID, *m_cacheEntryForValidation, m_response); + didComplete(); + return ShouldContinueDidReceiveResponse::No; + } + + m_cacheEntryForValidation = nullptr; + + return ShouldContinueDidReceiveResponse::Yes; +} + +void SpeculativeLoad::didReceiveBuffer(RefPtr<SharedBuffer>&& buffer, int reportedEncodedDataLength) +{ + 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; + } +} + +void SpeculativeLoad::didFinishLoading(double finishTime) +{ + ASSERT(!m_cacheEntryForValidation); + + bool allowStale = m_originalRequest.cachePolicy() >= ReturnCacheDataElseLoad; + bool hasCacheableRedirect = m_response.isHTTP() && redirectChainAllowsReuse(m_redirectChainCacheStatus, allowStale ? ReuseExpiredRedirection : DoNotReuseExpiredRedirection); + if (hasCacheableRedirect && m_redirectChainCacheStatus.status == RedirectChainCacheStatus::CachedRedirection) { + // Maybe we should cache the actual redirects instead of the end result? + auto now = std::chrono::system_clock::now(); + auto responseEndOfValidity = now + computeFreshnessLifetimeForHTTPFamily(m_response, now) - computeCurrentAge(m_response, now); + hasCacheableRedirect = responseEndOfValidity <= m_redirectChainCacheStatus.endOfValidity; + } + + if (m_bufferedDataForCache && hasCacheableRedirect) + m_cacheEntryForValidation = NetworkCache::singleton().store(m_originalRequest, m_response, WTFMove(m_bufferedDataForCache), [](NetworkCache::MappedBody& mappedBody) { }); + else if (!hasCacheableRedirect) { + // Make sure we don't keep a stale entry in the cache. + NetworkCache::singleton().remove(m_originalRequest); + } + + didComplete(); +} + +void SpeculativeLoad::didFailLoading(const ResourceError&) +{ + m_cacheEntryForValidation = nullptr; + + didComplete(); +} + +void SpeculativeLoad::didComplete() +{ + RELEASE_ASSERT(RunLoop::isMain()); + + m_networkLoad = nullptr; + + m_completionHandler(WTFMove(m_cacheEntryForValidation)); +} + +} // namespace NetworkCache +} // namespace WebKit + +#endif // ENABLE(NETWORK_CACHE_SPECULATIVE_REVALIDATION) diff --git a/Source/WebKit2/NetworkProcess/cache/NetworkCacheSpeculativeLoad.h b/Source/WebKit2/NetworkProcess/cache/NetworkCacheSpeculativeLoad.h new file mode 100644 index 000000000..3ce3b63bf --- /dev/null +++ b/Source/WebKit2/NetworkProcess/cache/NetworkCacheSpeculativeLoad.h @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2015 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. + */ + +#ifndef NetworkCacheSpeculativeLoad_h +#define NetworkCacheSpeculativeLoad_h + +#if ENABLE(NETWORK_CACHE_SPECULATIVE_REVALIDATION) + +#include "NetworkCache.h" +#include "NetworkCacheEntry.h" +#include "NetworkLoadClient.h" +#include <WebCore/ResourceRequest.h> +#include <WebCore/ResourceResponse.h> +#include <WebCore/SharedBuffer.h> + +namespace WebKit { + +class NetworkLoad; + +namespace NetworkCache { + +class SpeculativeLoad final : public NetworkLoadClient { + WTF_MAKE_FAST_ALLOCATED; +public: + typedef std::function<void (std::unique_ptr<NetworkCache::Entry>)> RevalidationCompletionHandler; + SpeculativeLoad(const GlobalFrameID&, const WebCore::ResourceRequest&, std::unique_ptr<NetworkCache::Entry>, RevalidationCompletionHandler&&); + + virtual ~SpeculativeLoad(); + +private: + // NetworkLoadClient. + virtual void didSendData(unsigned long long bytesSent, unsigned long long totalBytesToBeSent) override { } + virtual void canAuthenticateAgainstProtectionSpaceAsync(const WebCore::ProtectionSpace&) override { } + virtual bool isSynchronous() const override { return false; } + virtual void willSendRedirectedRequest(const WebCore::ResourceRequest&, const WebCore::ResourceRequest& redirectRequest, const WebCore::ResourceResponse& redirectResponse) override; + virtual ShouldContinueDidReceiveResponse didReceiveResponse(const WebCore::ResourceResponse&) override; + virtual void didReceiveBuffer(RefPtr<WebCore::SharedBuffer>&&, int reportedEncodedDataLength) override; + virtual void didFinishLoading(double finishTime) override; + virtual void didFailLoading(const WebCore::ResourceError&) override; + virtual void didConvertToDownload() override { ASSERT_NOT_REACHED(); } +#if PLATFORM(COCOA) + virtual void willCacheResponseAsync(CFCachedURLResponseRef) override { } +#endif + + void didComplete(); + + GlobalFrameID m_frameID; + RevalidationCompletionHandler m_completionHandler; + WebCore::ResourceRequest m_originalRequest; + + std::unique_ptr<NetworkLoad> m_networkLoad; + + WebCore::ResourceResponse m_response; + + RefPtr<WebCore::SharedBuffer> m_bufferedDataForCache; + std::unique_ptr<NetworkCache::Entry> m_cacheEntryForValidation; + + WebCore::RedirectChainCacheStatus m_redirectChainCacheStatus; +}; + +} // namespace NetworkCache +} // namespace WebKit + +#endif // ENABLE(NETWORK_CACHE_SPECULATIVE_REVALIDATION) + +#endif // NetworkCacheSpeculativeLoad_h diff --git a/Source/WebKit2/NetworkProcess/cache/NetworkCacheSpeculativeLoadManager.cpp b/Source/WebKit2/NetworkProcess/cache/NetworkCacheSpeculativeLoadManager.cpp new file mode 100644 index 000000000..027591100 --- /dev/null +++ b/Source/WebKit2/NetworkProcess/cache/NetworkCacheSpeculativeLoadManager.cpp @@ -0,0 +1,453 @@ +/* + * Copyright (C) 2015 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" + +#if ENABLE(NETWORK_CACHE_SPECULATIVE_REVALIDATION) +#include "NetworkCacheSpeculativeLoadManager.h" + +#include "Logging.h" +#include "NetworkCacheEntry.h" +#include "NetworkCacheSpeculativeLoad.h" +#include "NetworkCacheSubresourcesEntry.h" +#include "NetworkProcess.h" +#include <WebCore/DiagnosticLoggingKeys.h> +#include <WebCore/HysteresisActivity.h> +#include <wtf/HashCountedSet.h> +#include <wtf/NeverDestroyed.h> +#include <wtf/RefCounted.h> +#include <wtf/RunLoop.h> + +namespace WebKit { + +namespace NetworkCache { + +using namespace WebCore; + +static const auto preloadedEntryLifetime = 10_s; + +#if !LOG_DISABLED +static HashCountedSet<String>& allSpeculativeLoadingDiagnosticMessages() +{ + static NeverDestroyed<HashCountedSet<String>> messages; + return messages; +} + +static void printSpeculativeLoadingDiagnosticMessageCounts() +{ + LOG(NetworkCacheSpeculativePreloading, "-- Speculative loading statistics --"); + for (auto& pair : allSpeculativeLoadingDiagnosticMessages()) + LOG(NetworkCacheSpeculativePreloading, "%s: %u", pair.key.utf8().data(), pair.value); +} +#endif + +static void logSpeculativeLoadingDiagnosticMessage(const GlobalFrameID& frameID, const String& message) +{ +#if !LOG_DISABLED + if (WebKit2LogNetworkCacheSpeculativePreloading.state == WTFLogChannelOn) + allSpeculativeLoadingDiagnosticMessages().add(message); +#endif + NetworkProcess::singleton().logDiagnosticMessage(frameID.first, WebCore::DiagnosticLoggingKeys::networkCacheKey(), message, WebCore::ShouldSample::Yes); +} + +static const AtomicString& subresourcesType() +{ + ASSERT(RunLoop::isMain()); + static NeverDestroyed<const AtomicString> resource("subresources", AtomicString::ConstructFromLiteral); + return resource; +} + +static inline Key makeSubresourcesKey(const Key& resourceKey) +{ + return Key(resourceKey.partition(), subresourcesType(), resourceKey.range(), resourceKey.identifier()); +} + +static inline ResourceRequest constructRevalidationRequest(const Entry& entry) +{ + ResourceRequest revalidationRequest(entry.key().identifier()); + + String eTag = entry.response().httpHeaderField(HTTPHeaderName::ETag); + if (!eTag.isEmpty()) + revalidationRequest.setHTTPHeaderField(HTTPHeaderName::IfNoneMatch, eTag); + + String lastModified = entry.response().httpHeaderField(HTTPHeaderName::LastModified); + if (!lastModified.isEmpty()) + revalidationRequest.setHTTPHeaderField(HTTPHeaderName::IfModifiedSince, lastModified); + + return revalidationRequest; +} + +static bool responseNeedsRevalidation(const ResourceResponse& response, std::chrono::system_clock::time_point timestamp) +{ + if (response.cacheControlContainsNoCache()) + return true; + + auto age = computeCurrentAge(response, timestamp); + auto lifetime = computeFreshnessLifetimeForHTTPFamily(response, timestamp); + return age - lifetime > 0_ms; +} + +class SpeculativeLoadManager::ExpiringEntry { + WTF_MAKE_FAST_ALLOCATED; +public: + explicit ExpiringEntry(std::function<void()>&& expirationHandler) + : m_lifetimeTimer(WTFMove(expirationHandler)) + { + m_lifetimeTimer.startOneShot(preloadedEntryLifetime); + } + +private: + Timer m_lifetimeTimer; +}; + +class SpeculativeLoadManager::PreloadedEntry : private ExpiringEntry { + WTF_MAKE_FAST_ALLOCATED; +public: + PreloadedEntry(std::unique_ptr<Entry> entry, WasRevalidated wasRevalidated, std::function<void()>&& lifetimeReachedHandler) + : ExpiringEntry(WTFMove(lifetimeReachedHandler)) + , m_entry(WTFMove(entry)) + , m_wasRevalidated(wasRevalidated == WasRevalidated::Yes) + { } + + std::unique_ptr<Entry> takeCacheEntry() + { + ASSERT(m_entry); + return WTFMove(m_entry); + } + + bool wasRevalidated() const { return m_wasRevalidated; } + +private: + std::unique_ptr<Entry> m_entry; + bool m_wasRevalidated; +}; + +class SpeculativeLoadManager::PendingFrameLoad : public RefCounted<PendingFrameLoad> { +public: + static Ref<PendingFrameLoad> create(Storage& storage, const Key& mainResourceKey, std::function<void()>&& loadCompletionHandler) + { + return adoptRef(*new PendingFrameLoad(storage, mainResourceKey, WTFMove(loadCompletionHandler))); + } + + ~PendingFrameLoad() + { + ASSERT(m_didFinishLoad); + ASSERT(m_didRetrieveExistingEntry); + } + + void registerSubresource(const Key& subresourceKey) + { + ASSERT(RunLoop::isMain()); + m_subresourceKeys.append(subresourceKey); + m_loadHysteresisActivity.impulse(); + } + + void markLoadAsCompleted() + { + ASSERT(RunLoop::isMain()); + if (m_didFinishLoad) + return; + +#if !LOG_DISABLED + printSpeculativeLoadingDiagnosticMessageCounts(); +#endif + + m_didFinishLoad = true; + saveToDiskIfReady(); + m_loadCompletionHandler(); + } + + void setExistingSubresourcesEntry(std::unique_ptr<SubresourcesEntry> entry) + { + ASSERT(!m_existingEntry); + ASSERT(!m_didRetrieveExistingEntry); + + m_existingEntry = WTFMove(entry); + m_didRetrieveExistingEntry = true; + saveToDiskIfReady(); + } + +private: + PendingFrameLoad(Storage& storage, const Key& mainResourceKey, std::function<void()>&& loadCompletionHandler) + : m_storage(storage) + , m_mainResourceKey(mainResourceKey) + , m_loadCompletionHandler(WTFMove(loadCompletionHandler)) + , m_loadHysteresisActivity([this](HysteresisState state) { if (state == HysteresisState::Stopped) markLoadAsCompleted(); }) + { } + + void saveToDiskIfReady() + { + if (!m_didFinishLoad || !m_didRetrieveExistingEntry) + return; + + if (m_subresourceKeys.isEmpty()) + return; + +#if !LOG_DISABLED + LOG(NetworkCacheSpeculativePreloading, "(NetworkProcess) Saving to disk list of subresources for '%s':", m_mainResourceKey.identifier().utf8().data()); + for (auto& subresourceKey : m_subresourceKeys) + LOG(NetworkCacheSpeculativePreloading, "(NetworkProcess) * Subresource: '%s'.", subresourceKey.identifier().utf8().data()); +#endif + + if (m_existingEntry) { + m_existingEntry->updateSubresourceKeys(m_subresourceKeys); + m_storage.store(m_existingEntry->encodeAsStorageRecord(), [](const Data&) { }); + } else { + SubresourcesEntry entry(makeSubresourcesKey(m_mainResourceKey), m_subresourceKeys); + m_storage.store(entry.encodeAsStorageRecord(), [](const Data&) { }); + } + } + + Storage& m_storage; + Key m_mainResourceKey; + Vector<Key> m_subresourceKeys; + std::function<void()> m_loadCompletionHandler; + HysteresisActivity m_loadHysteresisActivity; + std::unique_ptr<SubresourcesEntry> m_existingEntry; + bool m_didFinishLoad { false }; + bool m_didRetrieveExistingEntry { false }; +}; + +SpeculativeLoadManager::SpeculativeLoadManager(Storage& storage) + : m_storage(storage) +{ +} + +SpeculativeLoadManager::~SpeculativeLoadManager() +{ +} + +bool SpeculativeLoadManager::retrieve(const GlobalFrameID& frameID, const Key& storageKey, const RetrieveCompletionHandler& completionHandler) +{ + // Check already preloaded entries. + if (auto preloadedEntry = m_preloadedEntries.take(storageKey)) { + LOG(NetworkCacheSpeculativePreloading, "(NetworkProcess) Retrieval: Using preloaded entry to satisfy request for '%s':", storageKey.identifier().utf8().data()); + if (preloadedEntry->wasRevalidated()) + logSpeculativeLoadingDiagnosticMessage(frameID, DiagnosticLoggingKeys::successfulSpeculativeWarmupWithRevalidationKey()); + else + logSpeculativeLoadingDiagnosticMessage(frameID, DiagnosticLoggingKeys::successfulSpeculativeWarmupWithoutRevalidationKey()); + + completionHandler(preloadedEntry->takeCacheEntry()); + return true; + } + + // Check pending speculative revalidations. + if (!m_pendingPreloads.contains(storageKey)) { + if (m_notPreloadedEntries.remove(storageKey)) + logSpeculativeLoadingDiagnosticMessage(frameID, DiagnosticLoggingKeys::entryWronglyNotWarmedUpKey()); + else + logSpeculativeLoadingDiagnosticMessage(frameID, DiagnosticLoggingKeys::unknownEntryRequestKey()); + + return false; + } + + LOG(NetworkCacheSpeculativePreloading, "(NetworkProcess) Retrieval: revalidation already in progress for '%s':", storageKey.identifier().utf8().data()); + + // FIXME: This breaks incremental loading when the revalidation is not successful. + auto addResult = m_pendingRetrieveRequests.add(storageKey, nullptr); + if (addResult.isNewEntry) + addResult.iterator->value = std::make_unique<Vector<RetrieveCompletionHandler>>(); + addResult.iterator->value->append(completionHandler); + return true; +} + +void SpeculativeLoadManager::registerLoad(const GlobalFrameID& frameID, const ResourceRequest& request, const Key& resourceKey) +{ + ASSERT(RunLoop::isMain()); + + if (!request.url().protocolIsInHTTPFamily() || request.httpMethod() != "GET") + return; + + auto isMainResource = request.requester() == ResourceRequest::Requester::Main; + if (isMainResource) { + // Mark previous load in this frame as completed if necessary. + if (auto* pendingFrameLoad = m_pendingFrameLoads.get(frameID)) + pendingFrameLoad->markLoadAsCompleted(); + + ASSERT(!m_pendingFrameLoads.contains(frameID)); + + // Start tracking loads in this frame. + RefPtr<PendingFrameLoad> pendingFrameLoad = PendingFrameLoad::create(m_storage, resourceKey, [this, frameID] { + bool wasRemoved = m_pendingFrameLoads.remove(frameID); + ASSERT_UNUSED(wasRemoved, wasRemoved); + }); + m_pendingFrameLoads.add(frameID, pendingFrameLoad); + + // Retrieve the subresources entry if it exists to start speculative revalidation and to update it. + retrieveSubresourcesEntry(resourceKey, [this, frameID, pendingFrameLoad](std::unique_ptr<SubresourcesEntry> entry) { + if (entry) + startSpeculativeRevalidation(frameID, *entry); + + pendingFrameLoad->setExistingSubresourcesEntry(WTFMove(entry)); + }); + return; + } + + if (auto* pendingFrameLoad = m_pendingFrameLoads.get(frameID)) + pendingFrameLoad->registerSubresource(resourceKey); +} + +void SpeculativeLoadManager::addPreloadedEntry(std::unique_ptr<Entry> entry, const GlobalFrameID& frameID, WasRevalidated wasRevalidated) +{ + ASSERT(entry); + ASSERT(!entry->needsValidation()); + auto key = entry->key(); + m_preloadedEntries.add(key, std::make_unique<PreloadedEntry>(WTFMove(entry), wasRevalidated, [this, key, frameID] { + auto preloadedEntry = m_preloadedEntries.take(key); + ASSERT(preloadedEntry); + if (preloadedEntry->wasRevalidated()) + logSpeculativeLoadingDiagnosticMessage(frameID, DiagnosticLoggingKeys::wastedSpeculativeWarmupWithRevalidationKey()); + else + logSpeculativeLoadingDiagnosticMessage(frameID, DiagnosticLoggingKeys::wastedSpeculativeWarmupWithoutRevalidationKey()); + })); +} + +void SpeculativeLoadManager::retrieveEntryFromStorage(const Key& key, const RetrieveCompletionHandler& completionHandler) +{ + m_storage.retrieve(key, static_cast<unsigned>(ResourceLoadPriority::Medium), [completionHandler](std::unique_ptr<Storage::Record> record) { + if (!record) { + completionHandler(nullptr); + return false; + } + auto entry = Entry::decodeStorageRecord(*record); + if (!entry) { + completionHandler(nullptr); + return false; + } + + auto& response = entry->response(); + if (!response.hasCacheValidatorFields()) { + completionHandler(nullptr); + return true; + } + + if (responseNeedsRevalidation(response, entry->timeStamp())) + entry->setNeedsValidation(); + + completionHandler(WTFMove(entry)); + return true; + }); +} + +bool SpeculativeLoadManager::satisfyPendingRequests(const Key& key, Entry* entry) +{ + auto completionHandlers = m_pendingRetrieveRequests.take(key); + if (!completionHandlers) + return false; + + for (auto& completionHandler : *completionHandlers) + completionHandler(entry ? std::make_unique<Entry>(*entry) : nullptr); + + return true; +} + +void SpeculativeLoadManager::revalidateEntry(std::unique_ptr<Entry> entry, const GlobalFrameID& frameID) +{ + ASSERT(entry); + ASSERT(entry->needsValidation()); + + auto key = entry->key(); + LOG(NetworkCacheSpeculativePreloading, "(NetworkProcess) Speculatively revalidating '%s':", key.identifier().utf8().data()); + auto revalidator = std::make_unique<SpeculativeLoad>(frameID, constructRevalidationRequest(*entry), WTFMove(entry), [this, key, frameID](std::unique_ptr<Entry> revalidatedEntry) { + ASSERT(!revalidatedEntry || !revalidatedEntry->needsValidation()); + auto protectRevalidator = m_pendingPreloads.take(key); + LOG(NetworkCacheSpeculativePreloading, "(NetworkProcess) Speculative revalidation completed for '%s':", key.identifier().utf8().data()); + + if (satisfyPendingRequests(key, revalidatedEntry.get())) { + if (revalidatedEntry) + logSpeculativeLoadingDiagnosticMessage(frameID, DiagnosticLoggingKeys::successfulSpeculativeWarmupWithRevalidationKey()); + return; + } + + if (revalidatedEntry) + addPreloadedEntry(WTFMove(revalidatedEntry), frameID, WasRevalidated::Yes); + }); + m_pendingPreloads.add(key, WTFMove(revalidator)); +} + +void SpeculativeLoadManager::preloadEntry(const Key& key, const GlobalFrameID& frameID) +{ + m_pendingPreloads.add(key, nullptr); + retrieveEntryFromStorage(key, [this, key, frameID](std::unique_ptr<Entry> entry) { + m_pendingPreloads.remove(key); + + if (satisfyPendingRequests(key, entry.get())) { + if (entry) + logSpeculativeLoadingDiagnosticMessage(frameID, DiagnosticLoggingKeys::successfulSpeculativeWarmupWithoutRevalidationKey()); + return; + } + + if (!entry) + return; + + if (entry->needsValidation()) + revalidateEntry(WTFMove(entry), frameID); + else + addPreloadedEntry(WTFMove(entry), frameID, WasRevalidated::No); + }); +} + +void SpeculativeLoadManager::startSpeculativeRevalidation(const GlobalFrameID& frameID, SubresourcesEntry& entry) +{ + for (auto& subresource : entry.subresources()) { + auto key = subresource.key; + if (!subresource.value.isTransient) + preloadEntry(key, frameID); + else { + LOG(NetworkCacheSpeculativePreloading, "(NetworkProcess) Not preloading '%s' because it is marked as transient", subresource.key.identifier().utf8().data()); + m_notPreloadedEntries.add(key, std::make_unique<ExpiringEntry>([this, key, frameID] { + logSpeculativeLoadingDiagnosticMessage(frameID, DiagnosticLoggingKeys::entryRightlyNotWarmedUpKey()); + m_notPreloadedEntries.remove(key); + })); + } + } +} + +void SpeculativeLoadManager::retrieveSubresourcesEntry(const Key& storageKey, std::function<void (std::unique_ptr<SubresourcesEntry>)> completionHandler) +{ + ASSERT(storageKey.type() == "resource"); + auto subresourcesStorageKey = makeSubresourcesKey(storageKey); + m_storage.retrieve(subresourcesStorageKey, static_cast<unsigned>(ResourceLoadPriority::Medium), [completionHandler](std::unique_ptr<Storage::Record> record) { + if (!record) { + completionHandler(nullptr); + return false; + } + + auto subresourcesEntry = SubresourcesEntry::decodeStorageRecord(*record); + if (!subresourcesEntry) { + completionHandler(nullptr); + return false; + } + + completionHandler(WTFMove(subresourcesEntry)); + return true; + }); +} + +} // namespace NetworkCache + +} // namespace WebKit + +#endif // ENABLE(NETWORK_CACHE_SPECULATIVE_REVALIDATION) diff --git a/Source/WebKit2/NetworkProcess/cache/NetworkCacheSpeculativeLoadManager.h b/Source/WebKit2/NetworkProcess/cache/NetworkCacheSpeculativeLoadManager.h new file mode 100644 index 000000000..4c1de7200 --- /dev/null +++ b/Source/WebKit2/NetworkProcess/cache/NetworkCacheSpeculativeLoadManager.h @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2015 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. + */ + +#ifndef NetworkCacheSpeculativeLoadManager_h +#define NetworkCacheSpeculativeLoadManager_h + +#if ENABLE(NETWORK_CACHE_SPECULATIVE_REVALIDATION) + +#include "NetworkCache.h" +#include "NetworkCacheStorage.h" +#include <WebCore/ResourceRequest.h> +#include <wtf/HashMap.h> +#include <wtf/Vector.h> + +namespace WebKit { + +namespace NetworkCache { + +class Entry; +class SpeculativeLoad; +class SubresourcesEntry; + +class SpeculativeLoadManager { +public: + explicit SpeculativeLoadManager(Storage&); + ~SpeculativeLoadManager(); + + void registerLoad(const GlobalFrameID&, const WebCore::ResourceRequest&, const Key& resourceKey); + + typedef std::function<void (std::unique_ptr<Entry>)> RetrieveCompletionHandler; + bool retrieve(const GlobalFrameID&, const Key& storageKey, const RetrieveCompletionHandler&); + +private: + enum class WasRevalidated { No, Yes }; + void addPreloadedEntry(std::unique_ptr<Entry>, const GlobalFrameID&, WasRevalidated); + void preloadEntry(const Key&, const GlobalFrameID&); + void retrieveEntryFromStorage(const Key&, const RetrieveCompletionHandler&); + void revalidateEntry(std::unique_ptr<Entry>, const GlobalFrameID&); + bool satisfyPendingRequests(const Key&, Entry*); + void retrieveSubresourcesEntry(const Key& storageKey, std::function<void (std::unique_ptr<SubresourcesEntry>)>); + void startSpeculativeRevalidation(const GlobalFrameID&, SubresourcesEntry&); + + Storage& m_storage; + + class PendingFrameLoad; + HashMap<GlobalFrameID, RefPtr<PendingFrameLoad>> m_pendingFrameLoads; + + HashMap<Key, std::unique_ptr<SpeculativeLoad>> m_pendingPreloads; + HashMap<Key, std::unique_ptr<Vector<RetrieveCompletionHandler>>> m_pendingRetrieveRequests; + + class PreloadedEntry; + HashMap<Key, std::unique_ptr<PreloadedEntry>> m_preloadedEntries; + + class ExpiringEntry; + HashMap<Key, std::unique_ptr<ExpiringEntry>> m_notPreloadedEntries; // For logging. +}; + +} // namespace NetworkCache + +} // namespace WebKit + +#endif // ENABLE(NETWORK_CACHE_SPECULATIVE_REVALIDATION) + +#endif // NetworkCacheSpeculativeLoadManager_h diff --git a/Source/WebKit2/NetworkProcess/cache/NetworkCacheStatistics.cpp b/Source/WebKit2/NetworkProcess/cache/NetworkCacheStatistics.cpp new file mode 100644 index 000000000..263393db7 --- /dev/null +++ b/Source/WebKit2/NetworkProcess/cache/NetworkCacheStatistics.cpp @@ -0,0 +1,462 @@ +/* + * Copyright (C) 2015 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" + +#if ENABLE(NETWORK_CACHE) +#include "NetworkCacheStatistics.h" + +#include "Logging.h" +#include "NetworkCache.h" +#include "NetworkCacheFileSystem.h" +#include "NetworkProcess.h" +#include <WebCore/DiagnosticLoggingKeys.h> +#include <WebCore/DiagnosticLoggingResultType.h> +#include <WebCore/ResourceRequest.h> +#include <WebCore/SQLiteDatabaseTracker.h> +#include <WebCore/SQLiteStatement.h> +#include <WebCore/SQLiteTransaction.h> +#include <wtf/RunLoop.h> + +namespace WebKit { +namespace NetworkCache { + +static const char* StatisticsDatabaseName = "WebKitCacheStatistics.db"; +static const std::chrono::milliseconds mininumWriteInterval = std::chrono::milliseconds(10000); + +static bool executeSQLCommand(WebCore::SQLiteDatabase& database, const String& sql) +{ + ASSERT(!RunLoop::isMain()); + ASSERT(WebCore::SQLiteDatabaseTracker::hasTransactionInProgress()); + ASSERT(database.isOpen()); + + bool result = database.executeCommand(sql); + if (!result) + LOG_ERROR("Network cache statistics: failed to execute statement \"%s\" error \"%s\"", sql.utf8().data(), database.lastErrorMsg()); + + return result; +} + +static bool executeSQLStatement(WebCore::SQLiteStatement& statement) +{ + ASSERT(!RunLoop::isMain()); + ASSERT(WebCore::SQLiteDatabaseTracker::hasTransactionInProgress()); + ASSERT(statement.database().isOpen()); + + if (statement.step() != SQLITE_DONE) { + LOG_ERROR("Network cache statistics: failed to execute statement \"%s\" error \"%s\"", statement.query().utf8().data(), statement.database().lastErrorMsg()); + return false; + } + + return true; +} + +std::unique_ptr<Statistics> Statistics::open(const String& cachePath) +{ + ASSERT(RunLoop::isMain()); + + String databasePath = WebCore::pathByAppendingComponent(cachePath, StatisticsDatabaseName); + return std::unique_ptr<Statistics>(new Statistics(databasePath)); +} + +Statistics::Statistics(const String& databasePath) + : m_serialBackgroundIOQueue(WorkQueue::create("com.apple.WebKit.Cache.Statistics.Background", WorkQueue::Type::Serial, WorkQueue::QOS::Background)) + , m_writeTimer(*this, &Statistics::writeTimerFired) +{ + initialize(databasePath); +} + +void Statistics::initialize(const String& databasePath) +{ + ASSERT(RunLoop::isMain()); + + auto startTime = std::chrono::system_clock::now(); + + StringCapture databasePathCapture(databasePath); + StringCapture networkCachePathCapture(singleton().recordsPath()); + serialBackgroundIOQueue().dispatch([this, databasePathCapture, networkCachePathCapture, startTime] { + WebCore::SQLiteTransactionInProgressAutoCounter transactionCounter; + + String databasePath = databasePathCapture.string(); + if (!WebCore::makeAllDirectories(WebCore::directoryName(databasePath))) + return; + + LOG(NetworkCache, "(NetworkProcess) Opening network cache statistics database at %s...", databasePath.utf8().data()); + m_database.open(databasePath); + m_database.disableThreadingChecks(); + if (!m_database.isOpen()) { + LOG_ERROR("Network cache statistics: Failed to open / create the network cache statistics database"); + return; + } + + executeSQLCommand(m_database, ASCIILiteral("CREATE TABLE IF NOT EXISTS AlreadyRequested (hash TEXT PRIMARY KEY)")); + executeSQLCommand(m_database, ASCIILiteral("CREATE TABLE IF NOT EXISTS UncachedReason (hash TEXT PRIMARY KEY, reason INTEGER)")); + + WebCore::SQLiteStatement statement(m_database, ASCIILiteral("SELECT count(*) FROM AlreadyRequested")); + if (statement.prepareAndStep() != SQLITE_ROW) { + LOG_ERROR("Network cache statistics: Failed to count the number of rows in AlreadyRequested table"); + return; + } + + m_approximateEntryCount = statement.getColumnInt(0); + +#if !LOG_DISABLED + auto elapsedMS = static_cast<int64_t>(std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now() - startTime).count()); +#endif + LOG(NetworkCache, "(NetworkProcess) Network cache statistics database load complete, entries=%lu time=%" PRIi64 "ms", static_cast<size_t>(m_approximateEntryCount), elapsedMS); + + if (!m_approximateEntryCount) { + bootstrapFromNetworkCache(networkCachePathCapture.string()); +#if !LOG_DISABLED + elapsedMS = static_cast<int64_t>(std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now() - startTime).count()); +#endif + LOG(NetworkCache, "(NetworkProcess) Network cache statistics database bootstrapping complete, entries=%lu time=%" PRIi64 "ms", static_cast<size_t>(m_approximateEntryCount), elapsedMS); + } + }); +} + +void Statistics::bootstrapFromNetworkCache(const String& networkCachePath) +{ + ASSERT(!RunLoop::isMain()); + + LOG(NetworkCache, "(NetworkProcess) Bootstrapping the network cache statistics database from the network cache..."); + + Vector<StringCapture> hashes; + traverseRecordsFiles(networkCachePath, ASCIILiteral("resource"), [&hashes](const String& fileName, const String& hashString, const String& type, bool isBodyBlob, const String& recordDirectoryPath) { + if (isBodyBlob) + return; + + Key::HashType hash; + if (!Key::stringToHash(hashString, hash)) + return; + + hashes.append(hashString); + }); + + WebCore::SQLiteTransactionInProgressAutoCounter transactionCounter; + WebCore::SQLiteTransaction writeTransaction(m_database); + writeTransaction.begin(); + + addHashesToDatabase(hashes); + + writeTransaction.commit(); +} + +void Statistics::shrinkIfNeeded() +{ + ASSERT(RunLoop::isMain()); + const size_t maxEntries = 100000; + + if (m_approximateEntryCount < maxEntries) + return; + + LOG(NetworkCache, "(NetworkProcess) shrinking statistics cache m_approximateEntryCount=%lu, maxEntries=%lu", static_cast<size_t>(m_approximateEntryCount), maxEntries); + + clear(); + + StringCapture networkCachePathCapture(singleton().recordsPath()); + serialBackgroundIOQueue().dispatch([this, networkCachePathCapture] { + bootstrapFromNetworkCache(networkCachePathCapture.string()); + LOG(NetworkCache, "(NetworkProcess) statistics cache shrink completed m_approximateEntryCount=%lu", static_cast<size_t>(m_approximateEntryCount)); + }); +} + +void Statistics::recordRetrievalRequest(uint64_t webPageID) +{ + NetworkProcess::singleton().logDiagnosticMessage(webPageID, WebCore::DiagnosticLoggingKeys::networkCacheKey(), WebCore::DiagnosticLoggingKeys::retrievalRequestKey(), WebCore::ShouldSample::Yes); +} + +void Statistics::recordNotCachingResponse(const Key& key, StoreDecision storeDecision) +{ + ASSERT(storeDecision != StoreDecision::Yes); + + m_storeDecisionsToAdd.set(key.hashAsString(), storeDecision); + if (!m_writeTimer.isActive()) + m_writeTimer.startOneShot(mininumWriteInterval); +} + +static String retrieveDecisionToDiagnosticKey(RetrieveDecision retrieveDecision) +{ + switch (retrieveDecision) { + case RetrieveDecision::NoDueToHTTPMethod: + return WebCore::DiagnosticLoggingKeys::unsupportedHTTPMethodKey(); + case RetrieveDecision::NoDueToConditionalRequest: + return WebCore::DiagnosticLoggingKeys::isConditionalRequestKey(); + case RetrieveDecision::NoDueToReloadIgnoringCache: + return WebCore::DiagnosticLoggingKeys::isReloadIgnoringCacheDataKey(); + case RetrieveDecision::Yes: + ASSERT_NOT_REACHED(); + break; + } + return emptyString(); +} + +void Statistics::recordNotUsingCacheForRequest(uint64_t webPageID, const Key& key, const WebCore::ResourceRequest& request, RetrieveDecision retrieveDecision) +{ + ASSERT(retrieveDecision != RetrieveDecision::Yes); + + String hash = key.hashAsString(); + WebCore::URL requestURL = request.url(); + queryWasEverRequested(hash, NeedUncachedReason::No, [this, hash, requestURL, webPageID, retrieveDecision](bool wasEverRequested, const Optional<StoreDecision>&) { + if (wasEverRequested) { + String diagnosticKey = retrieveDecisionToDiagnosticKey(retrieveDecision); + LOG(NetworkCache, "(NetworkProcess) webPageID %" PRIu64 ": %s was previously requested but we are not using the cache, reason: %s", webPageID, requestURL.string().ascii().data(), diagnosticKey.utf8().data()); + NetworkProcess::singleton().logDiagnosticMessageWithValue(webPageID, WebCore::DiagnosticLoggingKeys::networkCacheKey(), WebCore::DiagnosticLoggingKeys::unusedKey(), diagnosticKey, WebCore::ShouldSample::Yes); + } else { + NetworkProcess::singleton().logDiagnosticMessageWithValue(webPageID, WebCore::DiagnosticLoggingKeys::networkCacheKey(), WebCore::DiagnosticLoggingKeys::requestKey(), WebCore::DiagnosticLoggingKeys::neverSeenBeforeKey(), WebCore::ShouldSample::Yes); + markAsRequested(hash); + } + }); +} + +static String storeDecisionToDiagnosticKey(StoreDecision storeDecision) +{ + switch (storeDecision) { + case StoreDecision::NoDueToProtocol: + return WebCore::DiagnosticLoggingKeys::notHTTPFamilyKey(); + case StoreDecision::NoDueToHTTPMethod: + return WebCore::DiagnosticLoggingKeys::unsupportedHTTPMethodKey(); + case StoreDecision::NoDueToAttachmentResponse: + return WebCore::DiagnosticLoggingKeys::isAttachmentKey(); + case StoreDecision::NoDueToNoStoreResponse: + case StoreDecision::NoDueToNoStoreRequest: + return WebCore::DiagnosticLoggingKeys::cacheControlNoStoreKey(); + case StoreDecision::NoDueToHTTPStatusCode: + return WebCore::DiagnosticLoggingKeys::uncacheableStatusCodeKey(); + case StoreDecision::NoDueToUnlikelyToReuse: + return WebCore::DiagnosticLoggingKeys::unlikelyToReuseKey(); + case StoreDecision::NoDueToStreamingMedia: + return WebCore::DiagnosticLoggingKeys::streamingMedia(); + case StoreDecision::Yes: + // It was stored but could not be retrieved so it must have been pruned from the cache. + return WebCore::DiagnosticLoggingKeys::noLongerInCacheKey(); + } + return String(); +} + +void Statistics::recordRetrievalFailure(uint64_t webPageID, const Key& key, const WebCore::ResourceRequest& request) +{ + String hash = key.hashAsString(); + WebCore::URL requestURL = request.url(); + queryWasEverRequested(hash, NeedUncachedReason::Yes, [this, hash, requestURL, webPageID](bool wasPreviouslyRequested, const Optional<StoreDecision>& storeDecision) { + if (wasPreviouslyRequested) { + String diagnosticKey = storeDecisionToDiagnosticKey(storeDecision.value()); + LOG(NetworkCache, "(NetworkProcess) webPageID %" PRIu64 ": %s was previously request but is not in the cache, reason: %s", webPageID, requestURL.string().ascii().data(), diagnosticKey.utf8().data()); + NetworkProcess::singleton().logDiagnosticMessageWithValue(webPageID, WebCore::DiagnosticLoggingKeys::networkCacheKey(), WebCore::DiagnosticLoggingKeys::notInCacheKey(), diagnosticKey, WebCore::ShouldSample::Yes); + } else { + NetworkProcess::singleton().logDiagnosticMessageWithValue(webPageID, WebCore::DiagnosticLoggingKeys::networkCacheKey(), WebCore::DiagnosticLoggingKeys::requestKey(), WebCore::DiagnosticLoggingKeys::neverSeenBeforeKey(), WebCore::ShouldSample::Yes); + markAsRequested(hash); + } + }); +} + +static String cachedEntryReuseFailureToDiagnosticKey(UseDecision decision) +{ + switch (decision) { + case UseDecision::NoDueToVaryingHeaderMismatch: + return WebCore::DiagnosticLoggingKeys::varyingHeaderMismatchKey(); + case UseDecision::NoDueToMissingValidatorFields: + return WebCore::DiagnosticLoggingKeys::missingValidatorFieldsKey(); + case UseDecision::NoDueToDecodeFailure: + case UseDecision::NoDueToExpiredRedirect: + return WebCore::DiagnosticLoggingKeys::otherKey(); + case UseDecision::Use: + case UseDecision::Validate: + ASSERT_NOT_REACHED(); + break; + } + return emptyString(); +} + +void Statistics::recordRetrievedCachedEntry(uint64_t webPageID, const Key& key, const WebCore::ResourceRequest& request, UseDecision decision) +{ + WebCore::URL requestURL = request.url(); + if (decision == UseDecision::Use) { + LOG(NetworkCache, "(NetworkProcess) webPageID %" PRIu64 ": %s is in the cache and is used", webPageID, requestURL.string().ascii().data()); + NetworkProcess::singleton().logDiagnosticMessageWithResult(webPageID, WebCore::DiagnosticLoggingKeys::networkCacheKey(), WebCore::DiagnosticLoggingKeys::retrievalKey(), WebCore::DiagnosticLoggingResultPass, WebCore::ShouldSample::Yes); + return; + } + + if (decision == UseDecision::Validate) { + LOG(NetworkCache, "(NetworkProcess) webPageID %" PRIu64 ": %s is in the cache but needs revalidation", webPageID, requestURL.string().ascii().data()); + NetworkProcess::singleton().logDiagnosticMessageWithValue(webPageID, WebCore::DiagnosticLoggingKeys::networkCacheKey(), WebCore::DiagnosticLoggingKeys::retrievalKey(), WebCore::DiagnosticLoggingKeys::needsRevalidationKey(), WebCore::ShouldSample::Yes); + return; + } + + String diagnosticKey = cachedEntryReuseFailureToDiagnosticKey(decision); + LOG(NetworkCache, "(NetworkProcess) webPageID %" PRIu64 ": %s is in the cache but wasn't used, reason: %s", webPageID, requestURL.string().ascii().data(), diagnosticKey.utf8().data()); + NetworkProcess::singleton().logDiagnosticMessageWithValue(webPageID, WebCore::DiagnosticLoggingKeys::networkCacheKey(), WebCore::DiagnosticLoggingKeys::unusableCachedEntryKey(), diagnosticKey, WebCore::ShouldSample::Yes); +} + +void Statistics::recordRevalidationSuccess(uint64_t webPageID, const Key& key, const WebCore::ResourceRequest& request) +{ + WebCore::URL requestURL = request.url(); + LOG(NetworkCache, "(NetworkProcess) webPageID %" PRIu64 ": %s was successfully revalidated", webPageID, requestURL.string().ascii().data()); + + NetworkProcess::singleton().logDiagnosticMessageWithResult(webPageID, WebCore::DiagnosticLoggingKeys::networkCacheKey(), WebCore::DiagnosticLoggingKeys::revalidatingKey(), WebCore::DiagnosticLoggingResultPass, WebCore::ShouldSample::Yes); +} + +void Statistics::markAsRequested(const String& hash) +{ + ASSERT(RunLoop::isMain()); + + m_hashesToAdd.add(hash); + if (!m_writeTimer.isActive()) + m_writeTimer.startOneShot(mininumWriteInterval); +} + +void Statistics::writeTimerFired() +{ + ASSERT(RunLoop::isMain()); + + Vector<StringCapture> hashesToAdd; + copyToVector(m_hashesToAdd, hashesToAdd); + m_hashesToAdd.clear(); + + Vector<std::pair<StringCapture, StoreDecision>> storeDecisionsToAdd; + copyToVector(m_storeDecisionsToAdd, storeDecisionsToAdd); + m_storeDecisionsToAdd.clear(); + + shrinkIfNeeded(); + + serialBackgroundIOQueue().dispatch([this, hashesToAdd, storeDecisionsToAdd] { + if (!m_database.isOpen()) + return; + + WebCore::SQLiteTransactionInProgressAutoCounter transactionCounter; + WebCore::SQLiteTransaction writeTransaction(m_database); + writeTransaction.begin(); + + addHashesToDatabase(hashesToAdd); + addStoreDecisionsToDatabase(storeDecisionsToAdd); + + writeTransaction.commit(); + }); +} + +void Statistics::queryWasEverRequested(const String& hash, NeedUncachedReason needUncachedReason, const RequestedCompletionHandler& completionHandler) +{ + ASSERT(RunLoop::isMain()); + + // Query pending writes first. + bool wasAlreadyRequested = m_hashesToAdd.contains(hash); + if (wasAlreadyRequested && needUncachedReason == NeedUncachedReason::No) { + completionHandler(true, Nullopt); + return; + } + if (needUncachedReason == NeedUncachedReason::Yes && m_storeDecisionsToAdd.contains(hash)) { + completionHandler(true, m_storeDecisionsToAdd.get(hash)); + return; + } + + // Query the database. + auto everRequestedQuery = std::make_unique<EverRequestedQuery>(EverRequestedQuery { hash, needUncachedReason == NeedUncachedReason::Yes, completionHandler }); + auto& query = *everRequestedQuery; + m_activeQueries.add(WTFMove(everRequestedQuery)); + serialBackgroundIOQueue().dispatch([this, wasAlreadyRequested, &query] () mutable { + WebCore::SQLiteTransactionInProgressAutoCounter transactionCounter; + Optional<StoreDecision> storeDecision; + if (m_database.isOpen()) { + if (!wasAlreadyRequested) { + WebCore::SQLiteStatement statement(m_database, ASCIILiteral("SELECT hash FROM AlreadyRequested WHERE hash=?")); + if (statement.prepare() == SQLITE_OK) { + statement.bindText(1, query.hash); + wasAlreadyRequested = (statement.step() == SQLITE_ROW); + } + } + if (wasAlreadyRequested && query.needUncachedReason) { + WebCore::SQLiteStatement statement(m_database, ASCIILiteral("SELECT reason FROM UncachedReason WHERE hash=?")); + storeDecision = StoreDecision::Yes; + if (statement.prepare() == SQLITE_OK) { + statement.bindText(1, query.hash); + if (statement.step() == SQLITE_ROW) + storeDecision = static_cast<StoreDecision>(statement.getColumnInt(0)); + } + } + } + RunLoop::main().dispatch([this, &query, wasAlreadyRequested, storeDecision] { + query.completionHandler(wasAlreadyRequested, storeDecision); + m_activeQueries.remove(&query); + }); + }); +} + +void Statistics::clear() +{ + ASSERT(RunLoop::isMain()); + + serialBackgroundIOQueue().dispatch([this] { + if (m_database.isOpen()) { + WebCore::SQLiteTransactionInProgressAutoCounter transactionCounter; + WebCore::SQLiteTransaction deleteTransaction(m_database); + deleteTransaction.begin(); + executeSQLCommand(m_database, ASCIILiteral("DELETE FROM AlreadyRequested")); + executeSQLCommand(m_database, ASCIILiteral("DELETE FROM UncachedReason")); + deleteTransaction.commit(); + m_approximateEntryCount = 0; + } + }); +} + +void Statistics::addHashesToDatabase(const Vector<StringCapture>& hashes) +{ + ASSERT(!RunLoop::isMain()); + ASSERT(WebCore::SQLiteDatabaseTracker::hasTransactionInProgress()); + ASSERT(m_database.isOpen()); + + WebCore::SQLiteStatement statement(m_database, ASCIILiteral("INSERT OR IGNORE INTO AlreadyRequested (hash) VALUES (?)")); + if (statement.prepare() != SQLITE_OK) + return; + + for (auto& hash : hashes) { + statement.bindText(1, hash.string()); + if (executeSQLStatement(statement)) + ++m_approximateEntryCount; + statement.reset(); + } +} + +void Statistics::addStoreDecisionsToDatabase(const Vector<std::pair<StringCapture, StoreDecision>>& storeDecisions) +{ + ASSERT(!RunLoop::isMain()); + ASSERT(WebCore::SQLiteDatabaseTracker::hasTransactionInProgress()); + ASSERT(m_database.isOpen()); + + WebCore::SQLiteStatement statement(m_database, ASCIILiteral("INSERT OR REPLACE INTO UncachedReason (hash, reason) VALUES (?, ?)")); + if (statement.prepare() != SQLITE_OK) + return; + + for (auto& pair : storeDecisions) { + statement.bindText(1, pair.first.string()); + statement.bindInt(2, static_cast<int>(pair.second)); + executeSQLStatement(statement); + statement.reset(); + } +} + +} +} + +#endif // ENABLE(NETWORK_CACHE) diff --git a/Source/WebKit2/NetworkProcess/cache/NetworkCacheStatistics.h b/Source/WebKit2/NetworkProcess/cache/NetworkCacheStatistics.h new file mode 100644 index 000000000..9f048e722 --- /dev/null +++ b/Source/WebKit2/NetworkProcess/cache/NetworkCacheStatistics.h @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2015 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. + */ + +#ifndef NetworkCacheStatistics_h +#define NetworkCacheStatistics_h + +#if ENABLE(NETWORK_CACHE) + +#include "NetworkCache.h" +#include "NetworkCacheKey.h" +#include <WebCore/SQLiteDatabase.h> +#include <WebCore/Timer.h> +#include <wtf/WorkQueue.h> + +namespace WebCore { +class ResourceRequest; +} + +namespace WebKit { +namespace NetworkCache { + +class Statistics { +public: + static std::unique_ptr<Statistics> open(const String& cachePath); + + void clear(); + + void recordRetrievalRequest(uint64_t webPageID); + void recordNotCachingResponse(const Key&, StoreDecision); + void recordNotUsingCacheForRequest(uint64_t webPageID, const Key&, const WebCore::ResourceRequest&, RetrieveDecision); + void recordRetrievalFailure(uint64_t webPageID, const Key&, const WebCore::ResourceRequest&); + void recordRetrievedCachedEntry(uint64_t webPageID, const Key&, const WebCore::ResourceRequest&, UseDecision); + void recordRevalidationSuccess(uint64_t webPageID, const Key&, const WebCore::ResourceRequest&); + +private: + explicit Statistics(const String& databasePath); + + WorkQueue& serialBackgroundIOQueue() { return m_serialBackgroundIOQueue.get(); } + + void initialize(const String& databasePath); + void bootstrapFromNetworkCache(const String& networkCachePath); + void shrinkIfNeeded(); + + void addHashesToDatabase(const Vector<StringCapture>& hashes); + void addStoreDecisionsToDatabase(const Vector<std::pair<StringCapture, NetworkCache::StoreDecision>>&); + void writeTimerFired(); + + typedef std::function<void (bool wasEverRequested, const Optional<StoreDecision>&)> RequestedCompletionHandler; + enum class NeedUncachedReason { No, Yes }; + void queryWasEverRequested(const String&, NeedUncachedReason, const RequestedCompletionHandler&); + void markAsRequested(const String& hash); + + struct EverRequestedQuery { + String hash; + bool needUncachedReason; + RequestedCompletionHandler completionHandler; + }; + + std::atomic<size_t> m_approximateEntryCount { 0 }; + + mutable Ref<WorkQueue> m_serialBackgroundIOQueue; + mutable HashSet<std::unique_ptr<const EverRequestedQuery>> m_activeQueries; + WebCore::SQLiteDatabase m_database; + HashSet<String> m_hashesToAdd; + HashMap<String, NetworkCache::StoreDecision> m_storeDecisionsToAdd; + WebCore::Timer m_writeTimer; +}; + +} +} + +#endif // ENABLE(NETWORK_CACHE) + +#endif // NetworkCacheStatistics_h diff --git a/Source/WebKit2/NetworkProcess/cache/NetworkCacheStorage.cpp b/Source/WebKit2/NetworkProcess/cache/NetworkCacheStorage.cpp new file mode 100644 index 000000000..1f650bfd1 --- /dev/null +++ b/Source/WebKit2/NetworkProcess/cache/NetworkCacheStorage.cpp @@ -0,0 +1,1051 @@ +/* + * Copyright (C) 2014-2015 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 "NetworkCacheStorage.h" + +#if ENABLE(NETWORK_CACHE) + +#include "Logging.h" +#include "NetworkCacheCoders.h" +#include "NetworkCacheFileSystem.h" +#include "NetworkCacheIOChannel.h" +#include <mutex> +#include <wtf/Condition.h> +#include <wtf/Lock.h> +#include <wtf/RandomNumber.h> +#include <wtf/RunLoop.h> +#include <wtf/text/CString.h> +#include <wtf/text/StringBuilder.h> + +namespace WebKit { +namespace NetworkCache { + +static const char versionDirectoryPrefix[] = "Version "; +static const char recordsDirectoryName[] = "Records"; +static const char blobsDirectoryName[] = "Blobs"; +static const char blobSuffix[] = "-blob"; + +static double computeRecordWorth(FileTimes); + +struct Storage::ReadOperation { + WTF_MAKE_FAST_ALLOCATED; +public: + ReadOperation(const Key& key, const RetrieveCompletionHandler& completionHandler) + : key(key) + , completionHandler(completionHandler) + { } + + void cancel(); + bool finish(); + + const Key key; + const RetrieveCompletionHandler completionHandler; + + std::unique_ptr<Record> resultRecord; + SHA1::Digest expectedBodyHash; + BlobStorage::Blob resultBodyBlob; + std::atomic<unsigned> activeCount { 0 }; + bool isCanceled { false }; +}; + +void Storage::ReadOperation::cancel() +{ + ASSERT(RunLoop::isMain()); + + if (isCanceled) + return; + isCanceled = true; + completionHandler(nullptr); +} + +bool Storage::ReadOperation::finish() +{ + ASSERT(RunLoop::isMain()); + + if (isCanceled) + return false; + if (resultRecord && resultRecord->body.isNull()) { + if (resultBodyBlob.hash == expectedBodyHash) + resultRecord->body = resultBodyBlob.data; + else + resultRecord = nullptr; + } + return completionHandler(WTFMove(resultRecord)); +} + +struct Storage::WriteOperation { + WTF_MAKE_FAST_ALLOCATED; +public: + WriteOperation(const Record& record, const MappedBodyHandler& mappedBodyHandler) + : record(record) + , mappedBodyHandler(mappedBodyHandler) + { } + + const Record record; + const MappedBodyHandler mappedBodyHandler; + + std::atomic<unsigned> activeCount { 0 }; +}; + +struct Storage::TraverseOperation { + WTF_MAKE_FAST_ALLOCATED; +public: + TraverseOperation(const String& type, TraverseFlags flags, const TraverseHandler& handler) + : type(type) + , flags(flags) + , handler(handler) + { } + + const String type; + const TraverseFlags flags; + const TraverseHandler handler; + + Lock activeMutex; + Condition activeCondition; + unsigned activeCount { 0 }; +}; + +std::unique_ptr<Storage> Storage::open(const String& cachePath) +{ + ASSERT(RunLoop::isMain()); + + if (!WebCore::makeAllDirectories(cachePath)) + return nullptr; + return std::unique_ptr<Storage>(new Storage(cachePath)); +} + +static String makeVersionedDirectoryPath(const String& baseDirectoryPath) +{ + String versionSubdirectory = versionDirectoryPrefix + String::number(Storage::version); + return WebCore::pathByAppendingComponent(baseDirectoryPath, versionSubdirectory); +} + +static String makeRecordsDirectoryPath(const String& baseDirectoryPath) +{ + return WebCore::pathByAppendingComponent(makeVersionedDirectoryPath(baseDirectoryPath), recordsDirectoryName); +} + +static String makeBlobDirectoryPath(const String& baseDirectoryPath) +{ + return WebCore::pathByAppendingComponent(makeVersionedDirectoryPath(baseDirectoryPath), blobsDirectoryName); +} + +void traverseRecordsFiles(const String& recordsPath, const String& expectedType, const std::function<void (const String& fileName, const String& hashString, const String& type, bool isBlob, const String& recordDirectoryPath)>& function) +{ + traverseDirectory(recordsPath, [&recordsPath, &function, &expectedType](const String& partitionName, DirectoryEntryType entryType) { + if (entryType != DirectoryEntryType::Directory) + return; + String partitionPath = WebCore::pathByAppendingComponent(recordsPath, partitionName); + traverseDirectory(partitionPath, [&function, &partitionPath, &expectedType](const String& actualType, DirectoryEntryType entryType) { + if (entryType != DirectoryEntryType::Directory) + return; + if (!expectedType.isEmpty() && expectedType != actualType) + return; + String recordDirectoryPath = WebCore::pathByAppendingComponent(partitionPath, actualType); + traverseDirectory(recordDirectoryPath, [&function, &recordDirectoryPath, &actualType](const String& fileName, DirectoryEntryType entryType) { + if (entryType != DirectoryEntryType::File || fileName.length() < Key::hashStringLength()) + return; + + String hashString = fileName.substring(0, Key::hashStringLength()); + auto isBlob = fileName.length() > Key::hashStringLength() && fileName.endsWith(blobSuffix); + function(fileName, hashString, actualType, isBlob, recordDirectoryPath); + }); + }); + }); +} + +static void deleteEmptyRecordsDirectories(const String& recordsPath) +{ + traverseDirectory(recordsPath, [&recordsPath](const String& partitionName, DirectoryEntryType type) { + if (type != DirectoryEntryType::Directory) + return; + + // Delete [type] sub-folders. + String partitionPath = WebCore::pathByAppendingComponent(recordsPath, partitionName); + traverseDirectory(partitionPath, [&partitionPath](const String& subdirName, DirectoryEntryType entryType) { + if (entryType != DirectoryEntryType::Directory) + return; + + // Let system figure out if it is really empty. + WebCore::deleteEmptyDirectory(WebCore::pathByAppendingComponent(partitionPath, subdirName)); + }); + + // Delete [Partition] folders. + // Let system figure out if it is really empty. + WebCore::deleteEmptyDirectory(WebCore::pathByAppendingComponent(recordsPath, partitionName)); + }); +} + +Storage::Storage(const String& baseDirectoryPath) + : m_basePath(baseDirectoryPath) + , m_recordsPath(makeRecordsDirectoryPath(baseDirectoryPath)) + , m_readOperationTimeoutTimer(*this, &Storage::cancelAllReadOperations) + , m_writeOperationDispatchTimer(*this, &Storage::dispatchPendingWriteOperations) + , m_ioQueue(WorkQueue::create("com.apple.WebKit.Cache.Storage", WorkQueue::Type::Concurrent)) + , m_backgroundIOQueue(WorkQueue::create("com.apple.WebKit.Cache.Storage.background", WorkQueue::Type::Concurrent, WorkQueue::QOS::Background)) + , m_serialBackgroundIOQueue(WorkQueue::create("com.apple.WebKit.Cache.Storage.serialBackground", WorkQueue::Type::Serial, WorkQueue::QOS::Background)) + , m_blobStorage(makeBlobDirectoryPath(baseDirectoryPath)) +{ + deleteOldVersions(); + synchronize(); +} + +Storage::~Storage() +{ +} + +String Storage::basePath() const +{ + return m_basePath.isolatedCopy(); +} + +String Storage::versionPath() const +{ + return makeVersionedDirectoryPath(basePath()); +} + +String Storage::recordsPath() const +{ + return m_recordsPath.isolatedCopy(); +} + +size_t Storage::approximateSize() const +{ + return m_approximateRecordsSize + m_blobStorage.approximateSize(); +} + +void Storage::synchronize() +{ + ASSERT(RunLoop::isMain()); + + if (m_synchronizationInProgress || m_shrinkInProgress) + return; + m_synchronizationInProgress = true; + + LOG(NetworkCacheStorage, "(NetworkProcess) synchronizing cache"); + + backgroundIOQueue().dispatch([this] { + auto recordFilter = std::make_unique<ContentsFilter>(); + auto blobFilter = std::make_unique<ContentsFilter>(); + size_t recordsSize = 0; + unsigned count = 0; + String anyType; + traverseRecordsFiles(recordsPath(), anyType, [&recordFilter, &blobFilter, &recordsSize, &count](const String& fileName, const String& hashString, const String& type, bool isBlob, const String& recordDirectoryPath) { + auto filePath = WebCore::pathByAppendingComponent(recordDirectoryPath, fileName); + + Key::HashType hash; + if (!Key::stringToHash(hashString, hash)) { + WebCore::deleteFile(filePath); + return; + } + long long fileSize = 0; + WebCore::getFileSize(filePath, fileSize); + if (!fileSize) { + WebCore::deleteFile(filePath); + return; + } + + if (isBlob) { + blobFilter->add(hash); + return; + } + + recordFilter->add(hash); + recordsSize += fileSize; + ++count; + }); + + auto* recordFilterPtr = recordFilter.release(); + auto* blobFilterPtr = blobFilter.release(); + RunLoop::main().dispatch([this, recordFilterPtr, blobFilterPtr, recordsSize] { + auto recordFilter = std::unique_ptr<ContentsFilter>(recordFilterPtr); + auto blobFilter = std::unique_ptr<ContentsFilter>(blobFilterPtr); + + for (auto& recordFilterKey : m_recordFilterHashesAddedDuringSynchronization) + recordFilter->add(recordFilterKey); + m_recordFilterHashesAddedDuringSynchronization.clear(); + + for (auto& hash : m_blobFilterHashesAddedDuringSynchronization) + blobFilter->add(hash); + m_blobFilterHashesAddedDuringSynchronization.clear(); + + m_recordFilter = WTFMove(recordFilter); + m_blobFilter = WTFMove(blobFilter); + m_approximateRecordsSize = recordsSize; + m_synchronizationInProgress = false; + }); + + m_blobStorage.synchronize(); + + deleteEmptyRecordsDirectories(recordsPath()); + + LOG(NetworkCacheStorage, "(NetworkProcess) cache synchronization completed size=%zu count=%u", recordsSize, count); + }); +} + +void Storage::addToRecordFilter(const Key& key) +{ + ASSERT(RunLoop::isMain()); + + if (m_recordFilter) + m_recordFilter->add(key.hash()); + + // If we get new entries during filter synchronization take care to add them to the new filter as well. + if (m_synchronizationInProgress) + m_recordFilterHashesAddedDuringSynchronization.append(key.hash()); +} + +bool Storage::mayContain(const Key& key) const +{ + ASSERT(RunLoop::isMain()); + return !m_recordFilter || m_recordFilter->mayContain(key.hash()); +} + +bool Storage::mayContainBlob(const Key& key) const +{ + ASSERT(RunLoop::isMain()); + return !m_blobFilter || m_blobFilter->mayContain(key.hash()); +} + +String Storage::recordDirectoryPathForKey(const Key& key) const +{ + ASSERT(!key.partition().isEmpty()); + ASSERT(!key.type().isEmpty()); + return WebCore::pathByAppendingComponent(WebCore::pathByAppendingComponent(recordsPath(), key.partition()), key.type()); +} + +String Storage::recordPathForKey(const Key& key) const +{ + return WebCore::pathByAppendingComponent(recordDirectoryPathForKey(key), key.hashAsString()); +} + +static String blobPathForRecordPath(const String& recordPath) +{ + return recordPath + blobSuffix; +} + +String Storage::blobPathForKey(const Key& key) const +{ + return blobPathForRecordPath(recordPathForKey(key)); +} + +struct RecordMetaData { + RecordMetaData() { } + explicit RecordMetaData(const Key& key) + : cacheStorageVersion(Storage::version) + , key(key) + { } + + unsigned cacheStorageVersion; + Key key; + // FIXME: Add encoder/decoder for time_point. + std::chrono::milliseconds epochRelativeTimeStamp; + SHA1::Digest headerHash; + uint64_t headerSize; + SHA1::Digest bodyHash; + uint64_t bodySize; + bool isBodyInline; + + // Not encoded as a field. Header starts immediately after meta data. + uint64_t headerOffset; +}; + +static bool decodeRecordMetaData(RecordMetaData& metaData, const Data& fileData) +{ + bool success = false; + fileData.apply([&metaData, &success](const uint8_t* data, size_t size) { + Decoder decoder(data, size); + if (!decoder.decode(metaData.cacheStorageVersion)) + return false; + if (!decoder.decode(metaData.key)) + return false; + if (!decoder.decode(metaData.epochRelativeTimeStamp)) + return false; + if (!decoder.decode(metaData.headerHash)) + return false; + if (!decoder.decode(metaData.headerSize)) + return false; + if (!decoder.decode(metaData.bodyHash)) + return false; + if (!decoder.decode(metaData.bodySize)) + return false; + if (!decoder.decode(metaData.isBodyInline)) + return false; + if (!decoder.verifyChecksum()) + return false; + metaData.headerOffset = decoder.currentOffset(); + success = true; + return false; + }); + return success; +} + +static bool decodeRecordHeader(const Data& fileData, RecordMetaData& metaData, Data& headerData) +{ + if (!decodeRecordMetaData(metaData, fileData)) { + LOG(NetworkCacheStorage, "(NetworkProcess) meta data decode failure"); + return false; + } + + if (metaData.cacheStorageVersion != Storage::version) { + LOG(NetworkCacheStorage, "(NetworkProcess) version mismatch"); + return false; + } + + headerData = fileData.subrange(metaData.headerOffset, metaData.headerSize); + if (metaData.headerHash != computeSHA1(headerData)) { + LOG(NetworkCacheStorage, "(NetworkProcess) header checksum mismatch"); + return false; + } + return true; +} + +void Storage::readRecord(ReadOperation& readOperation, const Data& recordData) +{ + ASSERT(!RunLoop::isMain()); + + RecordMetaData metaData; + Data headerData; + if (!decodeRecordHeader(recordData, metaData, headerData)) + return; + + if (metaData.key != readOperation.key) + return; + + // Sanity check against time stamps in future. + auto timeStamp = std::chrono::system_clock::time_point(metaData.epochRelativeTimeStamp); + if (timeStamp > std::chrono::system_clock::now()) + return; + + Data bodyData; + if (metaData.isBodyInline) { + size_t bodyOffset = metaData.headerOffset + headerData.size(); + if (bodyOffset + metaData.bodySize != recordData.size()) + return; + bodyData = recordData.subrange(bodyOffset, metaData.bodySize); + if (metaData.bodyHash != computeSHA1(bodyData)) + return; + } + + readOperation.expectedBodyHash = metaData.bodyHash; + readOperation.resultRecord = std::make_unique<Storage::Record>(Storage::Record { + metaData.key, + timeStamp, + headerData, + bodyData + }); +} + +static Data encodeRecordMetaData(const RecordMetaData& metaData) +{ + Encoder encoder; + + encoder << metaData.cacheStorageVersion; + encoder << metaData.key; + encoder << metaData.epochRelativeTimeStamp; + encoder << metaData.headerHash; + encoder << metaData.headerSize; + encoder << metaData.bodyHash; + encoder << metaData.bodySize; + encoder << metaData.isBodyInline; + + encoder.encodeChecksum(); + + return Data(encoder.buffer(), encoder.bufferSize()); +} + +Optional<BlobStorage::Blob> Storage::storeBodyAsBlob(WriteOperation& writeOperation) +{ + auto blobPath = blobPathForKey(writeOperation.record.key); + + // Store the body. + auto blob = m_blobStorage.add(blobPath, writeOperation.record.body); + if (blob.data.isNull()) + return { }; + + ++writeOperation.activeCount; + + RunLoop::main().dispatch([this, blob, &writeOperation] { + if (m_blobFilter) + m_blobFilter->add(writeOperation.record.key.hash()); + if (m_synchronizationInProgress) + m_blobFilterHashesAddedDuringSynchronization.append(writeOperation.record.key.hash()); + + if (writeOperation.mappedBodyHandler) + writeOperation.mappedBodyHandler(blob.data); + + finishWriteOperation(writeOperation); + }); + return blob; +} + +Data Storage::encodeRecord(const Record& record, Optional<BlobStorage::Blob> blob) +{ + ASSERT(!blob || bytesEqual(blob.value().data, record.body)); + + RecordMetaData metaData(record.key); + metaData.epochRelativeTimeStamp = std::chrono::duration_cast<std::chrono::milliseconds>(record.timeStamp.time_since_epoch()); + metaData.headerHash = computeSHA1(record.header); + metaData.headerSize = record.header.size(); + metaData.bodyHash = blob ? blob.value().hash : computeSHA1(record.body); + metaData.bodySize = record.body.size(); + metaData.isBodyInline = !blob; + + auto encodedMetaData = encodeRecordMetaData(metaData); + auto headerData = concatenate(encodedMetaData, record.header); + + if (metaData.isBodyInline) + return concatenate(headerData, record.body); + + return { headerData }; +} + +void Storage::removeFromPendingWriteOperations(const Key& key) +{ + while (true) { + auto found = m_pendingWriteOperations.findIf([&key](const std::unique_ptr<WriteOperation>& operation) { + return operation->record.key == key; + }); + + if (found == m_pendingWriteOperations.end()) + break; + + m_pendingWriteOperations.remove(found); + } +} + +void Storage::remove(const Key& key) +{ + ASSERT(RunLoop::isMain()); + + if (!mayContain(key)) + return; + + // We can't remove the key from the Bloom filter (but some false positives are expected anyway). + // For simplicity we also don't reduce m_approximateSize on removals. + // The next synchronization will update everything. + + removeFromPendingWriteOperations(key); + + serialBackgroundIOQueue().dispatch([this, key] { + WebCore::deleteFile(recordPathForKey(key)); + m_blobStorage.remove(blobPathForKey(key)); + }); +} + +void Storage::updateFileModificationTime(const String& path) +{ + StringCapture filePathCapture(path); + serialBackgroundIOQueue().dispatch([filePathCapture] { + updateFileModificationTimeIfNeeded(filePathCapture.string()); + }); +} + +void Storage::dispatchReadOperation(std::unique_ptr<ReadOperation> readOperationPtr) +{ + ASSERT(RunLoop::isMain()); + + auto& readOperation = *readOperationPtr; + m_activeReadOperations.add(WTFMove(readOperationPtr)); + + // I/O pressure may make disk operations slow. If they start taking very long time we rather go to network. + const auto readTimeout = 1500_ms; + m_readOperationTimeoutTimer.startOneShot(readTimeout); + + bool shouldGetBodyBlob = mayContainBlob(readOperation.key); + + ioQueue().dispatch([this, &readOperation, shouldGetBodyBlob] { + auto recordPath = recordPathForKey(readOperation.key); + + ++readOperation.activeCount; + if (shouldGetBodyBlob) + ++readOperation.activeCount; + + auto channel = IOChannel::open(recordPath, IOChannel::Type::Read); + channel->read(0, std::numeric_limits<size_t>::max(), &ioQueue(), [this, &readOperation](const Data& fileData, int error) { + if (!error) + readRecord(readOperation, fileData); + finishReadOperation(readOperation); + }); + + if (shouldGetBodyBlob) { + // Read the blob in parallel with the record read. + auto blobPath = blobPathForKey(readOperation.key); + readOperation.resultBodyBlob = m_blobStorage.get(blobPath); + finishReadOperation(readOperation); + } + }); +} + +void Storage::finishReadOperation(ReadOperation& readOperation) +{ + ASSERT(readOperation.activeCount); + // Record and blob reads must finish. + if (--readOperation.activeCount) + return; + + RunLoop::main().dispatch([this, &readOperation] { + bool success = readOperation.finish(); + if (success) + updateFileModificationTime(recordPathForKey(readOperation.key)); + else if (!readOperation.isCanceled) + remove(readOperation.key); + + ASSERT(m_activeReadOperations.contains(&readOperation)); + m_activeReadOperations.remove(&readOperation); + + if (m_activeReadOperations.isEmpty()) + m_readOperationTimeoutTimer.stop(); + + dispatchPendingReadOperations(); + + LOG(NetworkCacheStorage, "(NetworkProcess) read complete success=%d", success); + }); +} + +void Storage::cancelAllReadOperations() +{ + ASSERT(RunLoop::isMain()); + + for (auto& readOperation : m_activeReadOperations) + readOperation->cancel(); + + size_t pendingCount = 0; + for (int priority = maximumRetrievePriority; priority >= 0; --priority) { + auto& pendingRetrieveQueue = m_pendingReadOperationsByPriority[priority]; + pendingCount += pendingRetrieveQueue.size(); + for (auto it = pendingRetrieveQueue.rbegin(), end = pendingRetrieveQueue.rend(); it != end; ++it) + (*it)->cancel(); + pendingRetrieveQueue.clear(); + } + + LOG(NetworkCacheStorage, "(NetworkProcess) retrieve timeout, canceled %u active and %zu pending", m_activeReadOperations.size(), pendingCount); +} + +void Storage::dispatchPendingReadOperations() +{ + ASSERT(RunLoop::isMain()); + + const int maximumActiveReadOperationCount = 5; + + for (int priority = maximumRetrievePriority; priority >= 0; --priority) { + if (m_activeReadOperations.size() > maximumActiveReadOperationCount) { + LOG(NetworkCacheStorage, "(NetworkProcess) limiting parallel retrieves"); + return; + } + auto& pendingRetrieveQueue = m_pendingReadOperationsByPriority[priority]; + if (pendingRetrieveQueue.isEmpty()) + continue; + dispatchReadOperation(pendingRetrieveQueue.takeLast()); + } +} + +template <class T> bool retrieveFromMemory(const T& operations, const Key& key, Storage::RetrieveCompletionHandler& completionHandler) +{ + for (auto& operation : operations) { + if (operation->record.key == key) { + LOG(NetworkCacheStorage, "(NetworkProcess) found write operation in progress"); + auto record = operation->record; + RunLoop::main().dispatch([record, completionHandler] { + completionHandler(std::make_unique<Storage::Record>(record)); + }); + return true; + } + } + return false; +} + +void Storage::dispatchPendingWriteOperations() +{ + ASSERT(RunLoop::isMain()); + + const int maximumActiveWriteOperationCount { 1 }; + + while (!m_pendingWriteOperations.isEmpty()) { + if (m_activeWriteOperations.size() >= maximumActiveWriteOperationCount) { + LOG(NetworkCacheStorage, "(NetworkProcess) limiting parallel writes"); + return; + } + dispatchWriteOperation(m_pendingWriteOperations.takeLast()); + } +} + +static bool shouldStoreBodyAsBlob(const Data& bodyData) +{ + const size_t maximumInlineBodySize { 16 * 1024 }; + return bodyData.size() > maximumInlineBodySize; +} + +void Storage::dispatchWriteOperation(std::unique_ptr<WriteOperation> writeOperationPtr) +{ + ASSERT(RunLoop::isMain()); + + auto& writeOperation = *writeOperationPtr; + m_activeWriteOperations.add(WTFMove(writeOperationPtr)); + + // This was added already when starting the store but filter might have been wiped. + addToRecordFilter(writeOperation.record.key); + + backgroundIOQueue().dispatch([this, &writeOperation] { + auto recordDirectorPath = recordDirectoryPathForKey(writeOperation.record.key); + auto recordPath = recordPathForKey(writeOperation.record.key); + + WebCore::makeAllDirectories(recordDirectorPath); + + ++writeOperation.activeCount; + + bool shouldStoreAsBlob = shouldStoreBodyAsBlob(writeOperation.record.body); + auto blob = shouldStoreAsBlob ? storeBodyAsBlob(writeOperation) : Nullopt; + + auto recordData = encodeRecord(writeOperation.record, blob); + + auto channel = IOChannel::open(recordPath, IOChannel::Type::Create); + size_t recordSize = recordData.size(); + channel->write(0, recordData, nullptr, [this, &writeOperation, recordSize](int error) { + // On error the entry still stays in the contents filter until next synchronization. + m_approximateRecordsSize += recordSize; + finishWriteOperation(writeOperation); + + LOG(NetworkCacheStorage, "(NetworkProcess) write complete error=%d", error); + }); + }); +} + +void Storage::finishWriteOperation(WriteOperation& writeOperation) +{ + ASSERT(RunLoop::isMain()); + ASSERT(writeOperation.activeCount); + ASSERT(m_activeWriteOperations.contains(&writeOperation)); + + if (--writeOperation.activeCount) + return; + + m_activeWriteOperations.remove(&writeOperation); + dispatchPendingWriteOperations(); + + shrinkIfNeeded(); +} + +void Storage::retrieve(const Key& key, unsigned priority, RetrieveCompletionHandler&& completionHandler) +{ + ASSERT(RunLoop::isMain()); + ASSERT(priority <= maximumRetrievePriority); + ASSERT(!key.isNull()); + + if (!m_capacity) { + completionHandler(nullptr); + return; + } + + if (!mayContain(key)) { + completionHandler(nullptr); + return; + } + + if (retrieveFromMemory(m_pendingWriteOperations, key, completionHandler)) + return; + if (retrieveFromMemory(m_activeWriteOperations, key, completionHandler)) + return; + + auto readOperation = std::make_unique<ReadOperation>(key, WTFMove(completionHandler)); + m_pendingReadOperationsByPriority[priority].prepend(WTFMove(readOperation)); + dispatchPendingReadOperations(); +} + +void Storage::store(const Record& record, MappedBodyHandler&& mappedBodyHandler) +{ + ASSERT(RunLoop::isMain()); + ASSERT(!record.key.isNull()); + + if (!m_capacity) + return; + + auto writeOperation = std::make_unique<WriteOperation>(record, WTFMove(mappedBodyHandler)); + m_pendingWriteOperations.prepend(WTFMove(writeOperation)); + + // Add key to the filter already here as we do lookups from the pending operations too. + addToRecordFilter(record.key); + + bool isInitialWrite = m_pendingWriteOperations.size() == 1; + if (!isInitialWrite) + return; + + // Delay the start of writes a bit to avoid affecting early page load. + // Completing writes will dispatch more writes without delay. + static const auto initialWriteDelay = 1_s; + m_writeOperationDispatchTimer.startOneShot(initialWriteDelay); +} + +void Storage::traverse(const String& type, TraverseFlags flags, TraverseHandler&& traverseHandler) +{ + ASSERT(RunLoop::isMain()); + ASSERT(traverseHandler); + // Avoid non-thread safe std::function copies. + + auto traverseOperationPtr = std::make_unique<TraverseOperation>(type, flags, WTFMove(traverseHandler)); + auto& traverseOperation = *traverseOperationPtr; + m_activeTraverseOperations.add(WTFMove(traverseOperationPtr)); + + ioQueue().dispatch([this, &traverseOperation] { + traverseRecordsFiles(recordsPath(), traverseOperation.type, [this, &traverseOperation](const String& fileName, const String& hashString, const String& type, bool isBlob, const String& recordDirectoryPath) { + ASSERT(type == traverseOperation.type); + if (isBlob) + return; + + auto recordPath = WebCore::pathByAppendingComponent(recordDirectoryPath, fileName); + + double worth = -1; + if (traverseOperation.flags & TraverseFlag::ComputeWorth) + worth = computeRecordWorth(fileTimes(recordPath)); + unsigned bodyShareCount = 0; + if (traverseOperation.flags & TraverseFlag::ShareCount) + bodyShareCount = m_blobStorage.shareCount(blobPathForRecordPath(recordPath)); + + std::unique_lock<Lock> lock(traverseOperation.activeMutex); + ++traverseOperation.activeCount; + + auto channel = IOChannel::open(recordPath, IOChannel::Type::Read); + channel->read(0, std::numeric_limits<size_t>::max(), nullptr, [this, &traverseOperation, worth, bodyShareCount](Data& fileData, int) { + RecordMetaData metaData; + Data headerData; + if (decodeRecordHeader(fileData, metaData, headerData)) { + Record record { + metaData.key, + std::chrono::system_clock::time_point(metaData.epochRelativeTimeStamp), + headerData, + { } + }; + RecordInfo info { + static_cast<size_t>(metaData.bodySize), + worth, + bodyShareCount, + String::fromUTF8(SHA1::hexDigest(metaData.bodyHash)) + }; + traverseOperation.handler(&record, info); + } + + std::lock_guard<Lock> lock(traverseOperation.activeMutex); + --traverseOperation.activeCount; + traverseOperation.activeCondition.notifyOne(); + }); + + const unsigned maximumParallelReadCount = 5; + traverseOperation.activeCondition.wait(lock, [&traverseOperation] { + return traverseOperation.activeCount <= maximumParallelReadCount; + }); + }); + // Wait for all reads to finish. + std::unique_lock<Lock> lock(traverseOperation.activeMutex); + traverseOperation.activeCondition.wait(lock, [&traverseOperation] { + return !traverseOperation.activeCount; + }); + RunLoop::main().dispatch([this, &traverseOperation] { + traverseOperation.handler(nullptr, { }); + m_activeTraverseOperations.remove(&traverseOperation); + }); + }); +} + +void Storage::setCapacity(size_t capacity) +{ + ASSERT(RunLoop::isMain()); + +#if !ASSERT_DISABLED + const size_t assumedAverageRecordSize = 50 << 10; + size_t maximumRecordCount = capacity / assumedAverageRecordSize; + // ~10 bits per element are required for <1% false positive rate. + size_t effectiveBloomFilterCapacity = ContentsFilter::tableSize / 10; + // If this gets hit it might be time to increase the filter size. + ASSERT(maximumRecordCount < effectiveBloomFilterCapacity); +#endif + + m_capacity = capacity; + + shrinkIfNeeded(); +} + +void Storage::clear(const String& type, std::chrono::system_clock::time_point modifiedSinceTime, std::function<void ()>&& completionHandler) +{ + ASSERT(RunLoop::isMain()); + LOG(NetworkCacheStorage, "(NetworkProcess) clearing cache"); + + if (m_recordFilter) + m_recordFilter->clear(); + if (m_blobFilter) + m_blobFilter->clear(); + m_approximateRecordsSize = 0; + + // Avoid non-thread safe std::function copies. + auto* completionHandlerPtr = completionHandler ? new std::function<void ()>(WTFMove(completionHandler)) : nullptr; + StringCapture typeCapture(type); + ioQueue().dispatch([this, modifiedSinceTime, completionHandlerPtr, typeCapture] { + auto recordsPath = this->recordsPath(); + traverseRecordsFiles(recordsPath, typeCapture.string(), [modifiedSinceTime](const String& fileName, const String& hashString, const String& type, bool isBlob, const String& recordDirectoryPath) { + auto filePath = WebCore::pathByAppendingComponent(recordDirectoryPath, fileName); + if (modifiedSinceTime > std::chrono::system_clock::time_point::min()) { + auto times = fileTimes(filePath); + if (times.modification < modifiedSinceTime) + return; + } + WebCore::deleteFile(filePath); + }); + + deleteEmptyRecordsDirectories(recordsPath); + + // This cleans unreferenced blobs. + m_blobStorage.synchronize(); + + if (completionHandlerPtr) { + RunLoop::main().dispatch([completionHandlerPtr] { + (*completionHandlerPtr)(); + delete completionHandlerPtr; + }); + } + }); +} + +static double computeRecordWorth(FileTimes times) +{ + using namespace std::chrono; + auto age = system_clock::now() - times.creation; + // File modification time is updated manually on cache read. We don't use access time since OS may update it automatically. + auto accessAge = times.modification - times.creation; + + // For sanity. + if (age <= 0_s || accessAge < 0_s || accessAge > age) + return 0; + + // We like old entries that have been accessed recently. + return duration<double>(accessAge) / age; +} + +static double deletionProbability(FileTimes times, unsigned bodyShareCount) +{ + static const double maximumProbability { 0.33 }; + static const unsigned maximumEffectiveShareCount { 5 }; + + auto worth = computeRecordWorth(times); + + // Adjust a bit so the most valuable entries don't get deleted at all. + auto effectiveWorth = std::min(1.1 * worth, 1.); + + auto probability = (1 - effectiveWorth) * maximumProbability; + + // It is less useful to remove an entry that shares its body data. + if (bodyShareCount) + probability /= std::min(bodyShareCount, maximumEffectiveShareCount); + + return probability; +} + +void Storage::shrinkIfNeeded() +{ + ASSERT(RunLoop::isMain()); + + if (approximateSize() > m_capacity) + shrink(); +} + +void Storage::shrink() +{ + ASSERT(RunLoop::isMain()); + + if (m_shrinkInProgress || m_synchronizationInProgress) + return; + m_shrinkInProgress = true; + + LOG(NetworkCacheStorage, "(NetworkProcess) shrinking cache approximateSize=%zu capacity=%zu", approximateSize(), m_capacity); + + backgroundIOQueue().dispatch([this] { + auto recordsPath = this->recordsPath(); + String anyType; + traverseRecordsFiles(recordsPath, anyType, [this](const String& fileName, const String& hashString, const String& type, bool isBlob, const String& recordDirectoryPath) { + if (isBlob) + return; + + auto recordPath = WebCore::pathByAppendingComponent(recordDirectoryPath, fileName); + auto blobPath = blobPathForRecordPath(recordPath); + + auto times = fileTimes(recordPath); + unsigned bodyShareCount = m_blobStorage.shareCount(blobPath); + auto probability = deletionProbability(times, bodyShareCount); + + bool shouldDelete = randomNumber() < probability; + + LOG(NetworkCacheStorage, "Deletion probability=%f bodyLinkCount=%d shouldDelete=%d", probability, bodyShareCount, shouldDelete); + + if (shouldDelete) { + WebCore::deleteFile(recordPath); + m_blobStorage.remove(blobPath); + } + }); + + RunLoop::main().dispatch([this] { + m_shrinkInProgress = false; + // We could synchronize during the shrink traversal. However this is fast and it is better to have just one code path. + synchronize(); + }); + + LOG(NetworkCacheStorage, "(NetworkProcess) cache shrink completed"); + }); +} + +void Storage::deleteOldVersions() +{ + backgroundIOQueue().dispatch([this] { + auto cachePath = basePath(); + traverseDirectory(cachePath, [&cachePath](const String& subdirName, DirectoryEntryType type) { + if (type != DirectoryEntryType::Directory) + return; + if (!subdirName.startsWith(versionDirectoryPrefix)) + return; + auto versionString = subdirName.substring(strlen(versionDirectoryPrefix)); + bool success; + unsigned directoryVersion = versionString.toUIntStrict(&success); + if (!success) + return; + if (directoryVersion >= version) + return; + +#if PLATFORM(MAC) + // Allow the last stable version of the cache to co-exist with the latest development one on Mac. + const unsigned lastStableVersion = 4; + if (directoryVersion == lastStableVersion) + return; +#endif + + auto oldVersionPath = WebCore::pathByAppendingComponent(cachePath, subdirName); + LOG(NetworkCacheStorage, "(NetworkProcess) deleting old cache version, path %s", oldVersionPath.utf8().data()); + + deleteDirectoryRecursively(oldVersionPath); + }); + }); +} + +} +} + +#endif diff --git a/Source/WebKit2/NetworkProcess/cache/NetworkCacheStorage.h b/Source/WebKit2/NetworkProcess/cache/NetworkCacheStorage.h new file mode 100644 index 000000000..4762a9cbf --- /dev/null +++ b/Source/WebKit2/NetworkProcess/cache/NetworkCacheStorage.h @@ -0,0 +1,178 @@ +/* + * Copyright (C) 2014-2015 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. + */ + +#ifndef NetworkCacheStorage_h +#define NetworkCacheStorage_h + +#if ENABLE(NETWORK_CACHE) + +#include "NetworkCacheBlobStorage.h" +#include "NetworkCacheData.h" +#include "NetworkCacheKey.h" +#include <WebCore/Timer.h> +#include <wtf/BloomFilter.h> +#include <wtf/Deque.h> +#include <wtf/HashSet.h> +#include <wtf/Optional.h> +#include <wtf/WorkQueue.h> +#include <wtf/text/WTFString.h> + +namespace WebKit { +namespace NetworkCache { + +class IOChannel; + +class Storage { + WTF_MAKE_NONCOPYABLE(Storage); +public: + static std::unique_ptr<Storage> open(const String& cachePath); + + struct Record { + WTF_MAKE_FAST_ALLOCATED; + public: + Key key; + std::chrono::system_clock::time_point timeStamp; + Data header; + Data body; + }; + // This may call completion handler synchronously on failure. + typedef std::function<bool (std::unique_ptr<Record>)> RetrieveCompletionHandler; + void retrieve(const Key&, unsigned priority, RetrieveCompletionHandler&&); + + typedef std::function<void (const Data& mappedBody)> MappedBodyHandler; + void store(const Record&, MappedBodyHandler&&); + + void remove(const Key&); + void clear(const String& type, std::chrono::system_clock::time_point modifiedSinceTime, std::function<void ()>&& completionHandler); + + struct RecordInfo { + size_t bodySize; + double worth; // 0-1 where 1 is the most valuable. + unsigned bodyShareCount; + String bodyHash; + }; + enum TraverseFlag { + ComputeWorth = 1 << 0, + ShareCount = 1 << 1, + }; + typedef unsigned TraverseFlags; + typedef std::function<void (const Record*, const RecordInfo&)> TraverseHandler; + // Null record signals end. + void traverse(const String& type, TraverseFlags, TraverseHandler&&); + + void setCapacity(size_t); + size_t capacity() const { return m_capacity; } + size_t approximateSize() const; + + static const unsigned version = 5; + + String basePath() const; + String versionPath() const; + String recordsPath() const; + + ~Storage(); + +private: + Storage(const String& directoryPath); + + String recordDirectoryPathForKey(const Key&) const; + String recordPathForKey(const Key&) const; + String blobPathForKey(const Key&) const; + + void synchronize(); + void deleteOldVersions(); + void shrinkIfNeeded(); + void shrink(); + + struct ReadOperation; + void dispatchReadOperation(std::unique_ptr<ReadOperation>); + void dispatchPendingReadOperations(); + void finishReadOperation(ReadOperation&); + void cancelAllReadOperations(); + + struct WriteOperation; + void dispatchWriteOperation(std::unique_ptr<WriteOperation>); + void dispatchPendingWriteOperations(); + void finishWriteOperation(WriteOperation&); + + Optional<BlobStorage::Blob> storeBodyAsBlob(WriteOperation&); + Data encodeRecord(const Record&, Optional<BlobStorage::Blob>); + void readRecord(ReadOperation&, const Data&); + + void updateFileModificationTime(const String& path); + void removeFromPendingWriteOperations(const Key&); + + WorkQueue& ioQueue() { return m_ioQueue.get(); } + WorkQueue& backgroundIOQueue() { return m_backgroundIOQueue.get(); } + WorkQueue& serialBackgroundIOQueue() { return m_serialBackgroundIOQueue.get(); } + + bool mayContain(const Key&) const; + bool mayContainBlob(const Key&) const; + + void addToRecordFilter(const Key&); + + const String m_basePath; + const String m_recordsPath; + + size_t m_capacity { std::numeric_limits<size_t>::max() }; + size_t m_approximateRecordsSize { 0 }; + + // 2^18 bit filter can support up to 26000 entries with false positive rate < 1%. + using ContentsFilter = BloomFilter<18>; + std::unique_ptr<ContentsFilter> m_recordFilter; + std::unique_ptr<ContentsFilter> m_blobFilter; + + bool m_synchronizationInProgress { false }; + bool m_shrinkInProgress { false }; + + Vector<Key::HashType> m_recordFilterHashesAddedDuringSynchronization; + Vector<Key::HashType> m_blobFilterHashesAddedDuringSynchronization; + + static const int maximumRetrievePriority = 4; + Deque<std::unique_ptr<ReadOperation>> m_pendingReadOperationsByPriority[maximumRetrievePriority + 1]; + HashSet<std::unique_ptr<ReadOperation>> m_activeReadOperations; + WebCore::Timer m_readOperationTimeoutTimer; + + Deque<std::unique_ptr<WriteOperation>> m_pendingWriteOperations; + HashSet<std::unique_ptr<WriteOperation>> m_activeWriteOperations; + WebCore::Timer m_writeOperationDispatchTimer; + + struct TraverseOperation; + HashSet<std::unique_ptr<TraverseOperation>> m_activeTraverseOperations; + + Ref<WorkQueue> m_ioQueue; + Ref<WorkQueue> m_backgroundIOQueue; + Ref<WorkQueue> m_serialBackgroundIOQueue; + + BlobStorage m_blobStorage; +}; + +// FIXME: Remove, used by NetworkCacheStatistics only. +void traverseRecordsFiles(const String& recordsPath, const String& type, const std::function<void (const String& fileName, const String& hashString, const String& type, bool isBodyBlob, const String& recordDirectoryPath)>&); + +} +} +#endif +#endif diff --git a/Source/WebKit2/NetworkProcess/cache/NetworkCacheSubresourcesEntry.cpp b/Source/WebKit2/NetworkProcess/cache/NetworkCacheSubresourcesEntry.cpp new file mode 100644 index 000000000..e762c3b33 --- /dev/null +++ b/Source/WebKit2/NetworkProcess/cache/NetworkCacheSubresourcesEntry.cpp @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2015 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" + +#if ENABLE(NETWORK_CACHE_SPECULATIVE_REVALIDATION) +#include "NetworkCacheSubresourcesEntry.h" + +#include "Logging.h" +#include "NetworkCacheCoders.h" +#include "NetworkCacheDecoder.h" +#include "NetworkCacheEncoder.h" + +namespace WebKit { +namespace NetworkCache { + +Storage::Record SubresourcesEntry::encodeAsStorageRecord() const +{ + Encoder encoder; + encoder << m_subresources; + + encoder.encodeChecksum(); + + return { m_key, m_timeStamp, { encoder.buffer(), encoder.bufferSize() } , { } }; +} + +std::unique_ptr<SubresourcesEntry> SubresourcesEntry::decodeStorageRecord(const Storage::Record& storageEntry) +{ + auto entry = std::make_unique<SubresourcesEntry>(storageEntry); + + Decoder decoder(storageEntry.header.data(), storageEntry.header.size()); + if (!decoder.decode(entry->m_subresources)) + return nullptr; + + if (!decoder.verifyChecksum()) { + LOG(NetworkCache, "(NetworkProcess) checksum verification failure\n"); + return nullptr; + } + + return entry; +} + +SubresourcesEntry::SubresourcesEntry(const Storage::Record& storageEntry) + : m_key(storageEntry.key) + , m_timeStamp(storageEntry.timeStamp) +{ + ASSERT(m_key.type() == "subresources"); +} + +SubresourcesEntry::SubresourcesEntry(Key&& key, const Vector<Key>& subresourceKeys) + : m_key(WTFMove(key)) + , m_timeStamp(std::chrono::system_clock::now()) +{ + ASSERT(m_key.type() == "subresources"); + for (auto& key : subresourceKeys) + m_subresources.add(key, SubresourceInfo()); +} + +void SubresourcesEntry::updateSubresourceKeys(const Vector<Key>& subresourceKeys) +{ + auto oldSubresources = WTFMove(m_subresources); + + // Mark keys that are common with last load as non-Transient. + for (auto& key : subresourceKeys) { + bool isTransient = !oldSubresources.contains(key); + m_subresources.add(key, SubresourceInfo(isTransient)); + } +} + +} // namespace WebKit +} // namespace NetworkCache + +#endif // ENABLE(NETWORK_CACHE_SPECULATIVE_REVALIDATION) diff --git a/Source/WebKit2/NetworkProcess/cache/NetworkCacheSubresourcesEntry.h b/Source/WebKit2/NetworkProcess/cache/NetworkCacheSubresourcesEntry.h new file mode 100644 index 000000000..d12282ffa --- /dev/null +++ b/Source/WebKit2/NetworkProcess/cache/NetworkCacheSubresourcesEntry.h @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2015 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. + */ + +#ifndef NetworkCacheSubresourcesEntry_h +#define NetworkCacheSubresourcesEntry_h + +#if ENABLE(NETWORK_CACHE_SPECULATIVE_REVALIDATION) + +#include "NetworkCacheDecoder.h" +#include "NetworkCacheEncoder.h" +#include "NetworkCacheStorage.h" +#include <wtf/HashMap.h> + +namespace WebKit { +namespace NetworkCache { + +class SubresourcesEntry { + WTF_MAKE_NONCOPYABLE(SubresourcesEntry); WTF_MAKE_FAST_ALLOCATED; +public: + struct SubresourceInfo { + void encode(Encoder& encoder) const { encoder << isTransient; } + static bool decode(Decoder& decoder, SubresourceInfo& info) { return decoder.decode(info.isTransient); } + + SubresourceInfo() = default; + SubresourceInfo(bool isTransient) : isTransient(isTransient) { } + + bool isTransient { false }; + }; + SubresourcesEntry(Key&&, const Vector<Key>& subresourceKeys); + explicit SubresourcesEntry(const Storage::Record&); + + Storage::Record encodeAsStorageRecord() const; + static std::unique_ptr<SubresourcesEntry> decodeStorageRecord(const Storage::Record&); + + const Key& key() const { return m_key; } + std::chrono::system_clock::time_point timeStamp() const { return m_timeStamp; } + const HashMap<Key, SubresourceInfo>& subresources() const { return m_subresources; } + + void updateSubresourceKeys(const Vector<Key>&); + +private: + Key m_key; + std::chrono::system_clock::time_point m_timeStamp; + HashMap<Key, SubresourceInfo> m_subresources; +}; + +} // namespace WebKit +} // namespace NetworkCache + +#endif // ENABLE(NETWORK_CACHE_SPECULATIVE_REVALIDATION) +#endif // NetworkCacheSubresourcesEntry_h diff --git a/Source/WebKit2/NetworkProcess/mac/DiskCacheMonitor.mm b/Source/WebKit2/NetworkProcess/mac/DiskCacheMonitor.mm deleted file mode 100644 index 34c81e61d..000000000 --- a/Source/WebKit2/NetworkProcess/mac/DiskCacheMonitor.mm +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright (C) 2013 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. - */ - -#import "config.h" -#import "DiskCacheMonitor.h" - -#import "NetworkConnectionToWebProcess.h" -#import "NetworkProcessConnectionMessages.h" -#import "NetworkResourceLoader.h" -#import "WebCoreArgumentCoders.h" - -#ifdef __has_include -#if __has_include(<CFNetwork/CFURLCachePriv.h>) -#include <CFNetwork/CFURLCachePriv.h> -#endif -#endif - -#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1090 - -typedef void (^CFCachedURLResponseCallBackBlock)(CFCachedURLResponseRef); -extern "C" void _CFCachedURLResponseSetBecameFileBackedCallBackBlock(CFCachedURLResponseRef, CFCachedURLResponseCallBackBlock, dispatch_queue_t); - -using namespace WebCore; - -namespace WebKit { - -// The maximum number of seconds we'll try to wait for a resource to be disk cached before we forget the request. -static const double diskCacheMonitorTimeout = 20; - -void DiskCacheMonitor::monitorFileBackingStoreCreation(CFCachedURLResponseRef cachedResponse, NetworkResourceLoader* loader) -{ - if (!cachedResponse) - return; - - ASSERT(loader); - - new DiskCacheMonitor(cachedResponse, loader); // Balanced by adoptPtr in the blocks setup in the constructor, one of which is guaranteed to run. -} - -DiskCacheMonitor::DiskCacheMonitor(CFCachedURLResponseRef cachedResponse, NetworkResourceLoader* loader) - : m_connectionToWebProcess(loader->connectionToWebProcess()) - , m_resourceRequest(loader->request()) -{ - ASSERT(isMainThread()); - - // Set up a delayed callback to cancel this monitor if the resource hasn't been cached yet. - __block DiskCacheMonitor* rawMonitor = this; - - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC * diskCacheMonitorTimeout), dispatch_get_main_queue(), ^{ - adoptPtr(rawMonitor); // Balanced by `new DiskCacheMonitor` in monitorFileBackingStoreCreation. - rawMonitor = 0; - }); - - // Set up the disk caching callback to create the ShareableResource and send it to the WebProcess. - CFCachedURLResponseCallBackBlock block = ^(CFCachedURLResponseRef cachedResponse) - { - // If the monitor isn't there then it timed out before this resource was cached to disk. - if (!rawMonitor) - return; - - OwnPtr<DiskCacheMonitor> monitor = adoptPtr(rawMonitor); // Balanced by `new DiskCacheMonitor` in monitorFileBackingStoreCreation. - rawMonitor = 0; - - ShareableResource::Handle handle; - NetworkResourceLoader::tryGetShareableHandleFromCFURLCachedResponse(handle, cachedResponse); - if (handle.isNull()) - return; - - monitor->send(Messages::NetworkProcessConnection::DidCacheResource(monitor->resourceRequest(), handle)); - }; - - _CFCachedURLResponseSetBecameFileBackedCallBackBlock(cachedResponse, block, dispatch_get_main_queue()); -} - -CoreIPC::Connection* DiskCacheMonitor::messageSenderConnection() -{ - return m_connectionToWebProcess->connection(); -} - -uint64_t DiskCacheMonitor::messageSenderDestinationID() -{ - return 0; -} - -} // namespace WebKit - -#endif // #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1090 diff --git a/Source/WebKit2/NetworkProcess/mac/NetworkProcessMac.mm b/Source/WebKit2/NetworkProcess/mac/NetworkProcessMac.mm deleted file mode 100644 index 3e96fa4dc..000000000 --- a/Source/WebKit2/NetworkProcess/mac/NetworkProcessMac.mm +++ /dev/null @@ -1,231 +0,0 @@ -/* - * Copyright (C) 2012 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. - */ - -#import "config.h" -#import "NetworkProcess.h" - -#if ENABLE(NETWORK_PROCESS) - -#import "NetworkProcessCreationParameters.h" -#import "NetworkResourceLoader.h" -#import "PlatformCertificateInfo.h" -#import "ResourceCachesToClear.h" -#import "SandboxExtension.h" -#import "SandboxInitializationParameters.h" -#import "StringUtilities.h" -#import <WebCore/FileSystem.h> -#import <WebCore/LocalizedStrings.h> -#import <WebKitSystemInterface.h> -#import <mach/host_info.h> -#import <mach/mach.h> -#import <mach/mach_error.h> -#import <sysexits.h> -#import <wtf/text/WTFString.h> - -#if USE(SECURITY_FRAMEWORK) -#import "SecItemShim.h" -#endif - -#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1090 -typedef struct _CFURLCache* CFURLCacheRef; -extern "C" CFURLCacheRef CFURLCacheCopySharedURLCache(); -extern "C" void _CFURLCacheSetMinSizeForVMCachedResource(CFURLCacheRef, CFIndex); -#endif - -using namespace WebCore; - -@interface NSURLRequest (Details) -+ (void)setAllowsSpecificHTTPSCertificate:(NSArray *)allow forHost:(NSString *)host; -@end - -namespace WebKit { - -void NetworkProcess::initializeProcess(const ChildProcessInitializationParameters&) -{ - // Having a window server connection in this process would result in spin logs (<rdar://problem/13239119>). - setApplicationIsDaemon(); -} - -void NetworkProcess::initializeProcessName(const ChildProcessInitializationParameters& parameters) -{ - NSString *applicationName = [NSString stringWithFormat:WEB_UI_STRING("%@ Networking", "visible name of the network process. The argument is the application name."), (NSString *)parameters.uiProcessName]; - WKSetVisibleApplicationName((CFStringRef)applicationName); -} - -#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1070 -static void overrideSystemProxies(const String& httpProxy, const String& httpsProxy) -{ - NSMutableDictionary *proxySettings = [NSMutableDictionary dictionary]; - - if (!httpProxy.isNull()) { - KURL httpProxyURL(KURL(), httpProxy); - if (httpProxyURL.isValid()) { - [proxySettings setObject:nsStringFromWebCoreString(httpProxyURL.host()) forKey:(NSString *)kCFNetworkProxiesHTTPProxy]; - if (httpProxyURL.hasPort()) { - NSNumber *port = [NSNumber numberWithInt:httpProxyURL.port()]; - [proxySettings setObject:port forKey:(NSString *)kCFNetworkProxiesHTTPPort]; - } - } - else - NSLog(@"Malformed HTTP Proxy URL '%s'. Expected 'http://<hostname>[:<port>]'\n", httpProxy.utf8().data()); - } - - if (!httpsProxy.isNull()) { - KURL httpsProxyURL(KURL(), httpsProxy); - if (httpsProxyURL.isValid()) { - [proxySettings setObject:nsStringFromWebCoreString(httpsProxyURL.host()) forKey:(NSString *)kCFNetworkProxiesHTTPSProxy]; - if (httpsProxyURL.hasPort()) { - NSNumber *port = [NSNumber numberWithInt:httpsProxyURL.port()]; - [proxySettings setObject:port forKey:(NSString *)kCFNetworkProxiesHTTPSPort]; - } - } else - NSLog(@"Malformed HTTPS Proxy URL '%s'. Expected 'https://<hostname>[:<port>]'\n", httpsProxy.utf8().data()); - } - - if ([proxySettings count] > 0) - WKCFNetworkSetOverrideSystemProxySettings((CFDictionaryRef)proxySettings); -} -#endif // __MAC_OS_X_VERSION_MIN_REQUIRED >= 1070 - -void NetworkProcess::platformInitializeNetworkProcess(const NetworkProcessCreationParameters& parameters) -{ - m_diskCacheDirectory = parameters.diskCacheDirectory; - - if (!m_diskCacheDirectory.isNull()) { - SandboxExtension::consumePermanently(parameters.diskCacheDirectoryExtensionHandle); - [NSURLCache setSharedURLCache:adoptNS([[NSURLCache alloc] - initWithMemoryCapacity:parameters.nsURLCacheMemoryCapacity - diskCapacity:parameters.nsURLCacheDiskCapacity - diskPath:parameters.diskCacheDirectory]).get()]; - } - -#if USE(SECURITY_FRAMEWORK) - SecItemShim::shared().initialize(this); -#endif - -#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1070 - if (!parameters.httpProxy.isNull() || !parameters.httpsProxy.isNull()) - overrideSystemProxies(parameters.httpProxy, parameters.httpsProxy); -#endif - -#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1090 - RetainPtr<CFURLCacheRef> cache = adoptCF(CFURLCacheCopySharedURLCache()); - if (!cache) - return; - - _CFURLCacheSetMinSizeForVMCachedResource(cache.get(), NetworkResourceLoader::fileBackedResourceMinimumSize()); -#endif -} - -static uint64_t memorySize() -{ - static host_basic_info_data_t hostInfo; - - static dispatch_once_t once; - dispatch_once(&once, ^() { - mach_port_t host = mach_host_self(); - mach_msg_type_number_t count = HOST_BASIC_INFO_COUNT; - kern_return_t r = host_info(host, HOST_BASIC_INFO, (host_info_t)&hostInfo, &count); - mach_port_deallocate(mach_task_self(), host); - - if (r != KERN_SUCCESS) - LOG_ERROR("%s : host_info(%d) : %s.\n", __FUNCTION__, r, mach_error_string(r)); - }); - - return hostInfo.max_mem; -} - -static uint64_t volumeFreeSize(const String& path) -{ - NSDictionary *fileSystemAttributesDictionary = [[NSFileManager defaultManager] attributesOfFileSystemForPath:(NSString *)path error:NULL]; - return [[fileSystemAttributesDictionary objectForKey:NSFileSystemFreeSize] unsignedLongLongValue]; -} - -void NetworkProcess::platformSetCacheModel(CacheModel cacheModel) -{ - - // As a fudge factor, use 1000 instead of 1024, in case the reported byte - // count doesn't align exactly to a megabyte boundary. - uint64_t memSize = memorySize() / 1024 / 1000; - uint64_t diskFreeSize = volumeFreeSize(m_diskCacheDirectory) / 1024 / 1000; - - unsigned cacheTotalCapacity = 0; - unsigned cacheMinDeadCapacity = 0; - unsigned cacheMaxDeadCapacity = 0; - double deadDecodedDataDeletionInterval = 0; - unsigned pageCacheCapacity = 0; - unsigned long urlCacheMemoryCapacity = 0; - unsigned long urlCacheDiskCapacity = 0; - - calculateCacheSizes(cacheModel, memSize, diskFreeSize, - cacheTotalCapacity, cacheMinDeadCapacity, cacheMaxDeadCapacity, deadDecodedDataDeletionInterval, - pageCacheCapacity, urlCacheMemoryCapacity, urlCacheDiskCapacity); - - - NSURLCache *nsurlCache = [NSURLCache sharedURLCache]; - [nsurlCache setMemoryCapacity:urlCacheMemoryCapacity]; - [nsurlCache setDiskCapacity:std::max<unsigned long>(urlCacheDiskCapacity, [nsurlCache diskCapacity])]; // Don't shrink a big disk cache, since that would cause churn. -} - -void NetworkProcess::allowSpecificHTTPSCertificateForHost(const PlatformCertificateInfo& certificateInfo, const String& host) -{ - [NSURLRequest setAllowsSpecificHTTPSCertificate:(NSArray *)certificateInfo.certificateChain() forHost:(NSString *)host]; -} - -void NetworkProcess::initializeSandbox(const ChildProcessInitializationParameters& parameters, SandboxInitializationParameters& sandboxParameters) -{ - // Need to overide the default, because service has a different bundle ID. - NSBundle *webkit2Bundle = [NSBundle bundleForClass:NSClassFromString(@"WKView")]; - sandboxParameters.setOverrideSandboxProfilePath([webkit2Bundle pathForResource:@"com.apple.WebKit.NetworkProcess" ofType:@"sb"]); - - ChildProcess::initializeSandbox(parameters, sandboxParameters); -} - -void NetworkProcess::clearCacheForAllOrigins(uint32_t cachesToClear) -{ - ResourceCachesToClear resourceCachesToClear = static_cast<ResourceCachesToClear>(cachesToClear); - if (resourceCachesToClear == InMemoryResourceCachesOnly) - return; - - if (!m_clearCacheDispatchGroup) - m_clearCacheDispatchGroup = dispatch_group_create(); - - dispatch_group_async(m_clearCacheDispatchGroup, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - [[NSURLCache sharedURLCache] removeAllCachedResponses]; - }); -} - -void NetworkProcess::platformTerminate() -{ - if (m_clearCacheDispatchGroup) { - dispatch_group_wait(m_clearCacheDispatchGroup, DISPATCH_TIME_FOREVER); - dispatch_release(m_clearCacheDispatchGroup); - m_clearCacheDispatchGroup = 0; - } -} - -} // namespace WebKit - -#endif // ENABLE(NETWORK_PROCESS) diff --git a/Source/WebKit2/NetworkProcess/mac/NetworkResourceLoadSchedulerMac.mm b/Source/WebKit2/NetworkProcess/mac/NetworkResourceLoadSchedulerMac.mm deleted file mode 100644 index 15f21c47b..000000000 --- a/Source/WebKit2/NetworkProcess/mac/NetworkResourceLoadSchedulerMac.mm +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (C) 2012 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. - */ - -#import "config.h" -#import "NetworkResourceLoadScheduler.h" - -#import <WebCore/ResourceRequest.h> -#import <WebCore/ResourceRequestCFNet.h> -#import <WebCore/WebCoreSystemInterface.h> -#import <WebKitSystemInterface.h> - -#if ENABLE(NETWORK_PROCESS) - -using namespace WebCore; - -namespace WebKit { - -void NetworkResourceLoadScheduler::platformInitializeMaximumHTTPConnectionCountPerHost() -{ - wkInitializeMaximumHTTPConnectionCountPerHost = WKInitializeMaximumHTTPConnectionCountPerHost; - wkSetHTTPPipeliningMaximumPriority = WKSetHTTPPipeliningMaximumPriority; - wkSetHTTPPipeliningMinimumFastLanePriority = WKSetHTTPPipeliningMinimumFastLanePriority; - - // Our preferred connection-per-host limit is the standard 6, but we need to let CFNetwork handle a 7th - // in case a synchronous XHRs is made while 6 loads are already outstanding. - static const unsigned preferredConnectionCount = 7; - static const unsigned unlimitedConnectionCount = 10000; - - // Always set the connection count per host, even when pipelining. - unsigned maximumHTTPConnectionCountPerHost = wkInitializeMaximumHTTPConnectionCountPerHost(preferredConnectionCount); - - Boolean keyExistsAndHasValidFormat = false; - Boolean prefValue = CFPreferencesGetAppBooleanValue(CFSTR("WebKitEnableHTTPPipelining"), kCFPreferencesCurrentApplication, &keyExistsAndHasValidFormat); - - if (keyExistsAndHasValidFormat) - ResourceRequest::setHTTPPipeliningEnabled(prefValue); - - if (ResourceRequest::httpPipeliningEnabled()) { - wkSetHTTPPipeliningMaximumPriority(toHTTPPipeliningPriority(ResourceLoadPriorityHighest)); - wkSetHTTPPipeliningMinimumFastLanePriority(toHTTPPipeliningPriority(ResourceLoadPriorityMedium)); - - // When pipelining do not rate-limit requests sent from WebCore since CFNetwork handles that. - m_maxRequestsInFlightPerHost = unlimitedConnectionCount; - - return; - } - - // We've asked for one more connection per host than we intend to use in most cases so synch XHRs can bypass that limit. - m_maxRequestsInFlightPerHost = maximumHTTPConnectionCountPerHost - 1; -} - -} // namespace WebKit - -#endif // ENABLE(NETWORK_PROCESS) diff --git a/Source/WebKit2/NetworkProcess/mac/NetworkResourceLoaderMac.mm b/Source/WebKit2/NetworkProcess/mac/NetworkResourceLoaderMac.mm deleted file mode 100644 index 84ea149c7..000000000 --- a/Source/WebKit2/NetworkProcess/mac/NetworkResourceLoaderMac.mm +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Copyright (C) 2013 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. - */ - -#import "config.h" -#import "NetworkResourceLoader.h" - -#import "DiskCacheMonitor.h" -#import "ShareableResource.h" -#import <WebCore/ResourceHandle.h> -#import <WebCore/SharedBuffer.h> - -using namespace WebCore; - -#ifdef __has_include -#if __has_include(<CFNetwork/CFURLCache.h>) -#include <CFNetwork/CFURLCache.h> -#endif -#if __has_include(<CFNetwork/CFURLCachePriv.h>) -#include <CFNetwork/CFURLCachePriv.h> -#endif -#endif - -#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1090 -typedef const struct _CFURLCache* CFURLCacheRef; -typedef const struct _CFCachedURLResponse* CFCachedURLResponseRef; -extern "C" CFURLCacheRef CFURLCacheCopySharedURLCache(); -extern "C" CFCachedURLResponseRef CFURLCacheCopyResponseForRequest(CFURLCacheRef, CFURLRequestRef); -extern "C" CFDataRef _CFCachedURLResponseGetMemMappedData(CFCachedURLResponseRef); -extern "C" CFBooleanRef _CFURLCacheIsResponseDataMemMapped(CFURLCacheRef, CFDataRef); -#endif - -@interface NSCachedURLResponse (NSCachedURLResponseDetails) --(CFCachedURLResponseRef)_CFCachedURLResponse; -@end - -namespace WebKit { - -#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1090 - -static void tryGetShareableHandleFromCFData(ShareableResource::Handle& handle, CFDataRef data) -{ - if (!data || CFDataGetLength(data) < (CFIndex)NetworkResourceLoader::fileBackedResourceMinimumSize()) - return; - - RefPtr<SharedMemory> sharedMemory = SharedMemory::createFromVMBuffer((void*)CFDataGetBytePtr(data), CFDataGetLength(data)); - if (!sharedMemory) { - LOG_ERROR("Failed to create VM shared memory for cached resource."); - return; - } - - size_t size = sharedMemory->size(); - RefPtr<ShareableResource> resource = ShareableResource::create(sharedMemory.release(), 0, size); - resource->createHandle(handle); -} - -void NetworkResourceLoader::tryGetShareableHandleFromCFURLCachedResponse(ShareableResource::Handle& handle, CFCachedURLResponseRef cachedResponse) -{ - CFDataRef data = _CFCachedURLResponseGetMemMappedData(cachedResponse); - - tryGetShareableHandleFromCFData(handle, data); -} - -void NetworkResourceLoader::tryGetShareableHandleFromSharedBuffer(ShareableResource::Handle& handle, SharedBuffer* buffer) -{ - RetainPtr<CFURLCacheRef> cache = adoptCF(CFURLCacheCopySharedURLCache()); - if (!cache) - return; - - RetainPtr<CFDataRef> data = adoptCF(buffer->createCFData()); - if (_CFURLCacheIsResponseDataMemMapped(cache.get(), data.get()) == kCFBooleanFalse) - return; - - tryGetShareableHandleFromCFData(handle, data.get()); -} -#endif // __MAC_OS_X_VERSION_MIN_REQUIRED >= 1090 - -size_t NetworkResourceLoader::fileBackedResourceMinimumSize() -{ - return SharedMemory::systemPageSize(); -} - -void NetworkResourceLoader::willCacheResponseAsync(ResourceHandle* handle, NSCachedURLResponse *nsResponse) -{ - ASSERT_UNUSED(handle, handle == m_handle); - -#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1090 - if (m_bytesReceived >= fileBackedResourceMinimumSize()) - DiskCacheMonitor::monitorFileBackingStoreCreation([nsResponse _CFCachedURLResponse], this); -#endif - - m_handle->continueWillCacheResponse(nsResponse); -} - -} // namespace WebKit diff --git a/Source/WebKit2/NetworkProcess/mac/RemoteNetworkingContext.mm b/Source/WebKit2/NetworkProcess/mac/RemoteNetworkingContext.mm deleted file mode 100644 index 53ec91c5d..000000000 --- a/Source/WebKit2/NetworkProcess/mac/RemoteNetworkingContext.mm +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Copyright (C) 2012 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. - */ - -#import "config.h" -#import "RemoteNetworkingContext.h" - -#import "WebErrors.h" -#import <WebCore/ResourceError.h> -#import <WebKitSystemInterface.h> -#import <wtf/MainThread.h> -#import <wtf/PassOwnPtr.h> - -using namespace WebCore; - -namespace WebKit { - -static OwnPtr<NetworkStorageSession>& privateBrowsingStorageSession() -{ - ASSERT(isMainThread()); - DEFINE_STATIC_LOCAL(OwnPtr<NetworkStorageSession>, session, ()); - return session; -} - -bool RemoteNetworkingContext::shouldClearReferrerOnHTTPSToHTTPRedirect() const -{ - return m_shouldClearReferrerOnHTTPSToHTTPRedirect; -} - -RemoteNetworkingContext::RemoteNetworkingContext(bool needsSiteSpecificQuirks, bool localFileContentSniffingEnabled, bool privateBrowsingEnabled, bool shouldClearReferrerOnHTTPSToHTTPRedirect) - : m_needsSiteSpecificQuirks(needsSiteSpecificQuirks) - , m_localFileContentSniffingEnabled(localFileContentSniffingEnabled) - , m_privateBrowsingEnabled(privateBrowsingEnabled) - , m_shouldClearReferrerOnHTTPSToHTTPRedirect(shouldClearReferrerOnHTTPSToHTTPRedirect) -{ -} - -RemoteNetworkingContext::~RemoteNetworkingContext() -{ -} - -bool RemoteNetworkingContext::isValid() const -{ - return true; -} - -bool RemoteNetworkingContext::needsSiteSpecificQuirks() const -{ - return m_needsSiteSpecificQuirks; -} - -bool RemoteNetworkingContext::localFileContentSniffingEnabled() const -{ - return m_localFileContentSniffingEnabled; -} - -NetworkStorageSession& RemoteNetworkingContext::storageSession() const -{ - if (m_privateBrowsingEnabled) { - NetworkStorageSession* privateSession = privateBrowsingStorageSession().get(); - if (privateSession) - return *privateSession; - // Some requests with private browsing mode requested may still be coming shortly after NetworkProcess was told to destroy its session. - // FIXME: Find a way to track private browsing sessions more rigorously. - LOG_ERROR("Private browsing was requested, but there was no session for it. Please file a bug unless you just disabled private browsing, in which case it's an expected race."); - } - - return NetworkStorageSession::defaultStorageSession(); -} - -NetworkStorageSession* RemoteNetworkingContext::privateBrowsingSession() -{ - return privateBrowsingStorageSession().get(); -} - -RetainPtr<CFDataRef> RemoteNetworkingContext::sourceApplicationAuditData() const -{ - return nil; -} - -ResourceError RemoteNetworkingContext::blockedError(const ResourceRequest& request) const -{ - return WebKit::blockedError(request); -} - -static String& privateBrowsingStorageSessionIdentifierBase() -{ - ASSERT(isMainThread()); - DEFINE_STATIC_LOCAL(String, base, ()); - return base; -} - -void RemoteNetworkingContext::setPrivateBrowsingStorageSessionIdentifierBase(const String& identifier) -{ - privateBrowsingStorageSessionIdentifierBase() = identifier; -} - -void RemoteNetworkingContext::ensurePrivateBrowsingSession() -{ - if (privateBrowsingStorageSession()) - return; - - ASSERT(!privateBrowsingStorageSessionIdentifierBase().isNull()); - RetainPtr<CFStringRef> cfIdentifier = String(privateBrowsingStorageSessionIdentifierBase() + ".PrivateBrowsing").createCFString(); - - privateBrowsingStorageSession() = NetworkStorageSession::createPrivateBrowsingSession(privateBrowsingStorageSessionIdentifierBase()); -} - -void RemoteNetworkingContext::destroyPrivateBrowsingSession() -{ - privateBrowsingStorageSession() = nullptr; -} - -} diff --git a/Source/WebKit2/NetworkProcess/mac/com.apple.WebKit.NetworkProcess.sb.in b/Source/WebKit2/NetworkProcess/mac/com.apple.WebKit.NetworkProcess.sb.in deleted file mode 100644 index 7ba86c038..000000000 --- a/Source/WebKit2/NetworkProcess/mac/com.apple.WebKit.NetworkProcess.sb.in +++ /dev/null @@ -1,148 +0,0 @@ -(version 1) -(deny default (with partial-symbolication)) -(allow system-audit file-read-metadata) - -(import "system.sb") - -;; Utility functions for home directory relative path filters -(define (home-regex home-relative-regex) - (regex (string-append "^" (regex-quote (param "HOME_DIR")) home-relative-regex))) - -(define (home-subpath home-relative-subpath) - (subpath (string-append (param "HOME_DIR") home-relative-subpath))) - -(define (home-literal home-relative-literal) - (literal (string-append (param "HOME_DIR") home-relative-literal))) - -#if __MAC_OS_X_VERSION_MIN_REQUIRED == 1070 -;; Low level networking. Defined in system.sb on newer OS versions. -(define (system-network) - (allow file-read* - (literal "/Library/Preferences/com.apple.networkd.plist")) - (allow mach-lookup - (global-name "com.apple.SystemConfiguration.PPPController") - (global-name "com.apple.SystemConfiguration.SCNetworkReachability") - (global-name "com.apple.networkd")) - (allow network-outbound - (control-name "com.apple.netsrc") - (control-name "com.apple.network.statistics")) - (allow system-socket - (require-all (socket-domain AF_SYSTEM) - (socket-protocol 2)) ; SYSPROTO_CONTROL - (socket-domain AF_ROUTE))) -#endif - -;; Read-only preferences and data -(allow file-read* - ;; Basic system paths - (subpath "/Library/Frameworks") - (subpath "/Library/Managed Preferences") - - ;; System and user preferences - (literal "/Library/Preferences/.GlobalPreferences.plist") - (regex #"^/Library/Managed Preferences/[^/]+/com\.apple\.networkConnect\.plist$") - (home-literal "/Library/Preferences/.GlobalPreferences.plist") - (home-regex #"/Library/Preferences/ByHost/\.GlobalPreferences\.") - (home-regex #"/Library/Preferences/ByHost/com\.apple\.networkConnect\.") - (home-literal "/Library/Preferences/com.apple.DownloadAssessment.plist") - (home-literal "/Library/Preferences/com.apple.WebFoundation.plist") - - ;; On-disk WebKit2 framework location, to account for debug installations - ;; outside of /System/Library/Frameworks - (subpath (param "WEBKIT2_FRAMEWORK_DIR"))) - -;; Sandbox extensions -(define (apply-read-and-issue-extension op path-filter) - (op file-read* path-filter) - (op file-issue-extension (require-all (extension-class "com.apple.app-sandbox.read") path-filter))) -(define (apply-write-and-issue-extension op path-filter) - (op file-write* path-filter) - (op file-issue-extension (require-all (extension-class "com.apple.app-sandbox.read-write") path-filter))) -(define (read-only-and-issue-extensions path-filter) - (apply-read-and-issue-extension allow path-filter)) -(define (read-write-and-issue-extensions path-filter) - (apply-read-and-issue-extension allow path-filter) - (apply-write-and-issue-extension allow path-filter)) -(read-only-and-issue-extensions (extension "com.apple.app-sandbox.read")) -(read-write-and-issue-extensions (extension "com.apple.app-sandbox.read-write")) - -#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1080 -(allow file-read* file-write* (subpath (param "DARWIN_USER_CACHE_DIR"))) -#else -(allow file-read* file-write* (subpath (string-append (param "DARWIN_USER_CACHE_DIR") "/mds"))) -#endif - -(allow file-read* file-write* (subpath (param "DARWIN_USER_TEMP_DIR"))) - -;; IOKit user clients -(allow iokit-open - (iokit-user-client-class "RootDomainUserClient")) - -;; cookied. -;; FIXME: Update for <rdar://problem/13642852>. -(allow ipc-posix-shm-read-data - (ipc-posix-name "FNetwork.defaultStorageSession") - (ipc-posix-name-regex #"\.PrivateBrowsing-") - (ipc-posix-name-regex #"^Private WebKit Session-")) - -;; Various services required by CFNetwork and other frameworks -(allow mach-lookup - (global-name "com.apple.PowerManagement.control") - (global-name "com.apple.SystemConfiguration.configd") - (global-name "com.apple.cookied") - (global-name "com.apple.cfnetwork.AuthBrokerAgent")) - -;; Security framework -(allow mach-lookup - (global-name "com.apple.ocspd") - (global-name "com.apple.SecurityServer")) -(allow file-read* file-write* (home-subpath "/Library/Keychains")) ;; FIXME: This should be removed when <rdar://problem/10479685> is fixed. -(allow file-read* file-write* (subpath "/private/var/db/mds/system")) ;; FIXME: This should be removed when <rdar://problem/9538414> is fixed. -(allow file-read* - (subpath "/Library/Keychains") - (subpath "/private/var/db/mds") - (literal "/private/var/db/DetachedSignatures") - (literal "/Library/Preferences/com.apple.crypto.plist") - (literal "/Library/Preferences/com.apple.security.plist") - (literal "/Library/Preferences/com.apple.security.common.plist") - (literal "/Library/Preferences/com.apple.security.revocation.plist") - (home-literal "/Library/Application Support/SyncServices/Local/ClientsWithChanges/com.apple.Keychain") - (home-literal "/Library/Preferences/com.apple.security.plist") - (home-literal "/Library/Preferences/com.apple.security.revocation.plist")) -(allow ipc-posix-shm-read* ipc-posix-shm-write-data - (ipc-posix-name "com.apple.AppleDatabaseChanged")) - -(system-network) -(allow network-outbound - ;; Local mDNSResponder for DNS, arbitrary outbound TCP - (literal "/private/var/run/mDNSResponder") - (remote tcp)) - -;; FIXME should be removed when <rdar://problem/9347205> + related radar in Safari is fixed -(allow mach-lookup - (global-name "org.h5l.kcm") - (global-name "com.apple.system.logger") - (global-name "com.apple.system.notification_center")) -(allow network-outbound - (remote udp)) -(allow file-read* - (home-subpath "/Library/Preferences/com.apple.Kerberos.plist") - (home-subpath "/Library/Preferences/com.apple.GSS.plist") - (home-subpath "/Library/Preferences/edu.mit.Kerberos") - (literal "/Library/Preferences/com.apple.Kerberos.plist") - (literal "/Library/Preferences/com.apple.GSS.plist") - (literal "/Library/Preferences/edu.mit.Kerberos") - (literal "/private/etc/krb5.conf") - (literal "/private/etc/services") - (literal "/private/etc/host")) - -(if (defined? 'vnode-type) - (deny file-write-create (vnode-type SYMLINK))) - -(deny file-read* file-write* (with no-log) -#if __MAC_OS_X_VERSION_MIN_REQUIRED <= 1080 - (home-literal "/Library/Caches/Cache.db") ;; <rdar://problem/9422957> -#endif - ;; FIXME: Should be removed after <rdar://problem/10463881> is fixed. - (home-literal "/Library/Preferences/com.apple.LaunchServices.QuarantineEventsV2") - (home-literal "/Library/Preferences/com.apple.LaunchServices.QuarantineEventsV2-journal")) diff --git a/Source/WebKit2/NetworkProcess/qt/NetworkProcessMainQt.cpp b/Source/WebKit2/NetworkProcess/qt/NetworkProcessMainQt.cpp new file mode 100644 index 000000000..d3799bb24 --- /dev/null +++ b/Source/WebKit2/NetworkProcess/qt/NetworkProcessMainQt.cpp @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2016 Konstantin Tokarev <annulen@yandex.ru> + * Copyright (C) 2014 Igalia S.L. + * Copyright (C) 2013 Company 100 Inc. + * + * 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 THE COPYRIGHT HOLDERS ``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 "NetworkProcess.h" + +#include "ChildProcessMain.h" + +#include <QCoreApplication> +#include <QNetworkProxyFactory> + +using namespace WebCore; + +namespace WebKit { + +class EnvHttpProxyFactory : public QNetworkProxyFactory { +public: + EnvHttpProxyFactory() { } + + bool initializeFromEnvironment(); + + QList<QNetworkProxy> queryProxy(const QNetworkProxyQuery& = QNetworkProxyQuery()); + +private: + QList<QNetworkProxy> m_httpProxy; + QList<QNetworkProxy> m_httpsProxy; +}; + +bool EnvHttpProxyFactory::initializeFromEnvironment() +{ + bool wasSetByEnvironment = false; + + QUrl proxyUrl = QUrl::fromUserInput(QString::fromLocal8Bit(qgetenv("http_proxy"))); + if (proxyUrl.isValid() && !proxyUrl.host().isEmpty()) { + int proxyPort = (proxyUrl.port() > 0) ? proxyUrl.port() : 8080; + m_httpProxy << QNetworkProxy(QNetworkProxy::HttpProxy, proxyUrl.host(), proxyPort); + wasSetByEnvironment = true; + } else + m_httpProxy << QNetworkProxy::NoProxy; + + proxyUrl = QUrl::fromUserInput(QString::fromLocal8Bit(qgetenv("https_proxy"))); + if (proxyUrl.isValid() && !proxyUrl.host().isEmpty()) { + int proxyPort = (proxyUrl.port() > 0) ? proxyUrl.port() : 8080; + m_httpsProxy << QNetworkProxy(QNetworkProxy::HttpProxy, proxyUrl.host(), proxyPort); + wasSetByEnvironment = true; + } else + m_httpsProxy << QNetworkProxy::NoProxy; + + return wasSetByEnvironment; +} + +QList<QNetworkProxy> EnvHttpProxyFactory::queryProxy(const QNetworkProxyQuery& query) +{ + QString protocol = query.protocolTag().toLower(); + bool localHost = false; + + if (!query.peerHostName().compare(QLatin1String("localhost"), Qt::CaseInsensitive) || !query.peerHostName().compare(QLatin1String("127.0.0.1"), Qt::CaseInsensitive)) + localHost = true; + if (protocol == QLatin1String("http") && !localHost) + return m_httpProxy; + if (protocol == QLatin1String("https") && !localHost) + return m_httpsProxy; + + QList<QNetworkProxy> proxies; + proxies << QNetworkProxy::NoProxy; + return proxies; +} + +static void initializeProxy() +{ + QList<QNetworkProxy> proxylist = QNetworkProxyFactory::systemProxyForQuery(); + if (proxylist.count() == 1) { + QNetworkProxy proxy = proxylist.first(); + if (proxy == QNetworkProxy::NoProxy || proxy == QNetworkProxy::DefaultProxy) { + auto proxyFactory = std::make_unique<EnvHttpProxyFactory>(); + if (proxyFactory->initializeFromEnvironment()) { + QNetworkProxyFactory::setApplicationProxyFactory(proxyFactory.release()); + return; + } + } + } + QNetworkProxyFactory::setUseSystemConfiguration(true); +} + +class NetworkProcessMain final: public ChildProcessMainBase { +public: + + bool platformInitialize() final + { + initializeProxy(); + return true; + } +}; + +Q_DECL_EXPORT int NetworkProcessMainQt(int argc, char** argv) +{ + QCoreApplication app(argc, argv); + return ChildProcessMain<NetworkProcess, ChildProcessMainBase>(argc, argv); +} + +} // namespace WebKit diff --git a/Source/WebKit2/NetworkProcess/qt/NetworkProcessQt.cpp b/Source/WebKit2/NetworkProcess/qt/NetworkProcessQt.cpp new file mode 100644 index 000000000..f822b3d20 --- /dev/null +++ b/Source/WebKit2/NetworkProcess/qt/NetworkProcessQt.cpp @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2016 Konstantin Tokarev <annulen@yandex.ru> + * + * 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. ``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 + * 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 "NetworkProcess.h" + +#include "NetworkProcessCreationParameters.h" +#include "QtNetworkAccessManager.h" + +#include <QNetworkDiskCache> +#include <WebCore/CertificateInfo.h> +#include <WebCore/CookieJarQt.h> + +using namespace WebCore; + +namespace WebKit { + +void NetworkProcess::platformInitializeNetworkProcess(const NetworkProcessCreationParameters& parameters) +{ + if (!parameters.cookiePersistentStoragePath.isEmpty()) { + WebCore::SharedCookieJarQt* jar = WebCore::SharedCookieJarQt::create(parameters.cookiePersistentStoragePath); + m_networkAccessManager.setCookieJar(jar); + // Do not let QNetworkAccessManager delete the jar. + jar->setParent(0); + } + + if (!parameters.diskCacheDirectory.isEmpty()) { + QNetworkDiskCache* diskCache = new QNetworkDiskCache(); + diskCache->setCacheDirectory(parameters.diskCacheDirectory); + // The m_networkAccessManager takes ownership of the diskCache object upon the following call. + m_networkAccessManager.setCache(diskCache); + } +} + +void NetworkProcess::platformTerminate() +{ +} + +void NetworkProcess::allowSpecificHTTPSCertificateForHost(const CertificateInfo&, const String&) +{ +} + +void NetworkProcess::clearCacheForAllOrigins(uint32_t) +{ +} + +void NetworkProcess::clearDiskCache(std::chrono::system_clock::time_point, std::function<void()>) +{ +} + +void NetworkProcess::platformSetCacheModel(CacheModel) +{ +} + +} // namespace WebKit diff --git a/Source/WebKit2/NetworkProcess/qt/QtNetworkAccessManager.cpp b/Source/WebKit2/NetworkProcess/qt/QtNetworkAccessManager.cpp new file mode 100644 index 000000000..5e38a7717 --- /dev/null +++ b/Source/WebKit2/NetworkProcess/qt/QtNetworkAccessManager.cpp @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2011 Zeno Albisser <zeno@webkit.org> + * Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies) + * + * 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 "QtNetworkAccessManager.h" + +#include "WebPage.h" +#include "WebPageProxyMessages.h" +#include "WebProcess.h" +#include <QAuthenticator> +#include <QNetworkProxy> +#include <QNetworkReply> +#include <QNetworkRequest> + +namespace WebKit { + +QtNetworkAccessManager::QtNetworkAccessManager() + : QNetworkAccessManager() +{ + connect(this, SIGNAL(authenticationRequired(QNetworkReply*, QAuthenticator*)), SLOT(onAuthenticationRequired(QNetworkReply*, QAuthenticator*))); + connect(this, SIGNAL(proxyAuthenticationRequired(const QNetworkProxy&, QAuthenticator*)), SLOT(onProxyAuthenticationRequired(const QNetworkProxy&, QAuthenticator*))); +#ifndef QT_NO_SSL + connect(this, SIGNAL(sslErrors(QNetworkReply*, QList<QSslError>)), SLOT(onSslErrors(QNetworkReply*, QList<QSslError>))); +#endif +} + +WebPage* QtNetworkAccessManager::obtainOriginatingWebPage(const QNetworkRequest& request) +{ + // QTFIXME: Old process model + if (!m_webProcess) + return nullptr; + + QObject* originatingObject = request.originatingObject(); + if (!originatingObject) + return 0; + + qulonglong pageID = originatingObject->property("pageID").toULongLong(); + return m_webProcess->webPage(pageID); +} + +void QtNetworkAccessManager::onProxyAuthenticationRequired(const QNetworkProxy& proxy, QAuthenticator* authenticator) +{ + // FIXME: Check if there is a better way to get a reference to the page. + WebPage* webPage = m_webProcess->focusedWebPage(); + + if (!webPage) + return; + + String hostname = proxy.hostName(); + uint16_t port = static_cast<uint16_t>(proxy.port()); + String prefilledUsername = authenticator->user(); + String username; + String password; + + if (webPage->sendSync( + Messages::WebPageProxy::ProxyAuthenticationRequiredRequest(hostname, port, prefilledUsername), + Messages::WebPageProxy::ProxyAuthenticationRequiredRequest::Reply(username, password))) { + if (!username.isEmpty()) + authenticator->setUser(username); + if (!password.isEmpty()) + authenticator->setPassword(password); + } + +} + +void QtNetworkAccessManager::onAuthenticationRequired(QNetworkReply* reply, QAuthenticator* authenticator) +{ + WebPage* webPage = obtainOriginatingWebPage(reply->request()); + + // FIXME: This check can go away once our Qt version is up-to-date. See: QTBUG-23512. + if (!webPage) + return; + + String hostname = reply->url().toString(QUrl::RemovePath | QUrl::RemoveQuery | QUrl::RemoveFragment | QUrl::StripTrailingSlash); + String realm = authenticator->realm(); + String prefilledUsername = authenticator->user(); + String username; + String password; + + if (webPage->sendSync( + Messages::WebPageProxy::AuthenticationRequiredRequest(hostname, realm, prefilledUsername), + Messages::WebPageProxy::AuthenticationRequiredRequest::Reply(username, password))) { + if (!username.isEmpty()) + authenticator->setUser(username); + if (!password.isEmpty()) + authenticator->setPassword(password); + } +} + +void QtNetworkAccessManager::onSslErrors(QNetworkReply* reply, const QList<QSslError>& qSslErrors) +{ +#ifndef QT_NO_SSL + WebPage* webPage = obtainOriginatingWebPage(reply->request()); + + // FIXME: This check can go away once our Qt version is up-to-date. See: QTBUG-23512. + if (!webPage) + return; + + String hostname = reply->url().host(); + bool ignoreErrors = false; + + if (webPage->sendSync( + Messages::WebPageProxy::CertificateVerificationRequest(hostname), + Messages::WebPageProxy::CertificateVerificationRequest::Reply(ignoreErrors))) { + if (ignoreErrors) + reply->ignoreSslErrors(qSslErrors); + } +#endif +} + +} + +#include "moc_QtNetworkAccessManager.cpp" diff --git a/Source/WebKit2/NetworkProcess/qt/QtNetworkAccessManager.h b/Source/WebKit2/NetworkProcess/qt/QtNetworkAccessManager.h new file mode 100644 index 000000000..5b958f13e --- /dev/null +++ b/Source/WebKit2/NetworkProcess/qt/QtNetworkAccessManager.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2011 Zeno Albisser <zeno@webkit.org> + * Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies) + * + * 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. + */ + +#ifndef QtNetworkAccessManager_h +#define QtNetworkAccessManager_h + +#include <QMultiHash> +#include <QNetworkAccessManager> +#include <QNetworkProxy> +#include <QString> + +namespace WebKit { + +class WebPage; +class WebProcess; + +class QtNetworkAccessManager : public QNetworkAccessManager { + Q_OBJECT +public: + QtNetworkAccessManager(); + +private Q_SLOTS: + void onAuthenticationRequired(QNetworkReply *, QAuthenticator *); + void onProxyAuthenticationRequired(const QNetworkProxy&, QAuthenticator *); + void onSslErrors(QNetworkReply*, const QList<QSslError>&); + +private: + WebPage* obtainOriginatingWebPage(const QNetworkRequest&); + + WebProcess* m_webProcess { nullptr }; +}; + +} // namespace WebKit + +#endif // QtNetworkAccessManager_h diff --git a/Source/WebKit2/NetworkProcess/mac/DiskCacheMonitor.h b/Source/WebKit2/NetworkProcess/qt/RemoteNetworkingContextQt.cpp index c137995e5..96da15886 100644 --- a/Source/WebKit2/NetworkProcess/mac/DiskCacheMonitor.h +++ b/Source/WebKit2/NetworkProcess/qt/RemoteNetworkingContextQt.cpp @@ -1,5 +1,7 @@ /* * Copyright (C) 2013 Apple Inc. All rights reserved. + * Copyright (C) 2013 University of Szeged. All rights reserved. + * Copyright (C) 2013 Company 100 Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -23,38 +25,41 @@ * THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef DiskCacheMonitor_h -#define DiskCacheMonitor_h +#include "config.h" +#include "RemoteNetworkingContext.h" -#include "MessageSender.h" -#include <WebCore/ResourceRequest.h> -#include <WebCore/RunLoop.h> +#include "NetworkProcess.h" +#include <WebCore/NetworkStorageSession.h> +#include <WebCore/NotImplemented.h> +#include <WebCore/ResourceHandle.h> +#include <wtf/NeverDestroyed.h> -typedef const struct _CFCachedURLResponse* CFCachedURLResponseRef; +using namespace WebCore; namespace WebKit { -class NetworkConnectionToWebProcess; -class NetworkResourceLoader; +RemoteNetworkingContext::~RemoteNetworkingContext() +{ +} -class DiskCacheMonitor : public CoreIPC::MessageSender { -public: - static void monitorFileBackingStoreCreation(CFCachedURLResponseRef, NetworkResourceLoader*); +bool RemoteNetworkingContext::isValid() const +{ + return true; +} - const WebCore::ResourceRequest& resourceRequest() const { return m_resourceRequest; } +void RemoteNetworkingContext::ensurePrivateBrowsingSession(SessionID) +{ + notImplemented(); +} -private: - DiskCacheMonitor(CFCachedURLResponseRef, NetworkResourceLoader*); +NetworkStorageSession& RemoteNetworkingContext::storageSession() const +{ + return NetworkStorageSession::defaultStorageSession(); +} - // CoreIPC::MessageSender - virtual CoreIPC::Connection* messageSenderConnection() OVERRIDE; - virtual uint64_t messageSenderDestinationID() OVERRIDE; +QNetworkAccessManager* RemoteNetworkingContext::networkAccessManager() const +{ + return &NetworkProcess::singleton().networkAccessManager(); +} - RefPtr<NetworkConnectionToWebProcess> m_connectionToWebProcess; - WebCore::ResourceRequest m_resourceRequest; -}; - - -} // namespace WebKit - -#endif // DiskCacheMonitor_h +} diff --git a/Source/WebKit2/NetworkProcess/unix/NetworkProcessMainUnix.h b/Source/WebKit2/NetworkProcess/unix/NetworkProcessMainUnix.h index cb62eda2d..fa064c358 100644 --- a/Source/WebKit2/NetworkProcess/unix/NetworkProcessMainUnix.h +++ b/Source/WebKit2/NetworkProcess/unix/NetworkProcessMainUnix.h @@ -27,15 +27,13 @@ #ifndef NetworkProcessMainUnix_h #define NetworkProcessMainUnix_h -#include <WebKit2/WKBase.h> +#include <WebKit/WKBase.h> namespace WebKit { -#ifdef __cplusplus extern "C" { -WK_EXPORT int NetworkProcessMain(int argc, char* argv[]); -} // extern "C" -#endif // __cplusplus +WK_EXPORT int NetworkProcessMainUnix(int argc, char** argv); +} } // namespace WebKit |