webkitdownload.cpp revision 2daae5fd11344eaa88a0d92b0f6d65f8d2255c00
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#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 47using namespace WebKit; 48using 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 60class 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 76struct _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 89enum { 90 // Normal signals. 91 ERROR, 92 LAST_SIGNAL 93}; 94 95static guint webkit_download_signals[LAST_SIGNAL] = { 0 }; 96 97enum { 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 110G_DEFINE_TYPE(WebKitDownload, webkit_download, G_TYPE_OBJECT); 111 112 113static void webkit_download_set_response(WebKitDownload* download, const ResourceResponse& response); 114static void webkit_download_set_status(WebKitDownload* download, WebKitDownloadStatus status); 115 116static 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 139static 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 169static 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 203static 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 226static 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 390static 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 */ 411WebKitDownload* 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 419WebKitDownload* 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 438static 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 463static 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 */ 481void 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 */ 518void 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 */ 549const 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 */ 568WebKitNetworkRequest* 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 */ 587WebKitNetworkResponse* 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 595static 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 */ 615const 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 631void 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 */ 653const 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 */ 670void 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 */ 731WebKitDownloadStatus 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 739static 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 */ 763guint64 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 */ 786guint64 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 */ 804gdouble 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 */ 832gdouble 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 843static 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 894static 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 906static 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 920DownloadClient::DownloadClient(WebKitDownload* download) 921 : m_download(download) 922{ 923} 924 925void DownloadClient::didReceiveResponse(ResourceHandle*, const ResourceResponse& response) 926{ 927 webkit_download_set_response(m_download, response); 928} 929 930void DownloadClient::didReceiveData(ResourceHandle*, const char* data, int length, int encodedDataLength) 931{ 932 webkit_download_received_data(m_download, data, length); 933} 934 935void DownloadClient::didFinishLoading(ResourceHandle*, double) 936{ 937 webkit_download_finished_loading(m_download); 938} 939 940void DownloadClient::didFail(ResourceHandle*, const ResourceError& error) 941{ 942 webkit_download_error(m_download, error); 943} 944 945void DownloadClient::wasBlocked(ResourceHandle*) 946{ 947 // FIXME: Implement this when we have the new frame loader signals 948 // and error handling. 949 notImplemented(); 950} 951 952void DownloadClient::cannotShowURL(ResourceHandle*) 953{ 954 // FIXME: Implement this when we have the new frame loader signals 955 // and error handling. 956 notImplemented(); 957} 958