| /* |
| * Copyright (C) 2012, 2014 Igalia S.L. |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Library General Public |
| * License as published by the Free Software Foundation; either |
| * version 2 of the License, or (at your option) any later version. |
| * |
| * This library is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Library General Public License for more details. |
| * |
| * You should have received a copy of the GNU Library General Public License |
| * along with this library; see the file COPYING.LIB. If not, write to |
| * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
| * Boston, MA 02110-1301, USA. |
| */ |
| |
| #include "config.h" |
| #include "WebKitDownload.h" |
| |
| #include "DownloadProxy.h" |
| #include "WebErrors.h" |
| #include "WebKitDownloadPrivate.h" |
| #include "WebKitPrivate.h" |
| #include "WebKitURIRequestPrivate.h" |
| #include "WebKitURIResponsePrivate.h" |
| #include <WebCore/ResourceResponse.h> |
| #include <glib/gi18n-lib.h> |
| #include <wtf/glib/GRefPtr.h> |
| #include <wtf/glib/GUniquePtr.h> |
| #include <wtf/glib/WTFGType.h> |
| #include <wtf/text/CString.h> |
| |
| using namespace WebKit; |
| using namespace WebCore; |
| |
| /** |
| * SECTION: WebKitDownload |
| * @Short_description: Object used to communicate with the application when downloading |
| * @Title: WebKitDownload |
| * |
| * #WebKitDownload carries information about a download request and |
| * response, including a #WebKitURIRequest and a #WebKitURIResponse |
| * objects. The application may use this object to control the |
| * download process, or to simply figure out what is to be downloaded, |
| * and handle the download process itself. |
| * |
| */ |
| |
| enum { |
| RECEIVED_DATA, |
| FINISHED, |
| FAILED, |
| DECIDE_DESTINATION, |
| CREATED_DESTINATION, |
| |
| LAST_SIGNAL |
| }; |
| |
| enum { |
| PROP_0, |
| |
| PROP_DESTINATION, |
| PROP_RESPONSE, |
| PROP_ESTIMATED_PROGRESS, |
| PROP_ALLOW_OVERWRITE |
| }; |
| |
| struct _WebKitDownloadPrivate { |
| ~_WebKitDownloadPrivate() |
| { |
| if (webView) |
| g_object_remove_weak_pointer(G_OBJECT(webView), reinterpret_cast<void**>(&webView)); |
| } |
| |
| RefPtr<DownloadProxy> download; |
| |
| GRefPtr<WebKitURIRequest> request; |
| GRefPtr<WebKitURIResponse> response; |
| WebKitWebView* webView; |
| CString destinationURI; |
| guint64 currentSize; |
| bool isCancelled; |
| GUniquePtr<GTimer> timer; |
| gdouble lastProgress; |
| gdouble lastElapsed; |
| bool allowOverwrite; |
| }; |
| |
| static guint signals[LAST_SIGNAL] = { 0, }; |
| |
| WEBKIT_DEFINE_TYPE(WebKitDownload, webkit_download, G_TYPE_OBJECT) |
| |
| static void webkitDownloadSetProperty(GObject* object, guint propId, const GValue* value, GParamSpec* paramSpec) |
| { |
| WebKitDownload* download = WEBKIT_DOWNLOAD(object); |
| |
| switch (propId) { |
| case PROP_ALLOW_OVERWRITE: |
| webkit_download_set_allow_overwrite(download, g_value_get_boolean(value)); |
| break; |
| default: |
| G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propId, paramSpec); |
| } |
| } |
| |
| static void webkitDownloadGetProperty(GObject* object, guint propId, GValue* value, GParamSpec* paramSpec) |
| { |
| WebKitDownload* download = WEBKIT_DOWNLOAD(object); |
| |
| switch (propId) { |
| case PROP_DESTINATION: |
| g_value_set_string(value, webkit_download_get_destination(download)); |
| break; |
| case PROP_RESPONSE: |
| g_value_set_object(value, webkit_download_get_response(download)); |
| break; |
| case PROP_ESTIMATED_PROGRESS: |
| g_value_set_double(value, webkit_download_get_estimated_progress(download)); |
| break; |
| case PROP_ALLOW_OVERWRITE: |
| g_value_set_boolean(value, webkit_download_get_allow_overwrite(download)); |
| break; |
| default: |
| G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propId, paramSpec); |
| } |
| } |
| |
| static gboolean webkitDownloadDecideDestination(WebKitDownload* download, const gchar* suggestedFilename) |
| { |
| if (!download->priv->destinationURI.isNull()) |
| return FALSE; |
| |
| GUniquePtr<char> filename(g_strdelimit(g_strdup(suggestedFilename), G_DIR_SEPARATOR_S, '_')); |
| const gchar *downloadsDir = g_get_user_special_dir(G_USER_DIRECTORY_DOWNLOAD); |
| if (!downloadsDir) { |
| // If we don't have XDG user dirs info, set just to HOME. |
| downloadsDir = g_get_home_dir(); |
| } |
| GUniquePtr<char> destination(g_build_filename(downloadsDir, filename.get(), NULL)); |
| GUniquePtr<char> destinationURI(g_filename_to_uri(destination.get(), 0, 0)); |
| download->priv->destinationURI = destinationURI.get(); |
| g_object_notify(G_OBJECT(download), "destination"); |
| return TRUE; |
| } |
| |
| static void webkit_download_class_init(WebKitDownloadClass* downloadClass) |
| { |
| GObjectClass* objectClass = G_OBJECT_CLASS(downloadClass); |
| objectClass->set_property = webkitDownloadSetProperty; |
| objectClass->get_property = webkitDownloadGetProperty; |
| |
| downloadClass->decide_destination = webkitDownloadDecideDestination; |
| |
| /** |
| * WebKitDownload:destination: |
| * |
| * The local URI to where the download will be saved. |
| */ |
| g_object_class_install_property(objectClass, |
| PROP_DESTINATION, |
| g_param_spec_string("destination", |
| _("Destination"), |
| _("The local URI to where the download will be saved"), |
| 0, |
| WEBKIT_PARAM_READABLE)); |
| |
| /** |
| * WebKitDownload:response: |
| * |
| * The #WebKitURIResponse associated with this download. |
| */ |
| g_object_class_install_property(objectClass, |
| PROP_RESPONSE, |
| g_param_spec_object("response", |
| _("Response"), |
| _("The response of the download"), |
| WEBKIT_TYPE_URI_RESPONSE, |
| WEBKIT_PARAM_READABLE)); |
| |
| /** |
| * WebKitDownload:estimated-progress: |
| * |
| * An estimate of the percent completion for the download operation. |
| * This value will range from 0.0 to 1.0. The value is an estimate |
| * based on the total number of bytes expected to be received for |
| * a download. |
| * If you need a more accurate progress information you can connect to |
| * #WebKitDownload::received-data signal to track the progress. |
| */ |
| g_object_class_install_property(objectClass, |
| PROP_ESTIMATED_PROGRESS, |
| g_param_spec_double("estimated-progress", |
| _("Estimated Progress"), |
| _("Determines the current progress of the download"), |
| 0.0, 1.0, 1.0, |
| WEBKIT_PARAM_READABLE)); |
| |
| /** |
| * WebKitDownload:allow-overwrite: |
| * |
| * Whether or not the download is allowed to overwrite an existing file on |
| * disk. If this property is %FALSE and the destination already exists, |
| * the download will fail. |
| * |
| * Since: 2.6 |
| */ |
| g_object_class_install_property( |
| objectClass, |
| PROP_ALLOW_OVERWRITE, |
| g_param_spec_boolean( |
| "allow-overwrite", |
| _("Allow Overwrite"), |
| _("Whether the destination may be overwritten"), |
| FALSE, |
| WEBKIT_PARAM_READWRITE)); |
| |
| /** |
| * WebKitDownload::received-data: |
| * @download: the #WebKitDownload |
| * @data_length: the length of data received in bytes |
| * |
| * This signal is emitted after response is received, |
| * every time new data has been written to the destination. It's |
| * useful to know the progress of the download operation. |
| */ |
| signals[RECEIVED_DATA] = g_signal_new( |
| "received-data", |
| G_TYPE_FROM_CLASS(objectClass), |
| G_SIGNAL_RUN_LAST, |
| 0, nullptr, nullptr, |
| g_cclosure_marshal_generic, |
| G_TYPE_NONE, 1, |
| G_TYPE_UINT64); |
| |
| /** |
| * WebKitDownload::finished: |
| * @download: the #WebKitDownload |
| * |
| * This signal is emitted when download finishes successfully or due to an error. |
| * In case of errors #WebKitDownload::failed signal is emitted before this one. |
| */ |
| signals[FINISHED] = |
| g_signal_new("finished", |
| G_TYPE_FROM_CLASS(objectClass), |
| G_SIGNAL_RUN_LAST, |
| 0, 0, 0, |
| g_cclosure_marshal_VOID__VOID, |
| G_TYPE_NONE, 0); |
| |
| /** |
| * WebKitDownload::failed: |
| * @download: the #WebKitDownload |
| * @error: the #GError that was triggered |
| * |
| * This signal is emitted when an error occurs during the download |
| * operation. The given @error, of the domain %WEBKIT_DOWNLOAD_ERROR, |
| * contains further details of the failure. If the download is cancelled |
| * with webkit_download_cancel(), this signal is emitted with error |
| * %WEBKIT_DOWNLOAD_ERROR_CANCELLED_BY_USER. The download operation finishes |
| * after an error and #WebKitDownload::finished signal is emitted after this one. |
| */ |
| signals[FAILED] = |
| g_signal_new( |
| "failed", |
| G_TYPE_FROM_CLASS(objectClass), |
| G_SIGNAL_RUN_LAST, |
| 0, 0, 0, |
| g_cclosure_marshal_VOID__BOXED, |
| G_TYPE_NONE, 1, |
| G_TYPE_ERROR | G_SIGNAL_TYPE_STATIC_SCOPE); |
| |
| /** |
| * WebKitDownload::decide-destination: |
| * @download: the #WebKitDownload |
| * @suggested_filename: the filename suggested for the download |
| * |
| * This signal is emitted after response is received to |
| * decide a destination URI for the download. If this signal is not |
| * handled the file will be downloaded to %G_USER_DIRECTORY_DOWNLOAD |
| * directory using @suggested_filename. |
| * |
| * Returns: %TRUE to stop other handlers from being invoked for the event. |
| * %FALSE to propagate the event further. |
| */ |
| signals[DECIDE_DESTINATION] = g_signal_new( |
| "decide-destination", |
| G_TYPE_FROM_CLASS(objectClass), |
| G_SIGNAL_RUN_LAST, |
| G_STRUCT_OFFSET(WebKitDownloadClass, decide_destination), |
| g_signal_accumulator_true_handled, NULL, |
| g_cclosure_marshal_generic, |
| G_TYPE_BOOLEAN, 1, |
| G_TYPE_STRING); |
| |
| /** |
| * WebKitDownload::created-destination: |
| * @download: the #WebKitDownload |
| * @destination: the destination URI |
| * |
| * This signal is emitted after #WebKitDownload::decide-destination and before |
| * #WebKitDownload::received-data to notify that destination file has been |
| * created successfully at @destination. |
| */ |
| signals[CREATED_DESTINATION] = |
| g_signal_new( |
| "created-destination", |
| G_TYPE_FROM_CLASS(objectClass), |
| G_SIGNAL_RUN_LAST, |
| 0, 0, 0, |
| g_cclosure_marshal_VOID__STRING, |
| G_TYPE_NONE, 1, |
| G_TYPE_STRING); |
| } |
| |
| WebKitDownload* webkitDownloadCreate(DownloadProxy* downloadProxy) |
| { |
| ASSERT(downloadProxy); |
| WebKitDownload* download = WEBKIT_DOWNLOAD(g_object_new(WEBKIT_TYPE_DOWNLOAD, NULL)); |
| download->priv->download = downloadProxy; |
| return download; |
| } |
| |
| static void webkitDownloadUpdateRequest(WebKitDownload* download) |
| { |
| download->priv->request = adoptGRef(webkitURIRequestCreateForResourceRequest(download->priv->download->request())); |
| } |
| |
| void webkitDownloadStarted(WebKitDownload* download) |
| { |
| // Update with the final request if needed. |
| if (download->priv->request) |
| webkitDownloadUpdateRequest(download); |
| } |
| |
| void webkitDownloadSetResponse(WebKitDownload* download, WebKitURIResponse* response) |
| { |
| download->priv->response = response; |
| g_object_notify(G_OBJECT(download), "response"); |
| } |
| |
| void webkitDownloadSetWebView(WebKitDownload* download, WebKitWebView* webView) |
| { |
| download->priv->webView = webView; |
| g_object_add_weak_pointer(G_OBJECT(webView), reinterpret_cast<void**>(&download->priv->webView)); |
| } |
| |
| bool webkitDownloadIsCancelled(WebKitDownload* download) |
| { |
| return download->priv->isCancelled; |
| } |
| |
| void webkitDownloadNotifyProgress(WebKitDownload* download, guint64 bytesReceived) |
| { |
| WebKitDownloadPrivate* priv = download->priv; |
| if (priv->isCancelled) |
| return; |
| |
| if (!download->priv->timer) |
| download->priv->timer.reset(g_timer_new()); |
| |
| priv->currentSize += bytesReceived; |
| g_signal_emit(download, signals[RECEIVED_DATA], 0, bytesReceived); |
| |
| // Throttle progress notification to not consume high amounts of |
| // CPU on fast links, except when the last notification occurred |
| // more than 0.016 secs ago (60 FPS), or the last notified progress |
| // is passed in 1% or we reached the end. |
| gdouble currentElapsed = g_timer_elapsed(priv->timer.get(), 0); |
| gdouble currentProgress = webkit_download_get_estimated_progress(download); |
| |
| if (priv->lastElapsed |
| && priv->lastProgress |
| && (currentElapsed - priv->lastElapsed) < 0.016 |
| && (currentProgress - priv->lastProgress) < 0.01 |
| && currentProgress < 1.0) { |
| return; |
| } |
| priv->lastElapsed = currentElapsed; |
| priv->lastProgress = currentProgress; |
| g_object_notify(G_OBJECT(download), "estimated-progress"); |
| } |
| |
| void webkitDownloadFailed(WebKitDownload* download, const ResourceError& resourceError) |
| { |
| GUniquePtr<GError> webError(g_error_new_literal(g_quark_from_string(resourceError.domain().utf8().data()), |
| toWebKitError(resourceError.errorCode()), resourceError.localizedDescription().utf8().data())); |
| if (download->priv->timer) |
| g_timer_stop(download->priv->timer.get()); |
| |
| g_signal_emit(download, signals[FAILED], 0, webError.get()); |
| g_signal_emit(download, signals[FINISHED], 0, NULL); |
| } |
| |
| void webkitDownloadCancelled(WebKitDownload* download) |
| { |
| WebKitDownloadPrivate* priv = download->priv; |
| webkitDownloadFailed(download, downloadCancelledByUserError(priv->response ? webkitURIResponseGetResourceResponse(priv->response.get()) : ResourceResponse())); |
| } |
| |
| void webkitDownloadFinished(WebKitDownload* download) |
| { |
| if (download->priv->isCancelled) { |
| // Since cancellation is asynchronous, didFinish might be called even |
| // if the download was cancelled. User cancelled the download, |
| // so we should fail with cancelled error even if the download |
| // actually finished successfully. |
| webkitDownloadCancelled(download); |
| return; |
| } |
| if (download->priv->timer) |
| g_timer_stop(download->priv->timer.get()); |
| g_signal_emit(download, signals[FINISHED], 0, NULL); |
| } |
| |
| String webkitDownloadDecideDestinationWithSuggestedFilename(WebKitDownload* download, const CString& suggestedFilename, bool& allowOverwrite) |
| { |
| if (download->priv->isCancelled) |
| return emptyString(); |
| gboolean returnValue; |
| g_signal_emit(download, signals[DECIDE_DESTINATION], 0, suggestedFilename.data(), &returnValue); |
| allowOverwrite = download->priv->allowOverwrite; |
| GUniquePtr<char> destinationPath(g_filename_from_uri(download->priv->destinationURI.data(), nullptr, nullptr)); |
| if (!destinationPath) |
| return emptyString(); |
| return String::fromUTF8(destinationPath.get()); |
| } |
| |
| void webkitDownloadDestinationCreated(WebKitDownload* download, const String& destinationPath) |
| { |
| if (download->priv->isCancelled) |
| return; |
| GUniquePtr<char> destinationURI(g_filename_to_uri(destinationPath.utf8().data(), nullptr, nullptr)); |
| ASSERT(destinationURI); |
| g_signal_emit(download, signals[CREATED_DESTINATION], 0, destinationURI.get()); |
| } |
| |
| /** |
| * webkit_download_get_request: |
| * @download: a #WebKitDownload |
| * |
| * Retrieves the #WebKitURIRequest object that backs the download |
| * process. |
| * |
| * Returns: (transfer none): the #WebKitURIRequest of @download |
| */ |
| WebKitURIRequest* webkit_download_get_request(WebKitDownload* download) |
| { |
| g_return_val_if_fail(WEBKIT_IS_DOWNLOAD(download), nullptr); |
| |
| WebKitDownloadPrivate* priv = download->priv; |
| if (!priv->request) |
| webkitDownloadUpdateRequest(download); |
| return priv->request.get(); |
| } |
| |
| /** |
| * webkit_download_get_destination: |
| * @download: a #WebKitDownload |
| * |
| * Obtains the URI to which the downloaded file will be written. You |
| * can connect to #WebKitDownload::created-destination to make |
| * sure this method returns a valid destination. |
| * |
| * Returns: the destination URI or %NULL |
| */ |
| const gchar* webkit_download_get_destination(WebKitDownload* download) |
| { |
| g_return_val_if_fail(WEBKIT_IS_DOWNLOAD(download), 0); |
| |
| return download->priv->destinationURI.data(); |
| } |
| |
| /** |
| * webkit_download_set_destination: |
| * @download: a #WebKitDownload |
| * @uri: the destination URI |
| * |
| * Sets the URI to which the downloaded file will be written. |
| * This method should be called before the download transfer |
| * starts or it will not have any effect on the ongoing download |
| * operation. To set the destination using the filename suggested |
| * by the server connect to #WebKitDownload::decide-destination |
| * signal and call webkit_download_set_destination(). If you want to |
| * set a fixed destination URI that doesn't depend on the suggested |
| * filename you can connect to notify::response signal and call |
| * webkit_download_set_destination(). |
| * If #WebKitDownload::decide-destination signal is not handled |
| * and destination URI is not set when the download transfer starts, |
| * the file will be saved with the filename suggested by the server in |
| * %G_USER_DIRECTORY_DOWNLOAD directory. |
| */ |
| void webkit_download_set_destination(WebKitDownload* download, const gchar* uri) |
| { |
| g_return_if_fail(WEBKIT_IS_DOWNLOAD(download)); |
| g_return_if_fail(uri); |
| |
| WebKitDownloadPrivate* priv = download->priv; |
| if (priv->destinationURI == uri) |
| return; |
| |
| priv->destinationURI = uri; |
| g_object_notify(G_OBJECT(download), "destination"); |
| } |
| |
| /** |
| * webkit_download_get_response: |
| * @download: a #WebKitDownload |
| * |
| * Retrieves the #WebKitURIResponse object that backs the download |
| * process. This method returns %NULL if called before the response |
| * is received from the server. You can connect to notify::response |
| * signal to be notified when the response is received. |
| * |
| * Returns: (transfer none): the #WebKitURIResponse, or %NULL if |
| * the response hasn't been received yet. |
| */ |
| WebKitURIResponse* webkit_download_get_response(WebKitDownload* download) |
| { |
| g_return_val_if_fail(WEBKIT_IS_DOWNLOAD(download), 0); |
| |
| return download->priv->response.get(); |
| } |
| |
| /** |
| * webkit_download_cancel: |
| * @download: a #WebKitDownload |
| * |
| * Cancels the download. When the ongoing download |
| * operation is effectively cancelled the signal |
| * #WebKitDownload::failed is emitted with |
| * %WEBKIT_DOWNLOAD_ERROR_CANCELLED_BY_USER error. |
| */ |
| void webkit_download_cancel(WebKitDownload* download) |
| { |
| g_return_if_fail(WEBKIT_IS_DOWNLOAD(download)); |
| |
| download->priv->isCancelled = true; |
| download->priv->download->cancel(); |
| } |
| |
| /** |
| * webkit_download_get_estimated_progress: |
| * @download: a #WebKitDownload |
| * |
| * Gets the value of the #WebKitDownload:estimated-progress property. |
| * You can monitor the estimated progress of the download operation by |
| * connecting to the notify::estimated-progress signal of @download. |
| * |
| * Returns: an estimate of the of the percent complete for a download |
| * as a range from 0.0 to 1.0. |
| */ |
| gdouble webkit_download_get_estimated_progress(WebKitDownload* download) |
| { |
| g_return_val_if_fail(WEBKIT_IS_DOWNLOAD(download), 0); |
| |
| WebKitDownloadPrivate* priv = download->priv; |
| if (!priv->response) |
| return 0; |
| |
| guint64 contentLength = webkit_uri_response_get_content_length(priv->response.get()); |
| if (!contentLength) |
| return 0; |
| |
| return static_cast<gdouble>(priv->currentSize) / static_cast<gdouble>(contentLength); |
| } |
| |
| /** |
| * webkit_download_get_elapsed_time: |
| * @download: a #WebKitDownload |
| * |
| * Gets the elapsed time in seconds, including any fractional part. |
| * If the download finished, had an error or was cancelled this is |
| * the time between its start and the event. |
| * |
| * Returns: seconds since the download was started |
| */ |
| gdouble webkit_download_get_elapsed_time(WebKitDownload* download) |
| { |
| g_return_val_if_fail(WEBKIT_IS_DOWNLOAD(download), 0); |
| |
| WebKitDownloadPrivate* priv = download->priv; |
| if (!priv->timer) |
| return 0; |
| |
| return g_timer_elapsed(priv->timer.get(), 0); |
| } |
| |
| /** |
| * webkit_download_get_received_data_length: |
| * @download: a #WebKitDownload |
| * |
| * Gets the length of the data already downloaded for @download |
| * in bytes. |
| * |
| * Returns: the amount of bytes already downloaded. |
| */ |
| guint64 webkit_download_get_received_data_length(WebKitDownload* download) |
| { |
| g_return_val_if_fail(WEBKIT_IS_DOWNLOAD(download), 0); |
| |
| return download->priv->currentSize; |
| } |
| |
| /** |
| * webkit_download_get_web_view: |
| * @download: a #WebKitDownload |
| * |
| * Get the #WebKitWebView that initiated the download. |
| * |
| * Returns: (transfer none): the #WebKitWebView that initiated @download, |
| * or %NULL if @download was not initiated by a #WebKitWebView. |
| */ |
| WebKitWebView* webkit_download_get_web_view(WebKitDownload* download) |
| { |
| g_return_val_if_fail(WEBKIT_IS_DOWNLOAD(download), 0); |
| |
| return download->priv->webView; |
| } |
| |
| /** |
| * webkit_download_get_allow_overwrite: |
| * @download: a #WebKitDownload |
| * |
| * Returns the current value of the #WebKitDownload:allow-overwrite property, |
| * which determines whether the download will overwrite an existing file on |
| * disk, or if it will fail if the destination already exists. |
| * |
| * Returns: the current value of the #WebKitDownload:allow-overwrite property |
| * |
| * Since: 2.6 |
| */ |
| gboolean webkit_download_get_allow_overwrite(WebKitDownload* download) |
| { |
| g_return_val_if_fail(WEBKIT_IS_DOWNLOAD(download), FALSE); |
| |
| return download->priv->allowOverwrite; |
| } |
| |
| /** |
| * webkit_download_set_allow_overwrite: |
| * @download: a #WebKitDownload |
| * @allowed: the new value for the #WebKitDownload:allow-overwrite property |
| * |
| * Sets the #WebKitDownload:allow-overwrite property, which determines whether |
| * the download may overwrite an existing file on disk, or if it will fail if |
| * the destination already exists. |
| * |
| * Since: 2.6 |
| */ |
| void webkit_download_set_allow_overwrite(WebKitDownload* download, gboolean allowed) |
| { |
| g_return_if_fail(WEBKIT_IS_DOWNLOAD(download)); |
| |
| if (allowed == download->priv->allowOverwrite) |
| return; |
| |
| download->priv->allowOverwrite = allowed; |
| g_object_notify(G_OBJECT(download), "allow-overwrite"); |
| } |