From 59adc0787f7b77922c671964e471d0fd2dd7fc90 Mon Sep 17 00:00:00 2001 From: Brendan Taylor Date: Fri, 3 Dec 2010 13:47:09 -0700 Subject: let webkit handle downloads (breaks backward compatibility) --- src/callbacks.c | 125 +++++++++++++++++++++++++++++++++++++++++++++++++++++--- src/callbacks.h | 5 +-- src/events.c | 7 ++-- src/events.h | 3 +- src/uzbl-core.c | 1 + src/uzbl-core.h | 1 + 6 files changed, 129 insertions(+), 13 deletions(-) (limited to 'src') diff --git a/src/callbacks.c b/src/callbacks.c index f596472..803428d 100644 --- a/src/callbacks.c +++ b/src/callbacks.c @@ -771,13 +771,128 @@ create_web_view_cb (WebKitWebView *web_view, WebKitWebFrame *frame, gpointer us return NULL; } +void +download_progress_cb(WebKitDownload *download, GParamSpec *pspec, gpointer user_data) { + (void) pspec; (void) user_data; + + gdouble progress; + g_object_get(download, "progress", &progress, NULL); + + const gchar *dest_uri = webkit_download_get_destination_uri(download); + const gchar *dest_path = dest_uri + strlen("file://"); + + gchar *details = g_strdup_printf("%s %.2lf", dest_path, progress); + send_event(DOWNLOAD_PROGRESS, details, NULL); + g_free(details); +} + +void +download_status_cb(WebKitDownload *download, GParamSpec *pspec, gpointer user_data) { + (void) pspec; (void) user_data; + + WebKitDownloadStatus status; + g_object_get(download, "status", &status, NULL); + + switch(status) { + case WEBKIT_DOWNLOAD_STATUS_CREATED: + case WEBKIT_DOWNLOAD_STATUS_STARTED: + case WEBKIT_DOWNLOAD_STATUS_ERROR: + case WEBKIT_DOWNLOAD_STATUS_CANCELLED: + return; /* these are irrelevant */ + case WEBKIT_DOWNLOAD_STATUS_FINISHED: + { + const gchar *dest_uri = webkit_download_get_destination_uri(download); + const gchar *dest_path = dest_uri + strlen("file://"); + send_event(DOWNLOAD_COMPLETE, dest_path, NULL); + } + } +} + gboolean -download_cb (WebKitWebView *web_view, GObject *download, gpointer user_data) { - (void) web_view; - (void) user_data; +download_cb(WebKitWebView *web_view, WebKitDownload *download, gpointer user_data) { + (void) web_view; (void) user_data; - send_event(DOWNLOAD_REQ, webkit_download_get_uri ((WebKitDownload*)download), NULL); - return (FALSE); + /* get the URI being downloaded */ + const gchar *uri = webkit_download_get_uri(download); + + if (uzbl.state.verbose) + printf("Download requested -> %s\n", uri); + + if (!uzbl.behave.download_handler) { + webkit_download_cancel(download); + return FALSE; /* reject downloads when there's no download handler */ + } + + /* get a reasonable suggestion for a filename */ + const gchar *suggested_filename; + g_object_get(download, "suggested-filename", &suggested_filename, NULL); + + /* get the mimetype of the download */ + const gchar *content_type; + WebKitNetworkResponse *r = webkit_download_get_network_response(download); + /* downloads can be initiated from the context menu, in that case there is + no network response yet and trying to get one would crash. */ + if(WEBKIT_IS_NETWORK_RESPONSE(r)) { + SoupMessage *m = webkit_network_response_get_message(r); + SoupMessageHeaders *h; + g_object_get(m, "response-headers", &h, NULL); + content_type = soup_message_headers_get_one(h, "Content-Type"); + } else + content_type = "application/octet-stream"; + + /* get the filesize of the download, as given by the server. + (this may be inaccurate, there's nothing we can do about that.) */ + unsigned int total_size = webkit_download_get_total_size(download); + + gchar *ev = g_strdup_printf("'%s' '%s' '%s' %d", uri, suggested_filename, + content_type, total_size); + run_handler(uzbl.behave.download_handler, ev); + g_free(ev); + + /* no response, cancel the download */ + if(!uzbl.comm.sync_stdout) { + webkit_download_cancel(download); + return FALSE; + } + + /* no response, cancel the download */ + if(uzbl.comm.sync_stdout[0] == 0) { + webkit_download_cancel(download); + uzbl.comm.sync_stdout = strfree(uzbl.comm.sync_stdout); + return FALSE; + } + + /* we got a response, it's the path we should download the file to */ + gchar *destination_path = uzbl.comm.sync_stdout; + uzbl.comm.sync_stdout = NULL; + + /* presumably people don't need newlines in their filenames. */ + char *p = strchr(destination_path, '\n'); + if ( p != NULL ) *p = '\0'; + + /* set up progress callbacks */ + g_signal_connect(download, "notify::status", G_CALLBACK(download_status_cb), NULL); + g_signal_connect(download, "notify::progress", G_CALLBACK(download_progress_cb), NULL); + + /* convert relative path to absolute path */ + if(destination_path[0] != '/') { + gchar *rel_path = destination_path; + gchar *cwd = g_get_current_dir(); + destination_path = g_strconcat(cwd, "/", destination_path, NULL); + g_free(cwd); + g_free(rel_path); + } + + send_event(DOWNLOAD_STARTED, destination_path, NULL); + + /* convert absolute path to file:// URI */ + gchar *destination_uri = g_strconcat("file://", destination_path, NULL); + g_free(destination_path); + + webkit_download_set_destination_uri(download, destination_uri); + g_free(destination_uri); + + return TRUE; } gboolean diff --git a/src/callbacks.h b/src/callbacks.h index a4258f2..40fa80d 100644 --- a/src/callbacks.h +++ b/src/callbacks.h @@ -133,9 +133,6 @@ cmd_load_start(); WebKitWebSettings* view_settings(); -gboolean -download_cb (WebKitWebView *web_view, GObject *download, gpointer user_data); - void toggle_zoom_type (WebKitWebView* page, GArray *argv, GString *result); @@ -197,7 +194,7 @@ request_starting_cb(WebKitWebView *web_view, WebKitWebFrame *frame, WebKitWebRes create_web_view_cb (WebKitWebView *web_view, WebKitWebFrame *frame, gpointer user_data); gboolean -download_cb (WebKitWebView *web_view, GObject *download, gpointer user_data); +download_cb (WebKitWebView *web_view, WebKitDownload *download, gpointer user_data); void populate_popup_cb(WebKitWebView *v, GtkMenu *m, void *c); diff --git a/src/events.c b/src/events.c index 20e3675..55775a8 100644 --- a/src/events.c +++ b/src/events.c @@ -22,7 +22,6 @@ const char *event_table[LAST_EVENT] = { "REQUEST_STARTING" , "KEY_PRESS" , "KEY_RELEASE" , - "DOWNLOAD_REQUEST" , "COMMAND_EXECUTED" , "LINK_HOVER" , "TITLE_CHANGED" , @@ -45,10 +44,12 @@ const char *event_table[LAST_EVENT] = { "PLUG_CREATED" , "COMMAND_ERROR" , "BUILTINS" , - "PTR_MOVE" "PTR_MOVE" , "SCROLL_VERT" , - "SCROLL_HORIZ" + "SCROLL_HORIZ" , + "DOWNLOAD_STARTED" , + "DOWNLOAD_PROGRESS", + "DOWNLOAD_COMPLETE" }; void diff --git a/src/events.h b/src/events.h index bc7960d..4b04dd2 100644 --- a/src/events.h +++ b/src/events.h @@ -7,7 +7,7 @@ enum event_type { LOAD_START, LOAD_COMMIT, LOAD_FINISH, LOAD_ERROR, REQUEST_STARTING, - KEY_PRESS, KEY_RELEASE, DOWNLOAD_REQ, COMMAND_EXECUTED, + KEY_PRESS, KEY_RELEASE, COMMAND_EXECUTED, LINK_HOVER, TITLE_CHANGED, GEOMETRY_CHANGED, WEBINSPECTOR, NEW_WINDOW, SELECTION_CHANGED, VARIABLE_SET, FIFO_SET, SOCKET_SET, @@ -16,6 +16,7 @@ enum event_type { FOCUS_LOST, FOCUS_GAINED, FILE_INCLUDED, PLUG_CREATED, COMMAND_ERROR, BUILTINS, PTR_MOVE, SCROLL_VERT, SCROLL_HORIZ, + DOWNLOAD_STARTED, DOWNLOAD_PROGRESS, DOWNLOAD_COMPLETE, /* must be last entry */ LAST_EVENT diff --git a/src/uzbl-core.c b/src/uzbl-core.c index cb20fd7..c960936 100644 --- a/src/uzbl-core.c +++ b/src/uzbl-core.c @@ -93,6 +93,7 @@ const struct var_name_to_ptr_t { { "cookie_handler", PTR_V_STR(uzbl.behave.cookie_handler, 1, cmd_set_cookie_handler)}, { "authentication_handler", PTR_V_STR(uzbl.behave.authentication_handler, 1, set_authentication_handler)}, { "scheme_handler", PTR_V_STR(uzbl.behave.scheme_handler, 1, NULL)}, + { "download_handler", PTR_V_STR(uzbl.behave.download_handler, 1, NULL)}, { "fifo_dir", PTR_V_STR(uzbl.behave.fifo_dir, 1, cmd_fifo_dir)}, { "socket_dir", PTR_V_STR(uzbl.behave.socket_dir, 1, cmd_socket_dir)}, { "http_debug", PTR_V_INT(uzbl.behave.http_debug, 1, cmd_http_debug)}, diff --git a/src/uzbl-core.h b/src/uzbl-core.h index b5a502e..a62c3a6 100644 --- a/src/uzbl-core.h +++ b/src/uzbl-core.h @@ -133,6 +133,7 @@ typedef struct { gchar* fantasy_font_family; gchar* cursive_font_family; gchar* scheme_handler; + gchar* download_handler; gboolean show_status; gboolean forward_keys; gboolean status_top; -- cgit v1.2.3