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