diff options
author | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-06-27 06:07:23 +0000 |
---|---|---|
committer | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-06-27 06:07:23 +0000 |
commit | 1bf1084f2b10c3b47fd1a588d85d21ed0eb41d0c (patch) | |
tree | 46dcd36c86e7fbc6e5df36deb463b33e9967a6f7 /Source/WebKit2/NetworkProcess/CustomProtocols | |
parent | 32761a6cee1d0dee366b885b7b9c777e67885688 (diff) | |
download | WebKitGtk-tarball-master.tar.gz |
webkitgtk-2.16.5HEADwebkitgtk-2.16.5master
Diffstat (limited to 'Source/WebKit2/NetworkProcess/CustomProtocols')
4 files changed, 466 insertions, 0 deletions
diff --git a/Source/WebKit2/NetworkProcess/CustomProtocols/CustomProtocolManager.cpp b/Source/WebKit2/NetworkProcess/CustomProtocols/CustomProtocolManager.cpp new file mode 100644 index 000000000..df33db9fb --- /dev/null +++ b/Source/WebKit2/NetworkProcess/CustomProtocols/CustomProtocolManager.cpp @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2012, 2013 Apple Inc. All rights reserved. + * Copyright (C) 2017 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 "CustomProtocolManager.h" + +#include "ChildProcess.h" +#include "CustomProtocolManagerMessages.h" +#include "CustomProtocolManagerProxyMessages.h" +#include "NetworkProcessCreationParameters.h" +#include "WebCoreArgumentCoders.h" +#include <WebCore/ResourceRequest.h> + +namespace WebKit { + +static uint64_t generateCustomProtocolID() +{ + static uint64_t uniqueCustomProtocolID = 0; + return ++uniqueCustomProtocolID; +} + +const char* CustomProtocolManager::supplementName() +{ + return "CustomProtocolManager"; +} + +CustomProtocolManager::CustomProtocolManager(ChildProcess* childProcess) + : m_childProcess(childProcess) +{ + m_childProcess->addMessageReceiver(Messages::CustomProtocolManager::messageReceiverName(), *this); +} + +void CustomProtocolManager::initialize(const NetworkProcessCreationParameters& parameters) +{ + registerProtocolClass(); + + for (const auto& scheme : parameters.urlSchemesRegisteredForCustomProtocols) + registerScheme(scheme); +} + +uint64_t CustomProtocolManager::addCustomProtocol(CustomProtocol&& customProtocol) +{ + LockHolder locker(m_customProtocolMapMutex); + auto customProtocolID = generateCustomProtocolID(); + m_customProtocolMap.add(customProtocolID, WTFMove(customProtocol)); + return customProtocolID; +} + +void CustomProtocolManager::removeCustomProtocol(uint64_t customProtocolID) +{ + LockHolder locker(m_customProtocolMapMutex); + m_customProtocolMap.remove(customProtocolID); +} + +void CustomProtocolManager::startLoading(uint64_t customProtocolID, const WebCore::ResourceRequest& request) +{ + m_childProcess->send(Messages::CustomProtocolManagerProxy::StartLoading(customProtocolID, request)); +} + +void CustomProtocolManager::stopLoading(uint64_t customProtocolID) +{ + m_childProcess->send(Messages::CustomProtocolManagerProxy::StopLoading(customProtocolID), 0); +} + +} // namespace WebKit diff --git a/Source/WebKit2/NetworkProcess/CustomProtocols/CustomProtocolManager.h b/Source/WebKit2/NetworkProcess/CustomProtocols/CustomProtocolManager.h new file mode 100644 index 000000000..95937ba10 --- /dev/null +++ b/Source/WebKit2/NetworkProcess/CustomProtocols/CustomProtocolManager.h @@ -0,0 +1,137 @@ +/* + * 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. + */ + +#pragma once + +#include "MessageReceiver.h" +#include "NetworkProcessSupplement.h" +#include <wtf/HashMap.h> +#include <wtf/HashSet.h> +#include <wtf/text/StringHash.h> +#include <wtf/text/WTFString.h> + +#if PLATFORM(COCOA) +#include <wtf/RetainPtr.h> +OBJC_CLASS NSURLSessionConfiguration; +OBJC_CLASS WKCustomProtocol; +#endif + +#if USE(SOUP) +#include <wtf/glib/GRefPtr.h> + +typedef struct _GCancellable GCancellable; +typedef struct _GInputStream GInputStream; +typedef struct _GTask GTask; +typedef struct _WebKitSoupRequestGeneric WebKitSoupRequestGeneric; +#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::MessageReceiver { + WTF_MAKE_NONCOPYABLE(CustomProtocolManager); +public: + explicit CustomProtocolManager(ChildProcess*); + + static const char* supplementName(); + + void registerScheme(const String&); + void unregisterScheme(const String&); + bool supportsScheme(const String&); + +#if PLATFORM(COCOA) + typedef RetainPtr<WKCustomProtocol> CustomProtocol; +#endif +#if USE(SOUP) + struct WebSoupRequestAsyncData { + WebSoupRequestAsyncData(GRefPtr<GTask>&&, WebKitSoupRequestGeneric*); + ~WebSoupRequestAsyncData(); + + GRefPtr<GTask> task; + WebKitSoupRequestGeneric* request; + GRefPtr<GCancellable> cancellable; + GRefPtr<GInputStream> stream; + }; + typedef std::unique_ptr<WebSoupRequestAsyncData> CustomProtocol; +#endif + + uint64_t addCustomProtocol(CustomProtocol&&); + void removeCustomProtocol(uint64_t customProtocolID); + void startLoading(uint64_t customProtocolID, const WebCore::ResourceRequest&); + void stopLoading(uint64_t customProtocolID); + +#if PLATFORM(COCOA) && USE(NETWORK_SESSION) + void registerProtocolClass(NSURLSessionConfiguration*); +#endif + +private: + // NetworkProcessSupplement + void initialize(const NetworkProcessCreationParameters&) override; + + // IPC::MessageReceiver + void didReceiveMessage(IPC::Connection&, IPC::Decoder&) 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); + + void registerProtocolClass(); + + ChildProcess* m_childProcess; + + typedef HashMap<uint64_t, CustomProtocol> CustomProtocolMap; + CustomProtocolMap m_customProtocolMap; + Lock m_customProtocolMapMutex; + +#if PLATFORM(COCOA) + HashSet<String, ASCIICaseInsensitiveHash> m_registeredSchemes; + Lock m_registeredSchemesMutex; + + // 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); +#endif + +#if USE(SOUP) + GRefPtr<GPtrArray> m_registeredSchemes; +#endif +}; + +} // namespace WebKit + 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/soup/CustomProtocolManagerSoup.cpp b/Source/WebKit2/NetworkProcess/CustomProtocols/soup/CustomProtocolManagerSoup.cpp new file mode 100644 index 000000000..43a03b8cb --- /dev/null +++ b/Source/WebKit2/NetworkProcess/CustomProtocols/soup/CustomProtocolManagerSoup.cpp @@ -0,0 +1,209 @@ +/* + * Copyright (C) 2013 Igalia S.L. + * + * 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" + +#include "CustomProtocolManagerMessages.h" +#include "DataReference.h" +#include "NetworkProcess.h" +#include "WebKitSoupRequestInputStream.h" +#include <WebCore/NetworkStorageSession.h> +#include <WebCore/NotImplemented.h> +#include <WebCore/ResourceError.h> +#include <WebCore/ResourceRequest.h> +#include <WebCore/ResourceResponse.h> +#include <WebCore/SoupNetworkSession.h> +#include <WebCore/WebKitSoupRequestGeneric.h> +#include <wtf/NeverDestroyed.h> + +using namespace WebCore; + +namespace WebKit { + + +CustomProtocolManager::WebSoupRequestAsyncData::WebSoupRequestAsyncData(GRefPtr<GTask>&& requestTask, WebKitSoupRequestGeneric* requestGeneric) + : task(WTFMove(requestTask)) + , request(requestGeneric) + , cancellable(g_task_get_cancellable(task.get())) +{ + // If the struct contains a null request, it is because the request failed. + g_object_add_weak_pointer(G_OBJECT(request), reinterpret_cast<void**>(&request)); +} + +CustomProtocolManager::WebSoupRequestAsyncData::~WebSoupRequestAsyncData() +{ + if (request) + g_object_remove_weak_pointer(G_OBJECT(request), reinterpret_cast<void**>(&request)); +} + +class CustomProtocolRequestClient final : public WebKitSoupRequestGenericClient { +public: + static CustomProtocolRequestClient& singleton() + { + static NeverDestroyed<CustomProtocolRequestClient> client; + return client; + } + +private: + void startRequest(GRefPtr<GTask>&& task) override + { + WebKitSoupRequestGeneric* request = WEBKIT_SOUP_REQUEST_GENERIC(g_task_get_source_object(task.get())); + auto* customProtocolManager = NetworkProcess::singleton().supplement<CustomProtocolManager>(); + if (!customProtocolManager) + return; + + auto customProtocolID = customProtocolManager->addCustomProtocol(std::make_unique<CustomProtocolManager::WebSoupRequestAsyncData>(WTFMove(task), request)); + customProtocolManager->startLoading(customProtocolID, webkitSoupRequestGenericGetRequest(request)); + } +}; + +void CustomProtocolManager::registerProtocolClass() +{ + static_cast<WebKitSoupRequestGenericClass*>(g_type_class_ref(WEBKIT_TYPE_SOUP_REQUEST_GENERIC))->client = &CustomProtocolRequestClient::singleton(); + SoupNetworkSession::setCustomProtocolRequestType(WEBKIT_TYPE_SOUP_REQUEST_GENERIC); +} + +void CustomProtocolManager::registerScheme(const String& scheme) +{ + if (!m_registeredSchemes) + m_registeredSchemes = adoptGRef(g_ptr_array_new_with_free_func(g_free)); + + if (m_registeredSchemes->len) + g_ptr_array_remove_index_fast(m_registeredSchemes.get(), m_registeredSchemes->len - 1); + g_ptr_array_add(m_registeredSchemes.get(), g_strdup(scheme.utf8().data())); + g_ptr_array_add(m_registeredSchemes.get(), nullptr); + + auto* genericRequestClass = static_cast<SoupRequestClass*>(g_type_class_peek(WEBKIT_TYPE_SOUP_REQUEST_GENERIC)); + ASSERT(genericRequestClass); + genericRequestClass->schemes = const_cast<const char**>(reinterpret_cast<char**>(m_registeredSchemes->pdata)); + NetworkStorageSession::forEach([](const WebCore::NetworkStorageSession& session) { + if (auto* soupSession = session.soupNetworkSession()) + soupSession->setupCustomProtocols(); + }); +} + +void CustomProtocolManager::unregisterScheme(const String&) +{ + notImplemented(); +} + +bool CustomProtocolManager::supportsScheme(const String& scheme) +{ + if (scheme.isNull()) + return false; + + CString cScheme = scheme.utf8(); + for (unsigned i = 0; i < m_registeredSchemes->len; ++i) { + if (cScheme == static_cast<char*>(g_ptr_array_index(m_registeredSchemes.get(), i))) + return true; + } + + return false; +} + +void CustomProtocolManager::didFailWithError(uint64_t customProtocolID, const ResourceError& error) +{ + auto* data = m_customProtocolMap.get(customProtocolID); + ASSERT(data); + + // Either we haven't started reading the stream yet, in which case we need to complete the + // task first, or we failed reading it and the task was already completed by didLoadData(). + ASSERT(!data->stream || !data->task); + + if (!data->stream) { + GRefPtr<GTask> task = std::exchange(data->task, nullptr); + ASSERT(task.get()); + g_task_return_new_error(task.get(), g_quark_from_string(error.domain().utf8().data()), + error.errorCode(), "%s", error.localizedDescription().utf8().data()); + } else + webkitSoupRequestInputStreamDidFailWithError(WEBKIT_SOUP_REQUEST_INPUT_STREAM(data->stream.get()), error); + + removeCustomProtocol(customProtocolID); +} + +void CustomProtocolManager::didLoadData(uint64_t customProtocolID, const IPC::DataReference& dataReference) +{ + auto* data = m_customProtocolMap.get(customProtocolID); + // The data might have been removed from the request map if a previous chunk failed + // and a new message was sent by the UI process before being notified about the failure. + if (!data) + return; + + if (!data->stream) { + GRefPtr<GTask> task = std::exchange(data->task, nullptr); + ASSERT(task.get()); + + goffset soupContentLength = soup_request_get_content_length(SOUP_REQUEST(g_task_get_source_object(task.get()))); + uint64_t contentLength = soupContentLength == -1 ? 0 : static_cast<uint64_t>(soupContentLength); + if (!dataReference.size()) { + // Empty reply, just create and empty GMemoryInputStream. + data->stream = g_memory_input_stream_new(); + } else if (dataReference.size() == contentLength) { + // We don't expect more data, so we can just create a GMemoryInputStream with all the data. + data->stream = g_memory_input_stream_new_from_data(g_memdup(dataReference.data(), dataReference.size()), contentLength, g_free); + } else { + // We expect more data chunks from the UI process. + data->stream = webkitSoupRequestInputStreamNew(contentLength); + webkitSoupRequestInputStreamAddData(WEBKIT_SOUP_REQUEST_INPUT_STREAM(data->stream.get()), dataReference.data(), dataReference.size()); + } + g_task_return_pointer(task.get(), data->stream.get(), g_object_unref); + return; + } + + if (g_cancellable_is_cancelled(data->cancellable.get()) || !data->request) { + // ResourceRequest failed or it was cancelled. It doesn't matter here the error or if it was cancelled, + // because that's already handled by the resource handle client, we just want to notify the UI process + // to stop reading data from the user input stream. If UI process already sent all the data we simply + // finish silently. + if (!webkitSoupRequestInputStreamFinished(WEBKIT_SOUP_REQUEST_INPUT_STREAM(data->stream.get()))) + stopLoading(customProtocolID); + + return; + } + + webkitSoupRequestInputStreamAddData(WEBKIT_SOUP_REQUEST_INPUT_STREAM(data->stream.get()), dataReference.data(), dataReference.size()); +} + +void CustomProtocolManager::didReceiveResponse(uint64_t customProtocolID, const ResourceResponse& response, uint32_t) +{ + auto* data = m_customProtocolMap.get(customProtocolID); + // The data might have been removed from the request map if an error happened even before this point. + if (!data) + return; + + ASSERT(data->task); + + WebKitSoupRequestGeneric* request = WEBKIT_SOUP_REQUEST_GENERIC(g_task_get_source_object(data->task.get())); + webkitSoupRequestGenericSetContentLength(request, response.expectedContentLength() ? response.expectedContentLength() : -1); + webkitSoupRequestGenericSetContentType(request, !response.mimeType().isEmpty() ? response.mimeType().utf8().data() : 0); +} + +void CustomProtocolManager::didFinishLoading(uint64_t customProtocolID) +{ + ASSERT(m_customProtocolMap.contains(customProtocolID)); + removeCustomProtocol(customProtocolID); +} + +void CustomProtocolManager::wasRedirectedToRequest(uint64_t, const ResourceRequest&, const ResourceResponse&) +{ + notImplemented(); +} + +} // namespace WebKit |