WebKit/gtk/webkit/webkitdownload.cpp
changeset 0 4f2f89ce4247
equal deleted inserted replaced
-1:000000000000 0:4f2f89ce4247
       
     1 /*
       
     2  * Copyright (C) 2008 Collabora Ltd.
       
     3  * Copyright (C) 2009 Gustavo Noronha Silva <gns@gnome.org>
       
     4  *
       
     5  * This library is free software; you can redistribute it and/or
       
     6  * modify it under the terms of the GNU Library General Public
       
     7  * License as published by the Free Software Foundation; either
       
     8  * version 2 of the License, or (at your option) any later version.
       
     9  *
       
    10  * This library is distributed in the hope that it will be useful,
       
    11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
       
    12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
       
    13  * Library General Public License for more details.
       
    14  *
       
    15  * You should have received a copy of the GNU Library General Public License
       
    16  * along with this library; see the file COPYING.LIB.  If not, write to
       
    17  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
       
    18  * Boston, MA 02110-1301, USA.
       
    19  */
       
    20 
       
    21 #include "config.h"
       
    22 
       
    23 #include <glib/gi18n-lib.h>
       
    24 #include "GRefPtr.h"
       
    25 #include "Noncopyable.h"
       
    26 #include "NotImplemented.h"
       
    27 #include "ResourceHandleClient.h"
       
    28 #include "ResourceHandleInternal.h"
       
    29 #include "ResourceRequest.h"
       
    30 #include "ResourceResponse.h"
       
    31 #include "webkitdownload.h"
       
    32 #include "webkitenumtypes.h"
       
    33 #include "webkitmarshal.h"
       
    34 #include "webkitnetworkresponse.h"
       
    35 #include "webkitprivate.h"
       
    36 #include <wtf/text/CString.h>
       
    37 
       
    38 #include <glib/gstdio.h>
       
    39 
       
    40 #ifdef ERROR
       
    41 #undef ERROR
       
    42 #endif
       
    43 
       
    44 using namespace WebKit;
       
    45 using namespace WebCore;
       
    46 
       
    47 /**
       
    48  * SECTION:webkitdownload
       
    49  * @short_description: Object used to communicate with the application when downloading.
       
    50  *
       
    51  * #WebKitDownload carries information about a download request,
       
    52  * including a #WebKitNetworkRequest object. The application may use
       
    53  * this object to control the download process, or to simply figure
       
    54  * out what is to be downloaded, and do it itself.
       
    55  */
       
    56 
       
    57 class DownloadClient : public Noncopyable, public ResourceHandleClient {
       
    58     public:
       
    59         DownloadClient(WebKitDownload*);
       
    60 
       
    61         virtual void didReceiveResponse(ResourceHandle*, const ResourceResponse&);
       
    62         virtual void didReceiveData(ResourceHandle*, const char*, int, int);
       
    63         virtual void didFinishLoading(ResourceHandle*);
       
    64         virtual void didFail(ResourceHandle*, const ResourceError&);
       
    65         virtual void wasBlocked(ResourceHandle*);
       
    66         virtual void cannotShowURL(ResourceHandle*);
       
    67 
       
    68     private:
       
    69         WebKitDownload* m_download;
       
    70 };
       
    71 
       
    72 #define WEBKIT_DOWNLOAD_GET_PRIVATE(obj)    (G_TYPE_INSTANCE_GET_PRIVATE((obj), WEBKIT_TYPE_DOWNLOAD, WebKitDownloadPrivate))
       
    73 
       
    74 struct _WebKitDownloadPrivate {
       
    75     gchar* destinationURI;
       
    76     gchar* suggestedFilename;
       
    77     guint64 currentSize;
       
    78     GTimer* timer;
       
    79     WebKitDownloadStatus status;
       
    80     GFileOutputStream* outputStream;
       
    81     DownloadClient* downloadClient;
       
    82     WebKitNetworkRequest* networkRequest;
       
    83     WebKitNetworkResponse* networkResponse;
       
    84     RefPtr<ResourceHandle> resourceHandle;
       
    85 };
       
    86 
       
    87 enum {
       
    88     // Normal signals.
       
    89     ERROR,
       
    90     LAST_SIGNAL
       
    91 };
       
    92 
       
    93 static guint webkit_download_signals[LAST_SIGNAL] = { 0 };
       
    94 
       
    95 enum {
       
    96     PROP_0,
       
    97 
       
    98     PROP_NETWORK_REQUEST,
       
    99     PROP_DESTINATION_URI,
       
   100     PROP_SUGGESTED_FILENAME,
       
   101     PROP_PROGRESS,
       
   102     PROP_STATUS,
       
   103     PROP_CURRENT_SIZE,
       
   104     PROP_TOTAL_SIZE,
       
   105     PROP_NETWORK_RESPONSE
       
   106 };
       
   107 
       
   108 G_DEFINE_TYPE(WebKitDownload, webkit_download, G_TYPE_OBJECT);
       
   109 
       
   110 
       
   111 static void webkit_download_set_response(WebKitDownload* download, const ResourceResponse& response);
       
   112 static void webkit_download_set_status(WebKitDownload* download, WebKitDownloadStatus status);
       
   113 
       
   114 static void webkit_download_dispose(GObject* object)
       
   115 {
       
   116     WebKitDownload* download = WEBKIT_DOWNLOAD(object);
       
   117     WebKitDownloadPrivate* priv = download->priv;
       
   118 
       
   119     if (priv->outputStream) {
       
   120         g_object_unref(priv->outputStream);
       
   121         priv->outputStream = NULL;
       
   122     }
       
   123 
       
   124     if (priv->networkRequest) {
       
   125         g_object_unref(priv->networkRequest);
       
   126         priv->networkRequest = NULL;
       
   127     }
       
   128 
       
   129     if (priv->networkResponse) {
       
   130         g_object_unref(priv->networkResponse);
       
   131         priv->networkResponse = NULL;
       
   132     }
       
   133 
       
   134     G_OBJECT_CLASS(webkit_download_parent_class)->dispose(object);
       
   135 }
       
   136 
       
   137 static void webkit_download_finalize(GObject* object)
       
   138 {
       
   139     WebKitDownload* download = WEBKIT_DOWNLOAD(object);
       
   140     WebKitDownloadPrivate* priv = download->priv;
       
   141 
       
   142     // We don't call webkit_download_cancel() because we don't want to emit
       
   143     // signals when finalizing an object.
       
   144     if (priv->resourceHandle) {
       
   145         if (priv->status == WEBKIT_DOWNLOAD_STATUS_STARTED) {
       
   146             priv->resourceHandle->setClient(0);
       
   147             priv->resourceHandle->cancel();
       
   148         }
       
   149         priv->resourceHandle.release();
       
   150     }
       
   151 
       
   152     delete priv->downloadClient;
       
   153 
       
   154     // The download object may never have _start called on it, so we
       
   155     // need to make sure timer is non-NULL.
       
   156     if (priv->timer) {
       
   157         g_timer_destroy(priv->timer);
       
   158         priv->timer = NULL;
       
   159     }
       
   160 
       
   161     g_free(priv->destinationURI);
       
   162     g_free(priv->suggestedFilename);
       
   163 
       
   164     G_OBJECT_CLASS(webkit_download_parent_class)->finalize(object);
       
   165 }
       
   166 
       
   167 static void webkit_download_get_property(GObject* object, guint prop_id, GValue* value, GParamSpec* pspec)
       
   168 {
       
   169     WebKitDownload* download = WEBKIT_DOWNLOAD(object);
       
   170 
       
   171     switch(prop_id) {
       
   172     case PROP_NETWORK_REQUEST:
       
   173         g_value_set_object(value, webkit_download_get_network_request(download));
       
   174         break;
       
   175     case PROP_NETWORK_RESPONSE:
       
   176         g_value_set_object(value, webkit_download_get_network_response(download));
       
   177         break;
       
   178     case PROP_DESTINATION_URI:
       
   179         g_value_set_string(value, webkit_download_get_destination_uri(download));
       
   180         break;
       
   181     case PROP_SUGGESTED_FILENAME:
       
   182         g_value_set_string(value, webkit_download_get_suggested_filename(download));
       
   183         break;
       
   184     case PROP_PROGRESS:
       
   185         g_value_set_double(value, webkit_download_get_progress(download));
       
   186         break;
       
   187     case PROP_STATUS:
       
   188         g_value_set_enum(value, webkit_download_get_status(download));
       
   189         break;
       
   190     case PROP_CURRENT_SIZE:
       
   191         g_value_set_uint64(value, webkit_download_get_current_size(download));
       
   192         break;
       
   193     case PROP_TOTAL_SIZE:
       
   194         g_value_set_uint64(value, webkit_download_get_total_size(download));
       
   195         break;
       
   196     default:
       
   197         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
       
   198     }
       
   199 }
       
   200 
       
   201 static void webkit_download_set_property(GObject* object, guint prop_id, const GValue* value, GParamSpec *pspec)
       
   202 {
       
   203     WebKitDownload* download = WEBKIT_DOWNLOAD(object);
       
   204     WebKitDownloadPrivate* priv = download->priv;
       
   205 
       
   206     switch(prop_id) {
       
   207     case PROP_NETWORK_REQUEST:
       
   208         priv->networkRequest = WEBKIT_NETWORK_REQUEST(g_value_dup_object(value));
       
   209         break;
       
   210     case PROP_NETWORK_RESPONSE:
       
   211         priv->networkResponse = WEBKIT_NETWORK_RESPONSE(g_value_dup_object(value));
       
   212         break;
       
   213     case PROP_DESTINATION_URI:
       
   214         webkit_download_set_destination_uri(download, g_value_get_string(value));
       
   215         break;
       
   216     case PROP_STATUS:
       
   217         webkit_download_set_status(download, static_cast<WebKitDownloadStatus>(g_value_get_enum(value)));
       
   218         break;
       
   219     default:
       
   220         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
       
   221     }
       
   222 }
       
   223 
       
   224 static void webkit_download_class_init(WebKitDownloadClass* downloadClass)
       
   225 {
       
   226     GObjectClass* objectClass = G_OBJECT_CLASS(downloadClass);
       
   227     objectClass->dispose = webkit_download_dispose;
       
   228     objectClass->finalize = webkit_download_finalize;
       
   229     objectClass->get_property = webkit_download_get_property;
       
   230     objectClass->set_property = webkit_download_set_property;
       
   231 
       
   232     webkit_init();
       
   233 
       
   234     /**
       
   235      * WebKitDownload::error:
       
   236      * @download: the object on which the signal is emitted
       
   237      * @error_code: the corresponding error code
       
   238      * @error_detail: detailed error code for the error, see
       
   239      * #WebKitDownloadError
       
   240      * @reason: a string describing the error
       
   241      *
       
   242      * Emitted when @download is interrupted either by user action or by
       
   243      * network errors, @error_detail will take any value of
       
   244      * #WebKitDownloadError.
       
   245      *
       
   246      * Since: 1.1.2
       
   247      */
       
   248     webkit_download_signals[ERROR] = g_signal_new("error",
       
   249             G_TYPE_FROM_CLASS(downloadClass),
       
   250             (GSignalFlags)G_SIGNAL_RUN_LAST,
       
   251             0,
       
   252             g_signal_accumulator_true_handled,
       
   253             NULL,
       
   254             webkit_marshal_BOOLEAN__INT_INT_STRING,
       
   255             G_TYPE_BOOLEAN, 3,
       
   256             G_TYPE_INT,
       
   257             G_TYPE_INT,
       
   258             G_TYPE_STRING);
       
   259 
       
   260     // Properties.
       
   261 
       
   262     /**
       
   263      * WebKitDownload:network-request
       
   264      *
       
   265      * The #WebKitNetworkRequest instance associated with the download.
       
   266      *
       
   267      * Since: 1.1.2
       
   268      */
       
   269     g_object_class_install_property(objectClass,
       
   270                                     PROP_NETWORK_REQUEST,
       
   271                                     g_param_spec_object("network-request",
       
   272                                                         _("Network Request"),
       
   273                                                         _("The network request for the URI that should be downloaded"),
       
   274                                                         WEBKIT_TYPE_NETWORK_REQUEST,
       
   275                                                         (GParamFlags)(WEBKIT_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)));
       
   276 
       
   277     /**
       
   278      * WebKitDownload:network-response
       
   279      *
       
   280      * The #WebKitNetworkResponse instance associated with the download.
       
   281      *
       
   282      * Since: 1.1.16
       
   283      */
       
   284     g_object_class_install_property(objectClass,
       
   285                                     PROP_NETWORK_RESPONSE,
       
   286                                     g_param_spec_object("network-response",
       
   287                                                         _("Network Response"),
       
   288                                                         _("The network response for the URI that should be downloaded"),
       
   289                                                         WEBKIT_TYPE_NETWORK_RESPONSE,
       
   290                                                         (GParamFlags)(WEBKIT_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)));
       
   291 
       
   292     /**
       
   293      * WebKitDownload:destination-uri
       
   294      *
       
   295      * The URI of the save location for this download.
       
   296      *
       
   297      * Since: 1.1.2
       
   298      */
       
   299     g_object_class_install_property(objectClass,
       
   300                                     PROP_DESTINATION_URI,
       
   301                                     g_param_spec_string("destination-uri",
       
   302                                                         _("Destination URI"),
       
   303                                                         _("The destination URI where to save the file"),
       
   304                                                         "",
       
   305                                                         WEBKIT_PARAM_READWRITE));
       
   306 
       
   307     /**
       
   308      * WebKitDownload:suggested-filename
       
   309      *
       
   310      * The file name suggested as default when saving
       
   311      *
       
   312      * Since: 1.1.2
       
   313      */
       
   314     g_object_class_install_property(objectClass,
       
   315                                     PROP_SUGGESTED_FILENAME,
       
   316                                     g_param_spec_string("suggested-filename",
       
   317                                                         _("Suggested Filename"),
       
   318                                                         _("The filename suggested as default when saving"),
       
   319                                                         "",
       
   320                                                         WEBKIT_PARAM_READABLE));
       
   321 
       
   322     /**
       
   323      * WebKitDownload:progress:
       
   324      *
       
   325      * Determines the current progress of the download. Notice that,
       
   326      * although the progress changes are reported as soon as possible,
       
   327      * the emission of the notify signal for this property is
       
   328      * throttled, for the benefit of download managers. If you care
       
   329      * about every update, use WebKitDownload:current-size.
       
   330      *
       
   331      * Since: 1.1.2
       
   332      */
       
   333     g_object_class_install_property(objectClass, PROP_PROGRESS,
       
   334                                     g_param_spec_double("progress",
       
   335                                                         _("Progress"),
       
   336                                                         _("Determines the current progress of the download"),
       
   337                                                         0.0, 1.0, 1.0,
       
   338                                                         WEBKIT_PARAM_READABLE));
       
   339 
       
   340     /**
       
   341      * WebKitDownload:status:
       
   342      *
       
   343      * Determines the current status of the download.
       
   344      *
       
   345      * Since: 1.1.2
       
   346      */
       
   347     g_object_class_install_property(objectClass, PROP_STATUS,
       
   348                                     g_param_spec_enum("status",
       
   349                                                       _("Status"),
       
   350                                                       _("Determines the current status of the download"),
       
   351                                                       WEBKIT_TYPE_DOWNLOAD_STATUS,
       
   352                                                       WEBKIT_DOWNLOAD_STATUS_CREATED,
       
   353                                                       WEBKIT_PARAM_READABLE));
       
   354 
       
   355     /**
       
   356      * WebKitDownload:current-size
       
   357      *
       
   358      * The length of the data already downloaded
       
   359      *
       
   360      * Since: 1.1.2
       
   361      */
       
   362     g_object_class_install_property(objectClass,
       
   363                                     PROP_CURRENT_SIZE,
       
   364                                     g_param_spec_uint64("current-size",
       
   365                                                         _("Current Size"),
       
   366                                                         _("The length of the data already downloaded"),
       
   367                                                         0, G_MAXUINT64, 0,
       
   368                                                         WEBKIT_PARAM_READABLE));
       
   369 
       
   370     /**
       
   371      * WebKitDownload:total-size
       
   372      *
       
   373      * The total size of the file
       
   374      *
       
   375      * Since: 1.1.2
       
   376      */
       
   377     g_object_class_install_property(objectClass,
       
   378                                     PROP_CURRENT_SIZE,
       
   379                                     g_param_spec_uint64("total-size",
       
   380                                                         _("Total Size"),
       
   381                                                         _("The total size of the file"),
       
   382                                                         0, G_MAXUINT64, 0,
       
   383                                                         WEBKIT_PARAM_READABLE));
       
   384 
       
   385     g_type_class_add_private(downloadClass, sizeof(WebKitDownloadPrivate));
       
   386 }
       
   387 
       
   388 static void webkit_download_init(WebKitDownload* download)
       
   389 {
       
   390     WebKitDownloadPrivate* priv = WEBKIT_DOWNLOAD_GET_PRIVATE(download);
       
   391     download->priv = priv;
       
   392 
       
   393     priv->downloadClient = new DownloadClient(download);
       
   394     priv->currentSize = 0;
       
   395     priv->status = WEBKIT_DOWNLOAD_STATUS_CREATED;
       
   396 }
       
   397 
       
   398 /**
       
   399  * webkit_download_new:
       
   400  * @request: a #WebKitNetworkRequest
       
   401  *
       
   402  * Creates a new #WebKitDownload object for the given
       
   403  * #WebKitNetworkRequest object.
       
   404  *
       
   405  * Returns: the new #WebKitDownload
       
   406  *
       
   407  * Since: 1.1.2
       
   408  */
       
   409 WebKitDownload* webkit_download_new(WebKitNetworkRequest* request)
       
   410 {
       
   411     g_return_val_if_fail(request, NULL);
       
   412 
       
   413     return WEBKIT_DOWNLOAD(g_object_new(WEBKIT_TYPE_DOWNLOAD, "network-request", request, NULL));
       
   414 }
       
   415 
       
   416 // Internal usage only
       
   417 WebKitDownload* webkit_download_new_with_handle(WebKitNetworkRequest* request, WebCore::ResourceHandle* handle, const WebCore::ResourceResponse& response)
       
   418 {
       
   419     g_return_val_if_fail(request, NULL);
       
   420 
       
   421     ResourceHandleInternal* d = handle->getInternal();
       
   422     if (d->m_msg)
       
   423         soup_session_pause_message(webkit_get_default_session(), d->m_msg);
       
   424 
       
   425     WebKitDownload* download = WEBKIT_DOWNLOAD(g_object_new(WEBKIT_TYPE_DOWNLOAD, "network-request", request, NULL));
       
   426     WebKitDownloadPrivate* priv = download->priv;
       
   427 
       
   428     handle->ref();
       
   429     priv->resourceHandle = handle;
       
   430 
       
   431     webkit_download_set_response(download, response);
       
   432 
       
   433     return download;
       
   434 }
       
   435 
       
   436 static gboolean webkit_download_open_stream_for_uri(WebKitDownload* download, const gchar* uri, gboolean append=FALSE)
       
   437 {
       
   438     g_return_val_if_fail(uri, FALSE);
       
   439 
       
   440     WebKitDownloadPrivate* priv = download->priv;
       
   441     GFile* file = g_file_new_for_uri(uri);
       
   442     GError* error = NULL;
       
   443 
       
   444     if (append)
       
   445         priv->outputStream = g_file_append_to(file, G_FILE_CREATE_NONE, NULL, &error);
       
   446     else
       
   447         priv->outputStream = g_file_replace(file, NULL, TRUE, G_FILE_CREATE_NONE, NULL, &error);
       
   448 
       
   449     g_object_unref(file);
       
   450 
       
   451     if (error) {
       
   452         gboolean handled;
       
   453         g_signal_emit_by_name(download, "error", 0, WEBKIT_DOWNLOAD_ERROR_DESTINATION, error->message, &handled);
       
   454         g_error_free(error);
       
   455         return FALSE;
       
   456     }
       
   457 
       
   458     return TRUE;
       
   459 }
       
   460 
       
   461 static void webkit_download_close_stream(WebKitDownload* download)
       
   462 {
       
   463     WebKitDownloadPrivate* priv = download->priv;
       
   464     if (priv->outputStream) {
       
   465         g_object_unref(priv->outputStream);
       
   466         priv->outputStream = NULL;
       
   467     }
       
   468 }
       
   469 
       
   470 /**
       
   471  * webkit_download_start:
       
   472  * @download: the #WebKitDownload
       
   473  *
       
   474  * Initiates the download. Notice that you must have set the
       
   475  * destination-uri property before calling this method.
       
   476  *
       
   477  * Since: 1.1.2
       
   478  */
       
   479 void webkit_download_start(WebKitDownload* download)
       
   480 {
       
   481     g_return_if_fail(WEBKIT_IS_DOWNLOAD(download));
       
   482 
       
   483     WebKitDownloadPrivate* priv = download->priv;
       
   484     g_return_if_fail(priv->destinationURI);
       
   485     g_return_if_fail(priv->status == WEBKIT_DOWNLOAD_STATUS_CREATED);
       
   486     g_return_if_fail(priv->timer == NULL);
       
   487 
       
   488     if (!priv->resourceHandle)
       
   489         priv->resourceHandle = ResourceHandle::create(core(priv->networkRequest), priv->downloadClient, 0, false, false);
       
   490     else {
       
   491         priv->resourceHandle->setClient(priv->downloadClient);
       
   492 
       
   493         ResourceHandleInternal* d = priv->resourceHandle->getInternal();
       
   494         if (d->m_msg)
       
   495             soup_session_unpause_message(webkit_get_default_session(), d->m_msg);
       
   496     }
       
   497 
       
   498     priv->timer = g_timer_new();
       
   499     webkit_download_open_stream_for_uri(download, priv->destinationURI);
       
   500 }
       
   501 
       
   502 /**
       
   503  * webkit_download_cancel:
       
   504  * @download: the #WebKitDownload
       
   505  *
       
   506  * Cancels the download. Calling this will not free the
       
   507  * #WebKitDownload object, so you still need to call
       
   508  * g_object_unref() on it, if you are the owner of a reference. Notice
       
   509  * that cancelling the download provokes the emission of the
       
   510  * WebKitDownload::error signal, reporting that the download was
       
   511  * cancelled.
       
   512  *
       
   513  * Since: 1.1.2
       
   514  */
       
   515 void webkit_download_cancel(WebKitDownload* download)
       
   516 {
       
   517     g_return_if_fail(WEBKIT_IS_DOWNLOAD(download));
       
   518 
       
   519     WebKitDownloadPrivate* priv = download->priv;
       
   520 
       
   521     // Cancel may be called even if start was not called, so we need
       
   522     // to make sure timer is non-NULL.
       
   523     if (priv->timer)
       
   524         g_timer_stop(priv->timer);
       
   525 
       
   526     if (priv->resourceHandle)
       
   527         priv->resourceHandle->cancel();
       
   528 
       
   529     webkit_download_set_status(download, WEBKIT_DOWNLOAD_STATUS_CANCELLED);
       
   530 
       
   531     gboolean handled;
       
   532     g_signal_emit_by_name(download, "error", 0, WEBKIT_DOWNLOAD_ERROR_CANCELLED_BY_USER, _("User cancelled the download"), &handled);
       
   533 }
       
   534 
       
   535 /**
       
   536  * webkit_download_get_uri:
       
   537  * @download: the #WebKitDownload
       
   538  *
       
   539  * Convenience method to retrieve the URI from the
       
   540  * #WebKitNetworkRequest which is being downloaded.
       
   541  *
       
   542  * Returns: the uri
       
   543  *
       
   544  * Since: 1.1.2
       
   545  */
       
   546 const gchar* webkit_download_get_uri(WebKitDownload* download)
       
   547 {
       
   548     g_return_val_if_fail(WEBKIT_IS_DOWNLOAD(download), NULL);
       
   549 
       
   550     WebKitDownloadPrivate* priv = download->priv;
       
   551     return webkit_network_request_get_uri(priv->networkRequest);
       
   552 }
       
   553 
       
   554 /**
       
   555  * webkit_download_get_network_request:
       
   556  * @download: the #WebKitDownload
       
   557  *
       
   558  * Retrieves the #WebKitNetworkRequest object that backs the download
       
   559  * process.
       
   560  *
       
   561  * Returns: the #WebKitNetworkRequest instance
       
   562  *
       
   563  * Since: 1.1.2
       
   564  */
       
   565 WebKitNetworkRequest* webkit_download_get_network_request(WebKitDownload* download)
       
   566 {
       
   567     g_return_val_if_fail(WEBKIT_IS_DOWNLOAD(download), NULL);
       
   568 
       
   569     WebKitDownloadPrivate* priv = download->priv;
       
   570     return priv->networkRequest;
       
   571 }
       
   572 
       
   573 /**
       
   574  * webkit_download_get_network_response:
       
   575  * @download: the #WebKitDownload
       
   576  *
       
   577  * Retrieves the #WebKitNetworkResponse object that backs the download
       
   578  * process.
       
   579  *
       
   580  * Returns: the #WebKitNetworkResponse instance
       
   581  *
       
   582  * Since: 1.1.16
       
   583  */
       
   584 WebKitNetworkResponse* webkit_download_get_network_response(WebKitDownload* download)
       
   585 {
       
   586     g_return_val_if_fail(WEBKIT_IS_DOWNLOAD(download), NULL);
       
   587 
       
   588     WebKitDownloadPrivate* priv = download->priv;
       
   589     return priv->networkResponse;
       
   590 }
       
   591 
       
   592 static void webkit_download_set_response(WebKitDownload* download, const ResourceResponse& response)
       
   593 {
       
   594     WebKitDownloadPrivate* priv = download->priv;
       
   595     priv->networkResponse = webkit_network_response_new_with_core_response(response);
       
   596 
       
   597     if (!response.isNull() && !response.suggestedFilename().isEmpty())
       
   598         webkit_download_set_suggested_filename(download, response.suggestedFilename().utf8().data());
       
   599 }
       
   600 
       
   601 /**
       
   602  * webkit_download_get_suggested_filename:
       
   603  * @download: the #WebKitDownload
       
   604  *
       
   605  * Retrieves the filename that was suggested by the server, or the one
       
   606  * derived by WebKit from the URI.
       
   607  *
       
   608  * Returns: the suggested filename
       
   609  *
       
   610  * Since: 1.1.2
       
   611  */
       
   612 const gchar* webkit_download_get_suggested_filename(WebKitDownload* download)
       
   613 {
       
   614     g_return_val_if_fail(WEBKIT_IS_DOWNLOAD(download), NULL);
       
   615 
       
   616     WebKitDownloadPrivate* priv = download->priv;
       
   617     if (priv->suggestedFilename)
       
   618         return priv->suggestedFilename;
       
   619 
       
   620     KURL url = KURL(KURL(), webkit_network_request_get_uri(priv->networkRequest));
       
   621     url.setQuery(String());
       
   622     url.removeFragmentIdentifier();
       
   623     priv->suggestedFilename = g_strdup(decodeURLEscapeSequences(url.lastPathComponent()).utf8().data());
       
   624     return priv->suggestedFilename;
       
   625 }
       
   626 
       
   627 // for internal use only
       
   628 void webkit_download_set_suggested_filename(WebKitDownload* download, const gchar* suggestedFilename)
       
   629 {
       
   630     WebKitDownloadPrivate* priv = download->priv;
       
   631     g_free(priv->suggestedFilename);
       
   632     priv->suggestedFilename = g_strdup(suggestedFilename);
       
   633 
       
   634     g_object_notify(G_OBJECT(download), "suggested-filename");
       
   635 }
       
   636 
       
   637 
       
   638 /**
       
   639  * webkit_download_get_destination_uri:
       
   640  * @download: the #WebKitDownload
       
   641  *
       
   642  * Obtains the URI to which the downloaded file will be written. This
       
   643  * must have been set by the application before calling
       
   644  * webkit_download_start(), and may be %NULL.
       
   645  *
       
   646  * Returns: the destination URI or %NULL
       
   647  *
       
   648  * Since: 1.1.2
       
   649  */
       
   650 const gchar* webkit_download_get_destination_uri(WebKitDownload* download)
       
   651 {
       
   652     g_return_val_if_fail(WEBKIT_IS_DOWNLOAD(download), NULL);
       
   653 
       
   654     WebKitDownloadPrivate* priv = download->priv;
       
   655     return priv->destinationURI;
       
   656 }
       
   657 
       
   658 /**
       
   659  * webkit_download_set_destination_uri:
       
   660  * @download: the #WebKitDownload
       
   661  * @destination_uri: the destination URI
       
   662  *
       
   663  * Defines the URI that should be used to save the downloaded file to.
       
   664  *
       
   665  * Since: 1.1.2
       
   666  */
       
   667 void webkit_download_set_destination_uri(WebKitDownload* download, const gchar* destination_uri)
       
   668 {
       
   669     g_return_if_fail(WEBKIT_IS_DOWNLOAD(download));
       
   670     g_return_if_fail(destination_uri);
       
   671 
       
   672     WebKitDownloadPrivate* priv = download->priv;
       
   673     if (priv->destinationURI && !strcmp(priv->destinationURI, destination_uri))
       
   674         return;
       
   675 
       
   676     if (priv->status != WEBKIT_DOWNLOAD_STATUS_CREATED && priv->status != WEBKIT_DOWNLOAD_STATUS_CANCELLED) {
       
   677         ASSERT(priv->destinationURI);
       
   678 
       
   679         gboolean downloading = priv->outputStream != NULL;
       
   680         if (downloading)
       
   681             webkit_download_close_stream(download);
       
   682 
       
   683         GFile* src = g_file_new_for_uri(priv->destinationURI);
       
   684         GFile* dest = g_file_new_for_uri(destination_uri);
       
   685         GError* error = NULL;
       
   686 
       
   687         g_file_move(src, dest, G_FILE_COPY_BACKUP, NULL, NULL, NULL, &error);
       
   688 
       
   689         g_object_unref(src);
       
   690         g_object_unref(dest);
       
   691 
       
   692         g_free(priv->destinationURI);
       
   693         priv->destinationURI = g_strdup(destination_uri);
       
   694 
       
   695         if (error) {
       
   696             gboolean handled;
       
   697             g_signal_emit_by_name(download, "error", 0, WEBKIT_DOWNLOAD_ERROR_DESTINATION, error->message, &handled);
       
   698             g_error_free(error);
       
   699             return;
       
   700         }
       
   701 
       
   702         if (downloading) {
       
   703             if (!webkit_download_open_stream_for_uri(download, destination_uri, TRUE)) {
       
   704                 webkit_download_cancel(download);
       
   705                 return;
       
   706             }
       
   707         }
       
   708     } else {
       
   709         g_free(priv->destinationURI);
       
   710         priv->destinationURI = g_strdup(destination_uri);
       
   711     }
       
   712 
       
   713     // Only notify change if everything went fine.
       
   714     g_object_notify(G_OBJECT(download), "destination-uri");
       
   715 }
       
   716 
       
   717 /**
       
   718  * webkit_download_get_status:
       
   719  * @download: the #WebKitDownload
       
   720  *
       
   721  * Obtains the current status of the download, as a
       
   722  * #WebKitDownloadStatus.
       
   723  *
       
   724  * Returns: the current #WebKitDownloadStatus
       
   725  *
       
   726  * Since: 1.1.2
       
   727  */
       
   728 WebKitDownloadStatus webkit_download_get_status(WebKitDownload* download)
       
   729 {
       
   730     g_return_val_if_fail(WEBKIT_IS_DOWNLOAD(download), WEBKIT_DOWNLOAD_STATUS_ERROR);
       
   731 
       
   732     WebKitDownloadPrivate* priv = download->priv;
       
   733     return priv->status;
       
   734 }
       
   735 
       
   736 static void webkit_download_set_status(WebKitDownload* download, WebKitDownloadStatus status)
       
   737 {
       
   738     g_return_if_fail(WEBKIT_IS_DOWNLOAD(download));
       
   739 
       
   740     WebKitDownloadPrivate* priv = download->priv;
       
   741     priv->status = status;
       
   742 
       
   743     g_object_notify(G_OBJECT(download), "status");
       
   744 }
       
   745 
       
   746 /**
       
   747  * webkit_download_get_total_size:
       
   748  * @download: the #WebKitDownload
       
   749  *
       
   750  * Returns the expected total size of the download. This is expected
       
   751  * because the server may provide incorrect or missing
       
   752  * Content-Length. Notice that this may grow over time, as it will be
       
   753  * always the same as current_size in the cases where current size
       
   754  * surpasses it.
       
   755  *
       
   756  * Returns: the expected total size of the downloaded file
       
   757  *
       
   758  * Since: 1.1.2
       
   759  */
       
   760 guint64 webkit_download_get_total_size(WebKitDownload* download)
       
   761 {
       
   762     g_return_val_if_fail(WEBKIT_IS_DOWNLOAD(download), 0);
       
   763 
       
   764     WebKitDownloadPrivate* priv = download->priv;
       
   765     SoupMessage* message = priv->networkResponse ? webkit_network_response_get_message(priv->networkResponse) : NULL;
       
   766 
       
   767     if (!message)
       
   768         return 0;
       
   769 
       
   770     return MAX(priv->currentSize, static_cast<guint64>(soup_message_headers_get_content_length(message->response_headers)));
       
   771 }
       
   772 
       
   773 /**
       
   774  * webkit_download_get_current_size:
       
   775  * @download: the #WebKitDownload
       
   776  *
       
   777  * Current already downloaded size.
       
   778  *
       
   779  * Returns: the already downloaded size
       
   780  *
       
   781  * Since: 1.1.2
       
   782  */
       
   783 guint64 webkit_download_get_current_size(WebKitDownload* download)
       
   784 {
       
   785     g_return_val_if_fail(WEBKIT_IS_DOWNLOAD(download), 0);
       
   786 
       
   787     WebKitDownloadPrivate* priv = download->priv;
       
   788     return priv->currentSize;
       
   789 }
       
   790 
       
   791 /**
       
   792  * webkit_download_get_progress:
       
   793  * @download: a #WebKitDownload
       
   794  *
       
   795  * Determines the current progress of the download.
       
   796  *
       
   797  * Returns: a #gdouble ranging from 0.0 to 1.0.
       
   798  *
       
   799  * Since: 1.1.2
       
   800  */
       
   801 gdouble webkit_download_get_progress(WebKitDownload* download)
       
   802 {
       
   803     g_return_val_if_fail(WEBKIT_IS_DOWNLOAD(download), 1.0);
       
   804 
       
   805     WebKitDownloadPrivate* priv = download->priv;
       
   806     if (!priv->networkResponse)
       
   807         return 0.0;
       
   808 
       
   809     gdouble total_size = static_cast<gdouble>(webkit_download_get_total_size(download));
       
   810 
       
   811     if (total_size == 0)
       
   812         return 1.0;
       
   813 
       
   814     return ((gdouble)priv->currentSize) / total_size;
       
   815 }
       
   816 
       
   817 /**
       
   818  * webkit_download_get_elapsed_time:
       
   819  * @download: a #WebKitDownload
       
   820  *
       
   821  * Elapsed time for the download in seconds, including any fractional
       
   822  * part. If the download is finished, had an error or was cancelled
       
   823  * this is the time between its start and the event.
       
   824  *
       
   825  * Returns: seconds since the download was started, as a #gdouble
       
   826  *
       
   827  * Since: 1.1.2
       
   828  */
       
   829 gdouble webkit_download_get_elapsed_time(WebKitDownload* download)
       
   830 {
       
   831     g_return_val_if_fail(WEBKIT_IS_DOWNLOAD(download), 0.0);
       
   832 
       
   833     WebKitDownloadPrivate* priv = download->priv;
       
   834     if (!priv->timer)
       
   835         return 0;
       
   836 
       
   837     return g_timer_elapsed(priv->timer, NULL);
       
   838 }
       
   839 
       
   840 static void webkit_download_received_data(WebKitDownload* download, const gchar* data, int length)
       
   841 {
       
   842     WebKitDownloadPrivate* priv = download->priv;
       
   843 
       
   844     if (priv->currentSize == 0)
       
   845         webkit_download_set_status(download, WEBKIT_DOWNLOAD_STATUS_STARTED);
       
   846 
       
   847     ASSERT(priv->outputStream);
       
   848 
       
   849     gsize bytes_written;
       
   850     GError* error = NULL;
       
   851 
       
   852     g_output_stream_write_all(G_OUTPUT_STREAM(priv->outputStream),
       
   853                               data, length, &bytes_written, NULL, &error);
       
   854 
       
   855     if (error) {
       
   856         gboolean handled;
       
   857         g_signal_emit_by_name(download, "error", 0, WEBKIT_DOWNLOAD_ERROR_DESTINATION, error->message, &handled);
       
   858         g_error_free(error);
       
   859         return;
       
   860     }
       
   861 
       
   862     priv->currentSize += length;
       
   863     g_object_notify(G_OBJECT(download), "current-size");
       
   864 
       
   865     ASSERT(priv->networkResponse);
       
   866     if (priv->currentSize > webkit_download_get_total_size(download))
       
   867         g_object_notify(G_OBJECT(download), "total-size");
       
   868 
       
   869     // Throttle progress notification to not consume high amounts of
       
   870     // CPU on fast links, except when the last notification occured
       
   871     // in more then 0.7 secs from now, or the last notified progress
       
   872     // is passed in 1% or we reached the end.
       
   873     static gdouble lastProgress = 0;
       
   874     static gdouble lastElapsed = 0;
       
   875     gdouble currentElapsed = g_timer_elapsed(priv->timer, NULL);
       
   876     gdouble currentProgress = webkit_download_get_progress(download);
       
   877 
       
   878     if (lastElapsed
       
   879         && lastProgress
       
   880         && (currentElapsed - lastElapsed) < 0.7
       
   881         && (currentProgress - lastProgress) < 0.01
       
   882         && currentProgress < 1.0) {
       
   883         return;
       
   884     }
       
   885     lastElapsed = currentElapsed;
       
   886     lastProgress = currentProgress;
       
   887 
       
   888     g_object_notify(G_OBJECT(download), "progress");
       
   889 }
       
   890 
       
   891 static void webkit_download_finished_loading(WebKitDownload* download)
       
   892 {
       
   893     webkit_download_close_stream(download);
       
   894 
       
   895     WebKitDownloadPrivate* priv = download->priv;
       
   896 
       
   897     g_timer_stop(priv->timer);
       
   898 
       
   899     g_object_notify(G_OBJECT(download), "progress");
       
   900     webkit_download_set_status(download, WEBKIT_DOWNLOAD_STATUS_FINISHED);
       
   901 }
       
   902 
       
   903 static void webkit_download_error(WebKitDownload* download, const ResourceError& error)
       
   904 {
       
   905     webkit_download_close_stream(download);
       
   906 
       
   907     WebKitDownloadPrivate* priv = download->priv;
       
   908     GRefPtr<WebKitDownload> protect(download);
       
   909 
       
   910     g_timer_stop(priv->timer);
       
   911     webkit_download_set_status(download, WEBKIT_DOWNLOAD_STATUS_ERROR);
       
   912 
       
   913     gboolean handled;
       
   914     g_signal_emit_by_name(download, "error", 0, WEBKIT_DOWNLOAD_ERROR_NETWORK, error.localizedDescription().utf8().data(), &handled);
       
   915 }
       
   916 
       
   917 DownloadClient::DownloadClient(WebKitDownload* download)
       
   918     : m_download(download)
       
   919 {
       
   920 }
       
   921 
       
   922 void DownloadClient::didReceiveResponse(ResourceHandle*, const ResourceResponse& response)
       
   923 {
       
   924     webkit_download_set_response(m_download, response);
       
   925 }
       
   926 
       
   927 void DownloadClient::didReceiveData(ResourceHandle*, const char* data, int length, int lengthReceived)
       
   928 {
       
   929     webkit_download_received_data(m_download, data, length);
       
   930 }
       
   931 
       
   932 void DownloadClient::didFinishLoading(ResourceHandle*)
       
   933 {
       
   934     webkit_download_finished_loading(m_download);
       
   935 }
       
   936 
       
   937 void DownloadClient::didFail(ResourceHandle*, const ResourceError& error)
       
   938 {
       
   939     webkit_download_error(m_download, error);
       
   940 }
       
   941 
       
   942 void DownloadClient::wasBlocked(ResourceHandle*)
       
   943 {
       
   944     // FIXME: Implement this when we have the new frame loader signals
       
   945     // and error handling.
       
   946     notImplemented();
       
   947 }
       
   948 
       
   949 void DownloadClient::cannotShowURL(ResourceHandle*)
       
   950 {
       
   951     // FIXME: Implement this when we have the new frame loader signals
       
   952     // and error handling.
       
   953     notImplemented();
       
   954 }