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