From 95f06d80cd093c13be285049f7ff745a65de8fa5 Mon Sep 17 00:00:00 2001 From: Gustavo Noronha Silva Date: Thu, 12 Feb 2009 18:18:24 -0200 Subject: [PATCH] Implement download, and provide a nice object wrapping the download process --- ChangeLog | 8 + GNUmakefile.am | 2 + WebCore/ChangeLog | 11 + WebCore/platform/network/ResourceHandleInternal.h | 2 +- .../platform/network/soup/ResourceHandleSoup.cpp | 9 +- WebKit/gtk/ChangeLog | 54 ++ WebKit/gtk/WebCoreSupport/ContextMenuClientGtk.cpp | 17 +- WebKit/gtk/WebCoreSupport/FrameLoaderClientGtk.cpp | 29 +- WebKit/gtk/webkit/webkit.h | 1 + WebKit/gtk/webkit/webkitdefines.h | 3 + WebKit/gtk/webkit/webkitdownload.cpp | 786 ++++++++++++++++++++ WebKit/gtk/webkit/webkitdownload.h | 114 +++ WebKit/gtk/webkit/webkitprivate.h | 5 + WebKit/gtk/webkit/webkitwebview.cpp | 23 + WebKit/gtk/webkitmarshal.list | 2 + 15 files changed, 1057 insertions(+), 9 deletions(-) create mode 100644 WebKit/gtk/webkit/webkitdownload.cpp create mode 100644 WebKit/gtk/webkit/webkitdownload.h diff --git a/ChangeLog b/ChangeLog index 2e29890..99041db 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,13 @@ 2009-03-02 Gustavo Noronha Silva + Reviewed by NOBODY (OOPS!). + + Adding new files related to WebKitDownload to the GTK+ port. + + * GNUmakefile.am: + +2009-03-02 Gustavo Noronha Silva + Unreviewed build fix; adding missing files to EXTRA_DIST, so that they show up in the tarball. diff --git a/GNUmakefile.am b/GNUmakefile.am index b050c4c..d0e096c 100644 --- a/GNUmakefile.am +++ b/GNUmakefile.am @@ -304,6 +304,7 @@ endif webkitgtk_h_api += \ WebKit/gtk/webkit/webkit.h \ WebKit/gtk/webkit/webkitdefines.h \ + WebKit/gtk/webkit/webkitdownload.h \ WebKit/gtk/webkit/webkitnetworkrequest.h \ WebKit/gtk/webkit/webkitversion.h \ WebKit/gtk/webkit/webkitwebbackforwardlist.h \ @@ -348,6 +349,7 @@ webkitgtk_sources += \ WebKit/gtk/webkit/webkitwebnavigationaction.cpp \ WebKit/gtk/webkit/webkitwebpolicydecision.cpp \ WebKit/gtk/webkit/webkitwebsettings.cpp \ + WebKit/gtk/webkit/webkitdownload.cpp \ WebKit/gtk/webkit/webkitwebview.cpp \ WebKit/gtk/webkit/webkitwebwindowfeatures.cpp diff --git a/WebCore/ChangeLog b/WebCore/ChangeLog index 7e2284e..6270a81 100644 --- a/WebCore/ChangeLog +++ b/WebCore/ChangeLog @@ -1,5 +1,16 @@ 2009-03-02 Gustavo Noronha Silva + Reviewed by NOBODY (OOPS!). + + Make the Soup backend able to handle requests without a frame, + since we may have such things now that we support downloads. + + * platform/network/ResourceHandleInternal.h: + * platform/network/soup/ResourceHandleSoup.cpp: + (WebCore::ResourceHandle::start): + +2009-03-02 Gustavo Noronha Silva + Unreviewed build fix; adding missing files to EXTRA_DIST, so that they show up in the tarball. diff --git a/WebCore/platform/network/ResourceHandleInternal.h b/WebCore/platform/network/ResourceHandleInternal.h index 4e4417c..e501e7d 100644 --- a/WebCore/platform/network/ResourceHandleInternal.h +++ b/WebCore/platform/network/ResourceHandleInternal.h @@ -47,7 +47,7 @@ #if USE(SOUP) #include -class Frame; +class WebCore::Frame; #endif #if PLATFORM(QT) diff --git a/WebCore/platform/network/soup/ResourceHandleSoup.cpp b/WebCore/platform/network/soup/ResourceHandleSoup.cpp index 1ca657f..06edf23 100644 --- a/WebCore/platform/network/soup/ResourceHandleSoup.cpp +++ b/WebCore/platform/network/soup/ResourceHandleSoup.cpp @@ -545,9 +545,12 @@ bool ResourceHandle::start(Frame* frame) { ASSERT(!d->m_msg); - // If we are no longer attached to a Page, this must be an attempted load from an - // onUnload handler, so let's just block it. - if (!frame->page()) + + // The frame could be null is the ResourceHandle is not associated to any + // Frame, i.e. if we are downloading a file. + // If the frame is not null but the page is null this must be an attempted + // load from an onUnload handler, so let's just block it. + if (frame && !frame->page()) return false; KURL url = request().url(); diff --git a/WebKit/gtk/ChangeLog b/WebKit/gtk/ChangeLog index 65823c1..8dfd1dc 100644 --- a/WebKit/gtk/ChangeLog +++ b/WebKit/gtk/ChangeLog @@ -1,3 +1,57 @@ +2009-03-02 Gustavo Noronha Silva + + Reviewed by NOBODY (OOPS!). + + Implement download, and provide a nice object wrapping the + download process. Initial work done by Marco Barisione and + Pierre-Luc Beaudoin for Collabora. + + * WebCoreSupport/ContextMenuClientGtk.cpp: + (WebKit::ContextMenuClient::downloadURL): + * WebCoreSupport/FrameLoaderClientGtk.cpp: + (WebKit::FrameLoaderClient::download): + (WebKit::FrameLoaderClient::startDownload): + * webkit/webkit.h: + * webkit/webkitdefines.h: + * webkit/webkitdownload.cpp: Added. + (_WebKitDownloadPrivate::): + (_WebKitDownloadPrivate::webkit_download_dispose): + (_WebKitDownloadPrivate::webkit_download_finalize): + (_WebKitDownloadPrivate::webkit_download_get_property): + (_WebKitDownloadPrivate::webkit_download_set_property): + (_WebKitDownloadPrivate::webkit_download_class_init): + (_WebKitDownloadPrivate::webkit_download_init): + (_WebKitDownloadPrivate::webkit_download_new): + (_WebKitDownloadPrivate::webkit_download_open_stream_for_uri): + (_WebKitDownloadPrivate::webkit_download_close_stream): + (_WebKitDownloadPrivate::webkit_download_start): + (_WebKitDownloadPrivate::webkit_download_cancel): + (_WebKitDownloadPrivate::webkit_download_get_uri): + (_WebKitDownloadPrivate::webkit_download_get_network_request): + (_WebKitDownloadPrivate::webkit_download_set_response): + (_WebKitDownloadPrivate::webkit_download_get_suggested_filename): + (_WebKitDownloadPrivate::webkit_download_get_destination_uri): + (_WebKitDownloadPrivate::webkit_download_set_destination_uri): + (_WebKitDownloadPrivate::webkit_download_get_state): + (_WebKitDownloadPrivate::webkit_download_get_total_size): + (_WebKitDownloadPrivate::webkit_download_get_current_size): + (_WebKitDownloadPrivate::webkit_download_get_progress): + (_WebKitDownloadPrivate::webkit_download_get_elapsed_time): + (_WebKitDownloadPrivate::webkit_download_received_data): + (_WebKitDownloadPrivate::webkit_download_finished_loading): + (_WebKitDownloadPrivate::webkit_download_error): + (_WebKitDownloadPrivate::DownloadClient::DownloadClient): + (_WebKitDownloadPrivate::DownloadClient::didReceiveResponse): + (_WebKitDownloadPrivate::DownloadClient::didReceiveData): + (_WebKitDownloadPrivate::DownloadClient::didFinishLoading): + (_WebKitDownloadPrivate::DownloadClient::didFail): + (_WebKitDownloadPrivate::DownloadClient::wasBlocked): + (_WebKitDownloadPrivate::DownloadClient::cannotShowURL): + * webkit/webkitdownload.h: Added. + * webkit/webkitprivate.h: + * webkit/webkitwebview.cpp: + * webkitmarshal.list: + 2009-03-01 Jan Michael Alonzo Reviewed by Holger Freyther. diff --git a/WebKit/gtk/WebCoreSupport/ContextMenuClientGtk.cpp b/WebKit/gtk/WebCoreSupport/ContextMenuClientGtk.cpp index 490370c..42d4813 100644 --- a/WebKit/gtk/WebCoreSupport/ContextMenuClientGtk.cpp +++ b/WebKit/gtk/WebCoreSupport/ContextMenuClientGtk.cpp @@ -1,5 +1,6 @@ /* * Copyright (C) 2008 Nuanti Ltd. + * Copyright (C) 2009 Gustavo Noronha Silva * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -20,6 +21,7 @@ #include "ContextMenu.h" #include "ContextMenuClientGtk.h" +#include "CString.h" #include "HitTestResult.h" #include "KURL.h" #include "NotImplemented.h" @@ -163,7 +165,20 @@ void ContextMenuClient::contextMenuItemSelected(ContextMenuItem*, const ContextM void ContextMenuClient::downloadURL(const KURL& url) { - notImplemented(); + WebKitNetworkRequest* network_request = webkit_network_request_new(url.string().utf8().data()); + WebKitDownload* download = webkit_download_new(network_request); + g_object_unref(network_request); + + gboolean handled; + g_signal_emit_by_name(m_webView, "download-requested", download, &handled); + + if (!handled) { + webkit_download_cancel(download); + g_object_unref(download); + return; + } + + webkit_download_start(download); } void ContextMenuClient::copyImageToClipboard(const HitTestResult&) diff --git a/WebKit/gtk/WebCoreSupport/FrameLoaderClientGtk.cpp b/WebKit/gtk/WebCoreSupport/FrameLoaderClientGtk.cpp index 4044090..e417081 100644 --- a/WebKit/gtk/WebCoreSupport/FrameLoaderClientGtk.cpp +++ b/WebKit/gtk/WebCoreSupport/FrameLoaderClientGtk.cpp @@ -3,6 +3,7 @@ * Copyright (C) 2007, 2008 Holger Hans Peter Freyther * Copyright (C) 2007 Christian Dywan * Copyright (C) 2008 Collabora Ltd. All rights reserved. + * Copyright (C) 2009 Gustavo Noronha Silva * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -40,6 +41,8 @@ #include "MouseEvent.h" #include "NotImplemented.h" #include "PlatformString.h" +#include "ResourceHandle.h" +#include "ResourceHandleInternal.h" #include "PluginDatabase.h" #include "RenderPart.h" #include "ResourceRequest.h" @@ -774,9 +777,13 @@ void FrameLoaderClient::dispatchDidFailLoad(const ResourceError&) g_signal_emit_by_name(m_frame, "load-done", false); } -void FrameLoaderClient::download(ResourceHandle*, const ResourceRequest&, const ResourceRequest&, const ResourceResponse&) +void FrameLoaderClient::download(ResourceHandle* handle, const ResourceRequest& request, const ResourceRequest&, const ResourceResponse&) { - notImplemented(); + // we could reuse the same handle, but our replacing of the + // client seems to make this impossible; the main load fails + // and is stopped + handle->cancel(); + startDownload(request); } ResourceError FrameLoaderClient::cancelledError(const ResourceRequest&) @@ -862,9 +869,23 @@ void FrameLoaderClient::setMainDocumentError(DocumentLoader*, const ResourceErro } } -void FrameLoaderClient::startDownload(const ResourceRequest&) +void FrameLoaderClient::startDownload(const ResourceRequest& request) { - notImplemented(); + WebKitNetworkRequest* networkRequest = webkit_network_request_new(request.url().string().utf8().data()); + WebKitDownload* download = webkit_download_new(networkRequest); + g_object_unref(networkRequest); + + WebKitWebView* view = getViewFromFrame(m_frame); + gboolean handled; + g_signal_emit_by_name(view, "download-requested", download, &handled); + + if (!handled) { + webkit_download_cancel(download); + g_object_unref(download); + return; + } + + webkit_download_start(download); } void FrameLoaderClient::updateGlobalHistory() diff --git a/WebKit/gtk/webkit/webkit.h b/WebKit/gtk/webkit/webkit.h index 8c868a2..d334047 100644 --- a/WebKit/gtk/webkit/webkit.h +++ b/WebKit/gtk/webkit/webkit.h @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include diff --git a/WebKit/gtk/webkit/webkitdefines.h b/WebKit/gtk/webkit/webkitdefines.h index f94e710..b0ab5e9 100644 --- a/WebKit/gtk/webkit/webkitdefines.h +++ b/WebKit/gtk/webkit/webkitdefines.h @@ -68,6 +68,9 @@ typedef struct _WebKitWebWindowFeaturesClass WebKitWebWindowFeaturesClass; typedef struct _WebKitWebView WebKitWebView; typedef struct _WebKitWebViewClass WebKitWebViewClass; +typedef struct _WebKitDownload WebKitDownload; +typedef struct _WebKitDownloadClass WebKitDownloadClass; + G_END_DECLS #endif diff --git a/WebKit/gtk/webkit/webkitdownload.cpp b/WebKit/gtk/webkit/webkitdownload.cpp new file mode 100644 index 0000000..e01a0fa --- /dev/null +++ b/WebKit/gtk/webkit/webkitdownload.cpp @@ -0,0 +1,786 @@ +/* + * Copyright (C) 2008 Collabora Ltd. + * Copyright (C) 2009 Gustavo Noronha Silva + * + * 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 "CString.h" +#include "Noncopyable.h" +#include "NotImplemented.h" +#include "ResourceHandleClient.h" +#include "ResourceRequest.h" +#include "ResourceResponse.h" +#include "webkitmarshal.h" +#include "webkitdownload.h" +#include "webkitprivate.h" +#include + +using namespace WebKit; +using namespace WebCore; + +extern "C" { + +class DownloadClient : Noncopyable, public ResourceHandleClient { + public: + DownloadClient(WebKitDownload*); + + virtual void didReceiveResponse(ResourceHandle*, const ResourceResponse&); + virtual void didReceiveData(ResourceHandle*, const char*, int, int); + virtual void didFinishLoading(ResourceHandle*); + virtual void didFail(ResourceHandle*, const ResourceError&); + virtual void wasBlocked(ResourceHandle*); + virtual void cannotShowURL(ResourceHandle*); + + private: + WebKitDownload* m_download; +}; + +#define WEBKIT_DOWNLOAD_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), WEBKIT_TYPE_DOWNLOAD, WebKitDownloadPrivate)) + +struct _WebKitDownloadPrivate { + gchar* destination_uri; + gchar* suggested_filename; + guint current_size; + GTimer* timer; + WebKitDownloadState state; + GFileOutputStream* output_stream; + DownloadClient* download_client; + WebKitNetworkRequest* network_request; + WebCore::ResourceResponse* network_response; + RefPtr resource_handle; +}; + +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_CURRENT_SIZE, + PROP_TOTAL_SIZE, +}; + +G_DEFINE_TYPE(WebKitDownload, webkit_download, G_TYPE_OBJECT); + +static void webkit_download_dispose(GObject* object) +{ + WebKitDownload* download = WEBKIT_DOWNLOAD(object); + WebKitDownloadPrivate* priv = download->priv; + + if (priv->output_stream) { + g_object_unref(priv->output_stream); + priv->output_stream = NULL; + } + + if (priv->network_request) { + g_object_unref(priv->network_request); + priv->network_request = 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->resource_handle) { + if (priv->state == WEBKIT_DOWNLOAD_STATE_STARTED) { + priv->resource_handle->setClient(0); + priv->resource_handle->cancel(); + } + priv->resource_handle.release(); + } + + delete priv->download_client; + delete priv->network_response; + + g_timer_destroy(priv->timer); + + g_free(priv->destination_uri); + g_free(priv->suggested_filename); + + 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_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_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->network_request = (WebKitNetworkRequest*)g_value_dup_object(value); + /* This is safe as network-request is a construct only property and + * suggested_filename is initially null. + */ + priv->suggested_filename = g_path_get_basename(webkit_network_request_get_uri(priv->network_request)); + break; + case PROP_DESTINATION_URI: + webkit_download_set_destination_uri(download, g_value_get_string(value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + } +} + +static void webkit_download_class_init(WebKitDownloadClass* downloadClass) +{ + /* + * implementations of virtual methods + */ + 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; + + /** + * WebKitDownload::error: + * @download: the object on which the signal is emitted + * @current_bytes: the current count of bytes downloaded + * @total_bytes: the total bytes count in the downloaded file, aka file size. + * + * Indicates an error in the download. + * + * Since: 1.1.2 + */ + webkit_download_signals[ERROR] = g_signal_new("error", + G_TYPE_FROM_CLASS(downloadClass), + (GSignalFlags)(G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION), + 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: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. + * + * 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: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 = WEBKIT_DOWNLOAD_GET_PRIVATE(download); + download->priv = priv; + + priv->download_client = new DownloadClient(download); + priv->current_size = 0; + priv->state = WEBKIT_DOWNLOAD_STATE_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)); +} + +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->output_stream = g_file_append_to(file, G_FILE_CREATE_NONE, NULL, &error); + else + priv->output_stream = 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->output_stream) { + g_object_unref(priv->output_stream); + priv->output_stream = 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->destination_uri); + g_return_if_fail(priv->state == WEBKIT_DOWNLOAD_STATE_CREATED); + g_return_if_fail(priv->timer == NULL); + + if (priv->resource_handle) + priv->resource_handle->setClient(priv->download_client); + else { + /* FIXME use the actual request object when WebKitNetworkRequest is finished. */ + ResourceRequest request(webkit_network_request_get_uri(priv->network_request)); + priv->resource_handle = ResourceHandle::create(request, priv->download_client, 0, false, false, false); + } + + priv->timer = g_timer_new (); + webkit_download_open_stream_for_uri(download, priv->destination_uri); +} + +/** + * 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; + + g_timer_stop(priv->timer); + + if (priv->resource_handle) + priv->resource_handle->cancel(); + + priv->state = WEBKIT_DOWNLOAD_STATE_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->network_request); +} + +/** + * webkit_download_get_network_request: + * @download: the #WebKitDownload + * + * Retrieves the #WebKitNetworkRequest object that backs the download + * process. + * + * Returns: 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->network_request; +} + +static void webkit_download_set_response(WebKitDownload* download, const ResourceResponse& response) +{ + /* FIXME Use WebKitNetworkResponse when it's merged */ + WebKitDownloadPrivate* priv = download->priv; + priv->network_response = new ResourceResponse(response); +} + +/** + * 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; + return priv->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->destination_uri; +} + +/** + * 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->destination_uri && !strcmp(priv->destination_uri, destination_uri)) + return; + + /* FIXME can we have a better check? */ + if (priv->state != WEBKIT_DOWNLOAD_STATE_CREATED && priv->state != WEBKIT_DOWNLOAD_STATE_CANCELLED) { + ASSERT(priv->destination_uri); + + gboolean downloading = priv->output_stream != NULL; + if (downloading) + webkit_download_close_stream(download); + + GFile* src = g_file_new_for_uri(priv->destination_uri); + 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->destination_uri); + priv->destination_uri = 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->destination_uri); + priv->destination_uri = g_strdup(destination_uri); + } + + /* only notify change if everything went fine */ + g_object_notify(G_OBJECT(download), "destination-uri"); +} + +/** + * webkit_download_get_state: + * @download: the #WebKitDownload + * + * Obtains the current state of the download, as a + * #WebKitDownloadState. + * + * Returns: the current #WebKitDownloadState + * + * Since: 1.1.2 + */ +WebKitDownloadState webkit_download_get_state (WebKitDownload* download) +{ + g_return_val_if_fail(WEBKIT_IS_DOWNLOAD(download), WEBKIT_DOWNLOAD_STATE_ERROR); + + WebKitDownloadPrivate* priv = download->priv; + return priv->state; +} + +/** + * 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; + if (!priv->network_response) + return 0; + + return MAX(priv->current_size, priv->network_response->expectedContentLength()); +} + +/** + * 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->current_size; +} + +/** + * 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; + gdouble total_size = (gdouble)priv->network_response->expectedContentLength(); + + if (total_size == 0) + return 1.0; + + return ((gdouble)priv->current_size) / 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; + 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->current_size == 0) { + priv->state = WEBKIT_DOWNLOAD_STATE_STARTED; + } + + ASSERT(priv->output_stream); + + gsize bytes_written; + GError *error = NULL; + + g_output_stream_write_all(G_OUTPUT_STREAM(priv->output_stream), + 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->current_size += length; + g_object_notify(G_OBJECT(download), "current-size"); + + ASSERT(priv->network_response); + if (priv->current_size > priv->network_response->expectedContentLength()) + g_object_notify(G_OBJECT(download), "total-size"); + + /* FIXME throttle the number of updates? should we remove the + * previous g_object_notify()s if we are going to throttle the + * progress updates? */ + 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); + priv->state = WEBKIT_DOWNLOAD_STATE_FINISHED; + + g_object_notify(G_OBJECT(download), "progress"); +} + +static void webkit_download_error(WebKitDownload* download, const ResourceError& error) +{ + webkit_download_close_stream(download); + + WebKitDownloadPrivate* priv = download->priv; + + g_timer_stop(priv->timer); + priv->state = WEBKIT_DOWNLOAD_STATE_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*) +{ + webkit_download_finished_loading(m_download); +} + +void DownloadClient::didFail(ResourceHandle*, const ResourceError& error) +{ + webkit_download_error(m_download, error); +} + +void DownloadClient::wasBlocked(ResourceHandle*) +{ + /* FIXME when we have the new frame loader signals and error handling. */ + notImplemented(); +} + +void DownloadClient::cannotShowURL(ResourceHandle*) +{ + /* FIXME when we have the new frame loader signals and error handling. */ + notImplemented(); +} + +} diff --git a/WebKit/gtk/webkit/webkitdownload.h b/WebKit/gtk/webkit/webkitdownload.h new file mode 100644 index 0000000..0e8ec78 --- /dev/null +++ b/WebKit/gtk/webkit/webkitdownload.h @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2008 Collabora Ltd. + * Copyright (C) 2009 Gustavo Noronha Silva + * + * 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. + */ + +#ifndef WEBKIT_DOWNLOAD_H +#define WEBKIT_DOWNLOAD_H + +#include + +#include + +G_BEGIN_DECLS + +#define WEBKIT_TYPE_DOWNLOAD (webkit_download_get_type()) +#define WEBKIT_DOWNLOAD(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), WEBKIT_TYPE_DOWNLOAD, WebKitDownload)) +#define WEBKIT_DOWNLOAD_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), WEBKIT_TYPE_DOWNLOAD, WebKitDownloadClass)) +#define WEBKIT_IS_DOWNLOAD(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), WEBKIT_TYPE_DOWNLOAD)) +#define WEBKIT_IS_DOWNLOAD_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), WEBKIT_TYPE_DOWNLOAD)) +#define WEBKIT_DOWNLOAD_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), WEBKIT_TYPE_DOWNLOAD, WebKitDownloadClass)) + +typedef enum { + WEBKIT_DOWNLOAD_STATE_ERROR = -1, + WEBKIT_DOWNLOAD_STATE_CREATED = 0, + WEBKIT_DOWNLOAD_STATE_STARTED, + WEBKIT_DOWNLOAD_STATE_CANCELLED, + WEBKIT_DOWNLOAD_STATE_FINISHED +} WebKitDownloadState; + +typedef enum { + WEBKIT_DOWNLOAD_ERROR_CANCELLED_BY_USER, + WEBKIT_DOWNLOAD_ERROR_DESTINATION, + WEBKIT_DOWNLOAD_ERROR_NETWORK +} WebKitDownloadError; + +typedef struct _WebKitDownloadPrivate WebKitDownloadPrivate; + +struct _WebKitDownload { + GObject parent_instance; + + WebKitDownloadPrivate* priv; +}; + +struct _WebKitDownloadClass { + GObjectClass parent_class; + + /* Padding for future expansion */ + void (*_webkit_reserved0) (void); + void (*_webkit_reserved1) (void); + void (*_webkit_reserved2) (void); + void (*_webkit_reserved3) (void); +}; + +WEBKIT_API GType +webkit_download_get_type (void); + +WEBKIT_API WebKitDownload * +webkit_download_new (WebKitNetworkRequest *request); + +WEBKIT_API void +webkit_download_start (WebKitDownload *download); + +WEBKIT_API void +webkit_download_cancel (WebKitDownload *download); + +WEBKIT_API const gchar* +webkit_download_get_uri (WebKitDownload *download); + +WEBKIT_API WebKitNetworkRequest* +webkit_download_get_network_request (WebKitDownload *download); + +WEBKIT_API const gchar* +webkit_download_get_suggested_filename (WebKitDownload *download); + +WEBKIT_API const gchar* +webkit_download_get_destination_uri (WebKitDownload *download); + +WEBKIT_API void +webkit_download_set_destination_uri (WebKitDownload *download, + const gchar *destination_uri); + +WEBKIT_API gdouble +webkit_download_get_progress (WebKitDownload *download); + +WEBKIT_API gdouble +webkit_download_get_elapsed_time (WebKitDownload *download); + +WEBKIT_API guint64 +webkit_download_get_total_size (WebKitDownload *download); + +WEBKIT_API guint64 +webkit_download_get_current_size (WebKitDownload *download); + +WEBKIT_API WebKitDownloadState +webkit_download_get_state (WebKitDownload *download); + +G_END_DECLS + +#endif diff --git a/WebKit/gtk/webkit/webkitprivate.h b/WebKit/gtk/webkit/webkitprivate.h index ce4726d..9a602d7 100644 --- a/WebKit/gtk/webkit/webkitprivate.h +++ b/WebKit/gtk/webkit/webkitprivate.h @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include @@ -44,9 +45,13 @@ #include "InspectorClientGtk.h" #include "FrameLoaderClient.h" #include "WindowFeatures.h" +#include "ResourceHandle.h" +#include "ResourceResponse.h" #include +class DownloadClient; + namespace WebKit { WebKitWebView* getViewFromFrame(WebKitWebFrame*); diff --git a/WebKit/gtk/webkit/webkitwebview.cpp b/WebKit/gtk/webkit/webkitwebview.cpp index ee01895..acf8fb7 100644 --- a/WebKit/gtk/webkit/webkitwebview.cpp +++ b/WebKit/gtk/webkit/webkitwebview.cpp @@ -26,6 +26,7 @@ #include "config.h" +#include "webkitdownload.h" #include "webkitwebview.h" #include "webkitenumtypes.h" #include "webkitmarshal.h" @@ -134,6 +135,7 @@ enum { COPY_CLIPBOARD, PASTE_CLIPBOARD, CUT_CLIPBOARD, + DOWNLOAD_REQUESTED, LAST_SIGNAL }; @@ -1116,6 +1118,27 @@ static void webkit_web_view_class_init(WebKitWebViewClass* webViewClass) G_TYPE_POINTER, G_TYPE_POINTER); + /** + * WebKitWebView::download-requested: + * @web_view: the object on which the signal is emitted + * @download: the message text + * @return: TRUE if the download was handled. + * + * A new Download is being requested. By default, if the signal is + * not handled, the download is cancelled. + * + * Since: 1.1.2 + */ + webkit_web_view_signals[DOWNLOAD_REQUESTED] = g_signal_new("download-requested", + G_TYPE_FROM_CLASS(webViewClass), + (GSignalFlags)(G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION), + 0, + g_signal_accumulator_true_handled, + NULL, + webkit_marshal_BOOLEAN__OBJECT, + G_TYPE_BOOLEAN, 1, + G_TYPE_OBJECT); + /** * WebKitWebView::load-started: * @web_view: the object on which the signal is emitted diff --git a/WebKit/gtk/webkitmarshal.list b/WebKit/gtk/webkitmarshal.list index 8592b45..3aaf313 100644 --- a/WebKit/gtk/webkitmarshal.list +++ b/WebKit/gtk/webkitmarshal.list @@ -1,3 +1,5 @@ +BOOLEAN:INT,INT,STRING +BOOLEAN:OBJECT BOOLEAN:OBJECT,OBJECT,OBJECT,OBJECT BOOLEAN:OBJECT,OBJECT,STRING,OBJECT BOOLEAN:OBJECT,STRING -- 1.6.2.rc0