From 74fa727509874983884a35b44b646be034b1fd69 Mon Sep 17 00:00:00 2001 From: Richard Wilson Date: Sat, 2 Sep 2006 15:52:41 +0000 Subject: Experimental new frames code. svn path=/trunk/netsurf/; revision=2906 --- !NetSurf/!Sprites22,ff9 | Bin 18840 -> 18728 bytes !NetSurf/5Sprites22,ff9 | Bin 40912 -> 40800 bytes !NetSurf/Resources/de/Messages | 12 +- !NetSurf/Resources/de/Templates,fec | Bin 18687 -> 18840 bytes !NetSurf/Resources/en/Messages | 12 +- !NetSurf/Resources/en/Templates,fec | Bin 18616 -> 18781 bytes !NetSurf/Resources/fr/Messages | 14 +- !NetSurf/Resources/fr/Templates,fec | Bin 19029 -> 19194 bytes !NetSurf/Resources/nl/Messages | 12 +- !NetSurf/Resources/nl/Templates,fec | Bin 18702 -> 18867 bytes content/content.c | 2 +- content/urldb.c | 61 ++ content/urldb.h | 5 + desktop/browser.c | 1115 ++++++++++++++++++++++++++++++----- desktop/browser.h | 74 ++- desktop/gui.h | 6 +- desktop/history_core.c | 76 +-- desktop/netsurf.c | 5 + desktop/options.c | 5 +- desktop/options.h | 1 + render/box.h | 1 + render/box_construct.c | 530 ++++++++++------- render/html.c | 186 +++--- render/html.h | 66 ++- riscos/configure/con_content.c | 14 +- riscos/gui.c | 34 +- riscos/gui.h | 6 +- riscos/theme.c | 5 +- riscos/window.c | 859 ++++++++++++++++++--------- utils/filename.c | 24 + utils/filename.h | 1 + 31 files changed, 2319 insertions(+), 807 deletions(-) diff --git a/!NetSurf/!Sprites22,ff9 b/!NetSurf/!Sprites22,ff9 index bdeb46384..5c9d32287 100644 Binary files a/!NetSurf/!Sprites22,ff9 and b/!NetSurf/!Sprites22,ff9 differ diff --git a/!NetSurf/5Sprites22,ff9 b/!NetSurf/5Sprites22,ff9 index 3b8ef0936..766471428 100755 Binary files a/!NetSurf/5Sprites22,ff9 and b/!NetSurf/5Sprites22,ff9 differ diff --git a/!NetSurf/Resources/de/Messages b/!NetSurf/Resources/de/Messages index 8e00b308a..430b87b56 100644 --- a/!NetSurf/Resources/de/Messages +++ b/!NetSurf/Resources/de/Messages @@ -363,6 +363,11 @@ Encoding1:detected Encoding2:from EncodingUnk:Unknown +# Misc +# +Selecting:Selecting +FrameDrag:Resizing frames + # Errors # ====== @@ -809,9 +814,10 @@ HelpContentConfig:\Tcontent configuration \w HelpContentConfig2:This indicates whether NetSurf will atempt to block advertisements on web pages|MIn rare circumstances, this option may cause valid content to be blocked too. HelpContentConfig3:This indicates whether NetSurf will stop web sites from automatically opening new windows on your desktop. HelpContentConfig4:This indicates whether NetSurf will allow external plug-ins to handle additional types of content, such as Flash. -HelpContentConfig5:\Sreset the Content options back to their default values. -HelpContentConfig6:\Sclose this \w without saving changes.|M\Areturn the cache options to the last saved configuration. -HelpContentConfig7:\Ssave these settings and close the \w.|M\Asave these settings without closing the \w. +HelpContentConfig7:This indicates whether NetSurf will allow links to open in new windows. +HelpContentConfig8:\Sreset the Content options back to their default values. +HelpContentConfig9:\Sclose this \w without saving changes.|M\Areturn the cache options to the last saved configuration. +HelpContentConfig10:\Ssave these settings and close the \w.|M\Asave these settings without closing the \w. HelpFontConfig:\font configuration \w HelpFontConfig3:\Tcurrently selected sans-serif font.|MNetSurf will use this font wherever a web page specifies a sans-serif typeface. diff --git a/!NetSurf/Resources/de/Templates,fec b/!NetSurf/Resources/de/Templates,fec index 821894164..9e9adcce7 100755 Binary files a/!NetSurf/Resources/de/Templates,fec and b/!NetSurf/Resources/de/Templates,fec differ diff --git a/!NetSurf/Resources/en/Messages b/!NetSurf/Resources/en/Messages index 796c93add..b35f6aaed 100644 --- a/!NetSurf/Resources/en/Messages +++ b/!NetSurf/Resources/en/Messages @@ -363,6 +363,11 @@ Encoding1:detected Encoding2:from EncodingUnk:Unknown +# Misc +# +Selecting:Selecting +FrameDrag:Resizing frames + # Errors # ====== @@ -809,9 +814,10 @@ HelpContentConfig:\Tcontent configuration \w HelpContentConfig2:This indicates whether NetSurf will attempt to block advertisements on web pages|MIn rare circumstances, this option may cause valid content to be blocked too. HelpContentConfig3:This indicates whether NetSurf will stop web sites from automatically opening new windows on your desktop. HelpContentConfig4:This indicates whether NetSurf will allow external plug-ins to handle additional types of content, such as Flash. -HelpContentConfig5:\Sreset the Content options back to their default values. -HelpContentConfig6:\Sclose this \w without saving changes.|M\Areturn the content options to the last saved configuration. -HelpContentConfig7:\Ssave these settings and close the \w.|M\Asave these settings without closing the \w. +HelpContentConfig7:This indicates whether NetSurf will allow links to open in new windows. +HelpContentConfig8:\Sreset the Content options back to their default values. +HelpContentConfig9:\Sclose this \w without saving changes.|M\Areturn the content options to the last saved configuration. +HelpContentConfig10:\Ssave these settings and close the \w.|M\Asave these settings without closing the \w. HelpFontConfig:\Tfont configuration \w HelpFontConfig3:\Tcurrently selected sans-serif font.|MNetSurf will use this font wherever a web page specifies a sans-serif typeface. diff --git a/!NetSurf/Resources/en/Templates,fec b/!NetSurf/Resources/en/Templates,fec index 51a7b44ac..a821819cd 100644 Binary files a/!NetSurf/Resources/en/Templates,fec and b/!NetSurf/Resources/en/Templates,fec differ diff --git a/!NetSurf/Resources/fr/Messages b/!NetSurf/Resources/fr/Messages index 0b7732a6b..9ba9db628 100644 --- a/!NetSurf/Resources/fr/Messages +++ b/!NetSurf/Resources/fr/Messages @@ -363,6 +363,11 @@ Encoding1:détecté Encoding2:de EncodingUnk:Inconnu +# Misc +# +Selecting:Selecting +FrameDrag:Resizing frames + # Errors # ====== @@ -806,12 +811,13 @@ HelpConnectConfig28:\Sfermer cette fenêtre sans sauvegarder les changements.|M\ HelpConnectConfig29:\Ssauver ces réglages et fermer la fenêtre.|M\Asauver ces réglages sans fermer la fenêtre. HelpContentConfig:\Tla fenêtre de configuration du Contenu -HelpContentConfig2:Ceci indique si Netsurf doit essayer de bloquer les pubs sur les pages web.|MEn de rares circonstances, cette option peut également bloquer du contenu valide. +HelpContentConfig2:Ceci indique si Netsurf doit essayer de bloquer les pubs sur les pages web.|MEn de rares circonstances, cette option peut également bloquer du contenu valide. HelpContentConfig3:Ceci indique si Netsurf doit empêcher les sites web d'ouvrir automatiquement de nouvelles fenêtres sur votre Bureau. HelpContentConfig4:Ceci indique si Netsurf doit autoriser les plug-ins externes à manipuler des types de contenu supplémentaires, comme le Flash. -HelpContentConfig5:\Srevenir aux valeurs par défaut des options du Contenu. -HelpContentConfig6:\Sfermer cette fenêtre sans sauver les changements|M\Arevenir aux options de Contenu précédemment sauvegardées. -HelpContentConfig7:\Ssauver ces réglages et fermer la fenêtre.|M\Asauver ces réglages sans fermer la fenêtre. +HelpContentConfig7:This indicates whether NetSurf will allow links to open in new windows. +HelpContentConfig8:\Srevenir aux valeurs par défaut des options du Contenu. +HelpContentConfig9:\Sfermer cette fenêtre sans sauver les changements|M\Arevenir aux options de Contenu précédemment sauvegardées. +HelpContentConfig10:\Ssauver ces réglages et fermer la fenêtre.|M\Asauver ces réglages sans fermer la fenêtre. HelpFontConfig:\Tla fenêtre de configuration de Fontes HelpFontConfig3:\Tla fonte sans-sérif actuellement sélectionnée.|MNetsurf utilisera cette fonte à chaque fois qu'une page web spécifiera une police sans-sérif. diff --git a/!NetSurf/Resources/fr/Templates,fec b/!NetSurf/Resources/fr/Templates,fec index 61d278440..49ca62f20 100644 Binary files a/!NetSurf/Resources/fr/Templates,fec and b/!NetSurf/Resources/fr/Templates,fec differ diff --git a/!NetSurf/Resources/nl/Messages b/!NetSurf/Resources/nl/Messages index 8d41c6019..529451fa6 100644 --- a/!NetSurf/Resources/nl/Messages +++ b/!NetSurf/Resources/nl/Messages @@ -363,6 +363,11 @@ Encoding1:detected Encoding2:from EncodingUnk:Unknown +# Misc +# +Selecting:Selecting +FrameDrag:Resizing frames + # Errors # ====== @@ -809,9 +814,10 @@ HelpContentConfig:\Tcontent configuration \w HelpContentConfig2:This indicates whether NetSurf will atempt to block advertisements on web pages|MIn rare circumstances, this option may cause valid content to be blocked too. HelpContentConfig3:This indicates whether NetSurf will stop web sites from automatically opening new windows on your desktop. HelpContentConfig4:This indicates whether NetSurf will allow external plug-ins to handle additional types of content, such as Flash. -HelpContentConfig5:\Sreset the Content options back to their default values. -HelpContentConfig6:\Sclose this \w without saving changes.|M\Areturn the cache options to the last saved configuration. -HelpContentConfig7:\Ssave these settings and close the \w.|M\Asave these settings without closing the \w. +HelpContentConfig7:This indicates whether NetSurf will allow links to open in new windows. +HelpContentConfig8:\Sreset the Content options back to their default values. +HelpContentConfig9:\Sclose this \w without saving changes.|M\Areturn the cache options to the last saved configuration. +HelpContentConfig10:\Ssave these settings and close the \w.|M\Asave these settings without closing the \w. HelpFontConfig:\font configuration \w HelpFontConfig3:\Tcurrently selected sans-serif font.|MNetSurf will use this font wherever a web page specifies a sans-serif typeface. diff --git a/!NetSurf/Resources/nl/Templates,fec b/!NetSurf/Resources/nl/Templates,fec index 128de698c..6b58eb0e1 100644 Binary files a/!NetSurf/Resources/nl/Templates,fec and b/!NetSurf/Resources/nl/Templates,fec differ diff --git a/content/content.c b/content/content.c index 68c90a7f0..6b8d7b9fe 100644 --- a/content/content.c +++ b/content/content.c @@ -816,7 +816,6 @@ void content_destroy(struct content *c) if (c->type < HANDLER_MAP_COUNT && handler_map[c->type].destroy) handler_map[c->type].destroy(c); - talloc_free(c); } @@ -855,6 +854,7 @@ void content_quit(void) 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 && diff --git a/content/urldb.c b/content/urldb.c index f498f106e..1e51d45db 100644 --- a/content/urldb.c +++ b/content/urldb.c @@ -81,6 +81,7 @@ #include #include "netsurf/image/bitmap.h" +#include "netsurf/content/content.h" #include "netsurf/content/urldb.h" #include "netsurf/desktop/cookies.h" #include "netsurf/desktop/options.h" @@ -89,6 +90,7 @@ #include "netsurf/riscos/bitmap.h" #endif #include "netsurf/utils/log.h" +#include "netsurf/utils/filename.h" #include "netsurf/utils/url.h" #include "netsurf/utils/utils.h" @@ -117,6 +119,10 @@ struct auth_data { * username:password */ }; +struct cache_internal_data { + char filename[12]; /**< Cached filename, or first byte 0 for none */ +}; + struct url_internal_data { char *title; /**< Resource title */ unsigned int visits; /**< Visit count */ @@ -135,6 +141,7 @@ struct path_data { struct bitmap *thumb; /**< Thumbnail image of resource */ struct url_internal_data urld; /**< URL data for resource */ + struct cache_internal_data cache; /**< Cache data for resource */ struct auth_data auth; /**< Authentication data for resource */ struct cookie_internal_data *cookies; /**< Cookies associated with resource */ @@ -3361,6 +3368,60 @@ void urldb_save_cookie_paths(FILE *fp, struct path_data *parent) } +/** + * Sets the content data associated with a particular URL + * + * \param url the URL to associate content with + * \param content the content to associate + * \return true on success, false otherwise + */ +bool urldb_set_cache_data(const char *url, const struct content *content) { + struct path_data *p; + char *filename; + + assert(url && content); + + p = urldb_find_url(url); + if (!p) + return false; + + /* new filename needed */ + if (p->cache.filename[0] == 0) { + filename = filename_request(); + if (!filename) + return false; + sprintf(p->cache.filename, filename); + } + + /* todo: save content, set cache data etc */ + return true; +} + + +/** + * Gets a file:// URL for the cached data associated with a URL + * + * \param url the URL to get the associated content for + * \return a local URL allocated on heap, or NULL + */ +char *urldb_get_cache_data(const char *url) { + struct path_data *p; + + assert(url); + + p = urldb_find_url(url); + if (!p) + return NULL; + + /* no file cache */ + if (p->cache.filename[0] == 0) + return NULL; + + /* todo: handle cache expiry etc */ + return filename_as_url(p->cache.filename); +} + + #ifdef TEST_URLDB int option_expire_url = 0; diff --git a/content/urldb.h b/content/urldb.h index 20d97ebca..aa8ec7742 100644 --- a/content/urldb.h +++ b/content/urldb.h @@ -14,6 +14,7 @@ #include #include +#include "netsurf/content/content.h" #include "netsurf/content/content_type.h" typedef enum { @@ -100,4 +101,8 @@ void urldb_delete_cookie(const char *domain, const char *path, const char *name) void urldb_load_cookies(const char *filename); void urldb_save_cookies(const char *filename); +/* Cache */ +bool urldb_set_cache_data(const char *url, const struct content *content); +char *urldb_get_cache_data(const char *url); + #endif diff --git a/desktop/browser.c b/desktop/browser.c index d35ea1120..7efbad534 100644 --- a/desktop/browser.c +++ b/desktop/browser.c @@ -1,7 +1,7 @@ /* * This file is part of NetSurf, http://netsurf.sourceforge.net/ * Licensed under the GNU General Public License, - * http://www.opensource.org/licenses/gpl-license + * http://www.opensource.org/licenses/gpl-license * Copyright 2003 Phil Mellor * Copyright 2005 James Bursa * Copyright 2004 Andrew Timmins @@ -48,6 +48,8 @@ #include "netsurf/utils/utils.h" #include "netsurf/utils/utf8.h" +/** maximum frame resize margin */ +#define FRAME_RESIZE 6 /** browser window which is being redrawn. Valid only during redraw. */ struct browser_window *current_redraw_browser; @@ -55,10 +57,13 @@ struct browser_window *current_redraw_browser; /** fake content for being saved as a link */ struct content browser_window_href_content; - +static void browser_window_resize_frame(struct browser_window *bw, int x, int y); +static bool browser_window_resolve_frame_dimension(struct browser_window *bw, + struct browser_window *sibling, int x, int y, bool width, bool height); static void browser_window_callback(content_msg msg, struct content *c, intptr_t p1, intptr_t p2, union content_msg_data data); static void browser_window_refresh(void *p); +static bool browser_window_check_throbber(struct browser_window *bw); static void browser_window_convert_to_download(struct browser_window *bw); static void browser_window_start_throbber(struct browser_window *bw); static void browser_window_stop_throbber(struct browser_window *bw); @@ -67,8 +72,15 @@ static void browser_window_set_status(struct browser_window *bw, 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); +static void browser_window_destroy_children(struct browser_window *bw); +static void browser_window_destroy_internal(struct browser_window *bw); +static struct browser_window *browser_window_find_target(struct browser_window *bw, const char *target); +static void browser_window_find_target_internal(struct browser_window *bw, const char *target, + int depth, struct browser_window *page, int *rdepth, struct browser_window **bw_target); static void browser_window_mouse_action_html(struct browser_window *bw, browser_mouse_state mouse, int x, int y); +static bool browser_window_resize_frames(struct browser_window *bw, browser_mouse_state mouse, + int x, int y, gui_pointer_shape *pointer, const char **status, bool *action); static void browser_window_mouse_action_text(struct browser_window *bw, browser_mouse_state mouse, int x, int y); static void browser_window_mouse_track_html(struct browser_window *bw, @@ -95,7 +107,7 @@ static void browser_window_scroll_box(struct browser_window *bw, /** * Create and open a new browser window with the given page. * - * \param url URL to start fetching in the new window (copied) + * \param url URL to start fetching in the new window (copied) * \param clone The browser window to clone * \param referer The referring uri */ @@ -107,13 +119,12 @@ struct browser_window *browser_window_create(const char *url, struct browser_win assert(clone || history_add); - if ((bw = malloc(sizeof *bw)) == NULL) { + if ((bw = calloc(1, sizeof *bw)) == NULL) { warn_user("NoMemory", 0); return NULL; } - bw->current_content = NULL; - bw->loading_content = NULL; + /* content */ if (!clone) bw->history = history_create(); else @@ -122,32 +133,566 @@ struct browser_window *browser_window_create(const char *url, struct browser_win bw->gesturer = NULL; else bw->gesturer = gesturer_clone(clone->gesturer); + + /* window characteristics */ bw->sel = selection_create(bw); - bw->throbbing = false; - bw->caret_callback = NULL; - bw->paste_callback = NULL; - bw->move_callback = NULL; - bw->frag_id = NULL; + bw->refresh_interval = -1; bw->drag_type = DRAGGING_NONE; - bw->scrolling_box = NULL; - bw->referer = NULL; - bw->download = false; + bw->browser_window_type = BROWSER_WINDOW_NORMAL; + bw->scrolling = SCROLLING_YES; + bw->border = true; + bw->no_resize = true; + + /* gui window */ if ((bw->window = gui_create_browser_window(bw, clone)) == NULL) { - free(bw); + browser_window_destroy(bw); return NULL; } - bw->refresh_interval = -1; if (url) browser_window_go(bw, url, referer, history_add); return bw; } +/** + * Returns the browser window that is responsible for the child. + * + * \param bw The browser window to find the owner of + * \return the browser window's owner + */ + +struct browser_window *browser_window_owner(struct browser_window *bw) { + /* an iframe's parent is just the parent window */ + if (bw->browser_window_type == BROWSER_WINDOW_IFRAME) + return bw->parent; + + /* the parent of a frameset is either a NORMAL window or an IFRAME */ + while (bw->parent) { + 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; + } + } + return bw; +} + + +/** + * Create and open a iframes for a browser window. + * + * \param bw The browser window to create iframes for + * \param iframe The iframes to create + */ + +void browser_window_create_iframes(struct browser_window *bw, + struct content_html_iframe *iframe) { + struct browser_window *window; + struct content_html_iframe *cur; + int iframes = 0; + int index; + + for (cur = iframe; cur; cur = cur->next) + iframes++; + bw->iframes = calloc(iframes, sizeof(*bw)); + if (!bw->iframes) + return; + bw->iframe_count = iframes; + + index = 0; + for (cur = iframe; cur; cur = cur->next) { + window = &(bw->iframes[index++]); + + /* content */ + window->history = history_create(); + window->sel = selection_create(window); + window->refresh_interval = -1; + + /* window characteristics */ + window->drag_type = DRAGGING_NONE; + window->browser_window_type = BROWSER_WINDOW_IFRAME; + window->scrolling = cur->scrolling; + window->border = cur->border; + window->border_colour = cur->border_colour; + window->no_resize = true; + window->margin_width = cur->margin_width; + window->margin_height = cur->margin_height; + if (cur->name) + window->name = strdup(cur->name); + + /* linking */ + window->box = cur->box; + window->parent = bw; + + /* gui window */ + window->window = gui_create_browser_window(window, bw); + } + + /* calculate dimensions */ + gui_window_update_extent(bw->window); + browser_window_recalculate_iframes(bw); + + index = 0; + for (cur = iframe; cur; cur = cur->next) { + window = &(bw->iframes[index++]); + if (iframe->url) + browser_window_go(window, iframe->url, NULL, true); + } +} + + +/** + * Recalculate iframe positions following a resize. + * + * \param bw The browser window to reposition iframes for + */ + +void browser_window_recalculate_iframes(struct browser_window *bw) { + struct browser_window *window; + struct rect rect; + int bw_width, bw_height; + int index; + + assert(bw); + + /* update window dimensions */ + gui_window_get_dimensions(bw->window, &bw_width, &bw_height); + if (!bw->parent) { + bw->x0 = 0; + bw->y0 = 0; + bw->x1 = bw_width; + bw->y1 = bw_height; + } + + for (index = 0; index < bw->iframe_count; index++) { + window = &(bw->iframes[index]); + box_bounds(window->box, &rect); + gui_window_position_frame(window->window, rect.x0, rect.y0, + rect.x1, rect.y1); + } +} + + +/** + * Create and open aframeset for a browser window. + * + * \param bw The browser window to create the frameset for + * \param iframe The frameset to create + */ + +void browser_window_create_frameset(struct browser_window *bw, + struct content_html_frames *frameset) { + int row, col, index; + struct content_html_frames *frame; + struct browser_window *window; + + /* we use a 3 stage approach such that the content is initially formatted to the + * correct frameset dimensions */ + assert(bw && frameset); + + /* create children */ + assert(bw->children == NULL); + assert(frameset->cols + frameset->rows != 0); + + bw->children = calloc((frameset->cols * frameset->rows), sizeof(*bw)); + if (!bw->children) + return; + bw->cols = frameset->cols; + bw->rows = frameset->rows; + for (row = 0; row < bw->rows; row++) { + for (col = 0; col < bw->cols; col++) { + index = (row * bw->cols) + col; + frame = &frameset->children[index]; + window = &bw->children[index]; + + /* content */ + window->history = history_create(); + window->sel = selection_create(window); + window->refresh_interval = -1; + + /* window characteristics */ + window->drag_type = DRAGGING_NONE; + if (frame->children) + window->browser_window_type = BROWSER_WINDOW_FRAMESET; + else + window->browser_window_type = BROWSER_WINDOW_FRAME; + window->scrolling = frame->scrolling; + window->border = frame->border; + window->border_colour = frame->border_colour; + window->no_resize = frame->no_resize; + window->frame_width = frame->width; + window->frame_height = frame->height; + window->margin_width = frame->margin_width; + window->margin_height = frame->margin_height; + if (frame->name) + window->name = strdup(frame->name); + + /* linking */ + window->parent = bw; + + /* gui window */ + window->window = gui_create_browser_window(window, bw); + if (frame->children) + browser_window_create_frameset(window, frame); + } + } + + /* calculate dimensions */ + gui_window_update_extent(bw->window); + browser_window_recalculate_frameset(bw); + + /* launch content */ + for (row = 0; row < bw->rows; row++) { + for (col = 0; col < bw->cols; col++) { + index = (row * bw->cols) + col; + frame = &frameset->children[index]; + window = &bw->children[index]; + + if (frame->url) + browser_window_go(window, frame->url, NULL, true); + } + } +} + + +/** + * Recalculate frameset positions following a resize. + * + * \param bw The browser window to reposition framesets for + */ + +void browser_window_recalculate_frameset(struct browser_window *bw) { + int widths[bw->cols][bw->rows]; + int heights[bw->cols][bw->rows]; + int bw_width, bw_height; + int avail_width, avail_height; + int row, row2, col, index; + struct browser_window *window; + float relative; + int size, extent; + int x, y; + + assert(bw); + + /* window dimensions */ + if (!bw->parent) { + gui_window_get_dimensions(bw->window, &bw_width, &bw_height); + bw->x0 = 0; + bw->y0 = 0; + bw->x1 = bw_width; + bw->y1 = bw_height; + } else { + bw_width = bw->x1 - bw->x0; + bw_height = bw->y1 - bw->y0; + } + bw_width++; + bw_height++; + + /* widths */ + for (row = 0; row < bw->rows; row++) { + avail_width = bw_width; + relative = 0; + for (col = 0; col < bw->cols; col++) { + index = (row * bw->cols) + col; + window = &bw->children[index]; + + switch (window->frame_width.unit) { + case FRAME_DIMENSION_PIXELS: + widths[col][row] = window->frame_width.value; + break; + case FRAME_DIMENSION_PERCENT: + widths[col][row] = bw_width * window->frame_width.value / 100; + break; + case FRAME_DIMENSION_RELATIVE: + widths[col][row] = 0; + relative += window->frame_width.value; + break; + } + avail_width -= widths[col][row]; + } + + /* try to distribute remainder to relative values in preference */ + if ((relative > 0) && (avail_width > 0)) { + for (col = 0; col < bw->cols; col++) { + index = (row * bw->cols) + col; + window = &bw->children[index]; + + if (window->frame_width.unit == FRAME_DIMENSION_RELATIVE) { + size = avail_width * window->frame_width.value / relative; + avail_width -= size; + relative -= window->frame_width.value; + widths[col][row] += size; + } + } + } else { + /* proportionally distribute error */ + extent = bw_width - avail_width; + for (col = 0; col < bw->cols; col++) { + index = (row * bw->cols) + col; + window = &bw->children[index]; + + if (col == bw->cols - 1) { + widths[col][row] = bw_width; + } else { + size = bw_width * widths[col][row] / extent; + bw_width -= size; + extent -= widths[col][row]; + widths[col][row] = size; + } + } + } + } + + /* heights */ + for (col = 0; col < bw->cols; col++) { + avail_height = bw_height; + relative = 0; + for (row = 0; row < bw->rows; row++) { + index = (row * bw->cols) + col; + window = &bw->children[index]; + + switch (window->frame_height.unit) { + case FRAME_DIMENSION_PIXELS: + heights[col][row] = window->frame_height.value; + break; + case FRAME_DIMENSION_PERCENT: + heights[col][row] = bw_height * + window->frame_height.value / 100; + break; + case FRAME_DIMENSION_RELATIVE: + heights[col][row] = 0; + relative += window->frame_height.value; + break; + } + avail_height -= heights[col][row]; + } + + if (avail_height == 0) + continue; + + /* try to distribute remainder to relative values in preference */ + if ((relative > 0) && (avail_height > 0)) { + for (row = 0; row < bw->rows; row++) { + index = (row * bw->cols) + col; + window = &bw->children[index]; + + if (window->frame_height.unit == FRAME_DIMENSION_RELATIVE) { + size = avail_height * window->frame_height.value / relative; + avail_height -= size; + relative -= window->frame_height.value; + heights[col][row] += size; + } + } + } else { + /* proportionally distribute error */ + extent = bw_height - avail_height; + for (row = 0; row < bw->rows; row++) { + index = (row * bw->cols) + col; + window = &bw->children[index]; + + if (row == bw->rows - 1) { + heights[col][row] = bw_height; + } else { + size = bw_height * heights[col][row] / extent; + bw_height -= size; + extent -= heights[col][row]; + heights[col][row] = size; + } + } + } + } + + /* position frames and calculate children */ + for (row = 0; row < bw->rows; row++) { + x = 0; + for (col = 0; col < bw->cols; col++) { + index = (row * bw->cols) + col; + window = &bw->children[index]; + + y = 0; + for (row2 = 0; row2 < row; row2++) + y+= heights[col][row2]; + gui_window_position_frame(window->window, x, y, + x + widths[col][row] - 1, + y + heights[col][row] - 1); + x += widths[col][row]; + if (window->children) + browser_window_recalculate_frameset(window); + } + } +} + + +/** + * Resize a browser window that is a frame. + * + * \param bw The browser window to resize + */ + +void browser_window_resize_frame(struct browser_window *bw, int x, int y) { + struct browser_window *parent; + struct browser_window *sibling; + int col = -1, row = -1, i; + bool change = false; + + parent = bw->parent; + assert(parent); + + /* get frame location */ + for (i = 0; i < (parent->cols * parent->rows); i++) { + if (&parent->children[i] == bw) { + col = i % parent->cols; + row = i / parent->cols; + } + } + assert((col >= 0) && (row >= 0)); + + sibling = NULL; + if (bw->drag_resize_left) + sibling = &parent->children[row * parent->cols + (col - 1)]; + else if (bw->drag_resize_right) + sibling = &parent->children[row * parent->cols + (col + 1)]; + if (sibling) + change |= browser_window_resolve_frame_dimension(bw, sibling, x, y, true, false); + + sibling = NULL; + if (bw->drag_resize_up) + sibling = &parent->children[(row - 1) * parent->cols + col]; + else if (bw->drag_resize_down) + sibling = &parent->children[(row + 1) * parent->cols + col]; + if (sibling) + change |= browser_window_resolve_frame_dimension(bw, sibling, x, y, false, true); + + if (change) + browser_window_recalculate_frameset(parent); +} + + +bool browser_window_resolve_frame_dimension(struct browser_window *bw, struct browser_window *sibling, + int x, int y, bool width, bool height) { + int bw_dimension, sibling_dimension; + int bw_pixels, sibling_pixels; + struct frame_dimension *bw_d, *sibling_d; + float total_new; + int frame_size; + + assert(!(width && height)); + + /* extend/shrink the box to the pointer */ + if (width) { + if (bw->drag_resize_left) + bw_dimension = bw->x1 - x; + else + bw_dimension = x - bw->x0; + bw_pixels = (bw->x1 - bw->x0); + sibling_pixels = (sibling->x1 - sibling->x0); + bw_d = &bw->frame_width; + sibling_d = &sibling->frame_width; + frame_size = bw->parent->x1 - bw->parent->x0; + } else { + if (bw->drag_resize_up) + bw_dimension = bw->y1 - y; + else + bw_dimension = y - bw->y0; + bw_pixels = (bw->y1 - bw->y0); + sibling_pixels = (sibling->y1 - sibling->y0); + bw_d = &bw->frame_height; + sibling_d = &sibling->frame_height; + frame_size = bw->parent->y1 - bw->parent->y0; + } + sibling_dimension = bw_pixels + sibling_pixels - bw_dimension; + + /* check for no change or no frame size*/ + if ((bw_dimension == bw_pixels) || (frame_size == 0)) + return false; + /* check for both being 0 */ + total_new = bw_dimension + sibling_dimension; + if ((bw_dimension + sibling_dimension) == 0) + return false; + + /* our frame dimensions are now known to be: + * + * <-- frame_size --> [VISIBLE PIXELS] + * |<-- bw_pixels -->|<-- sibling_pixels -->| [VISIBLE PIXELS, BEFORE RESIZE] + * |<-- bw_d->value-->|<-- sibling_d->value-->| [SPECIFIED UNITS, BEFORE RESIZE] + * |<--bw_dimension-->|<--sibling_dimension-->| [VISIBLE PIXELS, AFTER RESIZE] + * |<-- total_new -->| [VISIBLE PIXELS, AFTER RESIZE] + * + * when we resize, we must retain the original unit specification such that any + * subsequent resizing of the parent window will recalculate the page as the + * author specified. + * + * if the units of both frames are the same then we can resize the values simply + * by updating the values to be a percentage of the original widths. + */ + if (bw_d->unit == sibling_d->unit) { + float total_specified = bw_d->value + sibling_d->value; + bw_d->value = (total_specified * bw_dimension) / total_new; + sibling_d->value = total_specified - bw_d->value; + return true; + } + + /* if one of the sizes is relative then we don't alter the relative width and + * just let it reflow across. the non-relative (pixel/percentage) value can + * simply be resolved to the specified width that will result in the required + * dimension. + */ + if (bw_d->unit == FRAME_DIMENSION_RELATIVE) { + if ((sibling_pixels == 0) && (bw_dimension == 0)) + return false; + if (sibling_d->value == 0) + bw_d->value = 1; + if (sibling_pixels == 0) + sibling_d->value = (sibling_d->value * bw_pixels) / bw_dimension; + else + sibling_d->value = + (sibling_d->value * sibling_dimension) / sibling_pixels; + + /* todo: the availble resize may have changed, update the drag box */ + return true; + } else if (sibling_d->unit == FRAME_DIMENSION_RELATIVE) { + if ((bw_pixels == 0) && (sibling_dimension == 0)) + return false; + if (bw_d->value == 0) + bw_d->value = 1; + if (bw_pixels == 0) + bw_d->value = (bw_d->value * sibling_pixels) / sibling_dimension; + else + bw_d->value = (bw_d->value * bw_dimension) / bw_pixels; + + /* todo: the availble resize may have changed, update the drag box */ + return true; + } + + /* finally we have a pixel/percentage mix. unlike relative values, percentages + * can easily be backwards-calculated as they can simply be scaled like pixel + * values + */ + if (bw_d->unit == FRAME_DIMENSION_PIXELS) { + float total_specified = bw_d->value + frame_size * sibling_d->value / 100; + bw_d->value = (total_specified * bw_dimension) / total_new; + sibling_d->value = (total_specified - bw_d->value) * 100 / frame_size; + return true; + } else if (sibling_d->unit == FRAME_DIMENSION_PIXELS) { + float total_specified = bw_d->value * frame_size / 100 + sibling_d->value; + sibling_d->value = (total_specified * sibling_dimension) / total_new; + bw_d->value = (total_specified - sibling_d->value) * 100 / frame_size; + return true; + } + assert(!"Invalid frame dimension unit"); + return false; +} + + /** * Start fetching a page in a browser window. * - * \param bw browser window - * \param url URL to start fetching (copied) + * \param bw browser window + * \param url URL to start fetching (copied) * \param referer the referring uri * * Any existing fetches in the window are aborted. @@ -163,13 +708,13 @@ void browser_window_go(struct browser_window *bw, const char *url, /** * Start fetching a page in a browser window, POSTing form data. * - * \param bw browser window - * \param url URL to start fetching (copied) - * \param post_urlenc url encoded post data, or 0 if none + * \param bw browser window + * \param url URL to start fetching (copied) + * \param post_urlenc url encoded post data, or 0 if none * \param post_multipart multipart post data, or 0 if none - * \param history_add add to window history - * \param referer the referring uri - * \param download download, rather than render the uri + * \param history_add add to window history + * \param referer the referring uri + * \param download download, rather than render the uri * * Any existing fetches in the window are aborted. * @@ -238,6 +783,7 @@ void browser_window_go_post(struct browser_window *bw, const char *url, browser_window_stop(bw); browser_window_remove_caret(bw); + browser_window_destroy_children(bw); browser_window_set_status(bw, messages_get("Loading")); bw->history_add = history_add; @@ -263,7 +809,6 @@ void browser_window_go_post(struct browser_window *bw, const char *url, } bw->download = download; - fetchcache_go(c, option_send_referer ? referer : 0, browser_window_callback, (intptr_t) bw, 0, gui_window_get_width(bw->window), @@ -283,6 +828,7 @@ void browser_window_callback(content_msg msg, struct content *c, char status[40]; char url[256]; + switch (msg) { case CONTENT_MSG_LOADING: assert(bw->loading_content == c); @@ -339,7 +885,7 @@ void browser_window_callback(content_msg msg, struct content *c, browser_window_update(bw, true); content_open(c, bw, 0, 0, 0, 0); browser_window_set_status(bw, c->status_message); - if (bw->history_add) { + if ((bw->history_add) && (bw->history)) { history_add(bw->history, c, bw->frag_id); if (urldb_add_url(c->url)) { urldb_set_url_title(c->url, @@ -550,6 +1096,9 @@ void browser_window_convert_to_download(struct browser_window *bw) void browser_window_start_throbber(struct browser_window *bw) { bw->throbbing = true; + + while (bw->parent) + bw = bw->parent; gui_window_start_throbber(bw->window); } @@ -563,14 +1112,42 @@ void browser_window_start_throbber(struct browser_window *bw) void browser_window_stop_throbber(struct browser_window *bw) { bw->throbbing = false; - gui_window_stop_throbber(bw->window); + + while (bw->parent) + bw = bw->parent; + + if (!browser_window_check_throbber(bw)) + gui_window_stop_throbber(bw->window); +} + +bool browser_window_check_throbber(struct browser_window *bw) +{ + int children, index; + + if (bw->throbbing) + return true; + + if (bw->children) { + children = bw->rows * bw->cols; + for (index = 0; index < children; index++) { + if (browser_window_check_throbber(&bw->children[index])) + return true; + } + } + if (bw->iframes) { + for (index = 0; index < bw->iframe_count; index++) { + if (browser_window_check_throbber(&bw->iframes[index])) + return true; + } + } + return false; } /** * Redraw browser window, set extent to content, and update title. * - * \param bw browser_window + * \param bw browser_window * \param scroll_to_top move view to top of page */ @@ -588,8 +1165,7 @@ void browser_window_update(struct browser_window *bw, } else gui_window_set_title(bw->window, bw->current_content->url); - gui_window_set_extent(bw->window, bw->current_content->width, - bw->current_content->height); + gui_window_update_extent(bw->window); if (scroll_to_top) gui_window_set_scroll(bw->window, 0, 0); @@ -614,6 +1190,8 @@ void browser_window_update(struct browser_window *bw, 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); @@ -628,6 +1206,17 @@ void browser_window_stop(struct browser_window *bw) } schedule_remove(browser_window_refresh, bw); + + if (bw->children) { + children = bw->rows * bw->cols; + for (index = 0; index < children; index++) + browser_window_stop(&bw->children[index]); + } + if (bw->iframes) { + children = bw->iframe_count; + for (index = 0; index < children; index++) + browser_window_stop(&bw->iframes[index]); + } browser_window_stop_throbber(bw); } @@ -672,12 +1261,14 @@ void browser_window_reload(struct browser_window *bw, bool all) /** * Change the status bar of a browser window. * - * \param bw browser window + * \param bw browser window * \param text new status text (copied) */ void browser_window_set_status(struct browser_window *bw, const char *text) { + while (bw->parent) + bw = bw->parent; gui_window_set_status(bw->window, text); } @@ -702,12 +1293,61 @@ void browser_window_set_pointer(struct gui_window *g, gui_pointer_shape shape) void browser_window_destroy(struct browser_window *bw) { + + /* can't destoy child windows on their own */ + assert(!bw->parent); + + /* destroy */ + browser_window_destroy_internal(bw); + free(bw); +} + + +/** + * Close and destroy all child browser window. + * + * \param bw browser window + */ + +void browser_window_destroy_children(struct browser_window *bw) +{ + int i; + + if (bw->children) { + for (i = 0; i < (bw->rows * bw->cols); i++) + browser_window_destroy_internal(&bw->children[i]); + free(bw->children); + bw->children = NULL; + bw->rows = 0; + bw->cols = 0; + } + if (bw->iframes) { + for (i = 0; i < bw->iframe_count; i++) + browser_window_destroy_internal(&bw->iframes[i]); + free(bw->iframes); + bw->iframes = NULL; + bw->iframe_count = 0; + } +} + + +/** + * Release all memory associated with a browser window. + * + * \param bw browser window + */ + +void browser_window_destroy_internal(struct browser_window *bw) +{ + assert(bw); + + if ((bw->children) || (bw->iframes)) + 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->current_content) { if (bw->current_content->status == CONTENT_STATUS_READY || bw->current_content->status == @@ -715,6 +1355,7 @@ void browser_window_destroy(struct browser_window *bw) content_close(bw->current_content); content_remove_user(bw->current_content, browser_window_callback, (intptr_t) bw, 0); + bw->current_content = 0; } schedule_remove(browser_window_refresh, bw); @@ -723,8 +1364,99 @@ void browser_window_destroy(struct browser_window *bw) history_destroy(bw->history); gui_window_destroy(bw->window); + free(bw->name); free(bw->frag_id); - free(bw); +} + + +/** + * Locate a browser window in the specified stack according. + * + * \param bw the browser_window to search all relatives of + * \param target the target to locate + */ + +struct browser_window *browser_window_find_target(struct browser_window *bw, const char *target) +{ + struct browser_window *bw_target; + struct browser_window *top; + struct content *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; + + /* allow target="_blank" to be ignored so zamez, tlsa and jmb don't lynch me */ + if (!option_target_blank) { + if ((target == TARGET_BLANK) || (!strcasecmp(target, "_blank"))) + target = TARGET_SELF; + } + + /* todo: correctly follow B.8 + * http://www.w3.org/TR/REC-html40/appendix/notes.html#notes-frames + */ + if ((!target) || (target == TARGET_SELF) || (!strcasecmp(target, "_self"))) + return bw; + else if ((target == TARGET_PARENT) || (!strcasecmp(target, "_parent"))) { + if (bw->parent) + return bw->parent; + return bw; + } else if ((target == TARGET_TOP) || (!strcasecmp(target, "_top"))) { + while (bw->parent) + bw = bw->parent; + return bw; + } else if ((target == TARGET_BLANK) || (!strcasecmp(target, "_blank"))) { + bw_target = browser_window_create(NULL, bw, NULL, false); + if (!bw_target) + return bw; + return bw_target; + } + + /* find frame according to B.8, ie using the following priorities: + * + * 1) current frame + * 2) closest to front + */ + + rdepth = -1; + bw_target = bw; + for (top = bw; top->parent; top = top->parent); + browser_window_find_target_internal(top, target, 0, bw, &rdepth, &bw_target); + return bw_target; +} + +void browser_window_find_target_internal(struct browser_window *bw, const char *target, + int depth, struct browser_window *page, int *rdepth, struct browser_window **bw_target) +{ + int i; + + if ((bw->name) && (!strcasecmp(bw->name, target))) { + if ((bw == page) || (depth > *rdepth)) { + *rdepth = depth; + *bw_target = bw; + } + } + + if ((!bw->children) && (!bw->iframes)) + return; + + depth++; + for (i = 0; i < (bw->cols * bw->rows); i++) { + if ((bw->children[i].name) && (!strcasecmp(bw->children[i].name, target))) { + if ((page == &bw->children[i]) || (depth > *rdepth)) { + *rdepth = depth; + *bw_target = &bw->children[i]; + } + } + if (bw->children[i].children) + browser_window_find_target_internal(&bw->children[i], target, depth, + page, rdepth, bw_target); + } + for (i = 0; i < bw->iframe_count; i++) + browser_window_find_target_internal(&bw->iframes[i], target, depth, page, + rdepth, bw_target); } @@ -770,10 +1502,10 @@ void download_window_callback(fetch_msg msg, void *p, const void *data, /** * Handle mouse clicks in a browser window. * - * \param bw browser window + * \param bw browser window * \param mouse state of mouse buttons and modifier keys - * \param x coordinate of mouse - * \param y coordinate of mouse + * \param x coordinate of mouse + * \param y coordinate of mouse */ void browser_window_mouse_click(struct browser_window *bw, @@ -817,10 +1549,10 @@ void browser_window_mouse_click(struct browser_window *bw, /** * Handle mouse clicks and movements in an HTML content window. * - * \param bw browser window + * \param bw browser window * \param mouse state of mouse buttons and modifier keys - * \param x coordinate of mouse - * \param y coordinate of mouse + * \param x coordinate of mouse + * \param y coordinate of mouse * * This function handles both hovering and clicking. It is important that the * code path is identical (except that hovering doesn't carry out the action), @@ -1089,18 +1821,9 @@ void browser_window_mouse_action_html(struct browser_window *bw, c->url, true); } else if (mouse & BROWSER_MOUSE_CLICK_1) { - struct content *page = 0; - unsigned int i = 0; - - html_find_target(url_content, target, &page, &i); - - if (page) { - if (!html_replace_object(page, i, url, 0, 0)) - warn_user("NoMemory", 0); - } else { - browser_window_go(bw, url, c->url, true); - } - + bw = browser_window_find_target(bw, target); + assert(bw); + browser_window_go(bw, url, c->url, true); } else if (mouse & BROWSER_MOUSE_CLICK_2 && mouse & BROWSER_MOUSE_MOD_1) { free(browser_window_href_content.url); @@ -1116,39 +1839,49 @@ void browser_window_mouse_action_html(struct browser_window *bw, } else { bool done = false; + + /* frame resizing */ + if (bw->parent) { + struct browser_window *parent; + for (parent = bw->parent; parent->parent; parent = parent->parent); + browser_window_resize_frames(parent, mouse, x + bw->x0, y + bw->y0, + &pointer, &status, &done); + } /* if clicking in the main page, remove the selection from any text areas */ - 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); - - if (text_box) { - int pixel_offset; - int idx; - - nsfont_position_in_string(text_box->style, - text_box->text, - text_box->length, - x - text_box_x, - &idx, - &pixel_offset); - - if (selection_click(bw->sel, mouse, text_box->byte_offset + idx)) { - /* key presses must be directed at the main browser - * window, paste text operations ignored */ - browser_window_remove_caret(bw); + if (!done) { + 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); - if (selection_dragging(bw->sel)) { - bw->drag_type = DRAGGING_SELECTION; - status = messages_get("Selecting"); - } else - status = c->status_message; + if (text_box) { + int pixel_offset; + int idx; + + nsfont_position_in_string(text_box->style, + text_box->text, + text_box->length, + x - text_box_x, + &idx, + &pixel_offset); - done = true; + if (selection_click(bw->sel, mouse, text_box->byte_offset + idx)) { + /* key presses must be directed at the main browser + * window, paste text operations ignored */ + browser_window_remove_caret(bw); + + if (selection_dragging(bw->sel)) { + bw->drag_type = DRAGGING_SELECTION; + status = messages_get("Selecting"); + } else + status = c->status_message; + + done = true; + } } } - + if (!done) { if (title) status = title; @@ -1186,14 +1919,115 @@ void browser_window_mouse_action_html(struct browser_window *bw, browser_window_set_pointer(bw->window, pointer); } +bool browser_window_resize_frames(struct browser_window *bw, browser_mouse_state mouse, int x, int y, + gui_pointer_shape *pointer, const char **status, bool *action) { + bool left, right, up, down; + int i, resize_margin; + + if ((x < bw->x0) || (x > bw->x1) || (y < bw->y0) || (y > bw->y1)) + return false; + + if ((!bw->no_resize) && (bw->parent)) { + resize_margin = FRAME_RESIZE; + if (resize_margin * 2 > (bw->x1 - bw->x0)) + resize_margin = (bw->x1 - bw->x0) / 2; + left = (x < bw->x0 + resize_margin); + right = (x > bw->x1 - resize_margin); + resize_margin = FRAME_RESIZE; + if (resize_margin * 2 > (bw->y1 - bw->y0)) + resize_margin = (bw->y1 - bw->y0) / 2; + up = (y < bw->y0 + resize_margin); + down = (y > bw->y1 - resize_margin); + + /* check if the edges can actually be moved */ + if (left || right || up || down) { + int row = -1, col = -1; + switch (bw->browser_window_type) { + case BROWSER_WINDOW_NORMAL: + case BROWSER_WINDOW_IFRAME: + assert(0); + break; + case BROWSER_WINDOW_FRAME: + case BROWSER_WINDOW_FRAMESET: + assert(bw->parent); + break; + } + for (i = 0; i < (bw->parent->cols * bw->parent->rows); i++) { + if (&bw->parent->children[i] == bw) { + col = i % bw->parent->cols; + row = i / bw->parent->cols; + } + } + assert((row >= 0) && (col >= 0)); + + left &= (col > 0); + right &= (col < bw->parent->cols - 1) & (!left); + up &= (row > 0); + down &= (row < bw->parent->rows - 1) & (!up); + } + + if (left || right || up || down) { + if (left) { + if (down) + *pointer = GUI_POINTER_LD; + else if (up) + *pointer = GUI_POINTER_LU; + else + *pointer = GUI_POINTER_LEFT; + } else if (right) { + if (down) + *pointer = GUI_POINTER_RD; + else if (up) + *pointer = GUI_POINTER_RU; + else + *pointer = GUI_POINTER_RIGHT; + } else if (up) { + *pointer = GUI_POINTER_UP; + } else { + *pointer = GUI_POINTER_DOWN; + } + if (mouse & (BROWSER_MOUSE_DRAG_1 | BROWSER_MOUSE_DRAG_2)) { + bw->drag_type = DRAGGING_FRAME; + bw->drag_start_x = x; + bw->drag_start_y = y; + bw->drag_resize_left = left; + bw->drag_resize_right = right; + bw->drag_resize_up = up; + bw->drag_resize_down = down; + gui_window_frame_resize_start(bw->window); + + *status = messages_get("FrameDrag"); + *action = true; + } + return true; + } + } + + if (bw->children) { + for (i = 0; i < (bw->cols * bw->rows); i++) + if (browser_window_resize_frames(&bw->children[i], mouse, x, y, pointer, status, + action)) + return true; + } + if (bw->iframes) { + for (i = 0; i < bw->iframe_count; i++) + if (browser_window_resize_frames(&bw->iframes[i], mouse, x, y, pointer, status, + action)) + return true; + } + return false; +} + + + /** * Handle mouse clicks and movements in a TEXTPLAIN content window. * - * \param bw browser window + * \param bw browser window * \param click type of mouse click - * \param x coordinate of mouse - * \param y coordinate of mouse + * \param x coordinate of mouse + * \param y coordinate of mouse * * This function handles both hovering and clicking. It is important that the * code path is identical (except that hovering doesn't carry out the action), @@ -1247,17 +2081,17 @@ void browser_window_mouse_action_text(struct browser_window *bw, /** * Handle mouse movements in a browser window. * - * \param bw browser window + * \param bw browser window * \param mouse state of mouse buttons and modifier keys - * \param x coordinate of mouse - * \param y coordinate of mouse + * \param x coordinate of mouse + * \param y coordinate of mouse */ void browser_window_mouse_track(struct browser_window *bw, browser_mouse_state mouse, int x, int y) { struct content *c = bw->current_content; - if (!c) + if ((!c) && (bw->drag_type != DRAGGING_FRAME)) return; /* detect end of drag operation in case the platform-specific code @@ -1266,18 +2100,19 @@ void browser_window_mouse_track(struct browser_window *bw, if (bw->drag_type != DRAGGING_NONE && !mouse) { browser_window_mouse_drag_end(bw, mouse, x, y); } - - if (bw->drag_type == DRAGGING_PAGE_SCROLL) { + if (bw->drag_type == DRAGGING_FRAME) { + browser_window_resize_frame(bw, bw->x0 + x, bw->y0 + y); + } else if (bw->drag_type == DRAGGING_PAGE_SCROLL) { /* mouse movement since drag started */ - int scrollx = bw->scrolling_start_x - x; - int scrolly = bw->scrolling_start_y - y; + int scrollx = bw->drag_start_x - x; + int scrolly = bw->drag_start_y - y; /* new scroll offsets */ - scrollx += bw->scrolling_start_scroll_x; - scrolly += bw->scrolling_start_scroll_y; + scrollx += bw->drag_start_scroll_x; + scrolly += bw->drag_start_scroll_y; - bw->scrolling_start_scroll_x = scrollx; - bw->scrolling_start_scroll_y = scrolly; + bw->drag_start_scroll_x = scrollx; + bw->drag_start_scroll_y = scrolly; gui_window_set_scroll(bw->window, scrollx, scrolly); @@ -1299,17 +2134,16 @@ void browser_window_mouse_track(struct browser_window *bw, /** * Handle mouse tracking (including drags) in an HTML content window. * - * \param bw browser window + * \param bw browser window * \param mouse state of mouse buttons and modifier keys - * \param x coordinate of mouse - * \param y coordinate of mouse + * \param x coordinate of mouse + * \param y coordinate of mouse */ void browser_window_mouse_track_html(struct browser_window *bw, browser_mouse_state mouse, int x, int y) { switch (bw->drag_type) { - case DRAGGING_HSCROLL: case DRAGGING_VSCROLL: case DRAGGING_2DSCROLL: { @@ -1322,9 +2156,9 @@ void browser_window_mouse_track_html(struct browser_window *bw, if (bw->drag_type == DRAGGING_HSCROLL) { scroll_y = box->scroll_y; } else { - scroll_y = bw->scrolling_start_scroll_y + - (float) (y - bw->scrolling_start_y) / - (float) bw->scrolling_well_height * + scroll_y = bw->drag_start_scroll_y + + (float) (y - bw->drag_start_y) / + (float) bw->drag_well_height * (float) (box->descendant_y1 - box->descendant_y0); if (scroll_y < box->descendant_y0) @@ -1338,9 +2172,9 @@ void browser_window_mouse_track_html(struct browser_window *bw, if (bw->drag_type == DRAGGING_VSCROLL) { scroll_x = box->scroll_x; } else { - scroll_x = bw->scrolling_start_scroll_x + - (float) (x - bw->scrolling_start_x) / - (float) bw->scrolling_well_width * + scroll_x = bw->drag_start_scroll_x + + (float) (x - bw->drag_start_x) / + (float) bw->drag_well_width * (float) (box->descendant_x1 - box->descendant_x0); if (scroll_x < box->descendant_x0) @@ -1388,10 +2222,10 @@ void browser_window_mouse_track_html(struct browser_window *bw, /** * Handle mouse tracking (including drags) in a TEXTPLAIN content window. * - * \param bw browser window + * \param bw browser window * \param mouse state of mouse buttons and modifier keys - * \param x coordinate of mouse - * \param y coordinate of mouse + * \param x coordinate of mouse + * \param y coordinate of mouse */ void browser_window_mouse_track_text(struct browser_window *bw, @@ -1421,10 +2255,10 @@ void browser_window_mouse_track_text(struct browser_window *bw, /** * Handles the end of a drag operation in a browser window. * - * \param bw browser window + * \param bw browser window * \param mouse state of mouse buttons and modifier keys - * \param x coordinate of mouse - * \param y coordinate of mouse + * \param x coordinate of mouse + * \param y coordinate of mouse */ void browser_window_mouse_drag_end(struct browser_window *bw, @@ -1475,6 +2309,7 @@ void browser_window_mouse_drag_end(struct browser_window *bw, case DRAGGING_2DSCROLL: case DRAGGING_PAGE_SCROLL: + case DRAGGING_FRAME: browser_window_set_pointer(bw->window, GUI_POINTER_DEFAULT); break; @@ -1489,13 +2324,13 @@ void browser_window_mouse_drag_end(struct browser_window *bw, /** * Handle mouse clicks in a box scrollbar. * - * \param bw browser window + * \param bw browser window * \param mouse state of mouse buttons and modifier keys - * \param box scrolling box + * \param box scrolling box * \param box_x position of box in global document coordinates * \param box_y position of box in global document coordinates - * \param x coordinate of click relative to box position - * \param y coordinate of click relative to box position + * \param x coordinate of click relative to box position + * \param y coordinate of click relative to box position * \return status bar message */ @@ -1525,12 +2360,12 @@ const char *browser_window_scrollbar_click(struct browser_window *bw, /* store some data for scroll drags */ bw->scrolling_box = box; - bw->scrolling_start_x = box_x + x; - bw->scrolling_start_y = box_y + y; - bw->scrolling_start_scroll_x = box->scroll_x; - bw->scrolling_start_scroll_y = box->scroll_y; - bw->scrolling_well_width = well_width; - bw->scrolling_well_height = well_height; + bw->drag_start_x = box_x + x; + bw->drag_start_y = box_y + y; + bw->drag_start_scroll_x = box->scroll_x; + bw->drag_start_scroll_y = box->scroll_y; + bw->drag_well_width = well_width; + bw->drag_well_height = well_height; /* determine which scrollbar was clicked */ if (box_vscrollbar_present(box) && @@ -1672,9 +2507,9 @@ void browser_radio_set(struct content *content, /** * Redraw a rectangular region of a browser window * - * \param bw browser window to be redrawn - * \param x x co-ord of top-left - * \param y y co-ord of top-left + * \param bw browser window to be redrawn + * \param x x co-ord of top-left + * \param y y co-ord of top-left * \param width width of rectangle * \param height height of rectangle */ @@ -1708,7 +2543,7 @@ void browser_window_redraw_rect(struct browser_window *bw, int x, int y, /** * Redraw a box. * - * \param c content containing the box, of type CONTENT_HTML + * \param c content containing the box, of type CONTENT_HTML * \param box box to redraw */ @@ -1742,8 +2577,8 @@ void browser_redraw_box(struct content *c, struct box *box) * Update the scroll offsets of a box within a browser window * (In future, copying where possible, rather than redrawing the entire box) * - * \param bw browser window - * \param box box to be updated + * \param bw browser window + * \param box box to be updated * \param scroll_x new horizontal scroll offset * \param scroll_y new vertical scroll offset */ @@ -1762,9 +2597,9 @@ void browser_window_scroll_box(struct browser_window *bw, struct box *box, /** * Process a selection from a form select menu. * - * \param bw browser window with menu + * \param bw browser window with menu * \param control form control with menu - * \param item index of item selected from the menu + * \param item index of item selected from the menu */ void browser_window_form_select(struct browser_window *bw, @@ -1912,7 +2747,7 @@ void browser_form_submit(struct browser_window *bw, struct form *form, if (!target) return; } else { - target = bw; + target = bw; } switch (form->method) { @@ -1926,7 +2761,7 @@ void browser_form_submit(struct browser_window *bw, struct form *form, url = calloc(1, strlen(form->action) + strlen(data) + 2); if (!url) { form_free_successful(success); - free(data); + free(data); warn_user("NoMemory", 0); return; } @@ -1973,8 +2808,8 @@ void browser_form_submit(struct browser_window *bw, struct form *form, * (dir -ve) or below-right (dir +ve) of the point 'x,y' * * \param box parent box - * \param x x ordinate relative to parent box - * \param y y ordinate relative to parent box + * \param x x ordinate relative to parent box + * \param y y ordinate relative to parent box * \param dir direction in which to search (-1 = above-left, +1 = below-right) * \return ptr to the nearest box, or NULL if none found */ @@ -2030,13 +2865,13 @@ struct box *browser_window_nearest_text_box(struct box *box, int x, int y, int d * the mouse pointer, or nearest in the given direction if the pointer is * not over a text box. * - * \param bw browser window + * \param bw browser window * \param mouse state of mouse buttons and modifier keys - * \param x coordinate of mouse - * \param y coordinate of mouse - * \param dx receives x ordinate of mouse relative to innermost containing box - * \param dy receives y ordinate - * \param dir direction to search (-1 = above-left, +1 = below-right) + * \param x coordinate of mouse + * \param y coordinate of mouse + * \param dx receives x ordinate of mouse relative to innermost containing box + * \param dy receives y ordinate + * \param dir direction to search (-1 = above-left, +1 = below-right) */ struct box *browser_window_pick_text_box(struct browser_window *bw, @@ -2111,10 +2946,10 @@ void browser_window_page_drag_start(struct browser_window *bw, int x, int y) { bw->drag_type = DRAGGING_PAGE_SCROLL; - bw->scrolling_start_x = x; - bw->scrolling_start_y = y; + bw->drag_start_x = x; + bw->drag_start_y = y; - gui_window_get_scroll(bw->window, &bw->scrolling_start_scroll_x, &bw->scrolling_start_scroll_y); + gui_window_get_scroll(bw->window, &bw->drag_start_scroll_x, &bw->drag_start_scroll_y); gui_window_scroll_start(bw->window); } diff --git a/desktop/browser.h b/desktop/browser.h index cb7246e91..b9516c78b 100644 --- a/desktop/browser.h +++ b/desktop/browser.h @@ -16,6 +16,7 @@ #include #include #include +#include "netsurf/render/html.h" struct box; struct content; @@ -38,6 +39,8 @@ typedef bool (*browser_paste_callback)(struct browser_window *bw, typedef void (*browser_move_callback)(struct browser_window *bw, void *p); + + /** Browser window data. */ struct browser_window { /** Page currently displayed, or 0. Must have status READY or DONE. */ @@ -84,20 +87,26 @@ struct browser_window { DRAGGING_HSCROLL, DRAGGING_SELECTION, DRAGGING_PAGE_SCROLL, - DRAGGING_2DSCROLL + DRAGGING_2DSCROLL, + DRAGGING_FRAME } drag_type; /** Box currently being scrolled, or 0. */ struct box *scrolling_box; /** Mouse position at start of current scroll drag. */ - int scrolling_start_x; - int scrolling_start_y; + int drag_start_x; + int drag_start_y; /** Scroll offsets at start of current scroll draw. */ - int scrolling_start_scroll_x; - int scrolling_start_scroll_y; + int drag_start_scroll_x; + int drag_start_scroll_y; /** Well dimensions for current scroll drag. */ - int scrolling_well_width; - int scrolling_well_height; + int drag_well_width; + int drag_well_height; + /** Frame resize directions for current frame resize drag. */ + unsigned int drag_resize_left : 1; + unsigned int drag_resize_right : 1; + unsigned int drag_resize_up : 1; + unsigned int drag_resize_down : 1; /** Referer for current fetch, or 0. */ char *referer; @@ -107,6 +116,50 @@ struct browser_window { /** Refresh interval (-1 if undefined) */ int refresh_interval; + + /** Window dimensions */ + int x0; + int y0; + int x1; + int y1; + + /** Window characteristics */ + enum { + BROWSER_WINDOW_NORMAL, + BROWSER_WINDOW_IFRAME, + BROWSER_WINDOW_FRAME, + BROWSER_WINDOW_FRAMESET, + } browser_window_type; + + /** frameset characteristics */ + int rows; + int cols; + + /** frame dimensions */ + struct frame_dimension frame_width; + struct frame_dimension frame_height; + int margin_width; + int margin_height; + + /** frame name for targetting */ + char *name; + + /** frame characteristics */ + bool no_resize; + frame_scrolling scrolling; + bool border; + colour border_colour; + + /** iframe parent box */ + struct box *box; + + /** [cols * rows] children */ + struct browser_window *children; + struct browser_window *parent; + + /** [iframe_count] iframes */ + int iframe_count; + struct browser_window *iframes; }; @@ -129,6 +182,7 @@ extern struct browser_window *current_redraw_browser; struct browser_window * browser_window_create(const char *url, struct browser_window *clone, char *referer, bool history_add); +struct browser_window * browser_window_owner(struct browser_window *bw); void browser_window_go(struct browser_window *bw, const char *url, char *referer, bool history_add); void browser_window_go_post(struct browser_window *bw, const char *url, @@ -139,6 +193,12 @@ void browser_window_stop(struct browser_window *bw); void browser_window_reload(struct browser_window *bw, bool all); void browser_window_destroy(struct browser_window *bw); void browser_window_update(struct browser_window *bw, bool scroll_to_top); +void browser_window_create_iframes(struct browser_window *bw, + struct content_html_iframe *iframe); +void browser_window_recalculate_iframes(struct browser_window *bw); +void browser_window_create_frameset(struct browser_window *bw, + struct content_html_frames *frameset); +void browser_window_recalculate_frameset(struct browser_window *bw); void browser_window_mouse_click(struct browser_window *bw, browser_mouse_state mouse, int x, int y); diff --git a/desktop/gui.h b/desktop/gui.h index 1e27d851f..4d09921a8 100644 --- a/desktop/gui.h +++ b/desktop/gui.h @@ -65,9 +65,12 @@ bool gui_window_get_scroll(struct gui_window *g, int *sx, int *sy); void gui_window_set_scroll(struct gui_window *g, int sx, int sy); void gui_window_scroll_visible(struct gui_window *g, int x0, int y0, int x1, int y1); +void gui_window_position_frame(struct gui_window *g, int x0, int y0, + int x1, int y1); +void gui_window_get_dimensions(struct gui_window *g, int *width, int *height); int gui_window_get_width(struct gui_window *g); int gui_window_get_height(struct gui_window *g); -void gui_window_set_extent(struct gui_window *g, int width, int height); +void gui_window_update_extent(struct gui_window *g); void gui_window_set_status(struct gui_window *g, const char *text); void gui_window_set_pointer(struct gui_window *g, gui_pointer_shape shape); void gui_window_hide_pointer(struct gui_window *g); @@ -81,6 +84,7 @@ 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); void gui_window_save_as_link(struct gui_window *g, struct content *c); +bool gui_window_frame_resize_start(struct gui_window *g); struct gui_download_window *gui_download_window_create(const char *url, const char *mime_type, struct fetch *fetch, diff --git a/desktop/history_core.c b/desktop/history_core.c index b7fd66648..227e811a5 100644 --- a/desktop/history_core.c +++ b/desktop/history_core.c @@ -33,12 +33,15 @@ #define RIGHT_MARGIN 50 #define BOTTOM_MARGIN 30 - -/** A node in the history tree. */ -struct history_entry { +struct history_page { char *url; /**< Page URL. */ char *frag_id; /** Fragment identifier */ char *title; /**< Page title. */ +}; + +/** A node in the history tree. */ +struct history_entry { + struct history_page page; struct history_entry *back; /**< Parent. */ struct history_entry *next; /**< Next sibling. */ struct history_entry *forward; /**< First child. */ @@ -153,16 +156,16 @@ struct history_entry *history_clone_entry(struct history *history, if (!new_entry) return 0; memcpy(new_entry, entry, sizeof *entry); - new_entry->url = strdup(entry->url); - if (entry->frag_id) - new_entry->frag_id = strdup(entry->frag_id); - new_entry->title = strdup(entry->title); - if (((entry->url) && (!new_entry->url)) || - ((entry->title) && (!new_entry->title)) || - ((entry->frag_id) && (!new_entry->frag_id))) { - free(new_entry->url); - free(new_entry->title); - free(new_entry->frag_id); + new_entry->page.url = strdup(entry->page.url); + if (entry->page.frag_id) + new_entry->page.frag_id = strdup(entry->page.frag_id); + new_entry->page.title = strdup(entry->page.title); + if (((entry->page.url) && (!new_entry->page.url)) || + ((entry->page.title) && (!new_entry->page.title)) || + ((entry->page.frag_id) && (!new_entry->page.frag_id))) { + free(new_entry->page.url); + free(new_entry->page.title); + free(new_entry->page.frag_id); free(new_entry); return 0; } @@ -230,9 +233,9 @@ void history_add(struct history *history, struct content *content, return; } - entry->url = url; - entry->frag_id = frag_id ? strdup(frag_id) : 0; - entry->title = title; + entry->page.url = url; + entry->page.frag_id = frag_id ? strdup(frag_id) : 0; + entry->page.title = title; entry->back = history->current; entry->next = 0; entry->forward = entry->forward_pref = entry->forward_last = 0; @@ -282,12 +285,12 @@ void history_update(struct history *history, struct content *content) if (!history || !history->current || !history->current->bitmap) return; - if (history->current->title) - free(history->current->title); + if (history->current->page.title) + free(history->current->page.title); if (content->title) - history->current->title = strdup(content->title); + history->current->page.title = strdup(content->title); else - history->current->title = 0; + history->current->page.title = 0; thumbnail_create(content, history->current->bitmap, 0); } @@ -318,10 +321,10 @@ void history_free_entry(struct history_entry *entry) return; history_free_entry(entry->forward); history_free_entry(entry->next); - free(entry->url); - if (entry->frag_id) - free(entry->frag_id); - free(entry->title); + free(entry->page.url); + if (entry->page.frag_id) + free(entry->page.frag_id); + free(entry->page.title); free(entry); } @@ -397,16 +400,17 @@ void history_go(struct browser_window *bw, struct history *history, char *url; struct history_entry *current; - if (entry->frag_id) { - url = malloc(strlen(entry->url) + strlen(entry->frag_id) + 5); + if (entry->page.frag_id) { + url = malloc(strlen(entry->page.url) + + strlen(entry->page.frag_id) + 5); if (!url) { warn_user("NoMemory", 0); return; } - sprintf(url, "%s#%s", entry->url, entry->frag_id); + sprintf(url, "%s#%s", entry->page.url, entry->page.frag_id); } else - url = entry->url; + url = entry->page.url; if (new_window) { current = history->current; @@ -418,7 +422,7 @@ void history_go(struct browser_window *bw, struct history *history, browser_window_go(bw, url, 0, false); } - if (entry->frag_id) + if (entry->page.frag_id) free(url); } @@ -552,19 +556,19 @@ bool history_redraw_entry(struct history *history, struct history_entry *child; colour c = entry == history->current ? 0x0000ff : 0x333333; int tailsize = 5; - - if (!plot.bitmap(entry->x, entry->y, WIDTH, HEIGHT, entry->bitmap, - 0xffffff)) + + if (!plot.bitmap(entry->x, entry->y, WIDTH, HEIGHT, + entry->bitmap, 0xffffff)) return false; if (!plot.rectangle(entry->x - 1, entry->y - 1, WIDTH + 1, HEIGHT + 1, entry == history->current ? 2 : 1, c, false, false)) return false; - if (!nsfont_position_in_string(&css_base_style, entry->title, - strlen(entry->title), WIDTH, &char_offset, &actual_x)) + if (!nsfont_position_in_string(&css_base_style, entry->page.title, + strlen(entry->page.title), WIDTH, &char_offset, &actual_x)) return false; if (!plot.text(entry->x, entry->y + HEIGHT + 12, &css_base_style, - entry->title, char_offset, 0xffffff, c)) + entry->page.title, char_offset, 0xffffff, c)) return false; for (child = entry->forward; child; child = child->next) { @@ -635,7 +639,7 @@ const char *history_position_url(struct history *history, int x, int y) if (!entry) return 0; - return entry->url; + return entry->page.url; } diff --git a/desktop/netsurf.c b/desktop/netsurf.c index 606774bf3..f0b7e32f6 100644 --- a/desktop/netsurf.c +++ b/desktop/netsurf.c @@ -102,10 +102,15 @@ void netsurf_poll(void) void netsurf_exit(void) { + LOG(("Closing GUI")); gui_quit(); + LOG(("Closing content")); content_quit(); + LOG(("Closing fetches")); fetch_quit(); + LOG(("Closing utf8")); utf8_finalise(); + LOG(("Exited successfully")); } diff --git a/desktop/options.c b/desktop/options.c index 58c9ef24b..9679e82fe 100644 --- a/desktop/options.c +++ b/desktop/options.c @@ -124,7 +124,8 @@ int option_max_fetchers_per_host = 2; * is this plus option_max_fetchers. */ int option_max_cached_fetch_handles = 6; -/** Whether to use knockout rendering */ +/** Whether to allow target="_blank" */ +bool option_target_blank = true; EXTRA_OPTION_DEFINE @@ -174,6 +175,8 @@ struct { OPTION_INTEGER, &option_max_fetchers_per_host }, { "max_cached_fetch_handles", OPTION_INTEGER, &option_max_cached_fetch_handles }, + { "target_blank", + OPTION_BOOL, &option_target_blank }, EXTRA_OPTION_TABLE }; diff --git a/desktop/options.h b/desktop/options.h index 551fef5f8..64944eb47 100644 --- a/desktop/options.h +++ b/desktop/options.h @@ -57,6 +57,7 @@ extern char *option_ca_bundle; extern char *option_cookie_file; extern char *option_cookie_jar; extern char *option_homepage_url; +extern bool option_target_blank; extern bool option_url_suggestion; extern int option_window_x; extern int option_window_y; diff --git a/render/box.h b/render/box.h index ce439381b..90d011486 100644 --- a/render/box.h +++ b/render/box.h @@ -253,6 +253,7 @@ struct object_param { extern const char *TARGET_SELF; extern const char *TARGET_PARENT; extern const char *TARGET_TOP; +extern const char *TARGET_BLANK; #define UNKNOWN_WIDTH INT_MAX diff --git a/render/box_construct.c b/render/box_construct.c index 4dfb54c05..b61cb5d24 100644 --- a/render/box_construct.c +++ b/render/box_construct.c @@ -1,10 +1,11 @@ /* * This file is part of NetSurf, http://netsurf.sourceforge.net/ * Licensed under the GNU General Public License, - * http://www.opensource.org/licenses/gpl-license + * http://www.opensource.org/licenses/gpl-license * Copyright 2005 James Bursa * Copyright 2003 Phil Mellor * Copyright 2005 John M Bell + * Copyright 2006 Richard Wilson */ /** \file @@ -22,6 +23,7 @@ #include "netsurf/utils/config.h" #include "netsurf/content/content.h" #include "netsurf/css/css.h" +#include "netsurf/desktop/browser.h" #include "netsurf/desktop/options.h" #include "netsurf/render/box.h" #include "netsurf/render/form.h" @@ -37,13 +39,6 @@ #include "netsurf/utils/utils.h" -/** MultiLength, as defined by HTML 4.01. */ -struct box_multi_length { - enum { LENGTH_PX, LENGTH_PERCENT, LENGTH_RELATIVE } type; - float value; -}; - - static const content_type image_types[] = { #ifdef WITH_JPEG CONTENT_JPEG, @@ -77,6 +72,7 @@ static const content_type image_types[] = { const char *TARGET_SELF = "_self"; const char *TARGET_PARENT = "_parent"; const char *TARGET_TOP = "_top"; +const char *TARGET_BLANK = "_blank"; static bool convert_xml_to_box(xmlNode *n, struct content *content, @@ -114,6 +110,8 @@ static bool box_input(BOX_SPECIAL_PARAMS); static bool box_input_text(BOX_SPECIAL_PARAMS, bool password); static bool box_button(BOX_SPECIAL_PARAMS); static bool box_frameset(BOX_SPECIAL_PARAMS); +static bool box_create_frameset(struct content_html_frames *f, xmlNode *n, + struct content *content); static bool box_select_add_option(struct form_control *control, xmlNode *n); static bool box_object(BOX_SPECIAL_PARAMS); static bool box_embed(BOX_SPECIAL_PARAMS); @@ -122,13 +120,13 @@ static bool box_pre(BOX_SPECIAL_PARAMS); static bool box_iframe(BOX_SPECIAL_PARAMS); static bool box_get_attribute(xmlNode *n, const char *attribute, void *context, char **value); -static struct box_multi_length *box_parse_multi_lengths(const char *s, - unsigned int *count, void *context); +static struct frame_dimension *box_parse_multi_lengths(const char *s, + unsigned int *count); /* element_table must be sorted by name */ struct element_entry { - char name[10]; /* element type */ + char name[10]; /* element type */ bool (*convert)(BOX_SPECIAL_PARAMS); }; static const struct element_entry element_table[] = { @@ -228,17 +226,17 @@ static const box_type box_map[] = { /** * Recursively construct a box tree from an xml tree and stylesheets. * - * \param n fragment of xml tree - * \param content content of type CONTENT_HTML that is being processed + * \param n fragment of xml tree + * \param content content of type CONTENT_HTML that is being processed * \param parent_style style at this point in xml tree - * \param parent parent in box tree + * \param parent parent in box tree * \param inline_container current inline container box, or 0, updated to - * new current inline container on exit + * new current inline container on exit * \param containing_block current containing block for absolutes, as defined - * by CSS 2.1 10.1 4 - * \param href current link URL, or 0 if not in a link - * \param target current link target, or 0 if none - * \param title current title, or 0 if none + * by CSS 2.1 10.1 4 + * \param href current link URL, or 0 if not in a link + * \param target current link target, or 0 if none + * \param title current title, or 0 if none * \return true on success, false on memory exhaustion */ @@ -266,17 +264,17 @@ bool convert_xml_to_box(xmlNode *n, struct content *content, /** * Construct the box tree for an XML element. * - * \param n XML node of type XML_ELEMENT_NODE - * \param content content of type CONTENT_HTML that is being processed + * \param n XML node of type XML_ELEMENT_NODE + * \param content content of type CONTENT_HTML that is being processed * \param parent_style style at this point in xml tree - * \param parent parent in box tree + * \param parent parent in box tree * \param inline_container current inline container box, or 0, updated to - * new current inline container on exit + * new current inline container on exit * \param containing_block current containing block for absolutes, as defined - * by CSS 2.1 10.1 4 - * \param href current link URL, or 0 if not in a link - * \param target current link target, or 0 if none - * \param title current title, or 0 if none + * by CSS 2.1 10.1 4 + * \param href current link URL, or 0 if not in a link + * \param target current link target, or 0 if none + * \param title current title, or 0 if none * \return true on success, false on memory exhaustion */ @@ -471,8 +469,8 @@ bool box_construct_element(xmlNode *n, struct content *content, xmlFree(s); } if (strcmp((const char *) n->name, "table") == 0) { - border_color = 0x888888; /* default colour */ - if ((s = (char *) xmlGetProp(n, + border_color = 0x888888; /* default colour */ + if ((s = (char *) xmlGetProp(n, (const xmlChar *) "cellpadding"))) { char *endp; long value = strtol(s, &endp, 10); @@ -481,7 +479,7 @@ bool box_construct_element(xmlNode *n, struct content *content, box_set_cellpadding(box, value); xmlFree(s); } - if ((s = (char *) xmlGetProp(n, + if ((s = (char *) xmlGetProp(n, (const xmlChar *) "bordercolor"))) { unsigned int r, g, b; if (s[0] == '#' && sscanf(s + 1, "%2x%2x%2x", &r, &g, &b) == 3) @@ -490,7 +488,7 @@ bool box_construct_element(xmlNode *n, struct content *content, border_color = named_colour(s); xmlFree(s); } - if ((s = (char *) xmlGetProp(n, + if ((s = (char *) xmlGetProp(n, (const xmlChar *) "border"))) { int value = atoi(s); if (!strrchr(s, '%') && 0 < value) /* % not implemented */ @@ -501,7 +499,7 @@ bool box_construct_element(xmlNode *n, struct content *content, /* transfer down to the elements */ if (strcmp((const char *) n->name, "tr") == 0) { - if ((s = (char *) xmlGetProp(n, + if ((s = (char *) xmlGetProp(n, (const xmlChar *) "height"))) { float value = atof(s); if (value < 0 || strlen(s) == 0) { @@ -515,12 +513,12 @@ bool box_construct_element(xmlNode *n, struct content *content, struct box *child; float current; for (child = box->children; child; child = child->next) { - if (child->type == BOX_TABLE_CELL) { - current = css_len2px( - &child->style->height.length, - child->style); - value = (value > current) ? - value : current; + if (child->type == BOX_TABLE_CELL) { + current = css_len2px( + &child->style->height.length, + child->style); + value = (value > current) ? + value : current; child->style->height.height = CSS_HEIGHT_LENGTH; child->style->height.length.unit = @@ -538,7 +536,7 @@ bool box_construct_element(xmlNode *n, struct content *content, if (style->background_image.type == CSS_BACKGROUND_IMAGE_URI) { if (!html_fetch_object(content, style->background_image.uri, box, image_types, content->available_width, - 1000, true, 0)) + 1000, true)) return false; } @@ -549,15 +547,15 @@ bool box_construct_element(xmlNode *n, struct content *content, /** * Construct the box tree for an XML text node. * - * \param n XML node of type XML_TEXT_NODE - * \param content content of type CONTENT_HTML that is being processed + * \param n XML node of type XML_TEXT_NODE + * \param content content of type CONTENT_HTML that is being processed * \param parent_style style at this point in xml tree - * \param parent parent in box tree + * \param parent parent in box tree * \param inline_container current inline container box, or 0, updated to - * new current inline container on exit - * \param href current link URL, or 0 if not in a link - * \param target current link target, or 0 if none - * \param title current title, or 0 if none + * new current inline container on exit + * \param href current link URL, or 0 if not in a link + * \param target current link target, or 0 if none + * \param title current title, or 0 if none * \return true on success, false on memory exhaustion */ @@ -726,9 +724,9 @@ bool box_construct_text(xmlNode *n, struct content *content, /** * Get the style for an element. * - * \param c content of type CONTENT_HTML that is being processed + * \param c content of type CONTENT_HTML that is being processed * \param parent_style style at this point in xml tree - * \param n node in xml tree + * \param n node in xml tree * \return the new style, or 0 on memory exhaustion * * The style is collected from three sources: @@ -985,7 +983,7 @@ struct css_style * box_get_style(struct content *c, * by CSS 2.1 9.7. * * \param style style to update - * \param root this is the root element + * \param root this is the root element */ void box_solve_display(struct css_style *style, bool root) @@ -1016,7 +1014,7 @@ void box_solve_display(struct css_style *style, bool root) /** * Set the cellpadding on a table. * - * \param box box to set cellpadding on + * \param box box to set cellpadding on * \param value padding in pixels * * The descendants of the box are searched for table cells, and the padding is @@ -1046,7 +1044,7 @@ void box_set_cellpadding(struct box *box, int value) break; default: break; - } + } } } @@ -1054,7 +1052,7 @@ void box_set_cellpadding(struct box *box, int value) /** * Set the borders on a table. * - * \param box box to set cellpadding on + * \param box box to set cellpadding on * \param value border in pixels * * The descendants of the box are searched for table cells, and the border is @@ -1067,8 +1065,8 @@ void box_set_table_border(struct box *box, int value, colour color) if (box->type == BOX_TABLE) { for (unsigned int i = 0; i != 4; i++) { - if (box->style->border[i].style == - CSS_BORDER_STYLE_NONE) { + if (box->style->border[i].style == + CSS_BORDER_STYLE_NONE) { box->style->border[i].color = color; box->style->border[i].width.width = CSS_BORDER_WIDTH_LENGTH; @@ -1092,8 +1090,8 @@ void box_set_table_border(struct box *box, int value, colour color) break; case BOX_TABLE_CELL: for (unsigned int i = 0; i != 4; i++) { - if (child->style->border[i].style == - CSS_BORDER_STYLE_NONE) { + if (child->style->border[i].style == + CSS_BORDER_STYLE_NONE) { child->style->border[i].color = color; child->style->border[i].width.width = CSS_BORDER_WIDTH_LENGTH; @@ -1108,7 +1106,7 @@ void box_set_table_border(struct box *box, int value, colour color) break; default: break; - } + } } } @@ -1116,9 +1114,9 @@ void box_set_table_border(struct box *box, int value, colour color) /** * Apply the CSS text-transform property to given text for its ASCII chars. * - * \param s string to transform + * \param s string to transform * \param len length of s - * \param tt transform type + * \param tt transform type */ void box_text_transform(char *s, unsigned int len, @@ -1231,11 +1229,13 @@ bool box_a(BOX_SPECIAL_PARAMS) /* target frame [16.3] */ if ((s = xmlGetProp(n, (const xmlChar *) "target"))) { - if (!strcmp(s, "_blank") || !strcmp(s, "_top")) + if (!strcasecmp(s, "_blank")) + box->target = TARGET_BLANK; + else if (!strcasecmp(s, "_top")) box->target = TARGET_TOP; - else if (!strcmp(s, "_parent")) + else if (!strcasecmp(s, "_parent")) box->target = TARGET_PARENT; - else if (!strcmp(s, "_self")) + else if (!strcasecmp(s, "_self")) /* the default may have been overridden by a * , so this is different to 0 */ box->target = TARGET_SELF; @@ -1246,7 +1246,7 @@ bool box_a(BOX_SPECIAL_PARAMS) xmlFree(s); return false; } - } + } xmlFree(s); } @@ -1294,7 +1294,7 @@ bool box_image(BOX_SPECIAL_PARAMS) /* start fetch */ ok = html_fetch_object(content, url, box, image_types, - content->available_width, 1000, false, 0); + content->available_width, 1000, false); free(url); return ok; } @@ -1406,7 +1406,7 @@ bool box_object(BOX_SPECIAL_PARAMS) /* start fetch (MIME type is ok or not specified) */ if (!html_fetch_object(content, params->data, box, 0, - content->available_width, 1000, false, 0)) + content->available_width, 1000, false)) return false; /* convert children and place into fallback */ @@ -1563,96 +1563,117 @@ no_memory: bool box_frameset(BOX_SPECIAL_PARAMS) { - unsigned int row, col; + bool ok; + + if (content->data.html.frameset) { + LOG(("Error: multiple framesets in document.")); + return false; + } + + content->data.html.frameset = talloc_zero(content, struct content_html_frames); + if (!content->data.html.frameset) + return false; + + ok = box_create_frameset(content->data.html.frameset, n, content); + if (ok) + box->style->display = CSS_DISPLAY_NONE; + + if (convert_children) + *convert_children = false; + return ok; +} + +bool box_create_frameset(struct content_html_frames *f, xmlNode *n, + struct content *content) { + unsigned int row, col, index, i; unsigned int rows = 1, cols = 1; - int object_width, object_height; - char *s, *s1, *url, *name; - struct box *row_box; - struct box *cell_box; - struct box *frameset_box; - struct css_style *style = box->style; - struct css_style *row_style; - struct css_style *cell_style; - struct box_multi_length *row_height = 0, *col_width = 0; + char *s, *url; + struct frame_dimension *row_height = 0, *col_width = 0; xmlNode *c; - url_func_result res; - - box->type = BOX_TABLE; + struct content_html_frames *frame; + bool default_border = true; + colour default_border_colour = 0x000000; /* parse rows and columns */ if ((s = (char *) xmlGetProp(n, (const xmlChar *) "rows"))) { - row_height = box_parse_multi_lengths(s, &rows, content); + row_height = box_parse_multi_lengths(s, &rows); xmlFree(s); if (!row_height) return false; + } else { + row_height = calloc(1, sizeof(struct frame_dimension)); + if (!row_height) + return false; + row_height->value = 100; + row_height->unit = FRAME_DIMENSION_PERCENT; } if ((s = (char *) xmlGetProp(n, (const xmlChar *) "cols"))) { - col_width = box_parse_multi_lengths(s, &cols, content); + col_width = box_parse_multi_lengths(s, &cols); xmlFree(s); if (!col_width) return false; + } else { + col_width = calloc(1, sizeof(struct frame_dimension)); + if (!col_width) + return false; + col_width->value = 100; + col_width->unit = FRAME_DIMENSION_PERCENT; } - LOG(("rows %u, cols %u", rows, cols)); - - box->min_width = 1; - box->max_width = 10000; - box->col = talloc_array(content, struct column, cols); - if (!box->col) - return false; - - if (col_width) { - for (col = 0; col != cols; col++) { - if (col_width[col].type == LENGTH_PX) { - box->col[col].type = COLUMN_WIDTH_FIXED; - box->col[col].width = col_width[col].value; - } else if (col_width[col].type == LENGTH_PERCENT) { - box->col[col].type = COLUMN_WIDTH_PERCENT; - box->col[col].width = col_width[col].value; - } else { - box->col[col].type = COLUMN_WIDTH_RELATIVE; - box->col[col].width = col_width[col].value; - } - box->col[col].min = 1; - box->col[col].max = 10000; + /* common extension: border="0|1" to control all children */ + if ((s = (char *) xmlGetProp(n, (const xmlChar *) "border"))) { + if ((s[0] == '0') && (s[1] == '\0')) + default_border = false; + xmlFree(s); + } + /* common extension: frameborder="yes|no" to control all children */ + if ((s = (char *) xmlGetProp(n, (const xmlChar *) "frameborder"))) { + if (!strcasecmp(s, "no")) + default_border = false; + xmlFree(s); + } + /* common extension: bordercolor="#RRGGBB|" to control all children */ + if ((s = (char *) xmlGetProp(n, (const xmlChar *) "bordercolor"))) { + unsigned int r, g, b; + if (s[0] == '#' && sscanf(s + 1, "%2x%2x%2x", &r, &g, &b) == 3) + default_border_colour = (b << 16) | (g << 8) | r; + else if (s[0] != '#') + default_border_colour = named_colour(s); + xmlFree(s); + } + + /* update frameset and create default children */ + f->cols = cols; + f->rows = rows; + f->scrolling = SCROLLING_NO; + f->children = talloc_array(content, struct content_html_frames, (rows * cols)); + for (row = 0; row < rows; row++) { + for (col = 0; col < cols; col++) { + index = (row * cols) + col; + frame = &f->children[index]; + frame->cols = 0; + frame->rows = 0; + frame->width = col_width[col]; + frame->height = row_height[row]; + frame->margin_width = -1; + frame->margin_height = -1; + frame->name = NULL; + frame->url = NULL; + frame->no_resize = false; + frame->scrolling = SCROLLING_AUTO; + frame->border = default_border; + frame->border_colour = default_border_colour; + frame->children = NULL; } - } else { - box->col[0].type = COLUMN_WIDTH_RELATIVE; - box->col[0].width = 1; - box->col[0].min = 1; - box->col[0].max = 10000; } + free(col_width); + free(row_height); - /* create the frameset table */ + /* create the frameset windows */ c = n->children; - for (row = 0; c && row != rows; row++) { - row_style = talloc_memdup(content, style, sizeof *style); - if (!row_style) - return false; - object_height = 1000; /** \todo get available height */ - /* if (row_height) { - row_style->height.height = CSS_HEIGHT_LENGTH; - row_style->height.length.unit = CSS_UNIT_PX; - if (row_height[row].type == LENGTH_PERCENT) - row_style->height.length.value = 1000 * - row_height[row].value / 100; - else if (row_height[row].type == LENGTH_RELATIVE) - row_style->height.length.value = 100 * - row_height[row].value; - else - row_style->height.length.value = - row_height[row].value; - object_height = row_style->height.length.value; - }*/ - row_box = box_create(row_style, 0, 0, 0, 0, content); - if (!row_box) - return false; - - row_box->type = BOX_TABLE_ROW; - box_add_child(box, row_box); - - for (col = 0; c && col != cols; col++) { + for (row = 0; c && row < rows; row++) { + for (col = 0; c && col < cols; col++) { while (c && !(c->type == XML_ELEMENT_NODE && ( strcmp((const char *) c->name, "frame") == 0 || strcmp((const char *) c->name, "frameset") == 0 @@ -1661,83 +1682,91 @@ bool box_frameset(BOX_SPECIAL_PARAMS) if (!c) break; - /* estimate frame width */ - object_width = content->available_width; - if (col_width && col_width[col].type == LENGTH_PX) - object_width = col_width[col].value; - - cell_style = talloc_memdup(content, style, - sizeof *style); - if (!cell_style) - return false; - cell_style->overflow = CSS_OVERFLOW_AUTO; - - cell_box = box_create(cell_style, 0, 0, 0, 0, content); - if (!cell_box) - return false; - cell_box->type = BOX_TABLE_CELL; - box_add_child(row_box, cell_box); + /* get current frame */ + index = (row * cols) + col; + frame = &f->children[index]; + /* nest framesets */ if (strcmp((const char *) c->name, "frameset") == 0) { - LOG(("frameset")); - frameset_box = box_create(cell_style, 0, 0, 0, - 0, content); - if (!frameset_box) - return false; - if (!box_frameset(c, content, frameset_box, 0)) + frame->border = 0; + if (!box_create_frameset(frame, c, content)) return false; - box_add_child(cell_box, frameset_box); - c = c->next; continue; } + /* get frame URL */ if (!(s = (char *) xmlGetProp(c, (const xmlChar *) "src"))) { c = c->next; continue; } - - s1 = strip(s); - res = url_join(s1, content->data.html.base_url, &url); + if (!box_extract_link(s, content->data.html.base_url, &url)) { + xmlFree(s); + c = c->next; + continue; + } xmlFree(s); - /* if url is equivalent to the parent's url, - * we've got infinite inclusion. stop it here. - * also bail if url_join failed. - */ - if (res != URL_FUNC_OK || strcasecmp(url, - content->data.html.base_url) == 0) { - LOG(("url_join failed")); + if (!url) { c = c->next; continue; } - name = xmlGetProp(c, (const xmlChar *) "name"); + /* don't include ourself */ + if (strcmp(content->data.html.base_url, url) == 0) { + free(url); + c = c->next; + continue; + } - LOG(("frame, url '%s', name '%s'", url, name)); + /* fill in specified values */ + frame->url = talloc_strdup(content, url); + if ((s = (char *) xmlGetProp(c, + (const xmlChar *) "name"))) { + frame->name = talloc_strdup(content, s); + xmlFree(s); + } + frame->no_resize = xmlHasProp(c, + (const xmlChar *) "noresize"); + if ((s = (char *) xmlGetProp(c, + (const xmlChar *) "frameborder"))) { + i = atoi(s); + frame->border = (i != 0); + xmlFree(s); + } + if ((s = (char *) xmlGetProp(c, + (const xmlChar *) "scrolling"))) { + if (!strcasecmp(s, "yes")) + frame->scrolling = SCROLLING_YES; + else if (!strcasecmp(s, "no")) + frame->scrolling = SCROLLING_NO; + xmlFree(s); + } + if ((s = (char *) xmlGetProp(c, + (const xmlChar *) "marginwidth"))) { + frame->margin_width = atoi(s); + xmlFree(s); + } + if ((s = (char *) xmlGetProp(c, + (const xmlChar *) "marginheight"))) { + frame->margin_width = atoi(s); + xmlFree(s); + } + if ((s = (char *) xmlGetProp(c, (const xmlChar *) "bordercolor"))) { + unsigned int r, g, b; + if (s[0] == '#' && sscanf(s + 1, "%2x%2x%2x", &r, &g, &b) == 3) + frame->border_colour = (b << 16) | (g << 8) | r; + else if (s[0] != '#') + frame->border_colour = named_colour(s); + xmlFree(s); + } - if (!html_fetch_object(content, url, - cell_box, 0, - object_width, object_height, false, - name)) - return false; + /* release temporary memory */ free(url); - - if (name) - xmlFree(name); - c = c->next; } } - talloc_free(row_height); - talloc_free(col_width); - - style->width.width = CSS_WIDTH_PERCENT; - style->width.value.percent = 100; - - if (convert_children) - *convert_children = false; return true; } @@ -1748,33 +1777,93 @@ bool box_frameset(BOX_SPECIAL_PARAMS) bool box_iframe(BOX_SPECIAL_PARAMS) { - bool ok; - char *url; - xmlChar *src; + char *url, *s; + struct content_html_iframe *iframe; + int i; /* get frame URL */ - if (!(src = xmlGetProp(n, (const xmlChar *) "src"))) + if (!(s = (char *) xmlGetProp(n, + (const xmlChar *) "src"))) return true; - if (!box_extract_link((char *) src, content->data.html.base_url, &url)) + if (!box_extract_link(s, content->data.html.base_url, &url)) { + xmlFree(s); return false; + } + xmlFree(s); if (!url) return true; - /* Don't include ourself */ + /* don't include ourself */ if (strcmp(content->data.html.base_url, url) == 0) { free(url); return true; } - /* start fetch */ - ok = html_fetch_object(content, url, box, 0, - content->available_width, 0, false, 0); - + /* create a new iframe */ + iframe = talloc(content, struct content_html_iframe); + if (!iframe) { + free(url); + return false; + } + iframe->box = box; + iframe->margin_width = -1; + iframe->margin_height = -1; + iframe->name = NULL; + iframe->url = talloc_strdup(content, url); + iframe->scrolling = SCROLLING_AUTO; + iframe->border = true; + iframe->next = content->data.html.iframe; + content->data.html.iframe = iframe; + + /* fill in specified values */ + if ((s = (char *) xmlGetProp(n, + (const xmlChar *) "name"))) { + iframe->name = talloc_strdup(content, s); + xmlFree(s); + } + if ((s = (char *) xmlGetProp(n, + (const xmlChar *) "frameborder"))) { + i = atoi(s); + iframe->border = (i != 0); + xmlFree(s); + } + if ((s = (char *) xmlGetProp(n, (const xmlChar *) "bordercolor"))) { + unsigned int r, g, b; + if (s[0] == '#' && sscanf(s + 1, "%2x%2x%2x", &r, &g, &b) == 3) + iframe->border_colour = (b << 16) | (g << 8) | r; + else if (s[0] != '#') + iframe->border_colour = named_colour(s); + xmlFree(s); + } + if ((s = (char *) xmlGetProp(n, + (const xmlChar *) "scrolling"))) { + if (!strcasecmp(s, "yes")) + iframe->scrolling = SCROLLING_YES; + else if (!strcasecmp(s, "no")) + iframe->scrolling = SCROLLING_NO; + xmlFree(s); + } + if ((s = (char *) xmlGetProp(n, + (const xmlChar *) "marginwidth"))) { + iframe->margin_width = atoi(s); + xmlFree(s); + } + if ((s = (char *) xmlGetProp(n, + (const xmlChar *) "marginheight"))) { + iframe->margin_width = atoi(s); + xmlFree(s); + } + + /* release temporary memory */ free(url); - *convert_children = false; + /* box */ + box->type = BOX_INLINE_BLOCK; + assert(box->style); - return ok; + if (convert_children) + *convert_children = false; + return true; } @@ -1976,12 +2065,12 @@ bool box_input(BOX_SPECIAL_PARAMS) if (!html_fetch_object(content, url, box, image_types, content->available_width, - 1000, false, 0)) { + 1000, false)) { free(url); goto no_memory; - } - } - free(url); + } + } + free(url); } } else { @@ -2244,7 +2333,7 @@ no_memory: * Add an option to a form select control (helper function for box_select()). * * \param control select containing the option - * \param n xml element node for