blob: 47c64a2fe43e951181ffb411792046f2440839c0 [file] [log] [blame]
/*
* Copyright (C) 2012 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 "WebKitWebResource.h"
#include "APIData.h"
#include "WebFrameProxy.h"
#include "WebKitURIRequest.h"
#include "WebKitWebResourcePrivate.h"
#include "WebPageProxy.h"
#include <glib/gi18n-lib.h>
#include <wtf/glib/GRefPtr.h>
#include <wtf/glib/WTFGType.h>
#include <wtf/text/CString.h>
using namespace WebKit;
/**
* SECTION: WebKitWebResource
* @Short_description: Represents a resource at the end of a URI
* @Title: WebKitWebResource
*
* A #WebKitWebResource encapsulates content for each resource at the
* end of a particular URI. For example, one #WebKitWebResource will
* be created for each separate image and stylesheet when a page is
* loaded.
*
* You can access the response and the URI for a given
* #WebKitWebResource, using webkit_web_resource_get_uri() and
* webkit_web_resource_get_response(), as well as the raw data, using
* webkit_web_resource_get_data().
*
*/
enum {
SENT_REQUEST,
RECEIVED_DATA,
FINISHED,
FAILED,
FAILED_WITH_TLS_ERRORS,
LAST_SIGNAL
};
enum {
PROP_0,
PROP_URI,
PROP_RESPONSE
};
struct _WebKitWebResourcePrivate {
RefPtr<WebFrameProxy> frame;
CString uri;
GRefPtr<WebKitURIResponse> response;
bool isMainResource;
};
WEBKIT_DEFINE_TYPE(WebKitWebResource, webkit_web_resource, G_TYPE_OBJECT)
static guint signals[LAST_SIGNAL] = { 0, };
static void webkitWebResourceGetProperty(GObject* object, guint propId, GValue* value, GParamSpec* paramSpec)
{
WebKitWebResource* resource = WEBKIT_WEB_RESOURCE(object);
switch (propId) {
case PROP_URI:
g_value_set_string(value, webkit_web_resource_get_uri(resource));
break;
case PROP_RESPONSE:
g_value_set_object(value, webkit_web_resource_get_response(resource));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propId, paramSpec);
}
}
static void webkit_web_resource_class_init(WebKitWebResourceClass* resourceClass)
{
GObjectClass* objectClass = G_OBJECT_CLASS(resourceClass);
objectClass->get_property = webkitWebResourceGetProperty;
/**
* WebKitWebResource:uri:
*
* The current active URI of the #WebKitWebResource.
* See webkit_web_resource_get_uri() for more details.
*/
g_object_class_install_property(objectClass,
PROP_URI,
g_param_spec_string("uri",
_("URI"),
_("The current active URI of the resource"),
0,
WEBKIT_PARAM_READABLE));
/**
* WebKitWebResource:response:
*
* The #WebKitURIResponse associated with this resource.
*/
g_object_class_install_property(objectClass,
PROP_RESPONSE,
g_param_spec_object("response",
_("Response"),
_("The response of the resource"),
WEBKIT_TYPE_URI_RESPONSE,
WEBKIT_PARAM_READABLE));
/**
* WebKitWebResource::sent-request:
* @resource: the #WebKitWebResource
* @request: a #WebKitURIRequest
* @redirected_response: a #WebKitURIResponse, or %NULL
*
* This signal is emitted when @request has been sent to the
* server. In case of a server redirection this signal is
* emitted again with the @request argument containing the new
* request sent to the server due to the redirection and the
* @redirected_response parameter containing the response
* received by the server for the initial request.
*/
signals[SENT_REQUEST] = g_signal_new(
"sent-request",
G_TYPE_FROM_CLASS(objectClass),
G_SIGNAL_RUN_LAST,
0, nullptr, nullptr,
g_cclosure_marshal_generic,
G_TYPE_NONE, 2,
WEBKIT_TYPE_URI_REQUEST,
WEBKIT_TYPE_URI_RESPONSE);
/**
* WebKitWebResource::received-data:
* @resource: the #WebKitWebResource
* @data_length: the length of data received in bytes
*
* This signal is emitted after response is received,
* every time new data has been received. It's
* useful to know the progress of the resource load operation.
*/
signals[RECEIVED_DATA] = g_signal_new(
"received-data",
G_TYPE_FROM_CLASS(objectClass),
G_SIGNAL_RUN_LAST,
0, nullptr, nullptr,
g_cclosure_marshal_generic,
G_TYPE_NONE, 1,
G_TYPE_UINT64);
/**
* WebKitWebResource::finished:
* @resource: the #WebKitWebResource
*
* This signal is emitted when the resource load finishes successfully
* or due to an error. In case of errors #WebKitWebResource::failed signal
* is emitted before this one.
*/
signals[FINISHED] =
g_signal_new("finished",
G_TYPE_FROM_CLASS(objectClass),
G_SIGNAL_RUN_LAST,
0, 0, 0,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0);
/**
* WebKitWebResource::failed:
* @resource: the #WebKitWebResource
* @error: the #GError that was triggered
*
* This signal is emitted when an error occurs during the resource
* load operation.
*/
signals[FAILED] =
g_signal_new(
"failed",
G_TYPE_FROM_CLASS(objectClass),
G_SIGNAL_RUN_LAST,
0, 0, 0,
g_cclosure_marshal_VOID__BOXED,
G_TYPE_NONE, 1,
G_TYPE_ERROR | G_SIGNAL_TYPE_STATIC_SCOPE);
/**
* WebKitWebResource::failed-with-tls-errors:
* @resource: the #WebKitWebResource
* @certificate: a #GTlsCertificate
* @errors: a #GTlsCertificateFlags with the verification status of @certificate
*
* This signal is emitted when a TLS error occurs during the resource load operation.
*
* Since: 2.8
*/
signals[FAILED_WITH_TLS_ERRORS] =
g_signal_new("failed-with-tls-errors",
G_TYPE_FROM_CLASS(objectClass),
G_SIGNAL_RUN_LAST,
0, nullptr, nullptr,
g_cclosure_marshal_generic,
G_TYPE_NONE, 2,
G_TYPE_TLS_CERTIFICATE,
G_TYPE_TLS_CERTIFICATE_FLAGS);
}
static void webkitWebResourceUpdateURI(WebKitWebResource* resource, const CString& requestURI)
{
if (resource->priv->uri == requestURI)
return;
resource->priv->uri = requestURI;
g_object_notify(G_OBJECT(resource), "uri");
}
WebKitWebResource* webkitWebResourceCreate(WebFrameProxy& frame, WebKitURIRequest* request, bool isMainResource)
{
WebKitWebResource* resource = WEBKIT_WEB_RESOURCE(g_object_new(WEBKIT_TYPE_WEB_RESOURCE, NULL));
resource->priv->frame = &frame;
resource->priv->uri = webkit_uri_request_get_uri(request);
resource->priv->isMainResource = isMainResource;
return resource;
}
void webkitWebResourceSentRequest(WebKitWebResource* resource, WebKitURIRequest* request, WebKitURIResponse* redirectResponse)
{
webkitWebResourceUpdateURI(resource, webkit_uri_request_get_uri(request));
g_signal_emit(resource, signals[SENT_REQUEST], 0, request, redirectResponse);
}
void webkitWebResourceSetResponse(WebKitWebResource* resource, WebKitURIResponse* response)
{
resource->priv->response = response;
g_object_notify(G_OBJECT(resource), "response");
}
void webkitWebResourceNotifyProgress(WebKitWebResource* resource, guint64 bytesReceived)
{
g_signal_emit(resource, signals[RECEIVED_DATA], 0, bytesReceived);
}
void webkitWebResourceFinished(WebKitWebResource* resource)
{
g_signal_emit(resource, signals[FINISHED], 0, NULL);
}
void webkitWebResourceFailed(WebKitWebResource* resource, GError* error)
{
g_signal_emit(resource, signals[FAILED], 0, error);
g_signal_emit(resource, signals[FINISHED], 0, NULL);
}
void webkitWebResourceFailedWithTLSErrors(WebKitWebResource* resource, GTlsCertificateFlags tlsErrors, GTlsCertificate* certificate)
{
g_signal_emit(resource, signals[FAILED_WITH_TLS_ERRORS], 0, certificate, tlsErrors);
g_signal_emit(resource, signals[FINISHED], 0, nullptr);
}
WebFrameProxy* webkitWebResourceGetFrame(WebKitWebResource* resource)
{
return resource->priv->frame.get();
}
/**
* webkit_web_resource_get_uri:
* @resource: a #WebKitWebResource
*
* Returns the current active URI of @resource. The active URI might change during
* a load operation:
*
* <orderedlist>
* <listitem><para>
* When the resource load starts, the active URI is the requested URI
* </para></listitem>
* <listitem><para>
* When the initial request is sent to the server, #WebKitWebResource::sent-request
* signal is emitted without a redirected response, the active URI is the URI of
* the request sent to the server.
* </para></listitem>
* <listitem><para>
* In case of a server redirection, #WebKitWebResource::sent-request signal
* is emitted again with a redirected response, the active URI is the URI the request
* was redirected to.
* </para></listitem>
* <listitem><para>
* When the response is received from the server, the active URI is the final
* one and it will not change again.
* </para></listitem>
* </orderedlist>
*
* You can monitor the active URI by connecting to the notify::uri
* signal of @resource.
*
* Returns: the current active URI of @resource
*/
const char* webkit_web_resource_get_uri(WebKitWebResource* resource)
{
g_return_val_if_fail(WEBKIT_IS_WEB_RESOURCE(resource), 0);
return resource->priv->uri.data();
}
/**
* webkit_web_resource_get_response:
* @resource: a #WebKitWebResource
*
* Retrieves the #WebKitURIResponse of the resource load operation.
* This method returns %NULL if called before the response
* is received from the server. You can connect to notify::response
* signal to be notified when the response is received.
*
* Returns: (transfer none): the #WebKitURIResponse, or %NULL if
* the response hasn't been received yet.
*/
WebKitURIResponse* webkit_web_resource_get_response(WebKitWebResource* resource)
{
g_return_val_if_fail(WEBKIT_IS_WEB_RESOURCE(resource), 0);
return resource->priv->response.get();
}
struct ResourceGetDataAsyncData {
RefPtr<API::Data> webData;
};
WEBKIT_DEFINE_ASYNC_DATA_STRUCT(ResourceGetDataAsyncData)
static void resourceDataCallback(API::Data* wkData, CallbackBase::Error error, GTask* task)
{
if (error != CallbackBase::Error::None) {
// This fails when the page is closed or frame is destroyed, so we can just cancel the operation.
g_task_return_new_error(task, G_IO_ERROR, G_IO_ERROR_CANCELLED, _("Operation was cancelled"));
return;
}
ResourceGetDataAsyncData* data = static_cast<ResourceGetDataAsyncData*>(g_task_get_task_data(task));
data->webData = wkData;
if (!wkData->bytes())
data->webData = API::Data::create(reinterpret_cast<const unsigned char*>(""), 1);
g_task_return_boolean(task, TRUE);
}
/**
* webkit_web_resource_get_data:
* @resource: a #WebKitWebResource
* @cancellable: (allow-none): a #GCancellable or %NULL to ignore
* @callback: (scope async): a #GAsyncReadyCallback to call when the request is satisfied
* @user_data: (closure): the data to pass to callback function
*
* Asynchronously get the raw data for @resource.
*
* When the operation is finished, @callback will be called. You can then call
* webkit_web_resource_get_data_finish() to get the result of the operation.
*/
void webkit_web_resource_get_data(WebKitWebResource* resource, GCancellable* cancellable, GAsyncReadyCallback callback, gpointer userData)
{
g_return_if_fail(WEBKIT_IS_WEB_RESOURCE(resource));
GRefPtr<GTask> task = adoptGRef(g_task_new(resource, cancellable, callback, userData));
g_task_set_task_data(task.get(), createResourceGetDataAsyncData(), reinterpret_cast<GDestroyNotify>(destroyResourceGetDataAsyncData));
if (resource->priv->isMainResource)
resource->priv->frame->getMainResourceData([task = WTFMove(task)](API::Data* data, CallbackBase::Error error) {
resourceDataCallback(data, error, task.get());
});
else {
String url = String::fromUTF8(resource->priv->uri.data());
resource->priv->frame->getResourceData(API::URL::create(url).ptr(), [task = WTFMove(task)](API::Data* data, CallbackBase::Error error) {
resourceDataCallback(data, error, task.get());
});
}
}
/**
* webkit_web_resource_get_data_finish:
* @resource: a #WebKitWebResource
* @result: a #GAsyncResult
* @length: (out) (allow-none): return location for the length of the resource data
* @error: return location for error or %NULL to ignore
*
* Finish an asynchronous operation started with webkit_web_resource_get_data().
*
* Returns: (transfer full) (array length=length) (element-type guint8): a
* string with the data of @resource, or %NULL in case of error. if @length
* is not %NULL, the size of the data will be assigned to it.
*/
guchar* webkit_web_resource_get_data_finish(WebKitWebResource* resource, GAsyncResult* result, gsize* length, GError** error)
{
g_return_val_if_fail(WEBKIT_IS_WEB_RESOURCE(resource), 0);
g_return_val_if_fail(g_task_is_valid(result, resource), 0);
GTask* task = G_TASK(result);
if (!g_task_propagate_boolean(task, error))
return 0;
ResourceGetDataAsyncData* data = static_cast<ResourceGetDataAsyncData*>(g_task_get_task_data(task));
if (length)
*length = data->webData->size();
return static_cast<guchar*>(g_memdup(data->webData->bytes(), data->webData->size()));
}