diff options
-rw-r--r-- | Makefile.sources | 4 | ||||
-rw-r--r-- | amiga/download.c | 24 | ||||
-rwxr-xr-x | amiga/download.h | 4 | ||||
-rw-r--r-- | beos/beos_gui.cpp | 12 | ||||
-rw-r--r-- | content/content.c | 25 | ||||
-rw-r--r-- | content/content.h | 9 | ||||
-rw-r--r-- | content/hlcache.c | 52 | ||||
-rw-r--r-- | content/hlcache.h | 10 | ||||
-rw-r--r-- | content/llcache.c | 66 | ||||
-rw-r--r-- | content/llcache.h | 15 | ||||
-rw-r--r-- | desktop/browser.c | 124 | ||||
-rw-r--r-- | desktop/download.c | 215 | ||||
-rw-r--r-- | desktop/download.h | 90 | ||||
-rw-r--r-- | desktop/gui.h | 12 | ||||
-rw-r--r-- | framebuffer/gui.c | 10 | ||||
-rw-r--r-- | gtk/gtk_download.c | 98 | ||||
-rw-r--r-- | gtk/gtk_download.h | 4 | ||||
-rw-r--r-- | utils/errors.h | 2 |
18 files changed, 573 insertions, 203 deletions
diff --git a/Makefile.sources b/Makefile.sources index 2e09a3d1a..8612e1255 100644 --- a/Makefile.sources +++ b/Makefile.sources @@ -33,8 +33,8 @@ S_PDF := $(addprefix desktop/save_pdf/,$(S_PDF)) # S_BROWSER are sources related to full browsers but are common # between RISC OS, GTK, BeOS and AmigaOS builds -S_BROWSER := browser.c frames.c history_core.c netsurf.c save_complete.c \ - save_text.c selection.c textinput.c +S_BROWSER := browser.c download.c frames.c history_core.c netsurf.c \ + save_complete.c save_text.c selection.c textinput.c S_BROWSER := $(addprefix desktop/,$(S_BROWSER)) # S_RISCOS are sources purely for the RISC OS build diff --git a/amiga/download.c b/amiga/download.c index 4f11f1b5f..1929d163f 100644 --- a/amiga/download.c +++ b/amiga/download.c @@ -37,11 +37,11 @@ #include "amiga/iff_dr2d.h" #include "amiga/theme.h" -#include "content/fetch.h" - +#include "desktop/download.h" #include "desktop/selection.h" #include "desktop/save_complete.h" +#include "utils/errors.h" #include "utils/messages.h" #include "utils/utils.h" @@ -55,10 +55,12 @@ #include <reaction/reaction_macros.h> -struct gui_download_window *gui_download_window_create(const char *url, - const char *mime_type, struct fetch *fetch, - unsigned int total_size, struct gui_window *gui) +struct gui_download_window *gui_download_window_create(download_context *ctx, + struct gui_window *gui) { + const char *url = download_context_get_url(ctx); + const char *mime_type = download_context_get_mime_type(ctx); + unsigned long total_size = download_context_get_total_length(ctx); struct gui_download_window *dw; APTR va[3]; @@ -139,18 +141,18 @@ struct gui_download_window *gui_download_window_create(const char *url, EndWindow; dw->win = (struct Window *)RA_OpenWindow(dw->objects[OID_MAIN]); - dw->fetch = fetch; + dw->ctx = ctx; dw->node = AddObject(window_list,AMINS_DLWINDOW); dw->node->objstruct = dw; return dw; } -void gui_download_window_data(struct gui_download_window *dw, const char *data, - unsigned int size) +nserror gui_download_window_data(struct gui_download_window *dw, + const char *data, unsigned int size) { APTR va[3]; - if(!dw) return; + if(!dw) return NSERROR_SAVE_FAILED; FWrite(dw->fh,data,1,size); @@ -176,6 +178,8 @@ void gui_download_window_data(struct gui_download_window *dw, const char *data, FUELGAUGE_VarArgs,va, TAG_DONE); } + + return NSERROR_OK; } void gui_download_window_error(struct gui_download_window *dw, @@ -187,7 +191,7 @@ void gui_download_window_error(struct gui_download_window *dw, void ami_download_window_abort(struct gui_download_window *dw) { - fetch_abort(dw->fetch); + download_context_abort(dw->ctx); gui_download_window_done(dw); } diff --git a/amiga/download.h b/amiga/download.h index a5dc2d0e1..9279869cb 100755 --- a/amiga/download.h +++ b/amiga/download.h @@ -20,6 +20,8 @@ #define AMIGA_DOWNLOAD_H #include "amiga/gui.h" +struct download_context; + struct dlnode { struct Node node; @@ -36,7 +38,7 @@ struct gui_download_window { uint32 downloaded; struct dlnode *dln; struct browser_window *bw; - struct fetch *fetch; + struct download_context *ctx; char *url; char fname[1024]; }; diff --git a/beos/beos_gui.cpp b/beos/beos_gui.cpp index 57f81877a..21ff04b40 100644 --- a/beos/beos_gui.cpp +++ b/beos/beos_gui.cpp @@ -778,17 +778,17 @@ void gui_quit(void) -struct gui_download_window *gui_download_window_create(const char *url, - const char *mime_type, struct fetch *fetch, - unsigned int total_size, struct gui_window *gui) +struct gui_download_window *gui_download_window_create(download_context *ctx, + struct gui_window *gui) { - return 0; + return NULL; } -void gui_download_window_data(struct gui_download_window *dw, const char *data, - unsigned int size) +nserror gui_download_window_data(struct gui_download_window *dw, + const char *data, unsigned int size) { + return NSERROR_OK; } diff --git a/content/content.c b/content/content.c index a247dda2b..a44eb2847 100644 --- a/content/content.c +++ b/content/content.c @@ -1409,28 +1409,3 @@ nserror content_abort(struct content *c) return llcache_handle_abort(c->llcache); } -/** - * Convert a content into a download - * - * \param h Content to convert - * \return Pointer to low-level cache handle - */ -llcache_handle *content_convert_to_download(hlcache_handle *h) -{ - struct content *c = hlcache_handle_get_content(h); - llcache_handle *stream = c->llcache; - - assert(c != NULL); - assert(c->status == CONTENT_STATUS_LOADING); - - /** \todo Is this safe? */ - c->llcache = NULL; - - /** \todo Tell the llcache to stream the data without caching it */ - - /** \todo Invalidate the content object so it's flushed from the - * cache at the earliest opportunity */ - - return stream; -} - diff --git a/content/content.h b/content/content.h index ef91135ee..b9af6c267 100644 --- a/content/content.h +++ b/content/content.h @@ -33,11 +33,10 @@ #include "content/content_type.h" #include "desktop/plot_style.h" -struct llcache_handle; - struct box; struct browser_window; struct content; +struct llcache_handle; struct hlcache_handle; struct object_params; @@ -63,6 +62,7 @@ typedef enum { CONTENT_MSG_REFORMAT, /**< content_reformat done */ CONTENT_MSG_REDRAW, /**< needs redraw (eg. new animation frame) */ CONTENT_MSG_REFRESH, /**< wants refresh */ + CONTENT_MSG_DOWNLOAD /**< download, not for display */ } content_msg; /** Extra data for some content_msg messages. */ @@ -82,6 +82,8 @@ union content_msg_data { float object_width, object_height; } redraw; int delay; /**< Minimum delay, for CONTENT_MSG_REFRESH */ + /** Low-level cache handle, for CONTENT_MSG_DOWNLOAD */ + struct llcache_handle *download; }; @@ -142,7 +144,4 @@ void content_invalidate_reuse_data(struct hlcache_handle *c); const char *content_get_refresh_url(struct hlcache_handle *c); struct bitmap *content_get_bitmap(struct hlcache_handle *c); -/* Download support */ -struct llcache_handle *content_convert_to_download(struct hlcache_handle *c); - #endif diff --git a/content/hlcache.c b/content/hlcache.c index 298fe8151..bbe0cd753 100644 --- a/content/hlcache.c +++ b/content/hlcache.c @@ -40,6 +40,8 @@ struct hlcache_retrieval_ctx { hlcache_handle *handle; /**< High-level handle for object */ + uint32_t flags; /**< Retrieval flags */ + const content_type *accepted_types; /**< Accepted types, or NULL */ hlcache_child_context child; /**< Child context */ @@ -67,7 +69,8 @@ static hlcache_entry *hlcache_content_list; static nserror hlcache_llcache_callback(llcache_handle *handle, const llcache_event *event, void *pw); static bool hlcache_type_is_acceptable(llcache_handle *llcache, - const content_type *accepted_types); + const content_type *accepted_types, + content_type *computed_type); static nserror hlcache_find_content(hlcache_retrieval_ctx *ctx); static void hlcache_content_callback(struct content *c, content_msg msg, union content_msg_data data, void *pw); @@ -104,6 +107,7 @@ nserror hlcache_handle_retrieve(const char *url, uint32_t flags, ctx->child.quirks = child->quirks; } + ctx->flags = flags; ctx->accepted_types = accepted_types; ctx->handle->cb = cb; @@ -227,10 +231,28 @@ nserror hlcache_llcache_callback(llcache_handle *handle, switch (event->type) { case LLCACHE_EVENT_HAD_HEADERS: - if (hlcache_type_is_acceptable(handle, ctx->accepted_types)) { + { + content_type type; + + if (hlcache_type_is_acceptable(handle, + ctx->accepted_types, &type)) { error = hlcache_find_content(ctx); if (error != NSERROR_OK) return error; + } else if (type == CONTENT_OTHER && + ctx->flags & HLCACHE_RETRIEVE_MAY_DOWNLOAD) { + /* Unknown type, and we can download, so convert */ + llcache_handle_force_stream(handle); + + if (ctx->handle->cb != NULL) { + hlcache_event hlevent; + + hlevent.type = CONTENT_MSG_DOWNLOAD; + hlevent.data.download = handle; + + ctx->handle->cb(ctx->handle, &hlevent, + ctx->handle->pw); + } } else { /* Unacceptable type: abort fetch and report error */ llcache_handle_abort(handle); @@ -249,6 +271,7 @@ nserror hlcache_llcache_callback(llcache_handle *handle, /* No longer require retrieval context */ free(ctx); + } break; case LLCACHE_EVENT_HAD_DATA: /* fall through */ @@ -277,19 +300,18 @@ nserror hlcache_llcache_callback(llcache_handle *handle, * * \param llcache Low-level cache object to consider * \param accepted_types Array of acceptable types, or NULL for any + * \param computed_type Pointer to location to receive computed type of object * \return True if the type is acceptable, false otherwise */ bool hlcache_type_is_acceptable(llcache_handle *llcache, - const content_type *accepted_types) + const content_type *accepted_types, content_type *computed_type) { const char *content_type_header; char *mime_type; http_parameter *params; content_type type; nserror error; - - if (accepted_types == NULL) - return true; + bool acceptable; content_type_header = llcache_handle_get_header(llcache, "Content-Type"); @@ -306,14 +328,22 @@ bool hlcache_type_is_acceptable(llcache_handle *llcache, free(mime_type); http_parameter_list_destroy(params); - while (*accepted_types != CONTENT_UNKNOWN) { - if (*accepted_types == type) - break; + if (accepted_types == NULL) { + acceptable = type != CONTENT_OTHER; + } else { + while (*accepted_types != CONTENT_UNKNOWN) { + if (*accepted_types == type) + break; + + accepted_types++; + } - accepted_types++; + acceptable = *accepted_types == type; } - return *accepted_types == type; + *computed_type = type; + + return acceptable; } /** diff --git a/content/hlcache.h b/content/hlcache.h index 1208b15c4..9f03052fa 100644 --- a/content/hlcache.h +++ b/content/hlcache.h @@ -53,6 +53,16 @@ typedef struct { typedef nserror (*hlcache_handle_callback)(hlcache_handle *handle, const hlcache_event *event, void *pw); +/** Flags for high-level cache object retrieval */ +enum hlcache_retrieve_flag { + /* Note: low-level cache retrieval flags occupy the bottom 16 bits of + * the flags word. High-level cache flags occupy the top 16 bits. + * To avoid confusion, high-level flags are allocated from bit 31 down. + */ + /** It's permitted to convert this request into a download */ + HLCACHE_RETRIEVE_MAY_DOWNLOAD = (1 << 31) +}; + /** * Retrieve a high-level cache handle for an object * diff --git a/content/llcache.c b/content/llcache.c index 94df0f6e5..7f44cc916 100644 --- a/content/llcache.c +++ b/content/llcache.c @@ -169,6 +169,8 @@ static nserror llcache_object_add_to_list(llcache_object *object, llcache_object **list); static nserror llcache_object_remove_from_list(llcache_object *object, llcache_object **list); +static bool llcache_object_in_list(const llcache_object *object, + const llcache_object *list); static nserror llcache_object_notify_users(llcache_object *object); @@ -365,6 +367,28 @@ nserror llcache_handle_abort(llcache_handle *handle) } /* See llcache.h for documentation */ +nserror llcache_handle_force_stream(llcache_handle *handle) +{ + llcache_object_user *user = (llcache_object_user *) handle; + llcache_object *object = handle->object; + + /* Cannot stream if there are multiple users */ + if (user->prev != NULL || user->next != NULL) + return NSERROR_OK; + + /* Forcibly uncache this object */ + if (llcache_object_in_list(object, llcache_cached_objects)) { + llcache_object_remove_from_list(object, + &llcache_cached_objects); + llcache_object_add_to_list(object, &llcache_uncached_objects); + } + + object->fetch.flags |= LLCACHE_RETRIEVE_STREAM_DATA; + + return NSERROR_OK; +} + +/* See llcache.h for documentation */ const char *llcache_handle_get_url(const llcache_handle *handle) { return handle->object != NULL ? handle->object->url : NULL; @@ -1055,6 +1079,26 @@ nserror llcache_object_remove_from_list(llcache_object *object, } /** + * Determine if a low-level cache object resides in a given list + * + * \param object Object to search for + * \param list List to search in + * \return True if object resides in list, false otherwise + */ +bool llcache_object_in_list(const llcache_object *object, + const llcache_object *list) +{ + while (list != NULL) { + if (list == object) + break; + + list = list->next; + } + + return list != NULL; +} + +/** * Notify users of an object's current state * * \param object Object to notify users about @@ -1139,16 +1183,24 @@ nserror llcache_object_notify_users(llcache_object *object) if (handle->state == LLCACHE_FETCH_DATA && objstate >= LLCACHE_FETCH_DATA && object->source_len > handle->bytes) { - size_t oldbytes = handle->bytes; + /* Construct HAD_DATA event */ + event.type = LLCACHE_EVENT_HAD_DATA; + event.data.data.buf = + object->source_data + handle->bytes; + event.data.data.len = + object->source_len - handle->bytes; /* Update record of last byte emitted */ - handle->bytes = object->source_len; - - /* Emit HAD_DATA event */ - event.type = LLCACHE_EVENT_HAD_DATA; - event.data.data.buf = object->source_data + oldbytes; - event.data.data.len = object->source_len - oldbytes; + if (object->fetch.flags & + LLCACHE_RETRIEVE_STREAM_DATA) { + /* Streaming, so reset to zero to + * minimise amount of cached source data */ + handle->bytes = object->source_len = 0; + } else { + handle->bytes = object->source_len; + } + /* Emit event */ error = handle->cb(handle, &event, handle->pw); if (error != NSERROR_OK) { user->iterator_target = false; diff --git a/content/llcache.h b/content/llcache.h index 434d0b641..3c965d3dc 100644 --- a/content/llcache.h +++ b/content/llcache.h @@ -82,6 +82,9 @@ typedef nserror (*llcache_handle_callback)(llcache_handle *handle, /** Flags for low-level cache object retrieval */ enum llcache_retrieve_flag { + /* Note: We're permitted a maximum of 16 flags which must reside in the + * bottom 16 bits of the flags word. See hlcache.h for further details. + */ /** Force a new fetch */ LLCACHE_RETRIEVE_FORCE_FETCH = (1 << 0), /** Requested URL was verified */ @@ -89,7 +92,9 @@ enum llcache_retrieve_flag { /** Permit content-type sniffing */ LLCACHE_RETRIEVE_SNIFF_TYPE = (1 << 2), /**< No error pages */ - LLCACHE_RETRIEVE_NO_ERROR_PAGES = (1 << 3) + LLCACHE_RETRIEVE_NO_ERROR_PAGES = (1 << 3), + /**< Stream data (implies that object is not cacheable) */ + LLCACHE_RETRIEVE_STREAM_DATA = (1 << 4) }; /** Low-level cache query types */ @@ -219,6 +224,14 @@ nserror llcache_handle_clone(llcache_handle *handle, llcache_handle **result); nserror llcache_handle_abort(llcache_handle *handle); /** + * Force a low-level cache handle into streaming mode + * + * \param handle Handle to stream + * \return NSERROR_OK on success, appropriate error otherwise + */ +nserror llcache_handle_force_stream(llcache_handle *handle); + +/** * Retrieve the post-redirect URL of a low-level cache object * * \param handle Handle to retrieve URL from diff --git a/desktop/browser.c b/desktop/browser.c index e1b071fdb..df53819c7 100644 --- a/desktop/browser.c +++ b/desktop/browser.c @@ -43,6 +43,7 @@ #include "css/css.h" #include "desktop/401login.h" #include "desktop/browser.h" +#include "desktop/download.h" #include "desktop/frames.h" #include "desktop/history_core.h" #include "desktop/gui.h" @@ -81,7 +82,8 @@ static nserror browser_window_callback(hlcache_handle *c, const hlcache_event *event, void *pw); static void browser_window_refresh(void *p); static bool browser_window_check_throbber(struct browser_window *bw); -static void browser_window_convert_to_download(struct browser_window *bw); +static void browser_window_convert_to_download(struct browser_window *bw, + llcache_handle *stream); static void browser_window_start_throbber(struct browser_window *bw); static void browser_window_stop_throbber(struct browser_window *bw); static void browser_window_set_icon(struct browser_window *bw); @@ -89,8 +91,6 @@ static void browser_window_set_status(struct browser_window *bw, const char *text); static void browser_window_set_pointer(struct gui_window *g, gui_pointer_shape shape); -static nserror download_window_callback(llcache_handle *handle, - const llcache_event *event, void *pw); static void browser_window_destroy_children(struct browser_window *bw); static void browser_window_destroy_internal(struct browser_window *bw); static void browser_window_set_scale_internal(struct browser_window *bw, @@ -338,15 +338,24 @@ void browser_window_go_post(struct browser_window *bw, const char *url, if (download) { llcache_handle *l; - error = llcache_handle_retrieve(url2, - fetch_flags | LLCACHE_RETRIEVE_FORCE_FETCH, - referer, fetch_is_post ? &post : NULL, - download_window_callback, NULL, &l); + fetch_flags |= LLCACHE_RETRIEVE_FORCE_FETCH; + fetch_flags |= LLCACHE_RETRIEVE_STREAM_DATA; + + error = llcache_handle_retrieve(url2, fetch_flags, referer, + fetch_is_post ? &post : NULL, + NULL, NULL, &l); if (error != NSERROR_OK) LOG(("Failed to fetch download: %d", error)); free(url2); + error = download_context_create(l, bw->window); + if (error != NSERROR_OK) { + LOG(("Failed creating download context: %d", error)); + llcache_handle_abort(l); + llcache_handle_release(l); + } + return; } @@ -406,7 +415,9 @@ void browser_window_go_post(struct browser_window *bw, const char *url, browser_window_set_status(bw, messages_get("Loading")); bw->history_add = add_to_history; - error = hlcache_handle_retrieve(url2, 0, referer, + error = hlcache_handle_retrieve(url2, + fetch_flags | HLCACHE_RETRIEVE_MAY_DOWNLOAD, + referer, fetch_is_post ? &post : NULL, browser_window_callback, bw, parent != NULL ? &child : NULL, @@ -439,21 +450,26 @@ nserror browser_window_callback(hlcache_handle *c, struct browser_window *bw = pw; switch (event->type) { + case CONTENT_MSG_DOWNLOAD: + assert(bw->loading_content == c); + + browser_window_convert_to_download(bw, event->data.download); + + break; + case CONTENT_MSG_LOADING: assert(bw->loading_content == c); - if (content_get_type(c) == CONTENT_OTHER) - browser_window_convert_to_download(bw); #ifdef WITH_THEME_INSTALL - else if (content_get_type(c) == CONTENT_THEME) { + if (content_get_type(c) == CONTENT_THEME) { theme_install_start(c); bw->loading_content = NULL; //newcache do we not just pass ownership to the theme installation stuff? hlcache_handle_release(c); browser_window_stop_throbber(bw); - } + } else #endif - else { + { bw->refresh_interval = -1; browser_window_set_status(bw, content_get_status_message(c)); @@ -612,24 +628,18 @@ nserror browser_window_callback(hlcache_handle *c, * Transfer the loading_content to a new download window. */ -void browser_window_convert_to_download(struct browser_window *bw) +void browser_window_convert_to_download(struct browser_window *bw, + llcache_handle *stream) { - struct gui_download_window *download_window; - hlcache_handle *c = bw->loading_content; - llcache_handle *stream; - - assert(c); - - stream = content_convert_to_download(c); + nserror error; - /** \todo Sort parameters out here */ - download_window = gui_download_window_create( - llcache_handle_get_url(stream), - llcache_handle_get_header(stream, "Content-Type"), - NULL, 0, NULL); + error = download_context_create(stream, bw->window); + if (error != NSERROR_OK) { + llcache_handle_abort(stream); + llcache_handle_release(stream); - llcache_handle_change_callback(stream, - download_window_callback, download_window); + return; + } /* remove content from browser window */ hlcache_handle_release(bw->loading_content); @@ -1341,64 +1351,6 @@ void browser_window_find_target_internal(struct browser_window *bw, /** - * Callback for fetch for download window fetches. - */ - -nserror download_window_callback(llcache_handle *handle, - const llcache_event *event, void *pw) -{ - struct gui_download_window *download_window = pw; - - switch (event->type) { - case LLCACHE_EVENT_HAD_HEADERS: - assert(download_window == NULL); - - /** \todo Ensure parameters are correct here */ - download_window = gui_download_window_create( - llcache_handle_get_url(handle), - llcache_handle_get_header(handle, - "Content-Type"), - NULL, 0, NULL); - if (download_window == NULL) - return NSERROR_NOMEM; - - llcache_handle_change_callback(handle, - download_window_callback, download_window); - break; - - case LLCACHE_EVENT_HAD_DATA: - assert(download_window != NULL); - - /** \todo Lose ugly cast */ - gui_download_window_data(download_window, - (char *) event->data.data.buf, - event->data.data.len); - - break; - - case LLCACHE_EVENT_DONE: - assert(download_window != NULL); - - gui_download_window_done(download_window); - - break; - - case LLCACHE_EVENT_ERROR: - if (download_window != NULL) - gui_download_window_error(download_window, - event->data.msg); - - break; - - case LLCACHE_EVENT_PROGRESS: - break; - } - - return NSERROR_OK; -} - - -/** * Handle mouse clicks in a browser window. * * \param bw browser window diff --git a/desktop/download.c b/desktop/download.c new file mode 100644 index 000000000..7ec41c37d --- /dev/null +++ b/desktop/download.c @@ -0,0 +1,215 @@ +/* + * Copyright 2010 John-Mark Bell <jmb@netsurf-browser.org> + * + * This file is part of NetSurf, http://www.netsurf-browser.org/ + * + * NetSurf is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * NetSurf 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +/** + * \file Core download context (implementation) + */ + +#include <assert.h> +#include <stdlib.h> + +#include "content/llcache.h" +#include "desktop/download.h" +#include "desktop/gui.h" +#include "utils/http.h" + +/** + * A context for a download + */ +struct download_context { + llcache_handle *llcache; /**< Low-level cache handle */ + struct gui_window *parent; /**< Parent window */ + + char *mime_type; /**< MIME type of download */ + unsigned long total_length; /**< Length of data, in bytes */ + + struct gui_download_window *window; /**< GUI download window */ +}; + +/** + * Process fetch headers for a download context. + * Extracts MIME type, total length, and creates gui_download_window + * + * \param ctx Context to process + * \return NSERROR_OK on success, appropriate error otherwise + */ +static nserror download_context_process_headers(download_context *ctx) +{ + const char *http_header; + char *mime_type; + http_parameter *params; + unsigned long length; + nserror error; + + /* Retrieve and parse Content-Type */ + http_header = llcache_handle_get_header(ctx->llcache, "Content-Type"); + if (http_header == NULL) + http_header = "text/plain"; + + error = http_parse_content_type(http_header, &mime_type, ¶ms); + if (error != NSERROR_OK) + return error; + + /* Don't care about parameters */ + http_parameter_list_destroy(params); + + /* Retrieve and parse Content-Length */ + http_header = llcache_handle_get_header(ctx->llcache, "Content-Length"); + if (http_header == NULL) + length = 0; + else + length = strtoul(http_header, NULL, 10); + + ctx->mime_type = mime_type; + ctx->total_length = length; + + /* Create the frontend window */ + ctx->window = gui_download_window_create(ctx, ctx->parent); + if (ctx->window == NULL) { + free(ctx->mime_type); + ctx->mime_type = NULL; + return NSERROR_NOMEM; + } + + return NSERROR_OK; +} + +/** + * Callback for low-level cache events + * + * \param handle Low-level cache handle + * \param event Event object + * \param pw Our context + * \return NSERROR_OK on success, appropriate error otherwise + */ +static nserror download_callback(llcache_handle *handle, + const llcache_event *event, void *pw) +{ + download_context *ctx = pw; + nserror error = NSERROR_OK; + + switch (event->type) { + case LLCACHE_EVENT_HAD_HEADERS: + error = download_context_process_headers(ctx); + if (error != NSERROR_OK) { + llcache_handle_abort(handle); + download_context_destroy(ctx); + } + + break; + + case LLCACHE_EVENT_HAD_DATA: + /* If we didn't know up-front that this fetch was for download, + * then we won't receive the HAD_HEADERS event. Catch up now. + */ + if (ctx->window == NULL) { + error = download_context_process_headers(ctx); + if (error != NSERROR_OK) { + llcache_handle_abort(handle); + download_context_destroy(ctx); + } + } + + if (error == NSERROR_OK) { + /** \todo Lose ugly cast */ + error = gui_download_window_data(ctx->window, + (char *) event->data.data.buf, + event->data.data.len); + if (error != NSERROR_OK) + llcache_handle_abort(handle); + } + + break; + + case LLCACHE_EVENT_DONE: + assert(ctx->window != NULL); + + gui_download_window_done(ctx->window); + + break; + + case LLCACHE_EVENT_ERROR: + if (ctx->window != NULL) + gui_download_window_error(ctx->window, event->data.msg); + + break; + + case LLCACHE_EVENT_PROGRESS: + break; + } + + return error; +} + +/* See download.h for documentation */ +nserror download_context_create(llcache_handle *llcache, + struct gui_window *parent) +{ + download_context *ctx; + + ctx = malloc(sizeof(*ctx)); + if (ctx == NULL) + return NSERROR_NOMEM; + + ctx->llcache = llcache; + ctx->parent = parent; + ctx->mime_type = NULL; + ctx->total_length = 0; + ctx->window = NULL; + + llcache_handle_change_callback(llcache, download_callback, ctx); + + return NSERROR_OK; +} + +/* See download.h for documentation */ +void download_context_destroy(download_context *ctx) +{ + llcache_handle_release(ctx->llcache); + + free(ctx->mime_type); + + /* Window is not owned by us, so don't attempt to destroy it */ + + free(ctx); +} + +/* See download.h for documentation */ +void download_context_abort(download_context *ctx) +{ + llcache_handle_abort(ctx->llcache); +} + +/* See download.h for documentation */ +const char *download_context_get_url(const download_context *ctx) +{ + return llcache_handle_get_url(ctx->llcache); +} + +/* See download.h for documentation */ +const char *download_context_get_mime_type(const download_context *ctx) +{ + return ctx->mime_type; +} + +/* See download.h for documentation */ +unsigned long download_context_get_total_length(const download_context *ctx) +{ + return ctx->total_length; +} + diff --git a/desktop/download.h b/desktop/download.h new file mode 100644 index 000000000..206253602 --- /dev/null +++ b/desktop/download.h @@ -0,0 +1,90 @@ +/* + * Copyright 2010 John-Mark Bell <jmb@netsurf-browser.org> + * + * This file is part of NetSurf, http://www.netsurf-browser.org/ + * + * NetSurf is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * NetSurf 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +/** + * \file Core download context (interface) + */ + +#ifndef NETSURF_DESKTOP_DOWNLOAD_H_ +#define NETSURF_DESKTOP_DOWNLOAD_H_ + +#include "utils/errors.h" + +struct gui_window; +struct llcache_handle; + +/** Type of a download context */ +typedef struct download_context download_context; + +/** + * Create a download context + * + * \param llcache Low-level cache handle for download + * \param parent Parent window, for UI + * \return NSERROR_OK on success, appropriate error otherwise + * + * This must only be called by the core browser window fetch infrastructure. + * Ownership of the download context object created is passed to the frontend. + */ +nserror download_context_create(struct llcache_handle *llcache, + struct gui_window *parent); + +/** + * Destroy a download context + * + * \param ctx Context to destroy + * + * Called by the frontend when it has finished with a download context + */ +void download_context_destroy(download_context *ctx); + +/** + * Abort a download fetch + * + * \param ctx Context to abort + * + * Called by the frontend to abort a download. + * The context must be destroyed independently. + */ +void download_context_abort(download_context *ctx); + +/** + * Retrieve the URL for a download + * + * \param ctx Context to retrieve URL from + * \return URL string + */ +const char *download_context_get_url(const download_context *ctx); + +/** + * Retrieve the MIME type for a download + * + * \param ctx Context to retrieve MIME type from + * \return MIME type string + */ +const char *download_context_get_mime_type(const download_context *ctx); + +/** + * Retrieve total byte length of download + * + * \param ctx Context to retrieve byte length from + * \return Total length, in bytes, or 0 if unknown + */ +unsigned long download_context_get_total_length(const download_context *ctx); + +#endif diff --git a/desktop/gui.h b/desktop/gui.h index 95301d1c1..80ba33cc8 100644 --- a/desktop/gui.h +++ b/desktop/gui.h @@ -41,7 +41,6 @@ typedef enum { GUI_SAVE_CLIPBOARD_CONTENTS } gui_save_type; -struct fetch; struct gui_window; struct gui_download_window; @@ -58,7 +57,9 @@ typedef enum { GUI_POINTER_DEFAULT, GUI_POINTER_POINT, GUI_POINTER_CARET, #include "content/content.h" #include "content/hlcache.h" #include "desktop/browser.h" +#include "desktop/download.h" #include "desktop/search.h" +#include "utils/errors.h" void gui_stdout(void); void gui_multitask(void); @@ -102,11 +103,10 @@ void gui_window_save_link(struct gui_window *g, const char *url, const char *title); void gui_window_set_scale(struct gui_window *g, float scale); -struct gui_download_window *gui_download_window_create(const char *url, - const char *mime_type, struct fetch *fetch, - unsigned int total_size, struct gui_window *gui); -void gui_download_window_data(struct gui_download_window *dw, const char *data, - unsigned int size); +struct gui_download_window *gui_download_window_create(download_context *ctx, + struct gui_window *parent); +nserror gui_download_window_data(struct gui_download_window *dw, + const char *data, unsigned int size); void gui_download_window_error(struct gui_download_window *dw, const char *error_msg); void gui_download_window_done(struct gui_download_window *dw); diff --git a/framebuffer/gui.c b/framebuffer/gui.c index 8d2759d84..e691bebdb 100644 --- a/framebuffer/gui.c +++ b/framebuffer/gui.c @@ -1358,16 +1358,16 @@ gui_window_set_search_ico(hlcache_handle *ico) { } -struct gui_download_window *gui_download_window_create(const char *url, - const char *mime_type, struct fetch *fetch, - unsigned int total_size, struct gui_window *gui) +struct gui_download_window *gui_download_window_create(download_context *ctx, + struct gui_window *parent) { return NULL; } -void gui_download_window_data(struct gui_download_window *dw, const char *data, - unsigned int size) +nserror gui_download_window_data(struct gui_download_window *dw, + const char *data, unsigned int size) { + return NSERROR_OK; } void gui_download_window_error(struct gui_download_window *dw, diff --git a/gtk/gtk_download.c b/gtk/gtk_download.c index fb8541587..64da611ad 100644 --- a/gtk/gtk_download.c +++ b/gtk/gtk_download.c @@ -27,7 +27,6 @@ #include "utils/utils.h" #include "utils/url.h" #include "utils/messages.h" -#include "content/fetch.h" #include "desktop/gui.h" #include "gtk/gtk_gui.h" #include "gtk/gtk_scaffolding.h" @@ -195,10 +194,11 @@ gboolean nsgtk_download_hide (GtkWidget *window) return TRUE; } -struct gui_download_window *gui_download_window_create(const char *url, - const char *mime_type, struct fetch *fetch, - unsigned int total_size, struct gui_window *gui) +struct gui_download_window *gui_download_window_create(download_context *ctx, + struct gui_window *gui) { + const char *url = download_context_get_url(ctx); + unsigned long total_size = download_context_get_total_length(ctx); gchar *domain; gchar *filename; gchar *destination; @@ -207,28 +207,48 @@ struct gui_download_window *gui_download_window_create(const char *url, messages_get("gtkUnknownSize") : human_friendly_bytesize(total_size)); - nsgtk_download_parent = nsgtk_scaffolding_window(nsgtk_get_scaffold( - gui)); + nsgtk_download_parent = + nsgtk_scaffolding_window(nsgtk_get_scaffold(gui)); + struct gui_download_window *download = malloc(sizeof *download); + if (download == NULL) + return NULL; - if (url_nice(url, &filename, false) != URL_FUNC_OK) - strcpy(filename, messages_get("gtkUnknownFile")); - if (url_host(url, &domain) != URL_FUNC_OK) - strcpy(domain, messages_get("gtkUnknownHost")); + if (url_nice(url, &filename, false) != URL_FUNC_OK) { + filename = g_strdup(messages_get("gtkUnknownFile")); + if (filename == NULL) { + free(download); + return NULL; + } + } + + if (url_host(url, &domain) != URL_FUNC_OK) { + domain = g_strdup(messages_get("gtkUnknownHost")); + if (domain == NULL) { + g_free(filename); + free(download); + return NULL; + } + } destination = nsgtk_download_dialog_show(filename, domain, size); - if (destination == NULL) + if (destination == NULL) { + g_free(domain); + g_free(filename); + free(download); return NULL; + } /* Add the new row and store the reference to it (which keeps track of * the tree changes) */ gtk_list_store_prepend(nsgtk_download_store, &nsgtk_download_iter); download->row = gtk_tree_row_reference_new( GTK_TREE_MODEL(nsgtk_download_store), - gtk_tree_model_get_path(GTK_TREE_MODEL - (nsgtk_download_store), &nsgtk_download_iter)); + gtk_tree_model_get_path( + GTK_TREE_MODEL(nsgtk_download_store), + &nsgtk_download_iter)); - download->fetch = fetch; + download->ctx = ctx; download->name = g_string_new(filename); download->time_left = g_string_new(""); download->size_total = total_size; @@ -240,38 +260,39 @@ struct gui_download_window *gui_download_window_create(const char *url, download->filename = destination; download->progress = 0; download->error = NULL; - download->write = g_io_channel_new_file(destination, "w", - &download->error); + download->write = + g_io_channel_new_file(destination, "w", &download->error); + if (nsgtk_download_handle_error(download->error)) { - g_string_free(download->name, TRUE); - g_string_free(download->time_left, TRUE); - g_free(download->filename); + g_string_free(download->name, TRUE); + g_string_free(download->time_left, TRUE); + g_free(download->filename); free(download); return NULL; } - g_io_channel_set_encoding(download->write, NULL, - &download->error); - + g_io_channel_set_encoding(download->write, NULL, &download->error); + nsgtk_download_change_sensitivity(download, NSGTK_DOWNLOAD_CANCEL); nsgtk_download_store_create_item(download); nsgtk_download_show(nsgtk_download_parent); if (unknown_size) - nsgtk_download_change_status(download, - NSGTK_DOWNLOAD_WORKING); + nsgtk_download_change_status(download, NSGTK_DOWNLOAD_WORKING); - if (nsgtk_downloads_num_active == 0) - g_timeout_add(UPDATE_RATE, (GSourceFunc)nsgtk_download_update, - FALSE); + if (nsgtk_downloads_num_active == 0) { + g_timeout_add(UPDATE_RATE, + (GSourceFunc) nsgtk_download_update, FALSE); + } + nsgtk_downloads_list = g_list_prepend(nsgtk_downloads_list, download); return download; } -void gui_download_window_data(struct gui_download_window *dw, const char *data, - unsigned int size) +nserror gui_download_window_data(struct gui_download_window *dw, + const char *data, unsigned int size) { g_io_channel_write_chars(dw->write, data, size, NULL, &dw->error); if (dw->error != NULL) { @@ -282,12 +303,14 @@ void gui_download_window_data(struct gui_download_window *dw, const char *data, nsgtk_download_change_status(dw, NSGTK_DOWNLOAD_ERROR); nsgtk_download_update(TRUE); - fetch_abort(dw->fetch); gtk_window_present(nsgtk_download_window); - return; + + return NSERROR_SAVE_FAILED; } dw->size_downloaded += size; + + return NSERROR_OK; } @@ -532,9 +555,11 @@ void nsgtk_download_store_clear_item (struct gui_download_window *dl) gtk_tree_row_reference_get_path(dl->row)); gtk_list_store_remove(nsgtk_download_store, &nsgtk_download_iter); - g_string_free(dl->name, TRUE); - g_string_free(dl->time_left, TRUE); - g_free(dl->filename); + + download_context_destroy(dl->ctx); + g_string_free(dl->name, TRUE); + g_string_free(dl->time_left, TRUE); + g_free(dl->filename); g_free(dl); nsgtk_download_sensitivity_evaluate(nsgtk_download_selection); @@ -551,9 +576,8 @@ void nsgtk_download_store_cancel_item (struct gui_download_window *dl) dl->time_remaining = -1; nsgtk_download_change_sensitivity(dl, NSGTK_DOWNLOAD_CLEAR); nsgtk_download_change_status(dl, NSGTK_DOWNLOAD_CANCELED); - - if (dl->fetch) - fetch_abort(dl->fetch); + + download_context_abort(dl->ctx); g_unlink(dl->filename); diff --git a/gtk/gtk_download.h b/gtk/gtk_download.h index a85980813..7e14600d4 100644 --- a/gtk/gtk_download.h +++ b/gtk/gtk_download.h @@ -21,6 +21,8 @@ #include <gtk/gtk.h> +struct download_context; + enum { NSGTK_DOWNLOAD_PROGRESS, NSGTK_DOWNLOAD_INFO, @@ -49,7 +51,7 @@ typedef enum { } nsgtk_download_actions; struct gui_download_window { - struct fetch *fetch; + struct download_context *ctx; nsgtk_download_actions sensitivity; nsgtk_download_status status; diff --git a/utils/errors.h b/utils/errors.h index 7aa486569..818ccc5fd 100644 --- a/utils/errors.h +++ b/utils/errors.h @@ -34,6 +34,8 @@ typedef enum { NSERROR_NO_FETCH_HANDLER, /**< No fetch handler for URL scheme */ NSERROR_NOT_FOUND, /**< Requested item not found */ + + NSERROR_SAVE_FAILED /**< Failed to save data */ } nserror; #endif |