diff options
106 files changed, 6574 insertions, 2887 deletions
diff --git a/Makefile.sources b/Makefile.sources index ec7edc7a5..2e09a3d1a 100644 --- a/Makefile.sources +++ b/Makefile.sources @@ -5,13 +5,13 @@ # for each build. # -S_CONTENT := content.c fetch.c fetchcache.c urldb.c \ +S_CONTENT := content.c fetch.c hlcache.c llcache.c urldb.c \ fetchers/fetch_curl.c fetchers/fetch_data.c S_CSS := css.c dump.c internal.c select.c utils.c S_RENDER := box.c box_construct.c box_normalise.c directory.c favicon.c \ font.c form.c html.c html_redraw.c hubbub_binding.c imagemap.c \ layout.c list.c table.c textplain.c -S_UTILS := base64.c filename.c hashtable.c locale.c \ +S_UTILS := base64.c filename.c hashtable.c http.c locale.c \ messages.c talloc.c url.c utf8.c utils.c useragent.c S_DESKTOP := knockout.c options.c plot_style.c print.c search.c \ searchweb.c scroll.c textarea.c tree.c version.c diff --git a/amiga/download.c b/amiga/download.c index 2172fb8cc..064923eff 100644 --- a/amiga/download.c +++ b/amiga/download.c @@ -252,14 +252,15 @@ void ami_free_download_list(struct List *dllist) }while(node=nnode); } -void gui_window_save_as_link(struct gui_window *g, struct content *c) +void +gui_window_save_link(struct gui_window *g, const char *url, const char *title) { BPTR fh = 0; char fname[1024]; STRPTR openurlstring,linkname; struct DiskObject *dobj = NULL; - linkname = ASPrintf("Link_to_%s",FilePart(c->url)); + linkname = ASPrintf("Link_to_%s",FilePart(url)); if(AslRequestTags(savereq, ASLFR_TitleText,messages_get("NetSurf"), @@ -272,11 +273,11 @@ void gui_window_save_as_link(struct gui_window *g, struct content *c) ami_update_pointer(g->shared->win,GUI_POINTER_WAIT); if(fh = FOpen(fname,MODE_NEWFILE,0)) { - openurlstring = ASPrintf("openurl \"%s\"\n",c->url); + openurlstring = ASPrintf("openurl \"%s\"\n",url); FWrite(fh,openurlstring,1,strlen(openurlstring)); FClose(fh); FreeVec(openurlstring); - SetComment(fname,c->url); + SetComment(fname,url); dobj = GetIconTags(NULL,ICONGETA_GetDefaultName,"url", ICONGETA_GetDefaultType,WBPROJECT, diff --git a/amiga/fetch_file.c b/amiga/fetch_file.c index 953478423..bc03962ab 100755 --- a/amiga/fetch_file.c +++ b/amiga/fetch_file.c @@ -56,7 +56,7 @@ static bool ami_fetch_file_initialise(const char *scheme); static void ami_fetch_file_finalise(const char *scheme); static void * ami_fetch_file_setup(struct fetch *parent_fetch, const char *url, bool only_2xx, const char *post_urlenc, - struct form_successful_control *post_multipart, + struct fetch_multipart_data *post_multipart, const char **headers); static bool ami_fetch_file_start(void *vfetch); static void ami_fetch_file_abort(void *vf); @@ -135,7 +135,7 @@ void ami_fetch_file_finalise(const char *scheme) void * ami_fetch_file_setup(struct fetch *parent_fetch, const char *url, bool only_2xx, const char *post_urlenc, - struct form_successful_control *post_multipart, + struct fetch_multipart_data *post_multipart, const char **headers) { struct ami_file_fetch_info *fetch; @@ -280,6 +280,7 @@ void ami_fetch_file_poll(const char *scheme_ignored) if(fetch->fh) { + char header[64]; struct ExamineData *fib; if(fib = ExamineObjectTags(EX_FileHandleInput,fetch->fh,TAG_DONE)) { @@ -291,9 +292,18 @@ void ami_fetch_file_poll(const char *scheme_ignored) fetch->mimetype = fetch_mimetype(fetch->path); LOG(("mimetype %s len %ld",fetch->mimetype,fetch->len)); - ami_fetch_file_send_callback(FETCH_TYPE, - fetch, fetch->mimetype, (ULONG)fetch->len, - errorcode); + snprintf(header, sizeof header, + "Content-Type: %s", + fetch->mimetype); + ami_fetch_file_send_callback(FETCH_HEADER, + fetch, header, strlen(header), errorcode); + + snprintf(header, sizeof header, + "Content-Length: %ld", + fetch->len); + ami_fetch_file_send_callback(FETCH_HEADER, + fetch, header, strlen(header), errorcode); + } else { diff --git a/beos/beos_fetch_rsrc.cpp b/beos/beos_fetch_rsrc.cpp index 89916cb08..8b2fa5e60 100644 --- a/beos/beos_fetch_rsrc.cpp +++ b/beos/beos_fetch_rsrc.cpp @@ -38,7 +38,6 @@ extern "C" { #include "content/urldb.h" #include "desktop/netsurf.h" #include "desktop/options.h" -#include "render/form.h" #include "utils/log.h" #include "utils/messages.h" #include "utils/url.h" @@ -83,7 +82,7 @@ static void fetch_rsrc_finalise(const char *scheme) static void *fetch_rsrc_setup(struct fetch *parent_fetch, const char *url, bool only_2xx, const char *post_urlenc, - struct form_successful_control *post_multipart, + struct fetch_multipart_data *post_multipart, const char **headers) { struct fetch_rsrc_context *ctx; @@ -277,6 +276,8 @@ static void fetch_rsrc_poll(const char *scheme) /* Only process non-aborted fetches */ if (!c->aborted && fetch_rsrc_process(c) == true) { + char header[64]; + fetch_set_http_code(c->parent_fetch, 200); LOG(("setting rsrc: MIME type to %s, length to %zd", c->mimetype, c->datalen)); @@ -284,8 +285,16 @@ static void fetch_rsrc_poll(const char *scheme) * Therefore, we _must_ check for this after _every_ * call to fetch_rsrc_send_callback(). */ - fetch_rsrc_send_callback(FETCH_TYPE, - c, c->mimetype, c->datalen, FETCH_ERROR_NO_ERROR); + snprintf(header, sizeof header, "Content-Type: %s", + c->mimetype); + fetch_rsrc_send_callback(FETCH_HEADER, c, header, + strlen(header), FETCH_ERROR_NO_ERROR); + + snprintf(header, sizeof header, "Content-Length: %zd", + c->datalen); + fetch_rsrc_send_callback(FETCH_HEADER, c, header, + strlen(header), FETCH_ERROR_NO_ERROR); + if (!c->aborted) { fetch_rsrc_send_callback(FETCH_DATA, c, c->data, c->datalen, FETCH_ERROR_NO_ERROR); diff --git a/beos/beos_gui.cpp b/beos/beos_gui.cpp index 6c05bc401..90db0ff94 100644 --- a/beos/beos_gui.cpp +++ b/beos/beos_gui.cpp @@ -424,7 +424,15 @@ static int32 bapp_thread(void *arg) int main(int argc, char** argv) { setbuf(stderr, NULL); - return netsurf_main(argc, argv); + + /* initialise netsurf */ + netsurf_init(argc, argv); + + netsurf_main_loop(); + + netsurf_exit(); + + return 0; } void gui_init(int argc, char** argv) @@ -843,7 +851,8 @@ void gui_create_form_select_menu(struct browser_window *bw, #endif } -void gui_window_save_as_link(struct gui_window *g, struct content *c) +void +gui_window_save_link(struct gui_window *g, const char *url, const char *title) { } diff --git a/content/content.c b/content/content.c index 8b6cfd47d..1d7345207 100644 --- a/content/content.c +++ b/content/content.c @@ -32,9 +32,9 @@ #include <strings.h> #include <time.h> #include "utils/config.h" -#include "content/content.h" -#include "content/fetch.h" +#include "content/content_protected.h" #include "content/fetchcache.h" +#include "content/hlcache.h" #include "css/css.h" #include "image/bitmap.h" #include "desktop/options.h" @@ -78,16 +78,13 @@ #ifdef WITH_PNG #include "image/png.h" #endif +#include "utils/http.h" #include "utils/log.h" #include "utils/messages.h" #include "utils/talloc.h" #include "utils/utils.h" -/** Linked list of all content structures. May include more than one content - * per URL. Doubly-linked. */ -struct content *content_list = 0; - /** An entry in mime_map. */ struct mime_entry { char mime_type[40]; @@ -250,8 +247,7 @@ const char * const content_status_name[] = { /** An entry in handler_map. */ struct handler_entry { - bool (*create)(struct content *c, struct content *parent, - const char *params[]); + bool (*create)(struct content *c, const http_parameter *params); bool (*process_data)(struct content *c, char *data, unsigned int size); bool (*convert)(struct content *c, int width, int height); void (*reformat)(struct content *c, int width, int height); @@ -359,16 +355,16 @@ static const struct handler_entry handler_map[] = { }; #define HANDLER_MAP_COUNT (sizeof(handler_map) / sizeof(handler_map[0])) - +static nserror content_llcache_callback(llcache_handle *llcache, + const llcache_event *event, void *pw); +static void content_convert(struct content *c, int width, int height); static void content_update_status(struct content *c); -static void content_destroy(struct content *c); -static void content_stop_check(struct content *c); /** * Convert a MIME type to a content_type. * - * The returned ::content_type will always be suitable for content_set_type(). + * The returned ::content_type will always be suitable for content_create(). */ content_type content_lookup(const char *mime_type) @@ -397,289 +393,188 @@ content_type content_lookup(const char *mime_type) * CONTENT_STATUS_TYPE_UNKNOWN. */ -struct content * content_create(const char *url) +struct content * content_create(llcache_handle *llcache, + const char *fallback_charset, bool quirks) { struct content *c; struct content_user *user_sentinel; + const char *content_type_header; + content_type type; + char *mime_type; + http_parameter *params; + nserror error; + + content_type_header = + llcache_handle_get_header(llcache, "Content-Type"); + if (content_type_header == NULL) + content_type_header = "text/plain"; + + error = http_parse_content_type(content_type_header, &mime_type, + ¶ms); + if (error != NSERROR_OK) + return NULL; + + type = content_lookup(mime_type); + if (type == CONTENT_OTHER) { + http_parameter_list_destroy(params); + free(mime_type); + return NULL; + } c = talloc_zero(0, struct content); - if (!c) - return 0; + if (c == NULL) { + http_parameter_list_destroy(params); + free(mime_type); + return NULL; + } - LOG(("url %s -> %p", url, c)); + LOG(("url %s -> %p", llcache_handle_get_url(llcache), c)); user_sentinel = talloc(c, struct content_user); - if (!user_sentinel) { + if (user_sentinel == NULL) { talloc_free(c); - return 0; + http_parameter_list_destroy(params); + free(mime_type); + return NULL; } - c->url = talloc_strdup(c, url); - if (!c->url) { + + c->fallback_charset = talloc_strdup(c, fallback_charset); + if (fallback_charset != NULL && c->fallback_charset == NULL) { talloc_free(c); - return 0; + http_parameter_list_destroy(params); + free(mime_type); + return NULL; } - talloc_set_name_const(c, c->url); - c->type = CONTENT_UNKNOWN; - c->mime_type = 0; - c->status = CONTENT_STATUS_TYPE_UNKNOWN; + + c->mime_type = talloc_strdup(c, mime_type); + if (c->mime_type == NULL) { + talloc_free(c); + http_parameter_list_destroy(params); + free(mime_type); + return NULL; + } + + /* No longer require mime_type */ + free(mime_type); + + c->llcache = llcache; + c->type = type; + c->status = CONTENT_STATUS_LOADING; c->width = 0; c->height = 0; c->available_width = 0; + c->quirks = quirks; c->refresh = 0; - c->bitmap = 0; + c->bitmap = NULL; c->fresh = false; c->time = wallclock(); c->size = 0; - c->title = 0; + c->title = NULL; c->active = 0; - user_sentinel->callback = 0; - user_sentinel->p1 = user_sentinel->p2 = 0; - user_sentinel->next = 0; + user_sentinel->callback = NULL; + user_sentinel->pw = NULL; + user_sentinel->next = NULL; c->user_list = user_sentinel; c->sub_status[0] = 0; c->locked = false; - c->fetch = 0; - c->source_data = 0; - c->source_size = 0; - c->source_allocated = 0; c->total_size = 0; c->http_code = 0; - c->no_error_pages = false; - c->download = false; - c->tried_with_auth = false; - c->redirect_count = 0; c->error_count = 0; - c->cache_data.req_time = 0; - c->cache_data.res_time = 0; - c->cache_data.date = 0; - c->cache_data.expires = 0; - c->cache_data.age = INVALID_AGE; - c->cache_data.max_age = INVALID_AGE; - c->cache_data.no_cache = false; - c->cache_data.etag = 0; - c->cache_data.last_modified = 0; content_set_status(c, messages_get("Loading")); - c->prev = 0; - c->next = content_list; - if (content_list) - content_list->prev = c; - content_list = c; - - return c; -} - - -/** - * Get a content from the memory cache. - * - * \param url URL of content - * \return content if found, or 0 - * - * Searches the list of contents for one corresponding to the given url, and - * which is fresh and shareable. - */ + if (handler_map[type].create) { + if (handler_map[type].create(c, params) == false) { + talloc_free(c); + http_parameter_list_destroy(params); + return NULL; + } + } -struct content * content_get(const char *url) -{ - struct content *c; + http_parameter_list_destroy(params); - for (c = content_list; c; c = c->next) { - if (!c->fresh) - /* not fresh */ - continue; - if (c->status == CONTENT_STATUS_ERROR) - /* error state */ - continue; - /** \todo We need to reconsider the entire caching strategy in - * the light of data being shared between specific contents. - * - * For example, string dictionaries are owned by the document, - * and all stylesheets used by the document share the same - * dictionary. - * - * The CSS content handler retrieves the dictionary from its - * parent content. This relies upon there being a 1:1 mapping - * between documents and stylesheets. - * - * The type of a content is only known once we've received the - * headers from the fetch layer (and potentially some of the - * content data, too, if we ever sniff for the type). There - * is thus a problem with returning contents of unknown type - * here -- when we subsequently discover that they must only - * have one user, we clone them. By that point, however, we've - * no idea what the parent content is, which means that they - * end up with the wrong parent (and thus wrong dictionary). - * - * Of course, the problem with ignoring unknown content types - * here is that, for all the content types which may be shared, - * we end up duplicating them and wasting memory. Hence the - * need to reconsider everything. - */ - if (c->type == CONTENT_UNKNOWN) - continue; - if (c->type != CONTENT_UNKNOWN && - handler_map[c->type].no_share && - c->user_list->next) - /* not shareable, and has a user already */ - continue; - if (strcmp(c->url, url)) - continue; - return c; + /* Finally, claim low-level cache events */ + if (llcache_handle_change_callback(llcache, + content_llcache_callback, c) != NSERROR_OK) { + talloc_free(c); + return NULL; } - return 0; + return c; } - /** - * Get a READY or DONE content from the memory cache. - * - * \param url URL of content - * \return content if found, or 0 + * Handler for low-level cache events * - * Searches the list of contents for one corresponding to the given url, and - * which is fresh, shareable and either READY or DONE. + * \param llcache Low-level cache handle + * \param event Event details + * \param pw Pointer to our context + * \return NSERROR_OK on success, appropriate error otherwise */ - -struct content * content_get_ready(const char *url) +nserror content_llcache_callback(llcache_handle *llcache, + const llcache_event *event, void *pw) { - struct content *c; + struct content *c = pw; + union content_msg_data msg_data; + nserror error = NSERROR_OK; + + switch (event->type) { + case LLCACHE_EVENT_HAD_HEADERS: + /* Will never happen: handled in hlcache */ + break; + case LLCACHE_EVENT_HAD_DATA: + if (handler_map[c->type].process_data) { + if (handler_map[c->type].process_data(c, + (char *) event->data.data.buf, + event->data.data.len) == false) { + c->status = CONTENT_STATUS_ERROR; + /** \todo It's not clear what error this is */ + error = NSERROR_NOMEM; + } + } + break; + case LLCACHE_EVENT_DONE: + { + const uint8_t *source; + size_t source_size; + + source = llcache_handle_get_source_data(llcache, &source_size); - for (c = content_list; c; c = c->next) { - if (!c->fresh) - /* not fresh */ - continue; - if (c->status != CONTENT_STATUS_READY && - c->status != CONTENT_STATUS_DONE) - /* not ready or done */ - continue; - if (c->type != CONTENT_UNKNOWN && - handler_map[c->type].no_share && - c->user_list->next) - /* not shareable, and has a user already */ - continue; - if (strcmp(c->url, url)) - continue; - return c; + content_set_status(c, messages_get("Converting"), source_size); + content_broadcast(c, CONTENT_MSG_STATUS, msg_data); + + content_convert(c, c->width, c->height); + } + break; + case LLCACHE_EVENT_ERROR: + /** \todo Error page? */ + c->status = CONTENT_STATUS_ERROR; + msg_data.error = event->data.msg; + content_broadcast(c, CONTENT_MSG_ERROR, msg_data); + break; + case LLCACHE_EVENT_PROGRESS: + content_set_status(c, "%s", event->data.msg); + content_broadcast(c, CONTENT_MSG_STATUS, msg_data); + break; } - return 0; + return error; } - /** * Get whether a content can reformat * - * \param c content to check + * \param h content to check * \return whether the content can reformat */ -bool content_can_reformat(struct content *c) +bool content_can_reformat(hlcache_handle *h) { - return (handler_map[c->type].reformat != NULL); -} - + struct content *c = hlcache_handle_get_content(h); -/** - * Initialise the content for the specified type. - * - * \param c content structure - * \param type content_type to initialise to - * \param mime_type MIME-type string for this content - * \param params array of strings, ordered attribute, value, attribute, ..., 0 - * \return true on success, false on error and error broadcast to users and - * possibly reported - * - * The type is updated to the given type, and a copy of mime_type is taken. The - * status is changed to CONTENT_STATUS_LOADING. CONTENT_MSG_LOADING is sent to - * all users. The create function for the type is called to initialise the type - * specific parts of the content structure. - */ - -bool content_set_type(struct content *c, content_type type, - const char *mime_type, const char *params[], - struct content *parent) -{ - union content_msg_data msg_data; - struct content *clone; - void (*callback)(content_msg msg, struct content *c, intptr_t p1, - intptr_t p2, union content_msg_data data); - intptr_t p1, p2; - - assert(c != 0); - assert(c->status == CONTENT_STATUS_TYPE_UNKNOWN); - assert(type < CONTENT_UNKNOWN); - - LOG(("content %s (%p), type %i", c->url, c, type)); - - c->mime_type = talloc_strdup(c, mime_type); - if (!c->mime_type) { - c->status = CONTENT_STATUS_ERROR; - msg_data.error = messages_get("NoMemory"); - content_broadcast(c, CONTENT_MSG_ERROR, msg_data); + if (c == NULL) return false; - } - - c->type = type; - c->status = CONTENT_STATUS_LOADING; - if (handler_map[type].no_share && c->user_list->next && - c->user_list->next->next) { - /* type not shareable, and more than one user: split into - * a content per user */ - const char *referer = - c->fetch ? fetch_get_referer(c->fetch) : NULL; - struct content *parent = - c->fetch ? fetch_get_parent(c->fetch) : NULL; - - while (c->user_list->next->next) { - clone = content_create(c->url); - if (!clone) { - c->type = CONTENT_UNKNOWN; - c->status = CONTENT_STATUS_ERROR; - msg_data.error = messages_get("NoMemory"); - content_broadcast(c, CONTENT_MSG_ERROR, - msg_data); - return false; - } - - clone->width = c->width; - clone->height = c->height; - clone->fresh = c->fresh; - - callback = c->user_list->next->next->callback; - p1 = c->user_list->next->next->p1; - p2 = c->user_list->next->next->p2; - if (!content_add_user(clone, callback, p1, p2)) { - c->type = CONTENT_UNKNOWN; - c->status = CONTENT_STATUS_ERROR; - content_destroy(clone); - msg_data.error = messages_get("NoMemory"); - content_broadcast(c, CONTENT_MSG_ERROR, - msg_data); - return false; - } - content_remove_user(c, callback, p1, p2); - msg_data.new_url = NULL; - content_broadcast(clone, CONTENT_MSG_NEWPTR, msg_data); - fetchcache_go(clone, referer, - callback, p1, p2, - clone->width, clone->height, - 0, 0, false, parent); - } - } - - if (handler_map[type].create) { - if (!handler_map[type].create(c, parent, params)) { - c->type = CONTENT_UNKNOWN; - c->status = CONTENT_STATUS_ERROR; - return false; - } - } - - content_broadcast(c, CONTENT_MSG_LOADING, msg_data); - return true; + return (handler_map[c->type].reformat != NULL); } @@ -733,58 +628,6 @@ void content_update_status(struct content *c) /** - * Process a block of source data. - * - * Calls the process_data function for the content. - * - * \param c content structure - * \param data new data to process - * \param size size of data - * \return true on success, false on error and error broadcast to users and - * possibly reported - */ - -bool content_process_data(struct content *c, const char *data, - unsigned int size) -{ - char *source_data; - union content_msg_data msg_data; - unsigned int extra_space; - - assert(c); - assert(c->type < HANDLER_MAP_COUNT); - assert(c->status == CONTENT_STATUS_LOADING); - - if ((c->source_size + size) > c->source_allocated) { - extra_space = (c->source_size + size) / 4; - if (extra_space < 65536) - extra_space = 65536; - source_data = talloc_realloc(c, c->source_data, char, - c->source_size + size + extra_space); - if (!source_data) { - c->status = CONTENT_STATUS_ERROR; - msg_data.error = messages_get("NoMemory"); - content_broadcast(c, CONTENT_MSG_ERROR, msg_data); - return false; - } - c->source_data = source_data; - c->source_allocated = c->source_size + size + extra_space; - } - memcpy(c->source_data + c->source_size, data, size); - c->source_size += size; - - if (handler_map[c->type].process_data) { - if (!handler_map[c->type].process_data(c, - c->source_data + c->source_size - size, size)) { - c->status = CONTENT_STATUS_ERROR; - return false; - } - } - return true; -} - - -/** * All data has arrived, convert for display. * * Calls the convert function for the content. @@ -801,22 +644,12 @@ bool content_process_data(struct content *c, const char *data, void content_convert(struct content *c, int width, int height) { union content_msg_data msg_data; - char *source_data; assert(c); assert(c->type < HANDLER_MAP_COUNT); assert(c->status == CONTENT_STATUS_LOADING); assert(!c->locked); - LOG(("content %s (%p)", c->url, c)); - - if (c->source_allocated != c->source_size) { - source_data = talloc_realloc(c, c->source_data, char, - c->source_size); - if (source_data) { - c->source_data = source_data; - c->source_allocated = c->source_size; - } - } + LOG(("content %s (%p)", llcache_handle_get_url(c->llcache), c)); c->locked = true; c->available_width = width; @@ -860,14 +693,19 @@ void content_set_done(struct content *c) * Calls the reformat function for the content. */ -void content_reformat(struct content *c, int width, int height) +void content_reformat(hlcache_handle *h, int width, int height) +{ + content__reformat(hlcache_handle_get_content(h), width, height); +} + +void content__reformat(struct content *c, int width, int height) { union content_msg_data data; assert(c != 0); assert(c->status == CONTENT_STATUS_READY || c->status == CONTENT_STATUS_DONE); assert(!c->locked); - LOG(("%p %s", c, c->url)); + LOG(("%p %s", c, llcache_handle_get_url(c->llcache))); c->locked = true; c->available_width = width; if (handler_map[c->type].reformat) { @@ -879,65 +717,6 @@ void content_reformat(struct content *c, int width, int height) /** - * Clean unused contents from the content_list. - * - * Destroys any contents in the content_list with no users or in - * CONTENT_STATUS_ERROR. Fresh contents in CONTENT_STATUS_DONE may be kept even - * with no users. - * - * Each content is also checked for stop requests. - */ - -void content_clean(void) -{ - unsigned int size; - struct content *c, *next, *prev; - - /* destroy unused stale contents and contents with errors */ - for (c = content_list; c; c = next) { - next = c->next; - - /* this function must not be called from a content function */ - assert(!c->locked); - - if (c->user_list->next && c->status != CONTENT_STATUS_ERROR) - /* content has users */ - continue; - - if (c->fresh && c->status == CONTENT_STATUS_DONE) - /* content is fresh */ - continue; - - /* content can be destroyed */ - content_destroy(c); - } - - /* check for pending stops */ - for (c = content_list; c; c = c->next) { - if (c->status == CONTENT_STATUS_READY) - content_stop_check(c); - } - - /* attempt to shrink the memory cache (unused fresh contents) */ - size = 0; - next = 0; - for (c = content_list; c; c = c->next) { - next = c; - c->talloc_size = talloc_total_size(c); - size += c->size + c->talloc_size; - } - for (c = next; c && (unsigned int) option_memory_cache_size < size; - c = prev) { - prev = c->prev; - if (c->user_list->next) - continue; - size -= c->size + c->talloc_size; - content_destroy(c); - } -} - - -/** * Destroy and free a content. * * Calls the destroy function for the content, and frees the structure. @@ -946,92 +725,54 @@ void content_clean(void) void content_destroy(struct content *c) { assert(c); - LOG(("content %p %s", c, c->url)); + LOG(("content %p %s", c, llcache_handle_get_url(c->llcache))); assert(!c->locked); - if (c->fetch) - fetch_abort(c->fetch); - - if (c->next) - c->next->prev = c->prev; - if (c->prev) - c->prev->next = c->next; - else - content_list = c->next; - if (c->type < HANDLER_MAP_COUNT && handler_map[c->type].destroy) handler_map[c->type].destroy(c); talloc_free(c); } - /** - * Reset a content. + * Request a redraw of an area of a content * - * Calls the destroy function for the content, but does not free - * the structure. + * \param h Content handle + * \param x x co-ord of left edge + * \param y y co-ord of top edge + * \param width Width of rectangle + * \param height Height of rectangle */ - -void content_reset(struct content *c) +void content_request_redraw(struct hlcache_handle *h, + int x, int y, int width, int height) { - assert(c != 0); - LOG(("content %p %s", c, c->url)); - assert(!c->locked); - if (c->type < HANDLER_MAP_COUNT && handler_map[c->type].destroy) - handler_map[c->type].destroy(c); - c->type = CONTENT_UNKNOWN; - c->status = CONTENT_STATUS_TYPE_UNKNOWN; - c->size = 0; - talloc_free(c->mime_type); - c->mime_type = 0; - talloc_free(c->refresh); - c->refresh = 0; - talloc_free(c->title); - c->title = 0; - talloc_free(c->source_data); - c->source_data = 0; - c->source_size = c->source_allocated = 0; -} + struct content *c = hlcache_handle_get_content(h); + union content_msg_data data; + if (c == NULL) + return; -/** - * Free all contents in the content_list. - */ + data.redraw.x = x; + data.redraw.y = y; + data.redraw.width = width; + data.redraw.height = height; -void content_quit(void) -{ - bool progress = true; - struct content *c, *next; - - while (content_list && progress) { - progress = false; - for (c = content_list; c; c = next) { - assert(c->next != c); - next = c->next; - - if (c->user_list->next && - c->status != CONTENT_STATUS_ERROR) - /* content has users */ - continue; - - /* content can be destroyed */ - content_destroy(c); - progress = true; - } - } + data.redraw.full_redraw = true; - if (content_list) { - LOG(("bug: some contents could not be destroyed")); - } -} + data.redraw.object = c; + data.redraw.object_x = 0; + data.redraw.object_y = 0; + data.redraw.object_width = c->width; + data.redraw.object_height = c->height; + content_broadcast(c, CONTENT_MSG_REDRAW, data); +} /** * Display content on screen. * * Calls the redraw function for the content, if it exists. * - * \param c content + * \param h content * \param x coordinate for top-left of redraw * \param y coordinate for top-left of redraw * \param width available width (not used for HTML redraw) @@ -1051,11 +792,12 @@ void content_quit(void) * Units for x, y and clip_* are pixels. */ -bool content_redraw(struct content *c, int x, int y, +bool content_redraw(hlcache_handle *h, int x, int y, int width, int height, int clip_x0, int clip_y0, int clip_x1, int clip_y1, float scale, colour background_colour) { + struct content *c = hlcache_handle_get_content(h); assert(c != 0); // LOG(("%p %s", c, c->url)); if (c->locked) @@ -1076,12 +818,13 @@ bool content_redraw(struct content *c, int x, int y, * redraw function if it doesn't exist. */ -bool content_redraw_tiled(struct content *c, int x, int y, +bool content_redraw_tiled(hlcache_handle *h, int x, int y, int width, int height, int clip_x0, int clip_y0, int clip_x1, int clip_y1, float scale, colour background_colour, bool repeat_x, bool repeat_y) { + struct content *c = hlcache_handle_get_content(h); int x0, y0, x1, y1; assert(c != 0); @@ -1139,29 +882,27 @@ bool content_redraw_tiled(struct content *c, int x, int y, * * \param c the content to register * \param callback the callback function - * \param p1, p2 callback private data + * \param pw callback private data * \return true on success, false otherwise on memory exhaustion * - * The callback will be called with p1 and p2 when content_broadcast() is + * The callback will be called when content_broadcast() is * called with the content. */ bool content_add_user(struct content *c, - void (*callback)(content_msg msg, struct content *c, - intptr_t p1, intptr_t p2, union content_msg_data data), - intptr_t p1, intptr_t p2) + void (*callback)(struct content *c, content_msg msg, + union content_msg_data data, void *pw), + void *pw) { struct content_user *user; - LOG(("content %s (%p), user %p 0x%" PRIxPTR " 0x%" PRIxPTR, - c->url, c, callback, p1, p2)); + LOG(("content %s (%p), user %p %p", + llcache_handle_get_url(c->llcache), c, callback, pw)); user = talloc(c, struct content_user); if (!user) return false; user->callback = callback; - user->p1 = p1; - user->p2 = p2; - user->stop = false; + user->pw = pw; user->next = c->user_list->next; c->user_list->next = user; @@ -1170,49 +911,25 @@ bool content_add_user(struct content *c, /** - * Search the users of a content for the specified user. - * - * \return a content_user struct for the user, or 0 if not found - */ - -struct content_user * content_find_user(struct content *c, - void (*callback)(content_msg msg, struct content *c, - intptr_t p1, intptr_t p2, union content_msg_data data), - intptr_t p1, intptr_t p2) -{ - struct content_user *user; - - /* user_list starts with a sentinel */ - for (user = c->user_list; user->next && - !(user->next->callback == callback && - user->next->p1 == p1 && - user->next->p2 == p2); user = user->next) - ; - return user->next; -} - - -/** * Remove a callback user. * - * The callback function, p1, and p2 must be identical to those passed to + * The callback function and pw must be identical to those passed to * content_add_user(). */ void content_remove_user(struct content *c, - void (*callback)(content_msg msg, struct content *c, - intptr_t p1, intptr_t p2, union content_msg_data data), - intptr_t p1, intptr_t p2) + void (*callback)(struct content *c, content_msg msg, + union content_msg_data data, void *pw), + void *pw) { struct content_user *user, *next; - LOG(("content %s (%p), user %p 0x%" PRIxPTR " 0x%" PRIxPTR, - c->url, c, callback, p1, p2)); + LOG(("content %s (%p), user %p %p", + llcache_handle_get_url(c->llcache), c, callback, pw)); /* user_list starts with a sentinel */ for (user = c->user_list; user->next != 0 && !(user->next->callback == callback && - user->next->p1 == p1 && - user->next->p2 == p2); user = user->next) + user->next->pw == pw); user = user->next) ; if (user->next == 0) { LOG(("user not found in list")); @@ -1238,7 +955,7 @@ void content_broadcast(struct content *c, content_msg msg, for (user = c->user_list->next; user != 0; user = next) { next = user->next; /* user may be destroyed during callback */ if (user->callback != 0) - user->callback(msg, c, user->p1, user->p2, data); + user->callback(c, msg, data, user->pw); } } @@ -1250,11 +967,13 @@ void content_broadcast(struct content *c, content_msg msg, * stop, the loading is stopped and the content placed in CONTENT_STATUS_DONE. */ -void content_stop(struct content *c, - void (*callback)(content_msg msg, struct content *c, - intptr_t p1, intptr_t p2, union content_msg_data data), - intptr_t p1, intptr_t p2) +void content_stop(hlcache_handle *h, + void (*callback)(struct content *c, content_msg msg, + union content_msg_data data, void *pw), + void *pw) { +//newcache +#if 0 struct content_user *user; assert(c->status == CONTENT_STATUS_READY); @@ -1267,36 +986,10 @@ void content_stop(struct content *c, } LOG(("%p %s: stop user %p 0x%" PRIxPTR " 0x%" PRIxPTR, - c, c->url, callback, p1, p2)); + c, llcache_handle_get_url(c->llcache), + callback, p1, p2)); user->stop = true; -} - - -/** - * Check if all users have requested a stop, and do it if so. - */ - -void content_stop_check(struct content *c) -{ - struct content_user *user; - union content_msg_data data; - - assert(c->status == CONTENT_STATUS_READY); - - /* user_list starts with a sentinel */ - for (user = c->user_list->next; user; user = user->next) - if (!user->stop) - return; - - LOG(("%p %s", c, c->url)); - - /* all users have requested stop */ - assert(handler_map[c->type].stop); - handler_map[c->type].stop(c); - assert(c->status == CONTENT_STATUS_DONE); - - content_set_status(c, messages_get("Stopped")); - content_broadcast(c, CONTENT_MSG_DONE, data); +#endif } @@ -1314,13 +1007,14 @@ void content_stop_check(struct content *c) * Calls the open function for the content. */ -void content_open(struct content *c, struct browser_window *bw, +void content_open(hlcache_handle *h, struct browser_window *bw, struct content *page, unsigned int index, struct box *box, struct object_params *params) { + struct content *c = hlcache_handle_get_content(h); assert(c != 0); assert(c->type < CONTENT_UNKNOWN); - LOG(("content %p %s", c, c->url)); + LOG(("content %p %s", c, llcache_handle_get_url(c->llcache))); if (handler_map[c->type].open) handler_map[c->type].open(c, bw, page, index, box, params); } @@ -1332,11 +1026,12 @@ void content_open(struct content *c, struct browser_window *bw, * Calls the close function for the content. */ -void content_close(struct content *c) +void content_close(hlcache_handle *h) { + struct content *c = hlcache_handle_get_content(h); assert(c != 0); assert(c->type < CONTENT_UNKNOWN); - LOG(("content %p %s", c, c->url)); + LOG(("content %p %s", c, llcache_handle_get_url(c->llcache))); if (handler_map[c->type].close) handler_map[c->type].close(c); } @@ -1346,3 +1041,285 @@ void content_add_error(struct content *c, const char *token, unsigned int line) { } + +/** + * Retrieve type of content + * + * \param c Content to retrieve type of + * \return Content type + */ +content_type content_get_type(hlcache_handle *h) +{ + return content__get_type(hlcache_handle_get_content(h)); +} + +content_type content__get_type(struct content *c) +{ + if (c == NULL) + return CONTENT_UNKNOWN; + + return c->type; +} + +/** + * Retrieve URL associated with content + * + * \param c Content to retrieve URL from + * \return Pointer to URL, or NULL if not found. + */ +const char *content_get_url(hlcache_handle *h) +{ + return content__get_url(hlcache_handle_get_content(h)); +} + +const char *content__get_url(struct content *c) +{ + if (c == NULL) + return NULL; + + return llcache_handle_get_url(c->llcache); +} + +/** + * Retrieve title associated with content + * + * \param c Content to retrieve title from + * \return Pointer to title, or NULL if not found. + */ +const char *content_get_title(hlcache_handle *h) +{ + return content__get_title(hlcache_handle_get_content(h)); +} + +const char *content__get_title(struct content *c) +{ + if (c == NULL) + return NULL; + + return c->title != NULL ? c->title : llcache_handle_get_url(c->llcache); +} + +/** + * Retrieve status of content + * + * \param c Content to retrieve status of + * \return Content status + */ +content_status content_get_status(hlcache_handle *h) +{ + return content__get_status(hlcache_handle_get_content(h)); +} + +content_status content__get_status(struct content *c) +{ + if (c == NULL) + return CONTENT_STATUS_TYPE_UNKNOWN; + + return c->status; +} + +/** + * Retrieve status message associated with content + * + * \param c Content to retrieve status message from + * \return Pointer to status message, or NULL if not found. + */ +const char *content_get_status_message(hlcache_handle *h) +{ + return content__get_status_message(hlcache_handle_get_content(h)); +} + +const char *content__get_status_message(struct content *c) +{ + if (c == NULL) + return NULL; + + return c->status_message; +} + +/** + * Retrieve width of content + * + * \param c Content to retrieve width of + * \return Content width + */ +int content_get_width(hlcache_handle *h) +{ + return content__get_width(hlcache_handle_get_content(h)); +} + +int content__get_width(struct content *c) +{ + if (c == NULL) + return 0; + + return c->width; +} + +/** + * Retrieve height of content + * + * \param c Content to retrieve height of + * \return Content height + */ +int content_get_height(hlcache_handle *h) +{ + return content__get_height(hlcache_handle_get_content(h)); +} + +int content__get_height(struct content *c) +{ + if (c == NULL) + return 0; + + return c->height; +} + +/** + * Retrieve available width of content + * + * \param c Content to retrieve available width of + * \return Available width of content + */ +int content_get_available_width(hlcache_handle *h) +{ + return content__get_available_width(hlcache_handle_get_content(h)); +} + +int content__get_available_width(struct content *c) +{ + if (c == NULL) + return 0; + + return c->available_width; +} + + +/** + * Retrieve source of content + * + * \param c Content to retrieve source of + * \param size Pointer to location to receive byte size of source + * \return Pointer to source data + */ +const char *content_get_source_data(hlcache_handle *h, unsigned long *size) +{ + return content__get_source_data(hlcache_handle_get_content(h), size); +} + +const char *content__get_source_data(struct content *c, unsigned long *size) +{ + const uint8_t *data; + size_t len; + + assert(size != NULL); + + if (c == NULL) + return NULL; + + data = llcache_handle_get_source_data(c->llcache, &len); + + *size = (unsigned long) len; + + return (const char *) data; +} + +/** + * Invalidate content reuse data: causes subsequent requests for content URL + * to query server to determine if content can be reused. This is required + * behaviour for forced reloads etc. + * + * \param c Content to invalidate + */ +void content_invalidate_reuse_data(hlcache_handle *h) +{ + content__invalidate_reuse_data(hlcache_handle_get_content(h)); +} + +void content__invalidate_reuse_data(struct content *c) +{ + if (c == NULL) + return; + + /* For now, just cause the content to be completely ignored */ + c->fresh = false; +} + +/** + * Retrieve the refresh URL for a content + * + * \param c Content to retrieve refresh URL from + * \return Pointer to URL, or NULL if none + */ +const char *content_get_refresh_url(hlcache_handle *h) +{ + return content__get_refresh_url(hlcache_handle_get_content(h)); +} + +const char *content__get_refresh_url(struct content *c) +{ + if (c == NULL) + return NULL; + + return c->refresh; +} + +/** + * Retrieve the bitmap contained in an image content + * + * \param c Content to retrieve bitmap from + * \return Pointer to bitmap, or NULL if none. + */ +struct bitmap *content_get_bitmap(hlcache_handle *h) +{ + return content__get_bitmap(hlcache_handle_get_content(h)); +} + +struct bitmap *content__get_bitmap(struct content *c) +{ + if (c == NULL) + return NULL; + + return c->bitmap; +} + +/** + * Retrieve the low-level cache handle for a content + * + * \param h Content to retrieve from + * \return Low-level cache handle + */ +const llcache_handle *content_get_llcache_handle(struct content *c) +{ + if (c == NULL) + return NULL; + + return 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 db35b39a2..1dd7f83cd 100644 --- a/content/content.h +++ b/content/content.h @@ -26,64 +26,31 @@ #ifndef _NETSURF_CONTENT_CONTENT_H_ #define _NETSURF_CONTENT_CONTENT_H_ -/* Irritatingly this must come first, or odd include errors - * will occur to do with setjmp.h. - */ -#ifdef WITH_PNG -#include "image/png.h" -#endif +#include <stdbool.h> -#include <stdint.h> -#include <time.h> #include "utils/config.h" #include "content/content_type.h" -#include "css/css.h" -#include "render/html.h" -#include "render/textplain.h" -#ifdef WITH_JPEG -#include "image/jpeg.h" -#endif -#ifdef WITH_GIF -#include "image/gif.h" -#endif -#ifdef WITH_BMP -#include "image/bmp.h" -#include "image/ico.h" -#endif -#ifdef WITH_PLUGIN -#include "riscos/plugin.h" -#endif -#ifdef WITH_MNG -#include "image/mng.h" -#endif -#ifdef WITH_SPRITE -#include "riscos/sprite.h" -#endif -#ifdef WITH_NSSPRITE -#include "image/nssprite.h" -#endif -#ifdef WITH_DRAW -#include "riscos/draw.h" -#endif -#ifdef WITH_ARTWORKS -#include "riscos/artworks.h" -#endif -#ifdef WITH_NS_SVG -#include "image/svg.h" -#endif -#ifdef WITH_RSVG -#include "image/rsvg.h" -#endif +#include "desktop/plot_style.h" +struct llcache_handle; -struct bitmap; struct box; struct browser_window; struct content; -struct fetch; +struct hlcache_handle; struct object_params; -struct ssl_cert_info; +/** Status of a content */ +typedef enum { + CONTENT_STATUS_TYPE_UNKNOWN, /**< Type not yet known. */ + CONTENT_STATUS_LOADING, /**< Content is being fetched or + converted and is not safe to display. */ + CONTENT_STATUS_READY, /**< Some parts of content still being + loaded, but can be displayed. */ + CONTENT_STATUS_DONE, /**< All finished. */ + CONTENT_STATUS_ERROR /**< Error occurred, content will be + destroyed imminently. */ +} content_status; /** Used in callbacks to indicate what has occurred. */ typedef enum { @@ -129,204 +96,64 @@ union content_msg_data { } ssl; }; -struct cache_data { - time_t req_time; /**< Time of request */ - time_t res_time; /**< Time of response */ - time_t date; /**< Date: response header */ - time_t expires; /**< Expires: response header */ -#define INVALID_AGE -1 - int age; /**< Age: response header */ - int max_age; /**< Max-age Cache-control parameter */ - bool no_cache; /**< no-cache Cache-control parameter */ - char *etag; /**< Etag: response header */ - time_t last_modified; /**< Last-Modified: response header */ -}; - -/** Linked list of users of a content. */ -struct content_user -{ - void (*callback)(content_msg msg, struct content *c, intptr_t p1, - intptr_t p2, union content_msg_data data); - intptr_t p1; - intptr_t p2; - bool stop; - struct content_user *next; -}; - -/** Corresponds to a single URL. */ -struct content { - char *url; /**< URL, in standard form as from url_join. */ - content_type type; /**< Type of content. */ - char *mime_type; /**< Original MIME type of data, or 0. */ - enum { - CONTENT_STATUS_TYPE_UNKNOWN, /**< Type not yet known. */ - CONTENT_STATUS_LOADING, /**< Content is being fetched or - converted and is not safe to display. */ - CONTENT_STATUS_READY, /**< Some parts of content still being - loaded, but can be displayed. */ - CONTENT_STATUS_DONE, /**< All finished. */ - CONTENT_STATUS_ERROR /**< Error occurred, content will be - destroyed imminently. */ - } status; /**< Current status. */ - - int width, height; /**< Dimensions, if applicable. */ - int available_width; /**< Available width (eg window width). */ - - /** Data dependent on type. */ - union { - struct content_html_data html; - struct content_textplain_data textplain; - struct content_css_data css; -#ifdef WITH_JPEG - struct content_jpeg_data jpeg; -#endif -#ifdef WITH_GIF - struct content_gif_data gif; -#endif -#ifdef WITH_BMP - struct content_bmp_data bmp; - struct content_ico_data ico; -#endif -#ifdef WITH_MNG - struct content_mng_data mng; -#endif -#ifdef WITH_SPRITE - struct content_sprite_data sprite; -#endif -#ifdef WITH_NSSPRITE - struct content_nssprite_data nssprite; -#endif -#ifdef WITH_DRAW - struct content_draw_data draw; -#endif -#ifdef WITH_PLUGIN - struct content_plugin_data plugin; -#endif -#ifdef WITH_ARTWORKS - struct content_artworks_data artworks; -#endif -#ifdef WITH_NS_SVG - struct content_svg_data svg; -#endif -#ifdef WITH_RSVG - struct content_rsvg_data rsvg; -#endif -#ifdef WITH_PNG - struct content_png_data png; -#endif - } data; - - /**< URL for refresh request, in standard form as from url_join. */ - char *refresh; - - /** Bitmap, for various image contents. */ - struct bitmap *bitmap; - - /** This content may be given to new users. Indicates that the content - * was fetched using a simple GET, has not expired, and may be - * shared between users. */ - bool fresh; - struct cache_data cache_data; /**< Cache control data */ - unsigned int time; /**< Creation time, if TYPE_UNKNOWN, - LOADING or READY, - otherwise total time. */ - - unsigned int reformat_time; /**< Earliest time to attempt a - period reflow while fetching a - page's objects. */ - - unsigned int size; /**< Estimated size of all data - associated with this content, except - alloced as talloc children of this. */ - off_t talloc_size; /**< Used by content_clean() */ - char *title; /**< Title for browser window. */ - unsigned int active; /**< Number of child fetches or - conversions currently in progress. */ - struct content_user *user_list; /**< List of users. */ - char status_message[120]; /**< Full text for status bar. */ - char sub_status[80]; /**< Status of content. */ - /** Content is being processed: data structures may be inconsistent - * and content must not be redrawn or modified. */ - bool locked; - - struct fetch *fetch; /**< Associated fetch, or 0. */ - char *source_data; /**< Source data, as received. */ - unsigned long source_size; /**< Amount of data fetched so far. */ - unsigned long source_allocated; /**< Amount of space allocated so far. */ - unsigned long total_size; /**< Total data size, 0 if unknown. */ - long http_code; /**< HTTP status code, 0 if not HTTP. */ - - bool no_error_pages; /**< Used by fetchcache(). */ - bool download; /**< Used by fetchcache(). */ - bool tried_with_auth; /**< Used by fetchcache(). */ - unsigned int redirect_count; /**< Used by fetchcache(). */ - - /** Array of first n rendering errors or warnings. */ - struct { - const char *token; - unsigned int line; /**< Line no, 0 if not applicable. */ - } error_list[40]; - unsigned int error_count; /**< Number of valid error entries. */ - - struct content *prev; /**< Previous in global content list. */ - struct content *next; /**< Next in global content list. */ -}; +/* The following are for hlcache */ +content_type content_lookup(const char *mime_type); +struct content *content_create(struct llcache_handle *llcache, + const char *fallback_charset, bool quirks); +void content_destroy(struct content *c); + +bool content_add_user(struct content *h, + void (*callback)(struct content *c, content_msg msg, + union content_msg_data data, void *pw), + void *pw); +void content_remove_user(struct content *c, + void (*callback)(struct content *c, content_msg msg, + union content_msg_data data, void *pw), + void *pw); -extern struct content *content_list; -extern const char * const content_type_name[]; -extern const char * const content_status_name[]; +const struct llcache_handle *content_get_llcache_handle(struct content *c); -content_type content_lookup(const char *mime_type); -struct content * content_create(const char *url); -struct content * content_get(const char *url); -struct content * content_get_ready(const char *url); -bool content_can_reformat(struct content *c); -bool content_set_type(struct content *c, content_type type, - const char *mime_type, const char *params[], - struct content *parent); -void content_set_status(struct content *c, const char *status_message, ...); -bool content_process_data(struct content *c, const char *data, - unsigned int size); -void content_convert(struct content *c, int width, int height); -void content_set_done(struct content *c); -void content_reformat(struct content *c, int width, int height); -void content_clean(void); -void content_reset(struct content *c); -void content_quit(void); -bool content_redraw(struct content *c, int x, int y, +/* Client functions */ +bool content_can_reformat(struct hlcache_handle *h); +void content_reformat(struct hlcache_handle *h, int width, int height); +void content_request_redraw(struct hlcache_handle *h, + int x, int y, int width, int height); +bool content_redraw(struct hlcache_handle *h, int x, int y, int width, int height, int clip_x0, int clip_y0, int clip_x1, int clip_y1, float scale, colour background_colour); -bool content_redraw_tiled(struct content *c, int x, int y, +bool content_redraw_tiled(struct hlcache_handle *h, int x, int y, int width, int height, int clip_x0, int clip_y0, int clip_x1, int clip_y1, float scale, colour background_colour, bool repeat_x, bool repeat_y); -bool content_add_user(struct content *c, - void (*callback)(content_msg msg, struct content *c, - intptr_t p1, intptr_t p2, union content_msg_data data), - intptr_t p1, intptr_t p2); -struct content_user * content_find_user(struct content *c, - void (*callback)(content_msg msg, struct content *c, - intptr_t p1, intptr_t p2, union content_msg_data data), - intptr_t p1, intptr_t p2); -void content_remove_user(struct content *c, - void (*callback)(content_msg msg, struct content *c, - intptr_t p1, intptr_t p2, union content_msg_data data), - intptr_t p1, intptr_t p2); -void content_broadcast(struct content *c, content_msg msg, - union content_msg_data data); -void content_stop(struct content *c, - void (*callback)(content_msg msg, struct content *c, - intptr_t p1, intptr_t p2, union content_msg_data data), - intptr_t p1, intptr_t p2); -void content_open(struct content *c, struct browser_window *bw, +void content_stop(struct hlcache_handle *h, + void (*callback)(struct content *c, content_msg msg, + union content_msg_data data, void *pw), + void *pw); +void content_open(struct hlcache_handle *h, struct browser_window *bw, struct content *page, unsigned int index, struct box *box, struct object_params *params); -void content_close(struct content *c); -void content_add_error(struct content *c, const char *token, - unsigned int line); +void content_close(struct hlcache_handle *h); + +/* Member accessors */ +content_type content_get_type(struct hlcache_handle *c); +const char *content_get_url(struct hlcache_handle *c); +const char *content_get_title(struct hlcache_handle *c); +content_status content_get_status(struct hlcache_handle *c); +const char *content_get_status_message(struct hlcache_handle *c); +int content_get_width(struct hlcache_handle *c); +int content_get_height(struct hlcache_handle *c); +int content_get_available_width(struct hlcache_handle *c); +const char *content_get_source_data(struct hlcache_handle *c, + unsigned long *size); +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/content_protected.h b/content/content_protected.h new file mode 100644 index 000000000..261ee7bcb --- /dev/null +++ b/content/content_protected.h @@ -0,0 +1,221 @@ +/* + * Copyright 2005-2007 James Bursa <bursa@users.sourceforge.net> + * Copyright 2003 Philip Pemberton <philpem@users.sourceforge.net> + * + * 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 + * Content handling (interface). + * + * The content functions manipulate struct contents, which correspond to URLs. + */ + +#ifndef _NETSURF_CONTENT_CONTENT_PROTECTED_H_ +#define _NETSURF_CONTENT_CONTENT_PROTECTED_H_ + +/* Irritatingly this must come first, or odd include errors + * will occur to do with setjmp.h. + */ +#ifdef WITH_PNG +#include "image/png.h" +#endif + +#include <stdint.h> +#include <time.h> +#include "utils/config.h" +#include "content/content.h" +#include "content/llcache.h" +#include "css/css.h" +#include "render/html.h" +#include "render/textplain.h" +#ifdef WITH_JPEG +#include "image/jpeg.h" +#endif +#ifdef WITH_GIF +#include "image/gif.h" +#endif +#ifdef WITH_BMP +#include "image/bmp.h" +#include "image/ico.h" +#endif +#ifdef WITH_PLUGIN +#include "riscos/plugin.h" +#endif +#ifdef WITH_MNG +#include "image/mng.h" +#endif +#ifdef WITH_SPRITE +#include "riscos/sprite.h" +#endif +#ifdef WITH_NSSPRITE +#include "image/nssprite.h" +#endif +#ifdef WITH_DRAW +#include "riscos/draw.h" +#endif +#ifdef WITH_ARTWORKS +#include "riscos/artworks.h" +#endif +#ifdef WITH_NS_SVG +#include "image/svg.h" +#endif +#ifdef WITH_RSVG +#include "image/rsvg.h" +#endif + + +struct bitmap; +struct content; + +/** Linked list of users of a content. */ +struct content_user +{ + void (*callback)(struct content *c, content_msg msg, + union content_msg_data data, void *pw); + void *pw; + + struct content_user *next; +}; + +/** Corresponds to a single URL. */ +struct content { + llcache_handle *llcache; /**< Low-level cache object */ + + content_type type; /**< Type of content. */ + char *mime_type; /**< Original MIME type of data, or 0. */ + + content_status status; /**< Current status. */ + + int width, height; /**< Dimensions, if applicable. */ + int available_width; /**< Available width (eg window width). */ + + bool quirks; /**< Content is in quirks mode */ + char *fallback_charset; /**< Fallback charset, or NULL */ + + /** Data dependent on type. */ + union { + struct content_html_data html; + struct content_textplain_data textplain; + struct content_css_data css; +#ifdef WITH_JPEG + struct content_jpeg_data jpeg; +#endif +#ifdef WITH_GIF + struct content_gif_data gif; +#endif +#ifdef WITH_BMP + struct content_bmp_data bmp; + struct content_ico_data ico; +#endif +#ifdef WITH_MNG + struct content_mng_data mng; +#endif +#ifdef WITH_SPRITE + struct content_sprite_data sprite; +#endif +#ifdef WITH_NSSPRITE + struct content_nssprite_data nssprite; +#endif +#ifdef WITH_DRAW + struct content_draw_data draw; +#endif +#ifdef WITH_PLUGIN + struct content_plugin_data plugin; +#endif +#ifdef WITH_ARTWORKS + struct content_artworks_data artworks; +#endif +#ifdef WITH_NS_SVG + struct content_svg_data svg; +#endif +#ifdef WITH_RSVG + struct content_rsvg_data rsvg; +#endif +#ifdef WITH_PNG + struct content_png_data png; +#endif + } data; + + /**< URL for refresh request, in standard form as from url_join. */ + char *refresh; + + /** Bitmap, for various image contents. */ + struct bitmap *bitmap; + + /** This content may be given to new users. Indicates that the content + * was fetched using a simple GET, has not expired, and may be + * shared between users. */ + bool fresh; + unsigned int time; /**< Creation time, if TYPE_UNKNOWN, + LOADING or READY, + otherwise total time. */ + + unsigned int reformat_time; /**< Earliest time to attempt a + period reflow while fetching a + page's objects. */ + + unsigned int size; /**< Estimated size of all data + associated with this content, except + alloced as talloc children of this. */ + off_t talloc_size; /**< Used by content_clean() */ + char *title; /**< Title for browser window. */ + unsigned int active; /**< Number of child fetches or + conversions currently in progress. */ + struct content_user *user_list; /**< List of users. */ + char status_message[120]; /**< Full text for status bar. */ + char sub_status[80]; /**< Status of content. */ + /** Content is being processed: data structures may be inconsistent + * and content must not be redrawn or modified. */ + bool locked; + + unsigned long total_size; /**< Total data size, 0 if unknown. */ + long http_code; /**< HTTP status code, 0 if not HTTP. */ + + /** Array of first n rendering errors or warnings. */ + struct { + const char *token; + unsigned int line; /**< Line no, 0 if not applicable. */ + } error_list[40]; + unsigned int error_count; /**< Number of valid error entries. */ +}; + +extern const char * const content_type_name[]; +extern const char * const content_status_name[]; + +void content_set_done(struct content *c); +void content_set_status(struct content *c, const char *status_message, ...); +void content_broadcast(struct content *c, content_msg msg, + union content_msg_data data); +void content_add_error(struct content *c, const char *token, + unsigned int line); + +void content__reformat(struct content *c, int width, int height); + + +content_type content__get_type(struct content *c); +const char *content__get_url(struct content *c); +const char *content__get_title(struct content *c); +content_status content__get_status(struct content *c); +const char *content__get_status_message(struct content *c); +int content__get_width(struct content *c); +int content__get_height(struct content *c); +int content__get_available_width(struct content *c); +const char *content__get_source_data(struct content *c, unsigned long *size); +void content__invalidate_reuse_data(struct content *c); +const char *content__get_refresh_url(struct content *c); +struct bitmap *content__get_bitmap(struct content *c); + +#endif diff --git a/content/fetch.c b/content/fetch.c index f835ac121..627d7caf0 100644 --- a/content/fetch.c +++ b/content/fetch.c @@ -41,7 +41,6 @@ #include "content/urldb.h" #include "desktop/netsurf.h" #include "desktop/options.h" -#include "render/form.h" #include "utils/log.h" #include "utils/messages.h" #include "utils/url.h" @@ -213,7 +212,7 @@ void fetch_unref_fetcher(scheme_fetcher *fetcher) struct fetch * fetch_start(const char *url, const char *referer, fetch_callback callback, void *p, bool only_2xx, const char *post_urlenc, - struct form_successful_control *post_multipart, + struct fetch_multipart_data *post_multipart, bool verifiable, struct content *parent, char *headers[]) { @@ -598,6 +597,78 @@ bool fetch_get_verifiable(struct fetch *fetch) return fetch->verifiable; } +/** + * Clone a linked list of fetch_multipart_data. + * + * \param list List to clone + * \return Pointer to head of cloned list, or NULL on failure + */ +struct fetch_multipart_data *fetch_multipart_data_clone( + const struct fetch_multipart_data *list) +{ + struct fetch_multipart_data *clone, *last = NULL; + struct fetch_multipart_data *result = NULL; + + for (; list != NULL; list = list->next) { + clone = malloc(sizeof(struct fetch_multipart_data)); + if (clone == NULL) { + if (result != NULL) + fetch_multipart_data_destroy(result); + + return NULL; + } + + clone->file = list->file; + + clone->name = strdup(list->name); + if (clone->name == NULL) { + free(clone); + if (result != NULL) + fetch_multipart_data_destroy(result); + + return NULL; + } + + clone->value = strdup(list->value); + if (clone->value == NULL) { + free(clone->name); + free(clone); + if (result != NULL) + fetch_multipart_data_destroy(result); + + return NULL; + } + + clone->next = NULL; + + if (result == NULL) + result = clone; + else + last->next = clone; + + last = clone; + } + + return result; +} + +/** + * Free a linked list of fetch_multipart_data. + * + * \param list Pointer to head of list to free + */ +void fetch_multipart_data_destroy(struct fetch_multipart_data *list) +{ + struct fetch_multipart_data *next; + + for (; list != NULL; list = next) { + next = list->next; + free(list->name); + free(list->value); + free(list); + } +} + void fetch_send_callback(fetch_msg msg, struct fetch *fetch, const void *data, unsigned long size, fetch_error_code errorcode) @@ -665,8 +736,8 @@ fetch_set_cookie(struct fetch *fetch, const char *data) * that the request uri and the parent domain match, * so don't pass in the parent in this case. */ urldb_set_cookie(data, fetch->url, - fetch->verifiable ? NULL - : fetch->parent->url); + fetch->verifiable ? NULL + : content_get_url(fetch->parent)); } } diff --git a/content/fetch.h b/content/fetch.h index 168c9b252..16dae63d0 100644 --- a/content/fetch.h +++ b/content/fetch.h @@ -54,7 +54,15 @@ typedef enum { struct content; struct fetch; -struct form_successful_control; + +/** Fetch POST multipart data */ +struct fetch_multipart_data { + bool file; /**< Item is a file */ + char *name; /**< Name of item */ + char *value; /**< Item value */ + + struct fetch_multipart_data *next; /**< Next in linked list */ +}; struct ssl_cert_info { long version; /**< Certificate version */ @@ -77,7 +85,7 @@ void fetch_init(void); struct fetch * fetch_start(const char *url, const char *referer, fetch_callback callback, void *p, bool only_2xx, const char *post_urlenc, - struct form_successful_control *post_multipart, + struct fetch_multipart_data *post_multipart, bool verifiable, struct content *parent, char *headers[]); void fetch_abort(struct fetch *f); @@ -94,12 +102,16 @@ const char *fetch_get_referer(struct fetch *fetch); struct content *fetch_get_parent(struct fetch *fetch); bool fetch_get_verifiable(struct fetch *fetch); +void fetch_multipart_data_destroy(struct fetch_multipart_data *list); +struct fetch_multipart_data *fetch_multipart_data_clone( + const struct fetch_multipart_data *list); + /* API for fetchers themselves */ typedef bool (*fetcher_initialise)(const char *); typedef void* (*fetcher_setup_fetch)(struct fetch *, const char *, bool, const char *, - struct form_successful_control *, + struct fetch_multipart_data *, const char **); typedef bool (*fetcher_start_fetch)(void *); typedef void (*fetcher_abort_fetch)(void *); diff --git a/content/fetchcache.c b/content/fetchcache.c index 243d5c04b..3a0b667f9 100644 --- a/content/fetchcache.c +++ b/content/fetchcache.c @@ -102,7 +102,7 @@ struct content * fetchcache(const char *url, int width, int height, bool no_error_pages, char *post_urlenc, - struct form_successful_control *post_multipart, + struct fetch_multipart_data *post_multipart, bool verifiable, bool download) { @@ -250,7 +250,7 @@ void fetchcache_go(struct content *content, const char *referer, intptr_t p1, intptr_t p2, int width, int height, char *post_urlenc, - struct form_successful_control *post_multipart, + struct fetch_multipart_data *post_multipart, bool verifiable, struct content *parent) { char error_message[500]; diff --git a/content/fetchcache.h b/content/fetchcache.h index 24ed564ad..46e9b3d94 100644 --- a/content/fetchcache.h +++ b/content/fetchcache.h @@ -30,7 +30,7 @@ #include <stdint.h> #include "content/content.h" -struct form_successful_control; +struct fetch_multipart_data; void fetchcache_init(void); struct content * fetchcache(const char *url, @@ -40,7 +40,7 @@ struct content * fetchcache(const char *url, int width, int height, bool no_error_pages, char *post_urlenc, - struct form_successful_control *post_multipart, + struct fetch_multipart_data *post_multipart, bool verifiable, bool download); void fetchcache_go(struct content *content, const char *referer, @@ -49,7 +49,7 @@ void fetchcache_go(struct content *content, const char *referer, intptr_t p1, intptr_t p2, int width, int height, char *post_urlenc, - struct form_successful_control *post_multipart, + struct fetch_multipart_data *post_multipart, bool verifiable, struct content *parent); #endif diff --git a/content/fetchers/fetch_curl.c b/content/fetchers/fetch_curl.c index ca2d86845..9ac3ad7b3 100644 --- a/content/fetchers/fetch_curl.c +++ b/content/fetchers/fetch_curl.c @@ -30,6 +30,7 @@ #include <assert.h> #include <errno.h> +#include <inttypes.h> #include <stdbool.h> #include <string.h> #include <strings.h> @@ -43,7 +44,6 @@ #include "content/urldb.h" #include "desktop/netsurf.h" #include "desktop/options.h" -#include "render/form.h" #include "utils/log.h" #include "utils/messages.h" #include "utils/url.h" @@ -104,7 +104,7 @@ static bool fetch_curl_initialise(const char *scheme); static void fetch_curl_finalise(const char *scheme); static void * fetch_curl_setup(struct fetch *parent_fetch, const char *url, bool only_2xx, const char *post_urlenc, - struct form_successful_control *post_multipart, + struct fetch_multipart_data *post_multipart, const char **headers); static bool fetch_curl_start(void *vfetch); static bool fetch_curl_initiate_fetch(struct curl_fetch_info *fetch, @@ -132,7 +132,7 @@ static size_t fetch_curl_header(char *data, size_t size, size_t nmemb, void *_f); static bool fetch_curl_process_headers(struct curl_fetch_info *f); static struct curl_httppost *fetch_curl_post_convert( - struct form_successful_control *control); + struct fetch_multipart_data *control); static int fetch_curl_verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx); static int fetch_curl_cert_verify_callback(X509_STORE_CTX *x509_ctx, @@ -294,7 +294,7 @@ void fetch_curl_finalise(const char *scheme) void * fetch_curl_setup(struct fetch *parent_fetch, const char *url, bool only_2xx, const char *post_urlenc, - struct form_successful_control *post_multipart, + struct fetch_multipart_data *post_multipart, const char **headers) { char *host; @@ -1108,10 +1108,7 @@ size_t fetch_curl_header(char *data, size_t size, size_t nmemb, bool fetch_curl_process_headers(struct curl_fetch_info *f) { long http_code; - const char *type; CURLcode code; - struct stat s; - char *url_path = 0; f->had_headers = true; @@ -1142,7 +1139,7 @@ bool fetch_curl_process_headers(struct curl_fetch_info *f) /* handle HTTP 401 (Authentication errors) */ if (http_code == 401) { - fetch_send_callback(FETCH_AUTH, f->fetch_handle, f->realm,0, + fetch_send_callback(FETCH_AUTH, f->fetch_handle, f->realm, 0, FETCH_ERROR_AUTHENTICATION); return true; } @@ -1156,49 +1153,64 @@ bool fetch_curl_process_headers(struct curl_fetch_info *f) return true; } - /* find MIME type from headers or filetype for local files */ - code = curl_easy_getinfo(f->curl_handle, CURLINFO_CONTENT_TYPE, &type); - assert(code == CURLE_OK); - - if (strncmp(f->url, "file:///", 8) == 0) - url_path = curl_unescape(f->url + 7, - (int) strlen(f->url) - 7); - - if (url_path && stat(url_path, &s) == 0) { - /* file: URL and file exists */ - /* create etag */ - char etag_buf[20]; - snprintf(etag_buf, sizeof etag_buf, - "ETag: \"%10d\"", (int) s.st_mtime); - /* And send it to the header handler */ - fetch_send_callback(FETCH_HEADER, f->fetch_handle, etag_buf, - strlen(etag_buf), FETCH_ERROR_NO_ERROR); - - /* don't set last modified time so as to ensure that local - * files are revalidated at all times. */ - - /* If performed a conditional request and unmodified ... */ - if (f->last_modified && f->file_etag && - f->last_modified > s.st_mtime && - f->file_etag == s.st_mtime) { - fetch_send_callback(FETCH_NOTMODIFIED, f->fetch_handle, - 0, 0, FETCH_ERROR_NO_ERROR); - curl_free(url_path); - return true; - } - } - - if (type == 0) { - type = "text/plain"; - if (url_path) { + /* find MIME type from filetype for local files */ + if (strncmp(f->url, "file:///", 8) == 0) { + struct stat s; + char *url_path = curl_unescape(f->url + 7, + (int) strlen(f->url + 7)); + + if (url_path != NULL && stat(url_path, &s) == 0) { + /* file: URL and file exists */ + char header[64]; + const char *type; + + /* create etag */ + snprintf(header, sizeof header, + "ETag: \"%10" PRId64 "\"", + (int64_t) s.st_mtime); + /* And send it to the header handler */ + fetch_send_callback(FETCH_HEADER, f->fetch_handle, + header, strlen(header), + FETCH_ERROR_NO_ERROR); + + /* create Content-Type */ type = fetch_filetype(url_path); + snprintf(header, sizeof header, + "Content-Type: %s", type); + /* Send it to the header handler */ + fetch_send_callback(FETCH_HEADER, f->fetch_handle, + header, strlen(header), + FETCH_ERROR_NO_ERROR); + + /* create Content-Length */ + type = fetch_filetype(url_path); + snprintf(header, sizeof header, + "Content-Length: %" PRId64, + (int64_t) s.st_size); + /* Send it to the header handler */ + fetch_send_callback(FETCH_HEADER, f->fetch_handle, + header, strlen(header), + FETCH_ERROR_NO_ERROR); + + /* don't set last modified time so as to ensure that + * local files are revalidated at all times. */ + + /* Report not modified, if appropriate */ + if (f->last_modified && f->file_etag && + f->last_modified > s.st_mtime && + f->file_etag == s.st_mtime) { + fetch_send_callback(FETCH_NOTMODIFIED, + f->fetch_handle, 0, 0, + FETCH_ERROR_NO_ERROR); + curl_free(url_path); + return true; + } } - } - curl_free(url_path); + if (url_path != NULL) + curl_free(url_path); + } - LOG(("FETCH_TYPE, '%s'", type)); - fetch_send_callback(FETCH_TYPE, f->fetch_handle, type, f->content_length, FETCH_ERROR_NO_ERROR); if (f->abort) return true; @@ -1207,11 +1219,11 @@ bool fetch_curl_process_headers(struct curl_fetch_info *f) /** - * Convert a list of struct ::form_successful_control to a list of + * Convert a list of struct ::fetch_multipart_data to a list of * struct curl_httppost for libcurl. */ struct curl_httppost * -fetch_curl_post_convert(struct form_successful_control *control) +fetch_curl_post_convert(struct fetch_multipart_data *control) { struct curl_httppost *post = 0, *last = 0; CURLFORMcode code; diff --git a/content/fetchers/fetch_data.c b/content/fetchers/fetch_data.c index f2a90c50d..1790de56f 100644 --- a/content/fetchers/fetch_data.c +++ b/content/fetchers/fetch_data.c @@ -35,7 +35,6 @@ #include "content/urldb.h" #include "desktop/netsurf.h" #include "desktop/options.h" -#include "render/form.h" #include "utils/log.h" #include "utils/messages.h" #include "utils/url.h" @@ -78,7 +77,7 @@ static void fetch_data_finalise(const char *scheme) static void *fetch_data_setup(struct fetch *parent_fetch, const char *url, bool only_2xx, const char *post_urlenc, - struct form_successful_control *post_multipart, + struct fetch_multipart_data *post_multipart, const char **headers) { struct fetch_data_context *ctx = calloc(1, sizeof(*ctx)); @@ -232,20 +231,9 @@ static bool fetch_data_process(struct fetch_data_context *c) static void fetch_data_poll(const char *scheme) { struct fetch_data_context *c, *next; - struct cache_data cachedata; if (ring == NULL) return; - cachedata.req_time = time(NULL); - cachedata.res_time = time(NULL); - cachedata.date = 0; - cachedata.expires = 0; - cachedata.age = INVALID_AGE; - cachedata.max_age = 0; - cachedata.no_cache = true; - cachedata.etag = NULL; - cachedata.last_modified = 0; - /* Iterate over ring, processing each pending fetch */ c = ring; do { @@ -265,6 +253,8 @@ static void fetch_data_poll(const char *scheme) /* Only process non-aborted fetches */ if (!c->aborted && fetch_data_process(c) == true) { + char header[64]; + fetch_set_http_code(c->parent_fetch, 200); LOG(("setting data: MIME type to %s, length to %zd", c->mimetype, c->datalen)); @@ -272,9 +262,16 @@ static void fetch_data_poll(const char *scheme) * Therefore, we _must_ check for this after _every_ * call to fetch_data_send_callback(). */ - fetch_data_send_callback(FETCH_TYPE, - c, c->mimetype, c->datalen, - FETCH_ERROR_NO_ERROR); + snprintf(header, sizeof header, "Content-Type: %s", + c->mimetype); + fetch_data_send_callback(FETCH_HEADER, c, header, + strlen(header), FETCH_ERROR_NO_ERROR); + + snprintf(header, sizeof header, "Content-Length: %zd", + c->datalen); + fetch_data_send_callback(FETCH_HEADER, c, header, + strlen(header), FETCH_ERROR_NO_ERROR); + if (!c->aborted) { fetch_data_send_callback(FETCH_DATA, c, c->data, c->datalen, @@ -282,8 +279,7 @@ static void fetch_data_poll(const char *scheme) } if (!c->aborted) { fetch_data_send_callback(FETCH_FINISHED, - c, &cachedata, 0, - FETCH_ERROR_NO_ERROR); + c, 0, 0, FETCH_ERROR_NO_ERROR); } } else { LOG(("Processing of %s failed!", c->url)); diff --git a/content/hlcache.c b/content/hlcache.c new file mode 100644 index 000000000..94d5f0036 --- /dev/null +++ b/content/hlcache.c @@ -0,0 +1,362 @@ +/* + * Copyright 2009 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 + * High-level resource cache (implementation) + */ + +#include <assert.h> +#include <stdlib.h> +#include <string.h> + +#include "content/content.h" +#include "content/hlcache.h" +#include "utils/log.h" +#include "utils/url.h" + +typedef struct hlcache_entry hlcache_entry; +typedef struct hlcache_retrieval_ctx hlcache_retrieval_ctx; + +/** High-level cache retrieval context */ +struct hlcache_retrieval_ctx { + llcache_handle *llcache; /**< Low-level cache handle */ + + hlcache_handle *handle; /**< High-level handle for object */ + + /* The following are only used if a child content is requested */ + const char *charset; /**< Fallback charset, or NULL */ + bool quirks; /**< Whether object should be quirky */ +}; + +/** High-level cache handle */ +struct hlcache_handle { + hlcache_entry *entry; /**< Pointer to cache entry */ + + hlcache_handle_callback cb; /**< Client callback */ + void *pw; /**< Client data */ +}; + +/** Entry in high-level cache */ +struct hlcache_entry { + struct content *content; /**< Pointer to associated content */ + + hlcache_entry *next; /**< Next sibling */ + hlcache_entry *prev; /**< Previous sibling */ +}; + +/** List of cached content objects */ +static hlcache_entry *hlcache_content_list; + +static nserror hlcache_llcache_callback(llcache_handle *handle, + const llcache_event *event, void *pw); +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); + +/****************************************************************************** + * Public API * + ******************************************************************************/ + +/** + * Retrieve a high-level cache handle for an object + * + * \param url URL of the object to retrieve handle for + * \param flags Object retrieval flags + * \param referer Referring URL, or NULL if none + * \param post POST data, or NULL for a GET request + * \param width Available width for content + * \param height Available height for content + * \param cb Callback to handle object events + * \param pw Pointer to client-specific data for callback + * \param child Child retrieval context, or NULL for top-level content + * \param result Pointer to location to recieve cache handle + * \return NSERROR_OK on success, appropriate error otherwise + * + * \todo Is there any way to sensibly reduce the number of parameters here? + */ +nserror hlcache_handle_retrieve(const char *url, uint32_t flags, + const char *referer, llcache_post_data *post, + uint32_t width, uint32_t height, + hlcache_handle_callback cb, void *pw, + hlcache_child_context *child, hlcache_handle **result) +{ + hlcache_retrieval_ctx *ctx; + nserror error; + + assert(cb != NULL); + + ctx = calloc(1, sizeof(hlcache_retrieval_ctx)); + if (ctx == NULL) + return NSERROR_NOMEM; + + ctx->handle = calloc(1, sizeof(hlcache_handle)); + if (ctx->handle == NULL) { + free(ctx); + return NSERROR_NOMEM; + } + + if (child != NULL) { + /** \todo Is the charset guaranteed to exist during fetch? */ + ctx->charset = child->charset; + ctx->quirks = child->quirks; + } + + /** \todo What happens with width/height? */ + + ctx->handle->cb = cb; + ctx->handle->pw = pw; + + error = llcache_handle_retrieve(url, flags, referer, post, + hlcache_llcache_callback, ctx, + &ctx->llcache); + if (error != NSERROR_OK) { + free(ctx->handle); + free(ctx); + return error; + } + + *result = ctx->handle; + + return NSERROR_OK; +} + +/** + * Release a high-level cache handle + * + * \param handle Handle to release + * \return NSERROR_OK on success, appropriate error otherwise + */ +nserror hlcache_handle_release(hlcache_handle *handle) +{ + /** \todo What if this is called during fetch? */ + + if (handle->entry != NULL) { + content_remove_user(handle->entry->content, + hlcache_content_callback, handle); + } + + handle->cb = NULL; + handle->pw = NULL; + + /** \todo Provide hlcache_poll() to perform cache maintenance */ + + return NSERROR_OK; +} + +/** + * Retrieve a content object from a cache handle + * + * \param handle Cache handle to dereference + * \return Pointer to content object, or NULL if there is none + * + * \todo This may not be correct. Ideally, the client should never need to + * directly access a content object. It may, therefore, be better to provide a + * bunch of veneers here that take a hlcache_handle and invoke the + * corresponding content_ API. If there's no content object associated with the + * hlcache_handle (e.g. because the source data is still being fetched, so it + * doesn't exist yet), then these veneers would behave as a NOP. The important + * thing being that the client need not care about this possibility and can + * just call the functions with impugnity. + */ +struct content *hlcache_handle_get_content(const hlcache_handle *handle) +{ + assert(handle != NULL); + + if (handle->entry != NULL) + return handle->entry->content; + + return NULL; +} + +/****************************************************************************** + * High-level cache internals * + ******************************************************************************/ + +/** + * Handler for low-level cache events + * + * \param handle Handle for which event is issued + * \param event Event data + * \param pw Pointer to client-specific data + * \return NSERROR_OK on success, appropriate error otherwise + */ +nserror hlcache_llcache_callback(llcache_handle *handle, + const llcache_event *event, void *pw) +{ + hlcache_retrieval_ctx *ctx = pw; + nserror error; + + assert(ctx->llcache == handle); + + switch (event->type) { + case LLCACHE_EVENT_HAD_HEADERS: + error = hlcache_find_content(ctx); + if (error != NSERROR_OK) + return error; + /* No longer require retrieval context */ + free(ctx); + break; + case LLCACHE_EVENT_HAD_DATA: + /* fall through */ + case LLCACHE_EVENT_DONE: + /* should never happen: the handler must be changed */ + break; + case LLCACHE_EVENT_ERROR: + /** \todo handle errors */ + break; + case LLCACHE_EVENT_PROGRESS: + break; + } + + return NSERROR_OK; +} + +/** + * Find a content for the high-level cache handle + * + * \param ctx High-level cache retrieval context + * \return NSERROR_OK on success, appropriate error otherwise + * + * \pre handle::state == HLCACHE_HANDLE_NEW + * \pre Headers must have been received for associated low-level handle + * \post Low-level handle is either released, or associated with new content + * \post High-level handle is registered with content + */ +nserror hlcache_find_content(hlcache_retrieval_ctx *ctx) +{ + hlcache_entry *entry; + hlcache_event event; + + /* Search list of cached contents for a suitable one */ + for (entry = hlcache_content_list; entry != NULL; entry = entry->next) { + const llcache_handle *entry_llcache; + + /** \todo Need to ensure that quirks mode matches */ + /** \todo Need to ensure that content is shareable */ + /** \todo Need to ensure that content can be reused */ + if (entry->content == NULL) + continue; + + /* Ensure that content uses same low-level object as + * low-level handle */ + entry_llcache = content_get_llcache_handle(entry->content); + + if (llcache_handle_references_same_object(entry_llcache, + ctx->llcache)) + break; + } + + if (entry == NULL) { + /* No existing entry, so need to create one */ + entry = malloc(sizeof(hlcache_entry)); + if (entry == NULL) + return NSERROR_NOMEM; + + /* Create content using llhandle */ + entry->content = content_create(ctx->llcache, + ctx->charset, ctx->quirks); + if (entry->content == NULL) { + free(entry); + return NSERROR_NOMEM; + } + + /* Insert into cache */ + entry->prev = NULL; + entry->next = hlcache_content_list; + if (hlcache_content_list != NULL) + hlcache_content_list->prev = entry; + hlcache_content_list = entry; + } else { + /* Found a suitable content: no longer need low-level handle */ + llcache_handle_release(ctx->llcache); + } + + /* Associate handle with content */ + if (content_add_user(entry->content, + hlcache_content_callback, ctx->handle) == false) + return NSERROR_NOMEM; + + /* Associate cache entry with handle */ + ctx->handle->entry = entry; + + /* Catch handle up with state of content */ + if (ctx->handle->cb != NULL) { + content_status status = content_get_status(ctx->handle); + + if (status == CONTENT_STATUS_LOADING) { + event.type = CONTENT_MSG_LOADING; + ctx->handle->cb(ctx->handle, &event, ctx->handle->pw); + } else if (status == CONTENT_STATUS_READY) { + event.type = CONTENT_MSG_LOADING; + ctx->handle->cb(ctx->handle, &event, ctx->handle->pw); + + if (ctx->handle->cb != NULL) { + event.type = CONTENT_MSG_READY; + ctx->handle->cb(ctx->handle, &event, + ctx->handle->pw); + } + } else if (status == CONTENT_STATUS_DONE) { + event.type = CONTENT_MSG_LOADING; + ctx->handle->cb(ctx->handle, &event, ctx->handle->pw); + + /** \todo Reflow content to new width + if (content_get_available_width(ctx->handle) != width) + content_reformat(ctx->handle, width, height); + */ + + if (ctx->handle->cb != NULL) { + event.type = CONTENT_MSG_READY; + ctx->handle->cb(ctx->handle, &event, + ctx->handle->pw); + } + + if (ctx->handle->cb != NULL) { + event.type = CONTENT_MSG_DONE; + ctx->handle->cb(ctx->handle, &event, + ctx->handle->pw); + } + } + } + + return NSERROR_OK; +} + +/** + * Veneer between content callback API and hlcache callback API + * + * \param c Content to emit message for + * \param msg Message to emit + * \param data Data for message + * \param pw Pointer to private data (hlcache_handle) + */ +void hlcache_content_callback(struct content *c, content_msg msg, + union content_msg_data data, void *pw) +{ + hlcache_handle *handle = pw; + hlcache_event event; + nserror error; + + event.type = msg; + event.data = data; + + + error = handle->cb(handle, &event, handle->pw); + if (error != NSERROR_OK) + LOG(("Error in callback: %d", error)); +} + diff --git a/content/hlcache.h b/content/hlcache.h new file mode 100644 index 000000000..fb6ba219c --- /dev/null +++ b/content/hlcache.h @@ -0,0 +1,110 @@ +/* + * Copyright 2009 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 + * High-level resource cache (interface) + */ + +#ifndef NETSURF_CONTENT_HLCACHE_H_ +#define NETSURF_CONTENT_HLCACHE_H_ + +#include "content/content.h" +#include "content/llcache.h" +#include "utils/errors.h" + +/** High-level cache handle */ +typedef struct hlcache_handle hlcache_handle; + +/** Context for retrieving a child object */ +typedef struct hlcache_child_context { + const char *charset; /**< Charset of parent */ + bool quirks; /**< Whether parent is quirky */ +} hlcache_child_context; + +/** High-level cache event */ +typedef struct { + content_msg type; /**< Event type */ + union content_msg_data data; /**< Event data */ +} hlcache_event; + +/** + * Client callback for high-level cache events + * + * \param handle Handle to object generating event + * \param event Event data + * \param pw Pointer to client-specific data + * \return NSERROR_OK on success, appropriate error otherwise. + */ +typedef nserror (*hlcache_handle_callback)(hlcache_handle *handle, + const hlcache_event *event, void *pw); + +/** + * Retrieve a high-level cache handle for an object + * + * \param url URL of the object to retrieve handle for + * \param flags Object retrieval flags + * \param referer Referring URL, or NULL if none + * \param post POST data, or NULL for a GET request + * \param width Available width for content + * \param height Available height for content + * \param cb Callback to handle object events + * \param pw Pointer to client-specific data for callback + * \param child Child retrieval context, or NULL for top-level content + * \param result Pointer to location to recieve cache handle + * \return NSERROR_OK on success, appropriate error otherwise + * + * Child contents are keyed on the tuple < URL, quirks >. + * The quirks field is ignored for child contents whose behaviour is not + * affected by quirks mode. + * + * \todo The above rules should be encoded in the handler_map. + * + * \todo Is there any way to sensibly reduce the number of parameters here? + */ +nserror hlcache_handle_retrieve(const char *url, uint32_t flags, + const char *referer, llcache_post_data *post, + uint32_t width, uint32_t height, + hlcache_handle_callback cb, void *pw, + hlcache_child_context *child, hlcache_handle **result); + +/** + * Release a high-level cache handle + * + * \param handle Handle to release + * \return NSERROR_OK on success, appropriate error otherwise + */ +nserror hlcache_handle_release(hlcache_handle *handle); + +/** + * Retrieve a content object from a cache handle + * + * \param handle Cache handle to dereference + * \return Pointer to content object, or NULL if there is none + * + * \todo This may not be correct. Ideally, the client should never need to + * directly access a content object. It may, therefore, be better to provide a + * bunch of veneers here that take a hlcache_handle and invoke the + * corresponding content_ API. If there's no content object associated with the + * hlcache_handle (e.g. because the source data is still being fetched, so it + * doesn't exist yet), then these veneers would behave as a NOP. The important + * thing being that the client need not care about this possibility and can + * just call the functions with impugnity. + */ +struct content *hlcache_handle_get_content(const hlcache_handle *handle); + +#endif diff --git a/content/llcache.c b/content/llcache.c new file mode 100644 index 000000000..4d2a7f0b5 --- /dev/null +++ b/content/llcache.c @@ -0,0 +1,1815 @@ +/* + * Copyright 2009 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 + * Low-level resource cache (implementation) + */ + +#define _GNU_SOURCE /* For strndup. Ugh. */ +#include <stdlib.h> +#include <string.h> +#include <time.h> + +#include <curl/curl.h> + +#include "content/fetch.h" +#include "content/llcache.h" +#include "utils/messages.h" +#include "utils/url.h" +#include "utils/utils.h" + +/** State of a low-level cache object fetch */ +typedef enum { + LLCACHE_FETCH_INIT, /**< Initial state, before fetch */ + LLCACHE_FETCH_HEADERS, /**< Fetching headers */ + LLCACHE_FETCH_DATA, /**< Fetching object data */ + LLCACHE_FETCH_COMPLETE /**< Fetch completed */ +} llcache_fetch_state; + +/** Type of low-level cache object */ +typedef struct llcache_object llcache_object; + +/** Handle to low-level cache object */ +struct llcache_handle { + llcache_object *object; /**< Pointer to associated object */ + + llcache_handle_callback cb; /**< Client callback */ + void *pw; /**< Client data */ + + llcache_fetch_state state; /**< Last known state of object fetch */ + size_t bytes; /**< Last reported byte count */ +}; + +/** Low-level cache object user record */ +typedef struct llcache_object_user { + /* Must be first in struct */ + llcache_handle handle; /**< Handle data for client */ + + bool iterator_target; /**< This is the an iterator target */ + bool queued_for_delete; /**< This user is queued for deletion */ + + struct llcache_object_user *prev; /**< Previous in list */ + struct llcache_object_user *next; /**< Next in list */ +} llcache_object_user; + +/** Low-level cache object fetch context */ +typedef struct { + uint32_t flags; /**< Fetch flags */ + char *referer; /**< Referring URL, or NULL if none */ + llcache_post_data *post; /**< POST data, or NULL for GET */ + + struct fetch *fetch; /**< Fetch handle for this object */ + + llcache_fetch_state state; /**< Current state of object fetch */ +} llcache_fetch_ctx; + +/** Cache control data */ +typedef struct { + time_t req_time; /**< Time of request */ + time_t res_time; /**< Time of response */ + time_t date; /**< Date: response header */ + time_t expires; /**< Expires: response header */ +#define INVALID_AGE -1 + int age; /**< Age: response header */ + int max_age; /**< Max-Age Cache-control parameter */ + bool no_cache; /**< No-Cache Cache-control parameter */ + char *etag; /**< Etag: response header */ + time_t last_modified; /**< Last-Modified: response header */ +} llcache_cache_control; + +/** Representation of a fetch header */ +typedef struct { + char *name; /**< Header name */ + char *value; /**< Header value */ +} llcache_header; + +/** Low-level cache object */ +/** \todo Consider whether a list is a sane container */ +struct llcache_object { + llcache_object *prev; /**< Previous in list */ + llcache_object *next; /**< Next in list */ + + char *url; /**< Post-redirect URL for object */ + + /** \todo We need a generic dynamic buffer object */ + uint8_t *source_data; /**< Source data for object */ + size_t source_len; /**< Byte length of source data */ + size_t source_alloc; /**< Allocated size of source buffer */ + + llcache_object_user *users; /**< List of users */ + + llcache_fetch_ctx fetch; /**< Fetch context for object */ + + llcache_cache_control cache; /**< Cache control data for object */ + llcache_object *candidate; /**< Object to use, if fetch determines + * that it is still fresh */ + uint32_t candidate_count; /**< Count of objects this is a + * candidate for */ + + llcache_header *headers; /**< Fetch headers */ + size_t num_headers; /**< Number of fetch headers */ +}; + +/** Handler for fetch-related queries */ +static llcache_query_callback query_cb; +/** Data for fetch-related query handler */ +static void *query_cb_pw; + +/** Head of the low-level cached object list */ +static llcache_object *llcache_cached_objects; +/** Head of the low-level uncached object list */ +static llcache_object *llcache_uncached_objects; + +static nserror llcache_object_user_new(llcache_handle_callback cb, void *pw, + llcache_object_user **user); +static nserror llcache_object_user_destroy(llcache_object_user *user); + +static nserror llcache_object_retrieve(const char *url, uint32_t flags, + const char *referer, const llcache_post_data *post, + llcache_object **result); +static nserror llcache_object_retrieve_from_cache(const char *url, + uint32_t flags, const char *referer, + const llcache_post_data *post, llcache_object **result); +static bool llcache_object_is_fresh(const llcache_object *object); +static nserror llcache_object_cache_update(llcache_object *object); +static nserror llcache_object_clone_cache_data(const llcache_object *source, + llcache_object *destination, bool deep); +static nserror llcache_object_fetch(llcache_object *object, uint32_t flags, + const char *referer, const llcache_post_data *post); +static nserror llcache_object_refetch(llcache_object *object); + +static nserror llcache_object_new(const char *url, llcache_object **result); +static nserror llcache_object_destroy(llcache_object *object); +static nserror llcache_object_add_user(llcache_object *object, + llcache_object_user *user); +static nserror llcache_object_remove_user(llcache_object *object, + llcache_object_user *user); + +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 nserror llcache_object_notify_users(llcache_object *object); + +static nserror llcache_clean(void); + +static nserror llcache_post_data_clone(const llcache_post_data *orig, + llcache_post_data **clone); + +static nserror llcache_query_handle_response(bool proceed, void *cbpw); + +static void llcache_fetch_callback(fetch_msg msg, void *p, const void *data, + unsigned long size, fetch_error_code errorcode); +static nserror llcache_fetch_redirect(llcache_object *object, + const char *target, llcache_object **replacement); +static nserror llcache_fetch_notmodified(llcache_object *object, + llcache_object **replacement); +static nserror llcache_fetch_split_header(const char *data, size_t len, + char **name, char **value); +static nserror llcache_fetch_parse_header(llcache_object *object, + const char *data, size_t len, char **name, char **value); +static nserror llcache_fetch_process_header(llcache_object *object, + const char *data, size_t len); +static nserror llcache_fetch_process_data(llcache_object *object, + const uint8_t *data, size_t len); +static nserror llcache_fetch_auth(llcache_object *object, + const char *realm); +static nserror llcache_fetch_cert_error(llcache_object *object, + const struct ssl_cert_info *certs, size_t num); + + +/****************************************************************************** + * Public API * + ******************************************************************************/ + +/** + * Initialise the low-level cache + * + * \param cb Query handler + * \param pw Pointer to query handler data + * \return NSERROR_OK on success, appropriate error otherwise. + */ +nserror llcache_initialise(llcache_query_callback cb, void *pw) +{ + query_cb = cb; + query_cb_pw = pw; + + return NSERROR_OK; +} + +/** + * Poll the low-level cache + * + * \return NSERROR_OK on success, appropriate error otherwise. + */ +nserror llcache_poll(void) +{ + llcache_object *object; + + /* Catch new users up with state of objects */ + for (object = llcache_cached_objects; object != NULL; + object = object->next) { + llcache_object_notify_users(object); + } + + for (object = llcache_uncached_objects; object != NULL; + object = object->next) { + llcache_object_notify_users(object); + } + + /* Attempt to clean the cache */ + llcache_clean(); + + return NSERROR_OK; +} + +/** + * Retrieve a handle for a low-level cache object + * + * \param url URL of the object to fetch + * \param flags Object retrieval flags + * \param referer Referring URL, or NULL if none + * \param post POST data, or NULL for a GET request + * \param cb Client callback for events + * \param pw Pointer to client-specific data + * \param result Pointer to location to recieve cache handle + * \return NSERROR_OK on success, appropriate error otherwise + */ +nserror llcache_handle_retrieve(const char *url, uint32_t flags, + const char *referer, const llcache_post_data *post, + llcache_handle_callback cb, void *pw, + llcache_handle **result) +{ + nserror error; + llcache_object_user *user; + llcache_object *object; + + /* Can we fetch this URL at all? */ + if (fetch_can_fetch(url) == false) + return NSERROR_NO_FETCH_HANDLER; + + /* Create a new object user */ + error = llcache_object_user_new(cb, pw, &user); + if (error != NSERROR_OK) + return error; + + /* Retrieve a suitable object from the cache, + * creating a new one if needed. */ + error = llcache_object_retrieve(url, flags, referer, post, &object); + if (error != NSERROR_OK) { + llcache_object_user_destroy(user); + return error; + } + + /* Add user to object */ + llcache_object_add_user(object, user); + + *result = &user->handle; + + return NSERROR_OK; +} + +/** + * Change the callback associated with a low-level cache handle + * + * \param handle Handle to change callback of + * \param cb New callback + * \param pw Client data for new callback + * \return NSERROR_OK on success, appropriate error otherwise + */ +nserror llcache_handle_change_callback(llcache_handle *handle, + llcache_handle_callback cb, void *pw) +{ + handle->cb = cb; + handle->pw = pw; + + return NSERROR_OK; +} + +/** + * Release a low-level cache handle + * + * \param handle Handle to release + * \return NSERROR_OK on success, appropriate error otherwise + */ +nserror llcache_handle_release(llcache_handle *handle) +{ + nserror error = NSERROR_OK; + llcache_object *object = handle->object; + llcache_object_user *user = (llcache_object_user *) handle; + + /* Remove the user from the object and destroy it */ + error = llcache_object_remove_user(object, user); + if (error == NSERROR_OK) { + /* Can't delete user object if it's the target of an iterator */ + if (user->iterator_target) + user->queued_for_delete = true; + else + error = llcache_object_user_destroy(user); + } + + return error; +} + +/** + * Retrieve the post-redirect URL of a low-level cache object + * + * \param handle Handle to retrieve URL from + * \return Post-redirect URL of cache object + */ +const char *llcache_handle_get_url(const llcache_handle *handle) +{ + return handle->object != NULL ? handle->object->url : NULL; +} + +/** + * Retrieve source data of a low-level cache object + * + * \param handle Handle to retrieve source data from + * \param size Pointer to location to receive byte length of data + * \return Pointer to source data + */ +const uint8_t *llcache_handle_get_source_data(const llcache_handle *handle, + size_t *size) +{ + *size = handle->object != NULL ? handle->object->source_len : 0; + + return handle->object != NULL ? handle->object->source_data : NULL; +} + +/** + * Retrieve a header value associated with a low-level cache object + * + * \param handle Handle to retrieve header from + * \param key Header name + * \return Header value, or NULL if header does not exist + * + * \todo Make the key an enumeration, to avoid needless string comparisons + * \todo Forcing the client to parse the header value seems wrong. + * Better would be to return the actual value part and an array of + * key-value pairs for any additional parameters. + */ +const char *llcache_handle_get_header(const llcache_handle *handle, + const char *key) +{ + const llcache_object *object = handle->object; + size_t i; + + if (object == NULL) + return NULL; + + /* About as trivial as possible */ + for (i = 0; i < object->num_headers; i++) { + if (strcasecmp(key, object->headers[i].name) == 0) + return object->headers[i].value; + } + + return NULL; +} + +/** + * Determine if the same underlying object is referenced by the given handles + * + * \param a First handle + * \param b Second handle + * \return True if handles reference the same object, false otherwise + */ +bool llcache_handle_references_same_object(const llcache_handle *a, + const llcache_handle *b) +{ + return a->object == b->object; +} + +/****************************************************************************** + * Low-level cache internals * + ******************************************************************************/ + +/** + * Create a new object user + * + * \param cb Callback routine + * \param pw Private data for callback + * \param user Pointer to location to receive result + * \return NSERROR_OK on success, appropriate error otherwise + */ +nserror llcache_object_user_new(llcache_handle_callback cb, void *pw, + llcache_object_user **user) +{ + llcache_object_user *u = calloc(1, sizeof(llcache_object_user)); + if (u == NULL) + return NSERROR_NOMEM; + + u->handle.cb = cb; + u->handle.pw = pw; + + *user = u; + + return NSERROR_OK; +} + +/** + * Destroy an object user + * + * \param user User to destroy + * \return NSERROR_OK on success, appropriate error otherwise + * + * \pre User is not attached to an object + */ +nserror llcache_object_user_destroy(llcache_object_user *user) +{ + free(user); + + return NSERROR_OK; +} + +/** + * Retrieve an object from the cache, fetching it if necessary. + * + * \param url URL of object to retrieve + * \param flags Fetch flags + * \param referer Referring URL, or NULL if none + * \param post POST data, or NULL for a GET request + * \param result Pointer to location to recieve retrieved object + * \return NSERROR_OK on success, appropriate error otherwise + */ +nserror llcache_object_retrieve(const char *url, uint32_t flags, + const char *referer, const llcache_post_data *post, + llcache_object **result) +{ + nserror error; + llcache_object *obj; + bool has_query; + url_func_result res; + struct url_components components; + + /** + * Caching Rules: + * + * 1) Forced fetches are never cached + * 2) GET requests with query segments are never cached + * 3) POST requests are never cached + * + * \todo Find out if restriction (2) can be removed + */ + + /* Look for a query segment */ + res = url_get_components(url, &components); + if (res == URL_FUNC_NOMEM) + return NSERROR_NOMEM; + + has_query = (components.query != NULL); + + url_destroy_components(&components); + + if (flags & LLCACHE_RETRIEVE_FORCE_FETCH || has_query || post != NULL) { + /* Create new object */ + error = llcache_object_new(url, &obj); + if (error != NSERROR_OK) + return error; + + /* Attempt to kick-off fetch */ + error = llcache_object_fetch(obj, flags, referer, post); + if (error != NSERROR_OK) { + llcache_object_destroy(obj); + return error; + } + + /* Add new object to uncached list */ + llcache_object_add_to_list(obj, &llcache_uncached_objects); + } else { + error = llcache_object_retrieve_from_cache(url, flags, referer, + post, &obj); + if (error != NSERROR_OK) + return error; + + /* Returned object is already in the cached list */ + } + + *result = obj; + + return NSERROR_OK; +} + +/** + * Retrieve a potentially cached object + * + * \param url URL of object to retrieve + * \param flags Fetch flags + * \param referer Referring URL, or NULL if none + * \param post POST data, or NULL for a GET request + * \param result Pointer to location to recieve retrieved object + * \return NSERROR_OK on success, appropriate error otherwise + */ +nserror llcache_object_retrieve_from_cache(const char *url, uint32_t flags, + const char *referer, const llcache_post_data *post, + llcache_object **result) +{ + nserror error; + llcache_object *obj, *newest = NULL; + + /* Search for the most recently fetched matching object */ + for (obj = llcache_cached_objects; obj != NULL; obj = obj->next) { + if (strcasecmp(obj->url, url) == 0 && (newest == NULL || + obj->cache.req_time > newest->cache.req_time)) + newest = obj; + } + + if (newest != NULL && llcache_object_is_fresh(newest)) { + /* Found a suitable object, and it's still fresh, so use it */ + obj = newest; + + /* The client needs to catch up with the object's state. + * This will occur the next time that llcache_poll is called. + */ + } else if (newest != NULL) { + /* Found a candidate object but it needs freshness validation */ + /* Create a new object */ + error = llcache_object_new(url, &obj); + if (error != NSERROR_OK) + return error; + + /* Clone candidate's cache data */ + error = llcache_object_clone_cache_data(newest, obj, true); + if (error != NSERROR_OK) { + llcache_object_destroy(obj); + return error; + } + + /* Record candidate, so we can fall back if it is still fresh */ + newest->candidate_count++; + obj->candidate = newest; + + /* Attempt to kick-off fetch */ + error = llcache_object_fetch(obj, flags, referer, post); + if (error != NSERROR_OK) { + newest->candidate_count--; + llcache_object_destroy(obj); + return error; + } + + /* Add new object to cache */ + llcache_object_add_to_list(obj, &llcache_cached_objects); + } else { + /* No object found; create a new one */ + /* Create new object */ + error = llcache_object_new(url, &obj); + if (error != NSERROR_OK) + return error; + + /* Attempt to kick-off fetch */ + error = llcache_object_fetch(obj, flags, referer, post); + if (error != NSERROR_OK) { + llcache_object_destroy(obj); + return error; + } + + /* Add new object to cache */ + llcache_object_add_to_list(obj, &llcache_cached_objects); + } + + *result = obj; + + return NSERROR_OK; +} + +/** + * Determine if an object is still fresh + * + * \param object Object to consider + * \return True if object is still fresh, false otherwise + */ +bool llcache_object_is_fresh(const llcache_object *object) +{ + const llcache_cache_control *cd = &object->cache; + int current_age, freshness_lifetime; + time_t now = time(NULL); + + /* Calculate staleness of cached object as per RFC 2616 13.2.3/13.2.4 */ + current_age = max(0, (cd->res_time - cd->date)); + current_age = max(current_age, (cd->age == INVALID_AGE) ? 0 : cd->age); + current_age += cd->res_time - cd->req_time + now - cd->res_time; + + /* Determine freshness lifetime of this object */ + if (cd->max_age != INVALID_AGE) + freshness_lifetime = cd->max_age; + else if (cd->expires != 0) + freshness_lifetime = cd->expires - cd->date; + else if (cd->last_modified != 0) + freshness_lifetime = (now - cd->last_modified) / 10; + else + freshness_lifetime = 0; + + /* The object is fresh if its current age is within the freshness + * lifetime or if we're still fetching the object */ + return (freshness_lifetime > current_age || + object->fetch.state != LLCACHE_FETCH_COMPLETE); +} + +/** + * Update an object's cache state + * + * \param object Object to update cache for + * \return NSERROR_OK. + */ +nserror llcache_object_cache_update(llcache_object *object) +{ + if (object->cache.date == 0) + object->cache.date = time(NULL); + + /** \todo Any magic we need to do for no_cache? */ + + return NSERROR_OK; +} + +/** + * Clone an object's cache data + * + * \param source Source object containing cache data to clone + * \param destination Destination object to clone cache data into + * \param deep Whether to deep-copy the data or not + * \return NSERROR_OK on success, appropriate error otherwise + */ +nserror llcache_object_clone_cache_data(const llcache_object *source, + llcache_object *destination, bool deep) +{ + /* ETag must be first, as it can fail when deep cloning */ + if (source->cache.etag != NULL) { + char *etag = source->cache.etag; + + if (deep) { + /* Copy the etag */ + etag = strdup(source->cache.etag); + if (etag == NULL) + return NSERROR_NOMEM; + } + + if (destination->cache.etag != NULL) + free(destination->cache.etag); + + destination->cache.etag = etag; + } + + destination->cache.req_time = source->cache.req_time; + destination->cache.res_time = source->cache.res_time; + + if (source->cache.date != 0) + destination->cache.date = source->cache.date; + + if (source->cache.expires != 0) + destination->cache.expires = source->cache.expires; + + if (source->cache.age != INVALID_AGE) + destination->cache.age = source->cache.age; + + if (source->cache.max_age != INVALID_AGE) + destination->cache.max_age = source->cache.max_age; + + if (source->cache.no_cache) + destination->cache.no_cache = source->cache.no_cache; + + if (source->cache.last_modified != 0) + destination->cache.last_modified = source->cache.last_modified; + + return NSERROR_OK; +} + +/** + * Kick-off a fetch for an object + * + * \param object Object to fetch + * \param flags Fetch flags + * \param referer Referring URL, or NULL for none + * \param post POST data, or NULL for GET + * \return NSERROR_OK on success, appropriate error otherwise + * + * \pre object::url must contain the URL to fetch + * \pre If there is a freshness validation candidate, + * object::candidate and object::cache must be filled in + * \pre There must not be a fetch in progress for \a object + */ +nserror llcache_object_fetch(llcache_object *object, uint32_t flags, + const char *referer, const llcache_post_data *post) +{ + nserror error; + char *referer_clone = NULL; + llcache_post_data *post_clone = NULL; + + if (referer != NULL) { + referer_clone = strdup(referer); + if (referer_clone == NULL) + return NSERROR_NOMEM; + } + + if (post != NULL) { + error = llcache_post_data_clone(post, &post_clone); + if (error != NSERROR_OK) { + free(referer_clone); + return error; + } + } + + object->fetch.flags = flags; + object->fetch.referer = referer_clone; + object->fetch.post = post_clone; + + return llcache_object_refetch(object); +} + +/** + * (Re)fetch an object + * + * \param object Object to refetch + * \return NSERROR_OK on success, appropriate error otherwise + * + * \pre The fetch parameters in object->fetch must be populated + */ +nserror llcache_object_refetch(llcache_object *object) +{ + const char *urlenc = NULL; + /** \todo Why is fetch_start's post_multipart parameter not const? */ + struct fetch_multipart_data *multipart = NULL; + /** \todo Why is the headers parameter of fetch_start not const? */ + char **headers = NULL; + int header_idx = 0; + + if (object->fetch.post != NULL) { + if (object->fetch.post->type == LLCACHE_POST_URL_ENCODED) + urlenc = object->fetch.post->data.urlenc; + else + multipart = object->fetch.post->data.multipart; + } + + /* Generate cache-control headers */ + headers = malloc(3 * sizeof(char *)); + if (headers == NULL) + return NSERROR_NOMEM; + + if (object->cache.etag != NULL) { + const size_t len = SLEN("If-None-Match: ") + + strlen(object->cache.etag) + 1; + + headers[header_idx] = malloc(len); + if (headers[header_idx] == NULL) { + free(headers); + return NSERROR_NOMEM; + } + + snprintf(headers[header_idx], len, "If-None-Match: %s", + object->cache.etag); + + header_idx++; + } + if (object->cache.date != 0) { + /* Maximum length of an RFC 1123 date is 29 bytes */ + const size_t len = SLEN("If-Modified-Since: ") + 29 + 1; + + headers[header_idx] = malloc(len); + if (headers[header_idx] == NULL) { + while (--header_idx >= 0) + free(headers[header_idx]); + free(headers); + return NSERROR_NOMEM; + } + + snprintf(headers[header_idx], len, "If-Modified-Since: %s", + rfc1123_date(object->cache.date)); + + header_idx++; + } + headers[header_idx] = NULL; + + /* Reset cache control data */ + object->cache.req_time = time(NULL); + object->cache.res_time = 0; + object->cache.date = 0; + object->cache.expires = 0; + object->cache.age = INVALID_AGE; + object->cache.max_age = INVALID_AGE; + object->cache.no_cache = false; + free(object->cache.etag); + object->cache.etag = NULL; + object->cache.last_modified = 0; + + /* Kick off fetch */ + object->fetch.fetch = fetch_start(object->url, object->fetch.referer, + llcache_fetch_callback, object, + object->fetch.flags & LLCACHE_RETRIEVE_NO_ERROR_PAGES, + urlenc, multipart, + object->fetch.flags & LLCACHE_RETRIEVE_VERIFIABLE, + NULL, /** \todo Remove parent from this API */ + headers); + + /* Clean up cache-control headers */ + while (--header_idx >= 0) + free(headers[header_idx]); + free(headers); + + /* Did we succeed in creating a fetch? */ + if (object->fetch.fetch == NULL) + return NSERROR_NOMEM; + + return NSERROR_OK; +} + +/** + * Create a new low-level cache object + * + * \param url URL of object to create + * \param result Pointer to location to receive result + * \return NSERROR_OK on success, appropriate error otherwise + */ +nserror llcache_object_new(const char *url, llcache_object **result) +{ + llcache_object *obj = calloc(1, sizeof(llcache_object)); + if (obj == NULL) + return NSERROR_NOMEM; + + obj->url = strdup(url); + if (obj->url == NULL) { + free(obj); + return NSERROR_NOMEM; + } + + *result = obj; + + return NSERROR_OK; +} + +/** + * Destroy a low-level cache object + * + * \param object Object to destroy + * \return NSERROR_OK on success, appropriate error otherwise + * + * \pre Object is detached from cache list + * \pre Object has no users + * \pre Object is not a candidate (i.e. object::candidate_count == 0) + */ +nserror llcache_object_destroy(llcache_object *object) +{ + size_t i; + + free(object->url); + free(object->source_data); + + if (object->fetch.fetch != NULL) { + fetch_abort(object->fetch.fetch); + object->fetch.fetch = NULL; + } + + free(object->fetch.referer); + + if (object->fetch.post != NULL) { + if (object->fetch.post->type == LLCACHE_POST_URL_ENCODED) { + free(object->fetch.post->data.urlenc); + } else { + fetch_multipart_data_destroy( + object->fetch.post->data.multipart); + } + + free(object->fetch.post); + } + + free(object->cache.etag); + + for (i = 0; i < object->num_headers; i++) { + free(object->headers[i].name); + free(object->headers[i].value); + } + free(object->headers); + + free(object); + + return NSERROR_OK; +} + +/** + * Add a user to a low-level cache object + * + * \param object Object to add user to + * \param user User to add + * \return NSERROR_OK. + */ +nserror llcache_object_add_user(llcache_object *object, + llcache_object_user *user) +{ + user->handle.object = object; + + user->prev = NULL; + user->next = object->users; + + if (object->users != NULL) + object->users->prev = user; + object->users = user; + + return NSERROR_OK; +} + +/** + * Remove a user from a low-level cache object + * + * \param object Object to remove user from + * \param user User to remove + * \return NSERROR_OK. + */ +nserror llcache_object_remove_user(llcache_object *object, + llcache_object_user *user) +{ + if (user == object->users) + object->users = user->next; + else + user->prev->next = user->next; + + if (user->next != NULL) + user->next->prev = user->prev; + + return NSERROR_OK; +} + +/** + * Add a low-level cache object to a cache list + * + * \param object Object to add + * \param list List to add to + * \return NSERROR_OK + */ +nserror llcache_object_add_to_list(llcache_object *object, + llcache_object **list) +{ + object->prev = NULL; + object->next = *list; + + if (*list != NULL) + (*list)->prev = object; + *list = object; + + return NSERROR_OK; +} + +/** + * Remove a low-level cache object from a cache list + * + * \param object Object to remove + * \param list List to remove from + * \return NSERROR_OK + */ +nserror llcache_object_remove_from_list(llcache_object *object, + llcache_object **list) +{ + if (object == *list) + *list = object->next; + else + object->prev->next = object->next; + + if (object->next != NULL) + object->next->prev = object->next; + + return NSERROR_OK; +} + +/** + * Notify users of an object's current state + * + * \param object Object to notify users about + * \return NSERROR_OK on success, appropriate error otherwise + */ +nserror llcache_object_notify_users(llcache_object *object) +{ + nserror error; + llcache_object_user *user, *next_user; + llcache_event event; + + /** + * State transitions and event emission for users. + * Rows: user state. Cols: object state. + * + * User\Obj INIT HEADERS DATA COMPLETE + * INIT - T T* T* + * HEADERS - - T T* + * DATA - - M T + * COMPLETE - - - - + * + * T => transition user to object state + * M => no transition required, but may need to emit event + * + * The transitions marked with an asterisk can be removed by moving + * the user context into the subsequent state and then reevaluating. + * + * Events are issued as follows: + * + * HAD_HEADERS: on transition from HEADERS -> DATA state + * HAD_DATA : in DATA state, whenever there's new source data + * DONE : on transition from DATA -> COMPLETE state + */ + + for (user = object->users; user != NULL; user = next_user) { + /* Emit necessary events to bring the user up-to-date */ + llcache_handle *handle = &user->handle; + llcache_fetch_state hstate = handle->state; + llcache_fetch_state objstate = object->fetch.state; + + /* Save identity of next user in case client destroys + * the user underneath us */ + user->iterator_target = true; + next_user = user->next; + + /* User: INIT, Obj: HEADERS, DATA, COMPLETE => User->HEADERS */ + if (hstate == LLCACHE_FETCH_INIT && + objstate > LLCACHE_FETCH_INIT) { + hstate = LLCACHE_FETCH_HEADERS; + } + + /* User: HEADERS, Obj: DATA, COMPLETE => User->DATA */ + if (hstate == LLCACHE_FETCH_HEADERS && + objstate > LLCACHE_FETCH_HEADERS) { + /* Emit HAD_HEADERS event */ + event.type = LLCACHE_EVENT_HAD_HEADERS; + + error = handle->cb(handle, &event, handle->pw); + if (error != NSERROR_OK) { + user->iterator_target = false; + return error; + } + + if (user->queued_for_delete) { + llcache_object_user_destroy(user); + continue; + } + + hstate = LLCACHE_FETCH_DATA; + } + + /* User: DATA, Obj: DATA, COMPLETE, more source available */ + if (hstate == LLCACHE_FETCH_DATA && + objstate >= LLCACHE_FETCH_DATA && + object->source_len > handle->bytes) { + /* Emit 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; + + error = handle->cb(handle, &event, handle->pw); + if (error != NSERROR_OK) { + user->iterator_target = false; + return error; + } + + if (user->queued_for_delete) { + llcache_object_user_destroy(user); + continue; + } + + /* Update record of last byte emitted */ + handle->bytes = object->source_len; + } + + /* User: DATA, Obj: COMPLETE => User->COMPLETE */ + if (hstate == LLCACHE_FETCH_DATA && + objstate > LLCACHE_FETCH_DATA) { + /* Emit DONE event */ + event.type = LLCACHE_EVENT_DONE; + + error = handle->cb(handle, &event, handle->pw); + if (error != NSERROR_OK) { + user->iterator_target = false; + return error; + } + + if (user->queued_for_delete) { + llcache_object_user_destroy(user); + continue; + } + + hstate = LLCACHE_FETCH_COMPLETE; + } + + /* No longer the target of an iterator */ + user->iterator_target = false; + + /* Sync handle's state with reality */ + handle->state = hstate; + } + + return NSERROR_OK; +} + +/** + * Attempt to clean the cache + * + * \return NSERROR_OK. + */ +nserror llcache_clean(void) +{ + llcache_object *object, *next; + + /* Candidates for cleaning are (in order of priority): + * + * 1) Uncacheable objects with no users + * 2) Stale cacheable objects with no users or pending fetches + * 3) Fresh cacheable objects with no users or pending fetches + */ + + /* 1) Uncacheable objects with no users */ + for (object = llcache_uncached_objects; object != NULL; object = next) { + next = object->next; + + /* The candidate count of uncacheable objects is always 0 */ + if (object->users == NULL && object->candidate_count == 0) { + llcache_object_remove_from_list(object, + &llcache_uncached_objects); + llcache_object_destroy(object); + } + } + + /* 2) Stale cacheable objects with no users or pending fetches */ + for (object = llcache_cached_objects; object != NULL; object = next) { + next = object->next; + + if (object->users == NULL && object->candidate_count == 0 && + llcache_object_is_fresh(object) == false) { + llcache_object_remove_from_list(object, + &llcache_cached_objects); + llcache_object_destroy(object); + } + } + + /* 3) Fresh cacheable objects with no users or pending fetches */ + /** \todo This one only happens if the cache is too large */ + + return NSERROR_OK; +} + +/** + * Clone a POST data object + * + * \param orig Object to clone + * \param clone Pointer to location to receive clone + * \return NSERROR_OK on success, appropriate error otherwise + */ +nserror llcache_post_data_clone(const llcache_post_data *orig, + llcache_post_data **clone) +{ + llcache_post_data *post_clone; + + post_clone = calloc(1, sizeof(llcache_post_data)); + if (post_clone == NULL) + return NSERROR_NOMEM; + + post_clone->type = orig->type; + + /* Deep-copy the type-specific data */ + if (orig->type == LLCACHE_POST_URL_ENCODED) { + post_clone->data.urlenc = strdup(orig->data.urlenc); + if (post_clone->data.urlenc == NULL) { + free(post_clone); + + return NSERROR_NOMEM; + } + } else { + post_clone->data.multipart = fetch_multipart_data_clone( + orig->data.multipart); + if (post_clone->data.multipart == NULL) { + free(post_clone); + + return NSERROR_NOMEM; + } + } + + *clone = post_clone; + + return NSERROR_OK; +} + +/** + * Handle a query response + * + * \param proceed Whether to proceed with fetch + * \param cbpw Our context for query + * \return NSERROR_OK on success, appropriate error otherwise + */ +nserror llcache_query_handle_response(bool proceed, void *cbpw) +{ + nserror error; + llcache_event event; + llcache_object_user *user; + llcache_object *object = cbpw; + + /* Refetch, using existing fetch parameters, if client allows us to */ + if (proceed) + return llcache_object_refetch(object); + + /* Inform client(s) that object fetch failed */ + event.type = LLCACHE_EVENT_ERROR; + /** \todo More appropriate error message */ + event.data.msg = messages_get("FetchFailed"); + + for (user = object->users; user != NULL; user = user->next) { + error = user->handle.cb(&user->handle, &event, user->handle.pw); + if (error != NSERROR_OK) + return error; + } + + return NSERROR_OK; +} + +/** + * Handler for fetch events + * + * \param msg Type of fetch event + * \param p Our private data + * \param data Event data + * \param size Length of data in bytes + * \param errorcode Reason for fetch error + */ +void llcache_fetch_callback(fetch_msg msg, void *p, const void *data, + unsigned long size, fetch_error_code errorcode) +{ + nserror error = NSERROR_OK; + llcache_object *object = p; + llcache_object_user *user; + llcache_event event; + + switch (msg) { + /* 3xx responses */ + case FETCH_REDIRECT: + /* Request resulted in a redirect */ + error = llcache_fetch_redirect(object, data, &object); + break; + case FETCH_NOTMODIFIED: + /* Conditional request determined that cached object is fresh */ + error = llcache_fetch_notmodified(object, &object); + break; + + /* Normal 2xx state machine */ + case FETCH_HEADER: + /* Received a fetch header */ + object->fetch.state = LLCACHE_FETCH_HEADERS; + + error = llcache_fetch_process_header(object, data, size); + case FETCH_TYPE: + /** \todo Purge FETCH_TYPE completely */ + break; + case FETCH_DATA: + /* Received some data */ + object->fetch.state = LLCACHE_FETCH_DATA; + + error = llcache_fetch_process_data(object, data, size); + break; + case FETCH_FINISHED: + /* Finished fetching */ + object->fetch.state = LLCACHE_FETCH_COMPLETE; + object->fetch.fetch = NULL; + + llcache_object_cache_update(object); + break; + + /* Out-of-band information */ + case FETCH_ERROR: + /* An error occurred while fetching */ + fetch_abort(object->fetch.fetch); + object->fetch.fetch = NULL; + /** \todo Ensure this object becomes stale */ + + /** \todo Consider using errorcode for something */ + + event.type = LLCACHE_EVENT_ERROR; + event.data.msg = data; + + for (user = object->users; user != NULL; user = user->next) { + error = user->handle.cb(&user->handle, &event, + user->handle.pw); + if (error != NSERROR_OK) + break; + } + break; + case FETCH_PROGRESS: + /* Progress update */ + event.type = LLCACHE_EVENT_PROGRESS; + event.data.msg = data; + + for (user = object->users; user != NULL; user = user->next) { + error = user->handle.cb(&user->handle, &event, + user->handle.pw); + if (error != NSERROR_OK) + break; + } + break; + + /* Events requiring action */ + case FETCH_AUTH: + /* Need Authentication */ + error = llcache_fetch_auth(object, data); + break; + case FETCH_CERT_ERR: + /* Something went wrong when validating TLS certificates */ + error = llcache_fetch_cert_error(object, data, size); + break; + } + + /* Deal with any errors reported by event handlers */ + if (error != NSERROR_OK) { + /** \todo Error handling */ + if (object->fetch.fetch != NULL) { + fetch_abort(object->fetch.fetch); + object->fetch.fetch = NULL; + } + return; + } + + /* Keep users in sync with reality */ + error = llcache_object_notify_users(object); + if (error != NSERROR_OK) { + /** \todo Error handling */ + if (object->fetch.fetch != NULL) { + fetch_abort(object->fetch.fetch); + object->fetch.fetch = NULL; + } + } +} + +/** + * Handle FETCH_REDIRECT event + * + * \param object Object being redirected + * \param target Target of redirect (may be relative) + * \param replacement Pointer to location to receive replacement object + * \return NSERROR_OK on success, appropriate error otherwise + */ +nserror llcache_fetch_redirect(llcache_object *object, const char *target, + llcache_object **replacement) +{ + nserror error; + llcache_object *dest; + llcache_object_user *user, *next; + const llcache_post_data *post = object->fetch.post; + char *url, *absurl; + url_func_result result; + /* Extract HTTP response code from the fetch object */ + long http_code = fetch_http_code(object->fetch.fetch); + + /* Abort fetch for this object */ + fetch_abort(object->fetch.fetch); + object->fetch.fetch = NULL; + + /** \todo Limit redirect depth, or detect cycles */ + + /* Make target absolute */ + result = url_join(target, object->url, &absurl); + if (result != URL_FUNC_OK) { + return NSERROR_NOMEM; + } + + /* Ensure target is normalised */ + result = url_normalize(absurl, &url); + + /* No longer require absolute url */ + free(absurl); + + if (result != URL_FUNC_OK) { + return NSERROR_NOMEM; + } + + /** \todo Ensure that redirects to file:/// don't happen? */ + + /** \todo What happens if we've no way of handling this URL? */ + + /** \todo All the magical processing for the various redirect types */ + if (http_code == 301 || http_code == 302 || http_code == 303) { + /* 301, 302, 303 redirects are all unconditional GET requests */ + post = NULL; + } else { + /** \todo 300, 305, 307 */ + free(url); + return NSERROR_OK; + } + + /* Attempt to fetch target URL */ + error = llcache_object_retrieve(url, object->fetch.flags, + object->fetch.referer, object->fetch.post, + &dest); + + /* No longer require url */ + free(url); + + if (error != NSERROR_OK) + return error; + + /* Move user(s) to replacement object */ + for (user = object->users; user != NULL; user = next) { + next = user->next; + + llcache_object_remove_user(object, user); + llcache_object_add_user(dest, user); + } + + /* Dest is now our object */ + *replacement = dest; + + return NSERROR_OK; +} + +/** + * Handle FETCH_NOTMODIFIED event + * + * \param object Object to process + * \param replacement Pointer to location to receive replacement object + * \return NSERROR_OK. + */ +nserror llcache_fetch_notmodified(llcache_object *object, + llcache_object **replacement) +{ + llcache_object_user *user, *next; + + /* Move user(s) to candidate content */ + for (user = object->users; user != NULL; user = next) { + next = user->next; + + llcache_object_remove_user(object, user); + llcache_object_add_user(object->candidate, user); + } + + /* Candidate is no longer a candidate for us */ + object->candidate->candidate_count--; + + /* Clone our cache control data into the candidate */ + llcache_object_clone_cache_data(object, object->candidate, false); + /* Bring candidate's cache data up to date */ + llcache_object_cache_update(object->candidate); + + /* Invalidate our cache-control data */ + memset(&object->cache, 0, sizeof(llcache_cache_control)); + + /* Ensure fetch has stopped */ + /** \todo Are there any other fields that need invalidating? */ + fetch_abort(object->fetch.fetch); + object->fetch.fetch = NULL; + + /* Candidate is now our object */ + *replacement = object->candidate; + + /** \todo Ensure that old object gets flushed from the cache */ + + return NSERROR_OK; +} + +/** + * Split a fetch header into name and value + * + * \param data Header string + * \param len Byte length of header + * \param name Pointer to location to receive header name + * \param value Pointer to location to receive header value + * \return NSERROR_OK on success, appropriate error otherwise + */ +nserror llcache_fetch_split_header(const char *data, size_t len, char **name, + char **value) +{ + char *n, *v; + const char *colon; + + /* Find colon */ + colon = strchr(data, ':'); + if (colon == NULL) { + /* Failed, assume a key with no value */ + n = strdup(data); + if (n == NULL) + return NSERROR_NOMEM; + + v = strdup(""); + if (v == NULL) { + free(n); + return NSERROR_NOMEM; + } + } else { + /* Split header into name & value */ + + /* Strip leading whitespace from name */ + while (data[0] == ' ' || data[0] == '\t' || + data[0] == '\r' || data[0] == '\n') { + data++; + } + + /* Strip trailing whitespace from name */ + while (colon > data && (colon[-1] == ' ' || + colon[-1] == '\t' || colon[-1] == '\r' || + colon[-1] == '\n')) + colon--; + + n = strndup(data, colon - data); + if (n == NULL) + return NSERROR_NOMEM; + + /* Find colon again */ + while (*colon != ':') { + colon++; + } + + /* Skip over colon and any subsequent whitespace */ + do { + colon++; + } while (*colon == ' ' || *colon == '\t' || + *colon == '\r' || *colon == '\n'); + + /* Strip trailing whitespace from value */ + while (len > 0 && (data[len - 1] == ' ' || + data[len - 1] == '\t' || + data[len - 1] == '\r' || + data[len - 1] == '\n')) { + len--; + } + + v = strndup(colon, len - (colon - data)); + if (v == NULL) { + free(n); + return NSERROR_NOMEM; + } + } + + *name = n; + *value = v; + + return NSERROR_OK; +} + +/** + * Parse a fetch header + * + * \param object Object to parse header for + * \param data Header string + * \param len Byte length of header + * \param name Pointer to location to receive header name + * \param value Pointer to location to receive header value + * \return NSERROR_OK on success, appropriate error otherwise + */ +nserror llcache_fetch_parse_header(llcache_object *object, const char *data, + size_t len, char **name, char **value) +{ + nserror error; + + /* Set fetch response time if not already set */ + if (object->cache.res_time == 0) + object->cache.res_time = time(NULL); + + /* Decompose header into name-value pair */ + error = llcache_fetch_split_header(data, len, name, value); + if (error != NSERROR_OK) + return error; + + /* Parse cache headers to populate cache control data */ +#define SKIP_ST(p) while (*p != '\0' && (*p == ' ' || *p == '\t')) p++ + + if (5 < len && strcasecmp(*name, "Date") == 0) { + /* extract Date header */ + object->cache.date = curl_getdate(*value, NULL); + } else if (4 < len && strcasecmp(*name, "Age") == 0) { + /* extract Age header */ + if ('0' <= **value && **value <= '9') + object->cache.age = atoi(*value); + } else if (8 < len && strcasecmp(*name, "Expires") == 0) { + /* extract Expires header */ + object->cache.expires = curl_getdate(*value, NULL); + } else if (14 < len && strcasecmp(*name, "Cache-Control") == 0) { + /* extract and parse Cache-Control header */ + const char *start = *value; + const char *comma = *value; + + while (*comma != '\0') { + while (*comma != '\0' && *comma != ',') + comma++; + + if (8 < comma - start && (strncasecmp(start, + "no-cache", 8) == 0 || + strncasecmp(start, "no-store", 8) == 0)) + /* When we get a disk cache we should + * distinguish between these two */ + object->cache.no_cache = true; + else if (7 < comma - start && + strncasecmp(start, "max-age", 7) == 0) { + /* Find '=' */ + while (start < comma && *start != '=') + start++; + + /* Skip over it */ + start++; + + /* Skip whitespace */ + SKIP_ST(start); + + if (start < comma) + object->cache.max_age = atoi(start); + } + + if (*comma != '\0') { + /* Skip past comma */ + comma++; + /* Skip whitespace */ + SKIP_ST(comma); + } + + /* Set start for next token */ + start = comma; + } + } else if (5 < len && strcasecmp(*name, "ETag") == 0) { + /* extract ETag header */ + free(object->cache.etag); + object->cache.etag = strdup(*value); + if (object->cache.etag == NULL) + return NSERROR_NOMEM; + } else if (14 < len && strcasecmp(*name, "Last-Modified") == 0) { + /* extract Last-Modified header */ + object->cache.last_modified = curl_getdate(*value, NULL); + } + +#undef SKIP_ST + + return NSERROR_OK; +} + +/** + * Process a fetch header + * + * \param object Object being fetched + * \param data Header string + * \param len Byte length of header + * \return NSERROR_OK on success, appropriate error otherwise + */ +nserror llcache_fetch_process_header(llcache_object *object, const char *data, + size_t len) +{ + nserror error; + char *name, *value; + llcache_header *temp; + + error = llcache_fetch_parse_header(object, data, len, &name, &value); + if (error != NSERROR_OK) + return error; + + /* Append header data to the object's headers array */ + temp = realloc(object->headers, (object->num_headers + 1) * + sizeof(llcache_header)); + if (temp == NULL) { + free(name); + free(value); + return NSERROR_NOMEM; + } + + object->headers = temp; + + object->headers[object->num_headers].name = name; + object->headers[object->num_headers].value = value; + + object->num_headers++; + + return NSERROR_OK; +} + +/** + * Process a chunk of fetched data + * + * \param object Object being fetched + * \param data Data to process + * \param len Byte length of data + * \return NSERROR_OK on success, appropriate error otherwise. + */ +nserror llcache_fetch_process_data(llcache_object *object, const uint8_t *data, + size_t len) +{ + /* Resize source buffer if it's too small */ + if (object->source_len + len >= object->source_alloc) { + const size_t new_len = object->source_len + len + 64 * 1024; + uint8_t *temp = realloc(object->source_data, new_len); + if (temp == NULL) + return NSERROR_NOMEM; + + object->source_data = temp; + object->source_alloc = new_len; + } + + /* Append this data chunk to source buffer */ + memcpy(object->source_data + object->source_len, data, len); + object->source_len += len; + + return NSERROR_OK; +} + +/** + * Handle an authentication request + * + * \param object Object being fetched + * \param realm Authentication realm + * \return NSERROR_OK on success, appropriate error otherwise. + */ +nserror llcache_fetch_auth(llcache_object *object, const char *realm) +{ + nserror error = NSERROR_OK; + + /* Abort fetch for this object */ + fetch_abort(object->fetch.fetch); + object->fetch.fetch = NULL; + + if (query_cb != NULL) { + llcache_query query; + + /* Destroy headers */ + while (object->num_headers > 0) { + object->num_headers--; + + free(object->headers[object->num_headers].name); + free(object->headers[object->num_headers].value); + } + free(object->headers); + object->headers = NULL; + + /* Emit query for authentication details */ + query.type = LLCACHE_QUERY_AUTH; + query.url = object->url; + query.data.auth.realm = realm; + + error = query_cb(&query, query_cb_pw, + llcache_query_handle_response, object); + } else { + llcache_object_user *user; + llcache_event event; + + /* Inform client(s) that object fetch failed */ + event.type = LLCACHE_EVENT_ERROR; + /** \todo More appropriate error message */ + event.data.msg = messages_get("FetchFailed"); + + for (user = object->users; user != NULL; user = user->next) { + error = user->handle.cb(&user->handle, &event, + user->handle.pw); + if (error != NSERROR_OK) + break; + } + } + + return error; +} + +/** + * Handle a TLS certificate verification failure + * + * \param object Object being fetched + * \param certs Certificate chain + * \param num Number of certificates in chain + * \return NSERROR_OK on success, appropriate error otherwise + */ +nserror llcache_fetch_cert_error(llcache_object *object, + const struct ssl_cert_info *certs, size_t num) +{ + nserror error = NSERROR_OK; + + /* Abort fetch for this object */ + fetch_abort(object->fetch.fetch); + object->fetch.fetch = NULL; + + if (query_cb != NULL) { + llcache_query query; + + /* Emit query for TLS */ + query.type = LLCACHE_QUERY_SSL; + query.url = object->url; + query.data.ssl.certs = certs; + query.data.ssl.num = num; + + error = query_cb(&query, query_cb_pw, + llcache_query_handle_response, object); + } else { + llcache_object_user *user; + llcache_event event; + + /* Inform client(s) that object fetch failed */ + event.type = LLCACHE_EVENT_ERROR; + /** \todo More appropriate error message */ + event.data.msg = messages_get("FetchFailed"); + + for (user = object->users; user != NULL; user = user->next) { + error = user->handle.cb(&user->handle, &event, + user->handle.pw); + if (error != NSERROR_OK) + break; + } + } + + return error; +} + diff --git a/content/llcache.h b/content/llcache.h new file mode 100644 index 000000000..b2c856f3b --- /dev/null +++ b/content/llcache.h @@ -0,0 +1,238 @@ +/* + * Copyright 2009 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 + * Low-level resource cache (interface) + */ + +#ifndef NETSURF_CONTENT_LLCACHE_H_ +#define NETSURF_CONTENT_LLCACHE_H_ + +#include <stdbool.h> +#include <stddef.h> +#include <stdint.h> + +#include "utils/errors.h" + +struct ssl_cert_info; +struct fetch_multipart_data; + +/** Handle for low-level cache object */ +typedef struct llcache_handle llcache_handle; + +/** POST data object for low-level cache requests */ +typedef struct { + enum { + LLCACHE_POST_URL_ENCODED, + LLCACHE_POST_MULTIPART + } type; /**< Type of POST data */ + union { + char *urlenc; /**< URL encoded data */ + struct fetch_multipart_data *multipart; /**< Multipart data */ + } data; /**< POST data content */ +} llcache_post_data; + +/** Low-level cache event types */ +typedef enum { + LLCACHE_EVENT_HAD_HEADERS, /**< Received all headers */ + LLCACHE_EVENT_HAD_DATA, /**< Received some data */ + LLCACHE_EVENT_DONE, /**< Finished fetching data */ + + LLCACHE_EVENT_ERROR, /**< An error occurred during fetch */ + LLCACHE_EVENT_PROGRESS, /**< Fetch progress update */ +} llcache_event_type; + +/** Low-level cache events */ +typedef struct { + llcache_event_type type; /**< Type of event */ + union { + struct { + const uint8_t *buf; /**< Buffer of data */ + size_t len; /**< Length of buffer, in bytes */ + } data; /**< Received data */ + const char *msg; /**< Error or progress message */ + } data; /**< Event data */ +} llcache_event; + +/** + * Client callback for low-level cache events + * + * \param handle Handle for which event is issued + * \param event Event data + * \param pw Pointer to client-specific data + * \return NSERROR_OK on success, appropriate error otherwise. + */ +typedef nserror (*llcache_handle_callback)(llcache_handle *handle, + const llcache_event *event, void *pw); + +/** Flags for low-level cache object retrieval */ +#define LLCACHE_RETRIEVE_FORCE_FETCH (1 << 0) /* Force a new fetch */ +#define LLCACHE_RETRIEVE_VERIFIABLE (1 << 1) /* Requested URL was verified */ +#define LLCACHE_RETRIEVE_SNIFF_TYPE (1 << 2) /* Permit content-type sniffing */ +#define LLCACHE_RETRIEVE_NO_ERROR_PAGES (1 << 3) /* No error pages */ + +/** Low-level cache query types */ +typedef enum { + LLCACHE_QUERY_AUTH, /**< Need authentication details */ + LLCACHE_QUERY_REDIRECT, /**< Need permission to redirect */ + LLCACHE_QUERY_SSL /**< SSL chain needs inspection */ +} llcache_query_type; + +/** Low-level cache query */ +typedef struct { + llcache_query_type type; /**< Type of query */ + + const char *url; /**< URL being fetched */ + + union { + struct { + const char *realm; /**< Authentication realm */ + } auth; + + struct { + const char *target; /**< Redirect target */ + } redirect; + + struct { + const struct ssl_cert_info *certs; + size_t num; /**< Number of certs in chain */ + } ssl; + } data; +} llcache_query; + +/** + * Response handler for fetch-related queries + * + * \param proceed Whether to proceed with the fetch or not + * \param cbpw Opaque value provided to llcache_query_callback + * \return NSERROR_OK on success, appropriate error otherwise + */ +typedef nserror (*llcache_query_response)(bool proceed, void *cbpw); + +/** + * Callback to handle fetch-related queries + * + * \param query Object containing details of query + * \param pw Pointer to callback-specific data + * \param cb Callback that client should call once query is satisfied + * \param cbpw Opaque value to pass into \a cb + * \return NSERROR_OK on success, appropriate error otherwise + * + * \note This callback should return immediately. Once a suitable answer to + * the query has been obtained, the provided response callback should be + * called. This is intended to be an entirely asynchronous process. + */ +typedef nserror (*llcache_query_callback)(const llcache_query *query, void *pw, + llcache_query_response cb, void *cbpw); + +/** + * Initialise the low-level cache + * + * \param cb Query handler + * \param pw Pointer to query handler data + * \return NSERROR_OK on success, appropriate error otherwise. + */ +nserror llcache_initialise(llcache_query_callback cb, void *pw); + +/** + * Poll the low-level cache + * + * \return NSERROR_OK on success, appropriate error otherwise. + */ +nserror llcache_poll(void); + +/** + * Retrieve a handle for a low-level cache object + * + * \param url URL of the object to fetch + * \param flags Object retrieval flags + * \param referer Referring URL, or NULL if none + * \param post POST data, or NULL for a GET request + * \param cb Client callback for events + * \param pw Pointer to client-specific data + * \param result Pointer to location to recieve cache handle + * \return NSERROR_OK on success, appropriate error otherwise + */ +nserror llcache_handle_retrieve(const char *url, uint32_t flags, + const char *referer, const llcache_post_data *post, + llcache_handle_callback cb, void *pw, + llcache_handle **result); + +/** + * Change the callback associated with a low-level cache handle + * + * \param handle Handle to change callback of + * \param cb New callback + * \param pw Client data for new callback + * \return NSERROR_OK on success, appropriate error otherwise + */ +nserror llcache_handle_change_callback(llcache_handle *handle, + llcache_handle_callback cb, void *pw); + +/** + * Release a low-level cache handle + * + * \param handle Handle to release + * \return NSERROR_OK on success, appropriate error otherwise + */ +nserror llcache_handle_release(llcache_handle *handle); + +/** + * Retrieve the post-redirect URL of a low-level cache object + * + * \param handle Handle to retrieve URL from + * \return Post-redirect URL of cache object + */ +const char *llcache_handle_get_url(const llcache_handle *handle); + +/** + * Retrieve source data of a low-level cache object + * + * \param handle Handle to retrieve source data from + * \param size Pointer to location to receive byte length of data + * \return Pointer to source data + */ +const uint8_t *llcache_handle_get_source_data(const llcache_handle *handle, + size_t *size); + +/** + * Retrieve a header value associated with a low-level cache object + * + * \param handle Handle to retrieve header from + * \param key Header name + * \return Header value, or NULL if header does not exist + * + * \todo Make the key an enumeration, to avoid needless string comparisons + * \todo Forcing the client to parse the header value seems wrong. + * Better would be to return the actual value part and an array of + * key-value pairs for any additional parameters. + */ +const char *llcache_handle_get_header(const llcache_handle *handle, + const char *key); + +/** + * Determine if the same underlying object is referenced by the given handles + * + * \param a First handle + * \param b Second handle + * \return True if handles reference the same object, false otherwise + */ +bool llcache_handle_references_same_object(const llcache_handle *a, + const llcache_handle *b); + +#endif @@ -20,17 +20,18 @@ #include <libwapcaplet/libwapcaplet.h> -#include "content/content.h" +#include "content/content_protected.h" #include "content/fetch.h" -#include "content/fetchcache.h" +#include "content/hlcache.h" #include "css/css.h" #include "css/internal.h" #include "desktop/gui.h" #include "render/html.h" +#include "utils/http.h" #include "utils/messages.h" -static void nscss_import(content_msg msg, struct content *c, - intptr_t p1, intptr_t p2, union content_msg_data data); +static nserror nscss_import(hlcache_handle *handle, + const hlcache_event *event, void *pw); /** * Allocation callback for libcss @@ -49,125 +50,63 @@ static void *myrealloc(void *ptr, size_t size, void *pw) * Initialise a CSS content * * \param c Content to initialise - * \param parent Parent content, or NULL if top-level * \param params Content-Type parameters * \return true on success, false on failure */ -bool nscss_create(struct content *c, struct content *parent, - const char *params[]) +bool nscss_create(struct content *c, const http_parameter *params) { const char *charset = NULL; - css_origin origin = CSS_ORIGIN_AUTHOR; - uint64_t media = CSS_MEDIA_ALL; - lwc_context *dict = NULL; - bool quirks = true; - uint32_t i; union content_msg_data msg_data; - css_error error; + nserror error; /** \todo what happens about the allocator? */ /** \todo proper error reporting */ /* Find charset specified on HTTP layer, if any */ - /** \todo What happens if there isn't one and parent content exists? */ - for (i = 0; params[i] != NULL; i += 2) { - if (strcasecmp(params[i], "charset") == 0) { - charset = params[i + 1]; - break; - } + error = http_parameter_list_find_item(params, "charset", &charset); + if (error != NSERROR_OK) { + /* No charset specified, use fallback, if any */ + /** \todo libcss will take this as gospel, which is wrong */ + charset = c->fallback_charset; } - if (parent != NULL) { - assert(parent->type == CONTENT_HTML || - parent->type == CONTENT_CSS); - - if (parent->type == CONTENT_HTML) { - assert(parent->data.html.dict != NULL); - - if (c == parent->data.html. - stylesheets[STYLESHEET_BASE].c || - c == parent->data.html. - stylesheets[STYLESHEET_QUIRKS].c || - c == parent->data.html. - stylesheets[STYLESHEET_ADBLOCK].c) - origin = CSS_ORIGIN_UA; - - quirks = (parent->data.html.quirks != - BINDING_QUIRKS_MODE_NONE); - - for (i = 0; i < parent->data.html.stylesheet_count; - i++) { - if (parent->data.html.stylesheets[i].c == c) { - media = parent->data.html. - stylesheets[i].media; - break; - } - } - - dict = parent->data.html.dict; - } else { - assert(parent->data.css.sheet != NULL); - assert(parent->data.css.dict != NULL); - - error = css_stylesheet_get_origin( - parent->data.css.sheet, &origin); - if (error != CSS_OK) { - msg_data.error = "?"; - content_broadcast(c, CONTENT_MSG_ERROR, - msg_data); - return false; - } - - error = css_stylesheet_quirks_allowed( - parent->data.css.sheet, &quirks); - if (error != CSS_OK) { - msg_data.error = "?"; - content_broadcast(c, CONTENT_MSG_ERROR, - msg_data); - return false; - } - - for (i = 0; i < parent->data.css.import_count; i++) { - if (parent->data.css.imports[i].c == c) { - media = parent->data.css. - imports[i].media; - break; - } - } - - dict = parent->data.css.dict; - } + if (nscss_create_css_data(&c->data.css, content__get_url(c), + charset, c->quirks) != NSERROR_OK) { + msg_data.error = messages_get("NoMemory"); + content_broadcast(c, CONTENT_MSG_ERROR, msg_data); + return false; } - if (dict == NULL) { - lwc_error lerror = lwc_create_context(myrealloc, NULL, &dict); + return true; +} - if (lerror != lwc_error_ok) { - msg_data.error = messages_get("NoMemory"); - content_broadcast(c, CONTENT_MSG_ERROR, msg_data); - return false; - } - } +/** + * Create a struct content_css_data, creating a stylesheet object + * + * \param c Struct to populate + * \param url URL of stylesheet + * \param charset Stylesheet charset + * \param quirks Stylesheet quirks mode + * \return NSERROR_OK on success, NSERROR_NOMEM on memory exhaustion + */ +nserror nscss_create_css_data(struct content_css_data *c, + const char *url, const char *charset, bool quirks) +{ + css_error error; - c->data.css.dict = lwc_context_ref(dict); - c->data.css.import_count = 0; - c->data.css.imports = NULL; + c->import_count = 0; + c->imports = NULL; error = css_stylesheet_create(CSS_LEVEL_21, charset, - c->url, NULL, origin, media, quirks, false, - c->data.css.dict, + url, NULL, quirks, false, myrealloc, NULL, nscss_resolve_url, NULL, - &c->data.css.sheet); + &c->sheet); if (error != CSS_OK) { - lwc_context_unref(c->data.css.dict); - c->data.css.dict = NULL; - msg_data.error = messages_get("NoMemory"); - content_broadcast(c, CONTENT_MSG_ERROR, msg_data); - return false; + return NSERROR_NOMEM; } - return true; + return NSERROR_OK; } /** @@ -183,9 +122,7 @@ bool nscss_process_data(struct content *c, char *data, unsigned int size) union content_msg_data msg_data; css_error error; - error = css_stylesheet_append_data(c->data.css.sheet, - (const uint8_t *) data, size); - + error = nscss_process_css_data(&c->data.css, data, size); if (error != CSS_OK && error != CSS_NEEDDATA) { msg_data.error = "?"; content_broadcast(c, CONTENT_MSG_ERROR, msg_data); @@ -195,6 +132,21 @@ bool nscss_process_data(struct content *c, char *data, unsigned int size) } /** + * Process CSS data + * + * \param c CSS content object + * \param data Data to process + * \param size Number of bytes to process + * \return CSS_OK on success, appropriate error otherwise + */ +css_error nscss_process_css_data(struct content_css_data *c, char *data, + unsigned int size) +{ + return css_stylesheet_append_data(c->sheet, + (const uint8_t *) data, size); +} + +/** * Convert a CSS content ready for use * * \param c Content to convert @@ -209,22 +161,73 @@ bool nscss_convert(struct content *c, int w, int h) size_t size; css_error error; - error = css_stylesheet_data_done(c->data.css.sheet); + error = nscss_convert_css_data(&c->data.css, w, h); + if (error != CSS_OK) { + msg_data.error = "?"; + content_broadcast(c, CONTENT_MSG_ERROR, msg_data); + c->status = CONTENT_STATUS_ERROR; + return false; + } + + /* Retrieve the size of this sheet */ + error = css_stylesheet_size(c->data.css.sheet, &size); + if (error != CSS_OK) { + msg_data.error = "?"; + content_broadcast(c, CONTENT_MSG_ERROR, msg_data); + c->status = CONTENT_STATUS_ERROR; + return false; + } + c->size += size; + + /* Add on the size of the imported sheets */ + for (i = 0; i < c->data.css.import_count; i++) { + struct content *import = hlcache_handle_get_content( + c->data.css.imports[i].c); + + if (import != NULL) { + c->size += import->size; + } + } + + c->status = CONTENT_STATUS_DONE; + + return error == CSS_OK; +} + +/** + * Convert CSS data ready for use + * + * \param c CSS data to convert + * \param w Width of area content will be displayed in + * \param h Height of area content will be displayed in + * \return CSS error + */ +css_error nscss_convert_css_data(struct content_css_data *c, int w, int h) +{ + const char *referer; + uint32_t i = 0; + css_error error; + nserror nerror; + + error = css_stylesheet_get_url(c->sheet, &referer); + if (error != CSS_OK) { + return error; + } + + error = css_stylesheet_data_done(c->sheet); /* Process pending imports */ while (error == CSS_IMPORTS_PENDING) { + hlcache_child_context child; struct nscss_import *imports; lwc_string *uri; uint64_t media; css_stylesheet *sheet; - error = css_stylesheet_next_pending_import(c->data.css.sheet, + error = css_stylesheet_next_pending_import(c->sheet, &uri, &media); if (error != CSS_OK && error != CSS_INVALID) { - msg_data.error = "?"; - content_broadcast(c, CONTENT_MSG_ERROR, msg_data); - c->status = CONTENT_STATUS_ERROR; - return false; + return error; } /* Give up if there are no more imports */ @@ -234,111 +237,63 @@ bool nscss_convert(struct content *c, int w, int h) } /* Increase space in table */ - imports = realloc(c->data.css.imports, - (c->data.css.import_count + 1) * + imports = realloc(c->imports, (c->import_count + 1) * sizeof(struct nscss_import)); if (imports == NULL) { - msg_data.error = "?"; - content_broadcast(c, CONTENT_MSG_ERROR, msg_data); - c->status = CONTENT_STATUS_ERROR; - return false; + return CSS_NOMEM; } - c->data.css.imports = imports; + c->imports = imports; - /* Create content */ - i = c->data.css.import_count; - c->data.css.imports[c->data.css.import_count].media = media; - c->data.css.imports[c->data.css.import_count++].c = - fetchcache(lwc_string_data(uri), - nscss_import, (intptr_t) c, i, - c->width, c->height, true, NULL, NULL, - false, false); - if (c->data.css.imports[i].c == NULL) { - msg_data.error = "?"; - content_broadcast(c, CONTENT_MSG_ERROR, msg_data); - c->status = CONTENT_STATUS_ERROR; - return false; + /** \todo fallback charset */ + child.charset = NULL; + error = css_stylesheet_quirks_allowed(c->sheet, &child.quirks); + if (error != CSS_OK) { + return error; } - /* Fetch content */ - c->active++; - fetchcache_go(c->data.css.imports[i].c, c->url, - nscss_import, (intptr_t) c, i, - c->width, c->height, NULL, NULL, false, c); + /* Create content */ + i = c->import_count; + c->imports[c->import_count].media = media; + nerror = hlcache_handle_retrieve(lwc_string_data(uri), + 0, referer, NULL, w, h, nscss_import, c, + &child, &c->imports[c->import_count++].c); + if (error != NSERROR_OK) { + return CSS_NOMEM; + } /* Wait for import to fetch + convert */ - while (c->active > 0) { + /** \todo This blocking approach needs to die */ + while (c->imports[i].c != NULL && + content_get_status(c->imports[i].c) != + CONTENT_STATUS_DONE) { fetch_poll(); gui_multitask(); } - if (c->data.css.imports[i].c != NULL) { - sheet = c->data.css.imports[i].c->data.css.sheet; - c->data.css.imports[i].c->data.css.sheet = NULL; + if (c->imports[i].c != NULL) { + struct content *s = hlcache_handle_get_content( + c->imports[i].c); + sheet = s->data.css.sheet; } else { error = css_stylesheet_create(CSS_LEVEL_DEFAULT, - NULL, "", NULL, CSS_ORIGIN_AUTHOR, - media, false, false, c->data.css.dict, + NULL, "", NULL, false, false, myrealloc, NULL, nscss_resolve_url, NULL, &sheet); if (error != CSS_OK) { - msg_data.error = messages_get("NoMemory"); - content_broadcast(c, CONTENT_MSG_ERROR, - msg_data); - c->status = CONTENT_STATUS_ERROR; - return false; + return error; } } - error = css_stylesheet_register_import( - c->data.css.sheet, sheet); + error = css_stylesheet_register_import(c->sheet, sheet); if (error != CSS_OK) { - msg_data.error = "?"; - content_broadcast(c, CONTENT_MSG_ERROR, msg_data); - c->status = CONTENT_STATUS_ERROR; - return false; + return error; } error = CSS_IMPORTS_PENDING; } - /* Retrieve the size of this sheet */ - error = css_stylesheet_size(c->data.css.sheet, &size); - if (error != CSS_OK) { - msg_data.error = "?"; - content_broadcast(c, CONTENT_MSG_ERROR, msg_data); - c->status = CONTENT_STATUS_ERROR; - return false; - } - c->size += size; - - /* Add on the size of the imported sheets, removing ourselves from - * their user list as we go (they're of no use to us now, as we've - * inserted the sheet into ourselves) */ - for (i = 0; i < c->data.css.import_count; i++) { - if (c->data.css.imports[i].c != NULL) { - c->size += c->data.css.imports[i].c->size; - - content_remove_user(c->data.css.imports[i].c, - nscss_import, (uintptr_t) c, i); - } - - c->data.css.imports[i].c = NULL; - } - - /* Remove the imports */ - c->data.css.import_count = 0; - free(c->data.css.imports); - c->data.css.imports = NULL; - - c->status = CONTENT_STATUS_DONE; - - /* Filthy hack to stop this content being reused - * when whatever is using it has finished with it. */ - c->fresh = false; - - return error == CSS_OK; + return error; } /** @@ -348,80 +303,99 @@ bool nscss_convert(struct content *c, int w, int h) */ void nscss_destroy(struct content *c) { + nscss_destroy_css_data(&c->data.css); +} + +/** + * Clean up CSS data + * + * \param c CSS data to clean up + */ +void nscss_destroy_css_data(struct content_css_data *c) +{ uint32_t i; - for (i = 0; i < c->data.css.import_count; i++) { - if (c->data.css.imports[i].c != NULL) { - content_remove_user(c->data.css.imports[i].c, - nscss_import, (uintptr_t) c, i); + for (i = 0; i < c->import_count; i++) { + if (c->imports[i].c != NULL) { + hlcache_handle_release(c->imports[i].c); } - c->data.css.imports[i].c = NULL; + c->imports[i].c = NULL; } - free(c->data.css.imports); + free(c->imports); - if (c->data.css.sheet != NULL) { - css_stylesheet_destroy(c->data.css.sheet); - c->data.css.sheet = NULL; + if (c->sheet != NULL) { + css_stylesheet_destroy(c->sheet); + c->sheet = NULL; } +} - if (c->data.css.dict != NULL) { - lwc_context_unref(c->data.css.dict); - c->data.css.dict = NULL; - } +/** + * Retrieve imported stylesheets + * + * \param h Stylesheet containing imports + * \param n Pointer to location to receive number of imports + * \return Pointer to array of imported stylesheets + */ +struct nscss_import *nscss_get_imports(hlcache_handle *h, uint32_t *n) +{ + struct content *c = hlcache_handle_get_content(h); + + assert(c != NULL); + assert(c->type == CONTENT_CSS); + assert(n != NULL); + + *n = c->data.css.import_count; + + return c->data.css.imports; } /** - * Fetchcache handler for imported stylesheets + * Handler for imported stylesheet events * - * \param msg Message type - * \param c Content being fetched - * \param p1 Parent content - * \param p2 Index into parent's imported stylesheet array - * \param data Message data + * \param handle Handle for stylesheet + * \param event Event object + * \param pw Callback context + * \return NSERROR_OK on success, appropriate error otherwise */ -void nscss_import(content_msg msg, struct content *c, - intptr_t p1, intptr_t p2, union content_msg_data data) +nserror nscss_import(hlcache_handle *handle, + const hlcache_event *event, void *pw) { - struct content *parent = (struct content *) p1; - uint32_t i = (uint32_t) p2; + struct content_css_data *parent = pw; + uint32_t i = 0; - switch (msg) { + switch (event->type) { case CONTENT_MSG_LOADING: - if (c->type != CONTENT_CSS) { - content_remove_user(c, nscss_import, p1, p2); - if (c->user_list->next == NULL) { - fetch_abort(c->fetch); - c->fetch = NULL; - c->status = CONTENT_STATUS_ERROR; - } + if (content_get_type(handle) != CONTENT_CSS) { + hlcache_handle_release(handle); - parent->data.css.imports[i].c = NULL; - parent->active--; - content_add_error(parent, "NotCSS", 0); + for (i = 0; i < parent->import_count; i++) { + if (parent->imports[i].c == handle) { + parent->imports[i].c = NULL; + break; + } + } } break; case CONTENT_MSG_READY: break; case CONTENT_MSG_DONE: - parent->active--; break; - case CONTENT_MSG_AUTH: - case CONTENT_MSG_SSL: - case CONTENT_MSG_LAUNCH: case CONTENT_MSG_ERROR: - if (parent->data.css.imports[i].c == c) { - parent->data.css.imports[i].c = NULL; - parent->active--; + hlcache_handle_release(handle); + for (i = 0; i < parent->import_count; i++) { + if (parent->imports[i].c == handle) { + parent->imports[i].c = NULL; + break; + } } break; case CONTENT_MSG_STATUS: break; - case CONTENT_MSG_NEWPTR: - parent->data.css.imports[i].c = c; - break; default: assert(0); } + + return NSERROR_OK; } @@ -23,7 +23,11 @@ #include <libcss/libcss.h> +#include "utils/errors.h" + struct content; +struct hlcache_handle; +struct http_parameter; struct nscss_import; /** @@ -31,8 +35,6 @@ struct nscss_import; */ struct content_css_data { - lwc_context *dict; /**< Dictionary to intern strings in */ - css_stylesheet *sheet; /**< Stylesheet object */ uint32_t import_count; /**< Number of sheets imported */ @@ -43,12 +45,11 @@ struct content_css_data * Imported stylesheet record */ struct nscss_import { - struct content *c; /**< Content containing sheet */ + struct hlcache_handle *c; /**< Content containing sheet */ uint64_t media; /**< Media types that sheet applies to */ }; -bool nscss_create(struct content *c, struct content *parent, - const char *params[]); +bool nscss_create(struct content *c, const struct http_parameter *params); bool nscss_process_data(struct content *c, char *data, unsigned int size); @@ -56,5 +57,14 @@ bool nscss_convert(struct content *c, int w, int h); void nscss_destroy(struct content *c); +nserror nscss_create_css_data(struct content_css_data *c, + const char *url, const char *charset, bool quirks); +css_error nscss_process_css_data(struct content_css_data *c, char *data, + unsigned int size); +css_error nscss_convert_css_data(struct content_css_data *c, int w, int h); +void nscss_destroy_css_data(struct content_css_data *c); + +struct nscss_import *nscss_get_imports(struct hlcache_handle *h, uint32_t *n); + #endif diff --git a/css/internal.c b/css/internal.c index b9aa83f0d..4c80e639e 100644 --- a/css/internal.c +++ b/css/internal.c @@ -26,7 +26,6 @@ * URL resolution callback for libcss * * \param pw Resolution context - * \param ctx Dictionary to intern result in * \param base Base URI * \param rel Relative URL * \param abs Pointer to location to receive resolved URL @@ -34,8 +33,8 @@ * CSS_NOMEM on memory exhaustion, * CSS_INVALID if resolution failed. */ -css_error nscss_resolve_url(void *pw, lwc_context *ctx, - const char *base, lwc_string *rel, lwc_string **abs) +css_error nscss_resolve_url(void *pw, const char *base, + lwc_string *rel, lwc_string **abs) { lwc_error lerror; char *abs_url, *norm_url; @@ -57,7 +56,7 @@ css_error nscss_resolve_url(void *pw, lwc_context *ctx, free(abs_url); /* Intern it */ - lerror = lwc_context_intern(ctx, norm_url, strlen(norm_url), abs); + lerror = lwc_intern_string(norm_url, strlen(norm_url), abs); if (lerror != lwc_error_ok) { *abs = NULL; free(norm_url); diff --git a/css/internal.h b/css/internal.h index e675a4876..0344d6b32 100644 --- a/css/internal.h +++ b/css/internal.h @@ -21,7 +21,7 @@ #include "css/css.h" -css_error nscss_resolve_url(void *pw, lwc_context *ctx, - const char *base, lwc_string *rel, lwc_string **abs); +css_error nscss_resolve_url(void *pw, const char *base, + lwc_string *rel, lwc_string **abs); #endif diff --git a/css/select.c b/css/select.c index aeb7d3c58..be95a29c0 100644 --- a/css/select.c +++ b/css/select.c @@ -21,7 +21,7 @@ #include <string.h> #include <strings.h> -#include "content/content.h" +#include "content/content_protected.h" #include "content/urldb.h" #include "css/internal.h" #include "css/select.h" @@ -31,12 +31,10 @@ #include "utils/url.h" #include "utils/utils.h" -static css_error node_name(void *pw, void *node, - lwc_context *dict, lwc_string **name); +static css_error node_name(void *pw, void *node, lwc_string **name); static css_error node_classes(void *pw, void *node, - lwc_context *dict, lwc_string ***classes, uint32_t *n_classes); -static css_error node_id(void *pw, void *node, - lwc_context *dict, lwc_string **id); + lwc_string ***classes, uint32_t *n_classes); +static css_error node_id(void *pw, void *node, lwc_string **id); static css_error named_ancestor_node(void *pw, void *node, lwc_string *name, void **ancestor); static css_error named_parent_node(void *pw, void *node, @@ -125,21 +123,20 @@ static css_select_handler selection_handler = { * \param charset Charset of data, or NULL if unknown * \param url URL of document containing data * \param allow_quirks True to permit CSS parsing quirks - * \param dict String internment context * \param alloc Memory allocation function * \param pw Private word for allocator * \return Pointer to stylesheet, or NULL on failure. */ css_stylesheet *nscss_create_inline_style(const uint8_t *data, size_t len, const char *charset, const char *url, bool allow_quirks, - lwc_context *dict, css_allocator_fn alloc, void *pw) + css_allocator_fn alloc, void *pw) { css_stylesheet *sheet; css_error error; error = css_stylesheet_create(CSS_LEVEL_DEFAULT, charset, url, NULL, - CSS_ORIGIN_AUTHOR, CSS_MEDIA_ALL, allow_quirks, true, - dict, alloc, pw, nscss_resolve_url, NULL, &sheet); + allow_quirks, true, alloc, pw, nscss_resolve_url, + NULL, &sheet); if (error != CSS_OK) { LOG(("Failed creating sheet: %d", error)); return NULL; @@ -413,18 +410,16 @@ bool nscss_parse_colour(const char *data, css_color *result) * * \param pw HTML document * \param node DOM node - * \param dict Dictionary to intern result in * \param name Pointer to location to receive node name * \return CSS_OK on success, * CSS_NOMEM on memory exhaustion. */ -css_error node_name(void *pw, void *node, - lwc_context *dict, lwc_string **name) +css_error node_name(void *pw, void *node, lwc_string **name) { xmlNode *n = node; lwc_error lerror; - lerror = lwc_context_intern(dict, (const char *) n->name, + lerror = lwc_intern_string((const char *) n->name, strlen((const char *) n->name), name); switch (lerror) { case lwc_error_oom: @@ -444,7 +439,6 @@ css_error node_name(void *pw, void *node, * * \param pw HTML document * \param node DOM node - * \param dict Dictionary to intern result in * \param classes Pointer to location to receive class name array * \param n_classes Pointer to location to receive length of class name array * \return CSS_OK on success, @@ -454,8 +448,8 @@ css_error node_name(void *pw, void *node, * be allocated using the same allocator as used by libcss during style * selection. */ -css_error node_classes(void *pw, void *node, - lwc_context *dict, lwc_string ***classes, uint32_t *n_classes) +css_error node_classes(void *pw, void *node, + lwc_string ***classes, uint32_t *n_classes) { xmlNode *n = node; xmlAttr *class; @@ -503,8 +497,7 @@ css_error node_classes(void *pw, void *node, } result = temp; - lerror = lwc_context_intern(dict, start, p - start, - &result[items]); + lerror = lwc_intern_string(start, p - start, &result[items]); switch (lerror) { case lwc_error_oom: error = CSS_NOMEM; @@ -536,7 +529,7 @@ cleanup: uint32_t i; for (i = 0; i < items; i++) - lwc_context_string_unref(dict, result[i]); + lwc_string_unref(result[i]); free(result); } @@ -553,13 +546,11 @@ cleanup: * * \param pw HTML document * \param node DOM node - * \param dict Dictionary to intern result in * \param id Pointer to location to receive id value * \return CSS_OK on success, * CSS_NOMEM on memory exhaustion. */ -css_error node_id(void *pw, void *node, - lwc_context *dict, lwc_string **id) +css_error node_id(void *pw, void *node, lwc_string **id) { xmlNode *n = node; xmlAttr *attr; @@ -590,7 +581,7 @@ css_error node_id(void *pw, void *node, } /* Intern value */ - lerror = lwc_context_intern(dict, start, strlen(start), id); + lerror = lwc_intern_string(start, strlen(start), id); switch (lerror) { case lwc_error_oom: error = CSS_NOMEM; @@ -1285,9 +1276,7 @@ css_error node_presentational_hint(void *pw, void *node, lwc_string *iurl; lwc_error lerror; - lerror = lwc_context_intern( - html->data.html.dict, url, - strlen(url), &iurl); + lerror = lwc_intern_string(url, strlen(url), &iurl); free(url); diff --git a/css/select.h b/css/select.h index 7b87b2783..06868d5b2 100644 --- a/css/select.h +++ b/css/select.h @@ -29,7 +29,7 @@ struct content; css_stylesheet *nscss_create_inline_style(const uint8_t *data, size_t len, const char *charset, const char *url, bool allow_quirks, - lwc_context *dict, css_allocator_fn alloc, void *pw); + css_allocator_fn alloc, void *pw); css_computed_style *nscss_get_style(struct content *html, xmlNode *n, uint32_t pseudo_element, uint64_t media, diff --git a/desktop/401login.h b/desktop/401login.h index 8a45477fd..8b5a0a778 100644 --- a/desktop/401login.h +++ b/desktop/401login.h @@ -21,10 +21,10 @@ #include "utils/config.h" -#include "content/content.h" -#include "desktop/browser.h" +struct hlcache_handle; +struct browser_window; -void gui_401login_open(struct browser_window *bw, struct content *c, +void gui_401login_open(struct browser_window *bw, struct hlcache_handle *c, const char *realm); #endif diff --git a/desktop/browser.c b/desktop/browser.c index a4c1e2156..115c16c5c 100644 --- a/desktop/browser.c +++ b/desktop/browser.c @@ -38,7 +38,7 @@ #include "curl/curl.h" #include "utils/config.h" #include "content/fetch.h" -#include "content/fetchcache.h" +#include "content/hlcache.h" #include "content/urldb.h" #include "css/css.h" #include "desktop/401login.h" @@ -66,9 +66,6 @@ /** browser window which is being redrawn. Valid only during redraw. */ struct browser_window *current_redraw_browser; -/** fake content for <a> being saved as a link */ -struct content browser_window_href_content; - /** one or more windows require a reformat */ bool browser_reformat_pending; @@ -77,11 +74,11 @@ bool browser_reformat_pending; static void browser_window_go_post(struct browser_window *bw, const char *url, char *post_urlenc, - struct form_successful_control *post_multipart, + struct fetch_multipart_data *post_multipart, bool add_to_history, const char *referer, bool download, - bool verifiable, struct content *parent); -static void browser_window_callback(content_msg msg, struct content *c, - intptr_t p1, intptr_t p2, union content_msg_data data); + bool verifiable, hlcache_handle *parent); +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); @@ -92,8 +89,8 @@ 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 void download_window_callback(fetch_msg msg, void *p, const void *data, - unsigned long size, fetch_error_code errorcode); +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, @@ -112,7 +109,7 @@ static void browser_window_mouse_track_html(struct browser_window *bw, browser_mouse_state mouse, int x, int y); static void browser_window_mouse_track_text(struct browser_window *bw, browser_mouse_state mouse, int x, int y); -static void browser_radio_set(struct content *content, +static void browser_radio_set(hlcache_handle *content, struct form_control *radio); static gui_pointer_shape get_pointer_shape(struct browser_window *bw, struct box *box, bool imagemap); @@ -253,7 +250,7 @@ void browser_window_download(struct browser_window *bw, const char *url, void browser_window_go_unverifiable(struct browser_window *bw, const char *url, const char *referer, bool history_add, - struct content *parent) + hlcache_handle *parent) { /* All fetches passing through here are unverifiable * (i.e are not the result of user action) */ @@ -284,17 +281,22 @@ void browser_window_go_unverifiable(struct browser_window *bw, void browser_window_go_post(struct browser_window *bw, const char *url, char *post_urlenc, - struct form_successful_control *post_multipart, + struct fetch_multipart_data *post_multipart, bool add_to_history, const char *referer, bool download, - bool verifiable, struct content *parent) + bool verifiable, hlcache_handle *parent) { - struct content *c; + hlcache_handle *c; char *url2; char *fragment; url_func_result res; int depth = 0; struct browser_window *cur; int width, height; + uint32_t fetch_flags = 0; + bool fetch_is_post = (post_urlenc != NULL || post_multipart != NULL); + llcache_post_data post; + hlcache_child_context child; + nserror error; LOG(("bw %p, url %s", bw, url)); assert(bw); @@ -308,16 +310,44 @@ void browser_window_go_post(struct browser_window *bw, const char *url, return; } + /* Set up retrieval parameters */ + if (verifiable) + fetch_flags |= LLCACHE_RETRIEVE_VERIFIABLE; + + if (post_multipart != NULL) { + post.type = LLCACHE_POST_MULTIPART; + post.data.multipart = post_multipart; + } else if (post_urlenc != NULL) { + post.type = LLCACHE_POST_URL_ENCODED; + post.data.urlenc = post_urlenc; + } + + if (parent != NULL) { +//newcache extract charset and quirks from parent content + child.charset = NULL; + child.quirks = false; + } + + /* Normalize the request URL */ res = url_normalize(url, &url2); if (res != URL_FUNC_OK) { LOG(("failed to normalize url %s", url)); return; } - /* check we can actually handle this URL */ - if (!fetch_can_fetch(url2)) { - gui_launch_url(url2); + /* Get download out of the way */ + 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); + if (error != NSERROR_OK) + LOG(("Failed to fetch download: %d", error)); + free(url2); + return; } @@ -336,9 +366,10 @@ void browser_window_go_post(struct browser_window *bw, const char *url, bw->frag_id = fragment; /* Compare new URL with existing one (ignoring fragments) */ - if (bw->current_content && bw->current_content->url) { - res = url_compare(bw->current_content->url, url2, - true, &same_url); + if (bw->current_content != NULL && + content_get_url(bw->current_content) != NULL) { + res = url_compare(content_get_url(bw->current_content), + url2, true, &same_url); if (res == URL_FUNC_NOMEM) { free(url2); warn_user("NoMemory", 0); @@ -351,17 +382,17 @@ void browser_window_go_post(struct browser_window *bw, const char *url, /* if we're simply moving to another ID on the same page, * don't bother to fetch, just update the window. */ - if (same_url && !post_urlenc && !post_multipart && - !strchr(url2, '?')) { + if (same_url && fetch_is_post == false && + strchr(url2, '?') == 0) { free(url2); if (add_to_history) history_add(bw->history, bw->current_content, bw->frag_id); browser_window_update(bw, false); - if (bw->current_content) { + if (bw->current_content != NULL) { browser_window_refresh_url_bar(bw, - bw->current_content->url, - bw->frag_id); + content_get_url(bw->current_content), + bw->frag_id); } return; } @@ -376,28 +407,26 @@ 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; - c = fetchcache(url2, browser_window_callback, (intptr_t) bw, 0, - width, height, false, - post_urlenc, post_multipart, verifiable, download); - free(url2); - if (!c) { + + error = hlcache_handle_retrieve(url2, 0, referer, + fetch_is_post ? &post : NULL, width, height, + browser_window_callback, bw, + parent != NULL ? &child : NULL, &c); + if (error == NSERROR_NO_FETCH_HANDLER) { + gui_launch_url(url2); + free(url2); + return; + } else if (error != NSERROR_OK) { + free(url2); browser_window_set_status(bw, messages_get("NoMemory")); warn_user("NoMemory", 0); return; } + free(url2); + bw->loading_content = c; browser_window_start_throbber(bw); - - if (referer && referer != bw->referer) { - free(bw->referer); - bw->referer = strdup(referer); - } - - bw->download = download; - fetchcache_go(c, referer, browser_window_callback, - (intptr_t) bw, 0, width, height, - post_urlenc, post_multipart, verifiable, parent); } @@ -405,89 +434,95 @@ void browser_window_go_post(struct browser_window *bw, const char *url, * Callback for fetchcache() for browser window fetches. */ -void browser_window_callback(content_msg msg, struct content *c, - intptr_t p1, intptr_t p2, union content_msg_data data) +nserror browser_window_callback(hlcache_handle *c, + const hlcache_event *event, void *pw) { - struct browser_window *bw = (struct browser_window *) p1; + struct browser_window *bw = pw; - switch (msg) { + switch (event->type) { case CONTENT_MSG_LOADING: assert(bw->loading_content == c); - if (c->type == CONTENT_OTHER) + if (content_get_type(c) == CONTENT_OTHER) browser_window_convert_to_download(bw); #ifdef WITH_THEME_INSTALL - else if (c->type == CONTENT_THEME) { + else if (content_get_type(c) == CONTENT_THEME) { theme_install_start(c); - bw->loading_content = 0; - content_remove_user(c, browser_window_callback, - (intptr_t) bw, 0); + 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); } #endif else { bw->refresh_interval = -1; - browser_window_set_status(bw, c->status_message); + browser_window_set_status(bw, + content_get_status_message(c)); } break; case CONTENT_MSG_READY: assert(bw->loading_content == c); - if (bw->current_content) { - if (bw->current_content->status == - CONTENT_STATUS_READY || - bw->current_content->status == - CONTENT_STATUS_DONE) + if (bw->current_content != NULL) { + content_status status = + content_get_status(bw->current_content); + + if (status == CONTENT_STATUS_READY || + status == CONTENT_STATUS_DONE) content_close(bw->current_content); - content_remove_user(bw->current_content, - browser_window_callback, - (intptr_t) bw, 0); + + hlcache_handle_release(bw->current_content); } + bw->current_content = c; bw->loading_content = NULL; + browser_window_remove_caret(bw); + bw->scroll = NULL; + gui_window_new_content(bw->window); - if (bw->current_content) { - browser_window_refresh_url_bar(bw, - bw->current_content->url, - bw->frag_id); - } + + browser_window_refresh_url_bar(bw, + content_get_url(bw->current_content), + bw->frag_id); + /* new content; set scroll_to_top */ browser_window_update(bw, true); content_open(c, bw, 0, 0, 0, 0); - browser_window_set_status(bw, c->status_message); + browser_window_set_status(bw, content_get_status_message(c)); /* history */ if (bw->history_add && bw->history) { + const char *url = content_get_url(c); + history_add(bw->history, c, bw->frag_id); - if (urldb_add_url(c->url)) { - urldb_set_url_title(c->url, - c->title ? c->title : c->url); - urldb_update_url_visit_data(c->url); - urldb_set_url_content_type(c->url, - c->type); - /* This is safe as we've just - * added the URL */ - global_history_add( - urldb_get_url(c->url)); + if (urldb_add_url(url)) { + urldb_set_url_title(url, content_get_title(c)); + urldb_update_url_visit_data(url); + urldb_set_url_content_type(url, + content_get_type(c)); + /* This is safe as we've just added the URL */ + global_history_add(urldb_get_url(url)); } } /* text selection */ - if (c->type == CONTENT_HTML) + if (content_get_type(c) == CONTENT_HTML) selection_init(bw->sel, - bw->current_content->data.html.layout); - if (c->type == CONTENT_TEXTPLAIN) + html_get_box_tree(bw->current_content)); + if (content_get_type(c) == CONTENT_TEXTPLAIN) selection_init(bw->sel, NULL); /* frames */ - if (c->type == CONTENT_HTML && c->data.html.frameset) - browser_window_create_frameset(bw, - c->data.html.frameset); - if (c->type == CONTENT_HTML && c->data.html.iframe) - browser_window_create_iframes(bw, c->data.html.iframe); + if (content_get_type(c) == CONTENT_HTML && + html_get_frameset(c) != NULL) + browser_window_create_frameset(bw, + html_get_frameset(c)); + if (content_get_type(c) == CONTENT_HTML && + html_get_iframe(c) != NULL) + browser_window_create_iframes(bw, html_get_iframe(c)); break; @@ -495,137 +530,75 @@ void browser_window_callback(content_msg msg, struct content *c, assert(bw->current_content == c); browser_window_update(bw, false); - browser_window_set_status(bw, c->status_message); + browser_window_set_status(bw, content_get_status_message(c)); browser_window_stop_throbber(bw); browser_window_set_icon(bw); + history_update(bw->history, c); hotlist_visited(c); - free(bw->referer); - bw->referer = 0; + if (bw->refresh_interval != -1) schedule(bw->refresh_interval, browser_window_refresh, bw); break; case CONTENT_MSG_ERROR: - browser_window_set_status(bw, data.error); + browser_window_set_status(bw, event->data.error); /* Only warn the user about errors in top-level windows */ if (bw->browser_window_type == BROWSER_WINDOW_NORMAL) - warn_user(data.error, 0); + warn_user(event->data.error, 0); if (c == bw->loading_content) - bw->loading_content = 0; + bw->loading_content = NULL; else if (c == bw->current_content) { - bw->current_content = 0; + bw->current_content = NULL; browser_window_remove_caret(bw); bw->scroll = NULL; selection_init(bw->sel, NULL); } + + hlcache_handle_release(c); + browser_window_stop_throbber(bw); - free(bw->referer); - bw->referer = 0; break; case CONTENT_MSG_STATUS: - browser_window_set_status(bw, c->status_message); + browser_window_set_status(bw, content_get_status_message(c)); break; case CONTENT_MSG_REFORMAT: if (c == bw->current_content && - c->type == CONTENT_HTML) { + content_get_type(c) == CONTENT_HTML) { /* reposition frames */ - if (c->data.html.frameset) + if (html_get_frameset(c) != NULL) browser_window_recalculate_frameset(bw); /* reflow iframe positions */ - if (c->data.html.iframe) + if (html_get_iframe(c) != NULL) browser_window_recalculate_iframes(bw); /* box tree may have changed, need to relabel */ - selection_reinit(bw->sel, c->data.html.layout); + selection_reinit(bw->sel, html_get_box_tree(c)); } + if (bw->move_callback) bw->move_callback(bw, bw->caret_p); + browser_window_update(bw, false); break; case CONTENT_MSG_REDRAW: - gui_window_update_box(bw->window, &data); - break; - - case CONTENT_MSG_NEWPTR: - bw->loading_content = c; - if (data.new_url) { - /* Replacement URL too, so check for new fragment */ - char *fragment; - url_func_result res; - - /* Remove any existing fragment */ - free(bw->frag_id); - bw->frag_id = NULL; - - /* Extract new one, if any */ - res = url_fragment(data.new_url, &fragment); - if (res == URL_FUNC_OK) { - /* Save for later use */ - bw->frag_id = fragment; - } - /* Ignore memory exhaustion here -- it'll simply result - * in the window being scrolled to the top rather than - * to the fragment. That's acceptable, given that it's - * likely that more important things will complain - * about memory shortage. */ - } - break; - - case CONTENT_MSG_LAUNCH: - assert(data.launch_url != NULL); - - bw->loading_content = NULL; - - gui_launch_url(data.launch_url); - - browser_window_stop_throbber(bw); - free(bw->referer); - bw->referer = 0; - break; - - case CONTENT_MSG_AUTH: - gui_401login_open(bw, c, data.auth_realm); - if (c == bw->loading_content) - bw->loading_content = 0; - else if (c == bw->current_content) { - bw->current_content = 0; - browser_window_remove_caret(bw); - bw->scroll = NULL; - selection_init(bw->sel, NULL); - } - browser_window_stop_throbber(bw); - free(bw->referer); - bw->referer = 0; - break; - - case CONTENT_MSG_SSL: - gui_cert_verify(bw, c, data.ssl.certs, data.ssl.num); - if (c == bw->loading_content) - bw->loading_content = 0; - else if (c == bw->current_content) { - bw->current_content = 0; - browser_window_remove_caret(bw); - bw->scroll = NULL; - selection_init(bw->sel, NULL); - } - browser_window_stop_throbber(bw); - free(bw->referer); - bw->referer = 0; + gui_window_update_box(bw->window, &event->data); break; case CONTENT_MSG_REFRESH: - bw->refresh_interval = data.delay * 100; + bw->refresh_interval = event->data.delay * 100; break; default: assert(0); } + + return NSERROR_OK; } @@ -636,34 +609,26 @@ void browser_window_callback(content_msg msg, struct content *c, void browser_window_convert_to_download(struct browser_window *bw) { struct gui_download_window *download_window; - struct content *c = bw->loading_content; - struct fetch *fetch; + hlcache_handle *c = bw->loading_content; + llcache_handle *stream; assert(c); - fetch = c->fetch; + stream = content_convert_to_download(c); - if (fetch) { - /* create download window */ - download_window = gui_download_window_create(c->url, - c->mime_type, fetch, c->total_size, - bw->window); + /** \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); - if (download_window) { - /* extract fetch from content */ - c->fetch = 0; - c->fresh = false; - fetch_change_callback(fetch, download_window_callback, - download_window); - } - } else { - /* must already be a download window for this fetch */ - /** \todo open it at top of stack */ - } + llcache_handle_change_callback(stream, + download_window_callback, download_window); /* remove content from browser window */ - bw->loading_content = 0; - content_remove_user(c, browser_window_callback, (intptr_t) bw, 0); + hlcache_handle_release(bw->loading_content); + bw->loading_content = NULL; + browser_window_stop_throbber(bw); } @@ -678,23 +643,26 @@ void browser_window_refresh(void *p) { struct browser_window *bw = p; bool history_add = true; + const char *url; + const char *refresh; - assert(bw->current_content && - (bw->current_content->status == CONTENT_STATUS_READY || - bw->current_content->status == CONTENT_STATUS_DONE)); + assert(bw->current_content != NULL && + (content_get_status(bw->current_content) == + CONTENT_STATUS_READY || + content_get_status(bw->current_content) == + CONTENT_STATUS_DONE)); /* Ignore if the refresh URL has gone * (may happen if a fetch error occurred) */ - if (!bw->current_content->refresh) + refresh = content_get_refresh_url(bw->current_content); + if (refresh == NULL) return; /* mark this content as invalid so it gets flushed from the cache */ - bw->current_content->fresh = false; + content_invalidate_reuse_data(bw->current_content); - if ((bw->current_content->url) && - (bw->current_content->refresh) && - (!strcmp(bw->current_content->url, - bw->current_content->refresh))) + url = content_get_url(bw->current_content); + if (url != NULL && strcmp(url, refresh) == 0) history_add = false; /* Treat an (almost) immediate refresh in a top-level browser window as @@ -705,11 +673,9 @@ void browser_window_refresh(void *p) * all. */ if (bw->refresh_interval <= 100 && bw->parent == NULL) { - browser_window_go(bw, bw->current_content->refresh, - bw->current_content->url, history_add); + browser_window_go(bw, refresh, url, history_add); } else { - browser_window_go_unverifiable(bw, bw->current_content->refresh, - bw->current_content->url, history_add, + browser_window_go_unverifiable(bw, refresh, url, history_add, bw->current_content); } } @@ -727,6 +693,7 @@ void browser_window_start_throbber(struct browser_window *bw) while (bw->parent) bw = bw->parent; + gui_window_start_throbber(bw->window); } @@ -780,9 +747,11 @@ void browser_window_set_icon(struct browser_window *bw) { while (bw->parent) bw = bw->parent; - if ((bw->current_content != NULL) && (bw->current_content->type == CONTENT_HTML)) + + if (bw->current_content != NULL && + content_get_type(bw->current_content) == CONTENT_HTML) gui_window_set_icon(bw->window, - bw->current_content->data.html.favicon); + html_get_favicon(bw->current_content)); else gui_window_set_icon(bw->window, NULL); } @@ -794,19 +763,16 @@ void browser_window_set_icon(struct browser_window *bw) * \param scroll_to_top move view to top of page */ -void browser_window_update(struct browser_window *bw, - bool scroll_to_top) +void browser_window_update(struct browser_window *bw, bool scroll_to_top) { struct box *pos; int x, y; - if (!bw->current_content) + if (bw->current_content == NULL) return; - if (bw->current_content->title != NULL) { - gui_window_set_title(bw->window, bw->current_content->title); - } else - gui_window_set_title(bw->window, bw->current_content->url); + gui_window_set_title(bw->window, + content_get_title(bw->current_content)); gui_window_update_extent(bw->window); @@ -815,9 +781,11 @@ void browser_window_update(struct browser_window *bw, /** \todo don't do this if the user has scrolled */ /* if frag_id exists, then try to scroll to it */ - if (bw->frag_id && bw->current_content->type == CONTENT_HTML) { - if ((pos = box_find_by_id(bw->current_content->data.html.layout, - bw->frag_id)) != 0) { + if (bw->frag_id && + content_get_type(bw->current_content) == CONTENT_HTML) { + struct box *layout = html_get_box_tree(bw->current_content); + + if ((pos = box_find_by_id(layout, bw->frag_id)) != 0) { box_coords(pos, &x, &y); gui_window_set_scroll(bw->window, x, y); } @@ -837,17 +805,17 @@ void browser_window_stop(struct browser_window *bw) { int children, index; - if (bw->loading_content) { - content_remove_user(bw->loading_content, - browser_window_callback, (intptr_t) bw, 0); - bw->loading_content = 0; + if (bw->loading_content != NULL) { + hlcache_handle_release(bw->loading_content); + bw->loading_content = NULL; } - if (bw->current_content && - bw->current_content->status != CONTENT_STATUS_DONE) { - assert(bw->current_content->status == CONTENT_STATUS_READY); + if (bw->current_content != NULL && content_get_status( + bw->current_content) != CONTENT_STATUS_DONE) { + assert(content_get_status(bw->current_content) == + CONTENT_STATUS_READY); content_stop(bw->current_content, - browser_window_callback, (intptr_t) bw, 0); + browser_window_callback, bw); } schedule_remove(browser_window_refresh, bw); @@ -876,29 +844,43 @@ void browser_window_stop(struct browser_window *bw) void browser_window_reload(struct browser_window *bw, bool all) { - struct content *c; + hlcache_handle *c; unsigned int i; - if (!bw->current_content || bw->loading_content) + if (bw->current_content == NULL || bw->loading_content != NULL) return; - if (all && bw->current_content->type == CONTENT_HTML) { + if (all && content_get_type(bw->current_content) == CONTENT_HTML) { + struct html_stylesheet *sheets; + struct content_html_object *objects; + unsigned int count; + c = bw->current_content; + /* invalidate objects */ - for (i = 0; i != c->data.html.object_count; i++) { - if (c->data.html.object[i].content) - c->data.html.object[i].content->fresh = false; + objects = html_get_objects(c, &count); + + for (i = 0; i != count; i++) { + if (objects[i].content != NULL) + content_invalidate_reuse_data( + objects[i].content); } + /* invalidate stylesheets */ - for (i = STYLESHEET_START; i != c->data.html.stylesheet_count; - i++) { - if (c->data.html.stylesheets[i].c) - c->data.html.stylesheets[i].c->fresh = - false; + sheets = html_get_stylesheets(c, &count); + + for (i = STYLESHEET_START; i != count; i++) { + if (sheets[i].type == HTML_STYLESHEET_EXTERNAL && + sheets[i].data.external != NULL) { + content_invalidate_reuse_data( + sheets[i].data.external); + } } } - bw->current_content->fresh = false; - browser_window_go_post(bw, bw->current_content->url, 0, 0, + + content_invalidate_reuse_data(bw->current_content); + + browser_window_go_post(bw, content_get_url(bw->current_content), 0, 0, false, 0, false, true, 0); } @@ -1012,21 +994,22 @@ void browser_window_destroy_internal(struct browser_window *bw) LOG(("Destroying window")); - if ((bw->children) || (bw->iframes)) + if (bw->children != NULL || bw->iframes != NULL) browser_window_destroy_children(bw); - if (bw->loading_content) { - content_remove_user(bw->loading_content, - browser_window_callback, (intptr_t) bw, 0); - bw->loading_content = 0; + + if (bw->loading_content != NULL) { + hlcache_handle_release(bw->loading_content); + bw->loading_content = NULL; } - if (bw->current_content) { - if (bw->current_content->status == CONTENT_STATUS_READY || - bw->current_content->status == - CONTENT_STATUS_DONE) + + if (bw->current_content != NULL) { + content_status status = content_get_status(bw->current_content); + if (status == CONTENT_STATUS_READY || + status == CONTENT_STATUS_DONE) content_close(bw->current_content); - content_remove_user(bw->current_content, - browser_window_callback, (intptr_t) bw, 0); - bw->current_content = 0; + + hlcache_handle_release(bw->current_content); + bw->current_content = NULL; } schedule_remove(browser_window_refresh, bw); @@ -1058,15 +1041,15 @@ struct browser_window *browser_window_owner(struct browser_window *bw) return bw->parent; /* the parent of a frameset is either a NORMAL window or an IFRAME */ - while (bw->parent) { + while (bw->parent != NULL) { switch (bw->browser_window_type) { - case BROWSER_WINDOW_NORMAL: - case BROWSER_WINDOW_IFRAME: - return bw; - case BROWSER_WINDOW_FRAME: - case BROWSER_WINDOW_FRAMESET: - bw = bw->parent; - break; + case BROWSER_WINDOW_NORMAL: + case BROWSER_WINDOW_IFRAME: + return bw; + case BROWSER_WINDOW_FRAME: + case BROWSER_WINDOW_FRAMESET: + bw = bw->parent; + break; } } return bw; @@ -1083,9 +1066,9 @@ struct browser_window *browser_window_owner(struct browser_window *bw) void browser_window_reformat(struct browser_window *bw, int width, int height) { - struct content *c = bw->current_content; + hlcache_handle *c = bw->current_content; - if (!c) + if (c == NULL) return; content_reformat(c, width / bw->scale, height / bw->scale); @@ -1104,23 +1087,28 @@ void browser_window_set_scale(struct browser_window *bw, float scale, bool all) { while (bw->parent && all) bw = bw->parent; + browser_window_set_scale_internal(bw, scale); + if (bw->parent) bw = bw->parent; + browser_window_recalculate_frameset(bw); } void browser_window_set_scale_internal(struct browser_window *bw, float scale) { int i; - struct content *c; + hlcache_handle *c; if (fabs(bw->scale-scale) < 0.0001) return; + bw->scale = scale; c = bw->current_content; - if (c) { - if (!content_can_reformat(c)) { + + if (c != NULL) { + if (content_can_reformat(c) == false) { browser_window_update(bw, false); } else { bw->reformat_pending = true; @@ -1189,14 +1177,14 @@ struct browser_window *browser_window_find_target(struct browser_window *bw, { struct browser_window *bw_target; struct browser_window *top; - struct content *c; + hlcache_handle *c; int rdepth; /* use the base target if we don't have one */ c = bw->current_content; - if (!target && c && c->data.html.base_target) - target = c->data.html.base_target; - if (!target) + if (target == NULL && c != NULL && content_get_type(c) == CONTENT_HTML) + target = html_get_base_target(c); + if (target == NULL) target = TARGET_SELF; /* allow the simple case of target="_blank" to be ignored if requested @@ -1348,35 +1336,57 @@ void browser_window_find_target_internal(struct browser_window *bw, * Callback for fetch for download window fetches. */ -void download_window_callback(fetch_msg msg, void *p, const void *data, - unsigned long size, fetch_error_code errorcode) +nserror download_window_callback(llcache_handle *handle, + const llcache_event *event, void *pw) { - struct gui_download_window *download_window = p; + 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; - switch (msg) { - case FETCH_PROGRESS: - break; - case FETCH_DATA: - gui_download_window_data(download_window, data, size); - break; + case LLCACHE_EVENT_HAD_DATA: + assert(download_window != NULL); - case FETCH_FINISHED: - gui_download_window_done(download_window); - break; + /** \todo Lose ugly cast */ + gui_download_window_data(download_window, + (char *) event->data.data.buf, + event->data.data.len); - case FETCH_ERROR: - gui_download_window_error(download_window, data); - break; + break; - case FETCH_TYPE: - case FETCH_NOTMODIFIED: - case FETCH_AUTH: - case FETCH_CERT_ERR: - default: - /* not possible */ - assert(0); - 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; } @@ -1392,12 +1402,12 @@ void download_window_callback(fetch_msg msg, void *p, const void *data, void browser_window_mouse_click(struct browser_window *bw, browser_mouse_state mouse, int x, int y) { - struct content *c = bw->current_content; + hlcache_handle *c = bw->current_content; if (!c) return; - switch (c->type) { + switch (content_get_type(c)) { case CONTENT_HTML: browser_window_mouse_action_html(bw, mouse, x, y); break; @@ -1457,12 +1467,12 @@ void browser_window_mouse_action_html(struct browser_window *bw, struct box *url_box = 0; struct box *gadget_box = 0; struct box *text_box = 0; - struct content *c = bw->current_content; + hlcache_handle *c = bw->current_content; struct box *box; - struct content *content = c; - struct content *gadget_content = c; + hlcache_handle *content = c; + hlcache_handle *gadget_content = c; struct form_control *gadget = 0; - struct content *object = NULL; + hlcache_handle *object = NULL; struct box *next_box; struct box *drag_candidate = NULL; struct scroll *scroll = NULL; @@ -1522,7 +1532,7 @@ void browser_window_mouse_action_html(struct browser_window *bw, /* search the box tree for a link, imagemap, form control, or * box with scrollbars */ - box = c->data.html.layout; + box = html_get_box_tree(c); /* Consider the margins of the html page now */ box_x = box->margin[LEFT]; @@ -1715,7 +1725,7 @@ void browser_window_mouse_action_html(struct browser_window *bw, bw->drag_type = DRAGGING_SELECTION; status = messages_get("Selecting"); } else - status = c->status_message; + status = content_get_status_message(c); } else if (mouse & BROWSER_MOUSE_PRESS_1) selection_clear(bw->sel, true); @@ -1787,7 +1797,7 @@ void browser_window_mouse_action_html(struct browser_window *bw, bw->window); /* \todo should have a drag-saving object msg */ - status = c->status_message; + status = content_get_status_message(c); } else if (url) { if (title) { @@ -1803,17 +1813,10 @@ void browser_window_mouse_action_html(struct browser_window *bw, mouse & BROWSER_MOUSE_MOD_1) { /* force download of link */ browser_window_go_post(bw, url, 0, 0, false, - c->url, true, true, 0); + content_get_url(c), true, true, 0); } else if (mouse & BROWSER_MOUSE_CLICK_2 && mouse & BROWSER_MOUSE_MOD_1) { - free(browser_window_href_content.url); - browser_window_href_content.url = strdup(url); - if (!browser_window_href_content.url) - warn_user("NoMemory", 0); - else - gui_window_save_as_link(bw->window, - &browser_window_href_content); - + gui_window_save_link(bw->window, url, title); } else if (mouse & (BROWSER_MOUSE_CLICK_1 | BROWSER_MOUSE_CLICK_2)) action = ACTION_GO; @@ -1834,11 +1837,13 @@ void browser_window_mouse_action_html(struct browser_window *bw, /* if clicking in the main page, remove the selection from any * text areas */ if (!done) { + struct box *layout = html_get_box_tree(c); + if (text_box && (mouse & (BROWSER_MOUSE_CLICK_1 | BROWSER_MOUSE_CLICK_2)) && - selection_root(bw->sel) != c->data.html.layout) - selection_init(bw->sel, c->data.html.layout); + selection_root(bw->sel) != layout) + selection_init(bw->sel, layout); if (text_box) { int pixel_offset; @@ -1866,7 +1871,7 @@ void browser_window_mouse_action_html(struct browser_window *bw, status = messages_get("Selecting"); } else - status = c->status_message; + status = content_get_status_message(c); done = true; } @@ -1879,9 +1884,10 @@ void browser_window_mouse_action_html(struct browser_window *bw, if (title) status = title; else if (bw->loading_content) - status = bw->loading_content->status_message; + status = content_get_status_message( + bw->loading_content); else - status = c->status_message; + status = content_get_status_message(c); if (mouse & BROWSER_MOUSE_DRAG_1) { if (mouse & BROWSER_MOUSE_MOD_2) { @@ -1944,7 +1950,7 @@ void browser_window_mouse_action_html(struct browser_window *bw, break; case ACTION_GO: browser_window_go(browser_window_find_target(bw, target, mouse), - url, c->url, true); + url, content_get_url(c), true); break; case ACTION_NONE: break; @@ -1970,7 +1976,7 @@ void browser_window_mouse_action_html(struct browser_window *bw, void browser_window_mouse_action_text(struct browser_window *bw, browser_mouse_state mouse, int x, int y) { - struct content *c = bw->current_content; + hlcache_handle *c = bw->current_content; gui_pointer_shape pointer = GUI_POINTER_DEFAULT; const char *status = 0; size_t idx; @@ -1988,13 +1994,14 @@ void browser_window_mouse_action_text(struct browser_window *bw, status = messages_get("Selecting"); } else - status = c->status_message; + status = content_get_status_message(c); } else { if (bw->loading_content) - status = bw->loading_content->status_message; + status = content_get_status_message( + bw->loading_content); else - status = c->status_message; + status = content_get_status_message(c); if (mouse & (BROWSER_MOUSE_DRAG_1 | BROWSER_MOUSE_DRAG_2)) { browser_window_page_drag_start(bw, x, y); @@ -2021,7 +2028,7 @@ void browser_window_mouse_action_text(struct browser_window *bw, void browser_window_mouse_track(struct browser_window *bw, browser_mouse_state mouse, int x, int y) { - struct content *c = bw->current_content; + hlcache_handle *c = bw->current_content; if (c == NULL && bw->drag_type != DRAGGING_FRAME) return; @@ -2051,7 +2058,7 @@ void browser_window_mouse_track(struct browser_window *bw, } else { assert(c != NULL); - switch (c->type) { + switch (content_get_type(c)) { case CONTENT_HTML: browser_window_mouse_track_html(bw, mouse, x, y); break; @@ -2129,7 +2136,7 @@ void browser_window_mouse_track_text(struct browser_window *bw, switch (bw->drag_type) { case DRAGGING_SELECTION: { - struct content *c = bw->current_content; + hlcache_handle *c = bw->current_content; int dir = -1; size_t idx; @@ -2198,7 +2205,7 @@ void browser_window_mouse_drag_end(struct browser_window *bw, switch (bw->drag_type) { case DRAGGING_SELECTION: { - struct content *c = bw->current_content; + hlcache_handle *c = bw->current_content; if (c) { bool found = true; int dir = -1; @@ -2206,7 +2213,7 @@ void browser_window_mouse_drag_end(struct browser_window *bw, if (selection_dragging_start(bw->sel)) dir = 1; - if (c->type == CONTENT_HTML) { + if (content_get_type(c) == CONTENT_HTML) { int pixel_offset; struct box *box; int dx, dy; @@ -2236,7 +2243,8 @@ void browser_window_mouse_drag_end(struct browser_window *bw, found = false; } else { - assert(c->type == CONTENT_TEXTPLAIN); + assert(content_get_type(c) == + CONTENT_TEXTPLAIN); idx = textplain_offset_from_coords(c, x, y, dir); } @@ -2263,7 +2271,7 @@ void browser_window_mouse_drag_end(struct browser_window *bw, * \param radio form control of type GADGET_RADIO */ -void browser_radio_set(struct content *content, +void browser_radio_set(hlcache_handle *content, struct form_control *radio) { struct form_control *control; @@ -2309,26 +2317,7 @@ void browser_radio_set(struct content *content, void browser_window_redraw_rect(struct browser_window *bw, int x, int y, int width, int height) { - struct content *c = bw->current_content; - - if (c) { - union content_msg_data data; - - data.redraw.x = x; - data.redraw.y = y; - data.redraw.width = width; - data.redraw.height = height; - - data.redraw.full_redraw = true; - - data.redraw.object = c; - data.redraw.object_x = 0; - data.redraw.object_y = 0; - data.redraw.object_width = c->width; - data.redraw.object_height = c->height; - - content_broadcast(c, CONTENT_MSG_REDRAW, data); - } + content_request_redraw(bw->current_content, x, y, width, height); } @@ -2339,29 +2328,15 @@ void browser_window_redraw_rect(struct browser_window *bw, int x, int y, * \param box box to redraw */ -void browser_redraw_box(struct content *c, struct box *box) +void browser_redraw_box(hlcache_handle *c, struct box *box) { int x, y; - union content_msg_data data; box_coords(box, &x, &y); - data.redraw.x = x; - data.redraw.y = y; - data.redraw.width = box->padding[LEFT] + box->width + - box->padding[RIGHT]; - data.redraw.height = box->padding[TOP] + box->height + - box->padding[BOTTOM]; - - data.redraw.full_redraw = true; - - data.redraw.object = c; - data.redraw.object_x = 0; - data.redraw.object_y = 0; - data.redraw.object_width = c->width; - data.redraw.object_height = c->height; - - content_broadcast(c, CONTENT_MSG_REDRAW, data); + content_request_redraw(c, x, y, + box->padding[LEFT] + box->width + box->padding[RIGHT], + box->padding[TOP] + box->height + box->padding[BOTTOM]); } @@ -2441,7 +2416,8 @@ gui_pointer_shape get_pointer_shape(struct browser_window *bw, struct box *box, assert(bw); loading = (bw->loading_content != NULL || (bw->current_content && - bw->current_content->status == CONTENT_STATUS_READY)); + content_get_status(bw->current_content) == + CONTENT_STATUS_READY)); if (wallclock() - bw->last_action < 100 && loading) /* If less than 1 second since last link followed, show @@ -2540,14 +2516,15 @@ gui_pointer_shape get_pointer_shape(struct browser_window *bw, struct box *box, * Collect controls and submit a form. */ -void browser_form_submit(struct browser_window *bw, struct browser_window *target, +void browser_form_submit(struct browser_window *bw, + struct browser_window *target, struct form *form, struct form_control *submit_button) { char *data = 0, *url = 0; - struct form_successful_control *success; + struct fetch_multipart_data *success; assert(form); - assert(bw->current_content->type == CONTENT_HTML); + assert(content_get_type(bw->current_content) == CONTENT_HTML); if (!form_successful_controls(form, submit_button, &success)) { warn_user("NoMemory", 0); @@ -2558,14 +2535,14 @@ void browser_form_submit(struct browser_window *bw, struct browser_window *targe case method_GET: data = form_url_encode(form, success); if (!data) { - form_free_successful(success); + fetch_multipart_data_destroy(success); warn_user("NoMemory", 0); return; } url = calloc(1, strlen(form->action) + strlen(data) + 2); if (!url) { - form_free_successful(success); + fetch_multipart_data_destroy(success); free(data); warn_user("NoMemory", 0); return; @@ -2577,25 +2554,27 @@ void browser_form_submit(struct browser_window *bw, struct browser_window *targe sprintf(url, "%s?%s", form->action, data); } browser_window_go(target, url, - bw->current_content->url, true); + content_get_url(bw->current_content), + true); break; case method_POST_URLENC: data = form_url_encode(form, success); if (!data) { - form_free_successful(success); + fetch_multipart_data_destroy(success); warn_user("NoMemory", 0); return; } browser_window_go_post(target, form->action, data, 0, - true, bw->current_content->url, + true, + content_get_url(bw->current_content), false, true, 0); break; case method_POST_MULTIPART: browser_window_go_post(target, form->action, 0, success, true, - bw->current_content->url, + content_get_url(bw->current_content), false, true, 0); break; @@ -2603,7 +2582,7 @@ void browser_form_submit(struct browser_window *bw, struct browser_window *targe assert(0); } - form_free_successful(success); + fetch_multipart_data_destroy(success); free(data); free(url); } @@ -2864,11 +2843,11 @@ bool browser_window_nearest_text_box(struct box *box, int bx, int by, struct box *browser_window_pick_text_box(struct browser_window *bw, int x, int y, int dir, int *dx, int *dy) { - struct content *c = bw->current_content; + hlcache_handle *c = bw->current_content; struct box *text_box = NULL; - if (c && c->type == CONTENT_HTML) { - struct box *box = c->data.html.layout; + if (c && content_get_type(c) == CONTENT_HTML) { + struct box *box = html_get_box_tree(c); int nr_xd, nr_yd; int bx = box->margin[LEFT]; int by = box->margin[TOP]; @@ -3013,5 +2992,6 @@ bool browser_window_stop_available(struct browser_window *bw) { return (bw && (bw->loading_content || (bw->current_content && - (bw->current_content->status != CONTENT_STATUS_DONE)))); + (content_get_status(bw->current_content) != + CONTENT_STATUS_DONE)))); } diff --git a/desktop/browser.h b/desktop/browser.h index 52be9dedb..d72143968 100644 --- a/desktop/browser.h +++ b/desktop/browser.h @@ -31,10 +31,9 @@ #include "render/html.h" struct box; -struct content; +struct hlcache_handle; struct form; struct form_control; -struct form_successful_control; struct gui_window; struct history; struct selection; @@ -44,20 +43,20 @@ struct bitmap; struct scroll_msg_data; typedef bool (*browser_caret_callback)(struct browser_window *bw, - uint32_t key, void *p); + uint32_t key, void *p); typedef bool (*browser_paste_callback)(struct browser_window *bw, - const char *utf8, unsigned utf8_len, bool last, void *p); + const char *utf8, unsigned utf8_len, bool last, void *p); typedef void (*browser_move_callback)(struct browser_window *bw, - void *p); + void *p); /** Browser window data. */ struct browser_window { /** Page currently displayed, or 0. Must have status READY or DONE. */ - struct content *current_content; + struct hlcache_handle *current_content; /** Page being loaded, or 0. */ - struct content *loading_content; + struct hlcache_handle *loading_content; /** Window history structure. */ struct history *history; @@ -109,9 +108,6 @@ struct browser_window { /** Scroll capturing all mouse events */ struct scroll *scroll; - /** Referrer for current fetch, or 0. */ - char *referer; - /** Current fetch is download */ bool download; @@ -230,17 +226,17 @@ extern struct browser_window *current_redraw_browser; extern bool browser_reformat_pending; struct browser_window * browser_window_create(const char *url, - struct browser_window *clone, const char *referrer, - bool history_add, bool new_tab); + struct browser_window *clone, const char *referrer, + bool history_add, bool new_tab); void browser_window_initialise_common(struct browser_window *bw, - struct browser_window *clone); + struct browser_window *clone); void browser_window_go(struct browser_window *bw, const char *url, - const char *referrer, bool history_add); + const char *referrer, bool history_add); void browser_window_go_unverifiable(struct browser_window *bw, - const char *url, const char *referrer, bool history_add, - struct content *parent); + const char *url, const char *referrer, bool history_add, + struct hlcache_handle *parent); void browser_window_download(struct browser_window *bw, - const char *url, const char *referrer); + const char *url, const char *referrer); void browser_window_update(struct browser_window *bw, bool scroll_to_top); void browser_window_stop(struct browser_window *bw); void browser_window_reload(struct browser_window *bw, bool all); @@ -250,31 +246,32 @@ void browser_window_reformat(struct browser_window *bw, int width, int height); void browser_window_set_scale(struct browser_window *bw, float scale, bool all); void browser_window_refresh_url_bar(struct browser_window *bw, const char *url, - const char *frag); + const char *frag); void browser_window_mouse_click(struct browser_window *bw, - browser_mouse_state mouse, int x, int y); + browser_mouse_state mouse, int x, int y); void browser_window_mouse_track(struct browser_window *bw, - browser_mouse_state mouse, int x, int y); + browser_mouse_state mouse, int x, int y); void browser_window_mouse_drag_end(struct browser_window *bw, - browser_mouse_state mouse, int x, int y); + browser_mouse_state mouse, int x, int y); bool browser_window_key_press(struct browser_window *bw, uint32_t key); bool browser_window_paste_text(struct browser_window *bw, const char *utf8, - unsigned utf8_len, bool last); + unsigned utf8_len, bool last); void browser_window_form_select(struct browser_window *bw, - struct form_control *control, int item); -void browser_redraw_box(struct content *c, struct box *box); -void browser_form_submit(struct browser_window *bw, struct browser_window *target, - struct form *form, struct form_control *submit_button); + struct form_control *control, int item); +void browser_redraw_box(struct hlcache_handle *c, struct box *box); +void browser_form_submit(struct browser_window *bw, + struct browser_window *target, struct form *form, + struct form_control *submit_button); void browser_scroll_callback(void *client_data, - struct scroll_msg_data *scroll_data); + struct scroll_msg_data *scroll_data); void browser_select_menu_callback(void *client_data, - int x, int y, int width, int height); + int x, int y, int width, int height); void browser_window_redraw_rect(struct browser_window *bw, int x, int y, - int width, int height); + int width, int height); bool browser_window_back_available(struct browser_window *bw); bool browser_window_forward_available(struct browser_window *bw); @@ -282,7 +279,7 @@ bool browser_window_reload_available(struct browser_window *bw); bool browser_window_stop_available(struct browser_window *bw); /* In platform specific hotlist.c. */ -void hotlist_visited(struct content *content); +void hotlist_visited(struct hlcache_handle *content); /* In platform specific global_history.c. */ void global_history_add(const char *url); @@ -290,8 +287,8 @@ void global_history_add_recent(const char *url); char **global_history_get_recent(int *count); /* In platform specific thumbnail.c. */ -bool thumbnail_create(struct content *content, struct bitmap *bitmap, - const char *url); +bool thumbnail_create(struct hlcache_handle *content, struct bitmap *bitmap, + const char *url); /* In platform specific schedule.c. */ void schedule(int t, void (*callback)(void *p), void *p); @@ -300,7 +297,7 @@ bool schedule_run(void); /* In platform specific theme_install.c. */ #ifdef WITH_THEME_INSTALL -void theme_install_start(struct content *c); +void theme_install_start(struct hlcache_handle *c); #endif #endif diff --git a/desktop/frames.c b/desktop/frames.c index acabdc1b9..663ebcdbf 100644 --- a/desktop/frames.c +++ b/desktop/frames.c @@ -29,6 +29,7 @@ #include <time.h> #include <math.h> #include "utils/config.h" +#include "content/hlcache.h" #include "desktop/browser.h" #include "desktop/frames.h" #include "desktop/history_core.h" @@ -105,8 +106,8 @@ void browser_window_create_iframes(struct browser_window *bw, window = &(bw->iframes[index++]); if (cur->url) browser_window_go_unverifiable(window, cur->url, - bw->current_content->url, false, - bw->current_content); + content_get_url(bw->current_content), + false, bw->current_content); } } @@ -155,7 +156,7 @@ void browser_window_create_frameset(struct browser_window *bw, int row, col, index; struct content_html_frames *frame; struct browser_window *window; - struct content *parent; + hlcache_handle *parent; assert(bw && frameset); @@ -230,8 +231,9 @@ void browser_window_create_frameset(struct browser_window *bw, /* Use the URL of the first ancestor window containing html content * as the referer */ for (window = bw; window->parent; window = window->parent) { - if (window->current_content && - window->current_content->type == CONTENT_HTML) + if (window->current_content && + content_get_type(window->current_content) == + CONTENT_HTML) break; } @@ -247,8 +249,7 @@ void browser_window_create_frameset(struct browser_window *bw, if (frame->url) { browser_window_go_unverifiable(window, frame->url, - parent != NULL - ? parent->url : NULL, + content_get_url(parent), true, parent); } diff --git a/desktop/gui.h b/desktop/gui.h index f09e5f20f..162632a1a 100644 --- a/desktop/gui.h +++ b/desktop/gui.h @@ -41,6 +41,7 @@ typedef enum { GUI_SAVE_CLIPBOARD_CONTENTS } gui_save_type; +struct fetch; struct gui_window; struct gui_download_window; @@ -55,6 +56,7 @@ typedef enum { GUI_POINTER_DEFAULT, GUI_POINTER_POINT, GUI_POINTER_CARET, #include <stdbool.h> #include "utils/config.h" #include "content/content.h" +#include "content/hlcache.h" #include "desktop/browser.h" #include "desktop/search.h" @@ -89,8 +91,8 @@ void gui_window_hide_pointer(struct gui_window *g); void gui_window_set_url(struct gui_window *g, const char *url); void gui_window_start_throbber(struct gui_window *g); void gui_window_stop_throbber(struct gui_window *g); -void gui_window_set_icon(struct gui_window *g, struct content *icon); -void gui_window_set_search_ico(struct content *ico); +void gui_window_set_icon(struct gui_window *g, hlcache_handle *icon); +void gui_window_set_search_ico(hlcache_handle *ico); void gui_window_place_caret(struct gui_window *g, int x, int y, int height); void gui_window_remove_caret(struct gui_window *g); void gui_window_new_content(struct gui_window *g); @@ -98,7 +100,8 @@ bool gui_window_scroll_start(struct gui_window *g); bool gui_window_box_scroll_start(struct gui_window *g, int x0, int y0, int x1, int y1); bool gui_window_frame_resize_start(struct gui_window *g); -void gui_window_save_as_link(struct gui_window *g, struct content *c); +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, @@ -110,7 +113,7 @@ void gui_download_window_error(struct gui_download_window *dw, const char *error_msg); void gui_download_window_done(struct gui_download_window *dw); -void gui_drag_save_object(gui_save_type type, struct content *c, +void gui_drag_save_object(gui_save_type type, hlcache_handle *c, struct gui_window *g); void gui_drag_save_selection(struct selection *s, struct gui_window *g); void gui_start_selection(struct gui_window *g); @@ -133,7 +136,7 @@ bool gui_search_term_highlighted(struct gui_window *g, struct ssl_cert_info; -void gui_cert_verify(struct browser_window *bw, struct content *c, +void gui_cert_verify(struct browser_window *bw, hlcache_handle *c, const struct ssl_cert_info *certs, unsigned long num); #endif diff --git a/desktop/history_core.c b/desktop/history_core.c index 197a42327..fc807e928 100644 --- a/desktop/history_core.c +++ b/desktop/history_core.c @@ -27,6 +27,7 @@ #include <string.h> #include <time.h> #include "content/content.h" +#include "content/hlcache.h" #include "content/urldb.h" #include "css/css.h" #include "desktop/gui.h" @@ -220,7 +221,7 @@ struct history_entry *history_clone_entry(struct history *history, * The page is added after the current entry and becomes current. */ -void history_add(struct history *history, struct content *content, +void history_add(struct history *history, hlcache_handle *content, char *frag_id) { url_func_result res; @@ -237,14 +238,14 @@ void history_add(struct history *history, struct content *content, if (entry == NULL) return; - res = url_normalize(content->url, &url); + res = url_normalize(content_get_url(content), &url); if (res != URL_FUNC_OK) { warn_user("NoMemory", 0); free(entry); return; } - title = strdup(content->title ? content->title : url); + title = strdup(content_get_title(content)); if (title == NULL) { warn_user("NoMemory", 0); free(url); @@ -303,11 +304,9 @@ void history_add(struct history *history, struct content *content, * \param content content for current entry */ -void history_update(struct history *history, struct content *content) +void history_update(struct history *history, hlcache_handle *content) { char *title; - char *url; - url_func_result res; if (!history || !history->current || !history->current->bitmap) return; @@ -315,19 +314,10 @@ void history_update(struct history *history, struct content *content) assert(history->current->page.url); assert(history->current->page.title); - if (content->title) { - title = strdup(content->title); - if (!title) { - warn_user("NoMemory", 0); - return; - } - } else { - res = url_normalize(content->url, &url); - if (res != URL_FUNC_OK) { - warn_user("NoMemory", 0); - return; - } - title = url; + title = strdup(content_get_title(content)); + if (!title) { + warn_user("NoMemory", 0); + return; } assert(title); diff --git a/desktop/history_core.h b/desktop/history_core.h index 46de18848..55b4e0bf1 100644 --- a/desktop/history_core.h +++ b/desktop/history_core.h @@ -25,15 +25,15 @@ #include <stdbool.h> -struct content; +struct hlcache_handle; struct history; struct browser_window; struct history *history_create(void); struct history *history_clone(struct history *history); -void history_add(struct history *history, struct content *content, +void history_add(struct history *history, struct hlcache_handle *content, char *frag_id); -void history_update(struct history *history, struct content *content); +void history_update(struct history *history, struct hlcache_handle *content); void history_destroy(struct history *history); void history_back(struct browser_window *bw, struct history *history); void history_forward(struct browser_window *bw, struct history *history); diff --git a/desktop/netsurf.c b/desktop/netsurf.c index 9acddaf87..192ddc185 100644 --- a/desktop/netsurf.c +++ b/desktop/netsurf.c @@ -28,10 +28,13 @@ #include <libxml/globals.h> #include <libxml/xmlversion.h> +#include <libwapcaplet/libwapcaplet.h> + #include "utils/config.h" #include "utils/utsname.h" #include "content/fetch.h" #include "content/fetchcache.h" +#include "content/llcache.h" #include "content/urldb.h" #include "desktop/netsurf.h" #include "desktop/browser.h" @@ -44,39 +47,11 @@ bool netsurf_quit = false; bool verbose_log = false; -static void netsurf_poll(void); -static void lib_init(void); - - -/** - * Gui NetSurf main(). - */ - -int netsurf_main(int argc, char** argv) -{ - netsurf_init(argc, argv); - - netsurf_main_loop(); - - netsurf_exit(); - - return EXIT_SUCCESS; -} - - -/** - * Gui NetSurf main loop. - */ - -int netsurf_main_loop(void) +static void *netsurf_lwc_alloc(void *ptr, size_t len, void *pw) { - while (!netsurf_quit) - netsurf_poll(); - - return 0; + return realloc(ptr, len); } - /** * Initialise components used by gui NetSurf. */ @@ -126,35 +101,30 @@ void netsurf_init(int argc, char** argv) utsname.nodename, utsname.release, utsname.version, utsname.machine)); - lib_init(); + lwc_initialise(netsurf_lwc_alloc, NULL, 0); url_init(); gui_init(argc, argv); setlocale(LC_ALL, "C"); fetch_init(); - fetchcache_init(); + /** \todo The frontend needs to provide the llcache_query_handler */ + llcache_initialise(NULL, NULL); gui_init2(argc, argv); } + /** - * Poll components which require it. + * Gui NetSurf main loop. */ - -void netsurf_poll(void) +int netsurf_main_loop(void) { - static unsigned int last_clean = 0; - unsigned int current_time = wallclock(); - - /* avoid calling content_clean() more often than once every 5 - * seconds. - */ - if (last_clean + 500 < current_time) { - last_clean = current_time; - content_clean(); + while (!netsurf_quit) { + gui_poll(fetch_active); + fetch_poll(); + llcache_poll(); } - gui_poll(fetch_active); - fetch_poll(); -} + return 0; +} /** * Clean up components used by gui NetSurf. @@ -164,8 +134,6 @@ void netsurf_exit(void) { LOG(("Closing GUI")); gui_quit(); - LOG(("Closing content")); - content_quit(); LOG(("Closing fetches")); fetch_quit(); LOG(("Closing utf8")); @@ -176,18 +144,3 @@ void netsurf_exit(void) } -/** - * Initialises the libraries used in NetSurf. - */ -void lib_init(void) -{ - LOG(("xmlParserVersion %s, LIBXML_VERSION_STRING %s", - xmlParserVersion, LIBXML_VERSION_STRING)); - - /* Using encoding "X-SJIS" (unknown to libxmp2/iconv) instead as - * "Shift-JIS" is rather popular. - */ - if (xmlAddEncodingAlias(xmlGetCharEncodingName( - XML_CHAR_ENCODING_SHIFT_JIS), "X-SJIS") != 0) - die("Failed to add encoding alias"); -} diff --git a/desktop/netsurf.h b/desktop/netsurf.h index 05b870333..33733fa40 100644 --- a/desktop/netsurf.h +++ b/desktop/netsurf.h @@ -29,7 +29,6 @@ extern const int netsurf_version_minor; extern void netsurf_init(int argc, char** argv); extern void netsurf_exit(void); -extern int netsurf_main(int argc, char** argv); extern int netsurf_main_loop(void); #endif diff --git a/desktop/print.c b/desktop/print.c index 4f86bbc22..a5b5c125a 100644 --- a/desktop/print.c +++ b/desktop/print.c @@ -26,6 +26,7 @@ #include <string.h> #include "content/content.h" +#include "content/hlcache.h" #include "css/utils.h" #include "desktop/options.h" #include "desktop/print.h" @@ -39,11 +40,11 @@ #define DEFAULT_PAGE_HEIGHT 840 #define DEFAULT_COPIES 1 -static struct content *print_init(struct content *, struct print_settings *); -static bool print_apply_settings(struct content *, struct print_settings *); +static hlcache_handle *print_init(hlcache_handle *, struct print_settings *); +static bool print_apply_settings(hlcache_handle *, struct print_settings *); static float page_content_width, page_content_height; -static struct content *printed_content; +static hlcache_handle *printed_content; static float done_height; bool html_redraw_printing = false; @@ -59,7 +60,7 @@ int html_redraw_printing_top_cropped = 0; * \param settings The settings for printing to use * \return true if successful, false otherwise */ -bool print_basic_run(struct content *content, +bool print_basic_run(hlcache_handle *content, const struct printer *printer, struct print_settings *settings) { @@ -69,8 +70,8 @@ bool print_basic_run(struct content *content, if (!print_set_up(content, printer, settings, NULL)) ret = false; - - while (ret && (done_height < printed_content->height) ) + + while (ret && (done_height < content_get_height(printed_content)) ) ret = print_draw_next_page(printer, settings); print_cleanup(content, printer, settings); @@ -88,7 +89,7 @@ bool print_basic_run(struct content *content, * \param height updated to the height of the printed content * \return true if successful, false otherwise */ -bool print_set_up(struct content *content, +bool print_set_up(hlcache_handle *content, const struct printer *printer, struct print_settings *settings, double *height) { @@ -100,7 +101,7 @@ bool print_set_up(struct content *content, print_apply_settings(printed_content, settings); if (height) - *height = printed_content->height; + *height = content_get_height(printed_content); printer->print_begin(settings); @@ -158,11 +159,13 @@ bool print_draw_next_page(const struct printer *printer, * \param settings The settings for printing to use * \return true if successful, false otherwise */ -struct content *print_init(struct content *content, +hlcache_handle *print_init(hlcache_handle *content, struct print_settings *settings) { - struct content* printed_content; - struct content_user *user_sentinel; +// newcache +#if 0 + hlcache_handle* printed_content; + hlcache_handle_user *user_sentinel; content_add_user(content, NULL, (intptr_t) print_init, 0); @@ -173,7 +176,7 @@ struct content *print_init(struct content *content, printed_content->data.html.bw = 0; - user_sentinel = talloc(printed_content, struct content_user); + user_sentinel = talloc(printed_content, hlcache_handle_user); user_sentinel->callback = 0; user_sentinel->p1 = user_sentinel->p2 = 0; user_sentinel->next = 0; @@ -194,6 +197,9 @@ struct content *print_init(struct content *content, printed_content->data.html.font_func = settings->font_func; return printed_content; +#else + return NULL; +#endif } /** @@ -203,7 +209,7 @@ struct content *print_init(struct content *content, * \param settings The settings for printing to use * \return true if successful, false otherwise */ -bool print_apply_settings(struct content *content, +bool print_apply_settings(hlcache_handle *content, struct print_settings *settings) { if (settings == NULL) @@ -222,7 +228,8 @@ bool print_apply_settings(struct content *content, content_reformat(content, page_content_width, 0); LOG(("New layout applied.New height = %d ; New width = %d ", - content->height, content->width)); + content_get_height(content), + content_get_width(content))); return true; } @@ -234,7 +241,7 @@ bool print_apply_settings(struct content *content, * \param printer The printer interface for the printer to be used * \return true if successful, false otherwise */ -bool print_cleanup(struct content *content, const struct printer *printer, +bool print_cleanup(hlcache_handle *content, const struct printer *printer, struct print_settings *settings) { printer->print_end(); @@ -242,12 +249,11 @@ bool print_cleanup(struct content *content, const struct printer *printer, html_redraw_printing = false; if (printed_content) { - content_remove_user(printed_content, NULL, - (intptr_t) print_init, 0); + content_remove_user(printed_content, NULL, print_init); talloc_free(printed_content); } - content_remove_user(content, NULL, (intptr_t)print_init, 0); + content_remove_user(content, NULL, print_init); free((void *)settings->output); free(settings); diff --git a/desktop/print.h b/desktop/print.h index fece526be..63c2935f1 100644 --- a/desktop/print.h +++ b/desktop/print.h @@ -36,7 +36,7 @@ #include "css/css.h" -struct content; +struct hlcache_handle; struct printer; enum { MARGINLEFT = 0, MARGINRIGHT = 1, MARGINTOP = 2, MARGINBOTTOM = 3}; @@ -64,13 +64,13 @@ struct print_settings{ }; -bool print_basic_run(struct content *, const struct printer *, +bool print_basic_run(struct hlcache_handle *, const struct printer *, struct print_settings *); -bool print_set_up(struct content *content, const struct printer *printer, +bool print_set_up(struct hlcache_handle *content, const struct printer *printer, struct print_settings *settings, double *height); bool print_draw_next_page(const struct printer *printer, struct print_settings *settings); -bool print_cleanup(struct content *, const struct printer *, +bool print_cleanup(struct hlcache_handle *, const struct printer *, struct print_settings *settings); struct print_settings *print_make_settings(print_configuration configuration, diff --git a/desktop/save_complete.c b/desktop/save_complete.c index d55cde08f..78b2d7646 100644 --- a/desktop/save_complete.c +++ b/desktop/save_complete.c @@ -35,6 +35,7 @@ #include <libxml/parserInternals.h> #include "utils/config.h" #include "content/content.h" +#include "content/hlcache.h" #include "css/css.h" #include "render/box.h" #include "desktop/save_complete.h" @@ -46,14 +47,14 @@ regex_t save_complete_import_re; /** An entry in save_complete_list. */ struct save_complete_entry { - struct content *content; + hlcache_handle *content; struct save_complete_entry *next; /**< Next entry in list */ }; -static bool save_complete_html(struct content *c, const char *path, +static bool save_complete_html(hlcache_handle *c, const char *path, bool index, struct save_complete_entry **list); -static bool save_imported_sheets(struct content *c, const char *path, - struct save_complete_entry **list); +static bool save_imported_sheets(struct nscss_import *imports, uint32_t count, + const char *path, struct save_complete_entry **list); static char * rewrite_stylesheet_urls(const char *source, unsigned int size, int *osize, const char *base, struct save_complete_entry *list); @@ -63,11 +64,11 @@ static bool rewrite_urls(xmlNode *n, const char *base, struct save_complete_entry *list); static bool rewrite_url(xmlNode *n, const char *attr, const char *base, struct save_complete_entry *list); -static bool save_complete_list_add(struct content *content, +static bool save_complete_list_add(hlcache_handle *content, struct save_complete_entry **list); -static struct content * save_complete_list_find(const char *url, +static hlcache_handle * save_complete_list_find(const char *url, struct save_complete_entry *list); -static bool save_complete_list_check(struct content *content, +static bool save_complete_list_check(hlcache_handle *content, struct save_complete_entry *list); /* static void save_complete_list_dump(void); */ static bool save_complete_inventory(const char *path, @@ -81,7 +82,7 @@ static bool save_complete_inventory(const char *path, * \return true on success, false on error and error reported */ -bool save_complete(struct content *c, const char *path) +bool save_complete(hlcache_handle *c, const char *path) { bool result; struct save_complete_entry *list = NULL; @@ -111,50 +112,69 @@ bool save_complete(struct content *c, const char *path) * \return true on success, false on error and error reported */ -bool save_complete_html(struct content *c, const char *path, bool index, +bool save_complete_html(hlcache_handle *c, const char *path, bool index, struct save_complete_entry **list) { + struct html_stylesheet *sheets; + struct content_html_object *objects; + const char *base_url; char filename[256]; - unsigned int i; + unsigned int i, count; xmlDocPtr doc; bool res; - if (c->type != CONTENT_HTML) + if (content_get_type(c) != CONTENT_HTML) return false; if (save_complete_list_check(c, *list)) return true; - + + base_url = html_get_base_url(c); + /* save stylesheets, ignoring the base and adblocking sheets */ - for (i = STYLESHEET_START; i != c->data.html.stylesheet_count; i++) { - struct content *css = c->data.html.stylesheets[i].c; + sheets = html_get_stylesheets(c, &count); + + for (i = STYLESHEET_START; i != count; i++) { + hlcache_handle *css; + const char *css_data; + unsigned long css_size; char *source; int source_len; - bool is_style; + struct nscss_import *imports; + uint32_t import_count; + + if (sheets[i].type == HTML_STYLESHEET_INTERNAL) { + if (save_imported_sheets( + sheets[i].data.internal->imports, + sheets[i].data.internal->import_count, + path, list) == false) + return false; + + continue; + } + + css = sheets[i].data.external; if (!css) continue; if (save_complete_list_check(css, *list)) continue; - is_style = (strcmp(css->url, c->data.html.base_url) == 0); - - if (is_style == false) { - if (!save_complete_list_add(css, list)) { - warn_user("NoMemory", 0); - return false; - } + if (!save_complete_list_add(css, list)) { + warn_user("NoMemory", 0); + return false; } - if (!save_imported_sheets(css, path, list)) + imports = nscss_get_imports(css, &import_count); + if (!save_imported_sheets(imports, import_count, path, list)) return false; - if (is_style) - continue; /* don't save <style> elements */ - snprintf(filename, sizeof filename, "%p", css); - source = rewrite_stylesheet_urls(css->source_data, - css->source_size, &source_len, css->url, + + css_data = content_get_source_data(css, &css_size); + + source = rewrite_stylesheet_urls(css_data, css_size, + &source_len, content_get_url(css), *list); if (!source) { warn_user("NoMemory", 0); @@ -168,12 +188,21 @@ bool save_complete_html(struct content *c, const char *path, bool index, } /* save objects */ - for (i = 0; i != c->data.html.object_count; i++) { - struct content *obj = c->data.html.object[i].content; + objects = html_get_objects(c, &count); + + for (i = 0; i != count; i++) { + hlcache_handle *obj = objects[i].content; + const char *obj_data; + unsigned long obj_size; - /* skip difficult content types */ - if (!obj || obj->type >= CONTENT_OTHER || !obj->source_data) + if (obj == NULL || content_get_type(obj) >= CONTENT_OTHER) continue; + + obj_data = content_get_source_data(obj, &obj_size); + + if (obj_data == NULL) + continue; + if (save_complete_list_check(obj, *list)) continue; @@ -182,7 +211,7 @@ bool save_complete_html(struct content *c, const char *path, bool index, return false; } - if (obj->type == CONTENT_HTML) { + if (content_get_type(obj) == CONTENT_HTML) { if (!save_complete_html(obj, path, false, list)) return false; continue; @@ -190,7 +219,7 @@ bool save_complete_html(struct content *c, const char *path, bool index, snprintf(filename, sizeof filename, "%p", obj); res = save_complete_gui_save(path, filename, - obj->source_size, obj->source_data, obj->type); + obj_size, obj_data, content_get_type(obj)); if(res == false) return false; } @@ -198,14 +227,14 @@ bool save_complete_html(struct content *c, const char *path, bool index, /*save_complete_list_dump();*/ /* copy document */ - doc = xmlCopyDoc(c->data.html.document, 1); + doc = xmlCopyDoc(html_get_document(c), 1); if (doc == NULL) { warn_user("NoMemory", 0); return false; } /* rewrite all urls we know about */ - if (!rewrite_document_urls(doc, c->data.html.base_url, *list)) { + if (!rewrite_document_urls(doc, html_get_base_url(c), *list)) { xmlFreeDoc(doc); warn_user("NoMemory", 0); return false; @@ -237,13 +266,13 @@ bool save_complete_html(struct content *c, const char *path, bool index, /** * Save stylesheets imported by a CONTENT_CSS. * - * \param c a CONTENT_CSS - * \param path path to save to + * \param imports Array of imports + * \param count Number of imports in list + * \param path Path to save to * \return true on success, false on error and error reported */ - -bool save_imported_sheets(struct content *c, const char *path, - struct save_complete_entry **list) +bool save_imported_sheets(struct nscss_import *imports, uint32_t count, + const char *path, struct save_complete_entry **list) { char filename[256]; unsigned int j; @@ -251,10 +280,14 @@ bool save_imported_sheets(struct content *c, const char *path, int source_len; bool res; - for (j = 0; j != c->data.css.import_count; j++) { - struct content *css = c->data.css.imports[j].c; + for (j = 0; j != count; j++) { + hlcache_handle *css = imports[j].c; + const char *css_data; + unsigned long css_size; + struct nscss_import *child_imports; + uint32_t child_import_count; - if (!css) + if (css == NULL) continue; if (save_complete_list_check(css, *list)) continue; @@ -264,12 +297,17 @@ bool save_imported_sheets(struct content *c, const char *path, return false; } - if (!save_imported_sheets(css, path, list)) + child_imports = nscss_get_imports(css, &child_import_count); + if (!save_imported_sheets(child_imports, child_import_count, + path, list)) return false; snprintf(filename, sizeof filename, "%p", css); - source = rewrite_stylesheet_urls(css->source_data, - css->source_size, &source_len, css->url, + + css_data = content_get_source_data(css, &css_size); + + source = rewrite_stylesheet_urls(css_data, css_size, + &source_len, content_get_url(css), *list); if (!source) { warn_user("NoMemory", 0); @@ -344,7 +382,7 @@ char * rewrite_stylesheet_urls(const char *source, unsigned int size, char buf[20]; unsigned int offset = 0; int url_len = 0; - struct content *content; + hlcache_handle *content; int m; unsigned int i; unsigned int imports = 0; @@ -609,7 +647,7 @@ bool rewrite_url(xmlNode *n, const char *attr, const char *base, { char *url, *data; char rel[20]; - struct content *content; + hlcache_handle *content; url_func_result res; if (!xmlHasProp(n, (const xmlChar *) attr)) @@ -654,7 +692,7 @@ bool rewrite_url(xmlNode *n, const char *attr, const char *base, * \return true on success, false on out of memory */ -bool save_complete_list_add(struct content *content, +bool save_complete_list_add(hlcache_handle *content, struct save_complete_entry **list) { struct save_complete_entry *entry; @@ -675,12 +713,12 @@ bool save_complete_list_add(struct content *content, * \return content if found, 0 otherwise */ -struct content * save_complete_list_find(const char *url, +hlcache_handle * save_complete_list_find(const char *url, struct save_complete_entry *list) { struct save_complete_entry *entry; for (entry = list; entry; entry = entry->next) - if (strcmp(url, entry->content->url) == 0) + if (strcmp(url, content_get_url(entry->content)) == 0) return entry->content; return 0; } @@ -693,7 +731,7 @@ struct content * save_complete_list_find(const char *url, * \return true if the content is in the save_complete_list */ -bool save_complete_list_check(struct content *content, +bool save_complete_list_check(hlcache_handle *content, struct save_complete_entry *list) { struct save_complete_entry *entry; @@ -746,8 +784,10 @@ bool save_complete_inventory(const char *path, return false; } - for (entry = list; entry; entry = entry->next) - fprintf(fp, "%p %s\n", entry->content, entry->content->url); + for (entry = list; entry; entry = entry->next) { + fprintf(fp, "%p %s\n", entry->content, + content_get_url(entry->content)); + } fclose(fp); diff --git a/desktop/save_complete.h b/desktop/save_complete.h index e23092471..ad31fa486 100644 --- a/desktop/save_complete.h +++ b/desktop/save_complete.h @@ -28,10 +28,10 @@ #include <libxml/HTMLtree.h> #include "content/content.h" -struct content; +struct hlcache_handle; void save_complete_init(void); -bool save_complete(struct content *c, const char *path); +bool save_complete(struct hlcache_handle *c, const char *path); bool save_complete_gui_save(const char *path, const char *filename, size_t len, const char *sourcedata, content_type type); diff --git a/desktop/save_pdf/pdf_plotters.c b/desktop/save_pdf/pdf_plotters.c index 8dbe43971..f33070edb 100644 --- a/desktop/save_pdf/pdf_plotters.c +++ b/desktop/save_pdf/pdf_plotters.c @@ -30,6 +30,7 @@ #include <hpdf.h> +#include "content/hlcache.h" #include "desktop/options.h" #include "desktop/plotters.h" #include "desktop/print.h" @@ -410,32 +411,37 @@ bool pdf_plot_bitmap_tile(int x, int y, int width, int height, HPDF_Image pdf_extract_image(struct bitmap *bitmap) { HPDF_Image image = NULL; - struct content *content = NULL; + hlcache_handle *content = NULL; /* TODO - get content from bitmap pointer */ if (content) { + const char *source_data; + unsigned long source_size; + /*Not sure if I don't have to check if downloading has been finished. Other way - lock pdf plotting while fetching a website */ - switch(content->type){ - /*Handle "embeddable" types of images*/ - case CONTENT_JPEG: - image = HPDF_LoadJpegImageFromMem(pdf_doc, - (const HPDF_BYTE *)content->source_data, - content->source_size); - break; - - /*Disabled until HARU PNG support will be more stable. - - case CONTENT_PNG: - image = HPDF_LoadPngImageFromMem(pdf_doc, - (const HPDF_BYTE *)content->source_data, - content->total_size); - break;*/ - default: - break; + source_data = content_get_source_data(content, &source_size); + + switch(content_get_type(content)){ + /*Handle "embeddable" types of images*/ + case CONTENT_JPEG: + image = HPDF_LoadJpegImageFromMem(pdf_doc, + (const HPDF_BYTE *) source_data, + source_size); + break; + + /*Disabled until HARU PNG support will be more stable. + + case CONTENT_PNG: + image = HPDF_LoadPngImageFromMem(pdf_doc, + (const HPDF_BYTE *)content->source_data, + content->total_size); + break;*/ + default: + break; } } diff --git a/desktop/save_text.c b/desktop/save_text.c index e34b9ceb0..5e5cef761 100644 --- a/desktop/save_text.c +++ b/desktop/save_text.c @@ -27,6 +27,7 @@ #include "utils/config.h" #include "content/content.h" +#include "content/hlcache.h" #include "desktop/save_text.h" #include "render/box.h" #include "utils/log.h" @@ -48,7 +49,7 @@ static bool save_text_add_to_buffer(const char *text, size_t length, * \param path Path to save text file too. */ -void save_as_text(struct content *c, char *path) +void save_as_text(hlcache_handle *c, char *path) { FILE *out; struct save_text_state save = { NULL, 0, 0 }; @@ -57,11 +58,11 @@ void save_as_text(struct content *c, char *path) utf8_convert_ret ret; char *result; - if (!c || c->type != CONTENT_HTML) { + if (!c || content_get_type(c) != CONTENT_HTML) { return; } - extract_text(c->data.html.layout, &first, &before, &save); + extract_text(html_get_box_tree(c), &first, &before, &save); if (!save.block) return; diff --git a/desktop/save_text.h b/desktop/save_text.h index 825d08f63..06446cd58 100644 --- a/desktop/save_text.h +++ b/desktop/save_text.h @@ -25,7 +25,7 @@ #define _NETSURF_DESKTOP_SAVE_TEXT_H_ struct box; -struct content; +struct hlcache_handle; /* text currently being saved */ struct save_text_state { @@ -41,7 +41,7 @@ typedef enum { WHITESPACE_TWO_NEW_LINES } save_text_whitespace; -void save_as_text(struct content *c, char *path); +void save_as_text(struct hlcache_handle *c, char *path); void save_text_solve_whitespace(struct box *box, bool *first, save_text_whitespace *before, const char **whitespace_text, size_t *whitespace_length); diff --git a/desktop/search.c b/desktop/search.c index ba472fd41..f84f1f06e 100644 --- a/desktop/search.c +++ b/desktop/search.c @@ -26,6 +26,7 @@ #include <ctype.h> #include <string.h> #include "content/content.h" +#include "content/hlcache.h" #include "desktop/browser.h" #include "desktop/gui.h" #include "desktop/options.h" @@ -33,6 +34,7 @@ #include "desktop/selection.h" #include "render/box.h" #include "render/html.h" +#include "render/textplain.h" #include "utils/config.h" #include "utils/log.h" #include "utils/messages.h" @@ -60,7 +62,7 @@ struct list_entry { struct search_context { struct browser_window *bw; - struct content *content; + hlcache_handle *content; char *string; bool prev_case_sens; bool newsearch; @@ -80,7 +82,7 @@ static bool find_occurrences_html(const char *pattern, int p_len, struct box *cur, bool case_sens, struct search_context *context); static bool find_occurrences_text(const char *pattern, int p_len, - struct content *c, bool case_sens, + hlcache_handle *c, bool case_sens, struct search_context *context); static struct list_entry *add_entry(unsigned start_idx, unsigned end_idx, struct search_context *context); @@ -236,7 +238,7 @@ void search_text(const char *string, int string_len, struct search_context *context, search_flags_t flags) { struct rect bounds; - struct content *c; + hlcache_handle *c; struct box *box; bool case_sensitive, forwards, showall; @@ -250,11 +252,11 @@ void search_text(const char *string, int string_len, c = context->bw->current_content; /* only handle html contents */ - if ((!c) || (c->type != CONTENT_HTML && - c->type != CONTENT_TEXTPLAIN)) + if ((!c) || (content_get_type(c) != CONTENT_HTML && + content_get_type(c) != CONTENT_TEXTPLAIN)) return; - box = c->data.html.layout; + box = html_get_box_tree(c); if (!box) return; @@ -282,11 +284,11 @@ void search_text(const char *string, int string_len, (context->callbacks->hourglass != NULL)) context->callbacks->hourglass(true, context->p); - if (c->type == CONTENT_HTML) + if (content_get_type(c) == CONTENT_HTML) res = find_occurrences_html(string, string_len, box, case_sensitive, context); else { - assert(c->type == CONTENT_TEXTPLAIN); + assert(content_get_type(c) == CONTENT_TEXTPLAIN); res = find_occurrences_text(string, string_len, c, case_sensitive, context); } @@ -342,7 +344,7 @@ void search_text(const char *string, int string_len, if (context->current == NULL) return; - switch (c->type) { + switch (content_get_type(c)) { case CONTENT_HTML: /* get box position and jump to it */ box_coords(context->current->start_box, @@ -356,7 +358,7 @@ void search_text(const char *string, int string_len, break; default: - assert(c->type == CONTENT_TEXTPLAIN); + assert(content_get_type(c) == CONTENT_TEXTPLAIN); textplain_coords_from_range(c, context->current->start_idx, context->current->end_idx, &bounds); @@ -551,7 +553,7 @@ bool find_occurrences_html(const char *pattern, int p_len, struct box *cur, */ bool find_occurrences_text(const char *pattern, int p_len, - struct content *c, bool case_sens, + hlcache_handle *c, bool case_sens, struct search_context *context) { int nlines = textplain_line_count(c); @@ -642,15 +644,15 @@ void search_show_all(bool all, struct search_context *context) if (add && !a->sel) { a->sel = selection_create(context->bw); if (a->sel) { - struct content *c = context->bw-> + hlcache_handle *c = context->bw-> current_content; - switch (c->type) { + switch (content_get_type(c)) { case CONTENT_HTML: selection_init(a->sel, - c->data.html.layout); + html_get_box_tree(c)); break; default: - assert(c->type == + assert(content_get_type(c) == CONTENT_TEXTPLAIN); selection_init(a->sel, NULL); break; diff --git a/desktop/searchweb.c b/desktop/searchweb.c index bc0f71ad4..724edc9dd 100644 --- a/desktop/searchweb.c +++ b/desktop/searchweb.c @@ -24,8 +24,7 @@ #include <ctype.h> #include <string.h> #include "content/content.h" -#include "content/fetchcache.h" -#include "content/fetch.h" +#include "content/hlcache.h" #include "desktop/browser.h" #include "desktop/gui.h" #include "desktop/options.h" @@ -43,10 +42,13 @@ static struct search_provider { char *ico; /** < location of domain's favicon */ } current_search_provider; -static struct content *search_ico = NULL; +static hlcache_handle *search_ico = NULL; char *search_engines_file_location; char *search_default_ico_location; +static nserror search_web_ico_callback(hlcache_handle *ico, + const hlcache_event *event, void *pw); + /** * creates a new browser window according to the search term * \param searchterm such as "my search term" @@ -205,7 +207,8 @@ char *search_web_get_url(const char *encsearchterm) void search_web_retrieve_ico(bool localdefault) { char *url; - struct content *icocontent; + nserror error; + if (localdefault) { if (search_default_ico_location == NULL) return; @@ -221,26 +224,17 @@ void search_web_retrieve_ico(bool localdefault) url = search_web_ico_name(); } - icocontent = NULL; if (url == NULL) { warn_user(messages_get("NoMemory"), 0); return; } - icocontent = fetchcache(url, search_web_ico_callback, - 0, 0, 20, 20, true, 0, - 0, false, false); - free(url); - if (icocontent == NULL) - return; - - fetchcache_go(icocontent, 0, search_web_ico_callback, - 0, 0, 20, 20, - 0, 0, false, 0); - if (icocontent == NULL) - LOG(("web search ico loading delayed")); - else - search_ico = icocontent; + error = hlcache_handle_retrieve(url, 0, NULL, NULL, 20, 20, + search_web_ico_callback, NULL, NULL, &search_ico); + if (error != NSERROR_OK) + search_ico = NULL; + + free(url); } /** @@ -249,7 +243,7 @@ void search_web_retrieve_ico(bool localdefault) * responsibility */ -struct content *search_web_ico(void) +hlcache_handle *search_web_ico(void) { return search_ico; } @@ -259,20 +253,18 @@ struct content *search_web_ico(void) * else retry default from local file system */ -void search_web_ico_callback(content_msg msg, struct content *ico, - intptr_t p1, intptr_t p2, union content_msg_data data) +nserror search_web_ico_callback(hlcache_handle *ico, + const hlcache_event *event, void *pw) { - - switch (msg) { + switch (event->type) { case CONTENT_MSG_LOADING: case CONTENT_MSG_READY: break; case CONTENT_MSG_DONE: - LOG(("got favicon '%s'", ico->url)); + LOG(("got favicon '%s'", content_get_url(ico))); #ifdef WITH_BMP - if (ico->type == CONTENT_ICO) { - search_ico = ico; /* cache */ + if (content_get_type(ico) == CONTENT_ICO) { gui_window_set_search_ico(search_ico); } else #endif @@ -281,20 +273,20 @@ void search_web_ico_callback(content_msg msg, struct content *ico, } break; - case CONTENT_MSG_LAUNCH: case CONTENT_MSG_ERROR: - LOG(("favicon %s error: %s", ico->url, data.error)); - ico = 0; + LOG(("favicon %s error: %s", + content_get_url(ico), event->data.error)); + hlcache_handle_release(search_ico); + search_ico = NULL; search_web_retrieve_ico(true); break; case CONTENT_MSG_STATUS: - case CONTENT_MSG_NEWPTR: - case CONTENT_MSG_AUTH: - case CONTENT_MSG_SSL: break; default: assert(0); } + + return NSERROR_OK; } diff --git a/desktop/searchweb.h b/desktop/searchweb.h index f8dcb9db0..6cc23036a 100644 --- a/desktop/searchweb.h +++ b/desktop/searchweb.h @@ -20,9 +20,11 @@ #define _NETSURF_DESKTOP_SEARCH_WEB_H_ #include <ctype.h> +#include <stdbool.h> #include <string.h> -#include "content/content.h" -#include "desktop/browser.h" + +struct browser_window; +struct hlcache_handle; extern char *search_engines_file_location; extern char *search_default_ico_location; @@ -71,9 +73,6 @@ bool search_is_url(const char *url); void search_web_retrieve_ico(bool localdefault); -struct content *search_web_ico(void); - -void search_web_ico_callback(content_msg msg, struct content *ico, - intptr_t p1, intptr_t p2, union content_msg_data data); +struct hlcache_handle *search_web_ico(void); #endif diff --git a/desktop/selection.c b/desktop/selection.c index 93f5cc1a3..821dbb9bc 100644 --- a/desktop/selection.c +++ b/desktop/selection.c @@ -27,6 +27,7 @@ #include <stdbool.h> #include <string.h> +#include "content/hlcache.h" #include "desktop/gui.h" #include "desktop/plotters.h" #include "desktop/save_text.h" @@ -155,8 +156,8 @@ void selection_reinit(struct selection *s, struct box *root) s->max_idx = selection_label_subtree(root, root_idx); } else { - struct content *c = s->bw->current_content; - if (c && c->type == CONTENT_TEXTPLAIN) + hlcache_handle *c = s->bw->current_content; + if (c && content_get_type(c) == CONTENT_TEXTPLAIN) s->max_idx = textplain_size(c); else s->max_idx = 0; @@ -560,7 +561,7 @@ bool traverse_tree(struct box *box, unsigned start_idx, unsigned end_idx, bool selection_traverse(struct selection *s, seln_traverse_handler handler, void *handle) { - struct content *c; + hlcache_handle *c; save_text_whitespace before = WHITESPACE_NONE; bool first = true; const char *text; @@ -664,8 +665,9 @@ void selection_redraw(struct selection *s, unsigned start_idx, unsigned end_idx) return; } else { - struct content *c = s->bw->current_content; - if (c && c->type == CONTENT_TEXTPLAIN && end_idx > start_idx) { + hlcache_handle *c = s->bw->current_content; + if (c && content_get_type(c) == CONTENT_TEXTPLAIN && + end_idx > start_idx) { textplain_coords_from_range(c, start_idx, end_idx, &rdw.r); rdw.inited = true; @@ -952,7 +954,7 @@ bool save_handler(const char *text, size_t length, struct box *box, bool selection_save_text(struct selection *s, const char *path) { - struct content *c = s->bw->current_content; + hlcache_handle *c = s->bw->current_content; struct save_text_state sv = { NULL, 0, 0 }; utf8_convert_ret ret; char *result; diff --git a/desktop/textinput.c b/desktop/textinput.c index 5083d8c78..7739140fb 100644 --- a/desktop/textinput.c +++ b/desktop/textinput.c @@ -2091,11 +2091,14 @@ bool textarea_cut(struct browser_window *bw, void textarea_reflow(struct browser_window *bw, struct box *textarea, struct box *inline_container) { + struct content *c = hlcache_handle_get_content(bw->current_content); int width = textarea->width; int height = textarea->height; + + assert(c != NULL); + if (!layout_inline_container(inline_container, width, - textarea, 0, 0, - bw->current_content)) + textarea, 0, 0, c)) warn_user("NoMemory", 0); textarea->width = width; textarea->height = height; diff --git a/framebuffer/gui.c b/framebuffer/gui.c index 05c363c0b..d0f0490fb 100644 --- a/framebuffer/gui.c +++ b/framebuffer/gui.c @@ -70,15 +70,6 @@ struct gui_window *window_list = NULL; bool redraws_pending = false; - -#ifndef MIN -#define MIN(a,b) (((a) < (b)) ? (a) : (b)) -#endif - -#ifndef MAX -#define MAX(a,b) (((a) > (b)) ? (a) : (b)) -#endif - /* private data for browser user widget */ struct browser_widget_s { struct browser_window *bw; /**< The browser window connected to this gui window */ @@ -102,10 +93,10 @@ fb_queue_redraw(struct fbtk_widget_s *widget, int x0, int y0, int x1, int y1) { struct browser_widget_s *bwidget = fbtk_get_userpw(widget); - bwidget->redraw_box.x0 = MIN(bwidget->redraw_box.x0, x0); - bwidget->redraw_box.y0 = MIN(bwidget->redraw_box.y0, y0); - bwidget->redraw_box.x1 = MAX(bwidget->redraw_box.x1, x1); - bwidget->redraw_box.y1 = MAX(bwidget->redraw_box.y1, y1); + bwidget->redraw_box.x0 = min(bwidget->redraw_box.x0, x0); + bwidget->redraw_box.y0 = min(bwidget->redraw_box.y0, y0); + bwidget->redraw_box.x1 = max(bwidget->redraw_box.x1, x1); + bwidget->redraw_box.y1 = max(bwidget->redraw_box.y1, y1); if (fbtk_clip_to_widget(widget, &bwidget->redraw_box)) { bwidget->redraw_required = true; @@ -121,7 +112,6 @@ static void fb_pan(fbtk_widget_t *widget, struct browser_widget_s *bwidget, struct browser_window *bw) { - struct content *c; int x; int y; int width; @@ -129,13 +119,11 @@ static void fb_pan(fbtk_widget_t *widget, nsfb_bbox_t srcbox; nsfb_bbox_t dstbox; - c = bw->current_content; - - if ((!c) || (c->locked)) - return; - nsfb_t *nsfb = fbtk_get_nsfb(widget); + int content_height = content_get_height(bw->current_content); + int content_width = content_get_width(bw->current_content); + height = fbtk_get_height(widget); width = fbtk_get_width(widget); x = fbtk_get_x(widget); @@ -146,16 +134,16 @@ static void fb_pan(fbtk_widget_t *widget, bwidget->pany = - bwidget->scrolly; /* do not pan off the bottom of the content */ - if ((bwidget->scrolly + bwidget->pany) > (c->height - height)) - bwidget->pany = (c->height - height) - bwidget->scrolly; + if ((bwidget->scrolly + bwidget->pany) > (content_height - height)) + bwidget->pany = (content_height - height) - bwidget->scrolly; /* dont pan off the left */ if ((bwidget->scrollx + bwidget->panx) < 0) bwidget->panx = - bwidget->scrollx; /* do not pan off the right of the content */ - if ((bwidget->scrollx + bwidget->panx) > (c->width - width)) - bwidget->panx = (c->width - width) - bwidget->scrollx; + if ((bwidget->scrollx + bwidget->panx) > (content_width - width)) + bwidget->panx = (content_width - width) - bwidget->scrollx; LOG(("panning %d, %d",bwidget->panx, bwidget->pany)); @@ -268,16 +256,11 @@ static void fb_redraw(fbtk_widget_t *widget, struct browser_widget_s *bwidget, struct browser_window *bw) { - struct content *c; int x; int y; int width; int height; - c = bw->current_content; - - if ((!c) || (c->locked)) - return; LOG(("redraw box %d,%d to %d,%d",bwidget->redraw_box.x0,bwidget->redraw_box.y0, bwidget->redraw_box.x1, bwidget->redraw_box.y1)); @@ -292,13 +275,11 @@ static void fb_redraw(fbtk_widget_t *widget, bwidget->redraw_box.x0 += x; bwidget->redraw_box.x1 += x; - - nsfb_claim(fbtk_get_nsfb(widget), &bwidget->redraw_box); /* redraw bounding box is relative to window */ current_redraw_browser = bw; - content_redraw(c, + content_redraw(bw->current_content, x - bwidget->scrollx, y - bwidget->scrolly, width, height, bwidget->redraw_box.x0, bwidget->redraw_box.y0, @@ -328,9 +309,9 @@ fb_browser_window_redraw(fbtk_widget_t *root, fbtk_widget_t *widget, void *pw) if (bwidget->pan_required) { int pos; fb_pan(widget, bwidget, gw->bw); - pos = (bwidget->scrollx * 100) / gw->bw->current_content->width; + pos = (bwidget->scrollx * 100) / content_get_width(gw->bw->current_content); fbtk_set_scroll_pos(gw->hscroll, pos); - pos = (bwidget->scrolly * 100) / gw->bw->current_content->height; + pos = (bwidget->scrolly * 100) / content_get_height(gw->bw->current_content); fbtk_set_scroll_pos(gw->vscroll, pos); } @@ -409,11 +390,23 @@ static bool process_cmdline(int argc, char** argv) return true; } -/** Normal entry point from OS */ +/** Entry point from OS. + * + * /param argc The number of arguments in the string vector. + * /param argv The argument string vector. + * /return The return code to the OS + */ int main(int argc, char** argv) { setbuf(stderr, NULL); - return netsurf_main(argc, argv); + + netsurf_init(argc, argv); + + netsurf_main_loop(); + + netsurf_exit(); + + return 0; } void gui_init(int argc, char** argv) @@ -1176,15 +1169,17 @@ void gui_window_get_dimensions(struct gui_window *g, int *width, int *height, *height = fbtk_get_height(g->browser); } -void gui_window_update_extent(struct gui_window *g) +void gui_window_update_extent(struct gui_window *gw) { int pct; - pct = (fbtk_get_width(g->browser) * 100) / g->bw->current_content->width; - fbtk_set_scroll(g->hscroll, pct); + pct = (fbtk_get_width(gw->browser) * 100) / + content_get_width(gw->bw->current_content); + fbtk_set_scroll(gw->hscroll, pct); - pct = (fbtk_get_height(g->browser) * 100) / g->bw->current_content->height; - fbtk_set_scroll(g->vscroll, pct); + pct = (fbtk_get_height(gw->browser) * 100) / + content_get_height(gw->bw->current_content); + fbtk_set_scroll(gw->vscroll, pct); } @@ -1328,7 +1323,8 @@ bool gui_window_frame_resize_start(struct gui_window *g) return true; } -void gui_window_save_as_link(struct gui_window *g, struct content *c) +void +gui_window_save_link(struct gui_window *g, const char *url, const char *title) { } @@ -1340,7 +1336,8 @@ void gui_window_set_scale(struct gui_window *g, float scale) /** * set favicon */ -void gui_window_set_icon(struct gui_window *g, struct content *icon) +void +gui_window_set_icon(struct gui_window *g, hlcache_handle *icon) { } @@ -1349,7 +1346,8 @@ void gui_window_set_icon(struct gui_window *g, struct content *icon) * \param ico may be NULL for local calls; then access current cache from * search_web_ico() */ -void gui_window_set_search_ico(struct content *ico) +void +gui_window_set_search_ico(hlcache_handle *ico) { } @@ -1374,8 +1372,8 @@ void gui_download_window_done(struct gui_download_window *dw) { } -void gui_drag_save_object(gui_save_type type, struct content *c, - struct gui_window *g) +void gui_drag_save_object(gui_save_type type, hlcache_handle *c, + struct gui_window *w) { } @@ -1420,7 +1418,7 @@ void gui_launch_url(const char *url) { } -void gui_cert_verify(struct browser_window *bw, struct content *c, +void gui_cert_verify(struct browser_window *bw, struct hlcache_handle *c, const struct ssl_cert_info *certs, unsigned long num) { } diff --git a/framebuffer/hotlist.c b/framebuffer/hotlist.c index 48df23bb4..727200fe3 100644 --- a/framebuffer/hotlist.c +++ b/framebuffer/hotlist.c @@ -18,6 +18,6 @@ #include "desktop/browser.h" -void hotlist_visited(struct content *content) +void hotlist_visited(struct hlcache_handle *content) { } diff --git a/framebuffer/login.c b/framebuffer/login.c index 306de8459..7cd1cab2e 100644 --- a/framebuffer/login.c +++ b/framebuffer/login.c @@ -18,7 +18,7 @@ #include "desktop/401login.h" -void gui_401login_open(struct browser_window *bw, struct content *c, +void gui_401login_open(struct browser_window *bw, struct hlcache_handle *c, const char *realm) { } diff --git a/framebuffer/thumbnail.c b/framebuffer/thumbnail.c index 5ab7ed431..81419d282 100644 --- a/framebuffer/thumbnail.c +++ b/framebuffer/thumbnail.c @@ -18,7 +18,7 @@ #include "desktop/browser.h" -bool thumbnail_create(struct content *content, struct bitmap *bitmap, +bool thumbnail_create(struct hlcache_handle *content, struct bitmap *bitmap, const char *url) { return false; diff --git a/gtk/dialogs/gtk_options.c b/gtk/dialogs/gtk_options.c index eb7429223..b883195b4 100644 --- a/gtk/dialogs/gtk_options.c +++ b/gtk/dialogs/gtk_options.c @@ -570,7 +570,7 @@ ENTRY_CHANGED(entryHomePageURL, option_homepage_url) END_HANDLER BUTTON_CLICKED(setCurrentPage) - const gchar *url = current_browser->current_content->url; + const gchar *url = content_get_url(current_browser->current_content); gtk_entry_set_text(GTK_ENTRY(entryHomePageURL), url); option_homepage_url = strdup(gtk_entry_get_text(GTK_ENTRY(entryHomePageURL))); diff --git a/gtk/dialogs/gtk_source.c b/gtk/dialogs/gtk_source.c index 7ee3ef849..842d4c4ef 100644 --- a/gtk/dialogs/gtk_source.c +++ b/gtk/dialogs/gtk_source.c @@ -107,7 +107,7 @@ void nsgtk_source_dialog_init(GtkWindow *parent, struct browser_window *bw) { char glade_Location[strlen(res_dir_location) + SLEN("source.glade") + 1]; - if (bw->current_content->type != CONTENT_HTML) + if (content_get_type(bw->current_content) != CONTENT_HTML) return; if (option_source_tab) { @@ -121,12 +121,17 @@ void nsgtk_source_dialog_init(GtkWindow *parent, struct browser_window *bw) LOG(("error loading glade tree")); } + const char *source_data; + unsigned long source_size; char *data = NULL; + source_data = content_get_source_data(bw->current_content, + &source_size); + utf8_convert_ret r = utf8_from_enc( - bw->current_content->source_data, - bw->current_content->data.html.encoding, - bw->current_content->source_size, + source_data, + html_get_encoding(bw->current_content), + source_size, &data); if (r == UTF8_CONVERT_NOMEM) { warn_user("NoMemory",0); @@ -160,7 +165,7 @@ void nsgtk_source_dialog_init(GtkWindow *parent, struct browser_window *bw) return; } - thiswindow->url = strdup(bw->current_content->url); + thiswindow->url = strdup(content_get_url(bw->current_content)); if (thiswindow->url == NULL) { free(thiswindow); free(data); @@ -173,8 +178,8 @@ void nsgtk_source_dialog_init(GtkWindow *parent, struct browser_window *bw) thiswindow->sourcewindow = wndSource; thiswindow->bw = bw; - char title[strlen(bw->current_content->url) + SLEN("Source of ") + 1]; - sprintf(title, "Source of %s", bw->current_content->url); + char title[strlen(thiswindow->url) + SLEN("Source of ") + 1]; + sprintf(title, "Source of %s", thiswindow->url); thiswindow->next = nsgtk_source_list; thiswindow->prev = NULL; @@ -209,11 +214,17 @@ void nsgtk_source_dialog_init(GtkWindow *parent, struct browser_window *bw) } void nsgtk_source_tab_init(GtkWindow *parent, struct browser_window *bw) { + const char *source_data; + unsigned long source_size; char *ndata = 0; + + source_data = content_get_source_data(bw->current_content, + &source_size); + utf8_convert_ret r = utf8_from_enc( - bw->current_content->source_data, - bw->current_content->data.html.encoding, - bw->current_content->source_size, + source_data, + html_get_encoding(bw->current_content), + source_size, &ndata); if (r == UTF8_CONVERT_NOMEM) { warn_user("NoMemory",0); @@ -245,18 +256,9 @@ void nsgtk_source_tab_init(GtkWindow *parent, struct browser_window *bw) warn_user(messages_get("NoMemory"), 0); return; } - struct browser_window *newbw = browser_window_create(fileurl, bw, - NULL, false, true); + /* Open tab */ + browser_window_create(fileurl, bw, NULL, false, true); free(fileurl); - if (newbw->current_content) { - newbw->current_content->title = malloc( - strlen(bw->current_content->url) + - SLEN("source of ") + 1); - if (newbw->current_content->title == NULL) - return; - sprintf(newbw->current_content->title, "source of %s", - bw->current_content->url); - } } diff --git a/gtk/gtk_gui.c b/gtk/gtk_gui.c index 0251e173f..da8574a79 100644 --- a/gtk/gtk_gui.c +++ b/gtk/gtk_gui.c @@ -42,6 +42,7 @@ #include "content/content.h" #include "content/fetch.h" #include "content/fetchers/fetch_curl.h" +#include "content/hlcache.h" #include "content/urldb.h" #include "desktop/401login.h" #include "desktop/browser.h" @@ -122,7 +123,14 @@ int main(int argc, char** argv) setbuf(stderr, NULL); - return netsurf_main(argc, argv); + /* initialise netsurf */ + netsurf_init(argc, argv); + + netsurf_main_loop(); + + netsurf_exit(); + + return 0; } @@ -599,7 +607,8 @@ void gui_create_form_select_menu(struct browser_window *bw, } -void gui_window_save_as_link(struct gui_window *g, struct content *c) +void gui_window_save_link(struct gui_window *g, const char *url, + const char *title) { } @@ -630,12 +639,11 @@ void die(const char * const error) } -void hotlist_visited(struct content *content) +void hotlist_visited(hlcache_handle *content) { } - -void gui_cert_verify(struct browser_window *bw, struct content *c, +void gui_cert_verify(struct browser_window *bw, hlcache_handle *c, const struct ssl_cert_info *certs, unsigned long num) { GladeXML *x = glade_xml_new(glade_ssl_file_location, NULL, NULL); @@ -644,7 +652,7 @@ void gui_cert_verify(struct browser_window *bw, struct content *c, void **session = calloc(sizeof(void *), 4); session[0] = bw; - session[1] = strdup(c->url); + session[1] = strdup(content_get_url(c)); session[2] = x; session[3] = wnd; diff --git a/gtk/gtk_login.c b/gtk/gtk_login.c index 3bb11a66b..ed6f92407 100644 --- a/gtk/gtk_login.c +++ b/gtk/gtk_login.c @@ -25,6 +25,7 @@ #include "utils/log.h" #include "gtk/gtk_gui.h" #include "content/content.h" +#include "content/hlcache.h" #include "content/urldb.h" #include "desktop/browser.h" #include "desktop/401login.h" @@ -51,16 +52,16 @@ static void nsgtk_login_next(GtkWidget *w, gpointer data); static void nsgtk_login_ok_clicked(GtkButton *w, gpointer data); static void nsgtk_login_cancel_clicked(GtkButton *w, gpointer data); -void gui_401login_open(struct browser_window *bw, struct content *c, +void gui_401login_open(struct browser_window *bw, hlcache_handle *c, const char *realm) { char *host; url_func_result res; - res = url_host(c->url, &host); + res = url_host(content_get_url(c), &host); assert(res == URL_FUNC_OK); - create_login_window(bw, host, realm, c->url); + create_login_window(bw, host, realm, content_get_url(c)); free(host); } diff --git a/gtk/gtk_print.c b/gtk/gtk_print.c index 98b43cb89..f08880157 100644 --- a/gtk/gtk_print.c +++ b/gtk/gtk_print.c @@ -32,6 +32,7 @@ #include <gtk/gtk.h> #include "content/content.h" +#include "content/hlcache.h" #include "desktop/options.h" #include "desktop/plotters.h" #include "desktop/print.h" @@ -48,7 +49,7 @@ /* Globals */ cairo_t *gtk_print_current_cr; static struct print_settings* settings; -struct content *content_to_print; +hlcache_handle *content_to_print; static GdkRectangle cliprect; static inline void nsgtk_print_set_colour(colour c) diff --git a/gtk/gtk_print.h b/gtk/gtk_print.h index d2b89b92d..d44fad31f 100644 --- a/gtk/gtk_print.h +++ b/gtk/gtk_print.h @@ -26,9 +26,11 @@ #include <gtk/gtk.h> +struct hlcache_handle; + extern cairo_t *gtk_print_current_cr; -extern struct content *content_to_print; +extern struct hlcache_handle *content_to_print; /*handlers for signals from the GTK print operation*/ diff --git a/gtk/gtk_scaffolding.c b/gtk/gtk_scaffolding.c index f8ac3daa3..381711377 100644 --- a/gtk/gtk_scaffolding.c +++ b/gtk/gtk_scaffolding.c @@ -30,6 +30,7 @@ #include <libxml/debugXML.h> #include "gtk/gtk_scaffolding.h" #include "content/content.h" +#include "content/hlcache.h" #include "css/utils.h" #include "desktop/browser.h" #include "desktop/history_core.h" @@ -271,8 +272,10 @@ void nsgtk_window_update_back_forward(struct gtk_scaffolding *g) nsgtk_scaffolding_set_sensitivity(g); /* update the url bar, particularly necessary when tabbing */ - if (bw->current_content != NULL && bw->current_content->url != NULL) - browser_window_refresh_url_bar(bw, bw->current_content->url, + if (bw->current_content != NULL && + content_get_url(bw->current_content) != NULL) + browser_window_refresh_url_bar(bw, + content_get_url(bw->current_content), bw->frag_id); /* update the local history window, as well as queuing a redraw @@ -493,8 +496,8 @@ MULTIHANDLER(savepage) gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(fc), filter); gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(fc), filter); - res = url_nice(gui_window_get_browser_window( - g->top_level)->current_content->url, &path, false); + res = url_nice(content_get_url(gui_window_get_browser_window( + g->top_level)->current_content), &path, false); if (res != URL_FUNC_OK) { path = strdup(messages_get("SaveText")); if (path == NULL) { @@ -549,7 +552,7 @@ MULTIHANDLER(pdf) LOG(("Print preview (generating PDF) started.")); - res = url_nice(bw->current_content->url, &url_name, true); + res = url_nice(content_get_url(bw->current_content), &url_name, true); if (res != URL_FUNC_OK) { warn_user(messages_get(res == URL_FUNC_NOMEM ? "NoMemory" : "URIError"), 0); @@ -622,8 +625,8 @@ MULTIHANDLER(plaintext) char *filename; url_func_result res; - res = url_nice(gui_window_get_browser_window( - g->top_level)->current_content->url, &filename, false); + res = url_nice(content_get_url(gui_window_get_browser_window( + g->top_level)->current_content), &filename, false); if (res != URL_FUNC_OK) { filename = strdup(messages_get("SaveText")); if (filename == NULL) { @@ -710,7 +713,7 @@ MULTIHANDLER(print) G_CALLBACK(gtk_print_signal_draw_page), NULL); g_signal_connect(print_op, "end_print", G_CALLBACK(gtk_print_signal_end_print), settings); - if (bw->current_content->type != CONTENT_TEXTPLAIN) + if (content_get_type(bw->current_content) != CONTENT_TEXTPLAIN) res = gtk_print_operation_run(print_op, GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG, g->window, @@ -762,7 +765,7 @@ MENUHANDLER(savelink) return FALSE; browser_window_download(bw, current_menu_link_box->href, - bw->current_content->url); + content_get_url(bw->current_content)); return TRUE; } @@ -1125,11 +1128,11 @@ MULTIHANDLER(saveboxtree) struct browser_window *bw; bw = gui_window_get_browser_window(g->top_level); - if (bw->current_content && - bw->current_content->type == + if (bw->current_content && + content_get_type(bw->current_content) == CONTENT_HTML) { box_dump(fh, - bw->current_content->data.html.layout, + html_get_box_tree(bw->current_content), 0); } @@ -1174,12 +1177,11 @@ MULTIHANDLER(savedomtree) struct browser_window *bw; bw = gui_window_get_browser_window(g->top_level); - if (bw->current_content && - bw->current_content->type == + if (bw->current_content && + content_get_type(bw->current_content) == CONTENT_HTML) { xmlDebugDumpDocument(fh, - bw->current_content-> - data.html.document); + html_get_document(bw->current_content)); } fclose(fh); @@ -1871,80 +1873,93 @@ void gui_window_stop_throbber(struct gui_window* _g) /** * set favicon */ -void gui_window_set_icon(struct gui_window *_g, struct content *icon) +void gui_window_set_icon(struct gui_window *_g, hlcache_handle *icon) { struct gtk_scaffolding *g = nsgtk_get_scaffold(_g); - GtkImage *iconImage = NULL; - if (g->icoFav != NULL) - g_object_unref(g->icoFav); + struct bitmap *icon_bitmap; + GtkImage *iconImage; + + if (icon == NULL) + return; + #ifdef WITH_BMP - if ((icon != NULL) && (icon->type == CONTENT_ICO)) { + if (content_get_type(icon) == CONTENT_ICO) nsico_set_bitmap_from_size(icon, 16, 16); - } #endif - if ((icon != NULL) && (icon->bitmap != NULL)) { - GdkPixbuf *pb = gtk_bitmap_get_primary(icon->bitmap); - if ((pb != NULL) && (gdk_pixbuf_get_width(pb) > 0) && - (gdk_pixbuf_get_height(pb) > 0)) { - pb = gdk_pixbuf_scale_simple(pb, 16, 16, - GDK_INTERP_HYPER); - iconImage = GTK_IMAGE( - gtk_image_new_from_pixbuf(pb)); - } else { - iconImage = NULL; - } - } - if (iconImage == NULL) { - char imagepath[strlen(res_dir_location) + SLEN("favicon.png") - + 1]; + + icon_bitmap = content_get_bitmap(icon); + if (icon_bitmap == NULL) + return; + + GdkPixbuf *pb = gtk_bitmap_get_primary(icon_bitmap); + if (pb != NULL && gdk_pixbuf_get_width(pb) > 0 && + gdk_pixbuf_get_height(pb) > 0) { + pb = gdk_pixbuf_scale_simple(pb, 16, 16, GDK_INTERP_HYPER); + iconImage = GTK_IMAGE(gtk_image_new_from_pixbuf(pb)); + } else { + /** \todo Does pb need cleaning up? */ + char imagepath[strlen(res_dir_location) + + SLEN("favicon.png") + 1]; sprintf(imagepath, "%sfavicon.png", res_dir_location); iconImage = GTK_IMAGE(gtk_image_new_from_file(imagepath)); } + + if (iconImage == NULL) + return; + + if (g->icoFav != NULL) + g_object_unref(g->icoFav); g->icoFav = iconImage; - sexy_icon_entry_set_icon(SEXY_ICON_ENTRY(g->url_bar), + + sexy_icon_entry_set_icon(SEXY_ICON_ENTRY(g->url_bar), SEXY_ICON_ENTRY_PRIMARY, GTK_IMAGE(g->icoFav)); gtk_widget_show_all(GTK_WIDGET(g->buttons[URL_BAR_ITEM]->button)); } -void gui_window_set_search_ico(struct content *ico) +void gui_window_set_search_ico(hlcache_handle *ico) { - GdkPixbuf *pbico = NULL; - GtkImage *searchico = NULL; + GdkPixbuf *pbico; + GtkImage *searchico; + struct bitmap *ico_bitmap; nsgtk_scaffolding *current; - if (ico == NULL) - ico = search_web_ico(); + + if (ico == NULL && (ico = search_web_ico()) == NULL) + return; #ifdef WITH_BMP - if ((ico != NULL) && (ico->type == CONTENT_ICO)) { + if (content_get_type(ico) == CONTENT_ICO) nsico_set_bitmap_from_size(ico, 20, 20); - } #endif - if ((ico != NULL) && (ico->bitmap != NULL)) { - pbico = gtk_bitmap_get_primary(ico->bitmap); - if ((pbico != NULL) && (gdk_pixbuf_get_width(pbico) > 0) && - (gdk_pixbuf_get_height(pbico) > 0)) { - pbico = gdk_pixbuf_scale_simple(pbico, 20, 20, - GDK_INTERP_HYPER); - current = scaf_list; - searchico = GTK_IMAGE( - gtk_image_new_from_pixbuf(pbico)); - } else { - searchico = NULL; - } + ico_bitmap = content_get_bitmap(ico); + if (ico_bitmap == NULL) + return; + + pbico = gtk_bitmap_get_primary(ico_bitmap); + if (pbico != NULL && gdk_pixbuf_get_width(pbico) > 0 && + gdk_pixbuf_get_height(pbico) > 0) { + pbico = gdk_pixbuf_scale_simple(pbico, 20, 20, + GDK_INTERP_HYPER); + searchico = GTK_IMAGE(gtk_image_new_from_pixbuf(pbico)); + } else { + /** \todo Does pbico need cleaning up? */ + return; } - /* add ico to toolbar */ - current = scaf_list; - while (current) { + + /* add ico to each window's toolbar */ + for (current = scaf_list; current != NULL; current = current->next) { if (searchico != NULL) { + /** \todo Are we leaking webSearchIco here? */ current->webSearchIco = searchico; sexy_icon_entry_set_icon(SEXY_ICON_ENTRY( current->webSearchEntry), SEXY_ICON_ENTRY_PRIMARY, current->webSearchIco); } - searchico = GTK_IMAGE(gtk_image_new_from_pixbuf(pbico)); - current = current->next; + if (pbico != NULL) + searchico = GTK_IMAGE(gtk_image_new_from_pixbuf(pbico)); + else + searchico = NULL; } } @@ -2117,33 +2132,26 @@ void nsgtk_scaffolding_set_top_level (struct gui_window *gw) nsgtk_get_scaffold(gw)->top_level = gw; struct browser_window *bw = gui_window_get_browser_window(gw); + assert(bw != NULL); + /* Synchronise the history (will also update the URL bar) */ nsgtk_window_update_back_forward(nsgtk_get_scaffold(gw)); + /* clear effects of potential searches */ - if ((bw != NULL) && (bw->search_context != NULL)) + if (bw->search_context != NULL) search_destroy_context(bw->search_context); + nsgtk_search_set_forward_state(true, bw); nsgtk_search_set_back_state(true, bw); /* Ensure the window's title bar as well as favicon are updated */ - if (gui_window_get_browser_window(gw) != NULL && - gui_window_get_browser_window(gw)->current_content - != NULL) { - if (gui_window_get_browser_window(gw)->current_content->title - != NULL) { - gui_window_set_title(gw, - gui_window_get_browser_window(gw)-> - current_content->title); - } else { - gui_window_set_title(gw, - gui_window_get_browser_window(gw)-> - current_content->url); - } - if (gui_window_get_browser_window(gw)->current_content->type - == CONTENT_HTML) + if (bw->current_content != NULL) { + gui_window_set_title(gw, + content_get_title(bw->current_content)); + + if (content_get_type(bw->current_content) == CONTENT_HTML) gui_window_set_icon(gw, - gui_window_get_browser_window(gw)-> - current_content->data.html.favicon); + html_get_favicon(bw->current_content)); } } @@ -2287,7 +2295,8 @@ static guint nsgtk_scaffolding_update_link_operations_sensitivity( struct browser_window *bw = gui_window_get_browser_window(g->top_level); current_menu_link_box = NULL; - if (bw->current_content && bw->current_content->type == CONTENT_HTML) { + if (bw->current_content && + content_get_type(bw->current_content) == CONTENT_HTML) { current_menu_link_box = box_href_at_point(bw->current_content, x, y); } diff --git a/gtk/gtk_search.c b/gtk/gtk_search.c index bb5d0138c..30075be02 100644 --- a/gtk/gtk_search.c +++ b/gtk/gtk_search.c @@ -28,6 +28,7 @@ #include "gtk/gtk_window.h" #include "utils/config.h" #include "content/content.h" +#include "content/hlcache.h" #include "desktop/browser.h" #include "desktop/gui.h" #include "desktop/search.h" @@ -99,7 +100,7 @@ gboolean nsgtk_search_back_button_clicked(GtkWidget *widget, gpointer data) void nsgtk_search_init(struct gtk_scaffolding *g) { - struct content *c; + hlcache_handle *c; assert(gui_window_get_browser_window(nsgtk_scaffolding_top_level(g)) != NULL); @@ -107,7 +108,8 @@ void nsgtk_search_init(struct gtk_scaffolding *g) c = gui_window_get_browser_window(nsgtk_scaffolding_top_level(g))-> current_content; - if ((!c) || (c->type != CONTENT_HTML && c->type != CONTENT_TEXTPLAIN)) + if ((!c) || (content_get_type(c) != CONTENT_HTML && + content_get_type(c) != CONTENT_TEXTPLAIN)) return; } diff --git a/gtk/gtk_theme.c b/gtk/gtk_theme.c index 146d9f158..1995a3774 100644 --- a/gtk/gtk_theme.c +++ b/gtk/gtk_theme.c @@ -22,6 +22,7 @@ #include <unistd.h> #include "content/content.h" #include "content/content_type.h" +#include "content/hlcache.h" #include "gtk/gtk_gui.h" #include "gtk/gtk_scaffolding.h" #include "gtk/gtk_menu.h" @@ -62,10 +63,10 @@ static void nsgtk_theme_cache_searchimage(nsgtk_search_buttons i, const char *filename, const char *path); #ifdef WITH_THEME_INSTALL -static struct content *theme_install_content = NULL; +static hlcache_handle *theme_install_content = NULL; -static void theme_install_callback(content_msg msg, struct content *c, - intptr_t p1, intptr_t p2, union content_msg_data data); +static void theme_install_callback(hlcache_handle *c, content_msg msg, + union content_msg_data data, void *pw); static bool theme_install_read(const char *data, unsigned long len); #endif @@ -674,14 +675,14 @@ GtkImage *nsgtk_theme_image_default(nsgtk_toolbar_button i, GtkIconSize s) /** * when CONTENT_THEME needs handling call this function */ -void theme_install_start(struct content *c) +void theme_install_start(hlcache_handle *c) { assert(c); - assert(c->type == CONTENT_THEME); + assert(content_get_type(c) == CONTENT_THEME); /* stop theme sitting in memory cache */ - c->fresh = false; - if (!content_add_user(c, theme_install_callback, 0, 0)) { + content_invalidate_reuse_data(c); + if (!content_add_user(c, theme_install_callback, NULL)) { warn_user("NoMemory", 0); return; } @@ -692,17 +693,25 @@ void theme_install_start(struct content *c) * Callback for fetchcache() for theme install fetches. */ -void theme_install_callback(content_msg msg, struct content *c, - intptr_t p1, intptr_t p2, union content_msg_data data) +void theme_install_callback(hlcache_handle *c, content_msg msg, + union content_msg_data data, void *pw) { switch (msg) { case CONTENT_MSG_READY: break; case CONTENT_MSG_DONE: + { + const char *source_data; + unsigned long source_size; + theme_install_content = c; - if (!theme_install_read(c->source_data, c->source_size)) + + source_data = content_get_source_data(c, &source_size); + + if (!theme_install_read(source_data, source_size)) warn_user("ThemeInvalid", 0); + } break; case CONTENT_MSG_ERROR: diff --git a/gtk/gtk_thumbnail.c b/gtk/gtk_thumbnail.c index bbf7c4dd2..ec538da36 100644 --- a/gtk/gtk_thumbnail.c +++ b/gtk/gtk_thumbnail.c @@ -27,6 +27,7 @@ #include <assert.h> #include <gtk/gtk.h> #include "content/content.h" +#include "content/hlcache.h" #include "content/urldb.h" #include "desktop/plotters.h" #include "desktop/browser.h" @@ -45,7 +46,7 @@ * \param bitmap the bitmap to draw to * \param url the URL the thumnail belongs to, or NULL */ -bool thumbnail_create(struct content *content, struct bitmap *bitmap, +bool thumbnail_create(hlcache_handle *content, struct bitmap *bitmap, const char *url) { GdkPixbuf *pixbuf; @@ -59,8 +60,8 @@ bool thumbnail_create(struct content *content, struct bitmap *bitmap, assert(content); assert(bitmap); - cwidth = min(content->width, 1024); - cheight = min(content->height, 768); + cwidth = min(content_get_width(content), 1024); + cheight = min(content_get_height(content), 768); pixbuf = gtk_bitmap_get_primary(bitmap); width = gdk_pixbuf_get_width(pixbuf); @@ -68,7 +69,8 @@ bool thumbnail_create(struct content *content, struct bitmap *bitmap, depth = (gdk_screen_get_system_visual(gdk_screen_get_default()))->depth; LOG(("Trying to create a thumbnail pixmap for a content of %dx%d@%d", - content->width, content->width, depth)); + content_get_width(content), content_get_height(content), + depth)); pixmap = gdk_pixmap_new(NULL, cwidth, cwidth, depth); @@ -87,7 +89,8 @@ bool thumbnail_create(struct content *content, struct bitmap *bitmap, /* set the plotting functions up */ plot = nsgtk_plotters; - nsgtk_plot_set_scale((double) cwidth / (double) content->width); + nsgtk_plot_set_scale((double) cwidth / + (double) content_get_width(content)); /* set to plot to pixmap */ current_drawable = pixmap; @@ -98,8 +101,10 @@ bool thumbnail_create(struct content *content, struct bitmap *bitmap, plot.rectangle(0, 0, cwidth, cwidth, plot_style_fill_white); /* render the content */ - content_redraw(content, 0, 0, content->width, content->width, - 0, 0, content->width, content->width, 1.0, 0xFFFFFF); + content_redraw(content, 0, 0, content_get_width(content), + content_get_width(content), + 0, 0, content_get_width(content), + content_get_width(content), 1.0, 0xFFFFFF); /* resample the large plot down to the size of our thumbnail */ big = gdk_pixbuf_get_from_drawable(NULL, pixmap, NULL, 0, 0, 0, 0, diff --git a/gtk/gtk_toolbar.c b/gtk/gtk_toolbar.c index d3a5b52a1..b63163c90 100644 --- a/gtk/gtk_toolbar.c +++ b/gtk/gtk_toolbar.c @@ -412,15 +412,16 @@ void nsgtk_toolbar_close(nsgtk_scaffolding *g) NSGTK_WINDOW_SIGNAL_REDRAW)); if ((gui_window_get_browser_window(nsgtk_scaffolding_top_level( list))->current_content != NULL) && - (gui_window_get_browser_window( + (content_get_url(gui_window_get_browser_window( nsgtk_scaffolding_top_level(list))-> - current_content->url != NULL)) + current_content) != NULL)) browser_window_refresh_url_bar( gui_window_get_browser_window( nsgtk_scaffolding_top_level(list)), + content_get_url( gui_window_get_browser_window( nsgtk_scaffolding_top_level(list))-> - current_content->url, + current_content), gui_window_get_browser_window( nsgtk_scaffolding_top_level(list))-> frag_id); diff --git a/gtk/gtk_window.c b/gtk/gtk_window.c index 54e9c16a1..29ce2f431 100644 --- a/gtk/gtk_window.c +++ b/gtk/gtk_window.c @@ -19,6 +19,7 @@ #include <inttypes.h> #include <string.h> +#include "content/hlcache.h" #include "gtk/gtk_window.h" #include "desktop/browser.h" #include "desktop/options.h" @@ -144,7 +145,7 @@ struct gui_window *gui_create_browser_window(struct browser_window *bw, struct gui_window *g; /**< what we're creating to return */ GtkPolicyType scrollpolicy; - g = malloc(sizeof(*g)); + g = calloc(1, sizeof(*g)); if (!g) { warn_user("NoMemory", 0); return 0; @@ -349,7 +350,7 @@ gboolean nsgtk_window_expose_event(GtkWidget *widget, GdkEventExpose *event, gpointer data) { struct gui_window *g = data; - struct content *c; + hlcache_handle *c; float scale = g->bw->scale; assert(g); @@ -366,7 +367,7 @@ gboolean nsgtk_window_expose_event(GtkWidget *widget, return FALSE; /* HTML rendering handles scale itself */ - if (c->type == CONTENT_HTML) + if (content_get_type(c) == CONTENT_HTML) scale = 1; current_widget = (GtkWidget *)g->drawing_area; @@ -712,7 +713,7 @@ void gui_window_redraw_window(struct gui_window *g) void gui_window_update_box(struct gui_window *g, const union content_msg_data *data) { - struct content *c = g->bw->current_content; + hlcache_handle *c = g->bw->current_content; if (c == NULL) return; @@ -788,8 +789,8 @@ void gui_window_update_extent(struct gui_window *g) return; gtk_widget_set_size_request(GTK_WIDGET(g->drawing_area), - g->bw->current_content->width * g->bw->scale, - g->bw->current_content->height * g->bw->scale); + content_get_width(g->bw->current_content) * g->bw->scale, + content_get_height(g->bw->current_content) * g->bw->scale); gtk_widget_set_size_request(GTK_WIDGET(g->viewport), 0, 0); @@ -958,7 +959,7 @@ bool gui_window_box_scroll_start(struct gui_window *g, return true; } -void gui_drag_save_object(gui_save_type type, struct content *c, +void gui_drag_save_object(gui_save_type type, hlcache_handle *c, struct gui_window *g) { diff --git a/image/bmp.c b/image/bmp.c index 2a27231ac..1774f945c 100644 --- a/image/bmp.c +++ b/image/bmp.c @@ -30,7 +30,7 @@ #include <stdlib.h> #include <libnsbmp.h> #include "utils/config.h" -#include "content/content.h" +#include "content/content_protected.h" #include "desktop/plotters.h" #include "image/bitmap.h" #include "image/bmp.h" @@ -49,8 +49,7 @@ bmp_bitmap_callback_vt bmp_bitmap_callbacks = { .bitmap_get_bpp = bitmap_get_bpp }; -bool nsbmp_create(struct content *c, struct content *parent, - const char *params[]) +bool nsbmp_create(struct content *c, const struct http_parameter *params) { union content_msg_data msg_data; @@ -71,12 +70,16 @@ bool nsbmp_convert(struct content *c, int iwidth, int iheight) bmp_image *bmp; union content_msg_data msg_data; uint32_t swidth; + const char *data; + unsigned long size; /* set the bmp data */ bmp = c->data.bmp.bmp; + data = content__get_source_data(c, &size); + /* analyse the BMP */ - res = bmp_analyse(bmp, c->source_size, (unsigned char *)c->source_data); + res = bmp_analyse(bmp, size, (unsigned char *) data); switch (res) { case BMP_OK: break; @@ -98,7 +101,7 @@ bool nsbmp_convert(struct content *c, int iwidth, int iheight) c->title = malloc(100); if (c->title) snprintf(c->title, 100, messages_get("BMPTitle"), c->width, - c->height, c->source_size); + c->height, size); swidth = bmp->bitmap_callbacks.bitmap_get_bpp(bmp->bitmap) * bmp->width; c->size += (swidth * bmp->height) + 16 + 44 + 100; diff --git a/image/bmp.h b/image/bmp.h index f7b974f03..7d70a952d 100644 --- a/image/bmp.h +++ b/image/bmp.h @@ -33,6 +33,7 @@ struct content; struct bitmap; +struct http_parameter; struct content_bmp_data { bmp_image *bmp; /** BMP image data */ @@ -40,8 +41,7 @@ struct content_bmp_data { extern bmp_bitmap_callback_vt bmp_bitmap_callbacks; /** Only to be used by ICO code. */ -bool nsbmp_create(struct content *c, struct content *parent, - const char *params[]); +bool nsbmp_create(struct content *c, const struct http_parameter *params); bool nsbmp_convert(struct content *c, int width, int height); void nsbmp_destroy(struct content *c); bool nsbmp_redraw(struct content *c, int x, int y, diff --git a/image/gif.c b/image/gif.c index 62d73f0b0..81da8bd5d 100644 --- a/image/gif.c +++ b/image/gif.c @@ -37,7 +37,7 @@ #include <stdlib.h> #include <libnsgif.h> #include "utils/config.h" -#include "content/content.h" +#include "content/content_protected.h" #include "desktop/browser.h" #include "desktop/options.h" #include "desktop/plotters.h" @@ -64,8 +64,7 @@ gif_bitmap_callback_vt gif_bitmap_callbacks = { }; -bool nsgif_create(struct content *c, struct content *parent, - const char *params[]) +bool nsgif_create(struct content *c, const struct http_parameter *params) { union content_msg_data msg_data; /* Initialise our data structure */ @@ -85,26 +84,28 @@ bool nsgif_convert(struct content *c, int iwidth, int iheight) int res; struct gif_animation *gif; union content_msg_data msg_data; + const char *data; + unsigned long size; /* Get the animation */ gif = c->data.gif.gif; + data = content__get_source_data(c, &size); + /* Initialise the GIF */ do { - res = gif_initialise(gif, c->source_size, - (unsigned char *)c->source_data); - if (res != GIF_OK && res != GIF_WORKING && res != GIF_INSUFFICIENT_FRAME_DATA) { - switch (res) - { - case GIF_FRAME_DATA_ERROR: - case GIF_INSUFFICIENT_DATA: - case GIF_DATA_ERROR: - msg_data.error = messages_get("BadGIF"); - break; - case GIF_INSUFFICIENT_MEMORY: - msg_data.error = messages_get( - "NoMemory"); - break; + res = gif_initialise(gif, size, (unsigned char *) data); + if (res != GIF_OK && res != GIF_WORKING && + res != GIF_INSUFFICIENT_FRAME_DATA) { + switch (res) { + case GIF_FRAME_DATA_ERROR: + case GIF_INSUFFICIENT_DATA: + case GIF_DATA_ERROR: + msg_data.error = messages_get("BadGIF"); + break; + case GIF_INSUFFICIENT_MEMORY: + msg_data.error = messages_get("NoMemory"); + break; } content_broadcast(c, CONTENT_MSG_ERROR, msg_data); return false; @@ -125,7 +126,7 @@ bool nsgif_convert(struct content *c, int iwidth, int iheight) c->title = malloc(100); if (c->title) { snprintf(c->title, 100, messages_get("GIFTitle"), c->width, - c->height, c->source_size); + c->height, size); } c->size += (gif->width * gif->height * 4) + 16 + 44 + 100; diff --git a/image/gif.h b/image/gif.h index 0e3ef6605..87f647eba 100644 --- a/image/gif.h +++ b/image/gif.h @@ -31,14 +31,14 @@ #include <libnsgif.h> struct content; +struct http_parameter; struct content_gif_data { struct gif_animation *gif; /**< GIF animation data */ int current_frame; /**< current frame to display [0...(max-1)] */ }; -bool nsgif_create(struct content *c, struct content *parent, - const char *params[]); +bool nsgif_create(struct content *c, const struct http_parameter *params); bool nsgif_convert(struct content *c, int width, int height); void nsgif_destroy(struct content *c); bool nsgif_redraw(struct content *c, int x, int y, diff --git a/image/ico.c b/image/ico.c index e2fce245a..df07d50ed 100644 --- a/image/ico.c +++ b/image/ico.c @@ -29,7 +29,8 @@ #include <stdlib.h> #include <libnsbmp.h> #include "utils/config.h" -#include "content/content.h" +#include "content/content_protected.h" +#include "content/hlcache.h" #include "desktop/plotters.h" #include "image/bitmap.h" #include "image/ico.h" @@ -37,8 +38,7 @@ #include "utils/messages.h" #include "utils/utils.h" -bool nsico_create(struct content *c, struct content *parent, - const char *params[]) +bool nsico_create(struct content *c, const struct http_parameter *params) { union content_msg_data msg_data; c->data.ico.ico = calloc(sizeof(ico_collection), 1); @@ -58,26 +58,29 @@ bool nsico_convert(struct content *c, int iwidth, int iheight) bmp_result res; ico_collection *ico; union content_msg_data msg_data; + const char *data; + unsigned long size; /* set the ico data */ ico = c->data.ico.ico; + data = content__get_source_data(c, &size); + /* analyse the ico */ - res = ico_analyse(ico, c->source_size, (unsigned char *) - c->source_data); + res = ico_analyse(ico, size, (unsigned char *) data); switch (res) { - case BMP_OK: - break; - case BMP_INSUFFICIENT_MEMORY: - msg_data.error = messages_get("NoMemory"); - content_broadcast(c, CONTENT_MSG_ERROR, msg_data); - return false; - case BMP_INSUFFICIENT_DATA: - case BMP_DATA_ERROR: - msg_data.error = messages_get("BadICO"); - content_broadcast(c, CONTENT_MSG_ERROR, msg_data); - return false; + case BMP_OK: + break; + case BMP_INSUFFICIENT_MEMORY: + msg_data.error = messages_get("NoMemory"); + content_broadcast(c, CONTENT_MSG_ERROR, msg_data); + return false; + case BMP_INSUFFICIENT_DATA: + case BMP_DATA_ERROR: + msg_data.error = messages_get("BadICO"); + content_broadcast(c, CONTENT_MSG_ERROR, msg_data); + return false; } /* Store our content width and description */ @@ -86,7 +89,7 @@ bool nsico_convert(struct content *c, int iwidth, int iheight) c->title = malloc(100); if (c->title) snprintf(c->title, 100, messages_get("ICOTitle"), c->width, - c->height, c->source_size); + c->height, size); c->size += (ico->width * ico->height * 4) + 16 + 44 + 100; /* exit as a success */ @@ -117,14 +120,22 @@ bool nsico_redraw(struct content *c, int x, int y, /** sets the bitmap for an ico according to the dimensions */ -bool nsico_set_bitmap_from_size(struct content *c, int width, int height) +bool nsico_set_bitmap_from_size(hlcache_handle *h, int width, int height) { - struct bmp_image *bmp = ico_find(c->data.ico.ico, width, height); + struct content *c = hlcache_handle_get_content(h); + struct bmp_image *bmp; + + assert(c != NULL); + + bmp = ico_find(c->data.ico.ico, width, height); if (bmp == NULL) return false; + if ((bmp->decoded == false) && (bmp_decode(bmp) != BMP_OK)) return false; + c->bitmap = bmp->bitmap; + return true; } diff --git a/image/ico.h b/image/ico.h index f3686c0c5..e25da8361 100644 --- a/image/ico.h +++ b/image/ico.h @@ -30,13 +30,14 @@ #include <libnsbmp.h> struct content; +struct hlcache_handle; +struct http_parameter; struct content_ico_data { struct ico_collection *ico; /** ICO collection data */ }; -bool nsico_create(struct content *c, struct content *parent, - const char *params[]); +bool nsico_create(struct content *c, const struct http_parameter *params); bool nsico_convert(struct content *c, int width, int height); void nsico_destroy(struct content *c); bool nsico_redraw(struct content *c, int x, int y, @@ -48,7 +49,8 @@ bool nsico_redraw_tiled(struct content *c, int x, int y, int clip_x0, int clip_y0, int clip_x1, int clip_y1, float scale, colour background_colour, bool repeat_x, bool repeat_y); -bool nsico_set_bitmap_from_size(struct content *c, int width, int height); +bool nsico_set_bitmap_from_size(struct hlcache_handle *h, + int width, int height); #endif /* WITH_BMP */ diff --git a/image/jpeg.c b/image/jpeg.c index e96faaa67..1d66b05d3 100644 --- a/image/jpeg.c +++ b/image/jpeg.c @@ -27,7 +27,7 @@ #ifdef WITH_JPEG /* This must come first due to libpng issues */ -#include "content/content.h" +#include "content/content_protected.h" #include <assert.h> #include <setjmp.h> @@ -89,6 +89,10 @@ bool nsjpeg_convert(struct content *c, int w, int h) uint8_t * volatile pixels = NULL; size_t rowstride; union content_msg_data msg_data; + const char *data; + unsigned long size; + + data = content__get_source_data(c, &size); cinfo.err = jpeg_std_error(&jerr.pub); jerr.pub.error_exit = nsjpeg_error_exit; @@ -102,8 +106,8 @@ bool nsjpeg_convert(struct content *c, int w, int h) return false; } jpeg_create_decompress(&cinfo); - source_mgr.next_input_byte = (unsigned char *) c->source_data; - source_mgr.bytes_in_buffer = c->source_size; + source_mgr.next_input_byte = (unsigned char *) data; + source_mgr.bytes_in_buffer = size; cinfo.src = &source_mgr; jpeg_read_header(&cinfo, TRUE); cinfo.out_color_space = JCS_RGB; @@ -161,7 +165,7 @@ bool nsjpeg_convert(struct content *c, int w, int h) c->title = malloc(100); if (c->title) snprintf(c->title, 100, messages_get("JPEGTitle"), - width, height, c->source_size); + width, height, size); c->size += height * rowstride + 100; c->status = CONTENT_STATUS_DONE; /* Done: update status bar */ diff --git a/image/mng.c b/image/mng.c index d21d9e879..e76125b9b 100644 --- a/image/mng.c +++ b/image/mng.c @@ -24,7 +24,7 @@ #ifdef WITH_MNG /* This must come first due to libpng issues */ -#include "content/content.h" +#include "content/content_protected.h" #include <assert.h> #include <stdbool.h> @@ -69,8 +69,7 @@ static void nsmng_free(mng_ptr p, mng_size_t n); #endif -bool nsmng_create(struct content *c, struct content *parent, - const char *params[]) +bool nsmng_create(struct content *c, const struct http_parameter *params) { mng_retcode code; union content_msg_data msg_data; @@ -179,6 +178,8 @@ mng_bool nsmng_readdata(mng_handle mng, mng_ptr buffer, mng_uint32 size, mng_uint32 *bytesread) { struct content *c; + const char *data; + unsigned long data_size; assert(mng != NULL); assert(buffer != NULL); @@ -186,17 +187,18 @@ mng_bool nsmng_readdata(mng_handle mng, mng_ptr buffer, mng_uint32 size, /* Get our content back */ - c = (struct content *)mng_get_userdata(mng); + c = (struct content *) mng_get_userdata(mng); assert(c != NULL); /* Copy any data we have (maximum of 'size') */ - *bytesread = ((c->source_size - c->data.mng.read_size) < size) ? - (c->source_size - c->data.mng.read_size) : size; + data = content__get_source_data(c, &data_size); + + *bytesread = ((data_size - c->data.mng.read_size) < size) ? + (data_size - c->data.mng.read_size) : size; if ((*bytesread) > 0) { - memcpy(buffer, c->source_data + c->data.mng.read_size, - *bytesread); + memcpy(buffer, data + c->data.mng.read_size, *bytesread); c->data.mng.read_size += *bytesread; } @@ -302,9 +304,13 @@ bool nsmng_convert(struct content *c, int width, int height) mng_retcode status; union content_msg_data msg_data; + const char *data; + unsigned long size; assert(c != NULL); + data = content__get_source_data(c, &size); + /* by this point, the png should have been parsed * and the bitmap created, so ensure that's the case */ @@ -322,13 +328,13 @@ bool nsmng_convert(struct content *c, int width, int height) if (c->type == CONTENT_MNG) { snprintf(c->title, 100, messages_get("MNGTitle"), - c->width, c->height, c->source_size); + c->width, c->height, size); } else if (c->type == CONTENT_PNG) { snprintf(c->title, 100, messages_get("PNGTitle"), - c->width, c->height, c->source_size); + c->width, c->height, size); } else { snprintf(c->title, 100, messages_get("JNGTitle"), - c->width, c->height, c->source_size); + c->width, c->height, size); } c->size += c->width * c->height * 4 + 100; @@ -658,7 +664,8 @@ mng_bool nsmng_errorproc(mng_handle mng, mng_int32 code, chunk[3] = (char)((chunktype ) & 0xFF); chunk[4] = '\0'; - LOG(("error playing '%s' chunk %s (%d):", c->url, chunk, chunkseq)); + LOG(("error playing '%s' chunk %s (%d):", + content__get_url(c), chunk, chunkseq)); LOG(("code %d severity %d extra1 %d extra2 %d text:'%s'", code, severity, extra1, extra2, text)); diff --git a/image/mng.h b/image/mng.h index 2ea85409c..553fff36f 100644 --- a/image/mng.h +++ b/image/mng.h @@ -29,6 +29,7 @@ #include <stdbool.h> struct content; +struct http_parameter; struct content_mng_data { bool opaque_test_pending; @@ -40,8 +41,7 @@ struct content_mng_data { void *handle; }; -bool nsmng_create(struct content *c, struct content *parent, - const char *params[]); +bool nsmng_create(struct content *c, const struct http_parameter *params); bool nsmng_process_data(struct content *c, char *data, unsigned int size); bool nsmng_convert(struct content *c, int width, int height); void nsmng_destroy(struct content *c); diff --git a/image/nssprite.c b/image/nssprite.c index d5db6b905..9f48ffccf 100644 --- a/image/nssprite.c +++ b/image/nssprite.c @@ -31,7 +31,7 @@ #include "utils/config.h" #include "desktop/plotters.h" #include "image/bitmap.h" -#include "content/content.h" +#include "content/content_protected.h" #include "utils/log.h" #include "utils/messages.h" #include "utils/utils.h" @@ -61,8 +61,13 @@ bool nssprite_convert(struct content *c, int width, int height) union content_msg_data msg_data; struct rosprite_mem_context* ctx; - ERRCHK(rosprite_create_mem_context((uint8_t *) c->source_data, - c->source_size, &ctx)); + + const char *data; + unsigned long size; + + data = content__get_source_data(c, &size); + + ERRCHK(rosprite_create_mem_context((uint8_t *) data, size, &ctx)); struct rosprite_area* sprite_area; ERRCHK(rosprite_load(rosprite_mem_reader, ctx, &sprite_area)); diff --git a/image/png.c b/image/png.c index e8bd55c59..dfb427230 100644 --- a/image/png.c +++ b/image/png.c @@ -30,7 +30,7 @@ #include "desktop/plotters.h" -#include "content/content.h" +#include "content/content_protected.h" #include "image/bitmap.h" @@ -62,8 +62,7 @@ static void row_callback(png_structp png, png_bytep new_row, static void end_callback(png_structp png, png_infop info); -bool nspng_create(struct content *c, struct content *parent, - const char *params[]) +bool nspng_create(struct content *c, const struct http_parameter *params) { union content_msg_data msg_data; @@ -264,16 +263,21 @@ void end_callback(png_structp png, png_infop info) bool nspng_convert(struct content *c, int width, int height) { + const char *data; + unsigned long size; + assert(c->data.png.png != NULL); assert(c->data.png.info != NULL); + data = content__get_source_data(c, &size); + png_destroy_read_struct(&c->data.png.png, &c->data.png.info, 0); c->title = malloc(NSPNG_TITLE_LEN); if (c->title != NULL) { snprintf(c->title, NSPNG_TITLE_LEN, messages_get("PNGTitle"), - c->width, c->height, c->source_size); + c->width, c->height, size); } c->size += (c->width * c->height * 4) + NSPNG_TITLE_LEN; diff --git a/image/png.h b/image/png.h index a5fa6dfae..b940434e7 100644 --- a/image/png.h +++ b/image/png.h @@ -31,6 +31,7 @@ struct content; struct bitmap; +struct http_parameter; struct content_png_data { png_structp png; @@ -41,8 +42,7 @@ struct content_png_data { size_t rowbytes; /**< Number of bytes per row */ }; -bool nspng_create(struct content *c, struct content *parent, - const char *params[]); +bool nspng_create(struct content *c, const struct http_parameter *params); bool nspng_process_data(struct content *c, char *data, unsigned int size); bool nspng_convert(struct content *c, int width, int height); void nspng_destroy(struct content *c); diff --git a/image/rsvg.c b/image/rsvg.c index 86e1d5b66..ea4b58b42 100644 --- a/image/rsvg.c +++ b/image/rsvg.c @@ -38,7 +38,7 @@ #include <librsvg/rsvg-cairo.h> #include "image/rsvg.h" -#include "content/content.h" +#include "content/content_protected.h" #include "desktop/plotters.h" #include "image/bitmap.h" #include "utils/log.h" @@ -49,8 +49,7 @@ static inline void rsvg_argb_to_abgr(uint32_t pixels[], int width, int height, size_t rowstride); -bool rsvg_create(struct content *c, struct content *parent, - const char *params[]) +bool rsvg_create(struct content *c, const struct http_parameter *params) { struct content_rsvg_data *d = &c->data.rsvg; union content_msg_data msg_data; diff --git a/image/rsvg.h b/image/rsvg.h index b8c962787..ae28d3af1 100644 --- a/image/rsvg.h +++ b/image/rsvg.h @@ -33,6 +33,7 @@ #include "image/bitmap.h" struct content; +struct http_parameter; struct content_rsvg_data { RsvgHandle *rsvgh; /**< Context handle for RSVG renderer */ @@ -41,8 +42,7 @@ struct content_rsvg_data { struct bitmap *bitmap; /**< Created NetSurf bitmap */ }; -bool rsvg_create(struct content *c, struct content *parent, - const char *params[]); +bool rsvg_create(struct content *c, const struct http_parameter *params); bool rsvg_process_data(struct content *c, char *data, unsigned int size); bool rsvg_convert(struct content *c, int width, int height); void rsvg_destroy(struct content *c); diff --git a/image/svg.c b/image/svg.c index 4321a9fc3..99c4241eb 100644 --- a/image/svg.c +++ b/image/svg.c @@ -40,7 +40,7 @@ * Create a CONTENT_SVG. */ -bool svg_create(struct content *c, struct content *parent, const char *params[]) +bool svg_create(struct content *c, const struct http_parameter *params) { union content_msg_data msg_data; diff --git a/image/svg.h b/image/svg.h index b191c4cd6..fe9bea3d2 100644 --- a/image/svg.h +++ b/image/svg.h @@ -26,14 +26,14 @@ #include <stdbool.h> struct content; +struct http_parameter; struct svgtiny_diagram; struct content_svg_data { struct svgtiny_diagram *diagram; }; -bool svg_create(struct content *c, struct content *parent, - const char *params[]); +bool svg_create(struct content *c, const struct http_parameter *params); bool svg_convert(struct content *c, int width, int height); void svg_destroy(struct content *c); bool svg_redraw(struct content *c, int x, int y, diff --git a/render/box.c b/render/box.c index b355e68d1..e09a4772c 100644 --- a/render/box.c +++ b/render/box.c @@ -26,7 +26,8 @@ #include <stdbool.h> #include <stdio.h> #include <string.h> -#include "content/content.h" +#include "content/content_protected.h" +#include "content/hlcache.h" #include "css/css.h" #include "css/dump.h" #include "desktop/scroll.h" @@ -312,7 +313,7 @@ void box_bounds(struct box *box, struct rect *r) struct box *box_at_point(struct box *box, const int x, const int y, int *box_x, int *box_y, - struct content **content) + hlcache_handle **content) { int bx = *box_x, by = *box_y; struct box *child, *sibling; @@ -321,11 +322,14 @@ struct box *box_at_point(struct box *box, const int x, const int y, assert(box); /* drill into HTML objects */ - if (box->object) { - if (box->object->type == CONTENT_HTML && - box->object->data.html.layout) { + if (box->object != NULL) { + struct box *layout; + + if (content_get_type(box->object) == CONTENT_HTML && + (layout = html_get_box_tree(box->object)) != + NULL) { *content = box->object; - box = box->object->data.html.layout; + box = layout; } else { goto siblings; } @@ -503,20 +507,24 @@ bool box_contains_point(struct box *box, int x, int y, bool *physically) /** * Find the box containing an object at the given coordinates, if any. * - * \param c content to search, must have type CONTENT_HTML + * \param h content to search, must have type CONTENT_HTML * \param x coordinates in document units * \param y coordinates in document units */ -struct box *box_object_at_point(struct content *c, int x, int y) +struct box *box_object_at_point(hlcache_handle *h, int x, int y) { - struct box *box = c->data.html.layout; + struct content *c = hlcache_handle_get_content(h); + struct box *box; int box_x = 0, box_y = 0; - struct content *content = c; + hlcache_handle *content = h; struct box *object_box = 0; + assert(c != NULL); assert(c->type == CONTENT_HTML); + box = c->data.html.layout; + while ((box = box_at_point(box, x, y, &box_x, &box_y, &content))) { if (box->style && css_computed_visibility(box->style) == CSS_VISIBILITY_HIDDEN) @@ -533,20 +541,24 @@ struct box *box_object_at_point(struct content *c, int x, int y) /** * Find the box containing an href at the given coordinates, if any. * - * \param c content to search, must have type CONTENT_HTML + * \param h content to search, must have type CONTENT_HTML * \param x coordinates in document units * \param y coordinates in document units */ -struct box *box_href_at_point(struct content *c, int x, int y) +struct box *box_href_at_point(hlcache_handle *h, int x, int y) { - struct box *box = c->data.html.layout; + struct content *c = hlcache_handle_get_content(h); + struct box *box; int box_x = 0, box_y = 0; - struct content *content = c; + hlcache_handle *content = h; struct box *href_box = 0; + assert(c != NULL); assert(c->type == CONTENT_HTML); + box = c->data.html.layout; + while ((box = box_at_point(box, x, y, &box_x, &box_y, &content))) { if (box->style && css_computed_visibility(box->style) == CSS_VISIBILITY_HIDDEN) @@ -663,8 +675,10 @@ void box_dump(FILE *stream, struct box *box, unsigned int depth) (int) box->length, box->text); if (box->space) fprintf(stream, "space "); - if (box->object) - fprintf(stream, "(object '%s') ", box->object->url); + if (box->object) { + fprintf(stream, "(object '%s') ", + content_get_url(box->object)); + } if (box->gadget) fprintf(stream, "(gadget) "); if (box->style) @@ -860,42 +874,42 @@ bool box_duplicate_main_tree(struct box *box, struct content *c, int *count) box->last = prev; - if (box->object && option_suppress_images && ( + if (box->object != NULL && option_suppress_images && ( #ifdef WITH_JPEG - box->object->type == CONTENT_JPEG || + content_get_type(box->object) == CONTENT_JPEG || #endif #ifdef WITH_GIF - box->object->type == CONTENT_GIF || + content_get_type(box->object) == CONTENT_GIF || #endif #ifdef WITH_BMP - box->object->type == CONTENT_BMP || - box->object->type == CONTENT_ICO || + content_get_type(box->object) == CONTENT_BMP || + content_get_type(box->object) == CONTENT_ICO || #endif #if defined(WITH_MNG) || defined(WITH_PNG) - box->object->type == CONTENT_PNG || + content_get_type(box->object) == CONTENT_PNG || #endif #ifdef WITH_MNG - box->object->type == CONTENT_JNG || - box->object->type == CONTENT_MNG || + content_get_type(box->object) == CONTENT_JNG || + content_get_type(box->object) == CONTENT_MNG || #endif #if defined(WITH_SPRITE) || defined(WITH_NSSPRITE) - box->object->type == CONTENT_SPRITE || + content_get_type(box->object) == CONTENT_SPRITE || #endif #ifdef WITH_DRAW - box->object->type == CONTENT_DRAW || + content_get_type(box->object) == CONTENT_DRAW || #endif #ifdef WITH_PLUGIN - box->object->type == CONTENT_PLUGIN || + content_get_type(box->object) == CONTENT_PLUGIN || #endif - box->object->type == CONTENT_DIRECTORY || + content_get_type(box->object) == CONTENT_DIRECTORY || #ifdef WITH_THEME_INSTALL - box->object->type == CONTENT_THEME || + content_get_type(box->object) == CONTENT_THEME || #endif #ifdef WITH_ARTWORKS - box->object->type == CONTENT_ARTWORKS || + content_get_type(box->object) == CONTENT_ARTWORKS || #endif #if defined(WITH_NS_SVG) || defined(WITH_RSVG) - box->object->type == CONTENT_SVG || + content_get_type(box->object) == CONTENT_SVG || #endif false)) box->object = NULL; diff --git a/render/box.h b/render/box.h index b53e0481c..665565f2f 100644 --- a/render/box.h +++ b/render/box.h @@ -239,10 +239,10 @@ struct box { char *id; /**< value of id attribute (or name for anchors) */ /** Background image for this box, or 0 if none */ - struct content *background; + struct hlcache_handle *background; /** Object in this box (usually an image), or 0 if none. */ - struct content* object; + struct hlcache_handle* object; /** Parameters for the object, or 0. */ struct object_params *object_params; }; @@ -307,9 +307,9 @@ void box_free_object_params(struct object_params *op); void box_bounds(struct box *box, struct rect *r); void box_coords(struct box *box, int *x, int *y); struct box *box_at_point(struct box *box, const int x, const int y, - int *box_x, int *box_y, struct content **content); -struct box *box_object_at_point(struct content *c, int x, int y); -struct box *box_href_at_point(struct content *c, int x, int y); + int *box_x, int *box_y, struct hlcache_handle **content); +struct box *box_object_at_point(struct hlcache_handle *h, int x, int y); +struct box *box_href_at_point(struct hlcache_handle *h, int x, int y); struct box *box_find_by_id(struct box *box, const char *id); bool box_visible(struct box *box); void box_dump(FILE *stream, struct box *box, unsigned int depth); diff --git a/render/box_construct.c b/render/box_construct.c index bdab6159e..71d30ddae 100644 --- a/render/box_construct.c +++ b/render/box_construct.c @@ -35,7 +35,7 @@ #include <libxml/HTMLparser.h> #include <libxml/parserInternals.h> #include "utils/config.h" -#include "content/content.h" +#include "content/content_protected.h" #include "css/css.h" #include "css/utils.h" #include "css/select.h" @@ -841,9 +841,9 @@ css_computed_style *box_get_style(struct content *c, if ((s = (char *) xmlGetProp(n, (const xmlChar *) "style"))) { inline_style = nscss_create_inline_style( (uint8_t *) s, strlen(s), - c->data.html.encoding, c->url, + c->data.html.encoding, content__get_url(c), c->data.html.quirks != BINDING_QUIRKS_MODE_NONE, - c->data.html.dict, myrealloc, c); + myrealloc, c); xmlFree(s); diff --git a/render/directory.c b/render/directory.c index 82f24efa2..9f002d831 100644 --- a/render/directory.c +++ b/render/directory.c @@ -28,7 +28,7 @@ #include <stdlib.h> #include <sys/stat.h> #include <time.h> -#include "content/content.h" +#include "content/content_protected.h" #include "render/directory.h" #include "render/html.h" #include "utils/messages.h" @@ -40,9 +40,8 @@ static const char header[] = "<html>\n<head>\n<title>\n"; static const char footer[] = "</pre>\n</body>\n</html>\n"; -bool directory_create(struct content *c, struct content *parent, - const char *params[]) { - if (!html_create(c, parent, params)) +bool directory_create(struct content *c, const struct http_parameter *params) { + if (!html_create(c, params)) /* html_create() must have broadcast MSG_ERROR already, so we * don't need to. */ return false; @@ -64,7 +63,7 @@ bool directory_convert(struct content *c, int width, int height) { bool compare; char *up; - path = url_to_path(c->url); + path = url_to_path(content__get_url(c)); if (!path) { msg_data.error = messages_get("NoMemory"); content_broadcast(c, CONTENT_MSG_ERROR, msg_data); @@ -100,9 +99,9 @@ bool directory_convert(struct content *c, int width, int height) { binding_parse_chunk(c->data.html.parser_binding, (uint8_t *) buffer, strlen(buffer)); - res = url_parent(c->url, &up); + res = url_parent(content__get_url(c), &up); if (res == URL_FUNC_OK) { - res = url_compare(c->url, up, false, &compare); + res = url_compare(content__get_url(c), up, false, &compare); if ((res == URL_FUNC_OK) && !compare) { snprintf(buffer, sizeof(buffer), "<a href=\"..\">[..]</a>\n"); @@ -124,7 +123,8 @@ bool directory_convert(struct content *c, int width, int height) { continue; snprintf(buffer, sizeof(buffer), "<a href=\"%s/%s\">%s</a>\n", - c->url, entry->d_name, entry->d_name); + content__get_url(c), entry->d_name, + entry->d_name); binding_parse_chunk(c->data.html.parser_binding, (uint8_t *) buffer, strlen(buffer)); diff --git a/render/directory.h b/render/directory.h index a54d516fb..766593294 100644 --- a/render/directory.h +++ b/render/directory.h @@ -28,9 +28,9 @@ #include <stdbool.h> #include "content/content_type.h" +struct http_parameter; -bool directory_create(struct content *c, struct content *parent, - const char *params[]); +bool directory_create(struct content *c, const struct http_parameter *params); bool directory_convert(struct content *c, int width, int height); void directory_destroy(struct content *c); diff --git a/render/favicon.c b/render/favicon.c index ec5caeae0..191c55e8a 100644 --- a/render/favicon.c +++ b/render/favicon.c @@ -18,8 +18,8 @@ */ #include <string.h> -#include "content/fetch.h" -#include "content/fetchcache.h" +#include "content/content_protected.h" +#include "content/hlcache.h" #include "render/favicon.h" #include "render/html.h" #include "utils/log.h" @@ -29,8 +29,8 @@ #include "utils/utils.h" static char *favicon_get_icon_ref(struct content *c, xmlNode *html); -static void favicon_callback(content_msg msg, struct content *icon, - intptr_t p1, intptr_t p2, union content_msg_data data); +static nserror favicon_callback(hlcache_handle *icon, + const hlcache_event *event, void *pw); /** * retrieve 1 url reference to 1 favicon @@ -39,68 +39,80 @@ static void favicon_callback(content_msg msg, struct content *icon, */ char *favicon_get_icon_ref(struct content *c, xmlNode *html) { - xmlNode *node; - char *rel, *href, *url, *url2; + xmlNode *node = html; + char *rel, *href, *url, *url2 = NULL; url_func_result res; - union content_msg_data msg_data; - url2 = NULL; - node = html; while (node) { - if (node->children) { /* children */ + if (node->children != NULL) { /* children */ node = node->children; - } else if (node->next) { /* siblings */ + } else if (node->next != NULL) { /* siblings */ node = node->next; } else { /* ancestor siblings */ - while (node && !node->next) + while (node != NULL && node->next == NULL) node = node->parent; - if (!node) + + if (node == NULL) break; + node = node->next; } - assert(node); + + assert(node != NULL); if (node->type != XML_ELEMENT_NODE) continue; + if (strcmp((const char *) node->name, "link") == 0) { /* rel=<space separated list, including 'icon'> */ if ((rel = (char *) xmlGetProp(node, (const xmlChar *) "rel")) == NULL) continue; + if (strcasestr(rel, "icon") == 0) { xmlFree(rel); continue; } - LOG(("icon node found")); + if (strcasecmp(rel, "apple-touch-icon") == 0) { xmlFree(rel); continue; } + xmlFree(rel); - if (( href = (char *) xmlGetProp(node, + + if ((href = (char *) xmlGetProp(node, (const xmlChar *) "href")) == NULL) continue; - res = url_join(href, c->data.html.base_url, - &url); + + res = url_join(href, c->data.html.base_url, &url); + xmlFree(href); + if (res != URL_FUNC_OK) continue; - LOG(("most recent favicon '%s'", url)); + if (url2 != NULL) { free(url2); url2 = NULL; } + res = url_normalize(url, &url2); + free(url); + if (res != URL_FUNC_OK) { url2 = NULL; + if (res == URL_FUNC_NOMEM) - goto no_memory; + return NULL; + continue; } } } + if (url2 == NULL) { char *scheme; @@ -123,12 +135,8 @@ char *favicon_get_icon_ref(struct content *c, xmlNode *html) != URL_FUNC_OK) return NULL; } - LOG(("favicon %s", url2)); + return url2; -no_memory: - msg_data.error = messages_get("NoMemory"); - /* content_broadcast(c, CONTENT_MSG_ERROR, msg_data); */ - return false; } /** @@ -140,31 +148,27 @@ no_memory: bool favicon_get_icon(struct content *c, xmlNode *html) { - char *url = favicon_get_icon_ref(c, html); - struct content *favcontent = NULL; + char *url; + nserror error; + + url = favicon_get_icon_ref(c, html); if (url == NULL) return false; - - favcontent = fetchcache(url, favicon_callback, (intptr_t) c, 0, - c->width, c->height, true, 0, 0, false, false); - free(url); - if (favcontent == NULL) - return false; - c->data.html.favicon = favcontent; - - fetchcache_go(favcontent, c->url, favicon_callback, (intptr_t) c, 0, - c->width, c->height, 0, 0, false, c); + error = hlcache_handle_retrieve(url, 0, NULL, NULL, c->width, c->height, + favicon_callback, c, NULL, &c->data.html.favicon); + + free(url); - return true; + return error == NSERROR_OK; } /** * Callback for fetchcache() for linked favicon */ -void favicon_callback(content_msg msg, struct content *icon, - intptr_t p1, intptr_t p2, union content_msg_data data) +nserror favicon_callback(hlcache_handle *icon, + const hlcache_event *event, void *pw) { static const content_type permitted_types[] = { #ifdef WITH_BMP @@ -178,88 +182,57 @@ void favicon_callback(content_msg msg, struct content *icon, #endif CONTENT_UNKNOWN }; - struct content *c = (struct content *) p1; - unsigned int i = p2; + struct content *c = pw; const content_type *type; - - switch (msg) { + switch (event->type) { case CONTENT_MSG_LOADING: /* check that the favicon is really a correct image type */ for (type = permitted_types; *type != CONTENT_UNKNOWN; type++) - if (icon->type == *type) + if (content_get_type(icon) == *type) break; if (*type == CONTENT_UNKNOWN) { - c->data.html.favicon = 0; - LOG(("%s is not a favicon", icon->url)); + union content_msg_data msg_data; + + hlcache_handle_release(c->data.html.favicon); + c->data.html.favicon = NULL; + LOG(("%s is not a favicon", content_get_url(icon))); content_add_error(c, "NotFavIco", 0); - html_set_status(c, messages_get("NotFavIco")); - content_broadcast(c, CONTENT_MSG_STATUS, data); - content_remove_user(icon, - favicon_callback, - (intptr_t) c, i); - if (!icon->user_list->next) { - /* we were the only user and we don't want this - * content, so stop it fetching and mark it as - * having an error so it gets removed from the - * cache next time content_clean() gets called - */ - fetch_abort(icon->fetch); - icon->fetch = 0; - icon->status = CONTENT_STATUS_ERROR; - } + + msg_data.error = messages_get("NotFavIco"); + content_broadcast(c, CONTENT_MSG_STATUS, msg_data); } break; case CONTENT_MSG_READY: - break; - + /* Fall through */ case CONTENT_MSG_DONE: - LOG(("got favicon '%s'", icon->url)); break; - case CONTENT_MSG_LAUNCH: - /* Fall through */ case CONTENT_MSG_ERROR: - LOG(("favicon %s failed: %s", icon->url, data.error)); - /* The favicon we were fetching may have been - * redirected, in that case, the object pointers - * will differ, so ensure that the object that's - * in error is still in use by us before invalidating - * the pointer */ - if (c->data.html.favicon == icon) { - c->data.html.favicon = 0; - content_add_error(c, "?", 0); - } - break; + LOG(("favicon %s failed: %s", + content_get_url(icon), event->data.error)); + hlcache_handle_release(c->data.html.favicon); + c->data.html.favicon = NULL; - case CONTENT_MSG_STATUS: - html_set_status(c, icon->status_message); - content_broadcast(c, CONTENT_MSG_STATUS, data); - break; - - case CONTENT_MSG_NEWPTR: - c->data.html.favicon = icon; - break; - - case CONTENT_MSG_AUTH: - c->data.html.favicon = 0; content_add_error(c, "?", 0); break; - case CONTENT_MSG_SSL: - c->data.html.favicon = 0; - content_add_error(c, "?", 0); + case CONTENT_MSG_STATUS: + content_broadcast(c, CONTENT_MSG_STATUS, event->data); break; + case CONTENT_MSG_REDRAW: - /* currently no support for favicon animations */ + /* Fall through */ case CONTENT_MSG_REFRESH: - break; + /* Fall through */ case CONTENT_MSG_REFORMAT: - /* would be unusual :) */ break; + default: assert(0); } + + return NSERROR_OK; } diff --git a/render/favicon.h b/render/favicon.h index 30030101a..428655ecd 100644 --- a/render/favicon.h +++ b/render/favicon.h @@ -20,7 +20,8 @@ #define _NETSURF_RENDER_FAVICON_H_ #include <libxml/tree.h> -#include "content/content.h" + +struct content; bool favicon_get_icon(struct content *c, xmlNode *html); diff --git a/render/form.c b/render/form.c index 968397a21..b81d2ca34 100644 --- a/render/form.c +++ b/render/form.c @@ -30,6 +30,7 @@ #include <stdbool.h> #include <stdio.h> #include <string.h> +#include "content/fetch.h" #include "css/css.h" #include "css/utils.h" #include "desktop/gui.h" @@ -308,18 +309,18 @@ bool form_add_option(struct form_control *control, char *value, char *text, * \param form form to search for successful controls * \param submit_button control used to submit the form, if any * \param successful_controls updated to point to linked list of - * form_successful_control, 0 if no controls + * fetch_multipart_data, 0 if no controls * \return true on success, false on memory exhaustion * * See HTML 4.01 section 17.13.2. */ bool form_successful_controls(struct form *form, struct form_control *submit_button, - struct form_successful_control **successful_controls) + struct fetch_multipart_data **successful_controls) { struct form_control *control; struct form_option *option; - struct form_successful_control sentinel, *last_success, *success_new; + struct fetch_multipart_data sentinel, *last_success, *success_new; char *value = NULL; bool had_submit = false; char *charset; @@ -603,7 +604,7 @@ bool form_successful_controls(struct form *form, no_memory: warn_user("NoMemory", 0); - form_free_successful(sentinel.next); + fetch_multipart_data_destroy(sentinel.next); return false; #undef ENCODE_ITEM @@ -659,12 +660,12 @@ char *form_textarea_value(struct form_control *textarea) * Encode controls using application/x-www-form-urlencoded. * * \param form form to which successful controls relate - * \param control linked list of form_successful_control + * \param control linked list of fetch_multipart_data * \return URL-encoded form, or 0 on memory exhaustion */ char *form_url_encode(struct form *form, - struct form_successful_control *control) + struct fetch_multipart_data *control) { char *name, *value; char *s = malloc(1), *s2; @@ -713,24 +714,6 @@ char *form_url_encode(struct form *form, return s; } - -/** - * Free a linked list of form_successful_control. - * - * \param control Pointer to head of list to free - */ - -void form_free_successful(struct form_successful_control *control) -{ - struct form_successful_control *next; - for (; control; control = next) { - next = control->next; - free(control->name); - free(control->value); - free(control); - } -} - /** * Find an acceptable character set encoding with which to submit the form * diff --git a/render/form.h b/render/form.h index a31c24975..d5026e039 100644 --- a/render/form.h +++ b/render/form.h @@ -125,14 +125,6 @@ struct form_option { struct form_option* next; }; -/** Successful control, as defined by HTML 4.01 17.13. */ -struct form_successful_control { - bool file; /**< It's a file */ - char *name; /**< Control name. */ - char *value; /**< Current value. */ - struct form_successful_control *next; /**< Next in linked list. */ -}; - /** * Called by the select menu when it wants an area to be redrawn. The * coordinates are menu origin relative. @@ -157,10 +149,9 @@ bool form_add_option(struct form_control *control, char *value, char *text, bool selected); bool form_successful_controls(struct form *form, struct form_control *submit_button, - struct form_successful_control **successful_controls); + struct fetch_multipart_data **successful_controls); char *form_url_encode(struct form *form, - struct form_successful_control *control); -void form_free_successful(struct form_successful_control *control); + struct fetch_multipart_data *control); bool form_open_select_menu(void *client_data, struct form_control *control, diff --git a/render/html.c b/render/html.c index 82becef5c..d63c68764 100644 --- a/render/html.c +++ b/render/html.c @@ -29,9 +29,10 @@ #include <strings.h> #include <stdlib.h> #include "utils/config.h" -#include "content/content.h" +#include "content/content_protected.h" #include "content/fetch.h" #include "content/fetchcache.h" +#include "content/hlcache.h" #include "desktop/browser.h" #include "desktop/gui.h" #include "desktop/options.h" @@ -43,6 +44,7 @@ #include "render/html.h" #include "render/imagemap.h" #include "render/layout.h" +#include "utils/http.h" #include "utils/log.h" #include "utils/messages.h" #include "utils/talloc.h" @@ -57,16 +59,18 @@ #define ALWAYS_DUMP_FRAMESET 0 #define ALWAYS_DUMP_BOX 0 -static void html_convert_css_callback(content_msg msg, struct content *css, - intptr_t p1, intptr_t p2, union content_msg_data data); +static nserror html_convert_css_callback(hlcache_handle *css, + const hlcache_event *event, void *pw); static bool html_meta_refresh(struct content *c, xmlNode *head); static bool html_head(struct content *c, xmlNode *head); static bool html_find_stylesheets(struct content *c, xmlNode *html); static bool html_process_style_element(struct content *c, unsigned int *index, xmlNode *style); -static void html_object_callback(content_msg msg, struct content *object, - intptr_t p1, intptr_t p2, union content_msg_data data); -static void html_object_done(struct box *box, struct content *object, +static bool html_replace_object(struct content *c, unsigned int i, + const char *url); +static nserror html_object_callback(hlcache_handle *object, + const hlcache_event *event, void *pw); +static void html_object_done(struct box *box, hlcache_handle *object, bool background); static void html_object_failed(struct box *box, struct content *content, bool background); @@ -113,57 +117,45 @@ static void *myrealloc(void *ptr, size_t len, void *pw) * created. */ -bool html_create(struct content *c, struct content *parent, - const char *params[]) +bool html_create(struct content *c, const http_parameter *params) { - unsigned int i; struct content_html_data *html = &c->data.html; + const char *charset; union content_msg_data msg_data; binding_error error; - lwc_context *dict; - lwc_error lerror; + nserror nerror; html->parser_binding = NULL; - html->document = 0; + html->document = NULL; html->quirks = BINDING_QUIRKS_MODE_NONE; - html->encoding = 0; - html->base_url = c->url; + html->encoding = NULL; + html->base_url = (char *) content__get_url(c); html->base_target = NULL; - html->layout = 0; + html->layout = NULL; html->background_colour = NS_TRANSPARENT; html->stylesheet_count = 0; html->stylesheets = NULL; html->select_ctx = NULL; html->object_count = 0; - html->object = 0; - html->forms = 0; - html->imagemaps = 0; - html->bw = 0; - html->frameset = 0; - html->iframe = 0; - html->page = 0; + html->object = NULL; + html->forms = NULL; + html->imagemaps = NULL; + html->bw = NULL; + html->frameset = NULL; + html->iframe = NULL; + html->page = NULL; html->index = 0; - html->box = 0; + html->box = NULL; html->font_func = &nsfont; - lerror = lwc_create_context(myrealloc, c, &dict); - if (lerror != lwc_error_ok) { - error = BINDING_NOMEM; - goto error; - } - - html->dict = lwc_context_ref(dict); - - for (i = 0; params[i]; i += 2) { - if (strcasecmp(params[i], "charset") == 0) { - html->encoding = talloc_strdup(c, params[i + 1]); - if (!html->encoding) { - error = BINDING_NOMEM; - goto error; - } - html->encoding_source = ENCODING_SOURCE_HEADER; - break; + nerror = http_parameter_list_find_item(params, "charset", &charset); + if (nerror == NSERROR_OK) { + html->encoding = talloc_strdup(c, charset); + if (!html->encoding) { + error = BINDING_NOMEM; + goto error; } + html->encoding_source = ENCODING_SOURCE_HEADER; } /* Create the parser binding */ @@ -294,10 +286,17 @@ encoding_change: return false; } - /* Recurse to reprocess all that data. This is safe because - * the encoding is now specified at parser-start which means - * it cannot be changed again. */ - return html_process_data(c, c->source_data, c->source_size); + { + const char *source_data; + unsigned long source_size; + + source_data = content__get_source_data(c, &source_size); + + /* Recurse to reprocess all that data. This is safe because + * the encoding is now specified at parser-start which means + * it cannot be changed again. */ + return html_process_data(c, (char *) source_data, source_size); + } } /** @@ -321,11 +320,13 @@ bool html_convert(struct content *c, int width, int height) binding_error err; xmlNode *html, *head; union content_msg_data msg_data; + unsigned long size; unsigned int time_before, time_taken; struct form *f; /* finish parsing */ - if (c->source_size == 0) { + content__get_source_data(c, &size); + if (size == 0) { /* Destroy current binding */ binding_destroy_tree(c->data.html.parser_binding); @@ -502,8 +503,6 @@ bool html_convert(struct content *c, int width, int height) binding_destroy_tree(c->data.html.parser_binding); c->data.html.parser_binding = NULL; - c->size += lwc_context_size(c->data.html.dict); - if (c->active == 0) c->status = CONTENT_STATUS_DONE; else @@ -689,7 +688,7 @@ bool html_meta_refresh(struct content *c, xmlNode *head) /* Just delay specified, so refresh current page */ xmlFree(content); - c->refresh = talloc_strdup(c, c->url); + c->refresh = talloc_strdup(c, content__get_url(c)); if (!c->refresh) { msg_data.error = messages_get("NoMemory"); content_broadcast(c, @@ -808,67 +807,66 @@ bool html_find_stylesheets(struct content *c, xmlNode *html) unsigned int last_active = 0; union content_msg_data msg_data; url_func_result res; - struct nscss_import *stylesheets; + struct html_stylesheet *stylesheets; + hlcache_child_context child; css_error error; + nserror ns_error; + + child.charset = c->data.html.encoding; + child.quirks = c->quirks; /* stylesheet 0 is the base style sheet, * stylesheet 1 is the quirks mode style sheet, * stylesheet 2 is the adblocking stylesheet */ - c->data.html.stylesheets = talloc_array(c, struct nscss_import, + c->data.html.stylesheets = talloc_array(c, struct html_stylesheet, STYLESHEET_START); if (c->data.html.stylesheets == NULL) goto no_memory; - c->data.html.stylesheets[STYLESHEET_BASE].c = NULL; - c->data.html.stylesheets[STYLESHEET_BASE].media = CSS_MEDIA_ALL; - c->data.html.stylesheets[STYLESHEET_QUIRKS].c = NULL; - c->data.html.stylesheets[STYLESHEET_QUIRKS].media = CSS_MEDIA_ALL; - c->data.html.stylesheets[STYLESHEET_ADBLOCK].c = NULL; - c->data.html.stylesheets[STYLESHEET_ADBLOCK].media = CSS_MEDIA_ALL; + c->data.html.stylesheets[STYLESHEET_BASE].type = + HTML_STYLESHEET_EXTERNAL; + c->data.html.stylesheets[STYLESHEET_BASE].data.external = NULL; + c->data.html.stylesheets[STYLESHEET_QUIRKS].type = + HTML_STYLESHEET_EXTERNAL; + c->data.html.stylesheets[STYLESHEET_QUIRKS].data.external = NULL; + c->data.html.stylesheets[STYLESHEET_ADBLOCK].type = + HTML_STYLESHEET_EXTERNAL; + c->data.html.stylesheets[STYLESHEET_ADBLOCK].data.external = NULL; c->data.html.stylesheet_count = STYLESHEET_START; c->active = 0; - c->data.html.stylesheets[STYLESHEET_BASE].c = fetchcache( - default_stylesheet_url, - html_convert_css_callback, (intptr_t) c, - STYLESHEET_BASE, c->width, c->height, - true, 0, 0, false, false); - if (c->data.html.stylesheets[STYLESHEET_BASE].c == NULL) + ns_error = hlcache_handle_retrieve(default_stylesheet_url, 0, + content__get_url(c), NULL, c->width, c->height, + html_convert_css_callback, c, &child, + &c->data.html.stylesheets[ + STYLESHEET_BASE].data.external); + if (ns_error != NSERROR_OK) goto no_memory; + c->active++; - fetchcache_go(c->data.html.stylesheets[STYLESHEET_BASE].c, - c->url, html_convert_css_callback, (intptr_t) c, - STYLESHEET_BASE, c->width, c->height, - 0, 0, false, c); if (c->data.html.quirks == BINDING_QUIRKS_MODE_FULL) { - c->data.html.stylesheets[STYLESHEET_QUIRKS].c = - fetchcache(quirks_stylesheet_url, - html_convert_css_callback, (intptr_t) c, - STYLESHEET_QUIRKS, c->width, c->height, - true, 0, 0, false, false); - if (c->data.html.stylesheets[STYLESHEET_QUIRKS].c == NULL) + ns_error = hlcache_handle_retrieve(quirks_stylesheet_url, 0, + content__get_url(c), NULL, c->width, c->height, + html_convert_css_callback, c, &child, + &c->data.html.stylesheets[ + STYLESHEET_QUIRKS].data.external); + if (ns_error != NSERROR_OK) goto no_memory; + c->active++; - fetchcache_go(c->data.html.stylesheets[STYLESHEET_QUIRKS].c, - c->url, html_convert_css_callback, - (intptr_t) c, STYLESHEET_QUIRKS, c->width, - c->height, 0, 0, false, c); } if (option_block_ads) { - c->data.html.stylesheets[STYLESHEET_ADBLOCK].c = - fetchcache(adblock_stylesheet_url, - html_convert_css_callback, (intptr_t) c, - STYLESHEET_ADBLOCK, c->width, - c->height, true, 0, 0, false, false); - if (c->data.html.stylesheets[STYLESHEET_ADBLOCK].c == NULL) + ns_error = hlcache_handle_retrieve(adblock_stylesheet_url, 0, + content__get_url(c), NULL, c->width, c->height, + html_convert_css_callback, c, &child, + &c->data.html.stylesheets[ + STYLESHEET_ADBLOCK].data.external); + if (ns_error != NSERROR_OK) goto no_memory; + c->active++; - fetchcache_go(c->data.html.stylesheets[STYLESHEET_ADBLOCK].c, - c->url, html_convert_css_callback, - (intptr_t) c, STYLESHEET_ADBLOCK, c->width, - c->height, 0, 0, false, c); } node = html; @@ -958,29 +956,30 @@ bool html_find_stylesheets(struct content *c, xmlNode *html) /* start fetch */ stylesheets = talloc_realloc(c, c->data.html.stylesheets, - struct nscss_import, i + 1); + struct html_stylesheet, i + 1); if (stylesheets == NULL) { free(url2); goto no_memory; } c->data.html.stylesheets = stylesheets; - /** \todo Reflect actual media specified in link */ - c->data.html.stylesheets[i].media = CSS_MEDIA_ALL; - c->data.html.stylesheets[i].c = fetchcache(url2, - html_convert_css_callback, - (intptr_t) c, i, c->width, c->height, - true, 0, 0, false, false); + c->data.html.stylesheet_count++; + c->data.html.stylesheets[i].type = + HTML_STYLESHEET_EXTERNAL; + ns_error = hlcache_handle_retrieve(url2, 0, + content__get_url(c), NULL, + c->width, c->height, + html_convert_css_callback, c, &child, + &c->data.html.stylesheets[i]. + data.external); + free(url2); - if (c->data.html.stylesheets[i].c == NULL) + + if (ns_error != NSERROR_OK) goto no_memory; c->active++; - fetchcache_go(c->data.html.stylesheets[i].c, - c->url, - html_convert_css_callback, - (intptr_t) c, i, c->width, c->height, - 0, 0, false, c); + i++; } else if (strcmp((const char *) node->name, "style") == 0) { if (!html_process_style_element(c, &i, node)) @@ -988,7 +987,7 @@ bool html_find_stylesheets(struct content *c, xmlNode *html) } } - c->data.html.stylesheet_count = i; + assert(c->data.html.stylesheet_count == i); /* complete the fetches */ while (c->active != 0) { @@ -1002,7 +1001,7 @@ bool html_find_stylesheets(struct content *c, xmlNode *html) } /* check that the base stylesheet loaded; layout fails without it */ - if (c->data.html.stylesheets[STYLESHEET_BASE].c == NULL) { + if (c->data.html.stylesheets[STYLESHEET_BASE].data.external == NULL) { msg_data.error = "Base stylesheet failed to load"; content_broadcast(c, CONTENT_MSG_ERROR, msg_data); return false; @@ -1015,11 +1014,30 @@ bool html_find_stylesheets(struct content *c, xmlNode *html) /* Add sheets to it */ for (i = STYLESHEET_BASE; i != c->data.html.stylesheet_count; i++) { - if (c->data.html.stylesheets[i].c != NULL) { + const struct html_stylesheet *hsheet = + &c->data.html.stylesheets[i]; + css_stylesheet *sheet; + css_origin origin = CSS_ORIGIN_AUTHOR; + + if (i < STYLESHEET_START) + origin = CSS_ORIGIN_UA; + + if (hsheet->type == HTML_STYLESHEET_EXTERNAL && + hsheet->data.external != NULL) { + struct content *s = hlcache_handle_get_content( + hsheet->data.external); + + sheet = s-> data.css.sheet; + } else if (hsheet->type == HTML_STYLESHEET_INTERNAL) { + sheet = hsheet->data.internal->sheet; + } else { + sheet = NULL; + } + + if (sheet != NULL) { error = css_select_ctx_append_sheet( - c->data.html.select_ctx, - c->data.html.stylesheets[i].c-> - data.css.sheet); + c->data.html.select_ctx, sheet, + origin, CSS_MEDIA_SCREEN); if (error != CSS_OK) goto no_memory; } @@ -1050,9 +1068,9 @@ bool html_process_style_element(struct content *c, unsigned int *index, xmlNode *child; char *type, *media, *data; union content_msg_data msg_data; - struct nscss_import *stylesheets; - struct nscss_import *sheet; - const char *params[] = { 0 }; + struct html_stylesheet *stylesheets; + struct content_css_data *sheet; + nserror error; /* type='text/css', or not present (invalid but common) */ if ((type = (char *) xmlGetProp(style, (const xmlChar *) "type"))) { @@ -1075,26 +1093,27 @@ bool html_process_style_element(struct content *c, unsigned int *index, /* Extend array */ stylesheets = talloc_realloc(c, c->data.html.stylesheets, - struct nscss_import, *index + 1); + struct html_stylesheet, *index + 1); if (stylesheets == NULL) goto no_memory; c->data.html.stylesheets = stylesheets; + c->data.html.stylesheet_count++; - /* create stylesheet */ - sheet = &c->data.html.stylesheets[(*index)]; + c->data.html.stylesheets[(*index)].type = HTML_STYLESHEET_INTERNAL; + c->data.html.stylesheets[(*index)].data.internal = NULL; - /** \todo Reflect specified media */ - sheet->media = CSS_MEDIA_ALL; - sheet->c = content_create(c->data.html.base_url); - if (sheet->c == NULL) + /* create stylesheet */ + sheet = talloc(c, struct content_css_data); + if (sheet == NULL) { + c->data.html.stylesheet_count--; goto no_memory; + } - if (content_set_type(sheet->c, - CONTENT_CSS, "text/css", params, c) == false) { - /** \todo not necessarily caused by - * memory exhaustion */ - sheet->c = NULL; + error = nscss_create_css_data(sheet, + c->data.html.base_url, NULL, c->data.html.quirks); + if (error != NSERROR_OK) { + c->data.html.stylesheet_count--; goto no_memory; } @@ -1103,43 +1122,29 @@ bool html_process_style_element(struct content *c, unsigned int *index, * the content */ for (child = style->children; child != 0; child = child->next) { data = (char *) xmlNodeGetContent(child); - if (content_process_data(sheet->c, data, strlen(data)) == + if (nscss_process_css_data(sheet, data, strlen(data)) == false) { xmlFree(data); + nscss_destroy_css_data(sheet); + talloc_free(sheet); + c->data.html.stylesheet_count--; /** \todo not necessarily caused by * memory exhaustion */ - sheet->c = NULL; goto no_memory; } xmlFree(data); } /* Convert the content -- manually, as we want the result */ - if (sheet->c->source_allocated != sheet->c->source_size) { - /* Minimise source data block */ - char *data = talloc_realloc(sheet->c, sheet->c->source_data, - char, sheet->c->source_size); - - if (data != NULL) { - sheet->c->source_data = data; - sheet->c->source_allocated = sheet->c->source_size; - } - } - - if (nscss_convert(sheet->c, c->width, c->height)) { - if (content_add_user(sheet->c, - html_convert_css_callback, - (intptr_t) c, (*index)) == false) { - /* no memory */ - sheet->c = NULL; - goto no_memory; - } - } else { + if (nscss_convert_css_data(sheet, c->width, c->height) != CSS_OK) { /* conversion failed */ - sheet->c = NULL; + nscss_destroy_css_data(sheet); + talloc_free(sheet); + sheet = NULL; } /* Update index */ + c->data.html.stylesheets[(*index)].data.internal = sheet; (*index)++; return true; @@ -1155,36 +1160,40 @@ no_memory: * Callback for fetchcache() for linked stylesheets. */ -void html_convert_css_callback(content_msg msg, struct content *css, - intptr_t p1, intptr_t p2, union content_msg_data data) +nserror html_convert_css_callback(hlcache_handle *css, + const hlcache_event *event, void *pw) { - struct content *c = (struct content *) p1; - unsigned int i = p2; + struct content *parent = pw; + unsigned int i; + struct html_stylesheet *s; + + /* Find sheet */ + for (i = 0, s = parent->data.html.stylesheets; + i != parent->data.html.stylesheet_count; i++, s++) { + if (s->type == HTML_STYLESHEET_EXTERNAL && + s->data.external == css) + break; + } + + assert(i != parent->data.html.stylesheet_count); - switch (msg) { + switch (event->type) { case CONTENT_MSG_LOADING: /* check that the stylesheet is really CSS */ - if (css->type != CONTENT_CSS) { - c->data.html.stylesheets[i].c = NULL; - c->active--; - LOG(("%s is not CSS", css->url)); - content_add_error(c, "NotCSS", 0); - html_set_status(c, messages_get("NotCSS")); - content_broadcast(c, CONTENT_MSG_STATUS, data); - content_remove_user(css, - html_convert_css_callback, - (intptr_t) c, i); - if (css->user_list->next == NULL) { - /* we were the only user and we - * don't want this content, so - * stop it fetching and mark it - * as having an error so it gets - * removed from the cache next time - * content_clean() gets called */ - fetch_abort(css->fetch); - css->fetch = 0; - css->status = CONTENT_STATUS_ERROR; - } + if (content_get_type(css) != CONTENT_CSS) { + hlcache_handle_release(css); + s->data.external = NULL; + + parent->active--; + + LOG(("%s is not CSS", content_get_url(css))); + + content_add_error(parent, "NotCSS", 0); + + html_set_status(parent, messages_get("NotCSS")); + + content_broadcast(parent, CONTENT_MSG_STATUS, + event->data); } break; @@ -1192,50 +1201,29 @@ void html_convert_css_callback(content_msg msg, struct content *css, break; case CONTENT_MSG_DONE: - LOG(("got stylesheet '%s'", css->url)); - c->active--; + LOG(("got stylesheet '%s'", content_get_url(css))); + parent->active--; break; - case CONTENT_MSG_LAUNCH: - /* Fall through */ case CONTENT_MSG_ERROR: - LOG(("stylesheet %s failed: %s", css->url, data.error)); - /* The stylesheet we were fetching may have been - * redirected, in that case, the object pointers - * will differ, so ensure that the object that's - * in error is still in use by us before invalidating - * the pointer */ - if (c->data.html.stylesheets[i].c == css) { - c->data.html.stylesheets[i].c = NULL; - c->active--; - content_add_error(c, "?", 0); - } + LOG(("stylesheet %s failed: %s", + content_get_url(css), event->data.error)); + hlcache_handle_release(css); + s->data.external = NULL; + parent->active--; + content_add_error(parent, "?", 0); break; case CONTENT_MSG_STATUS: - html_set_status(c, css->status_message); - content_broadcast(c, CONTENT_MSG_STATUS, data); - break; - - case CONTENT_MSG_NEWPTR: - c->data.html.stylesheets[i].c = css; - break; - - case CONTENT_MSG_AUTH: - c->data.html.stylesheets[i].c = NULL; - c->active--; - content_add_error(c, "?", 0); - break; - - case CONTENT_MSG_SSL: - c->data.html.stylesheets[i].c = NULL; - c->active--; - content_add_error(c, "?", 0); + html_set_status(parent, content_get_status_message(css)); + content_broadcast(parent, CONTENT_MSG_STATUS, event->data); break; default: assert(0); } + + return NSERROR_OK; } @@ -1260,9 +1248,14 @@ bool html_fetch_object(struct content *c, const char *url, struct box *box, { unsigned int i = c->data.html.object_count; struct content_html_object *object; - struct content *c_fetch; + hlcache_handle *c_fetch; + hlcache_child_context child; char *url2; url_func_result res; + nserror error; + + child.charset = c->data.html.encoding; + child.quirks = c->quirks; /* Normalize the URL */ res = url_normalize(url, &url2); @@ -1271,23 +1264,22 @@ bool html_fetch_object(struct content *c, const char *url, struct box *box, return res != URL_FUNC_NOMEM; } - /* initialise fetch */ - c_fetch = fetchcache(url2, html_object_callback, - (intptr_t) c, i, available_width, available_height, - true, 0, 0, false, false); + error = hlcache_handle_retrieve(url2, 0, content__get_url(c), NULL, + available_width, available_height, + html_object_callback, c, &child, + &c_fetch); /* No longer need normalized url */ free(url2); - if (!c_fetch) + if (error != NSERROR_OK) return false; /* add to object list */ object = talloc_realloc(c, c->data.html.object, struct content_html_object, i + 1); - if (!object) { - content_remove_user(c_fetch, html_object_callback, - (intptr_t) c, i); + if (object == NULL) { + hlcache_handle_release(c_fetch); return false; } c->data.html.object = object; @@ -1298,12 +1290,6 @@ bool html_fetch_object(struct content *c, const char *url, struct box *box, c->data.html.object_count++; c->active++; - /* start fetch */ - fetchcache_go(c_fetch, c->url, - html_object_callback, (intptr_t) c, i, - available_width, available_height, - 0, 0, false, c); - return true; } @@ -1314,31 +1300,31 @@ bool html_fetch_object(struct content *c, const char *url, struct box *box, * \param c content of type CONTENT_HTML * \param i index of object to replace in c->data.html.object * \param url URL of object to fetch (copied) - * \param post_urlenc url encoded post data, or 0 if none - * \param post_multipart multipart post data, or 0 if none * \return true on success, false on memory exhaustion */ -bool html_replace_object(struct content *c, unsigned int i, char *url, - char *post_urlenc, - struct form_successful_control *post_multipart) +bool html_replace_object(struct content *c, unsigned int i, const char *url) { - struct content *c_fetch; + hlcache_handle *c_fetch; + hlcache_child_context child; struct content *page; char *url2; url_func_result res; + nserror error; assert(c->type == CONTENT_HTML); + child.charset = c->data.html.encoding; + child.quirks = c->quirks; + if (c->data.html.object[i].content) { /* remove existing object */ - if (c->data.html.object[i].content->status != + if (content_get_status(c->data.html.object[i].content) != CONTENT_STATUS_DONE) c->active--; - content_remove_user(c->data.html.object[i].content, - html_object_callback, (intptr_t) c, i); - c->data.html.object[i].content = 0; - c->data.html.object[i].box->object = 0; + hlcache_handle_release(c->data.html.object[i].content); + c->data.html.object[i].content = NULL; + c->data.html.object[i].box->object = NULL; } res = url_normalize(url, &url2); @@ -1346,15 +1332,15 @@ bool html_replace_object(struct content *c, unsigned int i, char *url, return res != URL_FUNC_NOMEM; /* initialise fetch */ - c_fetch = fetchcache(url2, html_object_callback, - (intptr_t) c, i, + error = hlcache_handle_retrieve(url2, 0, content__get_url(c), NULL, c->data.html.object[i].box->width, c->data.html.object[i].box->height, - false, post_urlenc, post_multipart, false, false); + html_object_callback, c, &child, + &c_fetch); free(url2); - if (!c_fetch) + if (error != NSERROR_OK) return false; c->data.html.object[i].content = c_fetch; @@ -1365,13 +1351,6 @@ bool html_replace_object(struct content *c, unsigned int i, char *url, page->status = CONTENT_STATUS_READY; } - /* start fetch */ - fetchcache_go(c_fetch, c->url, - html_object_callback, (intptr_t) c, i, - c->data.html.object[i].box->width, - c->data.html.object[i].box->height, - post_urlenc, post_multipart, false, c); - return true; } @@ -1380,157 +1359,146 @@ bool html_replace_object(struct content *c, unsigned int i, char *url, * Callback for fetchcache() for objects. */ -void html_object_callback(content_msg msg, struct content *object, - intptr_t p1, intptr_t p2, union content_msg_data data) +nserror html_object_callback(hlcache_handle *object, + const hlcache_event *event, void *pw) { - struct content *c = (struct content *) p1; - unsigned int i = p2; + struct content *c = pw; + unsigned int i; + struct content_html_object *o; int x, y; - struct box *box = c->data.html.object[i].box; - - switch (msg) { - case CONTENT_MSG_LOADING: - /* check if the type is acceptable for this object */ - if (html_object_type_permitted(object->type, - c->data.html.object[i].permitted_types)) { - if (c->data.html.bw) - content_open(object, - c->data.html.bw, c, - i, box, - box->object_params); - break; - } + struct box *box; - /* not acceptable */ - c->data.html.object[i].content = 0; - c->active--; - content_add_error(c, "?", 0); - html_set_status(c, messages_get("BadObject")); - content_broadcast(c, CONTENT_MSG_STATUS, data); - content_remove_user(object, html_object_callback, - (intptr_t) c, i); - if (!object->user_list->next) { - /* we were the only user and we - * don't want this content, so - * stop it fetching and mark it - * as having an error so it gets - * removed from the cache next time - * content_clean() gets called */ - fetch_abort(object->fetch); - object->fetch = 0; - object->status = CONTENT_STATUS_ERROR; - } - html_object_failed(box, c, - c->data.html.object[i].background); + /* Find object record in parent */ + for (i = 0, o = c->data.html.object; i != c->data.html.object_count; + i++, o++) { + if (o->content == object) break; + } - case CONTENT_MSG_READY: - if (object->type == CONTENT_HTML) { - html_object_done(box, object, - c->data.html.object[i].background); - if (c->status == CONTENT_STATUS_READY || - c->status == - CONTENT_STATUS_DONE) - content_reformat(c, - c->available_width, - c->height); - } - break; + assert(i != c->data.html.object_count); - case CONTENT_MSG_DONE: - html_object_done(box, object, - c->data.html.object[i].background); - c->active--; - break; + box = o->box; - case CONTENT_MSG_LAUNCH: - /* Fall through */ - case CONTENT_MSG_ERROR: - /* The object we were fetching may have been - * redirected, in that case, the object pointers - * will differ, so ensure that the object that's - * in error is still in use by us before invalidating - * the pointer */ - if (c->data.html.object[i].content == object) { - c->data.html.object[i].content = 0; - c->active--; - content_add_error(c, "?", 0); - html_set_status(c, data.error); - content_broadcast(c, CONTENT_MSG_STATUS, - data); - html_object_failed(box, c, - c->data.html.object[i].background); - } + switch (event->type) { + case CONTENT_MSG_LOADING: + /* check if the type is acceptable for this object */ + if (html_object_type_permitted(content_get_type(object), + o->permitted_types)) { + if (c->data.html.bw != NULL) + content_open(object, + c->data.html.bw, c, + i, box, + box->object_params); break; + } - case CONTENT_MSG_STATUS: - html_set_status(c, object->status_message); - /* content_broadcast(c, CONTENT_MSG_STATUS, 0); */ - break; + /* not acceptable */ + hlcache_handle_release(object); - case CONTENT_MSG_REFORMAT: - break; + o->content = NULL; - case CONTENT_MSG_REDRAW: - if (!box_visible(box)) - break; - box_coords(box, &x, &y); - if (object == data.redraw.object) { - data.redraw.x = data.redraw.x * - box->width / object->width; - data.redraw.y = data.redraw.y * - box->height / object->height; - data.redraw.width = data.redraw.width * - box->width / object->width; - data.redraw.height = data.redraw.height * - box->height / object->height; - data.redraw.object_width = box->width; - data.redraw.object_height = box->height; - } - data.redraw.x += x + box->padding[LEFT]; - data.redraw.y += y + box->padding[TOP]; - data.redraw.object_x += x + box->padding[LEFT]; - data.redraw.object_y += y + box->padding[TOP]; - content_broadcast(c, CONTENT_MSG_REDRAW, data); - break; + c->active--; - case CONTENT_MSG_NEWPTR: - c->data.html.object[i].content = object; - break; + content_add_error(c, "?", 0); + html_set_status(c, messages_get("BadObject")); + content_broadcast(c, CONTENT_MSG_STATUS, event->data); - case CONTENT_MSG_AUTH: - c->data.html.object[i].content = 0; - c->active--; - content_add_error(c, "?", 0); - break; + html_object_failed(box, c, + c->data.html.object[i].background); + break; - case CONTENT_MSG_SSL: - c->data.html.object[i].content = 0; - c->active--; - content_add_error(c, "?", 0); - break; + case CONTENT_MSG_READY: + if (content_get_type(object) == CONTENT_HTML) { + html_object_done(box, object, o->background); + if (c->status == CONTENT_STATUS_READY || + c->status == CONTENT_STATUS_DONE) + content__reformat(c, + c->available_width, + c->height); + } + break; + + case CONTENT_MSG_DONE: + html_object_done(box, object, o->background); + c->active--; + break; + + case CONTENT_MSG_ERROR: + hlcache_handle_release(object); + + o->content = NULL; + + c->active--; + + content_add_error(c, "?", 0); + html_set_status(c, event->data.error); + content_broadcast(c, CONTENT_MSG_STATUS, event->data); + html_object_failed(box, c, o->background); + break; + + case CONTENT_MSG_STATUS: + html_set_status(c, content_get_status_message(object)); + /* content_broadcast(c, CONTENT_MSG_STATUS, 0); */ + break; + + case CONTENT_MSG_REFORMAT: + break; - case CONTENT_MSG_REFRESH: - if (object->type == CONTENT_HTML) - /* only for HTML objects */ - schedule(data.delay * 100, - html_object_refresh, object); + case CONTENT_MSG_REDRAW: + { + union content_msg_data data = event->data; + + if (!box_visible(box)) break; - default: - assert(0); + box_coords(box, &x, &y); + + if (hlcache_handle_get_content(object) == + event->data.redraw.object) { + data.redraw.x = data.redraw.x * + box->width / content_get_width(object); + data.redraw.y = data.redraw.y * + box->height / + content_get_height(object); + data.redraw.width = data.redraw.width * + box->width / content_get_width(object); + data.redraw.height = data.redraw.height * + box->height / + content_get_height(object); + data.redraw.object_width = box->width; + data.redraw.object_height = box->height; + } + + data.redraw.x += x + box->padding[LEFT]; + data.redraw.y += y + box->padding[TOP]; + data.redraw.object_x += x + box->padding[LEFT]; + data.redraw.object_y += y + box->padding[TOP]; + + content_broadcast(c, CONTENT_MSG_REDRAW, data); + } + break; + + case CONTENT_MSG_REFRESH: + if (content_get_type(object) == CONTENT_HTML) + /* only for HTML objects */ + schedule(event->data.delay * 100, + html_object_refresh, object); + break; + + default: + assert(0); } if (c->status == CONTENT_STATUS_READY && c->active == 0 && - (msg == CONTENT_MSG_LOADING || - msg == CONTENT_MSG_DONE || - msg == CONTENT_MSG_ERROR || - msg == CONTENT_MSG_AUTH)) { + (event->type == CONTENT_MSG_LOADING || + event->type == CONTENT_MSG_DONE || + event->type == CONTENT_MSG_ERROR)) { /* all objects have arrived */ - content_reformat(c, c->available_width, c->height); + content__reformat(c, c->available_width, c->height); html_set_status(c, ""); content_set_done(c); } + /* If 1) the configuration option to reflow pages while objects are * fetched is set * 2) an object is newly fetched & converted, @@ -1538,17 +1506,20 @@ void html_object_callback(content_msg msg, struct content *object, * 4) the time since the previous reformat is more than the * configured minimum time between reformats * then reformat the page to display newly fetched objects */ - else if (option_incremental_reflow && msg == CONTENT_MSG_DONE && + else if (option_incremental_reflow && + event->type == CONTENT_MSG_DONE && (c->status == CONTENT_STATUS_READY || c->status == CONTENT_STATUS_DONE) && (wallclock() > c->reformat_time)) { unsigned int time_before = wallclock(), time_taken; - content_reformat(c, c->available_width, c->height); + content__reformat(c, c->available_width, c->height); time_taken = wallclock() - time_before; c->reformat_time = wallclock() + ((time_taken < option_min_reflow_period ? option_min_reflow_period : time_taken * 1.25)); } + + return NSERROR_OK; } @@ -1556,7 +1527,7 @@ void html_object_callback(content_msg msg, struct content *object, * Update a box whose content has completed rendering. */ -void html_object_done(struct box *box, struct content *object, +void html_object_done(struct box *box, hlcache_handle *object, bool background) { struct box *b; @@ -1714,7 +1685,7 @@ void html_object_refresh(void *p) c->fresh = false; if (!html_replace_object(c->data.html.page, c->data.html.index, - c->refresh, 0, 0)) { + c->refresh)) { /** \todo handle memory exhaustion */ } } @@ -1726,24 +1697,22 @@ void html_object_refresh(void *p) void html_stop(struct content *c) { unsigned int i; - struct content *object; + hlcache_handle *object; assert(c->status == CONTENT_STATUS_READY); for (i = 0; i != c->data.html.object_count; i++) { object = c->data.html.object[i].content; - if (!object) + if (object == NULL) continue; - if (object->status == CONTENT_STATUS_DONE) + if (content_get_status(object) == CONTENT_STATUS_DONE) ; /* already loaded: do nothing */ - else if (object->status == CONTENT_STATUS_READY) - content_stop(object, html_object_callback, - (intptr_t) c, i); + else if (content_get_status(object) == CONTENT_STATUS_READY) + content_stop(object, html_object_callback, NULL); else { - content_remove_user(c->data.html.object[i].content, - html_object_callback, (intptr_t) c, i); - c->data.html.object[i].content = 0; + hlcache_handle_release(object); + c->data.html.object[i].content = NULL; } } c->status = CONTENT_STATUS_DONE; @@ -1836,11 +1805,14 @@ void html_destroy(struct content *c) /* Free stylesheets */ if (c->data.html.stylesheet_count) { for (i = 0; i != c->data.html.stylesheet_count; i++) { - if (c->data.html.stylesheets[i].c) - content_remove_user(c->data.html. - stylesheets[i].c, - html_convert_css_callback, - (intptr_t) c, i); + if (c->data.html.stylesheets[i].type == + HTML_STYLESHEET_EXTERNAL) { + hlcache_handle_release(c->data.html. + stylesheets[i].data.external); + } else { + nscss_destroy_css_data(c->data.html. + stylesheets[i].data.internal); + } } } @@ -1848,15 +1820,14 @@ void html_destroy(struct content *c) for (i = 0; i != c->data.html.object_count; i++) { LOG(("object %i %p", i, c->data.html.object[i].content)); if (c->data.html.object[i].content) { - content_remove_user(c->data.html.object[i].content, - html_object_callback, (intptr_t) c, i); - if (c->data.html.object[i].content->type == CONTENT_HTML) + if (content_get_type(c->data.html.object[i].content) == + CONTENT_HTML) schedule_remove(html_object_refresh, c->data.html.object[i].content); + + hlcache_handle_release(c->data.html.object[i].content); } } - - lwc_context_unref(c->data.html.dict); } void html_destroy_frameset(struct content_html_frames *frameset) { @@ -1942,7 +1913,8 @@ void html_open(struct content *c, struct browser_window *bw, for (i = 0; i != c->data.html.object_count; i++) { if (c->data.html.object[i].content == 0) continue; - if (c->data.html.object[i].content->type == CONTENT_UNKNOWN) + if (content_get_type(c->data.html.object[i].content) == + CONTENT_UNKNOWN) continue; content_open(c->data.html.object[i].content, bw, c, i, @@ -1964,7 +1936,8 @@ void html_close(struct content *c) for (i = 0; i != c->data.html.object_count; i++) { if (c->data.html.object[i].content == 0) continue; - if (c->data.html.object[i].content->type == CONTENT_UNKNOWN) + if (content_get_type(c->data.html.object[i].content) == + CONTENT_UNKNOWN) continue; content_close(c->data.html.object[i].content); } @@ -2023,3 +1996,173 @@ void html_dump_frameset(struct content_html_frames *frame, } #endif + +/** + * Retrieve HTML document tree + * + * \param h HTML content to retrieve document tree from + * \return Pointer to document tree + */ +xmlDoc *html_get_document(hlcache_handle *h) +{ + struct content *c = hlcache_handle_get_content(h); + + assert(c != NULL); + assert(c->type == CONTENT_HTML); + + return c->data.html.document; +} + +/** + * Retrieve box tree + * + * \param h HTML content to retrieve tree from + * \return Pointer to box tree + * + * \todo This API must die, as must all use of the box tree outside render/ + */ +struct box *html_get_box_tree(hlcache_handle *h) +{ + struct content *c = hlcache_handle_get_content(h); + + assert(c != NULL); + assert(c->type == CONTENT_HTML); + + return c->data.html.layout; +} + +/** + * Retrieve the charset of an HTML document + * + * \param h Content to retrieve charset from + * \return Pointer to charset, or NULL + */ +const char *html_get_encoding(hlcache_handle *h) +{ + struct content *c = hlcache_handle_get_content(h); + + assert(c != NULL); + assert(c->type == CONTENT_HTML); + + return c->data.html.encoding; +} + +/** + * Retrieve framesets used in an HTML document + * + * \param h Content to inspect + * \return Pointer to framesets, or NULL if none + */ +struct content_html_frames *html_get_frameset(hlcache_handle *h) +{ + struct content *c = hlcache_handle_get_content(h); + + assert(c != NULL); + assert(c->type == CONTENT_HTML); + + return c->data.html.frameset; +} + +/** + * Retrieve iframes used in an HTML document + * + * \param h Content to inspect + * \return Pointer to iframes, or NULL if none + */ +struct content_html_iframe *html_get_iframe(hlcache_handle *h) +{ + struct content *c = hlcache_handle_get_content(h); + + assert(c != NULL); + assert(c->type == CONTENT_HTML); + + return c->data.html.iframe; +} + +/** + * Retrieve an HTML content's base URL + * + * \param h Content to retrieve base target from + * \return Pointer to URL + */ +const char *html_get_base_url(hlcache_handle *h) +{ + struct content *c = hlcache_handle_get_content(h); + + assert(c != NULL); + assert(c->type == CONTENT_HTML); + + return c->data.html.base_url; +} + +/** + * Retrieve an HTML content's base target + * + * \param h Content to retrieve base target from + * \return Pointer to target, or NULL if none + */ +const char *html_get_base_target(hlcache_handle *h) +{ + struct content *c = hlcache_handle_get_content(h); + + assert(c != NULL); + assert(c->type == CONTENT_HTML); + + return c->data.html.base_target; +} + +/** + * Retrieve stylesheets used by HTML document + * + * \param h Content to retrieve stylesheets from + * \param n Pointer to location to receive number of sheets + * \return Pointer to array of stylesheets + */ +struct html_stylesheet *html_get_stylesheets(hlcache_handle *h, unsigned int *n) +{ + struct content *c = hlcache_handle_get_content(h); + + assert(c != NULL); + assert(c->type == CONTENT_HTML); + assert(n != NULL); + + *n = c->data.html.stylesheet_count; + + return c->data.html.stylesheets; +} + +/** + * Retrieve objects used by HTML document + * + * \param h Content to retrieve objects from + * \param n Pointer to location to receive number of objects + * \return Pointer to array of objects + */ +struct content_html_object *html_get_objects(hlcache_handle *h, unsigned int *n) +{ + struct content *c = hlcache_handle_get_content(h); + + assert(c != NULL); + assert(c->type == CONTENT_HTML); + assert(n != NULL); + + *n = c->data.html.object_count; + + return c->data.html.object; +} + +/** + * Retrieve favicon associated with an HTML document + * + * \param h HTML document to retrieve favicon from + * \return Pointer to favicon, or NULL if none + */ +hlcache_handle *html_get_favicon(hlcache_handle *h) +{ + struct content *c = hlcache_handle_get_content(h); + + assert(c != NULL); + assert(c->type == CONTENT_HTML); + + return c->data.html.favicon; +} diff --git a/render/html.h b/render/html.h index 51abba3ba..5ce92ba3d 100644 --- a/render/html.h +++ b/render/html.h @@ -31,11 +31,13 @@ #include "desktop/plot_style.h" #include "render/parser_binding.h" +struct fetch_multipart_data; struct box; struct rect; struct browser_window; struct content; -struct form_successful_control; +struct hlcache_handle; +struct http_parameter; struct imagemap; struct object_params; struct plotters; @@ -50,6 +52,18 @@ extern char *default_stylesheet_url; extern char *adblock_stylesheet_url; extern char *quirks_stylesheet_url; +/** + * Container for stylesheets used by an HTML document + */ +struct html_stylesheet { + /** Type of sheet */ + enum { HTML_STYLESHEET_EXTERNAL, HTML_STYLESHEET_INTERNAL } type; + union { + struct hlcache_handle *external; + struct content_css_data *internal; + } data; /**< Sheet data */ +}; + struct frame_dimension { float value; enum { @@ -68,7 +82,7 @@ typedef enum { /** An object (<img>, <object>, etc.) in a CONTENT_HTML document. */ struct content_html_object { - struct content *content; /**< Content, or 0. */ + struct hlcache_handle *content; /**< Content, or 0. */ struct box *box; /**< Node in box tree containing it. */ /** Pointer to array of permitted content_type, terminated by * CONTENT_UNKNOWN, or 0 if any type is acceptable. */ @@ -120,8 +134,6 @@ struct content_html_data { xmlDoc *document; binding_quirks_mode quirks; /**< Quirkyness of document */ - lwc_context *dict; /**< Internment context for this document */ - char *encoding; /**< Encoding of source, 0 if unknown. */ binding_encoding_source encoding_source; /**< Source of encoding information. */ @@ -133,12 +145,12 @@ struct content_html_data { colour background_colour; /**< Document background colour. */ const struct font_functions *font_func; - struct content *favicon; /**< the favicon for the page */ + struct hlcache_handle *favicon; /**< the favicon for the page */ /** Number of entries in stylesheet_content. */ unsigned int stylesheet_count; /** Stylesheets. Each may be 0. */ - struct nscss_import *stylesheets; + struct html_stylesheet *stylesheets; /**< Style selection context */ css_select_ctx *select_ctx; @@ -173,8 +185,7 @@ struct content_html_data { extern bool html_redraw_debug; -bool html_create(struct content *c, struct content *parent, - const char *params[]); +bool html_create(struct content *c, const struct http_parameter *params); bool html_process_data(struct content *c, char *data, unsigned int size); bool html_convert(struct content *c, int width, int height); void html_reformat(struct content *c, int width, int height); @@ -183,9 +194,6 @@ bool html_fetch_object(struct content *c, const char *url, struct box *box, const content_type *permitted_types, int available_width, int available_height, bool background); -bool html_replace_object(struct content *c, unsigned int i, char *url, - char *post_urlenc, - struct form_successful_control *post_multipart); void html_stop(struct content *c); void html_open(struct content *c, struct browser_window *bw, struct content *page, unsigned int index, struct box *box, @@ -212,4 +220,17 @@ bool text_redraw(const char *utf8_text, size_t utf8_len, float scale, bool excluded); +xmlDoc *html_get_document(struct hlcache_handle *h); +struct box *html_get_box_tree(struct hlcache_handle *h); +const char *html_get_encoding(struct hlcache_handle *h); +struct content_html_frames *html_get_frameset(struct hlcache_handle *h); +struct content_html_iframe *html_get_iframe(struct hlcache_handle *h); +const char *html_get_base_url(struct hlcache_handle *h); +const char *html_get_base_target(struct hlcache_handle *h); +struct html_stylesheet *html_get_stylesheets(struct hlcache_handle *h, + unsigned int *n); +struct content_html_object *html_get_objects(struct hlcache_handle *h, + unsigned int *n); +struct hlcache_handle *html_get_favicon(struct hlcache_handle *h); + #endif diff --git a/render/html_redraw.c b/render/html_redraw.c index 2a5154035..3165e1d38 100644 --- a/render/html_redraw.c +++ b/render/html_redraw.c @@ -31,7 +31,7 @@ #include <string.h> #include <math.h> #include "utils/config.h" -#include "content/content.h" +#include "content/content_protected.h" #include "css/css.h" #include "css/utils.h" #include "desktop/gui.h" @@ -1589,31 +1589,35 @@ bool html_redraw_background(int x, int y, struct box *box, float scale, } /* handle background-repeat */ switch (css_computed_background_repeat(background->style)) { - case CSS_BACKGROUND_REPEAT_REPEAT: - repeat_x = repeat_y = true; - /* optimisation: only plot the colour if - * bitmap is not opaque */ - if (background->background->bitmap) - plot_colour = !bitmap_get_opaque( - background->background->bitmap); - break; - case CSS_BACKGROUND_REPEAT_REPEAT_X: - repeat_x = true; - break; - case CSS_BACKGROUND_REPEAT_REPEAT_Y: - repeat_y = true; - break; - case CSS_BACKGROUND_REPEAT_NO_REPEAT: - break; - default: - break; + case CSS_BACKGROUND_REPEAT_REPEAT: + { + struct bitmap *bmp = content_get_bitmap( + background->background); + repeat_x = repeat_y = true; + /* optimisation: only plot the colour if + * bitmap is not opaque */ + if (bmp != NULL) + plot_colour = !bitmap_get_opaque(bmp); + } + break; + case CSS_BACKGROUND_REPEAT_REPEAT_X: + repeat_x = true; + break; + case CSS_BACKGROUND_REPEAT_REPEAT_Y: + repeat_y = true; + break; + case CSS_BACKGROUND_REPEAT_NO_REPEAT: + break; + default: + break; } /* handle background-position */ css_computed_background_position(background->style, &hpos, &hunit, &vpos, &vunit); if (hunit == CSS_UNIT_PCT) { - x += (width - background->background->width) * + x += (width - + content_get_width(background->background)) * scale * FIXTOFLT(hpos) / 100.; } else { x += (int) (FIXTOFLT(nscss_len2px(hpos, hunit, @@ -1621,7 +1625,8 @@ bool html_redraw_background(int x, int y, struct box *box, float scale, } if (vunit == CSS_UNIT_PCT) { - y += (height - background->background->height) * + y += (height - + content_get_height(background->background)) * scale * FIXTOFLT(vpos) / 100.; } else { y += (int) (FIXTOFLT(nscss_len2px(vpos, vunit, @@ -1651,6 +1656,8 @@ bool html_redraw_background(int x, int y, struct box *box, float scale, for (; clip_box; clip_box = clip_box->next) { /* clip to child boxes if needed */ if (clip_to_children) { + struct bitmap *bmp = NULL; + assert(clip_box->type == BOX_TABLE_CELL); /* update clip_* to the child cell */ @@ -1668,15 +1675,15 @@ bool html_redraw_background(int x, int y, struct box *box, float scale, if (clip_x1 > px1) clip_x1 = px1; if (clip_y1 > py1) clip_y1 = py1; + if (clip_box->background != NULL) + bmp = content_get_bitmap(clip_box->background); + /* <td> attributes override <tr> */ if ((clip_x0 >= clip_x1) || (clip_y0 >= clip_y1) || (css_computed_background_color( clip_box->style, &bgcol) != CSS_BACKGROUND_COLOR_TRANSPARENT) || - (clip_box->background && - clip_box->background->bitmap && - bitmap_get_opaque( - clip_box->background->bitmap))) + (bmp != NULL && bitmap_get_opaque(bmp))) continue; } @@ -1693,35 +1700,30 @@ bool html_redraw_background(int x, int y, struct box *box, float scale, } /* and plot the image */ if (plot_content) { + width = content_get_width(background->background); + height = content_get_height(background->background); + if (!repeat_x) { if (clip_x0 < x) clip_x0 = x; - if (clip_x1 > x + - background->background->width * - scale) - clip_x1 = x + background->background-> - width * scale; + if (clip_x1 > x + width * scale) + clip_x1 = x + width * scale; } if (!repeat_y) { if (clip_y0 < y) clip_y0 = y; - if (clip_y1 > y + - background->background->height * - scale) - clip_y1 = y + background->background-> - height * scale; + if (clip_y1 > y + height * scale) + clip_y1 = y + height * scale; } /* valid clipping rectangles only */ if ((clip_x0 < clip_x1) && (clip_y0 < clip_y1)) { if (!plot.clip(clip_x0, clip_y0, clip_x1, clip_y1)) return false; - if (!content_redraw_tiled(background-> - background, x, y, - ceilf(background->background-> - width * scale), - ceilf(background->background-> - height * scale), + if (!content_redraw_tiled( + background->background, x, y, + ceilf(width * scale), + ceilf(height * scale), clip_x0, clip_y0, clip_x1, clip_y1, scale, *background_colour, @@ -1784,32 +1786,35 @@ bool html_redraw_inline_background(int x, int y, struct box *box, float scale, if (plot_content) { /* handle background-repeat */ switch (css_computed_background_repeat(box->style)) { - case CSS_BACKGROUND_REPEAT_REPEAT: - repeat_x = repeat_y = true; - /* optimisation: only plot the colour if - * bitmap is not opaque */ - if (box->background->bitmap) - plot_colour = !bitmap_get_opaque( - box->background->bitmap); - break; - case CSS_BACKGROUND_REPEAT_REPEAT_X: - repeat_x = true; - break; - case CSS_BACKGROUND_REPEAT_REPEAT_Y: - repeat_y = true; - break; - case CSS_BACKGROUND_REPEAT_NO_REPEAT: - break; - default: - break; + case CSS_BACKGROUND_REPEAT_REPEAT: + { + struct bitmap *bmp = + content_get_bitmap(box->background); + repeat_x = repeat_y = true; + /* optimisation: only plot the colour if + * bitmap is not opaque */ + if (bmp != NULL) + plot_colour = !bitmap_get_opaque(bmp); + } + break; + case CSS_BACKGROUND_REPEAT_REPEAT_X: + repeat_x = true; + break; + case CSS_BACKGROUND_REPEAT_REPEAT_Y: + repeat_y = true; + break; + case CSS_BACKGROUND_REPEAT_NO_REPEAT: + break; + default: + break; } /* handle background-position */ css_computed_background_position(box->style, &hpos, &hunit, &vpos, &vunit); if (hunit == CSS_UNIT_PCT) { - x += (px1 - px0 - box->background->width * scale) * - FIXTOFLT(hpos) / 100.; + x += (px1 - px0 - content_get_width(box->background) * + scale) * FIXTOFLT(hpos) / 100.; if (!repeat_x && ((hpos < 2 && !first) || (hpos > 98 && !last))){ @@ -1821,8 +1826,8 @@ bool html_redraw_inline_background(int x, int y, struct box *box, float scale, } if (vunit == CSS_UNIT_PCT) { - y += (py1 - py0 - box->background->height * scale) * - FIXTOFLT(vpos) / 100.; + y += (py1 - py0 - content_get_height(box->background) * + scale) * FIXTOFLT(vpos) / 100.; } else { y += (int) (FIXTOFLT(nscss_len2px(vpos, vunit, box->style)) * scale); @@ -1843,35 +1848,29 @@ bool html_redraw_inline_background(int x, int y, struct box *box, float scale, } /* and plot the image */ if (plot_content) { + int width = content_get_width(box->background); + int height = content_get_height(box->background); + if (!repeat_x) { if (clip_x0 < x) clip_x0 = x; - if (clip_x1 > x + - box->background->width * - scale) - clip_x1 = x + box->background-> - width * scale; + if (clip_x1 > x + width * scale) + clip_x1 = x + width * scale; } if (!repeat_y) { if (clip_y0 < y) clip_y0 = y; - if (clip_y1 > y + - box->background->height * - scale) - clip_y1 = y + box->background-> - height * scale; + if (clip_y1 > y + height * scale) + clip_y1 = y + height * scale; } /* valid clipping rectangles only */ if ((clip_x0 < clip_x1) && (clip_y0 < clip_y1)) { if (!plot.clip(clip_x0, clip_y0, clip_x1, clip_y1)) return false; - if (!content_redraw_tiled(box-> - background, x, y, - ceilf(box->background-> - width * scale), - ceilf(box->background-> - height * scale), + if (!content_redraw_tiled(box->background, x, y, + ceilf(width * scale), + ceilf(height * scale), clip_x0, clip_y0, clip_x1, clip_y1, scale, *background_colour, diff --git a/render/imagemap.c b/render/imagemap.c index d52e5bdd4..07a46d404 100644 --- a/render/imagemap.c +++ b/render/imagemap.c @@ -24,7 +24,8 @@ #include <stdbool.h> #include <string.h> #include <strings.h> -#include "content/content.h" +#include "content/content_protected.h" +#include "content/hlcache.h" #include "render/box.h" #include "render/imagemap.h" #include "utils/log.h" @@ -626,20 +627,21 @@ void imagemap_freelist(struct mapentry *list) /** * Retrieve url associated with imagemap entry * - * \param c The containing content - * \param key The map name to search for - * \param x The left edge of the containing box - * \param y The top edge of the containing box - * \param click_x The horizontal location of the click - * \param click_y The vertical location of the click - * \param target Pointer to location to receive target pointer (if any) + * \param h The containing content + * \param key The map name to search for + * \param x The left edge of the containing box + * \param y The top edge of the containing box + * \param click_x The horizontal location of the click + * \param click_y The vertical location of the click + * \param target Pointer to location to receive target pointer (if any) * \return The url associated with this area, or NULL if not found */ -const char *imagemap_get(struct content *c, const char *key, +const char *imagemap_get(hlcache_handle *h, const char *key, unsigned long x, unsigned long y, unsigned long click_x, unsigned long click_y, const char **target) { + struct content *c = hlcache_handle_get_content(h); unsigned int slot = 0; struct imagemap *map; struct mapentry *entry; diff --git a/render/imagemap.h b/render/imagemap.h index 34a0c3b5f..87a84c3dd 100644 --- a/render/imagemap.h +++ b/render/imagemap.h @@ -22,11 +22,13 @@ #include <libxml/HTMLtree.h> struct content; +struct hlcache_handle; void imagemap_destroy(struct content *c); void imagemap_dump(struct content *c); bool imagemap_extract(xmlNode *node, struct content *c); -const char *imagemap_get(struct content *c, const char *key, + +const char *imagemap_get(struct hlcache_handle *h, const char *key, unsigned long x, unsigned long y, unsigned long click_x, unsigned long click_y, const char **target); diff --git a/render/layout.c b/render/layout.c index 45a075750..336be3e83 100644 --- a/render/layout.c +++ b/render/layout.c @@ -41,7 +41,7 @@ #include <math.h> #include "css/css.h" #include "css/utils.h" -#include "content/content.h" +#include "content/content_protected.h" #include "desktop/gui.h" #include "desktop/options.h" #include "desktop/scroll.h" @@ -256,12 +256,14 @@ bool layout_block_context(struct box *block, int viewport_height, if (!layout_block_object(block)) return false; if (block->height == AUTO) { - if (block->object->width) - block->height = block->object->height * - (float) block->width / - block->object->width; + if (content_get_width(block->object)) + block->height = + content_get_height(block->object) * + (float) block->width / + content_get_width(block->object); else - block->height = block->object->height; + block->height = + content_get_height(block->object); } return true; } @@ -691,13 +693,13 @@ void layout_minmax_block(struct box *block, } if (block->object) { - if (block->object->type == CONTENT_HTML) { - layout_minmax_block(block->object->data.html.layout, + if (content_get_type(block->object) == CONTENT_HTML) { + layout_minmax_block(html_get_box_tree(block->object), font_func); - min = block->object->data.html.layout->min_width; - max = block->object->data.html.layout->max_width; + min = html_get_box_tree(block->object)->min_width; + max = html_get_box_tree(block->object)->max_width; } else { - min = max = block->object->width; + min = max = content_get_width(block->object); } } else { /* recurse through children */ @@ -795,9 +797,9 @@ bool layout_block_object(struct box *block) LOG(("block %p, object %s, width %i", block, block->object->url, block->width)); - if (block->object->type == CONTENT_HTML) { + if (content_get_type(block->object) == CONTENT_HTML) { content_reformat(block->object, block->width, 1); - block->height = block->object->height; + block->height = content_get_height(block->object); } else { /* this case handled already in * layout_block_find_dimensions() */ @@ -837,25 +839,25 @@ void layout_block_find_dimensions(int available_width, int viewport_height, &width, &height, &max_width, &min_width, margin, padding, border); - if (box->object && box->object->type != CONTENT_HTML) { + if (box->object && content_get_type(box->object) != CONTENT_HTML) { /* block-level replaced element, see 10.3.4 and 10.6.2 */ if (width == AUTO && height == AUTO) { - width = box->object->width; - height = box->object->height; + width = content_get_width(box->object); + height = content_get_height(box->object); } else if (width == AUTO) { - if (box->object->height) - width = box->object->width * + if (content_get_height(box->object)) + width = content_get_width(box->object) * (float) height / - box->object->height; + content_get_height(box->object); else - width = box->object->width; + width = content_get_width(box->object); } else if (height == AUTO) { - if (box->object->width) - height = box->object->height * + if (content_get_width(box->object)) + height = content_get_height(box->object) * (float) width / - box->object->width; + content_get_width(box->object); else - height = box->object->height; + height = content_get_height(box->object); } } @@ -1168,18 +1170,20 @@ void layout_float_find_dimensions(int available_width, padding[RIGHT] += scrollbar_width; padding[BOTTOM] += scrollbar_width; - if (box->object && box->object->type != CONTENT_HTML) { + if (box->object && content_get_type(box->object) != CONTENT_HTML) { /* Floating replaced element, with intrinsic width or height. * See 10.3.6 and 10.6.2 */ if (width == AUTO && height == AUTO) { - width = box->object->width; - height = box->object->height; + width = content_get_width(box->object); + height = content_get_height(box->object); } else if (width == AUTO) - width = box->object->width * (float) height / - box->object->height; + width = content_get_width(box->object) * + (float) height / + content_get_height(box->object); else if (height == AUTO) - height = box->object->height * (float) width / - box->object->width; + height = content_get_height(box->object) * + (float) width / + content_get_width(box->object); } else if (box->gadget && (box->gadget->type == GADGET_TEXTBOX || box->gadget->type == GADGET_PASSWORD || box->gadget->type == GADGET_FILE || @@ -2079,22 +2083,25 @@ bool layout_line(struct box *first, int *width, int *y, if (b->object) { if (b->width == AUTO && b->height == AUTO) { - b->width = b->object->width; - b->height = b->object->height; + b->width = content_get_width(b->object); + b->height = content_get_height(b->object); } else if (b->width == AUTO) { - if (b->object->height) - b->width = b->object->width * - (float) b->height / - b->object->height; + if (content_get_height(b->object)) + b->width = + content_get_width(b->object) * + (float) b->height / + content_get_height(b->object); else - b->width = b->object->width; + b->width = content_get_width(b->object); } else if (b->height == AUTO) { - if (b->object->width) - b->height = b->object->height * - (float) b->width / - b->object->width; + if (content_get_width(b->object)) + b->height = + content_get_height(b->object) * + (float) b->width / + content_get_width(b->object); else - b->height = b->object->height; + b->height = + content_get_height(b->object); } } else { /* form control with no object */ @@ -2106,14 +2113,15 @@ bool layout_line(struct box *first, int *width, int *y, CSS_UNIT_EM, b->style)); } - if (b->object && b->object->type == CONTENT_HTML && - b->width != b->object->available_width) { + if (b->object && content_get_type(b->object) == CONTENT_HTML && + b->width != + content_get_available_width(b->object)) { htype = css_computed_height(b->style, &value, &unit); content_reformat(b->object, b->width, b->height); if (htype == CSS_HEIGHT_AUTO) - b->height = b->object->height; + b->height = content_get_height(b->object); } if (height < b->height) @@ -2745,14 +2753,14 @@ struct box *layout_minmax_line(struct box *first, if (b->object) { if (width == AUTO && height == AUTO) { - width = b->object->width; + width = content_get_width(b->object); } else if (width == AUTO) { - if (b->object->height) - width = b->object->width * - (float) height / - b->object->height; + if (content_get_height(b->object)) + width = content_get_width(b->object) * + (float) height / + content_get_height(b->object); else - width = b->object->width; + width = content_get_width(b->object); } fixed = frac = 0; calculate_mbp_width(b->style, LEFT, true, true, true, @@ -3709,9 +3717,11 @@ void layout_lists(struct box *box, if (child->list_marker) { marker = child->list_marker; if (marker->object) { - marker->width = marker->object->width; + marker->width = + content_get_width(marker->object); marker->x = -marker->width; - marker->height = marker->object->height; + marker->height = + content_get_height(marker->object); marker->y = (line_height(marker->style) - marker->height) / 2; } else if (marker->text) { diff --git a/render/textplain.c b/render/textplain.c index 07f37610c..befa9aa50 100644 --- a/render/textplain.c +++ b/render/textplain.c @@ -28,7 +28,8 @@ #include <strings.h> #include <math.h> #include <iconv.h> -#include "content/content.h" +#include "content/content_protected.h" +#include "content/hlcache.h" #include "css/css.h" #include "css/utils.h" #include "desktop/gui.h" @@ -39,6 +40,7 @@ #include "render/box.h" #include "render/font.h" #include "render/textplain.h" +#include "utils/http.h" #include "utils/log.h" #include "utils/messages.h" #include "utils/talloc.h" @@ -72,14 +74,13 @@ static float textplain_line_height(void); * Create a CONTENT_TEXTPLAIN. */ -bool textplain_create(struct content *c, struct content *parent, - const char *params[]) +bool textplain_create(struct content *c, const http_parameter *params) { - unsigned int i; char *utf8_data; - const char *encoding = "iso-8859-1"; + const char *encoding; iconv_t iconv_cd; union content_msg_data msg_data; + nserror error; textplain_style.size = (option_font_size * FONT_SIZE_SCALE) / 10; @@ -87,13 +88,9 @@ bool textplain_create(struct content *c, struct content *parent, if (!utf8_data) goto no_memory; - for (i = 0; params[i]; i += 2) { - if (strcasecmp(params[i], "charset") == 0) { - encoding = talloc_strdup(c, params[i + 1]); - if (!encoding) - goto no_memory; - break; - } + error = http_parameter_list_find_item(params, "charset", &encoding); + if (error != NSERROR_OK) { + encoding = "Windows-1252"; } iconv_cd = iconv_open("utf-8", encoding); @@ -141,18 +138,22 @@ bool textplain_process_data(struct content *c, char *data, unsigned int size) iconv_t iconv_cd = c->data.textplain.iconv_cd; size_t count; union content_msg_data msg_data; + const char *source_data; + unsigned long source_size; + + source_data = content__get_source_data(c, &source_size); do { - char *inbuf = c->source_data + c->data.textplain.converted; - size_t inbytesleft = c->source_size - + char *inbuf = (char *) source_data + c->data.textplain.converted; + size_t inbytesleft = source_size - c->data.textplain.converted; char *outbuf = c->data.textplain.utf8_data + c->data.textplain.utf8_data_size; size_t outbytesleft = c->data.textplain.utf8_data_allocated - c->data.textplain.utf8_data_size; count = iconv(iconv_cd, &inbuf, &inbytesleft, &outbuf, &outbytesleft); - c->data.textplain.converted = inbuf - c->source_data; + c->data.textplain.converted = inbuf - source_data; c->data.textplain.utf8_data_size = c->data.textplain. utf8_data_allocated - outbytesleft; @@ -180,7 +181,7 @@ bool textplain_process_data(struct content *c, char *data, unsigned int size) } gui_multitask(); - } while (!(c->data.textplain.converted == c->source_size || + } while (!(c->data.textplain.converted == source_size || (count == (size_t)(-1) && errno == EINVAL))); return true; @@ -466,6 +467,35 @@ bool textplain_redraw(struct content *c, int x, int y, return true; } +/** + * Retrieve number of lines in content + * + * \param h Content to retrieve line count from + * \return Number of lines + */ +unsigned long textplain_line_count(hlcache_handle *h) +{ + struct content *c = hlcache_handle_get_content(h); + + assert(c != NULL); + + return c->data.textplain.physical_line_count; +} + +/** + * Retrieve the size (in bytes) of text data + * + * \param h Content to retrieve size of + * \return Size, in bytes, of data + */ +size_t textplain_size(hlcache_handle *h) +{ + struct content *c = hlcache_handle_get_content(h); + + assert(c != NULL); + + return c->data.textplain.utf8_data_size; +} /** * Return byte offset within UTF8 textplain content, given the co-ordinates @@ -473,15 +503,16 @@ bool textplain_redraw(struct content *c, int x, int y, * which to search (-1 = above-left, +1 = below-right) if the co-ordinates are not * contained within a line. * - * \param c content of type CONTENT_TEXTPLAIN + * \param h content of type CONTENT_TEXTPLAIN * \param x x ordinate of point * \param y y ordinate of point * \param dir direction of search if not within line * \return byte offset of character containing (or nearest to) point */ -size_t textplain_offset_from_coords(struct content *c, int x, int y, int dir) +size_t textplain_offset_from_coords(hlcache_handle *h, int x, int y, int dir) { + struct content *c = hlcache_handle_get_content(h); float line_height = textplain_line_height(); struct textplain_line *line; const char *text; @@ -489,6 +520,7 @@ size_t textplain_offset_from_coords(struct content *c, int x, int y, int dir) size_t length; int idx; + assert(c != NULL); assert(c->type == CONTENT_TEXTPLAIN); y = (int)((float)(y - MARGIN) / line_height); @@ -552,19 +584,24 @@ size_t textplain_offset_from_coords(struct content *c, int x, int y, int dir) * Given a byte offset within the text, return the line number * of the line containing that offset (or -1 if offset invalid) * - * \param c content of type CONTENT_TEXTPLAIN + * \param h content of type CONTENT_TEXTPLAIN * \param offset byte offset within textual representation * \return line number, or -1 if offset invalid (larger than size) */ -int textplain_find_line(struct content *c, unsigned offset) +int textplain_find_line(hlcache_handle *h, unsigned offset) { - struct textplain_line *line = c->data.textplain.physical_line; - int nlines = c->data.textplain.physical_line_count; + struct content *c = hlcache_handle_get_content(h); + struct textplain_line *line; + int nlines; int lineno = 0; + assert(c != NULL); assert(c->type == CONTENT_TEXTPLAIN); + line = c->data.textplain.physical_line; + nlines = c->data.textplain.physical_line_count; + if (offset > c->data.textplain.utf8_data_size) return -1; @@ -622,30 +659,33 @@ int textplain_coord_from_offset(const char *text, size_t offset, size_t length) * Given a range of byte offsets within a UTF8 textplain content, * return a box that fully encloses the text * - * \param c content of type CONTENT_TEXTPLAIN + * \param h content of type CONTENT_TEXTPLAIN * \param start byte offset of start of text range * \param end byte offset of end * \param r rectangle to be completed */ -void textplain_coords_from_range(struct content *c, unsigned start, unsigned end, - struct rect *r) +void textplain_coords_from_range(hlcache_handle *h, unsigned start, + unsigned end, struct rect *r) { + struct content *c = hlcache_handle_get_content(h); float line_height = textplain_line_height(); - char *utf8_data = c->data.textplain.utf8_data; + char *utf8_data; struct textplain_line *line; unsigned lineno = 0; unsigned nlines; + assert(c != NULL); assert(c->type == CONTENT_TEXTPLAIN); assert(start <= end); assert(end <= c->data.textplain.utf8_data_size); + utf8_data = c->data.textplain.utf8_data; nlines = c->data.textplain.physical_line_count; line = c->data.textplain.physical_line; /* find start */ - lineno = textplain_find_line(c, start); + lineno = textplain_find_line(h, start); r->y0 = (int)(MARGIN + lineno * line_height); @@ -654,7 +694,7 @@ void textplain_coords_from_range(struct content *c, unsigned start, unsigned end forwards most of the time */ /* find end */ - lineno = textplain_find_line(c, end); + lineno = textplain_find_line(h, end); r->x0 = 0; r->x1 = c->data.textplain.formatted_width; @@ -677,18 +717,20 @@ void textplain_coords_from_range(struct content *c, unsigned start, unsigned end /** * Return a pointer to the requested line of text. * - * \param c content of type CONTENT_TEXTPLAIN + * \param h content of type CONTENT_TEXTPLAIN * \param lineno line number * \param poffset receives byte offset of line start within text * \param plen receives length of returned line * \return pointer to text, or NULL if invalid line number */ -char *textplain_get_line(struct content *c, unsigned lineno, +char *textplain_get_line(hlcache_handle *h, unsigned lineno, size_t *poffset, size_t *plen) { + struct content *c = hlcache_handle_get_content(h); struct textplain_line *line; + assert(c != NULL); assert(c->type == CONTENT_TEXTPLAIN); if (lineno >= c->data.textplain.physical_line_count) @@ -706,20 +748,24 @@ char *textplain_get_line(struct content *c, unsigned lineno, * text to fit the window width. Thus only hard newlines are preserved * in the saved/copied text of a selection. * - * \param c content of type CONTENT_TEXTPLAIN + * \param h content of type CONTENT_TEXTPLAIN * \param start starting byte offset within UTF-8 text * \param end ending byte offset * \param plen receives validated length * \return pointer to text, or NULL if no text */ -char *textplain_get_raw_data(struct content *c, unsigned start, unsigned end, +char *textplain_get_raw_data(hlcache_handle *h, unsigned start, unsigned end, size_t *plen) { - size_t utf8_size = c->data.textplain.utf8_data_size; + struct content *c = hlcache_handle_get_content(h); + size_t utf8_size; + assert(c != NULL); assert(c->type == CONTENT_TEXTPLAIN); + utf8_size = c->data.textplain.utf8_data_size; + /* any text at all? */ if (!utf8_size) return NULL; diff --git a/render/textplain.h b/render/textplain.h index 10609a71b..50e29c5be 100644 --- a/render/textplain.h +++ b/render/textplain.h @@ -28,6 +28,8 @@ #include <iconv.h> struct content; +struct hlcache_handle; +struct http_parameter; struct textplain_line { size_t start; @@ -46,8 +48,7 @@ struct content_textplain_data { int formatted_width; }; -bool textplain_create(struct content *c, struct content *parent, - const char *params[]); +bool textplain_create(struct content *c, const struct http_parameter *params); bool textplain_process_data(struct content *c, char *data, unsigned int size); bool textplain_convert(struct content *c, int width, int height); void textplain_reformat(struct content *c, int width, int height); @@ -58,16 +59,17 @@ bool textplain_redraw(struct content *c, int x, int y, float scale, colour background_colour); /* access to lines for text selection and searching */ -#define textplain_line_count(c) ((c)->data.textplain.physical_line_count) -#define textplain_size(c) ((c)->data.textplain.utf8_data_size) +unsigned long textplain_line_count(struct hlcache_handle *h); +size_t textplain_size(struct hlcache_handle *h); -size_t textplain_offset_from_coords(struct content *c, int x, int y, int dir); -void textplain_coords_from_range(struct content *c, +size_t textplain_offset_from_coords(struct hlcache_handle *h, int x, int y, + int dir); +void textplain_coords_from_range(struct hlcache_handle *h, unsigned start, unsigned end, struct rect *r); -char *textplain_get_line(struct content *c, unsigned lineno, +char *textplain_get_line(struct hlcache_handle *h, unsigned lineno, size_t *poffset, size_t *plen); -int textplain_find_line(struct content *c, unsigned offset); -char *textplain_get_raw_data(struct content *c, +int textplain_find_line(struct hlcache_handle *h, unsigned offset); +char *textplain_get_raw_data(struct hlcache_handle *h, unsigned start, unsigned end, size_t *plen); #endif diff --git a/riscos/gui.c b/riscos/gui.c index a1c1980e2..d2b964146 100644 --- a/riscos/gui.c +++ b/riscos/gui.c @@ -279,7 +279,15 @@ static void *myrealloc(void *ptr, size_t len, void *pw) int main(int argc, char** argv) { setbuf(stderr, NULL); - return netsurf_main(argc, argv); + + /* initialise netsurf */ + netsurf_init(argc, argv); + + netsurf_main_loop(); + + netsurf_exit(); + + return 0; } /** diff --git a/riscos/window.c b/riscos/window.c index effb4dac6..335a36409 100644 --- a/riscos/window.c +++ b/riscos/window.c @@ -1396,12 +1396,10 @@ bool gui_window_frame_resize_start(struct gui_window *g) * \param g gui_window containing the content * \param c the content to save */ - -void gui_window_save_as_link(struct gui_window *g, struct content *c) +void gui_window_save_link(struct gui_window *g, const char *url, + const char *title) { - if (!c) - return; - ro_gui_save_prepare(GUI_SAVE_LINK_URL, NULL, NULL, c->url, c->title); + ro_gui_save_prepare(GUI_SAVE_LINK_URL, NULL, NULL, url, title); ro_gui_dialog_open_persistent(g->window, dialog_saveas, true); } diff --git a/test/Makefile b/test/Makefile new file mode 100644 index 000000000..f480f4809 --- /dev/null +++ b/test/Makefile @@ -0,0 +1,19 @@ +CFLAGS := -std=c99 -g -O0 -D_BSD_SOURCE -D_POSIX_C_SOURCE -I.. \ + `pkg-config --cflags libxml-2.0 libcurl libparserutils` +LDFLAGS := `pkg-config --libs libxml-2.0 libcurl libparserutils` + +llcache_SRCS := content/fetch.c content/fetchers/fetch_curl.c \ + content/fetchers/fetch_data.c content/llcache.c \ + content/urldb.c desktop/options.c desktop/version.c \ + utils/base64.c utils/hashtable.c utils/messages.c \ + utils/url.c utils/useragent.c utils/utf8.c utils/utils.c \ + test/llcache.c + +llcache: $(addprefix ../,$(llcache_SRCS)) + $(CC) $(CFLAGS) $^ -o $@ $(LDFLAGS) + + +.PHONY: clean + +clean: + $(RM) llcache diff --git a/test/llcache.c b/test/llcache.c new file mode 100644 index 000000000..be0df60ad --- /dev/null +++ b/test/llcache.c @@ -0,0 +1,282 @@ +#include <stdio.h> +#include <stdlib.h> + +#include "content/fetch.h" +#include "content/llcache.h" +#include "utils/ring.h" +#include "utils/url.h" + +/****************************************************************************** + * Things that we'd reasonably expect to have to implement * + ******************************************************************************/ + +/* desktop/netsurf.h */ +bool verbose_log; + +/* utils/utils.h */ +void die(const char * const error) +{ + fprintf(stderr, "%s\n", error); + + exit(1); +} + +/* utils/utils.h */ +void warn_user(const char *warning, const char *detail) +{ + fprintf(stderr, "%s %s\n", warning, detail); +} + +/* content/fetch.h */ +const char *fetch_filetype(const char *unix_path) +{ + return NULL; +} + +/* content/fetch.h */ +char *fetch_mimetype(const char *ro_path) +{ + return NULL; +} + +/****************************************************************************** + * Things that are absolutely not reasonable, and should disappear * + ******************************************************************************/ + +#include "desktop/cookies.h" +#include "desktop/tree.h" + +/* desktop/cookies.h -- used by urldb + * + * URLdb should have a cookies update event + handler registration + */ +bool cookies_update(const char *domain, const struct cookie_data *data) +{ + return true; +} + +/* image/bitmap.h -- used by urldb + * + * URLdb shouldn't care about bitmaps. + * This is because the legacy RO thumbnail stuff was hacked in and must die. + */ +void bitmap_destroy(void *bitmap) +{ +} + +/* desktop/tree.h -- used by options.c + * + * Why on earth is tree loading and saving in options.c? + */ +void tree_initialise(struct tree *tree) +{ +} + +/* desktop/tree.h */ +struct node *tree_create_folder_node(struct node *parent, const char *title) +{ + return NULL; +} + +/* desktop/tree.h */ +struct node *tree_create_URL_node(struct node *parent, const char *url, + const struct url_data *data, const char *title) +{ + return NULL; +} + +/* desktop/tree.h */ +struct node_element *tree_find_element(struct node *node, node_element_data d) +{ + return NULL; +} + +/****************************************************************************** + * test: protocol handler * + ******************************************************************************/ + +typedef struct test_context { + struct fetch *parent; + + bool aborted; + bool locked; + + struct test_context *r_prev; + struct test_context *r_next; +} test_context; + +static test_context *ring; + +bool test_initialise(const char *scheme) +{ + /* Nothing to do */ + return true; +} + +void test_finalise(const char *scheme) +{ + /* Nothing to do */ +} + +void *test_setup_fetch(struct fetch *parent, const char *url, bool only_2xx, + const char *post_urlenc, + struct fetch_multipart_data *post_multipart, + const char **headers) +{ + test_context *ctx = calloc(1, sizeof(test_context)); + + if (ctx == NULL) + return NULL; + + ctx->parent = parent; + + RING_INSERT(ring, ctx); + + return ctx; +} + +bool test_start_fetch(void *handle) +{ + /* Nothing to do */ + return true; +} + +void test_abort_fetch(void *handle) +{ + test_context *ctx = handle; + + ctx->aborted = true; +} + +void test_free_fetch(void *handle) +{ + test_context *ctx = handle; + + RING_REMOVE(ring, ctx); + + free(ctx); +} + +void test_process(test_context *ctx) +{ + /** \todo Implement */ +} + +void test_poll(const char *scheme) +{ + test_context *ctx, *next; + + if (ring == NULL) + return; + + ctx = ring; + do { + next = ctx->r_next; + + if (ctx->locked) + continue; + + if (ctx->aborted == false) { + test_process(ctx); + } + + fetch_remove_from_queues(ctx->parent); + fetch_free(ctx->parent); + } while ((ctx = next) != ring && ring != NULL); +} + +/****************************************************************************** + * The actual test code * + ******************************************************************************/ + +nserror query_handler(const llcache_query *query, void *pw, + llcache_query_response cb, void *cbpw) +{ + /* I'm too lazy to actually implement this. It should queue the query, + * then deliver the response from main(). */ + + return NSERROR_OK; +} + +nserror event_handler(const llcache_handle *handle, + const llcache_event *event, void *pw) +{ + static char *event_names[] = { + "HAD_HEADERS", "HAD_DATA", "DONE", "ERROR", "PROGRESS" + }; + bool *done = pw; + + if (event->type != LLCACHE_EVENT_PROGRESS) + fprintf(stdout, "%p : %s\n", handle, event_names[event->type]); + + /* Inform main() that the fetch completed */ + if (event->type == LLCACHE_EVENT_DONE) + *done = true; + + return NSERROR_OK; +} + +int main(int argc, char **argv) +{ + nserror error; + llcache_handle *handle; + llcache_handle *handle2; + bool done = false; + + /* Initialise subsystems */ + url_init(); + fetch_init(); + fetch_add_fetcher("test", test_initialise, test_setup_fetch, + test_start_fetch, test_abort_fetch, test_free_fetch, + test_poll, test_finalise); + + /* Initialise low-level cache */ + error = llcache_initialise(query_handler, NULL); + if (error != NSERROR_OK) { + fprintf(stderr, "llcache_initialise: %d\n", error); + return 1; + } + + /* Retrieve an URL from the low-level cache (may trigger fetch) */ + error = llcache_handle_retrieve("http://www.netsurf-browser.org/", + LLCACHE_RETRIEVE_VERIFIABLE, NULL, NULL, + event_handler, &done, &handle); + if (error != NSERROR_OK) { + fprintf(stderr, "llcache_handle_retrieve: %d\n", error); + return 1; + } + + /* Poll relevant components */ + while (done == false) { + fetch_poll(); + llcache_poll(); + } + + done = false; + error = llcache_handle_retrieve("http://www.netsurf-browser.org/", + LLCACHE_RETRIEVE_VERIFIABLE, NULL, NULL, + event_handler, &done, &handle2); + if (error != NSERROR_OK) { + fprintf(stderr, "llcache_handle_retrieve: %d\n", error); + return 1; + } + + while (done == false) { + fetch_poll(); + llcache_poll(); + } + + fprintf(stdout, "%p -> %p\n", handle, + llcache_object_from_handle(handle)); + fprintf(stdout, "%p -> %p\n", handle2, + llcache_object_from_handle(handle2)); + + /* Cleanup */ + llcache_handle_release(handle2); + llcache_handle_release(handle); + + fetch_quit(); + + return 0; +} + diff --git a/utils/errors.h b/utils/errors.h new file mode 100644 index 000000000..7aa486569 --- /dev/null +++ b/utils/errors.h @@ -0,0 +1,40 @@ +/* + * Copyright 2009 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 + * Error codes + */ + +#ifndef NETSURF_UTILS_ERRORS_H_ +#define NETSURF_UTILS_ERRORS_H_ + +/** + * Enumeration of error codes + */ +typedef enum { + NSERROR_OK, /**< No error */ + + NSERROR_NOMEM, /**< Memory exhaustion */ + + NSERROR_NO_FETCH_HANDLER, /**< No fetch handler for URL scheme */ + + NSERROR_NOT_FOUND, /**< Requested item not found */ +} nserror; + +#endif + diff --git a/utils/http.c b/utils/http.c new file mode 100644 index 000000000..12672261d --- /dev/null +++ b/utils/http.c @@ -0,0 +1,390 @@ +/* + * 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 + * HTTP header parsing functions + */ + +#include <inttypes.h> +#include <stdbool.h> +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "utils/http.h" + +/** + * Representation of an HTTP parameter + */ +struct http_parameter { + struct http_parameter *next; /**< Next parameter in list, or NULL */ + + char *name; /**< Parameter name */ + char *value; /**< Parameter value */ +}; + +/** + * Determine if a character is valid for an HTTP token + * + * \param c Character to consider + * \return True if character is valid, false otherwise + */ +static bool http_is_token_char(uint8_t c) +{ + /* [ 32 - 126 ] except ()<>@,;:\"/[]?={} SP HT */ + + if (c <= ' ' || 126 < c) + return false; + + return (strchr("()<>@,;:\\\"/[]?={}", c) == NULL); +} + +/** + * Parse an HTTP token + * + * \param input Pointer to current input byte. Updated on exit. + * \param value Pointer to location to receive on-heap token value. + * \return NSERROR_OK on success, + * NSERROR_NOMEM on memory exhaustion + * + * The returned value is owned by the caller + */ +static nserror http_parse_token(const char **input, char **value) +{ + const uint8_t *start = (const uint8_t *) *input; + const uint8_t *end; + char *token; + + end = start; + while (http_is_token_char(*end)) + end++; + + token = malloc(end - start + 1); + if (token == NULL) + return NSERROR_NOMEM; + + memcpy(token, start, end - start); + token[end - start] = '\0'; + + *value = token; + *input = (const char *) end; + + return NSERROR_OK; +} + +/** + * Parse an HTTP quoted-string + * + * \param input Pointer to current input byte. Updated on exit. + * \param value Pointer to location to receive on-heap string value. + * \return NSERROR_OK on success, + * NSERROR_NOMEM on memory exhaustion + * + * The returned value is owned by the caller + */ +static nserror http_parse_quoted_string(const char **input, char **value) +{ + const uint8_t *start = (const uint8_t *) *input; + const uint8_t *end; + uint8_t c; + char *string_value; + + /* <"> *( qdtext | quoted-pair ) <"> + * qdtext = any TEXT except <"> + * quoted-pair = "\" CHAR + * TEXT = [ HT, CR, LF, 32-126, 128-255 ] + * CHAR = [ 0 - 127 ] + * + * \todo TEXT may contain non 8859-1 chars encoded per RFC 2047 + * \todo Support quoted-pairs + */ + + if (*start == '"') { + end = start = start + 1; + + c = *end; + while (c == '\t' || c == '\r' || c == '\n' || + c == ' ' || c == '!' || + ('#' <= c && c <= 126) || c > 127) { + end++; + c = *end; + } + + if (*end != '"') { + start--; + end = start; + } + } + + string_value = malloc(end - start + 1); + if (string_value == NULL) + return NSERROR_NOMEM; + + memcpy(string_value, start, end - start); + string_value[end - start] = '\0'; + + *value = string_value; + + if (end != start) + *input = (const char *) end + 1; + + return NSERROR_OK; +} + +/** + * Parse an HTTP parameter + * + * \param input Pointer to current input byte. Updated on exit. + * \param parameter Pointer to location to receive on-heap parameter. + * \return NSERROR_OK on success, + * NSERROR_NOMEM on memory exhaustion + * + * The returned parameter is owned by the caller. + */ +static nserror http_parse_parameter(const char **input, + http_parameter **parameter) +{ + const char *pos = *input; + char *name; + char *value; + http_parameter *param; + nserror error; + + /* token "=" ( token | quoted-string ) */ + + error = http_parse_token(&pos, &name); + if (error != NSERROR_OK) + return error; + + while (*pos == ' ' || *pos == '\t') + pos++; + + if (*pos != '=') { + value = strdup(""); + if (value == NULL) { + free(name); + return NSERROR_NOMEM; + } + } else { + pos++; + + while (*pos == ' ' || *pos == '\t') + pos++; + + if (*pos == '"') + error = http_parse_quoted_string(&pos, &value); + else + error = http_parse_token(&pos, &value); + + if (error != NSERROR_OK) { + free(name); + return error; + } + } + + param = malloc(sizeof(*param)); + if (param == NULL) { + free(value); + free(name); + return NSERROR_NOMEM; + } + + param->next = NULL; + param->name = name; + param->value = value; + + *parameter = param; + *input = pos; + + return NSERROR_OK; +} + +/** + * Parse an HTTP parameter list + * + * \param input Pointer to current input byte. Updated on exit. + * \param parameters Pointer to location to receive on-heap parameter list. + * \return NSERROR_OK on success, + * NSERROR_NOMEM on memory exhaustion + * + * The returned parameter list is owned by the caller + */ +static nserror http_parse_parameter_list(const char **input, + http_parameter **parameters) +{ + const char *pos = *input; + http_parameter *param; + http_parameter *list = NULL; + nserror error; + + /* 1*( ";" parameter ) */ + + while (*pos == ';') { + pos++; + + while (*pos == ' ' || *pos == '\t') + pos++; + + error = http_parse_parameter(&pos, ¶m); + if (error != NSERROR_OK) { + while (list != NULL) { + param = list; + + list = param->next; + + free(param->name); + free(param->value); + free(param); + } + return error; + } + + if (list != NULL) + param->next = list; + + list = param; + + while (*pos == ' ' || *pos == '\t') + pos++; + } + + *parameters = list; + *input = pos; + + return NSERROR_OK; +} + +/* See http.h for documentation */ +nserror http_parse_content_type(const char *header_value, char **media_type, + http_parameter **parameters) +{ + const char *pos = header_value; + char *type; + char *subtype = NULL; + http_parameter *params = NULL; + char *mime; + size_t mime_len; + nserror error; + + /* type "/" subtype *( ";" parameter ) */ + + while (*pos == ' ' || *pos == '\t') + pos++; + + error = http_parse_token(&pos, &type); + if (error != NSERROR_OK) + return error; + + while (*pos == ' ' || *pos == '\t') + pos++; + + if (*pos == '/') { + pos++; + + while (*pos == ' ' || *pos == '\t') + pos++; + + error = http_parse_token(&pos, &subtype); + if (error != NSERROR_OK) { + free(type); + return error; + } + + while (*pos == ' ' || *pos == '\t') + pos++; + + if (*pos == ';') { + pos++; + + while (*pos == ' ' || *pos == '\t') + pos++; + + error = http_parse_parameter_list(&pos, ¶ms); + if (error != NSERROR_OK) { + free(subtype); + free(type); + return error; + } + } + } + + /* <type> + <subtype> + '/' */ + mime_len = strlen(type) + (subtype != NULL ? strlen(subtype) : 0) + 1; + + mime = malloc(mime_len + 1); + if (mime == NULL) { + http_parameter_list_destroy(params); + free(subtype); + free(type); + return NSERROR_OK; + } + + sprintf(mime, "%s/%s", type, subtype != NULL ? subtype : ""); + + free(subtype); + free(type); + + *media_type = mime; + *parameters = params; + + return NSERROR_OK; +} + +/* See http.h for documentation */ +nserror http_parameter_list_find_item(const http_parameter *list, + const char *name, const char **value) +{ + while (list != NULL && strcasecmp(name, list->name) != 0) + list = list->next; + + if (list == NULL) + return NSERROR_NOT_FOUND; + + *value = list->value; + + return NSERROR_OK; +} + +/* See http.h for documentation */ +const http_parameter *http_parameter_list_iterate(const http_parameter *cur, + const char **name, const char **value) +{ + if (cur == NULL) + return NULL; + + *name = cur->name; + *value = cur->value; + + return cur->next; +} + +/* See http.h for documentation */ +void http_parameter_list_destroy(http_parameter *list) +{ + while (list != NULL) { + http_parameter *victim = list; + + list = victim->next; + + free(victim->name); + free(victim->value); + free(victim); + } +} + diff --git a/utils/http.h b/utils/http.h new file mode 100644 index 000000000..1d8b4de6c --- /dev/null +++ b/utils/http.h @@ -0,0 +1,73 @@ +/* + * 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 + * HTTP header parsing functions + */ + +#ifndef NETSURF_UTILS_HTTP_H_ +#define NETSURF_UTILS_HTTP_H_ + +#include "utils/errors.h" + +typedef struct http_parameter http_parameter; + +/** + * Parse an HTTP Content-Type header value + * + * \param header_value Header value to parse + * \param media_type Pointer to location to receive media type + * \param parameters Pointer to location to receive parameter list + * \return NSERROR_OK on success, + * NSERROR_NOMEM on memory exhaustion + */ +nserror http_parse_content_type(const char *header_value, char **media_type, + http_parameter **parameters); + +/** + * Find a named item in an HTTP parameter list + * + * \param list List to search + * \param name Name of item to search for + * \param value Pointer to location to receive value + * \return NSERROR_OK on success, + * NSERROR_NOT_FOUND if requested item does not exist + */ +nserror http_parameter_list_find_item(const http_parameter *list, + const char *name, const char **value); + +/** + * Iterate over a parameter list + * + * \param cur Pointer to current iteration position, list head to start + * \param name Pointer to location to receive item name + * \param value Pointer to location to receive item value + * \return Pointer to next iteration position, or NULL for end of iteration + */ +const http_parameter *http_parameter_list_iterate(const http_parameter *cur, + const char **name, const char **value); + +/** + * Destroy a list of HTTP parameters + * + * \param list List to destroy + */ +void http_parameter_list_destroy(http_parameter *list); + +#endif + diff --git a/windows/bitmap.c b/windows/bitmap.c index 74c104963..0fd323574 100644 --- a/windows/bitmap.c +++ b/windows/bitmap.c @@ -19,21 +19,13 @@ #include <inttypes.h> #include <sys/types.h> +#include <string.h> -#include "assert.h" #include "image/bitmap.h" #include "windows/bitmap.h" #include "utils/log.h" -#ifndef MIN -#define MIN(a,b) (((a) < (b)) ? (a) : (b)) -#endif - -#ifndef MAX -#define MAX(a,b) (((a) > (b)) ? (a) : (b)) -#endif - /** * Create a bitmap. * diff --git a/windows/gui.c b/windows/gui.c index 248363795..fa0954b20 100644 --- a/windows/gui.c +++ b/windows/gui.c @@ -157,7 +157,6 @@ static void redraw(void) { struct gui_window *w = window_list; struct browser_window *bw; - struct content *c; HDC hdc; while (w != NULL) { @@ -173,20 +172,13 @@ static void redraw(void) continue; } - c = bw->current_content; - - if ((c == NULL) || (c->locked)) { - w = w->next; - continue; - } - current_hwnd = w->drawingarea; w->scrolly += w->requestscrolly; w->scrollx += w->requestscrollx; w->scrolly = MAX(w->scrolly, 0); - w->scrolly = MIN(w->scrolly, c->height * w->bw->scale - w->height); + w->scrolly = MIN(w->scrolly, content_get_height(bw->current_content) * w->bw->scale - w->height); w->scrollx = MAX(w->scrollx, 0); - w->scrollx = MIN(w->scrollx, c->width * w->bw->scale - w->width); + w->scrollx = MIN(w->scrollx, content_get_width(bw->current_content) * w->bw->scale - w->width); /* redraw */ current_redraw_browser = bw; nsws_plot_set_scale(bw->scale); @@ -204,9 +196,11 @@ static void redraw(void) doublebuffering = false; if (doublebuffering) bufferdc = w->bufferdc; - content_redraw(c, -w->scrollx / w->bw->scale, + content_redraw(bw->current_content, + -w->scrollx / w->bw->scale, -w->scrolly / w->bw->scale, - w->width, w->height, + w->width, + w->height, w->redraw.left - w->scrollx / w->bw->scale, w->redraw.top - w->scrolly / w->bw->scale, w->redraw.right - w->scrollx / w->bw->scale, @@ -809,13 +803,14 @@ nsws_drawable_hscroll(struct gui_window *gw, HWND hwnd, WPARAM wparam) si.fMask = SIF_POS; if ((gw->bw != NULL) && (gw->bw->current_content != NULL)) si.nPos = MIN(si.nPos, - gw->bw->current_content->width * + content_get_width(gw->bw->current_content) * gw->bw->scale - gw->width); si.nPos = MAX(si.nPos, 0); SetScrollInfo(hwnd, SB_HORZ, &si, TRUE); GetScrollInfo(hwnd, SB_HORZ, &si); - if (si.nPos != mem) + if (si.nPos != mem) { gui_window_set_scroll(gw, gw->scrollx + gw->requestscrollx + si.nPos - mem, gw->scrolly); + } } static void @@ -866,7 +861,7 @@ nsws_drawable_vscroll(struct gui_window *gw, HWND hwnd, WPARAM wparam) if ((gw->bw != NULL) && (gw->bw->current_content != NULL)) { si.nPos = MIN(si.nPos, - gw->bw->current_content->height * + content_get_height(gw->bw->current_content) * gw->bw->scale - gw->height); } si.nPos = MAX(si.nPos, 0); @@ -1942,11 +1937,10 @@ void gui_window_set_scroll(struct gui_window *w, int sx, int sy) si.cbSize = sizeof(si); si.fMask = SIF_ALL; si.nMin = 0; - si.nMax = (w->bw->current_content->height * w->bw->scale) -1; + si.nMax = (content_get_height(w->bw->current_content) * w->bw->scale) - 1; si.nPage = w->height; si.nPos = MAX(w->scrolly + w->requestscrolly, 0); - si.nPos = MIN(si.nPos, w->bw->current_content->height * w->bw->scale - - w->height); + si.nPos = MIN(si.nPos, content_get_height(w->bw->current_content) * w->bw->scale - w->height); SetScrollInfo(w->drawingarea, SB_VERT, &si, TRUE); LOG(("SetScrollInfo VERT min:%d max:%d page:%d pos:%d", si.nMin, si.nMax, si.nPage, si.nPos)); @@ -1954,11 +1948,10 @@ void gui_window_set_scroll(struct gui_window *w, int sx, int sy) si.cbSize = sizeof(si); si.fMask = SIF_ALL; si.nMin = 0; - si.nMax = (w->bw->current_content->width * w->bw->scale) -1; + si.nMax = (content_get_width(w->bw->current_content) * w->bw->scale) -1; si.nPage = w->width; si.nPos = MAX(w->scrollx + w->requestscrollx, 0); - si.nPos = MIN(si.nPos, w->bw->current_content->width * w->bw->scale - - w->width); + si.nPos = MIN(si.nPos, content_get_width(w->bw->current_content) * w->bw->scale - w->width); SetScrollInfo(w->drawingarea, SB_HORZ, &si, TRUE); LOG(("SetScrollInfo HORZ min:%d max:%d page:%d pos:%d", si.nMin, si.nMax, si.nPage, si.nPos)); @@ -2184,12 +2177,12 @@ gui_window_remove_caret(struct gui_window *w) } void -gui_window_set_icon(struct gui_window *g, struct content *icon) +gui_window_set_icon(struct gui_window *g, hlcache_handle *icon) { } void -gui_window_set_search_ico(struct content *ico) +gui_window_set_search_ico(hlcache_handle *ico) { } @@ -2235,7 +2228,8 @@ bool gui_window_frame_resize_start(struct gui_window *w) return true; } -void gui_window_save_as_link(struct gui_window *w, struct content *c) +void gui_window_save_link(struct gui_window *g, const char *url, + const char *title) { } @@ -2247,7 +2241,7 @@ void gui_window_set_scale(struct gui_window *w, float scale) LOG(("%.2f\n", scale)); } -void gui_drag_save_object(gui_save_type type, struct content *c, +void gui_drag_save_object(gui_save_type type, hlcache_handle *c, struct gui_window *w) { } @@ -2352,7 +2346,7 @@ void gui_launch_url(const char *url) { } -void gui_cert_verify(struct browser_window *bw, struct content *c, +void gui_cert_verify(struct browser_window *bw, hlcache_handle *c, const struct ssl_cert_info *certs, unsigned long num) { } @@ -2469,17 +2463,27 @@ WinMain(HINSTANCE hInstance, HINSTANCE hLastInstance, LPSTR lpcli, int ncmd) argv = malloc(sizeof(char *) * argc); while (argctemp < argc) { len = wcstombs(NULL, argvw[argctemp], 0) + 1; - if (len > 0) + if (len > 0) { argv[argctemp] = malloc(len); + } + if (argv[argctemp] != NULL) { wcstombs(argv[argctemp], argvw[argctemp], len); /* alter windows-style forward slash flags to - * hypen flags. + * hyphen flags. */ if (argv[argctemp][0] == '/') argv[argctemp][0] = '-'; } argctemp++; } - return netsurf_main(argc, argv); + + /* initialise netsurf */ + netsurf_init(argc, argv); + + netsurf_main_loop(); + + netsurf_exit(); + + return 0; } diff --git a/windows/hotlist.c b/windows/hotlist.c index 48df23bb4..dab18d11c 100644 --- a/windows/hotlist.c +++ b/windows/hotlist.c @@ -17,7 +17,8 @@ */ #include "desktop/browser.h" +#include "content/hlcache.h" -void hotlist_visited(struct content *content) +void hotlist_visited(hlcache_handle *content) { } diff --git a/windows/login.c b/windows/login.c index 306de8459..32c87591c 100644 --- a/windows/login.c +++ b/windows/login.c @@ -17,8 +17,9 @@ */ #include "desktop/401login.h" +#include "content/hlcache.h" -void gui_401login_open(struct browser_window *bw, struct content *c, +void gui_401login_open(struct browser_window *bw, hlcache_handle *c, const char *realm) { } diff --git a/windows/thumbnail.c b/windows/thumbnail.c index 8305ee1cc..e96c32201 100644 --- a/windows/thumbnail.c +++ b/windows/thumbnail.c @@ -23,29 +23,30 @@ #include "windows/bitmap.h" #include "windows/gui.h" #include "windows/plot.h" +#include "content/hlcache.h" -#ifndef MIN -#define MIN(a,b) (((a) < (b)) ? (a) : (b)) -#endif -#ifndef MAX -#define MAX(a,b) (((a) > (b)) ? (a) : (b)) -#endif - -bool thumbnail_create(struct content *content, struct bitmap *bitmap, - const char *url) +bool +thumbnail_create(hlcache_handle *content, + struct bitmap *bitmap, + const char *url) { - LOG(("creating thumbnail %p for url %s content %p", bitmap, url, - content)); - int width = MIN(content->width, 1024); - int height = MIN(content->height, 768); + int width = content_get_width(content); + int height = content_get_height(content); int i; uint8_t *pixdata; HDC hdc, minidc; HBITMAP bufferbm, minibm, minibm2; - BITMAPINFO *bmi = (BITMAPINFO *) malloc(sizeof(BITMAPINFOHEADER) + - (bitmap->width * bitmap->height * 4)); + BITMAPINFO *bmi; BITMAPINFOHEADER bmih; + + LOG(("creating thumbnail %p for url %s content %p", bitmap, url, content)); + + bmi = malloc(sizeof(BITMAPINFOHEADER) + (bitmap->width * bitmap->height * 4)); + if (bmi == NULL) { + return false; + } + bmih.biSize = sizeof(bmih); bmih.biWidth = bitmap->width; bmih.biHeight = - bitmap->height; @@ -81,7 +82,7 @@ bool thumbnail_create(struct content *content, struct bitmap *bitmap, } SelectObject(bufferdc, bufferbm); thumbnail = true; - content_redraw(content, 0, 0, content->width, content->height, 0, 0, + content_redraw(content, 0, 0, width, height, 0, 0, width, height, 1.0, 0xFFFFFF); thumbnail = false; |