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