From 230779848e22f5d185cbeb0ccaae0c16cbc5836c Mon Sep 17 00:00:00 2001 From: Vincent Sanders Date: Fri, 7 Oct 2011 18:12:47 +0000 Subject: [PATCH] metadata links stored in contents browser uses metadata links for favicons svn path=/trunk/netsurf/; revision=12977 --- content/content.c | 99 ++++++++++++++++++++ content/content.h | 20 +++- content/content_protected.h | 7 ++ desktop/browser.c | 177 ++++++++++++++++++++++++------------ desktop/browser.h | 4 +- image/image_cache.c | 11 +-- image/image_cache.h | 2 +- render/html.c | 101 ++++++++++++++------ 8 files changed, 321 insertions(+), 100 deletions(-) diff --git a/content/content.c b/content/content.c index 7b722a274..28d18bcab 100644 --- a/content/content.c +++ b/content/content.c @@ -376,6 +376,8 @@ void content__reformat(struct content *c, bool background, void content_destroy(struct content *c) { + struct content_rfc5988_link *link; + assert(c); LOG(("content %p %s", c, nsurl_access(llcache_handle_get_url(c->llcache)))); @@ -389,6 +391,12 @@ void content_destroy(struct content *c) lwc_string_unref(c->mime_type); + /* release metadata links */ + link = c->links; + while (link != NULL) { + link = content__free_rfc5988_link(link); + } + talloc_free(c); } @@ -744,6 +752,97 @@ bool content__set_title(struct content *c, const char *title) return true; } +struct content_rfc5988_link * +content_find_rfc5988_link(hlcache_handle *h, lwc_string *rel) +{ + struct content *c = hlcache_handle_get_content(h); + struct content_rfc5988_link *link = c->links; + bool rel_match = false; + + while (link != NULL) { + lwc_string_caseless_isequal(link->rel, rel, &rel_match); + if (rel_match) { + break; + } + link = link->next; + } + return link; +} + +struct content_rfc5988_link * +content__free_rfc5988_link(struct content_rfc5988_link *link) +{ + struct content_rfc5988_link *next; + + next = link->next; + + lwc_string_unref(link->rel); + nsurl_unref(link->href); + if (link->hreflang != NULL) { + lwc_string_unref(link->hreflang); + } + if (link->type != NULL) { + lwc_string_unref(link->type); + } + if (link->media != NULL) { + lwc_string_unref(link->media); + } + if (link->sizes != NULL) { + lwc_string_unref(link->sizes); + } + free(link); + + return next; +} + +bool content__add_rfc5988_link(struct content *c, + const struct content_rfc5988_link *link) +{ + struct content_rfc5988_link *newlink; + union content_msg_data msg_data; + + /* a link relation must be present for it to be a link */ + if (link->rel == NULL) { + return false; + } + + /* a link href must be present for it to be a link */ + if (link->href == NULL) { + return false; + } + + newlink = calloc(1, sizeof(struct content_rfc5988_link)); + if (newlink == NULL) { + return false; + } + + /* copy values */ + newlink->rel = lwc_string_ref(link->rel); + newlink->href = nsurl_ref(link->href); + if (link->hreflang != NULL) { + newlink->hreflang = lwc_string_ref(link->hreflang); + } + if (link->type != NULL) { + newlink->type = lwc_string_ref(link->type); + } + if (link->media != NULL) { + newlink->media = lwc_string_ref(link->media); + } + if (link->sizes != NULL) { + newlink->sizes = lwc_string_ref(link->sizes); + } + + /* add to metadata link to list */ + newlink->next = c->links; + c->links = newlink; + + /* broadcast the data */ + msg_data.rfc5988_link = newlink; + content_broadcast(c, CONTENT_MSG_LINK, msg_data); + + return true; +} + /** * Retrieve computed type of content * diff --git a/content/content.h b/content/content.h index eb8fbe689..3fd0bd63a 100644 --- a/content/content.h +++ b/content/content.h @@ -75,6 +75,18 @@ typedef enum { CONTENT_MSG_LINK, /**< RFC5988 link */ } content_msg; +/** RFC5988 metadata link */ +struct content_rfc5988_link { + struct content_rfc5988_link *next; /**< next rfc5988_link in list */ + + lwc_string *rel; /**< the link relationship - must be present */ + nsurl *href; /* the link href - must be present */ + lwc_string *hreflang; + lwc_string *type; + lwc_string *media; + lwc_string *sizes; +}; + /** Extra data for some content_msg messages. */ union content_msg_data { const char *error; /**< Error message, for CONTENT_MSG_ERROR. */ @@ -97,11 +109,7 @@ union content_msg_data { /** Low-level cache handle, for CONTENT_MSG_DOWNLOAD */ struct llcache_handle *download; /** rfc5988 link data CONTENT_MSG_RFC5988_LINK */ - struct { - nsurl *url; - char *rel; - char *type; - } rfc5988_link; + struct content_rfc5988_link *rfc5988_link; }; @@ -167,6 +175,8 @@ void content_close(struct hlcache_handle *h); struct selection *content_get_selection(struct hlcache_handle *h); void content_get_contextual_content(struct hlcache_handle *h, int x, int y, struct contextual_content *data); +struct content_rfc5988_link *content_find_rfc5988_link(struct hlcache_handle *c, + lwc_string *rel); /* Member accessors */ content_type content_get_type(struct hlcache_handle *c); diff --git a/content/content_protected.h b/content/content_protected.h index eeaa67cbf..05b1940f2 100644 --- a/content/content_protected.h +++ b/content/content_protected.h @@ -108,6 +108,8 @@ struct content { nsurl *refresh; /**< URL for refresh request */ + struct content_rfc5988_link *links; /**< list of metadata links */ + unsigned int time; /**< Creation time, if LOADING or READY, otherwise total time. */ @@ -160,6 +162,11 @@ void content_broadcast(struct content *c, content_msg msg, void content_add_error(struct content *c, const char *token, unsigned int line); +bool content__add_rfc5988_link(struct content *c, + const struct content_rfc5988_link *link); +struct content_rfc5988_link *content__free_rfc5988_link( + struct content_rfc5988_link *link); + void content__reformat(struct content *c, bool background, int width, int height); void content__request_redraw(struct content *c, diff --git a/desktop/browser.c b/desktop/browser.c index ed1cfc345..db9c9249f 100644 --- a/desktop/browser.c +++ b/desktop/browser.c @@ -877,6 +877,8 @@ void browser_window_go_post(struct browser_window *bw, const char *url, nsurl_unref(nsref); } + + /** * Callback for fetchcache() for browser window favicon fetches. */ @@ -889,16 +891,47 @@ static nserror browser_window_favicon_callback(hlcache_handle *c, switch (event->type) { case CONTENT_MSG_DONE: LOG(("favicon contents for %p done!", bw)); - /* content_get_bitmap on the bw->favicon should give - us the favicon at this point - */ + + if (bw->current_favicon != NULL) { + content_status status = + content_get_status(bw->current_favicon); + + if ((status == CONTENT_STATUS_READY) || + (status == CONTENT_STATUS_DONE)) + content_close(bw->current_favicon); + + hlcache_handle_release(bw->current_favicon); + } + + bw->current_favicon = c; + bw->loading_favicon = NULL; + + /* content_get_bitmap on the hlcache_handle should give + * us the favicon bitmap at this point + */ if (bw->window != NULL) { - gui_window_set_icon(bw->window, bw->favicon); + gui_window_set_icon(bw->window, c); } else { LOG(("null browser window on favicon!")); } break; + case CONTENT_MSG_ERROR: + + /* clean up after ourselves */ + if (c == bw->loading_favicon) + bw->loading_favicon = NULL; + else if (c == bw->current_favicon) { + bw->current_favicon = NULL; + } + + hlcache_handle_release(c); + + /** @todo if this was not the default + * resource:favicon.png start a fetch for it. + */ + break; + default: LOG(("favicon unhandled event")); break; @@ -906,6 +939,57 @@ static nserror browser_window_favicon_callback(hlcache_handle *c, return NSERROR_OK; } +static void browser_window_update_favicon(hlcache_handle *c, + struct browser_window *bw) +{ + lwc_string *icon_str; + struct content_rfc5988_link *link; + nsurl *nsref = NULL; + nsurl *nsurl; + nserror error; + + /* already fetching the favicon - use that */ + if (bw->loading_favicon != NULL) + return; + + /* look for favicon metadata link */ + lwc_intern_string("icon", SLEN("icon"), &icon_str); + link = content_find_rfc5988_link(c, icon_str); + lwc_string_unref(icon_str); + + if (link == NULL) { + lwc_intern_string("shortcut icon", SLEN("shortcut_icon"), + &icon_str); + link = content_find_rfc5988_link(c, icon_str); + lwc_string_unref(icon_str); + } + + if (link == NULL) { + /* no favicon via link, try for the default location - bletch */ + error = nsurl_join(content_get_url(c), "/favicon.ico", &nsurl); + if (error != NSERROR_OK) { + LOG(("Unable to create default location url")); + return; + } + } else { + nsurl = link->href; + nsurl_ref(nsurl); + } + + hlcache_handle_retrieve(nsurl, + HLCACHE_RETRIEVE_MAY_DOWNLOAD | + HLCACHE_RETRIEVE_SNIFF_TYPE, + nsref, + NULL, + browser_window_favicon_callback, + bw, + NULL, + CONTENT_IMAGE, + &bw->loading_favicon); + + nsurl_unref(nsurl); +} + /** * Callback for fetchcache() for browser window fetches. */ @@ -962,18 +1046,6 @@ nserror browser_window_callback(hlcache_handle *c, hlcache_handle_release(bw->current_content); } - if (bw->favicon != NULL) { - content_status status = - content_get_status(bw->favicon); - - if (status == CONTENT_STATUS_READY || - status == CONTENT_STATUS_DONE) - content_close(bw->favicon); - - hlcache_handle_release(bw->favicon); - bw->favicon = NULL; - } - bw->current_content = c; bw->loading_content = NULL; @@ -1040,32 +1112,7 @@ nserror browser_window_callback(hlcache_handle *c, browser_window_update(bw, false); browser_window_set_status(bw, content_get_status_message(c)); browser_window_stop_throbber(bw); - if (bw->favicon == NULL) { - /* no favicon via link - try for the default location - bletch */ - nsurl *nsref = NULL; - nsurl *nsurl; - nserror error; - - error = nsurl_join(content_get_url(c), "/favicon.ico", &nsurl); - if (error == NSERROR_OK) { - - - hlcache_handle_retrieve(nsurl, - HLCACHE_RETRIEVE_MAY_DOWNLOAD | - HLCACHE_RETRIEVE_SNIFF_TYPE, - nsref, - NULL, - browser_window_favicon_callback, - bw, - NULL, - CONTENT_IMAGE, - &bw->favicon); - - nsurl_unref(nsurl); - - } - - } + browser_window_update_favicon(c, bw); history_update(bw->history, c); hotlist_visited(c); @@ -1145,13 +1192,26 @@ nserror browser_window_callback(hlcache_handle *c, case CONTENT_MSG_LINK: /* content has an rfc5988 link element */ { nsurl *nsref = NULL; - if ((bw->favicon == NULL) && - (strstr(event->data.rfc5988_link.rel, "icon") != NULL)) { - /* its a favicon start a fetch for it */ - LOG(("fetching favicon rel:%s '%s'", - event->data.rfc5988_link.rel, - nsurl_access(event->data.rfc5988_link.url))); - hlcache_handle_retrieve(event->data.rfc5988_link.url, + lwc_string *icon_str; + lwc_string *shortcut_icon_str; + bool icon_match; + bool shortcut_icon_match; + + lwc_intern_string("icon", SLEN("icon"), &icon_str); + lwc_intern_string("shortcut icon", SLEN("shortcut_icon"), &shortcut_icon_str); + lwc_string_caseless_isequal(event->data.rfc5988_link->rel, icon_str, &icon_match); + lwc_string_caseless_isequal(event->data.rfc5988_link->rel, shortcut_icon_str, &shortcut_icon_match); + lwc_string_unref(icon_str); + lwc_string_unref(shortcut_icon_str); + + if ((bw->loading_favicon == NULL) && + (icon_match || shortcut_icon_match)) { + /* its a favicon and we are not already fetching one + start a fetch for it */ + LOG(("fetching favicon rel:%p '%s'", + event->data.rfc5988_link->rel, + nsurl_access(event->data.rfc5988_link->href))); + hlcache_handle_retrieve(event->data.rfc5988_link->href, HLCACHE_RETRIEVE_MAY_DOWNLOAD | HLCACHE_RETRIEVE_SNIFF_TYPE, nsref, @@ -1160,7 +1220,7 @@ nserror browser_window_callback(hlcache_handle *c, bw, NULL, CONTENT_IMAGE, - &bw->favicon); + &bw->loading_favicon); } } break; @@ -1724,16 +1784,21 @@ void browser_window_destroy_internal(struct browser_window *bw) bw->current_content = NULL; } - if (bw->favicon != NULL) { - content_status status = - content_get_status(bw->favicon); + if (bw->loading_favicon != NULL) { + hlcache_handle_abort(bw->loading_favicon); + hlcache_handle_release(bw->loading_favicon); + bw->loading_favicon = NULL; + } + + if (bw->current_favicon != NULL) { + content_status status = content_get_status(bw->current_favicon); if (status == CONTENT_STATUS_READY || status == CONTENT_STATUS_DONE) - content_close(bw->favicon); + content_close(bw->current_favicon); - hlcache_handle_release(bw->favicon); - bw->favicon = NULL; + hlcache_handle_release(bw->current_favicon); + bw->current_favicon = NULL; } if (bw->box != NULL) { diff --git a/desktop/browser.h b/desktop/browser.h index c96c4f177..e990f1173 100644 --- a/desktop/browser.h +++ b/desktop/browser.h @@ -76,7 +76,9 @@ struct browser_window { struct hlcache_handle *loading_content; /** Page Favicon */ - struct hlcache_handle *favicon; + struct hlcache_handle *current_favicon; + /** handle for favicon which we started loading early */ + struct hlcache_handle *loading_favicon; /** Window history structure. */ struct history *history; diff --git a/image/image_cache.c b/image/image_cache.c index 9a478a605..32fe74571 100644 --- a/image/image_cache.c +++ b/image/image_cache.c @@ -283,7 +283,7 @@ static void image_cache__background_update(void *p) } /* exported interface documented in image_cache.h */ -struct bitmap *image_cache_get_bitmap(struct content *c) +struct bitmap *image_cache_get_bitmap(const struct content *c) { struct image_cache_entry_s *centry; @@ -576,14 +576,7 @@ void image_cache_destroy(struct content *content) void *image_cache_get_internal(const struct content *c, void *context) { - struct image_cache_entry_s *centry; - - centry = image_cache__find(c); - if (centry == NULL) { - return NULL; - } - - return centry->bitmap; + return image_cache_get_bitmap(c); } content_type image_cache_content_type(void) diff --git a/image/image_cache.h b/image/image_cache.h index 568f3e951..e879c740d 100644 --- a/image/image_cache.h +++ b/image/image_cache.h @@ -79,7 +79,7 @@ nserror image_cache_remove(struct content *content); /** Obtain a bitmap from a content converting from source if neccessary. */ -struct bitmap *image_cache_get_bitmap(struct content *c); +struct bitmap *image_cache_get_bitmap(const struct content *c); /** Obtain a bitmap from a content with no conversion */ struct bitmap *image_cache_find_bitmap(struct content *c); diff --git a/render/html.c b/render/html.c index 4c08a6620..09efb3600 100644 --- a/render/html.c +++ b/render/html.c @@ -740,6 +740,78 @@ void html_box_convert_done(html_content *c, bool success) } +/** process link node */ +static bool html_process_link(html_content *c, xmlNode *node) +{ + struct content_rfc5988_link *link; + char *xmlstr; + nserror error; + lwc_string *rel; + nsurl *href; + + /* check that the relation exists - w3c spec says must be present */ + xmlstr = (char *)xmlGetProp(node, (const xmlChar *)"rel"); + if (xmlstr == NULL) { + return false; + } + lwc_intern_string(xmlstr, strlen(xmlstr), &rel); + xmlFree(xmlstr); + + /* check that the href exists - w3c spec says must be present */ + xmlstr = (char *)xmlGetProp(node, (const xmlChar *) "href"); + if (xmlstr == NULL) { + return false; + } + error = nsurl_join(c->base_url, xmlstr, &href); + xmlFree(xmlstr); + if (error != NSERROR_OK) { + lwc_string_unref(rel); + return false; + } + + link = calloc(1, sizeof(struct content_rfc5988_link)); + if (link == NULL) { + lwc_string_unref(rel); + nsurl_unref(href); + return false; + } + link->rel = rel; + link->href = href; + + /* look for optional properties */ + xmlstr = (char *)xmlGetProp(node, (const xmlChar *) "hreflang"); + if (xmlstr != NULL) { + lwc_intern_string(xmlstr, strlen(xmlstr), &link->hreflang); + xmlFree(xmlstr); + } + + xmlstr = (char *) xmlGetProp(node, (const xmlChar *) "type"); + if (xmlstr != NULL) { + lwc_intern_string(xmlstr, strlen(xmlstr), &link->type); + xmlFree(xmlstr); + } + + xmlstr = (char *) xmlGetProp(node, (const xmlChar *) "media"); + if (xmlstr != NULL) { + lwc_intern_string(xmlstr, strlen(xmlstr), &link->media); + xmlFree(xmlstr); + } + + xmlstr = (char *) xmlGetProp(node, (const xmlChar *) "sizes"); + if (xmlstr != NULL) { + lwc_intern_string(xmlstr, strlen(xmlstr), &link->sizes); + xmlFree(xmlstr); + } + + /* add to content */ + content__add_rfc5988_link(&c->base, link); + + /* release this copy */ + content__free_rfc5988_link(link); + + return true; +} + /** * Process elements in . * @@ -811,34 +883,7 @@ bool html_head(html_content *c, xmlNode *head) xmlFree(s); } } else if (strcmp((const char *) node->name, "link") == 0) { - union content_msg_data msg_data; - char *href; - nserror error; - - href = (char *) xmlGetProp(node, (const xmlChar *) "href"); - if (href) { - error = nsurl_join(c->base_url, href, &msg_data.rfc5988_link.url); - - xmlFree(href); - } - - msg_data.rfc5988_link.rel = (char *)xmlGetProp(node, - (const xmlChar *)"rel"); - msg_data.rfc5988_link.type = (char *)xmlGetProp(node, - (const xmlChar *)"type"); - - content_broadcast(&c->base, CONTENT_MSG_LINK, msg_data); - - if (error == NSERROR_OK) { - nsurl_unref(msg_data.rfc5988_link.url); - } - if (msg_data.rfc5988_link.rel) { - xmlFree(msg_data.rfc5988_link.rel); - } - if (msg_data.rfc5988_link.type) { - xmlFree(msg_data.rfc5988_link.type); - } - + html_process_link(c, node); } } return true;