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 "WebKitWebResource.h" 22 23#include "WebData.h" 24#include "WebFrameProxy.h" 25#include "WebKitMarshal.h" 26#include "WebKitURIRequest.h" 27#include "WebKitWebResourcePrivate.h" 28#include <glib/gi18n-lib.h> 29#include <wtf/gobject/GRefPtr.h> 30#include <wtf/text/CString.h> 31 32using namespace WebKit; 33 34/** 35 * SECTION: WebKitWebResource 36 * @Short_description: Represents a resource at the end of a URI 37 * @Title: WebKitWebResource 38 * 39 * A #WebKitWebResource encapsulates content for each resource at the 40 * end of a particular URI. For example, one #WebKitWebResource will 41 * be created for each separate image and stylesheet when a page is 42 * loaded. 43 * 44 * You can access the response and the URI for a given 45 * #WebKitWebResource, using webkit_web_resource_get_uri() and 46 * webkit_web_resource_get_response(), as well as the raw data, using 47 * webkit_web_resource_get_data(). 48 * 49 */ 50 51enum { 52 SENT_REQUEST, 53 RECEIVED_DATA, 54 FINISHED, 55 FAILED, 56 57 LAST_SIGNAL 58}; 59 60enum { 61 PROP_0, 62 63 PROP_URI, 64 PROP_RESPONSE 65}; 66 67 68struct _WebKitWebResourcePrivate { 69 RefPtr<WebFrameProxy> frame; 70 CString uri; 71 GRefPtr<WebKitURIResponse> response; 72 bool isMainResource; 73}; 74 75WEBKIT_DEFINE_TYPE(WebKitWebResource, webkit_web_resource, G_TYPE_OBJECT) 76 77static guint signals[LAST_SIGNAL] = { 0, }; 78 79static void webkitWebResourceGetProperty(GObject* object, guint propId, GValue* value, GParamSpec* paramSpec) 80{ 81 WebKitWebResource* resource = WEBKIT_WEB_RESOURCE(object); 82 83 switch (propId) { 84 case PROP_URI: 85 g_value_set_string(value, webkit_web_resource_get_uri(resource)); 86 break; 87 case PROP_RESPONSE: 88 g_value_set_object(value, webkit_web_resource_get_response(resource)); 89 break; 90 default: 91 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propId, paramSpec); 92 } 93} 94 95static void webkit_web_resource_class_init(WebKitWebResourceClass* resourceClass) 96{ 97 GObjectClass* objectClass = G_OBJECT_CLASS(resourceClass); 98 objectClass->get_property = webkitWebResourceGetProperty; 99 100 /** 101 * WebKitWebResource:uri: 102 * 103 * The current active URI of the #WebKitWebResource. 104 * See webkit_web_resource_get_uri() for more details. 105 */ 106 g_object_class_install_property(objectClass, 107 PROP_URI, 108 g_param_spec_string("uri", 109 _("URI"), 110 _("The current active URI of the resource"), 111 0, 112 WEBKIT_PARAM_READABLE)); 113 114 /** 115 * WebKitWebResource:response: 116 * 117 * The #WebKitURIResponse associated with this resource. 118 */ 119 g_object_class_install_property(objectClass, 120 PROP_RESPONSE, 121 g_param_spec_object("response", 122 _("Response"), 123 _("The response of the resource"), 124 WEBKIT_TYPE_URI_RESPONSE, 125 WEBKIT_PARAM_READABLE)); 126 127 /** 128 * WebKitWebResource::sent-request: 129 * @resource: the #WebKitWebResource 130 * @request: a #WebKitURIRequest 131 * @redirected_response: a #WebKitURIResponse, or %NULL 132 * 133 * This signal is emitted when @request has been sent to the 134 * server. In case of a server redirection this signal is 135 * emitted again with the @request argument containing the new 136 * request sent to the server due to the redirection and the 137 * @redirected_response parameter containing the response 138 * received by the server for the initial request. 139 */ 140 signals[SENT_REQUEST] = 141 g_signal_new("sent-request", 142 G_TYPE_FROM_CLASS(objectClass), 143 G_SIGNAL_RUN_LAST, 144 0, 0, 0, 145 webkit_marshal_VOID__OBJECT_OBJECT, 146 G_TYPE_NONE, 2, 147 WEBKIT_TYPE_URI_REQUEST, 148 WEBKIT_TYPE_URI_RESPONSE); 149 150 /** 151 * WebKitWebResource::received-data: 152 * @resource: the #WebKitWebResource 153 * @data_length: the length of data received in bytes 154 * 155 * This signal is emitted after response is received, 156 * every time new data has been received. It's 157 * useful to know the progress of the resource load operation. 158 */ 159 signals[RECEIVED_DATA] = 160 g_signal_new("received-data", 161 G_TYPE_FROM_CLASS(objectClass), 162 G_SIGNAL_RUN_LAST, 163 0, 0, 0, 164 webkit_marshal_VOID__UINT64, 165 G_TYPE_NONE, 1, 166 G_TYPE_UINT64); 167 168 /** 169 * WebKitWebResource::finished: 170 * @resource: the #WebKitWebResource 171 * 172 * This signal is emitted when the resource load finishes successfully 173 * or due to an error. In case of errors #WebKitWebResource::failed signal 174 * is emitted before this one. 175 */ 176 signals[FINISHED] = 177 g_signal_new("finished", 178 G_TYPE_FROM_CLASS(objectClass), 179 G_SIGNAL_RUN_LAST, 180 0, 0, 0, 181 g_cclosure_marshal_VOID__VOID, 182 G_TYPE_NONE, 0); 183 184 /** 185 * WebKitWebResource::failed: 186 * @resource: the #WebKitWebResource 187 * @error: the #GError that was triggered 188 * 189 * This signal is emitted when an error occurs during the resource 190 * load operation. 191 */ 192 signals[FAILED] = 193 g_signal_new("failed", 194 G_TYPE_FROM_CLASS(objectClass), 195 G_SIGNAL_RUN_LAST, 196 0, 0, 0, 197 g_cclosure_marshal_VOID__POINTER, 198 G_TYPE_NONE, 1, 199 G_TYPE_POINTER); 200} 201 202static void webkitWebResourceUpdateURI(WebKitWebResource* resource, const CString& requestURI) 203{ 204 if (resource->priv->uri == requestURI) 205 return; 206 207 resource->priv->uri = requestURI; 208 g_object_notify(G_OBJECT(resource), "uri"); 209} 210 211WebKitWebResource* webkitWebResourceCreate(WebFrameProxy* frame, WebKitURIRequest* request, bool isMainResource) 212{ 213 ASSERT(frame); 214 WebKitWebResource* resource = WEBKIT_WEB_RESOURCE(g_object_new(WEBKIT_TYPE_WEB_RESOURCE, NULL)); 215 resource->priv->frame = frame; 216 resource->priv->uri = webkit_uri_request_get_uri(request); 217 resource->priv->isMainResource = isMainResource; 218 return resource; 219} 220 221void webkitWebResourceSentRequest(WebKitWebResource* resource, WebKitURIRequest* request, WebKitURIResponse* redirectResponse) 222{ 223 webkitWebResourceUpdateURI(resource, webkit_uri_request_get_uri(request)); 224 g_signal_emit(resource, signals[SENT_REQUEST], 0, request, redirectResponse); 225} 226 227void webkitWebResourceSetResponse(WebKitWebResource* resource, WebKitURIResponse* response) 228{ 229 resource->priv->response = response; 230 g_object_notify(G_OBJECT(resource), "response"); 231} 232 233void webkitWebResourceNotifyProgress(WebKitWebResource* resource, guint64 bytesReceived) 234{ 235 g_signal_emit(resource, signals[RECEIVED_DATA], 0, bytesReceived); 236} 237 238void webkitWebResourceFinished(WebKitWebResource* resource) 239{ 240 g_signal_emit(resource, signals[FINISHED], 0, NULL); 241} 242 243void webkitWebResourceFailed(WebKitWebResource* resource, GError* error) 244{ 245 g_signal_emit(resource, signals[FAILED], 0, error); 246 g_signal_emit(resource, signals[FINISHED], 0, NULL); 247} 248 249WebFrameProxy* webkitWebResourceGetFrame(WebKitWebResource* resource) 250{ 251 return resource->priv->frame.get(); 252} 253 254/** 255 * webkit_web_resource_get_uri: 256 * @resource: a #WebKitWebResource 257 * 258 * Returns the current active URI of @resource. The active URI might change during 259 * a load operation: 260 * 261 * <orderedlist> 262 * <listitem><para> 263 * When the resource load starts, the active URI is the requested URI 264 * </para></listitem> 265 * <listitem><para> 266 * When the initial request is sent to the server, #WebKitWebResource::sent-request 267 * signal is emitted without a redirected response, the active URI is the URI of 268 * the request sent to the server. 269 * </para></listitem> 270 * <listitem><para> 271 * In case of a server redirection, #WebKitWebResource::sent-request signal 272 * is emitted again with a redirected response, the active URI is the URI the request 273 * was redirected to. 274 * </para></listitem> 275 * <listitem><para> 276 * When the response is received from the server, the active URI is the final 277 * one and it will not change again. 278 * </para></listitem> 279 * </orderedlist> 280 * 281 * You can monitor the active URI by connecting to the notify::uri 282 * signal of @resource. 283 * 284 * Returns: the current active URI of @resource 285 */ 286const char* webkit_web_resource_get_uri(WebKitWebResource* resource) 287{ 288 g_return_val_if_fail(WEBKIT_IS_WEB_RESOURCE(resource), 0); 289 290 return resource->priv->uri.data(); 291} 292 293/** 294 * webkit_web_resource_get_response: 295 * @resource: a #WebKitWebResource 296 * 297 * Retrieves the #WebKitURIResponse of the resource load operation. 298 * This method returns %NULL if called before the response 299 * is received from the server. You can connect to notify::response 300 * signal to be notified when the response is received. 301 * 302 * Returns: (transfer none): the #WebKitURIResponse, or %NULL if 303 * the response hasn't been received yet. 304 */ 305WebKitURIResponse* webkit_web_resource_get_response(WebKitWebResource* resource) 306{ 307 g_return_val_if_fail(WEBKIT_IS_WEB_RESOURCE(resource), 0); 308 309 return resource->priv->response.get(); 310} 311 312struct ResourceGetDataAsyncData { 313 RefPtr<WebData> webData; 314 GRefPtr<GCancellable> cancellable; 315}; 316WEBKIT_DEFINE_ASYNC_DATA_STRUCT(ResourceGetDataAsyncData) 317 318static void resourceDataCallback(WKDataRef wkData, WKErrorRef, void* context) 319{ 320 GRefPtr<GSimpleAsyncResult> result = adoptGRef(G_SIMPLE_ASYNC_RESULT(context)); 321 ResourceGetDataAsyncData* data = static_cast<ResourceGetDataAsyncData*>(g_simple_async_result_get_op_res_gpointer(result.get())); 322 GError* error = 0; 323 if (g_cancellable_set_error_if_cancelled(data->cancellable.get(), &error)) 324 g_simple_async_result_take_error(result.get(), error); 325 else 326 data->webData = toImpl(wkData); 327 g_simple_async_result_complete(result.get()); 328} 329 330/** 331 * webkit_web_resource_get_data: 332 * @resource: a #WebKitWebResource 333 * @cancellable: (allow-none): a #GCancellable or %NULL to ignore 334 * @callback: (scope async): a #GAsyncReadyCallback to call when the request is satisfied 335 * @user_data: (closure): the data to pass to callback function 336 * 337 * Asynchronously get the raw data for @resource. 338 * 339 * When the operation is finished, @callback will be called. You can then call 340 * webkit_web_resource_get_data_finish() to get the result of the operation. 341 */ 342void webkit_web_resource_get_data(WebKitWebResource* resource, GCancellable* cancellable, GAsyncReadyCallback callback, gpointer userData) 343{ 344 g_return_if_fail(WEBKIT_IS_WEB_RESOURCE(resource)); 345 346 GSimpleAsyncResult* result = g_simple_async_result_new(G_OBJECT(resource), callback, userData, 347 reinterpret_cast<gpointer>(webkit_web_resource_get_data)); 348 ResourceGetDataAsyncData* data = createResourceGetDataAsyncData(); 349 data->cancellable = cancellable; 350 g_simple_async_result_set_op_res_gpointer(result, data, reinterpret_cast<GDestroyNotify>(destroyResourceGetDataAsyncData)); 351 if (resource->priv->isMainResource) 352 resource->priv->frame->getMainResourceData(DataCallback::create(result, resourceDataCallback)); 353 else { 354 String url = String::fromUTF8(resource->priv->uri.data()); 355 resource->priv->frame->getResourceData(WebURL::create(url).get(), DataCallback::create(result, resourceDataCallback)); 356 } 357} 358 359/** 360 * webkit_web_resource_get_data_finish: 361 * @resource: a #WebKitWebResource 362 * @result: a #GAsyncResult 363 * @length: (out): return location for the length of the resource data 364 * @error: return location for error or %NULL to ignore 365 * 366 * Finish an asynchronous operation started with webkit_web_resource_get_data(). 367 * 368 * Returns: (transfer full): a string with the data of @resource, or %NULL in case 369 * of error. if @length is not %NULL, the size of the data will be assigned to it. 370 */ 371guchar* webkit_web_resource_get_data_finish(WebKitWebResource* resource, GAsyncResult* result, gsize* length, GError** error) 372{ 373 g_return_val_if_fail(WEBKIT_IS_WEB_RESOURCE(resource), 0); 374 g_return_val_if_fail(G_IS_ASYNC_RESULT(result), 0); 375 376 GSimpleAsyncResult* simple = G_SIMPLE_ASYNC_RESULT(result); 377 g_warn_if_fail(g_simple_async_result_get_source_tag(simple) == webkit_web_resource_get_data); 378 379 if (g_simple_async_result_propagate_error(simple, error)) 380 return 0; 381 382 ResourceGetDataAsyncData* data = static_cast<ResourceGetDataAsyncData*>(g_simple_async_result_get_op_res_gpointer(simple)); 383 if (length) 384 *length = data->webData->size(); 385 return static_cast<guchar*>(g_memdup(data->webData->bytes(), data->webData->size())); 386} 387