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