blob: f2d706f3b63255df41a5baca53d7abb2870d69cf [file] [log] [blame]
/*
* Copyright (C) 2008 Collabora Ltd.
* Copyright (C) 2009 Gustavo Noronha Silva <gns@gnome.org>
*
* 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 "webkitdownload.h"
#include "GRefPtr.h"
#include "Noncopyable.h"
#include "NotImplemented.h"
#include "ResourceHandleClient.h"
#include "ResourceHandleInternal.h"
#include "ResourceRequest.h"
#include "ResourceResponse.h"
#include "webkitdownloadprivate.h"
#include "webkitenumtypes.h"
#include "webkitglobals.h"
#include "webkitglobalsprivate.h"
#include "webkitmarshal.h"
#include "webkitnetworkrequestprivate.h"
#include "webkitnetworkresponse.h"
#include "webkitnetworkresponseprivate.h"
#include <glib/gi18n-lib.h>
#include <glib/gstdio.h>
#include <wtf/text/CString.h>
#ifdef ERROR
#undef ERROR
#endif
using namespace WebKit;
using namespace WebCore;
/**
* SECTION:webkitdownload
* @short_description: Object used to communicate with the application when downloading.
*
* #WebKitDownload carries information about a download request,
* including a #WebKitNetworkRequest object. The application may use
* this object to control the download process, or to simply figure
* out what is to be downloaded, and do it itself.
*/
class DownloadClient : public ResourceHandleClient {
WTF_MAKE_NONCOPYABLE(DownloadClient);
public:
DownloadClient(WebKitDownload*);
virtual void didReceiveResponse(ResourceHandle*, const ResourceResponse&);
virtual void didReceiveData(ResourceHandle*, const char*, int, int);
virtual void didFinishLoading(ResourceHandle*, double);
virtual void didFail(ResourceHandle*, const ResourceError&);
virtual void wasBlocked(ResourceHandle*);
virtual void cannotShowURL(ResourceHandle*);
private:
WebKitDownload* m_download;
};
struct _WebKitDownloadPrivate {
gchar* destinationURI;
gchar* suggestedFilename;
guint64 currentSize;
GTimer* timer;
WebKitDownloadStatus status;
GFileOutputStream* outputStream;
DownloadClient* downloadClient;
WebKitNetworkRequest* networkRequest;
WebKitNetworkResponse* networkResponse;
RefPtr<ResourceHandle> resourceHandle;
};
enum {
// Normal signals.
ERROR,
LAST_SIGNAL
};
static guint webkit_download_signals[LAST_SIGNAL] = { 0 };
enum {
PROP_0,
PROP_NETWORK_REQUEST,
PROP_DESTINATION_URI,
PROP_SUGGESTED_FILENAME,
PROP_PROGRESS,
PROP_STATUS,
PROP_CURRENT_SIZE,
PROP_TOTAL_SIZE,
PROP_NETWORK_RESPONSE
};
G_DEFINE_TYPE(WebKitDownload, webkit_download, G_TYPE_OBJECT);
static void webkit_download_set_response(WebKitDownload* download, const ResourceResponse& response);
static void webkit_download_set_status(WebKitDownload* download, WebKitDownloadStatus status);
static void webkit_download_dispose(GObject* object)
{
WebKitDownload* download = WEBKIT_DOWNLOAD(object);
WebKitDownloadPrivate* priv = download->priv;
if (priv->outputStream) {
g_object_unref(priv->outputStream);
priv->outputStream = NULL;
}
if (priv->networkRequest) {
g_object_unref(priv->networkRequest);
priv->networkRequest = NULL;
}
if (priv->networkResponse) {
g_object_unref(priv->networkResponse);
priv->networkResponse = NULL;
}
G_OBJECT_CLASS(webkit_download_parent_class)->dispose(object);
}
static void webkit_download_finalize(GObject* object)
{
WebKitDownload* download = WEBKIT_DOWNLOAD(object);
WebKitDownloadPrivate* priv = download->priv;
// We don't call webkit_download_cancel() because we don't want to emit
// signals when finalizing an object.
if (priv->resourceHandle) {
if (priv->status == WEBKIT_DOWNLOAD_STATUS_STARTED) {
priv->resourceHandle->setClient(0);
priv->resourceHandle->cancel();
}
priv->resourceHandle.release();
}
delete priv->downloadClient;
// The download object may never have _start called on it, so we
// need to make sure timer is non-NULL.
if (priv->timer) {
g_timer_destroy(priv->timer);
priv->timer = NULL;
}
g_free(priv->destinationURI);
g_free(priv->suggestedFilename);
G_OBJECT_CLASS(webkit_download_parent_class)->finalize(object);
}
static void webkit_download_get_property(GObject* object, guint prop_id, GValue* value, GParamSpec* pspec)
{
WebKitDownload* download = WEBKIT_DOWNLOAD(object);
switch(prop_id) {
case PROP_NETWORK_REQUEST:
g_value_set_object(value, webkit_download_get_network_request(download));
break;
case PROP_NETWORK_RESPONSE:
g_value_set_object(value, webkit_download_get_network_response(download));
break;
case PROP_DESTINATION_URI:
g_value_set_string(value, webkit_download_get_destination_uri(download));
break;
case PROP_SUGGESTED_FILENAME:
g_value_set_string(value, webkit_download_get_suggested_filename(download));
break;
case PROP_PROGRESS:
g_value_set_double(value, webkit_download_get_progress(download));
break;
case PROP_STATUS:
g_value_set_enum(value, webkit_download_get_status(download));
break;
case PROP_CURRENT_SIZE:
g_value_set_uint64(value, webkit_download_get_current_size(download));
break;
case PROP_TOTAL_SIZE:
g_value_set_uint64(value, webkit_download_get_total_size(download));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
}
}
static void webkit_download_set_property(GObject* object, guint prop_id, const GValue* value, GParamSpec *pspec)
{
WebKitDownload* download = WEBKIT_DOWNLOAD(object);
WebKitDownloadPrivate* priv = download->priv;
switch(prop_id) {
case PROP_NETWORK_REQUEST:
priv->networkRequest = WEBKIT_NETWORK_REQUEST(g_value_dup_object(value));
break;
case PROP_NETWORK_RESPONSE:
priv->networkResponse = WEBKIT_NETWORK_RESPONSE(g_value_dup_object(value));
break;
case PROP_DESTINATION_URI:
webkit_download_set_destination_uri(download, g_value_get_string(value));
break;
case PROP_STATUS:
webkit_download_set_status(download, static_cast<WebKitDownloadStatus>(g_value_get_enum(value)));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
}
}
static void webkit_download_class_init(WebKitDownloadClass* downloadClass)
{
GObjectClass* objectClass = G_OBJECT_CLASS(downloadClass);
objectClass->dispose = webkit_download_dispose;
objectClass->finalize = webkit_download_finalize;
objectClass->get_property = webkit_download_get_property;
objectClass->set_property = webkit_download_set_property;
webkitInit();
/**
* WebKitDownload::error:
* @download: the object on which the signal is emitted
* @error_code: the corresponding error code
* @error_detail: detailed error code for the error, see
* #WebKitDownloadError
* @reason: a string describing the error
*
* Emitted when @download is interrupted either by user action or by
* network errors, @error_detail will take any value of
* #WebKitDownloadError.
*
* Since: 1.1.2
*/
webkit_download_signals[ERROR] = g_signal_new("error",
G_TYPE_FROM_CLASS(downloadClass),
(GSignalFlags)G_SIGNAL_RUN_LAST,
0,
g_signal_accumulator_true_handled,
NULL,
webkit_marshal_BOOLEAN__INT_INT_STRING,
G_TYPE_BOOLEAN, 3,
G_TYPE_INT,
G_TYPE_INT,
G_TYPE_STRING);
// Properties.
/**
* WebKitDownload:network-request
*
* The #WebKitNetworkRequest instance associated with the download.
*
* Since: 1.1.2
*/
g_object_class_install_property(objectClass,
PROP_NETWORK_REQUEST,
g_param_spec_object("network-request",
_("Network Request"),
_("The network request for the URI that should be downloaded"),
WEBKIT_TYPE_NETWORK_REQUEST,
(GParamFlags)(WEBKIT_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)));
/**
* WebKitDownload:network-response
*
* The #WebKitNetworkResponse instance associated with the download.
*
* Since: 1.1.16
*/
g_object_class_install_property(objectClass,
PROP_NETWORK_RESPONSE,
g_param_spec_object("network-response",
_("Network Response"),
_("The network response for the URI that should be downloaded"),
WEBKIT_TYPE_NETWORK_RESPONSE,
(GParamFlags)(WEBKIT_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)));
/**
* WebKitDownload:destination-uri
*
* The URI of the save location for this download.
*
* Since: 1.1.2
*/
g_object_class_install_property(objectClass,
PROP_DESTINATION_URI,
g_param_spec_string("destination-uri",
_("Destination URI"),
_("The destination URI where to save the file"),
"",
WEBKIT_PARAM_READWRITE));
/**
* WebKitDownload:suggested-filename
*
* The file name suggested as default when saving
*
* Since: 1.1.2
*/
g_object_class_install_property(objectClass,
PROP_SUGGESTED_FILENAME,
g_param_spec_string("suggested-filename",
_("Suggested Filename"),
_("The filename suggested as default when saving"),
"",
WEBKIT_PARAM_READABLE));
/**
* WebKitDownload:progress:
*
* Determines the current progress of the download. Notice that,
* although the progress changes are reported as soon as possible,
* the emission of the notify signal for this property is
* throttled, for the benefit of download managers. If you care
* about every update, use WebKitDownload:current-size.
*
* Since: 1.1.2
*/
g_object_class_install_property(objectClass, PROP_PROGRESS,
g_param_spec_double("progress",
_("Progress"),
_("Determines the current progress of the download"),
0.0, 1.0, 1.0,
WEBKIT_PARAM_READABLE));
/**
* WebKitDownload:status:
*
* Determines the current status of the download.
*
* Since: 1.1.2
*/
g_object_class_install_property(objectClass, PROP_STATUS,
g_param_spec_enum("status",
_("Status"),
_("Determines the current status of the download"),
WEBKIT_TYPE_DOWNLOAD_STATUS,
WEBKIT_DOWNLOAD_STATUS_CREATED,
WEBKIT_PARAM_READABLE));
/**
* WebKitDownload:current-size
*
* The length of the data already downloaded
*
* Since: 1.1.2
*/
g_object_class_install_property(objectClass,
PROP_CURRENT_SIZE,
g_param_spec_uint64("current-size",
_("Current Size"),
_("The length of the data already downloaded"),
0, G_MAXUINT64, 0,
WEBKIT_PARAM_READABLE));
/**
* WebKitDownload:total-size
*
* The total size of the file
*
* Since: 1.1.2
*/
g_object_class_install_property(objectClass,
PROP_CURRENT_SIZE,
g_param_spec_uint64("total-size",
_("Total Size"),
_("The total size of the file"),
0, G_MAXUINT64, 0,
WEBKIT_PARAM_READABLE));
g_type_class_add_private(downloadClass, sizeof(WebKitDownloadPrivate));
}
static void webkit_download_init(WebKitDownload* download)
{
WebKitDownloadPrivate* priv = G_TYPE_INSTANCE_GET_PRIVATE(download, WEBKIT_TYPE_DOWNLOAD, WebKitDownloadPrivate);
download->priv = priv;
priv->downloadClient = new DownloadClient(download);
priv->currentSize = 0;
priv->status = WEBKIT_DOWNLOAD_STATUS_CREATED;
}
/**
* webkit_download_new:
* @request: a #WebKitNetworkRequest
*
* Creates a new #WebKitDownload object for the given
* #WebKitNetworkRequest object.
*
* Returns: the new #WebKitDownload
*
* Since: 1.1.2
*/
WebKitDownload* webkit_download_new(WebKitNetworkRequest* request)
{
g_return_val_if_fail(request, NULL);
return WEBKIT_DOWNLOAD(g_object_new(WEBKIT_TYPE_DOWNLOAD, "network-request", request, NULL));
}
// Internal usage only
WebKitDownload* webkit_download_new_with_handle(WebKitNetworkRequest* request, WebCore::ResourceHandle* handle, const WebCore::ResourceResponse& response)
{
g_return_val_if_fail(request, NULL);
ResourceHandleInternal* d = handle->getInternal();
if (d->m_soupMessage)
soup_session_pause_message(webkit_get_default_session(), d->m_soupMessage.get());
WebKitDownload* download = WEBKIT_DOWNLOAD(g_object_new(WEBKIT_TYPE_DOWNLOAD, "network-request", request, NULL));
WebKitDownloadPrivate* priv = download->priv;
handle->ref();
priv->resourceHandle = handle;
webkit_download_set_response(download, response);
return download;
}
static gboolean webkit_download_open_stream_for_uri(WebKitDownload* download, const gchar* uri, gboolean append=FALSE)
{
g_return_val_if_fail(uri, FALSE);
WebKitDownloadPrivate* priv = download->priv;
GFile* file = g_file_new_for_uri(uri);
GError* error = NULL;
if (append)
priv->outputStream = g_file_append_to(file, G_FILE_CREATE_NONE, NULL, &error);
else
priv->outputStream = g_file_replace(file, NULL, TRUE, G_FILE_CREATE_NONE, NULL, &error);
g_object_unref(file);
if (error) {
gboolean handled;
g_signal_emit_by_name(download, "error", 0, WEBKIT_DOWNLOAD_ERROR_DESTINATION, error->message, &handled);
g_error_free(error);
return FALSE;
}
return TRUE;
}
static void webkit_download_close_stream(WebKitDownload* download)
{
WebKitDownloadPrivate* priv = download->priv;
if (priv->outputStream) {
g_object_unref(priv->outputStream);
priv->outputStream = NULL;
}
}
/**
* webkit_download_start:
* @download: the #WebKitDownload
*
* Initiates the download. Notice that you must have set the
* destination-uri property before calling this method.
*
* Since: 1.1.2
*/
void webkit_download_start(WebKitDownload* download)
{
g_return_if_fail(WEBKIT_IS_DOWNLOAD(download));
WebKitDownloadPrivate* priv = download->priv;
g_return_if_fail(priv->destinationURI);
g_return_if_fail(priv->status == WEBKIT_DOWNLOAD_STATUS_CREATED);
g_return_if_fail(priv->timer == NULL);
// For GTK, when downloading a file NetworkingContext is null
if (!priv->resourceHandle)
priv->resourceHandle = ResourceHandle::create(/* Null NetworkingContext */ NULL, core(priv->networkRequest), priv->downloadClient, false, false);
else {
priv->resourceHandle->setClient(priv->downloadClient);
ResourceHandleInternal* d = priv->resourceHandle->getInternal();
if (d->m_soupMessage)
soup_session_unpause_message(webkit_get_default_session(), d->m_soupMessage.get());
}
priv->timer = g_timer_new();
webkit_download_open_stream_for_uri(download, priv->destinationURI);
}
/**
* webkit_download_cancel:
* @download: the #WebKitDownload
*
* Cancels the download. Calling this will not free the
* #WebKitDownload object, so you still need to call
* g_object_unref() on it, if you are the owner of a reference. Notice
* that cancelling the download provokes the emission of the
* WebKitDownload::error signal, reporting that the download was
* cancelled.
*
* Since: 1.1.2
*/
void webkit_download_cancel(WebKitDownload* download)
{
g_return_if_fail(WEBKIT_IS_DOWNLOAD(download));
WebKitDownloadPrivate* priv = download->priv;
// Cancel may be called even if start was not called, so we need
// to make sure timer is non-NULL.
if (priv->timer)
g_timer_stop(priv->timer);
if (priv->resourceHandle)
priv->resourceHandle->cancel();
webkit_download_set_status(download, WEBKIT_DOWNLOAD_STATUS_CANCELLED);
gboolean handled;
g_signal_emit_by_name(download, "error", 0, WEBKIT_DOWNLOAD_ERROR_CANCELLED_BY_USER, _("User cancelled the download"), &handled);
}
/**
* webkit_download_get_uri:
* @download: the #WebKitDownload
*
* Convenience method to retrieve the URI from the
* #WebKitNetworkRequest which is being downloaded.
*
* Returns: the uri
*
* Since: 1.1.2
*/
const gchar* webkit_download_get_uri(WebKitDownload* download)
{
g_return_val_if_fail(WEBKIT_IS_DOWNLOAD(download), NULL);
WebKitDownloadPrivate* priv = download->priv;
return webkit_network_request_get_uri(priv->networkRequest);
}
/**
* webkit_download_get_network_request:
* @download: the #WebKitDownload
*
* Retrieves the #WebKitNetworkRequest object that backs the download
* process.
*
* Returns: (transfer none): the #WebKitNetworkRequest instance
*
* Since: 1.1.2
*/
WebKitNetworkRequest* webkit_download_get_network_request(WebKitDownload* download)
{
g_return_val_if_fail(WEBKIT_IS_DOWNLOAD(download), NULL);
WebKitDownloadPrivate* priv = download->priv;
return priv->networkRequest;
}
/**
* webkit_download_get_network_response:
* @download: the #WebKitDownload
*
* Retrieves the #WebKitNetworkResponse object that backs the download
* process.
*
* Returns: (transfer none): the #WebKitNetworkResponse instance
*
* Since: 1.1.16
*/
WebKitNetworkResponse* webkit_download_get_network_response(WebKitDownload* download)
{
g_return_val_if_fail(WEBKIT_IS_DOWNLOAD(download), NULL);
WebKitDownloadPrivate* priv = download->priv;
return priv->networkResponse;
}
static void webkit_download_set_response(WebKitDownload* download, const ResourceResponse& response)
{
WebKitDownloadPrivate* priv = download->priv;
priv->networkResponse = kitNew(response);
if (!response.isNull() && !response.suggestedFilename().isEmpty())
webkit_download_set_suggested_filename(download, response.suggestedFilename().utf8().data());
}
/**
* webkit_download_get_suggested_filename:
* @download: the #WebKitDownload
*
* Retrieves the filename that was suggested by the server, or the one
* derived by WebKit from the URI.
*
* Returns: the suggested filename
*
* Since: 1.1.2
*/
const gchar* webkit_download_get_suggested_filename(WebKitDownload* download)
{
g_return_val_if_fail(WEBKIT_IS_DOWNLOAD(download), NULL);
WebKitDownloadPrivate* priv = download->priv;
if (priv->suggestedFilename)
return priv->suggestedFilename;
KURL url = KURL(KURL(), webkit_network_request_get_uri(priv->networkRequest));
url.setQuery(String());
url.removeFragmentIdentifier();
priv->suggestedFilename = g_strdup(decodeURLEscapeSequences(url.lastPathComponent()).utf8().data());
return priv->suggestedFilename;
}
// for internal use only
void webkit_download_set_suggested_filename(WebKitDownload* download, const gchar* suggestedFilename)
{
WebKitDownloadPrivate* priv = download->priv;
g_free(priv->suggestedFilename);
priv->suggestedFilename = g_strdup(suggestedFilename);
g_object_notify(G_OBJECT(download), "suggested-filename");
}
/**
* webkit_download_get_destination_uri:
* @download: the #WebKitDownload
*
* Obtains the URI to which the downloaded file will be written. This
* must have been set by the application before calling
* webkit_download_start(), and may be %NULL.
*
* Returns: the destination URI or %NULL
*
* Since: 1.1.2
*/
const gchar* webkit_download_get_destination_uri(WebKitDownload* download)
{
g_return_val_if_fail(WEBKIT_IS_DOWNLOAD(download), NULL);
WebKitDownloadPrivate* priv = download->priv;
return priv->destinationURI;
}
/**
* webkit_download_set_destination_uri:
* @download: the #WebKitDownload
* @destination_uri: the destination URI
*
* Defines the URI that should be used to save the downloaded file to.
*
* Since: 1.1.2
*/
void webkit_download_set_destination_uri(WebKitDownload* download, const gchar* destination_uri)
{
g_return_if_fail(WEBKIT_IS_DOWNLOAD(download));
g_return_if_fail(destination_uri);
WebKitDownloadPrivate* priv = download->priv;
if (priv->destinationURI && !strcmp(priv->destinationURI, destination_uri))
return;
if (priv->status != WEBKIT_DOWNLOAD_STATUS_CREATED && priv->status != WEBKIT_DOWNLOAD_STATUS_CANCELLED) {
ASSERT(priv->destinationURI);
gboolean downloading = priv->outputStream != NULL;
if (downloading)
webkit_download_close_stream(download);
GFile* src = g_file_new_for_uri(priv->destinationURI);
GFile* dest = g_file_new_for_uri(destination_uri);
GError* error = NULL;
g_file_move(src, dest, G_FILE_COPY_BACKUP, NULL, NULL, NULL, &error);
g_object_unref(src);
g_object_unref(dest);
g_free(priv->destinationURI);
priv->destinationURI = g_strdup(destination_uri);
if (error) {
gboolean handled;
g_signal_emit_by_name(download, "error", 0, WEBKIT_DOWNLOAD_ERROR_DESTINATION, error->message, &handled);
g_error_free(error);
return;
}
if (downloading) {
if (!webkit_download_open_stream_for_uri(download, destination_uri, TRUE)) {
webkit_download_cancel(download);
return;
}
}
} else {
g_free(priv->destinationURI);
priv->destinationURI = g_strdup(destination_uri);
}
// Only notify change if everything went fine.
g_object_notify(G_OBJECT(download), "destination-uri");
}
/**
* webkit_download_get_status:
* @download: the #WebKitDownload
*
* Obtains the current status of the download, as a
* #WebKitDownloadStatus.
*
* Returns: the current #WebKitDownloadStatus
*
* Since: 1.1.2
*/
WebKitDownloadStatus webkit_download_get_status(WebKitDownload* download)
{
g_return_val_if_fail(WEBKIT_IS_DOWNLOAD(download), WEBKIT_DOWNLOAD_STATUS_ERROR);
WebKitDownloadPrivate* priv = download->priv;
return priv->status;
}
static void webkit_download_set_status(WebKitDownload* download, WebKitDownloadStatus status)
{
g_return_if_fail(WEBKIT_IS_DOWNLOAD(download));
WebKitDownloadPrivate* priv = download->priv;
priv->status = status;
g_object_notify(G_OBJECT(download), "status");
}
/**
* webkit_download_get_total_size:
* @download: the #WebKitDownload
*
* Returns the expected total size of the download. This is expected
* because the server may provide incorrect or missing
* Content-Length. Notice that this may grow over time, as it will be
* always the same as current_size in the cases where current size
* surpasses it.
*
* Returns: the expected total size of the downloaded file
*
* Since: 1.1.2
*/
guint64 webkit_download_get_total_size(WebKitDownload* download)
{
g_return_val_if_fail(WEBKIT_IS_DOWNLOAD(download), 0);
WebKitDownloadPrivate* priv = download->priv;
SoupMessage* message = priv->networkResponse ? webkit_network_response_get_message(priv->networkResponse) : NULL;
if (!message)
return 0;
return MAX(priv->currentSize, static_cast<guint64>(soup_message_headers_get_content_length(message->response_headers)));
}
/**
* webkit_download_get_current_size:
* @download: the #WebKitDownload
*
* Current already downloaded size.
*
* Returns: the already downloaded size
*
* Since: 1.1.2
*/
guint64 webkit_download_get_current_size(WebKitDownload* download)
{
g_return_val_if_fail(WEBKIT_IS_DOWNLOAD(download), 0);
WebKitDownloadPrivate* priv = download->priv;
return priv->currentSize;
}
/**
* webkit_download_get_progress:
* @download: a #WebKitDownload
*
* Determines the current progress of the download.
*
* Returns: a #gdouble ranging from 0.0 to 1.0.
*
* Since: 1.1.2
*/
gdouble webkit_download_get_progress(WebKitDownload* download)
{
g_return_val_if_fail(WEBKIT_IS_DOWNLOAD(download), 1.0);
WebKitDownloadPrivate* priv = download->priv;
if (!priv->networkResponse)
return 0.0;
gdouble total_size = static_cast<gdouble>(webkit_download_get_total_size(download));
if (total_size == 0)
return 1.0;
return ((gdouble)priv->currentSize) / total_size;
}
/**
* webkit_download_get_elapsed_time:
* @download: a #WebKitDownload
*
* Elapsed time for the download in seconds, including any fractional
* part. If the download is finished, had an error or was cancelled
* this is the time between its start and the event.
*
* Returns: seconds since the download was started, as a #gdouble
*
* Since: 1.1.2
*/
gdouble webkit_download_get_elapsed_time(WebKitDownload* download)
{
g_return_val_if_fail(WEBKIT_IS_DOWNLOAD(download), 0.0);
WebKitDownloadPrivate* priv = download->priv;
if (!priv->timer)
return 0;
return g_timer_elapsed(priv->timer, NULL);
}
static void webkit_download_received_data(WebKitDownload* download, const gchar* data, int length)
{
WebKitDownloadPrivate* priv = download->priv;
if (priv->currentSize == 0)
webkit_download_set_status(download, WEBKIT_DOWNLOAD_STATUS_STARTED);
ASSERT(priv->outputStream);
gsize bytes_written;
GError* error = NULL;
g_output_stream_write_all(G_OUTPUT_STREAM(priv->outputStream),
data, length, &bytes_written, NULL, &error);
if (error) {
gboolean handled;
g_signal_emit_by_name(download, "error", 0, WEBKIT_DOWNLOAD_ERROR_DESTINATION, error->message, &handled);
g_error_free(error);
return;
}
priv->currentSize += length;
g_object_notify(G_OBJECT(download), "current-size");
ASSERT(priv->networkResponse);
if (priv->currentSize > webkit_download_get_total_size(download))
g_object_notify(G_OBJECT(download), "total-size");
// Throttle progress notification to not consume high amounts of
// CPU on fast links, except when the last notification occured
// in more then 0.7 secs from now, or the last notified progress
// is passed in 1% or we reached the end.
static gdouble lastProgress = 0;
static gdouble lastElapsed = 0;
gdouble currentElapsed = g_timer_elapsed(priv->timer, NULL);
gdouble currentProgress = webkit_download_get_progress(download);
if (lastElapsed
&& lastProgress
&& (currentElapsed - lastElapsed) < 0.7
&& (currentProgress - lastProgress) < 0.01
&& currentProgress < 1.0) {
return;
}
lastElapsed = currentElapsed;
lastProgress = currentProgress;
g_object_notify(G_OBJECT(download), "progress");
}
static void webkit_download_finished_loading(WebKitDownload* download)
{
webkit_download_close_stream(download);
WebKitDownloadPrivate* priv = download->priv;
g_timer_stop(priv->timer);
g_object_notify(G_OBJECT(download), "progress");
webkit_download_set_status(download, WEBKIT_DOWNLOAD_STATUS_FINISHED);
}
static void webkit_download_error(WebKitDownload* download, const ResourceError& error)
{
webkit_download_close_stream(download);
WebKitDownloadPrivate* priv = download->priv;
GRefPtr<WebKitDownload> protect(download);
g_timer_stop(priv->timer);
webkit_download_set_status(download, WEBKIT_DOWNLOAD_STATUS_ERROR);
gboolean handled;
g_signal_emit_by_name(download, "error", 0, WEBKIT_DOWNLOAD_ERROR_NETWORK, error.localizedDescription().utf8().data(), &handled);
}
DownloadClient::DownloadClient(WebKitDownload* download)
: m_download(download)
{
}
void DownloadClient::didReceiveResponse(ResourceHandle*, const ResourceResponse& response)
{
webkit_download_set_response(m_download, response);
}
void DownloadClient::didReceiveData(ResourceHandle*, const char* data, int length, int encodedDataLength)
{
webkit_download_received_data(m_download, data, length);
}
void DownloadClient::didFinishLoading(ResourceHandle*, double)
{
webkit_download_finished_loading(m_download);
}
void DownloadClient::didFail(ResourceHandle*, const ResourceError& error)
{
webkit_download_error(m_download, error);
}
void DownloadClient::wasBlocked(ResourceHandle*)
{
// FIXME: Implement this when we have the new frame loader signals
// and error handling.
notImplemented();
}
void DownloadClient::cannotShowURL(ResourceHandle*)
{
// FIXME: Implement this when we have the new frame loader signals
// and error handling.
notImplemented();
}