From ad6fcea6b008638ca532436ecf8ba9fe7e41ffdd Mon Sep 17 00:00:00 2001 From: John Mark Bell Date: Sun, 3 Feb 2008 12:04:48 +0000 Subject: Add url_fragment to extract fragment from URL Optionally allow url_compare to ignore fragments in comparison Fix handling of url_compare result in a few places Fix redirects which contain fragments in the Location header svn path=/trunk/netsurf/; revision=3826 --- content/content.c | 1 + content/content.h | 5 ++-- content/fetchcache.c | 18 +++++++------ desktop/browser.c | 71 ++++++++++++++++++++++++++++++++++------------------ render/directory.c | 4 +-- riscos/menus.c | 6 +++-- riscos/window.c | 4 +-- utils/url.c | 53 ++++++++++++++++++++++++++++++++++----- utils/url.h | 3 ++- 9 files changed, 118 insertions(+), 47 deletions(-) diff --git a/content/content.c b/content/content.c index c8389d80e..9310eb180 100644 --- a/content/content.c +++ b/content/content.c @@ -608,6 +608,7 @@ bool content_set_type(struct content *c, content_type type, 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, diff --git a/content/content.h b/content/content.h index 324af952f..7fb11ca2a 100644 --- a/content/content.h +++ b/content/content.h @@ -84,7 +84,7 @@ typedef enum { CONTENT_MSG_STATUS, /**< new status string */ CONTENT_MSG_REFORMAT, /**< content_reformat done */ CONTENT_MSG_REDRAW, /**< needs redraw (eg. new animation frame) */ - CONTENT_MSG_NEWPTR, /**< address of structure has changed */ + CONTENT_MSG_NEWPTR, /**< structure has been replaced */ CONTENT_MSG_REFRESH, /**< wants refresh */ #ifdef WITH_AUTH CONTENT_MSG_AUTH, /**< authentication required */ @@ -97,7 +97,8 @@ typedef enum { /** Extra data for some content_msg messages. */ union content_msg_data { const char *error; /**< Error message, for CONTENT_MSG_ERROR. */ - char *redirect; /**< Redirect URL, for CONTENT_MSG_REDIRECT. */ + const char *new_url; /**< Replacement URL (or NULL if the same + * as previous), for CONTENT_MSG_NEWPTR. */ /** Area of content which needs redrawing, for CONTENT_MSG_REDRAW. */ struct { float x, y, width, height; diff --git a/content/fetchcache.c b/content/fetchcache.c index b6b17a618..4fdbb93ca 100644 --- a/content/fetchcache.c +++ b/content/fetchcache.c @@ -689,6 +689,8 @@ void fetchcache_notmodified(struct content *c, const void *data) } content_remove_user(c, callback, p1, p2); + + msg_data.new_url = NULL; callback(CONTENT_MSG_NEWPTR, fb, p1, p2, msg_data); /* and catch user up with fallback's state */ @@ -786,25 +788,26 @@ void fetchcache_redirect(struct content *c, const void *data, { char *url, *url1; char *referer, *parent_url; - long http_code = fetch_http_code(c->fetch); - const char *ref = fetch_get_referer(c->fetch); - const char *parent = fetch_get_parent_url(c->fetch); + long http_code; + const char *ref; + const char *parent; union content_msg_data msg_data; url_func_result result; /* Preconditions */ assert(c && data); assert(c->status == CONTENT_STATUS_TYPE_UNKNOWN); - /* Ensure a redirect happened */ - assert(300 <= http_code && http_code <= 399); - /* 304 is handled by fetch_notmodified() */ - assert(http_code != 304); /* Extract fetch details */ http_code = fetch_http_code(c->fetch); ref = fetch_get_referer(c->fetch); parent = fetch_get_parent_url(c->fetch); + /* Ensure a redirect happened */ + assert(300 <= http_code && http_code <= 399); + /* 304 is handled by fetch_notmodified() */ + assert(http_code != 304); + /* Clone referer and parent url * originals are destroyed in fetch_abort() */ referer = ref ? strdup(ref) : NULL; @@ -941,6 +944,7 @@ void fetchcache_redirect(struct content *c, const void *data, replacement->redirect_count = c->redirect_count + 1; /* Notify user that content has changed */ + msg_data.new_url = url; callback(CONTENT_MSG_NEWPTR, replacement, p1, p2, msg_data); /* Start fetching the replacement content */ diff --git a/desktop/browser.c b/desktop/browser.c index 1f2709ec8..3cfcd0352 100644 --- a/desktop/browser.c +++ b/desktop/browser.c @@ -262,7 +262,7 @@ void browser_window_go_post(struct browser_window *bw, const char *url, { struct content *c; char *url2; - char *hash; + char *fragment; url_func_result res; char url_buf[256]; int depth = 0; @@ -296,38 +296,38 @@ void browser_window_go_post(struct browser_window *bw, const char *url, if (!download) gui_window_set_url(bw->window, url2); + free(bw->frag_id); + bw->frag_id = NULL; + /* find any fragment identifier on end of URL */ - hash = strchr(url2, '#'); - if (bw->frag_id) { - free(bw->frag_id); - bw->frag_id = 0; - } - if (hash) { - char *frag = curl_unescape(hash+1, strlen(hash + 1)); - if (!frag) { - free(url2); - warn_user("NoMemory", 0); - return; - } + res = url_fragment(url2, &fragment); + if (res == URL_FUNC_NOMEM) { + free(url2); + warn_user("NoMemory", 0); + return; + } else if (res == URL_FUNC_OK) { + bool same_url = false; - bw->frag_id = strdup(frag); - curl_free(frag); + bw->frag_id = fragment; - if (!bw->frag_id) { - free(url2); - warn_user("NoMemory", 0); - return; + /* 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 (res == URL_FUNC_NOMEM) { + free(url2); + warn_user("NoMemory", 0); + return; + } else if (res == URL_FUNC_FAILED) { + same_url = false; + } } /* if we're simply moving to another ID on the same page, * don't bother to fetch, just update the window. */ - if (!post_urlenc && !post_multipart && !strchr(url2, '?') && - bw->current_content && bw->current_content->url && - strncasecmp(bw->current_content->url, - url2, hash - url2) == 0 && - strlen(bw->current_content->url) == - (unsigned int)(hash - url2)) { + if (same_url && !post_urlenc && !post_multipart && + !strchr(url2, '?')) { free(url2); browser_window_update(bw, false); snprintf(url_buf, sizeof url_buf, "%s#%s", @@ -532,6 +532,27 @@ void browser_window_callback(content_msg msg, struct content *c, 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; #ifdef WITH_AUTH diff --git a/render/directory.c b/render/directory.c index 2f280fabe..61abf1b1f 100644 --- a/render/directory.c +++ b/render/directory.c @@ -96,8 +96,8 @@ bool directory_convert(struct content *c, int width, int height) { res = url_parent(c->url, &up); if (res == URL_FUNC_OK) { - res = url_compare(c->url, up, &compare); - if (!compare) { + res = url_compare(c->url, up, false, &compare); + if ((res == URL_FUNC_OK) && !compare) { snprintf(buffer, sizeof(buffer), "[..]\n"); htmlParseChunk(c->data.html.parser, buffer, diff --git a/riscos/menus.c b/riscos/menus.c index ff4537179..adfb9a109 100644 --- a/riscos/menus.c +++ b/riscos/menus.c @@ -1989,8 +1989,10 @@ void ro_gui_menu_prepare_action(wimp_w owner, menu_action action, if (result) { res = url_parent(c->url, &parent); if (res == URL_FUNC_OK) { - res = url_compare(c->url, parent, &compare); - result = !compare; + res = url_compare(c->url, parent, + false, &compare); + if (res == URL_FUNC_OK) + result = !compare; free(parent); } else { result = false; diff --git a/riscos/window.c b/riscos/window.c index 7824bbe7a..21e57b9f8 100644 --- a/riscos/window.c +++ b/riscos/window.c @@ -3329,8 +3329,8 @@ bool ro_gui_window_navigate_up(struct gui_window *g, const char *url) { res = url_parent(url, &parent); if (res == URL_FUNC_OK) { - res = url_compare(url, parent, &compare); - if (!compare && (res == URL_FUNC_OK)) + res = url_compare(url, parent, false, &compare); + if ((res == URL_FUNC_OK) && !compare) browser_window_go(g->bw, parent, 0, true); free(parent); } diff --git a/utils/url.c b/utils/url.c index 3a43e1765..ac46944eb 100644 --- a/utils/url.c +++ b/utils/url.c @@ -30,6 +30,7 @@ #include #include #include +#include "curl/curl.h" #include "utils/log.h" #include "utils/url.h" #include "utils/utils.h" @@ -718,6 +719,44 @@ url_func_result url_leafname(const char *url, char **result) return status; } +/** + * Extract fragment from an URL + * This will unescape any %xx entities in the fragment + * + * \param url an absolute URL + * \param result pointer to pointer to buffer to hold result + * \return URL_FUNC_OK on success + */ + +url_func_result url_fragment(const char *url, char **result) +{ + url_func_result status; + struct url_components components; + + assert(url); + + status = url_get_components(url, &components); + if (status == URL_FUNC_OK) { + if (!components.fragment) { + status = URL_FUNC_FAILED; + } else { + char *frag = curl_unescape(components.fragment, + strlen(components.fragment)); + if (!frag) { + status = URL_FUNC_NOMEM; + } else { + *result = strdup(frag); + if (!(*result)) + status = URL_FUNC_NOMEM; + curl_free(frag); + } + } + } + + url_destroy_components(&components); + return status; +} + /** * Attempt to find a nice filename for a URL. * @@ -897,12 +936,14 @@ url_func_result url_escape(const char *unescaped, bool sptoplus, /** * Compare two absolute, normalized URLs * - * \param url1 URL 1 - * \param url2 URL 2 - * \param result Pointer to location to store result (true if URLs match) + * \param url1 URL 1 + * \param url2 URL 2 + * \param nofrag Ignore fragment part in comparison + * \param result Pointer to location to store result (true if URLs match) * \return URL_FUNC_OK on success */ -url_func_result url_compare(const char *url1, const char *url2, bool *result) +url_func_result url_compare(const char *url1, const char *url2, + bool nofrag, bool *result) { url_func_result status; struct url_components c1, c2; @@ -930,7 +971,7 @@ url_func_result url_compare(const char *url1, const char *url2, bool *result) ((c1.path && c2.path) || (!c1.path && !c2.path)) && ((c1.query && c2.query) || (!c1.query && !c2.query)) && - ((c1.fragment && c2.fragment) || + (nofrag || (c1.fragment && c2.fragment) || (!c1.fragment && !c2.fragment))) { if (c1.scheme) @@ -946,7 +987,7 @@ url_func_result url_compare(const char *url1, const char *url2, bool *result) if (c1.query) res &= strcmp(c1.query, c2.query) == 0; - if (c1.fragment) + if (!nofrag && c1.fragment) res &= strcmp(c1.fragment, c2.fragment) == 0; } else { /* Can't match */ diff --git a/utils/url.h b/utils/url.h index b35b6e376..9a1b934a5 100644 --- a/utils/url.h +++ b/utils/url.h @@ -54,8 +54,9 @@ url_func_result url_parent(const char *url, char **result); url_func_result url_plq(const char *url, char **result); url_func_result url_path(const char *url, char **result); url_func_result url_leafname(const char *url, char **result); +url_func_result url_fragment(const char *url, char **result); url_func_result url_compare(const char *url1, const char *url2, - bool *result); + bool nofrag, bool *result); url_func_result url_get_components(const char *url, struct url_components *result); -- cgit v1.2.3