1/* 2 * Copyright (C) 2012 Igalia S.L. 3 * 4 * This library is free software; you can redistribute it and/or 5 * modify it under the terms of the GNU Library General Public 6 * License as published by the Free Software Foundation; either 7 * version 2 of the License, or (at your option) any later version. 8 * 9 * This library is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 * Library General Public License for more details. 13 * 14 * You should have received a copy of the GNU Library General Public License 15 * along with this library; see the file COPYING.LIB. If not, write to 16 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 17 * Boston, MA 02110-1301, USA. 18 */ 19 20#include "config.h" 21#include "WebSoupRequestManager.h" 22 23#include "DataReference.h" 24#include "WebErrors.h" 25#include "WebKitSoupRequestGeneric.h" 26#include "WebKitSoupRequestInputStream.h" 27#include "WebPageProxyMessages.h" 28#include "WebProcess.h" 29#include "WebSoupRequestManagerMessages.h" 30#include "WebSoupRequestManagerProxyMessages.h" 31#include <WebCore/ResourceHandle.h> 32#include <WebCore/ResourceRequest.h> 33#include <wtf/gobject/GOwnPtr.h> 34#include <wtf/text/CString.h> 35 36namespace WebKit { 37 38static uint64_t generateSoupRequestID() 39{ 40 static uint64_t uniqueSoupRequestID = 1; 41 return uniqueSoupRequestID++; 42} 43 44struct WebSoupRequestAsyncData { 45 WebSoupRequestAsyncData(GSimpleAsyncResult* result, WebKitSoupRequestGeneric* requestGeneric, GCancellable* cancellable) 46 : result(result) 47 , request(requestGeneric) 48 , cancellable(cancellable) 49 { 50 // If the struct contains a null request, it is because the request failed. 51 g_object_add_weak_pointer(G_OBJECT(request), reinterpret_cast<void**>(&request)); 52 } 53 54 ~WebSoupRequestAsyncData() 55 { 56 if (request) 57 g_object_remove_weak_pointer(G_OBJECT(request), reinterpret_cast<void**>(&request)); 58 } 59 60 bool requestFailed() 61 { 62 return g_cancellable_is_cancelled(cancellable.get()) || !request; 63 } 64 65 GRefPtr<GSimpleAsyncResult> releaseResult() 66 { 67 GSimpleAsyncResult* returnValue = result; 68 result = 0; 69 return adoptGRef(returnValue); 70 } 71 72 GSimpleAsyncResult* result; 73 WebKitSoupRequestGeneric* request; 74 GRefPtr<GCancellable> cancellable; 75 GRefPtr<GInputStream> stream; 76}; 77 78const char* WebSoupRequestManager::supplementName() 79{ 80 return "WebSoupRequestManager"; 81} 82 83WebSoupRequestManager::WebSoupRequestManager(WebProcess* process) 84 : m_process(process) 85 , m_schemes(adoptGRef(g_ptr_array_new_with_free_func(g_free))) 86{ 87 m_process->addMessageReceiver(Messages::WebSoupRequestManager::messageReceiverName(), this); 88} 89 90WebSoupRequestManager::~WebSoupRequestManager() 91{ 92} 93 94void WebSoupRequestManager::registerURIScheme(const String& scheme) 95{ 96 if (m_schemes->len) 97 g_ptr_array_remove_index_fast(m_schemes.get(), m_schemes->len - 1); 98 g_ptr_array_add(m_schemes.get(), g_strdup(scheme.utf8().data())); 99 g_ptr_array_add(m_schemes.get(), 0); 100 101 SoupSession* session = WebCore::ResourceHandle::defaultSession(); 102 SoupRequestClass* genericRequestClass = static_cast<SoupRequestClass*>(g_type_class_ref(WEBKIT_TYPE_SOUP_REQUEST_GENERIC)); 103 genericRequestClass->schemes = const_cast<const char**>(reinterpret_cast<char**>(m_schemes->pdata)); 104 soup_session_add_feature_by_type(session, WEBKIT_TYPE_SOUP_REQUEST_GENERIC); 105} 106 107void WebSoupRequestManager::didHandleURIRequest(const CoreIPC::DataReference& requestData, uint64_t contentLength, const String& mimeType, uint64_t requestID) 108{ 109 WebSoupRequestAsyncData* data = m_requestMap.get(requestID); 110 ASSERT(data); 111 GRefPtr<GSimpleAsyncResult> result = data->releaseResult(); 112 ASSERT(result.get()); 113 114 GRefPtr<WebKitSoupRequestGeneric> request = adoptGRef(WEBKIT_SOUP_REQUEST_GENERIC(g_async_result_get_source_object(G_ASYNC_RESULT(result.get())))); 115 webkitSoupRequestGenericSetContentLength(request.get(), contentLength ? contentLength : -1); 116 webkitSoupRequestGenericSetContentType(request.get(), !mimeType.isEmpty() ? mimeType.utf8().data() : 0); 117 118 GInputStream* dataStream; 119 if (!requestData.size()) { 120 // Empty reply, just create and empty GMemoryInputStream. 121 dataStream = g_memory_input_stream_new(); 122 m_requestMap.remove(requestID); 123 } else if (requestData.size() == contentLength) { 124 // We don't expect more data, so we can just create a GMemoryInputStream with all the data. 125 dataStream = g_memory_input_stream_new_from_data(g_memdup(requestData.data(), requestData.size()), contentLength, g_free); 126 m_requestMap.remove(requestID); 127 } else { 128 // We expect more data chunks from the UI process. 129 dataStream = webkitSoupRequestInputStreamNew(contentLength); 130 data->stream = dataStream; 131 webkitSoupRequestInputStreamAddData(WEBKIT_SOUP_REQUEST_INPUT_STREAM(dataStream), requestData.data(), requestData.size()); 132 } 133 g_simple_async_result_set_op_res_gpointer(result.get(), dataStream, g_object_unref); 134 g_simple_async_result_complete(result.get()); 135} 136 137void WebSoupRequestManager::didReceiveURIRequestData(const CoreIPC::DataReference& requestData, uint64_t requestID) 138{ 139 WebSoupRequestAsyncData* data = m_requestMap.get(requestID); 140 // The data might have been removed from the request map if a previous chunk failed 141 // and a new message was sent by the UI process before being notified about the failure. 142 if (!data) 143 return; 144 ASSERT(data->stream.get()); 145 146 if (data->requestFailed()) { 147 // ResourceRequest failed or it was cancelled. It doesn't matter here the error or if it was cancelled, 148 // because that's already handled by the resource handle client, we just want to notify the UI process 149 // to stop reading data from the user input stream. If UI process already sent all the data we simply 150 // finish silently. 151 if (!webkitSoupRequestInputStreamFinished(WEBKIT_SOUP_REQUEST_INPUT_STREAM(data->stream.get()))) 152 m_process->parentProcessConnection()->send(Messages::WebSoupRequestManagerProxy::DidFailToLoadURIRequest(requestID), 0); 153 m_requestMap.remove(requestID); 154 155 return; 156 } 157 158 webkitSoupRequestInputStreamAddData(WEBKIT_SOUP_REQUEST_INPUT_STREAM(data->stream.get()), requestData.data(), requestData.size()); 159 if (webkitSoupRequestInputStreamFinished(WEBKIT_SOUP_REQUEST_INPUT_STREAM(data->stream.get()))) 160 m_requestMap.remove(requestID); 161} 162 163void WebSoupRequestManager::didFailURIRequest(const WebCore::ResourceError& error, uint64_t requestID) 164{ 165 WebSoupRequestAsyncData* data = m_requestMap.get(requestID); 166 ASSERT(data); 167 GRefPtr<GSimpleAsyncResult> result = data->releaseResult(); 168 ASSERT(result.get()); 169 170 g_simple_async_result_take_error(result.get(), 171 g_error_new_literal(g_quark_from_string(error.domain().utf8().data()), 172 error.errorCode(), 173 error.localizedDescription().utf8().data())); 174 g_simple_async_result_complete(result.get()); 175 176 m_requestMap.remove(requestID); 177} 178 179void WebSoupRequestManager::send(GSimpleAsyncResult* result, GCancellable* cancellable) 180{ 181 GRefPtr<WebKitSoupRequestGeneric> request = adoptGRef(WEBKIT_SOUP_REQUEST_GENERIC(g_async_result_get_source_object(G_ASYNC_RESULT(result)))); 182 SoupRequest* soupRequest = SOUP_REQUEST(request.get()); 183 GOwnPtr<char> uriString(soup_uri_to_string(soup_request_get_uri(soupRequest), FALSE)); 184 185 uint64_t requestID = generateSoupRequestID(); 186 m_requestMap.set(requestID, adoptPtr(new WebSoupRequestAsyncData(result, request.get(), cancellable))); 187 188 uint64_t initiatingPageID = WebCore::ResourceHandle::getSoupRequestInitiatingPageID(soupRequest); 189 m_process->parentProcessConnection()->send(Messages::WebPageProxy::DidReceiveURIRequest(String::fromUTF8(uriString.get()), requestID), initiatingPageID); 190} 191 192GInputStream* WebSoupRequestManager::finish(GSimpleAsyncResult* result) 193{ 194 return G_INPUT_STREAM(g_object_ref(g_simple_async_result_get_op_res_gpointer(result))); 195} 196 197} // namespace WebKit 198