WebKitDownload.cpp [plain text]
#include "config.h"
#include "WebKitDownload.h"
#include "DownloadProxy.h"
#include "WebKitDownloadPrivate.h"
#include "WebKitMarshal.h"
#include "WebKitPrivate.h"
#include "WebKitURIRequestPrivate.h"
#include "WebKitURIResponsePrivate.h"
#include <WebCore/ErrorsGtk.h>
#include <WebCore/ResourceResponse.h>
#include <glib/gi18n-lib.h>
#include <wtf/glib/GRefPtr.h>
#include <wtf/glib/GUniquePtr.h>
using namespace WebKit;
using namespace WebCore;
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) {
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;
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));
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));
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));
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));
signals[RECEIVED_DATA] =
g_signal_new("received-data",
G_TYPE_FROM_CLASS(objectClass),
G_SIGNAL_RUN_LAST,
0, 0, 0,
webkit_marshal_VOID__UINT64,
G_TYPE_NONE, 1,
G_TYPE_UINT64);
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);
signals[FAILED] =
g_signal_new("failed",
G_TYPE_FROM_CLASS(objectClass),
G_SIGNAL_RUN_LAST,
0, 0, 0,
g_cclosure_marshal_VOID__POINTER,
G_TYPE_NONE, 1,
G_TYPE_POINTER);
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,
webkit_marshal_BOOLEAN__STRING,
G_TYPE_BOOLEAN, 1,
G_TYPE_STRING);
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;
}
WebKitDownload* webkitDownloadCreateForRequest(DownloadProxy* downloadProxy, const ResourceRequest& request)
{
WebKitDownload* download = webkitDownloadCreate(downloadProxy);
download->priv->request = adoptGRef(webkitURIRequestCreateForResourceRequest(request));
return 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);
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) {
webkitDownloadCancelled(download);
return;
}
if (download->priv->timer)
g_timer_stop(download->priv->timer.get());
g_signal_emit(download, signals[FINISHED], 0, NULL);
}
CString webkitDownloadDecideDestinationWithSuggestedFilename(WebKitDownload* download, const CString& suggestedFilename, bool& allowOverwrite)
{
if (download->priv->isCancelled)
return "";
gboolean returnValue;
g_signal_emit(download, signals[DECIDE_DESTINATION], 0, suggestedFilename.data(), &returnValue);
allowOverwrite = download->priv->allowOverwrite;
return download->priv->destinationURI;
}
void webkitDownloadDestinationCreated(WebKitDownload* download, const CString& destinationURI)
{
if (download->priv->isCancelled)
return;
g_signal_emit(download, signals[CREATED_DESTINATION], 0, destinationURI.data(), nullptr);
}
WebKitURIRequest* webkit_download_get_request(WebKitDownload* download)
{
g_return_val_if_fail(WEBKIT_IS_DOWNLOAD(download), 0);
WebKitDownloadPrivate* priv = download->priv;
if (!priv->request)
priv->request = adoptGRef(webkitURIRequestCreateForResourceRequest(priv->download->request()));
return priv->request.get();
}
const gchar* webkit_download_get_destination(WebKitDownload* download)
{
g_return_val_if_fail(WEBKIT_IS_DOWNLOAD(download), 0);
return download->priv->destinationURI.data();
}
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");
}
WebKitURIResponse* webkit_download_get_response(WebKitDownload* download)
{
g_return_val_if_fail(WEBKIT_IS_DOWNLOAD(download), 0);
return download->priv->response.get();
}
void webkit_download_cancel(WebKitDownload* download)
{
g_return_if_fail(WEBKIT_IS_DOWNLOAD(download));
download->priv->isCancelled = true;
download->priv->download->cancel();
}
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);
}
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);
}
guint64 webkit_download_get_received_data_length(WebKitDownload* download)
{
g_return_val_if_fail(WEBKIT_IS_DOWNLOAD(download), 0);
return download->priv->currentSize;
}
WebKitWebView* webkit_download_get_web_view(WebKitDownload* download)
{
g_return_val_if_fail(WEBKIT_IS_DOWNLOAD(download), 0);
return download->priv->webView;
}
gboolean webkit_download_get_allow_overwrite(WebKitDownload* download)
{
g_return_val_if_fail(WEBKIT_IS_DOWNLOAD(download), FALSE);
return download->priv->allowOverwrite;
}
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");
}