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