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