aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar Brendan Taylor <whateley@gmail.com>2010-12-03 13:47:09 -0700
committerGravatar Brendan Taylor <whateley@gmail.com>2010-12-03 15:09:57 -0700
commit59adc0787f7b77922c671964e471d0fd2dd7fc90 (patch)
tree5fdbbbf22ae041cbea208947aab202ddc0a05d36
parent518004933b6d110d2a6d140f86c759dd4e713607 (diff)
let webkit handle downloads (breaks backward compatibility)
-rw-r--r--README9
-rw-r--r--examples/config/config1
-rwxr-xr-xexamples/data/scripts/download.sh34
-rw-r--r--src/callbacks.c125
-rw-r--r--src/callbacks.h5
-rw-r--r--src/events.c7
-rw-r--r--src/events.h3
-rw-r--r--src/uzbl-core.c1
-rw-r--r--src/uzbl-core.h1
9 files changed, 154 insertions, 32 deletions
diff --git a/README b/README
index 34556f2..6dddb35 100644
--- a/README
+++ b/README
@@ -668,8 +668,13 @@ Events have this format:
* `EVENT [uzbl_instance_name] TITLE_CHANGED title_name`: When the title of the
page (and hence maybe, the window title) changed. `title_name` is the new
title.
-* `EVENT [uzbl_instance_name] DOWNLOAD_REQUEST download_uri`: When content needs
- to be downloaded, `download_uri` is the URI to get.
+* `EVENT [uzbl_instance_name] DOWNLOAD_STARTED destination_path`: A download
+ has been started, the file will be saved to `destination_path`.
+* `EVENT [uzbl_instance_name] DOWNLOAD_PROGRESS destination_path progress`:
+ While a download is active this event notifies you of the progress.
+ `progress` is a decimal between 0 and 1.
+* `EVENT [uzbl_instance_name] DOWNLOAD_COMPLETE destination_path`: The
+ download being saved to `destination_path` is now complete.
* `EVENT [uzbl_instance_name] LINK_HOVER uri`: The mouse hovers over the link
`uri`.
* `EVENT [uzbl_instance_name] LINK_UNHOVER uri`: The mouse leaves the link
diff --git a/examples/config/config b/examples/config/config
index 855f7c2..64e1c94 100644
--- a/examples/config/config
+++ b/examples/config/config
@@ -43,6 +43,7 @@ set scripts_dir = $XDG_DATA_HOME/uzbl:@prefix/share/uzbl/examples/data:scri
set cookie_handler = talk_to_socket $XDG_CACHE_HOME/uzbl/cookie_daemon_socket
set scheme_handler = sync_spawn @scripts_dir/scheme.py
set authentication_handler = sync_spawn @scripts_dir/auth.py
+set download_handler = sync_spawn @scripts_dir/download.sh
# === Dynamic event handlers =================================================
diff --git a/examples/data/scripts/download.sh b/examples/data/scripts/download.sh
index 606aa62..c6d95f7 100755
--- a/examples/data/scripts/download.sh
+++ b/examples/data/scripts/download.sh
@@ -1,26 +1,26 @@
#!/bin/sh
-# just an example of how you could handle your downloads
-# try some pattern matching on the uri to determine what we should do
+#
+# uzbl's example configuration sets this script up as its download_handler.
+# when uzbl starts a download it runs this script.
+# if the script prints a file path to stdout, uzbl will save the download to
+# that path.
+# if nothing is printed to stdout, the download will be cancelled.
. $UZBL_UTIL_DIR/uzbl-args.sh
. $UZBL_UTIL_DIR/uzbl-dir.sh
-# Some sites block the default wget --user-agent..
-GET="wget --user-agent=Firefox --content-disposition --load-cookies=$UZBL_COOKIE_JAR"
+# the URL that is being downloaded
+uri=$1
-url="$1"
+# a filename suggested by the server or based on the URL
+suggested_filename=${2:-$(echo "$uri" | sed 's/\W/-/g')}
-http_proxy="$2"
-export http_proxy
+# the mimetype of the file being downloaded
+content_type=$3
-if [ -z "$url" ]; then
- echo "you must supply a url! ($url)"
- exit 1
-fi
+# the size of the downloaded file in bytes. this is not always accurate, since
+# the server might not have sent a size with its response headers.
+total_size=$4
-# only changes the dir for the $get sub process
-if echo "$url" | grep -E '.*\.torrent' >/dev/null; then
- ( cd "$UZBL_DOWNLOAD_DIR"; $GET "$url")
-else
- ( cd "$UZBL_DOWNLOAD_DIR"; $GET "$url")
-fi
+# just save the file to the default directory with the suggested name
+echo $UZBL_DOWNLOAD_DIR/$suggested_filename
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;