summaryrefslogtreecommitdiffstats
path: root/Source/WebKit/gtk/webkit/webkitdownload.cpp
diff options
context:
space:
mode:
authorBen Murdoch <benm@google.com>2011-05-13 16:23:25 +0100
committerBen Murdoch <benm@google.com>2011-05-16 11:35:02 +0100
commit65f03d4f644ce73618e5f4f50dd694b26f55ae12 (patch)
treef478babb801e720de7bfaee23443ffe029f58731 /Source/WebKit/gtk/webkit/webkitdownload.cpp
parent47de4a2fb7262c7ebdb9cd133ad2c54c187454d0 (diff)
downloadexternal_webkit-65f03d4f644ce73618e5f4f50dd694b26f55ae12.zip
external_webkit-65f03d4f644ce73618e5f4f50dd694b26f55ae12.tar.gz
external_webkit-65f03d4f644ce73618e5f4f50dd694b26f55ae12.tar.bz2
Merge WebKit at r75993: Initial merge by git.
Change-Id: I602bbdc3974787a3b0450456a30a7868286921c3
Diffstat (limited to 'Source/WebKit/gtk/webkit/webkitdownload.cpp')
-rw-r--r--Source/WebKit/gtk/webkit/webkitdownload.cpp956
1 files changed, 956 insertions, 0 deletions
diff --git a/Source/WebKit/gtk/webkit/webkitdownload.cpp b/Source/WebKit/gtk/webkit/webkitdownload.cpp
new file mode 100644
index 0000000..60b8e6d
--- /dev/null
+++ b/Source/WebKit/gtk/webkit/webkitdownload.cpp
@@ -0,0 +1,956 @@
+/*
+ * 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 Noncopyable, public ResourceHandleClient {
+ 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 lengthReceived)
+{
+ 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();
+}