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 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 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 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 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 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 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 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 */ 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 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 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 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 */ 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 */ 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 */ 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 */ 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 */ 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 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 */ 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 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 */ 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 */ 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 */ 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 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 */ 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 */ 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 */ 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 */ 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 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 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 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 905 DownloadClient::DownloadClient(WebKitDownload* download) 906 : m_download(download) 907 { 908 } 909 910 void DownloadClient::didReceiveResponse(ResourceHandle*, const ResourceResponse& response) 911 { 912 webkit_download_set_response(m_download, response); 913 } 914 915 void DownloadClient::didReceiveData(ResourceHandle*, const char* data, int length, int lengthReceived) 916 { 917 webkit_download_received_data(m_download, data, length); 918 } 919 920 void DownloadClient::didFinishLoading(ResourceHandle*) 921 { 922 webkit_download_finished_loading(m_download); 923 } 924 925 void DownloadClient::didFail(ResourceHandle*, const ResourceError& error) 926 { 927 webkit_download_error(m_download, error); 928 } 929 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 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