diff options
Diffstat (limited to 'desktop')
41 files changed, 4084 insertions, 2716 deletions
diff --git a/desktop/Makefile b/desktop/Makefile index 0d88b2bc9..5e190275d 100644 --- a/desktop/Makefile +++ b/desktop/Makefile @@ -2,8 +2,8 @@ S_DESKTOP := cookie_manager.c knockout.c hotlist.c mouse.c \ plot_style.c print.c search.c searchweb.c scrollbar.c \ - sslcert_viewer.c textarea.c version.c system_colour.c \ - local_history.c global_history.c treeview.c + textarea.c version.c system_colour.c \ + local_history.c global_history.c treeview.c page-info.c S_DESKTOP := $(addprefix desktop/,$(S_DESKTOP)) @@ -12,7 +12,7 @@ desktop/version.c: testament $(OBJROOT)/testament.h # S_BROWSER are sources related to full browsers but are common # between RISC OS, GTK, BeOS and AmigaOS builds -S_BROWSER := browser.c browser_window.c browser_history.c \ +S_BROWSER := bitmap.c browser.c browser_window.c browser_history.c \ download.c frames.c netsurf.c cw_helper.c \ save_complete.c save_text.c selection.c textinput.c gui_factory.c \ save_pdf.c font_haru.c diff --git a/desktop/bitmap.c b/desktop/bitmap.c new file mode 100644 index 000000000..0602773ca --- /dev/null +++ b/desktop/bitmap.c @@ -0,0 +1,338 @@ +/* + * Copyright 2022 Michael Drake <tlsa@netsurf-browser.org> + * + * This file is part of NetSurf, http://www.netsurf-browser.org/ + * + * NetSurf is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * NetSurf is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +/** \file + * Internal core bitmap interface. + */ + +#include <stddef.h> +#include <stdint.h> +#include <stdbool.h> + +#include "utils/log.h" +#include "utils/errors.h" + +#include "desktop/bitmap.h" +#include "desktop/gui_internal.h" + +/** The client bitmap format. */ +bitmap_fmt_t bitmap_fmt; + +/** The client bitmap colour channel layout. */ +struct bitmap_colour_layout bitmap_layout = { + .r = 0, + .g = 1, + .b = 2, + .a = 3, +}; + +/** + * Get the colour layout for the given bitmap format. + * + * \param[in] fmt Pixel format to get channel layout for, + * \return channel layout structure. + */ +static struct bitmap_colour_layout bitmap__get_colour_layout( + const bitmap_fmt_t *fmt) +{ + switch (fmt->layout) { + default: + /* Fall through. */ + case BITMAP_LAYOUT_R8G8B8A8: + return (struct bitmap_colour_layout) { + .r = 0, + .g = 1, + .b = 2, + .a = 3, + }; + + case BITMAP_LAYOUT_B8G8R8A8: + return (struct bitmap_colour_layout) { + .b = 0, + .g = 1, + .r = 2, + .a = 3, + }; + + case BITMAP_LAYOUT_A8R8G8B8: + return (struct bitmap_colour_layout) { + .a = 0, + .r = 1, + .g = 2, + .b = 3, + }; + + case BITMAP_LAYOUT_A8B8G8R8: + return (struct bitmap_colour_layout) { + .a = 0, + .b = 1, + .g = 2, + .r = 3, + }; + } +} + +/** + * Get string for given pixel layout. + * + * \param[in] layout The pixel layout to get string for, + * \return String for given layout. + */ +static const char *bitmap__layout_to_str(enum bitmap_layout layout) +{ + const char *const str[] = { + [BITMAP_LAYOUT_R8G8B8A8] = "Byte-wise RGBA", + [BITMAP_LAYOUT_B8G8R8A8] = "Byte-wise BGRA", + [BITMAP_LAYOUT_A8R8G8B8] = "Byte-wise ARGB", + [BITMAP_LAYOUT_A8B8G8R8] = "Byte-wise ABGR", + [BITMAP_LAYOUT_RGBA8888] = "0xRRGGBBAA (native endian)", + [BITMAP_LAYOUT_BGRA8888] = "0xBBGGRRAA (native endian)", + [BITMAP_LAYOUT_ARGB8888] = "0xAARRGGBB (native endian)", + [BITMAP_LAYOUT_ABGR8888] = "0xAABBGGRR (native endian)", + }; + + if ((size_t)layout >= (sizeof(str)) / sizeof(*str) || + str[layout] == NULL) { + return "Unknown"; + } + + return str[layout]; +} + +/* Exported function, documented in include/netsurf/bitmap.h */ +void bitmap_set_format(const bitmap_fmt_t *bitmap_format) +{ + bitmap_fmt = *bitmap_format; + + NSLOG(netsurf, INFO, "Setting core bitmap format to: %s%s", + bitmap__layout_to_str(bitmap_format->layout), + bitmap_format->pma ? " pre multiplied alpha" : ""); + + bitmap_fmt.layout = bitmap_sanitise_bitmap_layout(bitmap_fmt.layout); + + if (bitmap_format->layout != bitmap_fmt.layout) { + NSLOG(netsurf, INFO, "Sanitised layout to: %s", + bitmap__layout_to_str(bitmap_fmt.layout)); + } + + bitmap_layout = bitmap__get_colour_layout(&bitmap_fmt); +} + +/** + * Swap colour component order. + * + * \param[in] width Bitmap width in pixels. + * \param[in] height Bitmap height in pixels. + * \param[in] buffer Pixel buffer. + * \param[in] rowstride Pixel buffer row stride in bytes. + * \param[in] to Pixel layout to convert to. + * \param[in] from Pixel layout to convert from. + */ +static inline void bitmap__format_convert( + int width, + int height, + uint8_t *buffer, + size_t rowstride, + struct bitmap_colour_layout to, + struct bitmap_colour_layout from) +{ + /* Just swapping the components around */ + for (int y = 0; y < height; y++) { + uint8_t *row = buffer; + + for (int x = 0; x < width; x++) { + const uint32_t px = *((uint32_t *)(void *) row); + + row[to.r] = ((const uint8_t *) &px)[from.r]; + row[to.g] = ((const uint8_t *) &px)[from.g]; + row[to.b] = ((const uint8_t *) &px)[from.b]; + row[to.a] = ((const uint8_t *) &px)[from.a]; + + row += sizeof(uint32_t); + } + + buffer += rowstride; + } +} + +/** + * Convert plain alpha to premultiplied alpha. + * + * \param[in] width Bitmap width in pixels. + * \param[in] height Bitmap height in pixels. + * \param[in] buffer Pixel buffer. + * \param[in] rowstride Pixel buffer row stride in bytes. + * \param[in] to Pixel layout to convert to. + * \param[in] from Pixel layout to convert from. + */ +static inline void bitmap__format_convert_to_pma( + int width, + int height, + uint8_t *buffer, + size_t rowstride, + struct bitmap_colour_layout to, + struct bitmap_colour_layout from) +{ + for (int y = 0; y < height; y++) { + uint8_t *row = buffer; + + for (int x = 0; x < width; x++) { + const uint32_t px = *((uint32_t *)(void *) row); + uint32_t a, r, g, b; + + r = ((const uint8_t *) &px)[from.r]; + g = ((const uint8_t *) &px)[from.g]; + b = ((const uint8_t *) &px)[from.b]; + a = ((const uint8_t *) &px)[from.a]; + + if (a != 0) { + r = ((r * (a + 1)) >> 8) & 0xff; + g = ((g * (a + 1)) >> 8) & 0xff; + b = ((b * (a + 1)) >> 8) & 0xff; + } else { + r = g = b = 0; + } + + row[to.r] = r; + row[to.g] = g; + row[to.b] = b; + row[to.a] = a; + + row += sizeof(uint32_t); + } + + buffer += rowstride; + } +} + +/** + * Convert from premultiplied alpha to plain alpha. + * + * \param[in] width Bitmap width in pixels. + * \param[in] height Bitmap height in pixels. + * \param[in] buffer Pixel buffer. + * \param[in] rowstride Pixel buffer row stride in bytes. + * \param[in] to Pixel layout to convert to. + * \param[in] from Pixel layout to convert from. + */ +static inline void bitmap__format_convert_from_pma( + int width, + int height, + uint8_t *buffer, + size_t rowstride, + struct bitmap_colour_layout to, + struct bitmap_colour_layout from) +{ + for (int y = 0; y < height; y++) { + uint8_t *row = buffer; + + for (int x = 0; x < width; x++) { + const uint32_t px = *((uint32_t *)(void *) row); + uint32_t a, r, g, b; + + r = ((const uint8_t *) &px)[from.r]; + g = ((const uint8_t *) &px)[from.g]; + b = ((const uint8_t *) &px)[from.b]; + a = ((const uint8_t *) &px)[from.a]; + + if (a != 0) { + r = (r << 8) / a; + g = (g << 8) / a; + b = (b << 8) / a; + + r = (r > 255) ? 255 : r; + g = (g > 255) ? 255 : g; + b = (b > 255) ? 255 : b; + } else { + r = g = b = 0; + } + + row[to.r] = r; + row[to.g] = g; + row[to.b] = b; + row[to.a] = a; + + row += sizeof(uint32_t); + } + + buffer += rowstride; + } +} + +/* Exported function, documented in desktop/bitmap.h */ +void bitmap_format_convert(void *bitmap, + const bitmap_fmt_t *fmt_from, + const bitmap_fmt_t *fmt_to) +{ + int width = guit->bitmap->get_width(bitmap); + int height = guit->bitmap->get_height(bitmap); + bool opaque = guit->bitmap->get_opaque(bitmap); + uint8_t *buffer = guit->bitmap->get_buffer(bitmap); + size_t rowstride = guit->bitmap->get_rowstride(bitmap); + struct bitmap_colour_layout to = bitmap__get_colour_layout(fmt_to); + struct bitmap_colour_layout from = bitmap__get_colour_layout(fmt_from); + + NSLOG(netsurf, DEEPDEBUG, "%p: format conversion (%u%s --> %u%s)", + bitmap, + fmt_from->layout, fmt_from->pma ? " pma" : "", + fmt_to->layout, fmt_to->pma ? " pma" : ""); + + if (fmt_from->pma == fmt_to->pma) { + /* Just component order to switch. */ + bitmap__format_convert( + width, height, buffer, + rowstride, to, from); + + } else if (opaque == false) { + /* Need to do conversion to/from premultiplied alpha. */ + if (fmt_to->pma) { + bitmap__format_convert_to_pma( + width, height, buffer, + rowstride, to, from); + } else { + bitmap__format_convert_from_pma( + width, height, buffer, + rowstride, to, from); + } + } +} + +/* Exported function, documented in desktop/bitmap.h */ +bool bitmap_test_opaque(void *bitmap) +{ + int width = guit->bitmap->get_width(bitmap); + int height = guit->bitmap->get_height(bitmap); + size_t rowstride = guit->bitmap->get_rowstride(bitmap); + const uint8_t *buffer = guit->bitmap->get_buffer(bitmap); + + width *= sizeof(uint32_t); + + for (int y = 0; y < height; y++) { + const uint8_t *row = buffer; + + for (int x = bitmap_layout.a; x < width; x += 4) { + if (row[x] != 0xff) { + return false; + } + } + + buffer += rowstride; + } + + return true; +} diff --git a/desktop/bitmap.h b/desktop/bitmap.h new file mode 100644 index 000000000..51ce2c908 --- /dev/null +++ b/desktop/bitmap.h @@ -0,0 +1,147 @@ +/* + * Copyright 2022 Michael Drake <tlsa@nesturf-browser.org> + * + * This file is part of NetSurf, http://www.netsurf-browser.org/ + * + * NetSurf is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * NetSurf is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +/** \file + * Internal core bitmap interface. + */ + +#ifndef _NETSURF_DESKTOP_BITMAP_H_ +#define _NETSURF_DESKTOP_BITMAP_H_ + +#include <nsutils/endian.h> + +#include "netsurf/types.h" +#include "netsurf/bitmap.h" + +/** Pixel format: colour component order. */ +struct bitmap_colour_layout { + uint8_t r; /**< Byte offset within pixel to red component. */ + uint8_t g; /**< Byte offset within pixel to green component. */ + uint8_t b; /**< Byte offset within pixel to blue component. */ + uint8_t a; /**< Byte offset within pixel to alpha component. */ +}; + +/** The client bitmap format. */ +extern bitmap_fmt_t bitmap_fmt; + +/** The client bitmap colour channel layout. */ +extern struct bitmap_colour_layout bitmap_layout; + +/** + * Convert a bitmap pixel to a NetSurf colour (0xAARRGGBB). + * + * The bitmap must be in the client format. + * + * \param[in] Pointer to a pixel in the bitmap's pixel data. + * \return The corresponding NetSurf colour. + */ +static inline colour bitmap_pixel_to_colour(const uint8_t *pixel) +{ + return (pixel[bitmap_layout.r] << 0) | + (pixel[bitmap_layout.g] << 8) | + (pixel[bitmap_layout.b] << 16) | + (pixel[bitmap_layout.a] << 24); +} + +/** + * Sanitise bitmap pixel component layout. + * + * Map endian-dependant layouts to byte-wise layout for the host. + * + * \param[in] layout Layout to convert. + * \return sanitised layout. + */ +static inline enum bitmap_layout bitmap_sanitise_bitmap_layout( + enum bitmap_layout layout) +{ + bool le = endian_host_is_le(); + + switch (layout) { + case BITMAP_LAYOUT_RGBA8888: + layout = (le) ? BITMAP_LAYOUT_A8B8G8R8 + : BITMAP_LAYOUT_R8G8B8A8; + break; + case BITMAP_LAYOUT_BGRA8888: + layout = (le) ? BITMAP_LAYOUT_A8R8G8B8 + : BITMAP_LAYOUT_B8G8R8A8; + break; + case BITMAP_LAYOUT_ARGB8888: + layout = (le) ? BITMAP_LAYOUT_B8G8R8A8 + : BITMAP_LAYOUT_A8R8G8B8; + break; + case BITMAP_LAYOUT_ABGR8888: + layout = (le) ? BITMAP_LAYOUT_R8G8B8A8 + : BITMAP_LAYOUT_A8B8G8R8; + break; + default: + break; + } + + return layout; +} + +/** + * Convert bitmap from one format to another. + * + * Note that both formats should be sanitised. + * + * \param[in] bitmap The bitmap to convert. + * \param[in] from The current bitmap format specifier. + * \param[in] to The bitmap format to convert to. + */ +void bitmap_format_convert(void *bitmap, + const bitmap_fmt_t *from, + const bitmap_fmt_t *to); + +/** + * Convert a bitmap to the client bitmap format. + * + * \param[in] bitmap The bitmap to convert. + * \param[in] current_fmt The current bitmap format specifier. + */ +static inline void bitmap_format_to_client( + void *bitmap, + const bitmap_fmt_t *current_fmt) +{ + bitmap_fmt_t from = *current_fmt; + + from.layout = bitmap_sanitise_bitmap_layout(from.layout); + if (from.layout != bitmap_fmt.layout || from.pma != bitmap_fmt.pma) { + bitmap_format_convert(bitmap, &from, &bitmap_fmt); + } +} + +/** + * Convert a bitmap to the client bitmap format. + * + * \param[in] bitmap The bitmap to convert. + * \param[in] target_fmt The target bitmap format specifier. + */ +static inline void bitmap_format_from_client( + void *bitmap, + const bitmap_fmt_t *target_fmt) +{ + bitmap_fmt_t to = *target_fmt; + + to.layout = bitmap_sanitise_bitmap_layout(to.layout); + if (to.layout != bitmap_fmt.layout || to.pma != bitmap_fmt.pma) { + bitmap_format_convert(bitmap, &bitmap_fmt, &to); + } +} + +#endif diff --git a/desktop/browser.c b/desktop/browser.c index c04488063..6968bf21b 100644 --- a/desktop/browser.c +++ b/desktop/browser.c @@ -23,12 +23,19 @@ */ #include "utils/errors.h" +#include "utils/log.h" +#include "utils/utils.h" #include "netsurf/browser.h" #include "css/utils.h" /* exported interface documented in netsurf/browser.h */ nserror browser_set_dpi(int dpi) { + if (dpi < 72 || dpi > 250) { + int bad = dpi; + dpi = min(max(dpi, 72), 250); + NSLOG(netsurf, INFO, "Clamping invalid DPI %d to %d", bad, dpi); + } nscss_screen_dpi = INTTOFIX(dpi); return NSERROR_OK; diff --git a/desktop/browser_history.c b/desktop/browser_history.c index 5b44670c1..ce9821af8 100644 --- a/desktop/browser_history.c +++ b/desktop/browser_history.c @@ -41,6 +41,7 @@ #include "desktop/gui_internal.h" #include "desktop/browser_private.h" +#include "desktop/local_history_private.h" #include "desktop/browser_history.h" /** @@ -105,7 +106,7 @@ browser_window_history__clone_entry(struct history *history, new_entry->page.bitmap = guit->bitmap->create( LOCAL_HISTORY_WIDTH, LOCAL_HISTORY_HEIGHT, - BITMAP_NEW | BITMAP_OPAQUE); + BITMAP_OPAQUE); if (new_entry->page.bitmap != NULL) { bmsrc_data = guit->bitmap->get_buffer(entry->page.bitmap); @@ -387,7 +388,7 @@ browser_window_history_add(struct browser_window *bw, entry->page.bitmap = guit->bitmap->create( LOCAL_HISTORY_WIDTH, LOCAL_HISTORY_HEIGHT, - BITMAP_NEW | BITMAP_CLEAR_MEMORY | BITMAP_OPAQUE); + BITMAP_CLEAR | BITMAP_OPAQUE); if (entry->page.bitmap != NULL) { ret = guit->bitmap->render(entry->page.bitmap, content); if (ret != NSERROR_OK) { @@ -434,9 +435,7 @@ nserror browser_window_history_update(struct browser_window *bw, history = bw->history; - if (!history || - !history->current || - !history->current->page.bitmap) { + if ((history == NULL) || (history->current == NULL)) { return NSERROR_INVALID; } @@ -455,7 +454,7 @@ nserror browser_window_history_update(struct browser_window *bw, guit->bitmap->render(history->current->page.bitmap, content); } - if (bw->window != NULL && + if ((bw->window != NULL) && guit->window->get_scroll(bw->window, &sx, &sy)) { int content_height = content_get_height(content); int content_width = content_get_width(content); @@ -489,9 +488,7 @@ browser_window_history_get_scroll(struct browser_window *bw, history = bw->history; - if (!history || - !history->current || - !history->current->page.bitmap) { + if ((history== NULL) || (history->current == NULL)) { return NSERROR_INVALID; } diff --git a/desktop/browser_history.h b/desktop/browser_history.h index 06041ebf4..9b6f1fd42 100644 --- a/desktop/browser_history.h +++ b/desktop/browser_history.h @@ -35,17 +35,6 @@ #include "utils/errors.h" -#include "content/handlers/css/utils.h" - -#define LOCAL_HISTORY_WIDTH \ - (FIXTOINT(nscss_pixels_css_to_physical(INTTOFIX(116)))) -#define LOCAL_HISTORY_HEIGHT \ - (FIXTOINT(nscss_pixels_css_to_physical(INTTOFIX(100)))) -#define LOCAL_HISTORY_RIGHT_MARGIN \ - (FIXTOINT(nscss_pixels_css_to_physical(INTTOFIX(50)))) -#define LOCAL_HISTORY_BOTTOM_MARGIN \ - (FIXTOINT(nscss_pixels_css_to_physical(INTTOFIX(30)))) - struct browser_window; struct history_entry; struct bitmap; diff --git a/desktop/browser_private.h b/desktop/browser_private.h index 6e45052d7..40c3b43ce 100644 --- a/desktop/browser_private.h +++ b/desktop/browser_private.h @@ -89,13 +89,6 @@ struct browser_fetch_parameters { bool parent_quirks; /**< Optional parent quirks */ }; -/** - * The SSL context for a fetch, as provided by the fetchers - */ -struct browser_ssl_info { - struct ssl_cert_info certs[MAX_SSL_CERTS]; /**< The certificate chain */ - size_t num; /**< The number of certificates in the chain */ -}; /** * Browser window data. @@ -113,9 +106,9 @@ struct browser_window { struct browser_fetch_parameters current_parameters; /** - * The SSL information for the current content + * The certificate chain for the current content */ - struct browser_ssl_info current_ssl_info; + struct cert_chain *current_cert_chain; /** * Content handle of page in process of being loaded or NULL @@ -129,9 +122,9 @@ struct browser_window { struct browser_fetch_parameters loading_parameters; /** - * The SSL information for the loading content + * The certificate chain for the loading content */ - struct browser_ssl_info loading_ssl_info; + struct cert_chain *loading_cert_chain; /** * Favicon @@ -270,7 +263,7 @@ struct browser_window { bool can_edit; /** current javascript context */ - struct jscontext *jsctx; + struct jsheap *jsheap; /** cache of the currently displayed status text. */ struct { @@ -290,8 +283,16 @@ struct browser_window { * \param existing The existing window if cloning, else NULL */ nserror browser_window_initialise_common(enum browser_window_create_flags flags, - struct browser_window *bw, struct browser_window *existing); + struct browser_window *bw, + const struct browser_window *existing); + +/** + * Release all memory associated with a browser window. + * + * \param bw browser window + */ +nserror browser_window_destroy_internal(struct browser_window *bw); /** * Get the dimensions of the area a browser window occupies @@ -315,12 +316,12 @@ void browser_window_update_extent(struct browser_window *bw); /** - * update an area of a browser window. + * Cause an area of a browser window to be marked invalid and hence redrawn. * * \param bw The browser window to update. * \param rect The area to redraw */ -void browser_window_update_box(struct browser_window *bw, struct rect *rect); +nserror browser_window_invalidate_rect(struct browser_window *bw, struct rect *rect); /** @@ -338,7 +339,8 @@ void browser_window_set_status(struct browser_window *bw, const char *text); * \param bw browser window to set the type of the current drag for * \return root browser window */ -struct browser_window * browser_window_get_root(struct browser_window *bw); +struct browser_window * browser_window_get_root( + struct browser_window *bw); /** diff --git a/desktop/browser_window.c b/desktop/browser_window.c index 6bcaabbb6..c70db7cf1 100644 --- a/desktop/browser_window.c +++ b/desktop/browser_window.c @@ -1,11 +1,7 @@ /* - * Copyright 2003 Phil Mellor <monkeyson@users.sourceforge.net> - * Copyright 2006 James Bursa <bursa@users.sourceforge.net> - * Copyright 2004 Andrew Timmins <atimmins@blueyonder.co.uk> - * Copyright 2004 John Tytgat <joty@netsurf-browser.org> - * Copyright 2006 Richard Wilson <info@tinct.net> * Copyright 2008 Michael Drake <tlsa@netsurf-browser.org> - * Copyright 2009 Paul Blokus <paul_pl@users.sourceforge.net> + * Copyright 2010 Daniel Silverstone <dsilvers@digital-scurf.org> + * Copyright 2010-2020 Vincent Sanders <vince@netsurf-browser.org> * * This file is part of NetSurf, http://www.netsurf-browser.org/ * @@ -28,63 +24,64 @@ * Browser window creation and manipulation implementation. */ -/** smallest scale that can be applied to a browser window*/ -#define SCALE_MINIMUM 0.2 -/** largests scale that can be applied to a browser window*/ -#define SCALE_MAXIMUM 10.0 - #include "utils/config.h" -#include <assert.h> -#include <limits.h> -#include <stdbool.h> -#include <stdint.h> #include <stdlib.h> #include <string.h> -#include <strings.h> #include <math.h> #include <nsutils/time.h> -#include "utils/corestrings.h" +#include "utils/errors.h" #include "utils/log.h" +#include "utils/corestrings.h" #include "utils/messages.h" -#include "utils/nsurl.h" -#include "utils/utils.h" -#include "utils/utf8.h" #include "utils/nsoption.h" -#include "netsurf/misc.h" +#include "netsurf/types.h" +#include "netsurf/browser_window.h" #include "netsurf/window.h" +#include "netsurf/misc.h" #include "netsurf/content.h" +#include "netsurf/search.h" #include "netsurf/plotters.h" -#include "content/content_debug.h" -#include "content/fetch.h" +#include "content/content.h" #include "content/hlcache.h" #include "content/urldb.h" -#include "css/utils.h" -#include "html/form_internal.h" +#include "content/content_debug.h" + #include "html/html.h" -#include "html/box.h" +#include "html/form_internal.h" #include "javascript/js.h" -#include "desktop/browser_history.h" #include "desktop/browser_private.h" +#include "desktop/scrollbar.h" +#include "desktop/gui_internal.h" #include "desktop/download.h" #include "desktop/frames.h" #include "desktop/global_history.h" +#include "desktop/textinput.h" #include "desktop/hotlist.h" #include "desktop/knockout.h" -#include "desktop/scrollbar.h" -#include "desktop/selection.h" +#include "desktop/browser_history.h" #include "desktop/theme.h" -#include "desktop/gui_internal.h" -#include "desktop/textinput.h" +#ifdef WITH_THEME_INSTALL +#include "desktop/theme.h" +#endif -/** maximum frame depth */ -#define FRAME_DEPTH 8 +/** + * smallest scale that can be applied to a browser window + */ +#define SCALE_MINIMUM 0.2 + +/** + * largests scale that can be applied to a browser window + */ +#define SCALE_MAXIMUM 10.0 -/* Have to forward declare browser_window_destroy_internal */ -static void browser_window_destroy_internal(struct browser_window *bw); +/** + * maximum frame depth + */ +#define FRAME_DEPTH 8 /* Forward declare internal navigation function */ static nserror browser_window__navigate_internal( @@ -94,27 +91,21 @@ static nserror browser_window__navigate_internal( /** * Close and destroy all child browser window. * - * \param bw browser window + * \param bw browser window */ static void browser_window_destroy_children(struct browser_window *bw) { int i; if (bw->children) { - for (i = 0; i < (bw->rows * bw->cols); i++) + 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; - } } @@ -124,7 +115,8 @@ static void browser_window_destroy_children(struct browser_window *bw) * \param bw The browser window */ static void -browser_window__free_fetch_parameters(struct browser_fetch_parameters *params) { +browser_window__free_fetch_parameters(struct browser_fetch_parameters *params) +{ if (params->url != NULL) { nsurl_unref(params->url); params->url = NULL; @@ -151,10 +143,10 @@ browser_window__free_fetch_parameters(struct browser_fetch_parameters *params) { /** * Get position of scrollbar widget within browser window. * - * \param bw The browser window - * \param horizontal Whether to get position of horizontal scrollbar - * \param x Updated to x-coord of top left of scrollbar widget - * \param y Updated to y-coord of top left of scrollbar widget + * \param bw The browser window + * \param horizontal Whether to get position of horizontal scrollbar + * \param x Updated to x-coord of top left of scrollbar widget + * \param y Updated to y-coord of top left of scrollbar widget */ static inline void browser_window_get_scrollbar_pos(struct browser_window *bw, @@ -201,9 +193,9 @@ static inline int get_vert_scrollbar_len(struct browser_window *bw) /** * Set or remove a selection. * - * \param bw browser window with selection - * \param selection true if bw has a selection, false if removing selection - * \param read_only true iff selection is read only (e.g. can't cut it) + * \param bw browser window with selection + * \param selection true if bw has a selection, false if removing selection + * \param read_only true iff selection is read only (e.g. can't cut it) */ static void browser_window_set_selection(struct browser_window *bw, @@ -218,7 +210,8 @@ browser_window_set_selection(struct browser_window *bw, assert(top != NULL); - if (bw != top->selection.bw && top->selection.bw != NULL && + if (bw != top->selection.bw && + top->selection.bw != NULL && top->selection.bw->current_content != NULL) { /* clear old selection */ content_clear_selection(top->selection.bw->current_content); @@ -305,7 +298,7 @@ browser_window__get_contextual_content(struct browser_window *bw, /* Pass request into this child */ return browser_window__get_contextual_content(bwc, - (x - bwc->x), (y - bwc->y), data); + (x - bwc->x), (y - bwc->y), data); } /* Coordinate not contained by any frame */ @@ -322,22 +315,6 @@ browser_window__get_contextual_content(struct browser_window *bw, /** - * slow script handler - */ -static bool slow_script(void *ctx) -{ - static int count = 0; - NSLOG(netsurf, INFO, "Continuing execution %d", count); - count++; - if (count > 1) { - count = 0; - return false; - } - return true; -} - - -/** * implements the download operation of a window navigate */ static nserror @@ -380,6 +357,11 @@ browser_window_download(struct browser_window *bw, } +/** + * recursively check browser windows for activity + * + * \param bw browser window to start checking from. + */ static bool browser_window_check_throbber(struct browser_window *bw) { int children, index; @@ -401,6 +383,7 @@ static bool browser_window_check_throbber(struct browser_window *bw) return true; } } + return false; } @@ -432,8 +415,9 @@ static nserror browser_window_stop_throbber(struct browser_window *bw) bw->throbbing = false; - while (bw->parent) + while (bw->parent) { bw = bw->parent; + } if (!browser_window_check_throbber(bw)) { res = guit->window->event(bw->window, GW_EVENT_STOP_THROBBER); @@ -511,6 +495,7 @@ browser_window_favicon_callback(hlcache_handle *c, default: break; + } return NSERROR_OK; } @@ -523,25 +508,27 @@ browser_window_favicon_callback(hlcache_handle *c, * \param bw A top level browser window. * \param link A link context or NULL to attempt fallback scanning. */ -static void +static nserror browser_window_update_favicon(hlcache_handle *c, struct browser_window *bw, struct content_rfc5988_link *link) { nsurl *nsref = NULL; nsurl *nsurl; - nserror error; + nserror res; assert(c != NULL); assert(bw !=NULL); - if (bw->window == NULL) + if (bw->window == NULL) { /* Not top-level browser window; not interested */ - return; + return NSERROR_OK; + } /* already fetching the favicon - use that */ - if (bw->favicon.loading != NULL) - return; + if (bw->favicon.loading != NULL) { + return NSERROR_OK; + } bw->favicon.failed = false; @@ -568,10 +555,12 @@ browser_window_update_favicon(hlcache_handle *c, * that there's a favicon living at /favicon.ico */ if ((lwc_string_caseless_isequal(scheme, corestring_lwc_http, - &match) == lwc_error_ok && match) || + &match) == lwc_error_ok && + match) || (lwc_string_caseless_isequal(scheme, corestring_lwc_https, - &match) == lwc_error_ok && match)) { + &match) == lwc_error_ok && + match)) { speculative_default = true; } @@ -579,33 +568,44 @@ browser_window_update_favicon(hlcache_handle *c, if (speculative_default) { /* no favicon via link, try for the default location */ - error = nsurl_join(nsurl, "/favicon.ico", &nsurl); + res = nsurl_join(nsurl, "/favicon.ico", &nsurl); } else { bw->favicon.failed = true; - error = nsurl_create("resource:favicon.ico", &nsurl); + res = nsurl_create("resource:favicon.ico", &nsurl); } - if (error != NSERROR_OK) { + if (res != NSERROR_OK) { NSLOG(netsurf, INFO, "Unable to create default location url"); - return; + return res; } } else { nsurl = nsurl_ref(link->href); } if (link == NULL) { - NSLOG(netsurf, INFO, "fetching general favicon from '%s'", + NSLOG(netsurf, INFO, + "fetching general favicon from '%s'", nsurl_access(nsurl)); } else { - NSLOG(netsurf, INFO, "fetching favicon rel:%s '%s'", - lwc_string_data(link->rel), nsurl_access(nsurl)); + NSLOG(netsurf, INFO, + "fetching favicon rel:%s '%s'", + lwc_string_data(link->rel), + nsurl_access(nsurl)); } - hlcache_handle_retrieve(nsurl, HLCACHE_RETRIEVE_SNIFF_TYPE, - nsref, NULL, browser_window_favicon_callback, - bw, NULL, CONTENT_IMAGE, &bw->favicon.loading); + res = hlcache_handle_retrieve(nsurl, + HLCACHE_RETRIEVE_SNIFF_TYPE, + nsref, + NULL, + browser_window_favicon_callback, + bw, + NULL, + CONTENT_IMAGE, + &bw->favicon.loading); nsurl_unref(nsurl); + + return res; } @@ -693,6 +693,130 @@ browser_window_convert_to_download(struct browser_window *bw, /** + * scroll to a fragment if present + * + * \param bw browser window + * \return true if the scroll was sucessful + */ +static bool frag_scroll(struct browser_window *bw) +{ + struct rect rect; + + if (bw->frag_id == NULL) { + return false; + } + + if (!html_get_id_offset(bw->current_content, + bw->frag_id, + &rect.x0, + &rect.y0)) { + return false; + } + + rect.x1 = rect.x0; + rect.y1 = rect.y0; + if (browser_window_set_scroll(bw, &rect) == NSERROR_OK) { + if (bw->current_content != NULL && + bw->history != NULL && + bw->history->current != NULL) { + browser_window_history_update(bw, bw->current_content); + } + return true; + } + return false; +} + + +/** + * Redraw browser window, set extent to content, and update title. + * + * \param bw browser_window + * \param scroll_to_top move view to top of page + */ +static void browser_window_update(struct browser_window *bw, bool scroll_to_top) +{ + static const struct rect zrect = { + .x0 = 0, + .y0 = 0, + .x1 = 0, + .y1 = 0 + }; + + if (bw->current_content == NULL) { + return; + } + + switch (bw->browser_window_type) { + + case BROWSER_WINDOW_NORMAL: + /* Root browser window, constituting a front end window/tab */ + guit->window->set_title(bw->window, + content_get_title(bw->current_content)); + + browser_window_update_extent(bw); + + /* if frag_id exists, then try to scroll to it */ + /** @todo don't do this if the user has scrolled */ + if (!frag_scroll(bw)) { + if (scroll_to_top) { + browser_window_set_scroll(bw, &zrect); + } + } + + guit->window->invalidate(bw->window, NULL); + + break; + + case BROWSER_WINDOW_IFRAME: + /* Internal iframe browser window */ + assert(bw->parent != NULL); + assert(bw->parent->current_content != NULL); + + browser_window_update_extent(bw); + + if (scroll_to_top) { + browser_window_set_scroll(bw, &zrect); + } + + /* if frag_id exists, then try to scroll to it */ + /** @todo don't do this if the user has scrolled */ + frag_scroll(bw); + + browser_window_invalidate_iframe(bw); + + break; + + case BROWSER_WINDOW_FRAME: + { + struct rect rect; + browser_window_update_extent(bw); + + if (scroll_to_top) { + browser_window_set_scroll(bw, &zrect); + } + + /* if frag_id exists, then try to scroll to it */ + /** @todo don't do this if the user has scrolled */ + frag_scroll(bw); + + rect.x0 = scrollbar_get_offset(bw->scroll_x); + rect.y0 = scrollbar_get_offset(bw->scroll_y); + rect.x1 = rect.x0 + bw->width; + rect.y1 = rect.y0 + bw->height; + + browser_window_invalidate_rect(bw, &rect); + } + break; + + default: + case BROWSER_WINDOW_FRAMESET: + /* Nothing to do */ + break; + } +} + + +/** * handle message for content ready on browser window */ static nserror browser_window_content_ready(struct browser_window *bw) @@ -714,9 +838,10 @@ static nserror browser_window_content_ready(struct browser_window *bw) browser_window__free_fetch_parameters(&bw->current_parameters); bw->current_parameters = bw->loading_parameters; memset(&bw->loading_parameters, 0, sizeof(bw->loading_parameters)); - /* Transfer the SSL info */ - bw->current_ssl_info = bw->loading_ssl_info; - bw->loading_ssl_info.num = 0; + /* Transfer the certificate chain */ + cert_chain_free(bw->current_cert_chain); + bw->current_cert_chain = bw->loading_cert_chain; + bw->loading_cert_chain = NULL; } /* Format the new content to the correct dimensions */ @@ -779,14 +904,15 @@ static nserror browser_window_content_ready(struct browser_window *bw) browser_window_set_status(bw, content_get_status_message(bw->current_content)); /* frames */ - if ((content_get_type(bw->current_content) == CONTENT_HTML) && - (html_get_frameset(bw->current_content) != NULL)) { - res = browser_window_create_frameset(bw, html_get_frameset(bw->current_content)); - } + res = browser_window_create_frameset(bw); + + /* iframes */ + res = browser_window_create_iframes(bw); - if (content_get_type(bw->current_content) == CONTENT_HTML && - html_get_iframe(bw->current_content) != NULL) { - browser_window_create_iframes(bw, html_get_iframe(bw->current_content)); + /* Indicate page status may have changed */ + if (res == NSERROR_OK) { + struct browser_window *root = browser_window_get_root(bw); + res = guit->window->event(root->window, GW_EVENT_PAGE_INFO_CHANGE); } return res; @@ -843,6 +969,7 @@ browser_window_content_done(struct browser_window *bw) return NSERROR_OK; } + /** * Handle query responses from SSL requests */ @@ -857,6 +984,7 @@ browser_window__handle_ssl_query_response(bool proceed, void *pw) browser_window_stop(bw); browser_window_remove_caret(bw, false); browser_window_destroy_children(bw); + browser_window_destroy_iframes(bw); } if (!proceed) { @@ -873,9 +1001,11 @@ browser_window__handle_ssl_query_response(bool proceed, void *pw) /* And then we navigate to the original loading parameters */ bw->internal_nav = false; + return browser_window__navigate_internal(bw, &bw->loading_parameters); } + /** * Unpack a "username:password" to components. * @@ -935,6 +1065,7 @@ browser_window__unpack_userpass(const char *userpass, return NSERROR_OK; } + /** * Build a "username:password" from components. * @@ -965,6 +1096,7 @@ browser_window__build_userpass(const char *username, return NSERROR_OK; } + /** * Handle a response from the UI when prompted for credentials */ @@ -998,11 +1130,13 @@ browser_window__handle_userpass_response(nsurl *url, browser_window_stop(bw); browser_window_remove_caret(bw, false); browser_window_destroy_children(bw); + browser_window_destroy_iframes(bw); } bw->internal_nav = false; return browser_window__navigate_internal(bw, &bw->loading_parameters); } + /** * Handle login request (BAD_AUTH) during fetch * @@ -1019,8 +1153,8 @@ browser_window__handle_login(struct browser_window *bw, /* Step one, retrieve what we have */ err = browser_window__unpack_userpass( - urldb_get_auth_details(url, realm), - &username, &password); + urldb_get_auth_details(url, realm), + &username, &password); if (err != NSERROR_OK) { goto out; } @@ -1072,7 +1206,7 @@ browser_window__handle_login(struct browser_window *bw, if (err == NSERROR_NOT_IMPLEMENTED) { err = NSERROR_OK; } -out: + out: if (username != NULL) { free(username); } @@ -1083,6 +1217,7 @@ out: return err; } + /** * Handle a certificate verification request (BAD_CERTS) during a fetch */ @@ -1094,11 +1229,14 @@ browser_window__handle_bad_certs(struct browser_window *bw, nserror err; /* Initially we don't know WHY the SSL cert was bad */ const char *reason = messages_get_sslcode(SSL_CERT_ERR_UNKNOWN); - size_t n; + size_t depth; + nsurl *chainurl = NULL; memset(¶ms, 0, sizeof(params)); params.url = nsurl_ref(corestring_nsurl_about_query_ssl); + params.referrer = nsurl_ref(url); + params.flags = BW_NAVIGATE_HISTORY | BW_NAVIGATE_NO_TERMINAL_HISTORY_UPDATE | BW_NAVIGATE_INTERNAL; err = fetch_multipart_data_new_kv(¶ms.post_multipart, "siteurl", @@ -1107,12 +1245,26 @@ browser_window__handle_bad_certs(struct browser_window *bw, goto out; } - for (n = 0; n < bw->loading_ssl_info.num; ++n) { - size_t idx = bw->loading_ssl_info.num - (n + 1); - ssl_cert_err err = bw->loading_ssl_info.certs[idx].err; - if (err != SSL_CERT_ERR_OK) { - reason = messages_get_sslcode(err); - break; + if (bw->loading_cert_chain != NULL) { + for (depth = 0; depth < bw->loading_cert_chain->depth; ++depth) { + size_t idx = bw->loading_cert_chain->depth - (depth + 1); + ssl_cert_err err = bw->loading_cert_chain->certs[idx].err; + if (err != SSL_CERT_ERR_OK) { + reason = messages_get_sslcode(err); + break; + } + } + + err = cert_chain_to_query(bw->loading_cert_chain, &chainurl); + if (err != NSERROR_OK) { + goto out; + } + + err = fetch_multipart_data_new_kv(¶ms.post_multipart, + "chainurl", + nsurl_access(chainurl)); + if (err != NSERROR_OK) { + goto out; } } @@ -1130,20 +1282,93 @@ browser_window__handle_bad_certs(struct browser_window *bw, goto out; } - err = guit->misc->cert_verify(url, - bw->loading_ssl_info.certs, - bw->loading_ssl_info.num, - browser_window__handle_ssl_query_response, - bw); + out: + browser_window__free_fetch_parameters(¶ms); + if (chainurl != NULL) + nsurl_unref(chainurl); + return err; +} - if (err == NSERROR_NOT_IMPLEMENTED) { - err = NSERROR_OK; + +/** + * Handle a timeout during a fetch + */ +static nserror +browser_window__handle_timeout(struct browser_window *bw, nsurl *url) +{ + struct browser_fetch_parameters params; + nserror err; + + memset(¶ms, 0, sizeof(params)); + + params.url = nsurl_ref(corestring_nsurl_about_query_timeout); + params.referrer = nsurl_ref(url); + params.flags = BW_NAVIGATE_HISTORY | BW_NAVIGATE_NO_TERMINAL_HISTORY_UPDATE | BW_NAVIGATE_INTERNAL; + + err = fetch_multipart_data_new_kv(¶ms.post_multipart, + "siteurl", + nsurl_access(url)); + if (err != NSERROR_OK) { + goto out; + } + + /* Now we issue the fetch */ + bw->internal_nav = true; + err = browser_window__navigate_internal(bw, ¶ms); + if (err != NSERROR_OK) { + goto out; + } + + out: + browser_window__free_fetch_parameters(¶ms); + return err; +} + + +/** + * Handle non specific errors during a fetch + */ +static nserror +browser_window__handle_fetcherror(struct browser_window *bw, + const char *reason, + nsurl *url) +{ + struct browser_fetch_parameters params; + nserror err; + + memset(¶ms, 0, sizeof(params)); + + params.url = nsurl_ref(corestring_nsurl_about_query_fetcherror); + params.referrer = nsurl_ref(url); + params.flags = BW_NAVIGATE_HISTORY | BW_NAVIGATE_NO_TERMINAL_HISTORY_UPDATE | BW_NAVIGATE_INTERNAL; + + err = fetch_multipart_data_new_kv(¶ms.post_multipart, + "siteurl", + nsurl_access(url)); + if (err != NSERROR_OK) { + goto out; + } + + err = fetch_multipart_data_new_kv(¶ms.post_multipart, + "reason", + reason); + if (err != NSERROR_OK) { + goto out; + } + + /* Now we issue the fetch */ + bw->internal_nav = true; + err = browser_window__navigate_internal(bw, ¶ms); + if (err != NSERROR_OK) { + goto out; } -out: + + out: browser_window__free_fetch_parameters(¶ms); return err; } + /** * Handle errors during content fetch */ @@ -1154,36 +1379,16 @@ browser_window__handle_error(struct browser_window *bw, { const char *message = event->data.errordata.errormsg; nserror code = event->data.errordata.errorcode; - bool do_warning = true; nserror res; nsurl *url = hlcache_handle_get_url(c); /* Unexpected OK? */ assert(code != NSERROR_OK); - switch (code) { - case NSERROR_BAD_AUTH: - do_warning = false; - break; - case NSERROR_BAD_CERTS: - do_warning = false; - break; - case NSERROR_BAD_REDIRECT: - /* The message is already filled out */ - break; - case NSERROR_UNKNOWN: + if (message == NULL) { message = messages_get_errorcode(code); - break; - default: - break; - } - - if (do_warning) { - browser_window_set_status(bw, message); - /* Only warn the user about errors in top-level windows */ - if (bw->browser_window_type == BROWSER_WINDOW_NORMAL) { - guit->misc->warning(message, NULL); - } + } else { + message = messages_get(message); } if (c == bw->loading_content) { @@ -1199,16 +1404,42 @@ browser_window__handle_error(struct browser_window *bw, case NSERROR_BAD_AUTH: res = browser_window__handle_login(bw, message, url); break; + case NSERROR_BAD_CERTS: res = browser_window__handle_bad_certs(bw, url); break; + + case NSERROR_TIMEOUT: + res = browser_window__handle_timeout(bw, url); + break; + default: + res = browser_window__handle_fetcherror(bw, message, url); break; } - browser_window_stop_throbber(bw); + return res; +} - return NSERROR_OK; + +/** + * Update URL bar for a given browser window to given URL + * + * \param bw Browser window to update URL bar for. + * \param url URL for content displayed by bw including any fragment. + */ +static inline nserror +browser_window_refresh_url_bar_internal(struct browser_window *bw, nsurl *url) +{ + assert(bw); + assert(url); + + if ((bw->parent != NULL) || (bw->window == NULL)) { + /* Not root window or no gui window so do not set a URL */ + return NSERROR_OK; + } + + return guit->window->set_url(bw->window, url); } @@ -1224,12 +1455,10 @@ browser_window_callback(hlcache_handle *c, const hlcache_event *event, void *pw) switch (event->type) { case CONTENT_MSG_SSL_CERTS: /* SSL certificate information has arrived, store it */ - assert(event->data.certs.num < MAX_SSL_CERTS); - memcpy(&bw->loading_ssl_info.certs[0], - event->data.certs.certs, - sizeof(struct ssl_cert_info) * event->data.certs.num); - bw->loading_ssl_info.num = event->data.certs.num; + cert_chain_free(bw->loading_cert_chain); + cert_chain_dup(event->data.chain, &bw->loading_cert_chain); break; + case CONTENT_MSG_LOG: browser_window_console_log(bw, event->data.log.src, @@ -1237,6 +1466,7 @@ browser_window_callback(hlcache_handle *c, const hlcache_event *event, void *pw) event->data.log.msglen, event->data.log.flags); break; + case CONTENT_MSG_DOWNLOAD: assert(bw->loading_content == c); @@ -1281,40 +1511,43 @@ browser_window_callback(hlcache_handle *c, const hlcache_event *event, void *pw) break; case CONTENT_MSG_REDIRECT: - if (urldb_add_url(event->data.redirect.from)) + if (urldb_add_url(event->data.redirect.from)) { urldb_update_url_visit_data(event->data.redirect.from); + } + browser_window_refresh_url_bar_internal(bw, event->data.redirect.to); break; case CONTENT_MSG_STATUS: if (event->data.explicit_status_text == NULL) { /* Object content's status text updated */ const char *status = NULL; - if (bw->loading_content != NULL) + if (bw->loading_content != NULL) { /* Give preference to any loading content */ status = content_get_status_message( - bw->loading_content); + bw->loading_content); + } - if (status == NULL) + if (status == NULL) { status = content_get_status_message(c); + } - if (status != NULL) + if (status != NULL) { browser_window_set_status(bw, status); + } } else { /* Object content wants to set explicit message */ browser_window_set_status(bw, - event->data.explicit_status_text); + event->data.explicit_status_text); } break; case CONTENT_MSG_REFORMAT: - if (c == bw->current_content && - content_get_type(c) == CONTENT_HTML) { - /* reposition frames */ - if (html_get_frameset(c) != NULL) - browser_window_recalculate_frameset(bw); - /* reflow iframe positions */ - if (html_get_iframe(c) != NULL) - browser_window_recalculate_iframes(bw); + if (c == bw->current_content) { + /* recompute frameset */ + browser_window_recalculate_frameset(bw); + + /* recompute iframe positions, sizes and scrollbars */ + browser_window_recalculate_iframes(bw); } /* Hide any caret, but don't remove it */ @@ -1327,133 +1560,146 @@ browser_window_callback(hlcache_handle *c, const hlcache_event *event, void *pw) break; case CONTENT_MSG_REDRAW: - { - struct rect rect = { - .x0 = event->data.redraw.x, - .y0 = event->data.redraw.y, - .x1 = event->data.redraw.x + event->data.redraw.width, - .y1 = event->data.redraw.y + event->data.redraw.height - }; - - browser_window_update_box(bw, &rect); - } - break; + { + struct rect rect = { + .x0 = event->data.redraw.x, + .y0 = event->data.redraw.y, + .x1 = event->data.redraw.x + event->data.redraw.width, + .y1 = event->data.redraw.y + event->data.redraw.height + }; + + browser_window_invalidate_rect(bw, &rect); + } + break; case CONTENT_MSG_REFRESH: bw->refresh_interval = event->data.delay * 100; break; case CONTENT_MSG_LINK: /* content has an rfc5988 link element */ - { - bool match; - - /* Handle "icon" and "shortcut icon" */ - if ((lwc_string_caseless_isequal( - event->data.rfc5988_link->rel, - corestring_lwc_icon, - &match) == lwc_error_ok && match) || - (lwc_string_caseless_isequal( - event->data.rfc5988_link->rel, - corestring_lwc_shortcut_icon, - &match) == lwc_error_ok && match)) { - /* it's a favicon perhaps start a fetch for it */ - browser_window_update_favicon(c, bw, + { + bool match; + + /* Handle "icon" and "shortcut icon" */ + if ((lwc_string_caseless_isequal( + event->data.rfc5988_link->rel, + corestring_lwc_icon, + &match) == lwc_error_ok && match) || + (lwc_string_caseless_isequal( + event->data.rfc5988_link->rel, + corestring_lwc_shortcut_icon, + &match) == lwc_error_ok && match)) { + /* it's a favicon perhaps start a fetch for it */ + browser_window_update_favicon(c, bw, event->data.rfc5988_link); + } } - } - break; + break; - case CONTENT_MSG_GETCTX: - /* only the content object created by the browser - * window requires a new global compartment object - */ - assert(bw->loading_content == c); - if (js_newcompartment(bw->jsctx, - bw, - hlcache_handle_get_content(c)) != NULL) { - *(event->data.jscontext) = bw->jsctx; + case CONTENT_MSG_GETTHREAD: + { + /* only the content object created by the browser + * window requires a new javascript thread object + */ + jsthread *thread; + assert(bw->loading_content == c); + + if (js_newthread(bw->jsheap, + bw, + hlcache_handle_get_content(c), + &thread) == NSERROR_OK) { + /* The content which is requesting the thread + * is required to keep hold of it and + * to destroy it when it is finished with it. + */ + *(event->data.jsthread) = thread; + } } break; case CONTENT_MSG_GETDIMS: - { - int width; - int height; - - browser_window_get_dimensions(bw, &width, &height); + { + int width; + int height; - *(event->data.getdims.viewport_width) = width / bw->scale; - *(event->data.getdims.viewport_height) = height / bw->scale; - break; - } + browser_window_get_dimensions(bw, &width, &height); - case CONTENT_MSG_SCROLL: - { - struct rect rect = { - .x0 = event->data.scroll.x0, - .y0 = event->data.scroll.y0, - }; - - /* Content wants to be scrolled */ - if (bw->current_content != c) { + *(event->data.getdims.viewport_width) = width / bw->scale; + *(event->data.getdims.viewport_height) = height / bw->scale; break; } - if (event->data.scroll.area) { - rect.x1 = event->data.scroll.x1; - rect.y1 = event->data.scroll.y1; - } else { - rect.x1 = event->data.scroll.x0; - rect.y1 = event->data.scroll.y0; - } - browser_window_set_scroll(bw, &rect); - - break; - } + case CONTENT_MSG_SCROLL: + { + struct rect rect = { + .x0 = event->data.scroll.x0, + .y0 = event->data.scroll.y0, + }; + + /* Content wants to be scrolled */ + if (bw->current_content != c) { + break; + } - case CONTENT_MSG_DRAGSAVE: - { - /* Content wants drag save of a content */ - struct browser_window *root = browser_window_get_root(bw); - hlcache_handle *save = event->data.dragsave.content; + if (event->data.scroll.area) { + rect.x1 = event->data.scroll.x1; + rect.y1 = event->data.scroll.y1; + } else { + rect.x1 = event->data.scroll.x0; + rect.y1 = event->data.scroll.y0; + } + browser_window_set_scroll(bw, &rect); - if (save == NULL) { - save = c; + break; } - switch(event->data.dragsave.type) { - case CONTENT_SAVE_ORIG: - guit->window->drag_save_object(root->window, save, - GUI_SAVE_OBJECT_ORIG); - break; + case CONTENT_MSG_DRAGSAVE: + { + /* Content wants drag save of a content */ + struct browser_window *root = browser_window_get_root(bw); + hlcache_handle *save = event->data.dragsave.content; - case CONTENT_SAVE_NATIVE: - guit->window->drag_save_object(root->window, save, - GUI_SAVE_OBJECT_NATIVE); - break; + if (save == NULL) { + save = c; + } - case CONTENT_SAVE_COMPLETE: - guit->window->drag_save_object(root->window, save, - GUI_SAVE_COMPLETE); - break; + switch(event->data.dragsave.type) { + case CONTENT_SAVE_ORIG: + guit->window->drag_save_object(root->window, + save, + GUI_SAVE_OBJECT_ORIG); + break; - case CONTENT_SAVE_SOURCE: - guit->window->drag_save_object(root->window, save, - GUI_SAVE_SOURCE); - break; + case CONTENT_SAVE_NATIVE: + guit->window->drag_save_object(root->window, + save, + GUI_SAVE_OBJECT_NATIVE); + break; + + case CONTENT_SAVE_COMPLETE: + guit->window->drag_save_object(root->window, + save, + GUI_SAVE_COMPLETE); + break; + + case CONTENT_SAVE_SOURCE: + guit->window->drag_save_object(root->window, + save, + GUI_SAVE_SOURCE); + break; + } } - } - break; + break; case CONTENT_MSG_SAVELINK: - { - /* Content wants a link to be saved */ - struct browser_window *root = browser_window_get_root(bw); - guit->window->save_link(root->window, - event->data.savelink.url, - event->data.savelink.title); - } - break; + { + /* Content wants a link to be saved */ + struct browser_window *root = browser_window_get_root(bw); + guit->window->save_link(root->window, + event->data.savelink.url, + event->data.savelink.title); + } + break; case CONTENT_MSG_POINTER: /* Content wants to have specific mouse pointer */ @@ -1461,23 +1707,23 @@ browser_window_callback(hlcache_handle *c, const hlcache_event *event, void *pw) break; case CONTENT_MSG_DRAG: - { - browser_drag_type bdt = DRAGGING_NONE; - - switch (event->data.drag.type) { - case CONTENT_DRAG_NONE: - bdt = DRAGGING_NONE; - break; - case CONTENT_DRAG_SCROLL: - bdt = DRAGGING_CONTENT_SCROLLBAR; - break; - case CONTENT_DRAG_SELECTION: - bdt = DRAGGING_SELECTION; - break; + { + browser_drag_type bdt = DRAGGING_NONE; + + switch (event->data.drag.type) { + case CONTENT_DRAG_NONE: + bdt = DRAGGING_NONE; + break; + case CONTENT_DRAG_SCROLL: + bdt = DRAGGING_CONTENT_SCROLLBAR; + break; + case CONTENT_DRAG_SELECTION: + bdt = DRAGGING_SELECTION; + break; + } + browser_window_set_drag_type(bw, bdt, event->data.drag.rect); } - browser_window_set_drag_type(bw, bdt, event->data.drag.rect); - } - break; + break; case CONTENT_MSG_CARET: switch (event->data.caret.type) { @@ -1523,6 +1769,37 @@ browser_window_callback(hlcache_handle *c, const hlcache_event *event, void *pw) break; + + case CONTENT_MSG_TEXTSEARCH: + switch (event->data.textsearch.type) { + case CONTENT_TEXTSEARCH_FIND: + guit->search->hourglass(event->data.textsearch.state, + event->data.textsearch.ctx); + break; + + case CONTENT_TEXTSEARCH_MATCH: + guit->search->status(event->data.textsearch.state, + event->data.textsearch.ctx); + break; + + case CONTENT_TEXTSEARCH_BACK: + guit->search->back_state(event->data.textsearch.state, + event->data.textsearch.ctx); + break; + + case CONTENT_TEXTSEARCH_FORWARD: + guit->search->forward_state(event->data.textsearch.state, + event->data.textsearch.ctx); + break; + + case CONTENT_TEXTSEARCH_RECENT: + guit->search->add_recent(event->data.textsearch.string, + event->data.textsearch.ctx); + + break; + } + break; + default: break; } @@ -1551,21 +1828,13 @@ static void scheduled_reformat(void *vbw) } } - -/** - * Release all memory associated with a browser window. - * - * \param bw browser window - */ -static void browser_window_destroy_internal(struct browser_window *bw) +/* exported interface documented in desktop/browser_private.h */ +nserror browser_window_destroy_internal(struct browser_window *bw) { assert(bw); - NSLOG(netsurf, INFO, "Destroying window"); - - if (bw->children != NULL || bw->iframes != NULL) { - browser_window_destroy_children(bw); - } + browser_window_destroy_children(bw); + browser_window_destroy_iframes(bw); /* Destroy scrollbars */ if (bw->scroll_x != NULL) { @@ -1629,13 +1898,9 @@ static void browser_window_destroy_internal(struct browser_window *bw) bw->favicon.current = NULL; } - if (bw->box != NULL) { - bw->box->iframe = NULL; - bw->box = NULL; - } - - if (bw->jsctx != NULL) { - js_destroycontext(bw->jsctx); + if (bw->jsheap != NULL) { + js_destroyheap(bw->jsheap); + bw->jsheap = NULL; } /* These simply free memory, so are safe here */ @@ -1646,6 +1911,11 @@ static void browser_window_destroy_internal(struct browser_window *bw) browser_window_history_destroy(bw); + cert_chain_free(bw->current_cert_chain); + cert_chain_free(bw->loading_cert_chain); + bw->current_cert_chain = NULL; + bw->loading_cert_chain = NULL; + free(bw->name); free(bw->status.text); bw->status.text = NULL; @@ -1653,62 +1923,8 @@ static void browser_window_destroy_internal(struct browser_window *bw) browser_window__free_fetch_parameters(&bw->loading_parameters); NSLOG(netsurf, INFO, "Status text cache match:miss %d:%d", bw->status.match, bw->status.miss); -} - - -/** - * Update URL bar for a given browser window to given URL - * - * \param bw Browser window to update URL bar for. - * \param url URL for content displayed by bw including any fragment. - */ -static inline nserror -browser_window_refresh_url_bar_internal(struct browser_window *bw, nsurl *url) -{ - assert(bw); - assert(url); - - if ((bw->parent != NULL) || (bw->window == NULL)) { - /* Not root window or no gui window so do not set a URL */ - return NSERROR_OK; - } - - return guit->window->set_url(bw->window, url); -} - - -/** - * scroll to a fragment if present - * - * \param bw browser window - * \return true if the scroll was sucessful - */ -static bool frag_scroll(struct browser_window *bw) -{ - struct rect rect; - if (bw->frag_id == NULL) { - return false; - } - - if (!html_get_id_offset(bw->current_content, - bw->frag_id, - &rect.x0, - &rect.y0)) { - return false; - } - - rect.x1 = rect.x0; - rect.y1 = rect.y0; - if (browser_window_set_scroll(bw, &rect) == NSERROR_OK) { - if (bw->current_content != NULL && - bw->history != NULL && - bw->history->current != NULL) { - browser_window_history_update(bw, bw->current_content); - } - return true; - } - return false; + return NSERROR_OK; } @@ -1744,7 +1960,7 @@ browser_window_set_scale_internal(struct browser_window *bw, float scale) res = browser_window_set_scale_internal(&bw->children[i], scale); } - /* sale iframes */ + /* scale iframes */ for (i = 0; i < bw->iframe_count; i++) { res = browser_window_set_scale_internal(&bw->iframes[i], scale); } @@ -1798,9 +2014,9 @@ browser_window_find_target_internal(struct browser_window *bw, } if (bw->children[i].children) browser_window_find_target_internal( - &bw->children[i], - target, depth, page, - rdepth, bw_target); + &bw->children[i], + target, depth, page, + rdepth, bw_target); } } @@ -1882,8 +2098,8 @@ browser_window_mouse_drag_end(struct browser_window *bw, */ static void browser_window_mouse_click_internal(struct browser_window *bw, - browser_mouse_state mouse, - int x, int y) + browser_mouse_state mouse, + int x, int y) { hlcache_handle *c = bw->current_content; const char *status = NULL; @@ -1909,11 +2125,11 @@ browser_window_mouse_click_internal(struct browser_window *bw, /* It's this child that contains the click; pass it * on to child. */ - browser_window_mouse_click_internal(child, mouse, - x - child->x + scrollbar_get_offset( - child->scroll_x), - y - child->y + scrollbar_get_offset( - child->scroll_y)); + browser_window_mouse_click_internal( + child, + mouse, + x - child->x + scrollbar_get_offset(child->scroll_x), + y - child->y + scrollbar_get_offset(child->scroll_y)); /* Mouse action was for this child, we're done */ return; @@ -1934,9 +2150,9 @@ browser_window_mouse_click_internal(struct browser_window *bw, if (scr_x > 0 && scr_x < get_horz_scrollbar_len(bw) && scr_y > 0 && scr_y < SCROLLBAR_WIDTH) { status = scrollbar_mouse_status_to_message( - scrollbar_mouse_action( - bw->scroll_x, mouse, - scr_x, scr_y)); + scrollbar_mouse_action( + bw->scroll_x, mouse, + scr_x, scr_y)); pointer = BROWSER_POINTER_DEFAULT; if (status != NULL) @@ -1956,13 +2172,16 @@ browser_window_mouse_click_internal(struct browser_window *bw, if (scr_y > 0 && scr_y < get_vert_scrollbar_len(bw) && scr_x > 0 && scr_x < SCROLLBAR_WIDTH) { status = scrollbar_mouse_status_to_message( - scrollbar_mouse_action( - bw->scroll_y, mouse, - scr_x, scr_y)); + scrollbar_mouse_action( + bw->scroll_y, + mouse, + scr_x, + scr_y)); pointer = BROWSER_POINTER_DEFAULT; - if (status != NULL) + if (status != NULL) { browser_window_set_status(bw, status); + } browser_window_set_pointer(bw, pointer); return; @@ -1972,19 +2191,19 @@ browser_window_mouse_click_internal(struct browser_window *bw, switch (content_get_type(c)) { case CONTENT_HTML: case CONTENT_TEXTPLAIN: - { - /* Give bw focus */ - struct browser_window *root_bw = browser_window_get_root(bw); - if (bw != root_bw->focus) { - browser_window_remove_caret(bw, false); - browser_window_set_selection(bw, false, true); - root_bw->focus = bw; - } + { + /* Give bw focus */ + struct browser_window *root_bw = browser_window_get_root(bw); + if (bw != root_bw->focus) { + browser_window_remove_caret(bw, false); + browser_window_set_selection(bw, false, true); + root_bw->focus = bw; + } - /* Pass mouse action to content */ - content_mouse_action(c, bw, mouse, x, y); - } - break; + /* Pass mouse action to content */ + content_mouse_action(c, bw, mouse, x, y); + } + break; default: if (mouse & BROWSER_MOUSE_MOD_2) { if (mouse & BROWSER_MOUSE_DRAG_2) { @@ -2032,14 +2251,15 @@ browser_window_mouse_track_internal(struct browser_window *bw, browser_window_get_position(drag_bw, true, &off_x, &off_y); if (drag_bw->browser_window_type == BROWSER_WINDOW_FRAME) { - browser_window_mouse_track_internal(drag_bw, mouse, - x - off_x, y - off_y); + browser_window_mouse_track_internal(drag_bw, + mouse, + x - off_x, + y - off_y); - } else if (drag_bw->browser_window_type == - BROWSER_WINDOW_IFRAME) { + } else if (drag_bw->browser_window_type == BROWSER_WINDOW_IFRAME) { browser_window_mouse_track_internal(drag_bw, mouse, - x - off_x / bw->scale, - y - off_y / bw->scale); + x - off_x / bw->scale, + y - off_y / bw->scale); } return; } @@ -2102,9 +2322,10 @@ browser_window_mouse_track_internal(struct browser_window *bw, bw->drag.type == DRAGGING_NONE)) { /* Start a scrollbar drag, or continue existing drag */ status = scrollbar_mouse_status_to_message( - scrollbar_mouse_action( - bw->scroll_x, mouse, - scr_x, scr_y)); + scrollbar_mouse_action(bw->scroll_x, + mouse, + scr_x, + scr_y)); pointer = BROWSER_POINTER_DEFAULT; if (status != NULL) { @@ -2131,9 +2352,10 @@ browser_window_mouse_track_internal(struct browser_window *bw, bw->drag.type == DRAGGING_NONE)) { /* Start a scrollbar drag, or continue existing drag */ status = scrollbar_mouse_status_to_message( - scrollbar_mouse_action( - bw->scroll_y, mouse, - scr_x, scr_y)); + scrollbar_mouse_action(bw->scroll_y, + mouse, + scr_x, + scr_y)); pointer = BROWSER_POINTER_DEFAULT; if (status != NULL) { @@ -2178,8 +2400,8 @@ browser_window_mouse_track_internal(struct browser_window *bw, */ static bool browser_window_scroll_at_point_internal(struct browser_window *bw, - int x, int y, - int scrx, int scry) + int x, int y, + int scrx, int scry) { bool handled_scroll = false; assert(bw != NULL); @@ -2207,26 +2429,29 @@ browser_window_scroll_at_point_internal(struct browser_window *bw, /* Pass request into this child */ return browser_window_scroll_at_point_internal( - bwc, - (x - bwc->x), - (y - bwc->y), - scrx, scry); + bwc, + (x - bwc->x), + (y - bwc->y), + scrx, scry); } } /* Try to scroll any current content */ - if (bw->current_content != NULL && content_scroll_at_point( - bw->current_content, x, y, scrx, scry) == true) + if (bw->current_content != NULL && + content_scroll_at_point(bw->current_content, x, y, scrx, scry) == true) { /* Scroll handled by current content */ return true; + } /* Try to scroll this window, if scroll not already handled */ if (handled_scroll == false) { - if (bw->scroll_y && scrollbar_scroll(bw->scroll_y, scry)) + if (bw->scroll_y && scrollbar_scroll(bw->scroll_y, scry)) { handled_scroll = true; + } - if (bw->scroll_x && scrollbar_scroll(bw->scroll_x, scrx)) + if (bw->scroll_x && scrollbar_scroll(bw->scroll_x, scrx)) { handled_scroll = true; + } } return handled_scroll; @@ -2270,10 +2495,11 @@ browser_window_drop_file_at_point_internal(struct browser_window *bw, continue; /* Pass request into this child */ - return browser_window_drop_file_at_point_internal(bwc, - (x - bwc->x), - (y - bwc->y), - file); + return browser_window_drop_file_at_point_internal( + bwc, + (x - bwc->x), + (y - bwc->y), + file); } } @@ -2311,6 +2537,10 @@ is_internal_navigate_url(nsurl *url) is_internal = true; } else if (path == corestring_lwc_query_ssl) { is_internal = true; + } else if (path == corestring_lwc_query_timeout) { + is_internal = true; + } else if (path == corestring_lwc_query_fetcherror) { + is_internal = true; } } lwc_string_unref(path); @@ -2374,14 +2604,14 @@ browser_window_redraw(struct browser_window *bw, struct rect content_clip; nserror res; - x /= bw->scale; - y /= bw->scale; - if (bw == NULL) { NSLOG(netsurf, INFO, "NULL browser window"); return false; } + x /= bw->scale; + y /= bw->scale; + if ((bw->current_content == NULL) && (bw->children == NULL)) { /* Browser window has no content, render blank fill */ @@ -2416,7 +2646,7 @@ browser_window_redraw(struct browser_window *bw, /* Set current child */ child = &bw->children[cur_child]; - /* Get frame edge box in global coordinates */ + /* Get frame edge area in global coordinates */ content_clip.x0 = (x + child->x) * child->scale; content_clip.y0 = (y + child->y) * child->scale; content_clip.x1 = content_clip.x0 + @@ -2786,8 +3016,10 @@ browser_window_get_features(struct browser_window *bw, data->main = NULL; data->form_features = CTX_FORM_NONE; - return browser_window__get_contextual_content( - bw, x / bw->scale, y / bw->scale, data); + return browser_window__get_contextual_content(bw, + x / bw->scale, + y / bw->scale, + data); } @@ -2909,6 +3141,10 @@ browser_window_create(enum browser_window_create_flags flags, gw_flags |= GW_CREATE_TAB; if (flags & BW_CREATE_CLONE) gw_flags |= GW_CREATE_CLONE; + if (flags & BW_CREATE_FOREGROUND) + gw_flags |= GW_CREATE_FOREGROUND; + if (flags & BW_CREATE_FOCUS_LOCATION) + gw_flags |= GW_CREATE_FOCUS_LOCATION; ret->window = guit->window->create(ret, (existing != NULL) ? existing->window : NULL, @@ -2920,13 +3156,21 @@ browser_window_create(enum browser_window_create_flags flags, } if (url != NULL) { - enum browser_window_nav_flags nav_flags = BW_NAVIGATE_NO_TERMINAL_HISTORY_UPDATE; - if (flags & BW_CREATE_UNVERIFIABLE) + enum browser_window_nav_flags nav_flags; + nav_flags = BW_NAVIGATE_NO_TERMINAL_HISTORY_UPDATE; + if (flags & BW_CREATE_UNVERIFIABLE) { nav_flags |= BW_NAVIGATE_UNVERIFIABLE; - if (flags & BW_CREATE_HISTORY) + } + if (flags & BW_CREATE_HISTORY) { nav_flags |= BW_NAVIGATE_HISTORY; - browser_window_navigate(ret, url, referrer, nav_flags, NULL, - NULL, NULL); + } + browser_window_navigate(ret, + url, + referrer, + nav_flags, + NULL, + NULL, + NULL); } if (bw != NULL) { @@ -2941,14 +3185,13 @@ browser_window_create(enum browser_window_create_flags flags, nserror browser_window_initialise_common(enum browser_window_create_flags flags, struct browser_window *bw, - struct browser_window *existing) + const struct browser_window *existing) { nserror err; assert(bw); /* new javascript context for each window/(i)frame */ - err = js_newcontext(nsoption_int(script_timeout), - slow_script, NULL, &bw->jsctx); + err = js_newheap(nsoption_int(script_timeout), &bw->jsheap); if (err != NSERROR_OK) return err; @@ -3019,16 +3262,10 @@ nserror browser_window_refresh_url_bar(struct browser_window *bw) if (bw->current_content == NULL) { /* no content so return about:blank */ ret = browser_window_refresh_url_bar_internal(bw, - corestring_nsurl_about_blank); - } else if (bw->throbbing) { - /* We're throbbing, so show the loading parameters url, - * or if there isn't one, the current parameters url - */ - if (bw->loading_parameters.url != NULL) { - url = bw->loading_parameters.url; - } else { - url = bw->current_parameters.url; - } + corestring_nsurl_about_blank); + } else if (bw->throbbing && bw->loading_parameters.url != NULL) { + /* Throbbing and we have loading parameters, use those */ + url = bw->loading_parameters.url; ret = browser_window_refresh_url_bar_internal(bw, url); } else if (bw->frag_id == NULL) { if (bw->internal_nav) { @@ -3045,11 +3282,11 @@ nserror browser_window_refresh_url_bar(struct browser_window *bw) url = hlcache_handle_get_url(bw->current_content); } ret = nsurl_refragment( - url, - bw->frag_id, &display_url); + url, + bw->frag_id, &display_url); if (ret == NSERROR_OK) { ret = browser_window_refresh_url_bar_internal(bw, - display_url); + display_url); nsurl_unref(display_url); } } @@ -3183,10 +3420,10 @@ browser_window_navigate(struct browser_window *bw, /* Compare new URL with existing one (ignoring fragments) */ if ((bw->current_content != NULL) && (hlcache_handle_get_url(bw->current_content) != NULL)) { - same_url = nsurl_compare(url, - hlcache_handle_get_url( - bw->current_content), - NSURL_COMPLETE); + same_url = nsurl_compare( + url, + hlcache_handle_get_url(bw->current_content), + NSURL_COMPLETE); } /* if we're simply moving to another ID on the same page, @@ -3219,6 +3456,7 @@ browser_window_navigate(struct browser_window *bw, browser_window_stop(bw); browser_window_remove_caret(bw, false); browser_window_destroy_children(bw); + browser_window_destroy_iframes(bw); /* Set up the fetch parameters */ memset(¶ms, 0, sizeof(params)); @@ -3271,21 +3509,28 @@ browser_window_navigate(struct browser_window *bw, return error; } + +/** + * Internal navigation handler for normal fetches + */ static nserror -browser_window__navigate_internal_real(struct browser_window *bw, - struct browser_fetch_parameters *params) +navigate_internal_real(struct browser_window *bw, + struct browser_fetch_parameters *params) { uint32_t fetch_flags = 0; - bool fetch_is_post = (params->post_urlenc != NULL || params->post_multipart != NULL); + bool fetch_is_post; llcache_post_data post; hlcache_child_context child; - nserror error; + nserror res; hlcache_handle *c; NSLOG(netsurf, INFO, "Loading '%s'", nsurl_access(params->url)); + fetch_is_post = (params->post_urlenc != NULL || params->post_multipart != NULL); + /* Clear SSL info for load */ - bw->loading_ssl_info.num = 0; + cert_chain_free(bw->loading_cert_chain); + bw->loading_cert_chain = NULL; /* Set up retrieval parameters */ if (!(params->flags & BW_NAVIGATE_UNVERIFIABLE)) { @@ -3313,20 +3558,26 @@ browser_window__navigate_internal_real(struct browser_window *bw, fetch_flags |= HLCACHE_RETRIEVE_MAY_DOWNLOAD; } - error = hlcache_handle_retrieve(params->url, - fetch_flags | HLCACHE_RETRIEVE_SNIFF_TYPE, - params->referrer, - fetch_is_post ? &post : NULL, - browser_window_callback, bw, - params->parent_charset != NULL ? &child : NULL, - CONTENT_ANY, &c); + res = hlcache_handle_retrieve(params->url, + fetch_flags | HLCACHE_RETRIEVE_SNIFF_TYPE, + params->referrer, + fetch_is_post ? &post : NULL, + browser_window_callback, + bw, + params->parent_charset != NULL ? &child : NULL, + CONTENT_ANY, + &c); - switch (error) { + switch (res) { case NSERROR_OK: bw->loading_content = c; browser_window_start_throbber(bw); + if (bw->window != NULL) { + guit->window->set_icon(bw->window, NULL); + } if (bw->internal_nav == false) { - error = browser_window_refresh_url_bar_internal(bw, params->url); + res = browser_window_refresh_url_bar_internal(bw, + params->url); } break; @@ -3334,13 +3585,11 @@ browser_window__navigate_internal_real(struct browser_window *bw, /** \todo does this always try and download even * unverifiable content? */ - error = guit->misc->launch_url(params->url); + res = guit->misc->launch_url(params->url); break; default: /* report error to user */ - browser_window_set_status(bw, messages_get_errorcode(error)); - /** @todo should the caller report the error? */ - guit->misc->warning(messages_get_errorcode(error), NULL); + browser_window_set_status(bw, messages_get_errorcode(res)); break; } @@ -3348,9 +3597,10 @@ browser_window__navigate_internal_real(struct browser_window *bw, /* Record time */ nsu_getmonotonic_ms(&bw->last_action); - return error; + return res; } + /** * Internal navigation handler for the authentication query handler * @@ -3358,8 +3608,8 @@ browser_window__navigate_internal_real(struct browser_window *bw, * then we deal with that, otherwise we pass it on to the about: handler */ static nserror -browser_window__navigate_internal_query_auth(struct browser_window *bw, - struct browser_fetch_parameters *params) +navigate_internal_query_auth(struct browser_window *bw, + struct browser_fetch_parameters *params) { char *userpass = NULL; const char *username, *password, *realm, *siteurl; @@ -3374,7 +3624,7 @@ browser_window__navigate_internal_query_auth(struct browser_window *bw, if (!(is_login || is_cancel)) { /* This is a request, so pass it on */ - return browser_window__navigate_internal_real(bw, params); + return navigate_internal_real(bw, params); } if (is_cancel) { @@ -3424,7 +3674,7 @@ browser_window__navigate_internal_query_auth(struct browser_window *bw, /* Finally navigate to the original loading parameters */ bw->internal_nav = false; - return browser_window__navigate_internal_real(bw, &bw->loading_parameters); + return navigate_internal_real(bw, &bw->loading_parameters); } @@ -3435,35 +3685,123 @@ browser_window__navigate_internal_query_auth(struct browser_window *bw, * then we deal with that, otherwise we pass it on to the about: handler */ static nserror -browser_window__navigate_internal_query_ssl(struct browser_window *bw, - struct browser_fetch_parameters *params) +navigate_internal_query_ssl(struct browser_window *bw, + struct browser_fetch_parameters *params) { bool is_proceed = false, is_back = false; + const char *siteurl = NULL; + nsurl *siteurl_ns; assert(params->post_multipart != NULL); is_proceed = fetch_multipart_data_find(params->post_multipart, "proceed") != NULL; is_back = fetch_multipart_data_find(params->post_multipart, "back") != NULL; + siteurl = fetch_multipart_data_find(params->post_multipart, "siteurl"); - if (!(is_proceed || is_back)) { + if (!(is_proceed || is_back) || siteurl == NULL) { /* This is a request, so pass it on */ - return browser_window__navigate_internal_real(bw, params); + return navigate_internal_real(bw, params); + } + + if (nsurl_create(siteurl, &siteurl_ns) != NSERROR_OK) { + NSLOG(netsurf, ERROR, "Unable to reset ssl loading parameters"); + } else { + /* In order that we may proceed, replace the loading parameters */ + nsurl_unref(bw->loading_parameters.url); + bw->loading_parameters.url = siteurl_ns; } return browser_window__handle_ssl_query_response(is_proceed, bw); } +/** + * Internal navigation handler for the timeout query page. + * + * If the parameters indicate we're processing a *response* from the handler + * then we deal with that, otherwise we pass it on to the about: handler + */ +static nserror +navigate_internal_query_timeout(struct browser_window *bw, + struct browser_fetch_parameters *params) +{ + bool is_retry = false, is_back = false; + + NSLOG(netsurf, INFO, "bw:%p params:%p", bw, params); + + assert(params->post_multipart != NULL); + + is_retry = fetch_multipart_data_find(params->post_multipart, "retry") != NULL; + is_back = fetch_multipart_data_find(params->post_multipart, "back") != NULL; + + if (is_back) { + /* do a rough-and-ready nav to the old 'current' + * parameters, with any post data stripped away + */ + return browser_window__reload_current_parameters(bw); + } + + if (is_retry) { + /* Finally navigate to the original loading parameters */ + bw->internal_nav = false; + return navigate_internal_real(bw, &bw->loading_parameters); + } + + return navigate_internal_real(bw, params); +} + + +/** + * Internal navigation handler for the fetch error query page. + * + * If the parameters indicate we're processing a *response* from the handler + * then we deal with that, otherwise we pass it on to the about: handler + */ +static nserror +navigate_internal_query_fetcherror(struct browser_window *bw, + struct browser_fetch_parameters *params) +{ + bool is_retry = false, is_back = false; + + NSLOG(netsurf, INFO, "bw:%p params:%p", bw, params); + + assert(params->post_multipart != NULL); + + is_retry = fetch_multipart_data_find(params->post_multipart, "retry") != NULL; + is_back = fetch_multipart_data_find(params->post_multipart, "back") != NULL; + + if (is_back) { + /* do a rough-and-ready nav to the old 'current' + * parameters, with any post data stripped away + */ + return browser_window__reload_current_parameters(bw); + } + + if (is_retry) { + /* Finally navigate to the original loading parameters */ + bw->internal_nav = false; + return navigate_internal_real(bw, &bw->loading_parameters); + } + + return navigate_internal_real(bw, params); +} + + +/** + * dispatch to internal query handlers or normal navigation + * + * Here we determine if we're navigating to an internal query URI and + * if so, what we need to do about it. + * + * \note these check must match those in is_internal_navigate_url() + * + * If we're not, then we just move on to the real navigate. + */ nserror browser_window__navigate_internal(struct browser_window *bw, struct browser_fetch_parameters *params) { lwc_string *scheme, *path; - /* Here we determine if we're navigating to an internal query URI - * and if so, what we need to do about it. - * - * If we're not, then we just move on to the real navigate. - */ /* All our special URIs are in the about: scheme */ scheme = nsurl_get_component(params->url, NSURL_SCHEME); @@ -3477,18 +3815,28 @@ browser_window__navigate_internal(struct browser_window *bw, path = nsurl_get_component(params->url, NSURL_PATH); if (path == corestring_lwc_query_auth) { lwc_string_unref(path); - return browser_window__navigate_internal_query_auth(bw, params); + return navigate_internal_query_auth(bw, params); } if (path == corestring_lwc_query_ssl) { lwc_string_unref(path); - return browser_window__navigate_internal_query_ssl(bw, params); + return navigate_internal_query_ssl(bw, params); + } + if (path == corestring_lwc_query_timeout) { + lwc_string_unref(path); + return navigate_internal_query_timeout(bw, params); + } + if (path == corestring_lwc_query_fetcherror) { + lwc_string_unref(path); + return navigate_internal_query_fetcherror(bw, params); + } + if (path != NULL) { + lwc_string_unref(path); } - lwc_string_unref(path); /* Fall through to a normal about: fetch */ -normal_fetch: - return browser_window__navigate_internal_real(bw, params); + normal_fetch: + return navigate_internal_real(bw, params); } @@ -3499,12 +3847,12 @@ bool browser_window_up_available(struct browser_window *bw) if (bw != NULL && bw->current_content != NULL) { nsurl *parent; - nserror err = nsurl_parent(hlcache_handle_get_url( - bw->current_content), - &parent); + nserror err; + err = nsurl_parent(hlcache_handle_get_url(bw->current_content), + &parent); if (err == NSERROR_OK) { result = nsurl_compare(hlcache_handle_get_url( - bw->current_content), + bw->current_content), parent, NSURL_COMPLETE) == false; nsurl_unref(parent); @@ -3552,7 +3900,7 @@ nserror browser_window_navigate_up(struct browser_window *bw, bool new_window) /* Exported interface, documented in include/netsurf/browser_window.h */ -nsurl* browser_window_access_url(struct browser_window *bw) +nsurl* browser_window_access_url(const struct browser_window *bw) { assert(bw != NULL); @@ -3704,91 +4052,9 @@ browser_window_set_dimensions(struct browser_window *bw, int width, int height) } -/* Exported interface, documented in netsurf/browser_window.h */ -void browser_window_update(struct browser_window *bw, bool scroll_to_top) -{ - static const struct rect zrect = { - .x0 = 0, - .y0 = 0, - .x1 = 0, - .y1 = 0 - }; - - if (bw->current_content == NULL) { - return; - } - - switch (bw->browser_window_type) { - - case BROWSER_WINDOW_NORMAL: - /* Root browser window, constituting a front end window/tab */ - guit->window->set_title(bw->window, - content_get_title(bw->current_content)); - - browser_window_update_extent(bw); - - /* if frag_id exists, then try to scroll to it */ - /** @todo don't do this if the user has scrolled */ - if (!frag_scroll(bw)) { - if (scroll_to_top) { - browser_window_set_scroll(bw, &zrect); - } - } - - guit->window->invalidate(bw->window, NULL); - - break; - - case BROWSER_WINDOW_IFRAME: - /* Internal iframe browser window */ - assert(bw->parent != NULL); - assert(bw->parent->current_content != NULL); - - browser_window_update_extent(bw); - - if (scroll_to_top) { - browser_window_set_scroll(bw, &zrect); - } - - /* if frag_id exists, then try to scroll to it */ - /** @todo don't do this if the user has scrolled */ - frag_scroll(bw); - - html_redraw_a_box(bw->parent->current_content, bw->box); - break; - - case BROWSER_WINDOW_FRAME: - { - struct rect rect; - browser_window_update_extent(bw); - - if (scroll_to_top) { - browser_window_set_scroll(bw, &zrect); - } - - /* if frag_id exists, then try to scroll to it */ - /** @todo don't do this if the user has scrolled */ - frag_scroll(bw); - - rect.x0 = scrollbar_get_offset(bw->scroll_x); - rect.y0 = scrollbar_get_offset(bw->scroll_y); - rect.x1 = rect.x0 + bw->width; - rect.y1 = rect.y0 + bw->height; - - browser_window_update_box(bw, &rect); - } - break; - - default: - case BROWSER_WINDOW_FRAMESET: - /* Nothing to do */ - break; - } -} - - -/* Exported interface, documented in netsurf/browser_window.h */ -void browser_window_update_box(struct browser_window *bw, struct rect *rect) +/* Exported interface, documented in browser/browser_private.h */ +nserror +browser_window_invalidate_rect(struct browser_window *bw, struct rect *rect) { int pos_x; int pos_y; @@ -3813,7 +4079,7 @@ void browser_window_update_box(struct browser_window *bw, struct rect *rect) rect->x1 *= top->scale; rect->y1 *= top->scale; - guit->window->invalidate(top->window, rect); + return guit->window->invalidate(top->window, rect); } @@ -3828,8 +4094,8 @@ void browser_window_stop(struct browser_window *bw) bw->loading_content = NULL; } - if (bw->current_content != NULL && content_get_status( - bw->current_content) != CONTENT_STATUS_DONE) { + if (bw->current_content != NULL && + content_get_status(bw->current_content) != CONTENT_STATUS_DONE) { nserror error; assert(content_get_status(bw->current_content) == CONTENT_STATUS_READY); @@ -3859,13 +4125,16 @@ void browser_window_stop(struct browser_window *bw) /* Exported interface, documented in netsurf/browser_window.h */ -void browser_window_reload(struct browser_window *bw, bool all) +nserror browser_window_reload(struct browser_window *bw, bool all) { hlcache_handle *c; unsigned int i; + struct nsurl *reload_url; - if (bw->current_content == NULL || bw->loading_content != NULL) - return; + if ((bw->current_content) == NULL || + (bw->loading_content) != NULL) { + return NSERROR_INVALID; + } if (all && content_get_type(bw->current_content) == CONTENT_HTML) { struct html_stylesheet *sheets; @@ -3894,13 +4163,15 @@ void browser_window_reload(struct browser_window *bw, bool all) content_invalidate_reuse_data(bw->current_content); - browser_window_navigate(bw, - hlcache_handle_get_url(bw->current_content), - NULL, - BW_NAVIGATE_NONE, - NULL, - NULL, - NULL); + reload_url = hlcache_handle_get_url(bw->current_content); + + return browser_window_navigate(bw, + reload_url, + NULL, + BW_NAVIGATE_NONE, + NULL, + NULL, + NULL); } @@ -3990,8 +4261,11 @@ nserror browser_window_schedule_reformat(struct browser_window *bw) /* exported function documented in netsurf/browser_window.h */ -void browser_window_reformat(struct browser_window *bw, bool background, - int width, int height) +void +browser_window_reformat(struct browser_window *bw, + bool background, + int width, + int height) { hlcache_handle *c = bw->current_content; @@ -4079,10 +4353,14 @@ browser_window_find_target(struct browser_window *bw, /* use the base target if we don't have one */ c = bw->current_content; - if (target == NULL && c != NULL && content_get_type(c) == CONTENT_HTML) + if (target == NULL && + c != NULL && + content_get_type(c) == CONTENT_HTML) { target = html_get_base_target(c); - if (target == NULL) - target = TARGET_SELF; + } + if (target == NULL) { + target = "_self"; + } /* allow the simple case of target="_blank" to be ignored if requested */ @@ -4093,7 +4371,7 @@ browser_window_find_target(struct browser_window *bw, /* not a mouse button 2 click * not a mouse button 1 click with ctrl pressed * configured to ignore target="_blank" */ - if ((target == TARGET_BLANK) || (!strcasecmp(target, "_blank"))) + if (!strcasecmp(target, "_blank")) return bw; } @@ -4104,8 +4382,7 @@ browser_window_find_target(struct browser_window *bw, ((mouse & BROWSER_MOUSE_CLICK_1) && (mouse & BROWSER_MOUSE_MOD_2))) || ((nsoption_bool(button_2_tab)) && - ((target == TARGET_BLANK) || - (!strcasecmp(target, "_blank"))))) { + (!strcasecmp(target, "_blank")))) { /* open in new tab if: * - button_2 opens in new tab and button_2 was pressed * OR @@ -4131,8 +4408,7 @@ browser_window_find_target(struct browser_window *bw, ((mouse & BROWSER_MOUSE_CLICK_1) && (mouse & BROWSER_MOUSE_MOD_2))) || ((!nsoption_bool(button_2_tab)) && - ((target == TARGET_BLANK) || - (!strcasecmp(target, "_blank"))))) { + (!strcasecmp(target, "_blank")))) { /* open in new window if: * - button_2 doesn't open in new tabs and button_2 was pressed * OR @@ -4152,14 +4428,13 @@ browser_window_find_target(struct browser_window *bw, return bw; } return bw_target; - } else if ((target == TARGET_SELF) || (!strcasecmp(target, "_self"))) { + } else if (!strcasecmp(target, "_self")) { return bw; - } else if ((target == TARGET_PARENT) || - (!strcasecmp(target, "_parent"))) { + } else if (!strcasecmp(target, "_parent")) { if (bw->parent) return bw->parent; return bw; - } else if ((target == TARGET_TOP) || (!strcasecmp(target, "_top"))) { + } else if (!strcasecmp(target, "_top")) { while (bw->parent) bw = bw->parent; return bw; @@ -4200,8 +4475,6 @@ browser_window_find_target(struct browser_window *bw, * that begin with an underscore. */ if (target[0] != '_') { bw_target->name = strdup(target); - if (!bw_target->name) - guit->misc->warning("NoMemory", NULL); } return bw_target; } @@ -4365,6 +4638,7 @@ browser_window_console_log(struct browser_window *bw, return NSERROR_OK; } + /* Exported interface, documented in browser_private.h */ nserror browser_window__reload_current_parameters(struct browser_window *bw) @@ -4388,9 +4662,152 @@ browser_window__reload_current_parameters(struct browser_window *bw) bw->current_parameters.flags &= ~BW_NAVIGATE_HISTORY; bw->internal_nav = false; - + browser_window__free_fetch_parameters(&bw->loading_parameters); memcpy(&bw->loading_parameters, &bw->current_parameters, sizeof(bw->loading_parameters)); memset(&bw->current_parameters, 0, sizeof(bw->current_parameters)); return browser_window__navigate_internal(bw, &bw->loading_parameters); } + +/* Exported interface, documented in browser_window.h */ +browser_window_page_info_state browser_window_get_page_info_state( + const struct browser_window *bw) +{ + lwc_string *scheme; + bool match; + + assert(bw != NULL); + + /* Do we have any content? If not -- UNKNOWN */ + if (bw->current_content == NULL) { + return PAGE_STATE_UNKNOWN; + } + + scheme = nsurl_get_component( + hlcache_handle_get_url(bw->current_content), NSURL_SCHEME); + + /* Is this an internal scheme? */ + if ((lwc_string_isequal(scheme, corestring_lwc_about, + &match) == lwc_error_ok && + (match == true)) || + (lwc_string_isequal(scheme, corestring_lwc_data, + &match) == lwc_error_ok && + (match == true)) || + (lwc_string_isequal(scheme, corestring_lwc_resource, + &match) == lwc_error_ok && + (match == true))) { + lwc_string_unref(scheme); + return PAGE_STATE_INTERNAL; + } + + /* Is this file:/// ? */ + if (lwc_string_isequal(scheme, corestring_lwc_file, + &match) == lwc_error_ok && + match == true) { + lwc_string_unref(scheme); + return PAGE_STATE_LOCAL; + } + + /* If not https, from here on down that'd be insecure */ + if ((lwc_string_isequal(scheme, corestring_lwc_https, + &match) == lwc_error_ok && + (match == false))) { + /* Some remote content, not https, therefore insecure */ + lwc_string_unref(scheme); + return PAGE_STATE_INSECURE; + } + + lwc_string_unref(scheme); + + /* Did we have to override this SSL setting? */ + if (urldb_get_cert_permissions(hlcache_handle_get_url(bw->current_content))) { + return PAGE_STATE_SECURE_OVERRIDE; + } + + /* If we've seen insecure content internally then we need to say so */ + if (content_saw_insecure_objects(bw->current_content)) { + return PAGE_STATE_SECURE_ISSUES; + } + + /* All is well, return secure state */ + return PAGE_STATE_SECURE; +} + +/* Exported interface, documented in browser_window.h */ +nserror +browser_window_get_ssl_chain(struct browser_window *bw, + struct cert_chain **chain) +{ + assert(bw != NULL); + + if (bw->current_cert_chain == NULL) { + return NSERROR_NOT_FOUND; + } + + *chain = bw->current_cert_chain; + + return NSERROR_OK; +} + +/* Exported interface, documented in browser_window.h */ +int browser_window_get_cookie_count( + const struct browser_window *bw) +{ + int count = 0; + char *cookies = urldb_get_cookie(browser_window_access_url(bw), true); + if (cookies == NULL) { + return 0; + } + + for (char *c = cookies; *c != '\0'; c++) { + if (*c == ';') + count++; + } + + free(cookies); + + return count; +} + +/* Exported interface, documented in browser_window.h */ +nserror browser_window_show_cookies( + const struct browser_window *bw) +{ + nserror err; + nsurl *url = browser_window_access_url(bw); + lwc_string *host = nsurl_get_component(url, NSURL_HOST); + const char *string = (host != NULL) ? lwc_string_data(host) : NULL; + + err = guit->misc->present_cookies(string); + + if (host != NULL) { + lwc_string_unref(host); + } + return err; +} + +/* Exported interface, documented in browser_window.h */ +nserror browser_window_show_certificates(struct browser_window *bw) +{ + nserror res; + nsurl *url; + + if (bw->current_cert_chain == NULL) { + return NSERROR_NOT_FOUND; + } + + res = cert_chain_to_query(bw->current_cert_chain, &url); + if (res == NSERROR_OK) { + res = browser_window_create(BW_CREATE_HISTORY | + BW_CREATE_FOREGROUND | + BW_CREATE_TAB, + url, + NULL, + bw, + NULL); + + nsurl_unref(url); + } + + return res; +} diff --git a/desktop/cookie_manager.c b/desktop/cookie_manager.c index a2aab8e9f..b5ec89618 100644 --- a/desktop/cookie_manager.c +++ b/desktop/cookie_manager.c @@ -232,10 +232,6 @@ cookie_manager_field_builder(enum cookie_manager_field field, * * The time should be converted to text in the users locacle * - * \todo This should probably generate the user text using localtime - * and strftime with the c format specifier. Currently ctime will - * always generate output in the C locale. - * * \param field Cookie manager treeview field to build * \param fdata Cookie manager entry field data to set * \param value Time to show in field @@ -246,22 +242,20 @@ cookie_manager_field_builder_time(enum cookie_manager_field field, struct treeview_field_data *fdata, const time_t *value) { - const char *date; - char *date2; + struct tm *ftime; fdata->field = cm_ctx.fields[field].field; - - date = ctime(value); - date2 = strdup(date); - if (date2 == NULL) { - fdata->value = NULL; - fdata->value_len = 0; - } else { - assert(date2[24] == '\n'); - date2[24] = '\0'; - - fdata->value = date2; - fdata->value_len = strlen(date2); + fdata->value = NULL; + fdata->value_len = 0; + + if ((ftime = localtime(value)) != NULL) { + const size_t vsize = 256; + char *value = malloc(vsize); + if (value != NULL) { + fdata->value = value; + fdata->value_len = strftime(value, vsize, + "%a %b %e %H:%M:%S %Y", ftime); + } } return NSERROR_OK; @@ -545,6 +539,19 @@ void cookie_manager_remove(const struct cookie_data *data) } +/* exported interface documented in cookie_manager.h */ +nserror cookie_manager_set_search_string( + const char *string) +{ + /* If we don't have a cookie manager at the moment, just return */ + if (cm_ctx.tree == NULL) { + return NSERROR_NOT_FOUND; + } + + return treeview_set_search_string(cm_ctx.tree, string); +} + + /** * Initialise the treeview entry feilds * diff --git a/desktop/cookie_manager.h b/desktop/cookie_manager.h index 4ae74a25f..11d03252e 100644 --- a/desktop/cookie_manager.h +++ b/desktop/cookie_manager.h @@ -76,6 +76,15 @@ bool cookie_manager_add(const struct cookie_data *data); void cookie_manager_remove(const struct cookie_data *data); /** + * Set the cookie manager search string. + * + * \param string Sering to set as search string. + * \return NSERROR_OK on success, appropriate error otherwise + */ +nserror cookie_manager_set_search_string( + const char *string); + +/** * Redraw the cookies manager. * * \param x X coordinate to render treeview at diff --git a/desktop/download.c b/desktop/download.c index e17855aa1..4931371fb 100644 --- a/desktop/download.c +++ b/desktop/download.c @@ -42,7 +42,7 @@ struct download_context { struct gui_window *parent; /**< Parent window */ lwc_string *mime_type; /**< MIME type of download */ - unsigned long total_length; /**< Length of data, in bytes */ + unsigned long long int total_length; /**< Length of data, in bytes */ char *filename; /**< Suggested filename */ struct gui_download_window *window; /**< GUI download window */ @@ -94,7 +94,7 @@ static nserror download_context_process_headers(download_context *ctx) { const char *http_header; http_content_type *content_type; - unsigned long length; + unsigned long long int length; nserror error; /* Retrieve and parse Content-Type */ @@ -108,10 +108,11 @@ static nserror download_context_process_headers(download_context *ctx) /* Retrieve and parse Content-Length */ http_header = llcache_handle_get_header(ctx->llcache, "Content-Length"); - if (http_header == NULL) + if (http_header == NULL) { length = 0; - else - length = strtoul(http_header, NULL, 10); + } else { + length = strtoull(http_header, NULL, 10); + } /* Retrieve and parse Content-Disposition */ http_header = llcache_handle_get_header(ctx->llcache, @@ -299,7 +300,8 @@ const char *download_context_get_mime_type(const download_context *ctx) } /* See download.h for documentation */ -unsigned long download_context_get_total_length(const download_context *ctx) +unsigned long long int +download_context_get_total_length(const download_context *ctx) { return ctx->total_length; } diff --git a/desktop/download.h b/desktop/download.h index dc2befd90..b704c4c7d 100644 --- a/desktop/download.h +++ b/desktop/download.h @@ -91,7 +91,7 @@ const char *download_context_get_mime_type(const download_context *ctx); * \param ctx Context to retrieve byte length from * \return Total length, in bytes, or 0 if unknown */ -unsigned long download_context_get_total_length(const download_context *ctx); +unsigned long long int download_context_get_total_length(const download_context *ctx); /** * Retrieve the filename for a download diff --git a/desktop/frames.c b/desktop/frames.c index 1ed114540..85f18793e 100644 --- a/desktop/frames.c +++ b/desktop/frames.c @@ -35,6 +35,7 @@ #include "content/hlcache.h" #include "html/html.h" #include "html/box.h" +#include "html/box_inspect.h" #include "desktop/browser_private.h" #include "desktop/frames.h" @@ -68,7 +69,7 @@ void browser_window_scroll_callback(void *client_data, rect.x1 = rect.x0 + bw->width; rect.y1 = rect.y0 + bw->height; - browser_window_update_box(bw, &rect); + browser_window_invalidate_rect(bw, &rect); } break; case SCROLLBAR_MSG_SCROLL_START: @@ -180,20 +181,35 @@ void browser_window_handle_scrollbars(struct browser_window *bw) scrollbar_make_pair(bw->scroll_x, bw->scroll_y); } +/* exported function documented in desktop/frames.h */ +nserror browser_window_invalidate_iframe(struct browser_window *bw) +{ + html_redraw_a_box(bw->parent->current_content, bw->box); + return NSERROR_OK; +} /* exported function documented in desktop/frames.h */ -nserror browser_window_create_iframes(struct browser_window *bw, - struct content_html_iframe *iframe) +nserror browser_window_create_iframes(struct browser_window *bw) { + nserror ret = NSERROR_OK; struct browser_window *window; struct content_html_iframe *cur; struct rect rect; int iframes = 0; int index; - nserror ret = NSERROR_OK; + struct content_html_iframe *iframe; + + bw->iframe_count = 0; + + /* only html contents can have iframes */ + if (content_get_type(bw->current_content) != CONTENT_HTML) { + return NSERROR_OK; + } + /* obtain the iframes for this content */ + iframe = html_get_iframe(bw->current_content); if (iframe == NULL) { - return NSERROR_BAD_PARAMETER; + return NSERROR_OK; } /* Count iframe list and allocate enough space within the @@ -271,12 +287,7 @@ nserror browser_window_create_iframes(struct browser_window *bw, } -/** - * Recalculate iframe positions following a resize. - * - * \param bw The browser window to reposition iframes for - */ - +/* exported function documented in desktop/frames.h */ void browser_window_recalculate_iframes(struct browser_window *bw) { struct browser_window *window; @@ -292,123 +303,23 @@ void browser_window_recalculate_iframes(struct browser_window *bw) } -/* exported interface documented in desktop/frames.h */ -nserror browser_window_create_frameset(struct browser_window *bw, - struct content_html_frames *frameset) +/* exported function documented in desktop/frames.h */ +nserror browser_window_destroy_iframes(struct browser_window *bw) { - int row, col, index; - struct content_html_frames *frame; - struct browser_window *window; - hlcache_handle *parent; - - assert(bw && frameset); - - /* 1. 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 NSERROR_NOMEM; - } - - 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]; - - /* Initialise common parts */ - browser_window_initialise_common(BW_CREATE_NONE, - window, NULL); + int i; - /* window characteristics */ - 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); - if (!window->name) { - free(bw->children); - bw->children = NULL; - return NSERROR_NOMEM; - } + if (bw->iframes != NULL) { + for (i = 0; i < bw->iframe_count; i++) { + if (bw->iframes[i].box != NULL) { + bw->iframes[i].box->iframe = NULL; + bw->iframes[i].box = NULL; } - - window->scale = bw->scale; - - /* linking */ - window->parent = bw; - - if (window->name) - NSLOG(netsurf, INFO, "Created frame '%s'", - window->name); - else - NSLOG(netsurf, INFO, - "Created frame (unnamed)"); - } - } - - /* 2. Calculate dimensions */ - browser_window_update_extent(bw); - browser_window_recalculate_frameset(bw); - - /* 3. Recurse for grandchildren */ - 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->children) - browser_window_create_frameset(window, frame); + browser_window_destroy_internal(&bw->iframes[i]); } + free(bw->iframes); + bw->iframes = NULL; + bw->iframe_count = 0; } - - /* Use the URL of the first ancestor window containing html content - * as the referer */ - for (window = bw; window->parent; window = window->parent) { - if (window->current_content && - content_get_type(window->current_content) == - CONTENT_HTML) - break; - } - - parent = window->current_content; - - /* 4. 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_navigate(window, - frame->url, - hlcache_handle_get_url(parent), - BW_NAVIGATE_HISTORY | - BW_NAVIGATE_UNVERIFIABLE, - NULL, - NULL, - parent); - } - } - } - return NSERROR_OK; } @@ -416,10 +327,9 @@ nserror browser_window_create_frameset(struct browser_window *bw, /** * Recalculate frameset positions following a resize. * - * \param bw The browser window to reposition framesets for + * \param bw The browser window to reposition framesets for */ - -void browser_window_recalculate_frameset(struct browser_window *bw) +static void browser_window_recalculate_frameset_internal(struct browser_window *bw) { int widths[bw->cols][bw->rows]; int heights[bw->cols][bw->rows]; @@ -645,9 +555,177 @@ void browser_window_recalculate_frameset(struct browser_window *bw) x += widths[col][row]; if (window->children) - browser_window_recalculate_frameset(window); + browser_window_recalculate_frameset_internal(window); + } + } +} + + +/** + * Create and open a frameset for a browser window. + * + * \param[in,out] bw The browser window to create the frameset for + * \param[in] frameset The frameset to create + * \return NSERROR_OK or error code on faliure + */ +static nserror +browser_window_create_frameset_internal(struct browser_window *bw, + struct content_html_frames *frameset) +{ + int row, col, index; + struct content_html_frames *frame; + struct browser_window *window; + hlcache_handle *parent; + + assert(bw && frameset); + + /* 1. 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 NSERROR_NOMEM; + } + + 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]; + + /* Initialise common parts */ + browser_window_initialise_common(BW_CREATE_NONE, + window, NULL); + + /* window characteristics */ + 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); + if (!window->name) { + free(bw->children); + bw->children = NULL; + return NSERROR_NOMEM; + } + } + + window->scale = bw->scale; + + /* linking */ + window->parent = bw; + + if (window->name) + NSLOG(netsurf, INFO, "Created frame '%s'", + window->name); + else + NSLOG(netsurf, INFO, + "Created frame (unnamed)"); } } + + /* 2. Calculate dimensions */ + browser_window_update_extent(bw); + browser_window_recalculate_frameset_internal(bw); + + /* 3. Recurse for grandchildren */ + 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->children) + browser_window_create_frameset_internal(window, frame); + } + } + + /* Use the URL of the first ancestor window containing html content + * as the referer */ + for (window = bw; window->parent; window = window->parent) { + if (window->current_content && + content_get_type(window->current_content) == + CONTENT_HTML) + break; + } + + parent = window->current_content; + + /* 4. 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_navigate(window, + frame->url, + hlcache_handle_get_url(parent), + BW_NAVIGATE_HISTORY | + BW_NAVIGATE_UNVERIFIABLE, + NULL, + NULL, + parent); + } + } + } + + return NSERROR_OK; +} + + +/* exported interface documented in desktop/frames.h */ +nserror browser_window_create_frameset(struct browser_window *bw) +{ + struct content_html_frames *frameset; + + if (content_get_type(bw->current_content) != CONTENT_HTML) { + return NSERROR_OK; + } + + frameset = html_get_frameset(bw->current_content); + if (frameset == NULL) { + return NSERROR_OK; + } + + return browser_window_create_frameset_internal(bw, frameset); +} + + + + +/** + * Recalculate frameset positions following a resize. + * + * \param bw The browser window to reposition framesets for + */ + +void browser_window_recalculate_frameset(struct browser_window *bw) +{ + if (content_get_type(bw->current_content) != CONTENT_HTML) { + return; + } + + if (html_get_frameset(bw->current_content) == NULL) { + return; + } + + browser_window_recalculate_frameset_internal(bw); } @@ -702,7 +780,7 @@ void browser_window_resize_frame(struct browser_window *bw, int x, int y) } if (change) { - browser_window_recalculate_frameset(parent); + browser_window_recalculate_frameset_internal(parent); } } diff --git a/desktop/frames.h b/desktop/frames.h index 063e2c558..dda31824b 100644 --- a/desktop/frames.h +++ b/desktop/frames.h @@ -16,12 +16,13 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -/** \file +/** + * \file * Frame and frameset creation and manipulation (interface). */ -#ifndef _NETSURF_DESKTOP_FRAMES_H_ -#define _NETSURF_DESKTOP_FRAMES_H_ +#ifndef NETSURF_DESKTOP_FRAMES_H_ +#define NETSURF_DESKTOP_FRAMES_H_ struct scrollbar_msg_data; struct content_html_iframe; @@ -30,23 +31,40 @@ struct content_html_frames; /** * Create and open iframes for a browser window. * - * \param bw The browser window to create iframes for. - * \param iframe The iframes to create from. + * \param bw The browser window to create iframes for. * \return NSERROR_OK or error code on faliure. */ -nserror browser_window_create_iframes(struct browser_window *bw, - struct content_html_iframe *iframe); +nserror browser_window_create_iframes(struct browser_window *bw); + +/** + * Recalculate iframe positions following a resize. + * + * \param bw The browser window to reposition iframes for + */ void browser_window_recalculate_iframes(struct browser_window *bw); /** + * Invalidate an iframe causing a redraw. + * + * \param bw The browser window to invalidate + */ +nserror browser_window_invalidate_iframe(struct browser_window *bw); + +/** + * Destroy iframes opened in browser_window_create_iframes() + * + * \param bw The browser window to destroy iframes for. + * \return NSERROR_OK + */ +nserror browser_window_destroy_iframes(struct browser_window *bw); + +/** * Create and open a frameset for a browser window. * * \param[in,out] bw The browser window to create the frameset for - * \param[in] frameset The frameset to create * \return NSERROR_OK or error code on faliure */ -nserror browser_window_create_frameset(struct browser_window *bw, - struct content_html_frames *frameset); +nserror browser_window_create_frameset(struct browser_window *bw); void browser_window_recalculate_frameset(struct browser_window *bw); bool browser_window_frame_resize_start(struct browser_window *bw, diff --git a/desktop/global_history.c b/desktop/global_history.c index ad39a3e41..e98e4cb29 100644 --- a/desktop/global_history.c +++ b/desktop/global_history.c @@ -266,9 +266,9 @@ static nserror global_history_create_treeview_field_data( const char *title = (data->title != NULL) ? data->title : messages_get("NoTitle"); char buffer[16]; - const char *last_visited; - char *last_visited2; - int len; + struct tm *lvtime; + char *last_visited = NULL; + size_t len = 0; e->data[GH_TITLE].field = gh_ctx.fields[GH_TITLE].field; e->data[GH_TITLE].value = strdup(title); @@ -279,16 +279,18 @@ static nserror global_history_create_treeview_field_data( e->data[GH_URL].value = nsurl_access(e->url); e->data[GH_URL].value_len = nsurl_length(e->url); - last_visited = ctime(&data->last_visit); - last_visited2 = strdup(last_visited); - if (last_visited2 != NULL) { - assert(last_visited2[24] == '\n'); - last_visited2[24] = '\0'; + if ((lvtime = localtime(&data->last_visit)) != NULL) { + const size_t lvsize = 256; + last_visited = malloc(lvsize); + if (last_visited != NULL) { + len = strftime(last_visited, lvsize, + "%a %b %e %H:%M:%S %Y", lvtime); + } } e->data[GH_LAST_VISIT].field = gh_ctx.fields[GH_LAST_VISIT].field; - e->data[GH_LAST_VISIT].value = last_visited2; - e->data[GH_LAST_VISIT].value_len = (last_visited2 != NULL) ? 24 : 0; + e->data[GH_LAST_VISIT].value = last_visited; + e->data[GH_LAST_VISIT].value_len = len; len = snprintf(buffer, 16, "%u", data->visits); if (len == 16) { diff --git a/desktop/gui_factory.c b/desktop/gui_factory.c index 7ab7b465a..0d4d9a904 100644 --- a/desktop/gui_factory.c +++ b/desktop/gui_factory.c @@ -19,10 +19,13 @@ #include <stdlib.h> #include <stdint.h> #include <stdbool.h> +#include <string.h> +#include <unistd.h> #include "utils/config.h" #include "utils/errors.h" #include "utils/file.h" +#include "utils/inet.h" #include "netsurf/bitmap.h" #include "content/hlcache.h" #include "content/backing_store.h" @@ -471,6 +474,16 @@ static char *gui_default_mimetype(const char *path) return strdup(guit->fetch->filetype(path)); } +static int gui_default_socket_open(int domain, int type, int protocol) +{ + return (int) socket(domain, type, protocol); +} + +static int gui_default_socket_close(int fd) +{ + return (int) ns_close_socket(fd); +} + /** verify fetch table is valid */ static nserror verify_fetch_register(struct gui_fetch_table *gft) { @@ -497,6 +510,12 @@ static nserror verify_fetch_register(struct gui_fetch_table *gft) if (gft->mimetype == NULL) { gft->mimetype = gui_default_mimetype; } + if (gft->socket_open == NULL) { + gft->socket_open = gui_default_socket_open; + } + if (gft->socket_close == NULL) { + gft->socket_close = gui_default_socket_close; + } return NSERROR_OK; } @@ -559,10 +578,6 @@ static nserror verify_bitmap_register(struct gui_bitmap_table *gbt) return NSERROR_BAD_PARAMETER; } - if (gbt->test_opaque == NULL) { - return NSERROR_BAD_PARAMETER; - } - if (gbt->get_buffer == NULL) { return NSERROR_BAD_PARAMETER; } @@ -579,14 +594,6 @@ static nserror verify_bitmap_register(struct gui_bitmap_table *gbt) return NSERROR_BAD_PARAMETER; } - if (gbt->get_bpp == NULL) { - return NSERROR_BAD_PARAMETER; - } - - if (gbt->save == NULL) { - return NSERROR_BAD_PARAMETER; - } - if (gbt->modified == NULL) { return NSERROR_BAD_PARAMETER; } @@ -638,15 +645,6 @@ static nserror gui_default_launch_url(struct nsurl *url) } -static nserror gui_default_cert_verify(nsurl *url, - const struct ssl_cert_info *certs, - unsigned long num, - nserror (*cb)(bool proceed, void *pw), - void *cbpw) -{ - return NSERROR_NOT_IMPLEMENTED; -} - static nserror gui_default_401login_open( nsurl *url, const char *realm, const char *username, const char *password, @@ -666,6 +664,12 @@ gui_default_pdf_password(char **owner_pass, char **user_pass, char *path) save_pdf(path); } +static nserror +gui_default_present_cookies(const char *search_term) +{ + return NSERROR_NOT_IMPLEMENTED; +} + /** verify misc table is valid */ static nserror verify_misc_register(struct gui_misc_table *gmt) { @@ -678,9 +682,6 @@ static nserror verify_misc_register(struct gui_misc_table *gmt) if (gmt->schedule == NULL) { return NSERROR_BAD_PARAMETER; } - if (gmt->warning == NULL) { - return NSERROR_BAD_PARAMETER; - } /* fill in the optional entries with defaults */ if (gmt->quit == NULL) { @@ -689,15 +690,15 @@ static nserror verify_misc_register(struct gui_misc_table *gmt) if (gmt->launch_url == NULL) { gmt->launch_url = gui_default_launch_url; } - if (gmt->cert_verify == NULL) { - gmt->cert_verify = gui_default_cert_verify; - } if (gmt->login == NULL) { gmt->login = gui_default_401login_open; } if (gmt->pdf_password == NULL) { gmt->pdf_password = gui_default_pdf_password; } + if (gmt->present_cookies == NULL) { + gmt->present_cookies = gui_default_present_cookies; + } return NSERROR_OK; } diff --git a/desktop/hotlist.c b/desktop/hotlist.c index 4bdd7c8cb..20c0890a1 100644 --- a/desktop/hotlist.c +++ b/desktop/hotlist.c @@ -193,29 +193,32 @@ static nserror hotlist_create_treeview_field_visits_data( struct hotlist_entry *e, const struct url_data *data) { char buffer[16]; - const char *last_visited; - char *last_visited2; - int len; + char *last_visited = NULL; + size_t len = 0; /* Last visited */ if (data->visits != 0) { - last_visited = ctime(&data->last_visit); - last_visited2 = strdup(last_visited); - len = 24; + const size_t lvsize = 256; + struct tm *lvtime; + + if ((lvtime = localtime(&data->last_visit)) != NULL) { + last_visited = malloc(lvsize); + if (last_visited != NULL) { + len = strftime(last_visited, lvsize, + "%a %b %e %H:%M:%S %Y", + lvtime); + } + } } else { - last_visited2 = strdup("-"); + last_visited = strdup("-"); len = 1; } - if (last_visited2 == NULL) { + if (last_visited == NULL) { return NSERROR_NOMEM; - - } else if (len == 24) { - assert(last_visited2[24] == '\n'); - last_visited2[24] = '\0'; } e->data[HL_LAST_VISIT].field = hl_ctx.fields[HL_LAST_VISIT].field; - e->data[HL_LAST_VISIT].value = last_visited2; + e->data[HL_LAST_VISIT].value = last_visited; e->data[HL_LAST_VISIT].value_len = len; /* Visits */ @@ -890,7 +893,9 @@ static nserror hotlist_load(const char *path, bool *loaded) corestring_lwc_html); if (html == NULL) { dom_node_unref(document); - guit->misc->warning("TreeLoadError", "(<html> not found)"); + NSLOG(netsurf, WARNING, + "%s (<html> not found)", + messages_get("TreeLoadError")); return NSERROR_OK; } @@ -899,7 +904,9 @@ static nserror hotlist_load(const char *path, bool *loaded) if (body == NULL) { dom_node_unref(html); dom_node_unref(document); - guit->misc->warning("TreeLoadError", "(<html>...<body> not found)"); + NSLOG(netsurf, WARNING, + "%s (<html>...<body> not found)", + messages_get("TreeLoadError")); return NSERROR_OK; } @@ -909,8 +916,9 @@ static nserror hotlist_load(const char *path, bool *loaded) dom_node_unref(body); dom_node_unref(html); dom_node_unref(document); - guit->misc->warning("TreeLoadError", - "(<html>...<body>...<ul> not found.)"); + NSLOG(netsurf, WARNING, + "%s (<html>...<body>...<ul> not found.)", + messages_get("TreeLoadError")); return NSERROR_OK; } @@ -934,7 +942,9 @@ static nserror hotlist_load(const char *path, bool *loaded) dom_node_unref(document); if (err != NSERROR_OK) { - guit->misc->warning("TreeLoadError", "(Failed building tree.)"); + NSLOG(netsurf, WARNING, + "%s (Failed building tree.)", + messages_get("TreeLoadError")); return NSERROR_OK; } @@ -961,13 +971,13 @@ static nserror hotlist_generate(void) const char *url; const char *msg_key; } default_entries[] = { - { "http://www.netsurf-browser.org/", + { "https://www.netsurf-browser.org/", "HotlistHomepage" }, - { "http://www.netsurf-browser.org/downloads/", + { "https://www.netsurf-browser.org/downloads/", "HotlistDownloads" }, - { "http://www.netsurf-browser.org/documentation", + { "https://www.netsurf-browser.org/documentation", "HotlistDocumentation" }, - { "http://www.netsurf-browser.org/contact", + { "https://www.netsurf-browser.org/contact", "HotlistContact" } }; const int n_entries = sizeof(default_entries) / @@ -1396,6 +1406,9 @@ nserror hotlist_fini(void) /* Destroy the hotlist treeview */ err = treeview_destroy(hl_ctx.tree); + if (err != NSERROR_OK) { + NSLOG(netsurf, INFO, "Problem destroying the hotlist treeview."); + } hl_ctx.built = false; /* Free hotlist treeview entry fields */ @@ -1616,7 +1629,7 @@ nserror hotlist_add_entry(nsurl *url, const char *title, bool at_y, int y) enum treeview_relationship rel; if (url == NULL) { - err = nsurl_create("http://netsurf-browser.org/", &url); + err = nsurl_create("https://netsurf-browser.org/", &url); if (err != NSERROR_OK) { return err; } diff --git a/desktop/local_history.c b/desktop/local_history.c index aa70ebb42..5227c97e1 100644 --- a/desktop/local_history.c +++ b/desktop/local_history.c @@ -24,8 +24,9 @@ #include <stdlib.h> #include <string.h> -#include "utils/errors.h" #include "utils/nsurl.h" +#include "utils/errors.h" + #include "netsurf/types.h" #include "netsurf/layout.h" #include "netsurf/browser_window.h" @@ -33,11 +34,14 @@ #include "netsurf/plotters.h" #include "netsurf/keypress.h" +#include "utils/nscolour.h" + #include "desktop/cw_helper.h" #include "desktop/gui_internal.h" #include "desktop/system_colour.h" #include "desktop/browser_private.h" #include "desktop/browser_history.h" +#include "desktop/local_history_private.h" #include "desktop/local_history.h" /** @@ -173,17 +177,20 @@ redraw_entry(struct history *history, rect.y0 = entry->y - 1 + y; rect.x1 = entry->x + x + LOCAL_HISTORY_WIDTH; rect.y1 = entry->y + y + LOCAL_HISTORY_HEIGHT; - res = ctx->plot->rectangle(ctx, pstyle, &rect); - if (res != NSERROR_OK) { - return res; - } - /* If this is the cursor, show that */ - if (entry == cursor) { + /* Border */ + if (entry != cursor) { + /* Not cursor position */ + res = ctx->plot->rectangle(ctx, pstyle, &rect); + if (res != NSERROR_OK) { + return res; + } + } else { + /* Cursor position */ rect.x0 -= 1; rect.y0 -= 1; - rect.x1 += 2; - rect.y1 += 2; + rect.x1 += 1; + rect.y1 += 1; ctx->plot->rectangle(ctx, &pstyle_rect_cursor, &rect); } @@ -307,33 +314,19 @@ local_history_init(struct core_window_callback_table *cw_t, struct browser_window *bw, struct local_history_session **session) { - nserror res; struct local_history_session *nses; - res = ns_system_colour_char("Window", &pstyle_bg.fill_colour); - if (res != NSERROR_OK) { - return res; - } - pfstyle_node.background = pstyle_bg.fill_colour; - pfstyle_node_sel.background = pstyle_bg.fill_colour; + pstyle_bg.fill_colour = nscolours[NSCOLOUR_WIN_EVEN_BG]; + pstyle_line.stroke_colour = nscolours[NSCOLOUR_WIN_EVEN_BORDER]; - res = ns_system_colour_char("GrayText", &pstyle_line.stroke_colour); - if (res != NSERROR_OK) { - return res; - } pstyle_rect.stroke_colour = pstyle_line.stroke_colour; - pfstyle_node.foreground = pstyle_line.stroke_colour; - - res = ns_system_colour_char("ButtonText", &pstyle_rect_sel.stroke_colour); - if (res != NSERROR_OK) { - return res; - } - pfstyle_node_sel.foreground = pstyle_rect_sel.stroke_colour; + pstyle_rect_sel.stroke_colour = nscolours[NSCOLOUR_WIN_EVEN_BORDER]; + pstyle_rect_cursor.stroke_colour = nscolours[NSCOLOUR_SEL_BG]; - res = ns_system_colour_char("Highlight", &pstyle_rect_cursor.stroke_colour); - if (res != NSERROR_OK) { - return res; - } + pfstyle_node.foreground = nscolours[NSCOLOUR_WIN_EVEN_FG]; + pfstyle_node.background = nscolours[NSCOLOUR_WIN_EVEN_BG]; + pfstyle_node_sel.foreground = nscolours[NSCOLOUR_WIN_EVEN_FG]; + pfstyle_node_sel.background = nscolours[NSCOLOUR_WIN_EVEN_BG]; nses = calloc(1, sizeof(struct local_history_session)); if (nses == NULL) { diff --git a/desktop/local_history_private.h b/desktop/local_history_private.h new file mode 100644 index 000000000..fd25ab4d3 --- /dev/null +++ b/desktop/local_history_private.h @@ -0,0 +1,38 @@ +/* + * Copyright 2006 James Bursa <bursa@users.sourceforge.net> + * + * This file is part of NetSurf, http://www.netsurf-browser.org/ + * + * NetSurf is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * NetSurf is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +/** + * \file + * Interface to browser history private operations + */ + +#ifndef NETSURF_DESKTOP_BROWSER_HISTORY_PRIVATE_H +#define NETSURF_DESKTOP_BROWSER_HISTORY_PRIVATE_H + +#include "content/handlers/css/utils.h" + +#define LOCAL_HISTORY_WIDTH \ + (FIXTOINT(css_unit_css2device_px(INTTOFIX(116), nscss_screen_dpi))) +#define LOCAL_HISTORY_HEIGHT \ + (FIXTOINT(css_unit_css2device_px(INTTOFIX(100), nscss_screen_dpi))) +#define LOCAL_HISTORY_RIGHT_MARGIN \ + (FIXTOINT(css_unit_css2device_px(INTTOFIX( 50), nscss_screen_dpi))) +#define LOCAL_HISTORY_BOTTOM_MARGIN \ + (FIXTOINT(css_unit_css2device_px(INTTOFIX( 30), nscss_screen_dpi))) + +#endif diff --git a/desktop/netsurf.c b/desktop/netsurf.c index 0928442dc..bd785898f 100644 --- a/desktop/netsurf.c +++ b/desktop/netsurf.c @@ -24,15 +24,19 @@ #include <stdio.h> #include <stdlib.h> #include <libwapcaplet/libwapcaplet.h> +#include <dom/dom.h> #include "netsurf/inttypes.h" #include "utils/config.h" +#include "utils/errors.h" +#include "utils/nscolour.h" #include "utils/nsoption.h" #include "utils/corestrings.h" #include "utils/log.h" #include "utils/string.h" #include "utils/utf8.h" #include "utils/messages.h" +#include "utils/useragent.h" #include "content/content_factory.h" #include "content/fetchers.h" #include "content/hlcache.h" @@ -47,6 +51,7 @@ #include "netsurf/browser_window.h" #include "desktop/system_colour.h" +#include "desktop/page-info.h" #include "desktop/searchweb.h" #include "netsurf/misc.h" #include "desktop/gui_internal.h" @@ -94,7 +99,7 @@ static void netsurf_lwc_iterator(lwc_string *str, void *pw) { - NSLOG(netsurf, WARNING, "[%3u] %.*s", str->refcnt, + NSLOG(netsurf, WARNING, "[%3"PRIu32"] %.*s", str->refcnt, (int)lwc_string_length(str), lwc_string_data(str)); } @@ -132,6 +137,10 @@ nserror netsurf_init(const char *store_path) if (ret != NSERROR_OK) return ret; + ret = nscolour_update(); + if (ret != NSERROR_OK) + return ret; + /* set up cache limits based on the memory cache size option */ hlcache_parameters.llcache.limit = nsoption_int(memory_cache_size); @@ -146,10 +155,10 @@ nserror netsurf_init(const char *store_path) hlcache_parameters.llcache.fetch_attempts = nsoption_uint(max_retried_fetches); /* image cache is 25% of total memory cache size */ - image_cache_parameters.limit = (hlcache_parameters.llcache.limit * 25) / 100; + image_cache_parameters.limit = hlcache_parameters.llcache.limit / 4; /* image cache hysteresis is 20% of the image cache size */ - image_cache_parameters.hysteresis = (image_cache_parameters.limit * 20) / 100; + image_cache_parameters.hysteresis = image_cache_parameters.limit / 5; /* account for image cache use from total */ hlcache_parameters.llcache.limit -= image_cache_parameters.limit; @@ -158,10 +167,13 @@ nserror netsurf_init(const char *store_path) hlcache_parameters.llcache.store.limit = nsoption_uint(disc_cache_size); /* set backing store hysterissi to 20% */ - hlcache_parameters.llcache.store.hysteresis = (hlcache_parameters.llcache.store.limit * 20) / 100;; + hlcache_parameters.llcache.store.hysteresis = hlcache_parameters.llcache.store.limit / 5; /* set the path to the backing store */ - hlcache_parameters.llcache.store.path = store_path; + hlcache_parameters.llcache.store.path = + nsoption_charp(disc_cache_path) ? + nsoption_charp(disc_cache_path) : + store_path; /* image handler bitmap cache */ ret = image_cache_init(&image_cache_parameters); @@ -204,6 +216,11 @@ nserror netsurf_init(const char *store_path) js_initialise(); + ret = page_info_init(); + if (ret != NSERROR_OK) { + return ret; + } + return NSERROR_OK; } @@ -218,7 +235,10 @@ void netsurf_exit(void) NSLOG(netsurf, INFO, "Closing GUI"); guit->misc->quit(); - + + NSLOG(netsurf, INFO, "Finalising page-info module"); + page_info_fini(); + NSLOG(netsurf, INFO, "Finalising JavaScript"); js_finalise(); @@ -230,6 +250,8 @@ void netsurf_exit(void) NSLOG(netsurf, INFO, "Closing fetches"); fetcher_quit(); + /* Now the fetchers are done, our user-agent string can go */ + free_user_agent_string(); /* dump any remaining cache entries */ image_cache_fini(); @@ -250,6 +272,9 @@ void netsurf_exit(void) messages_destroy(); corestrings_fini(); + if (dom_namespace_finalise() != DOM_NO_ERR) { + NSLOG(netsurf, WARNING, "Unable to finalise DOM namespace strings"); + } NSLOG(netsurf, INFO, "Remaining lwc strings:"); lwc_iterate_strings(netsurf_lwc_iterator, NULL); diff --git a/desktop/options.h b/desktop/options.h index 9ae2b5ebb..b74fab829 100644 --- a/desktop/options.h +++ b/desktop/options.h @@ -16,7 +16,8 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -/** \file +/** + * \file * Option available on all platforms * * Non-platform specific options can be added by editing this file @@ -27,8 +28,8 @@ * with different macro definitions so there is no guard */ -#ifndef _NETSURF_DESKTOP_OPTIONS_H_ -#define _NETSURF_DESKTOP_OPTIONS_H_ +#ifndef NETSURF_DESKTOP_OPTIONS_H_ +#define NETSURF_DESKTOP_OPTIONS_H_ #include "netsurf/types.h" @@ -91,6 +92,9 @@ NSOPTION_STRING(accept_charset, NULL) /** Preferred maximum size of memory cache / bytes. */ NSOPTION_INTEGER(memory_cache_size, 12 * 1024 * 1024) +/** Preferred location of disc cache, or NULL for system provided location */ +NSOPTION_STRING(disc_cache_path, NULL) + /** Preferred expiry size of disc cache / bytes. */ NSOPTION_UINT(disc_cache_size, 1024 * 1024 * 1024) @@ -104,9 +108,6 @@ NSOPTION_BOOL(block_advertisements, false) * http://www.w3.org/Submission/2011/SUBM-web-tracking-protection-20110224/#dnt-uas */ NSOPTION_BOOL(do_not_track, false) -/** Minimum GIF animation delay */ -NSOPTION_INTEGER(minimum_gif_delay, 10) - /** Whether to send the referer HTTP header */ NSOPTION_BOOL(send_referer, true) @@ -122,6 +123,9 @@ NSOPTION_BOOL(animate_images, true) /** Whether to execute javascript */ NSOPTION_BOOL(enable_javascript, false) +/** Whether to allow Author level CSS. */ +NSOPTION_BOOL(author_level_css, true) + /** Maximum time (in seconds) to wait for a script to run */ NSOPTION_INTEGER(script_timeout, 10) @@ -167,12 +171,6 @@ NSOPTION_INTEGER(window_width, 0) /** default height of new windows */ NSOPTION_INTEGER(window_height, 0) -/** width of screen when above options were saved */ -NSOPTION_INTEGER(window_screen_width, 0) - -/** height of screen when above options were saved */ -NSOPTION_INTEGER(window_screen_height, 0) - /** default size of status bar vs. h scroll bar */ NSOPTION_INTEGER(toolbar_status_size, 6667) @@ -260,6 +258,9 @@ NSOPTION_BOOL(enable_PDF_compression, true) /** setting a password and encoding PDF documents */ NSOPTION_BOOL(enable_PDF_password, false) +/** whether to prefer dark mode (light on dark) */ +NSOPTION_BOOL(prefer_dark_mode, false) + /******** System colours ********/ NSOPTION_COLOUR(sys_colour_ActiveBorder, 0x00d3d3d3) NSOPTION_COLOUR(sys_colour_ActiveCaption, 0x00f1f1f1) diff --git a/desktop/page-info.c b/desktop/page-info.c new file mode 100644 index 000000000..0991d071b --- /dev/null +++ b/desktop/page-info.c @@ -0,0 +1,836 @@ +/* + * Copyright 2020 Michael Drake <tlsa@netsurf-browser.org> + * + * This file is part of NetSurf, http://www.netsurf-browser.org/ + * + * NetSurf is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * NetSurf is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +/** + * \file + * Pave info viewer window implementation + */ + +#include <stdlib.h> +#include <string.h> + +#include "css/utils.h" + +#include "utils/nsurl.h" +#include "utils/nscolour.h" + +#include "netsurf/mouse.h" +#include "netsurf/layout.h" +#include "netsurf/keypress.h" +#include "netsurf/plotters.h" +#include "netsurf/core_window.h" +#include "netsurf/browser_window.h" + +#include "desktop/knockout.h" +#include "desktop/page-info.h" +#include "desktop/gui_internal.h" +#include "desktop/system_colour.h" + +/** + * Plot style for heading font. + */ +static plot_font_style_t pi__heading[PAGE_STATE__COUNT] = { + [PAGE_STATE_UNKNOWN] = { + .family = PLOT_FONT_FAMILY_SANS_SERIF, + .size = 14 * PLOT_STYLE_SCALE, + .flags = FONTF_NONE, + .weight = 400, + }, + [PAGE_STATE_INTERNAL] = { + .family = PLOT_FONT_FAMILY_SANS_SERIF, + .size = 14 * PLOT_STYLE_SCALE, + .flags = FONTF_NONE, + .weight = 400, + }, + [PAGE_STATE_LOCAL] = { + .family = PLOT_FONT_FAMILY_SANS_SERIF, + .size = 14 * PLOT_STYLE_SCALE, + .flags = FONTF_NONE, + .weight = 400, + }, + [PAGE_STATE_INSECURE] = { + .family = PLOT_FONT_FAMILY_SANS_SERIF, + .size = 14 * PLOT_STYLE_SCALE, + .flags = FONTF_NONE, + .weight = 400, + }, + [PAGE_STATE_SECURE_OVERRIDE] = { + .family = PLOT_FONT_FAMILY_SANS_SERIF, + .size = 14 * PLOT_STYLE_SCALE, + .flags = FONTF_NONE, + .weight = 400, + }, + [PAGE_STATE_SECURE_ISSUES] = { + .family = PLOT_FONT_FAMILY_SANS_SERIF, + .size = 14 * PLOT_STYLE_SCALE, + .flags = FONTF_NONE, + .weight = 400, + }, + [PAGE_STATE_SECURE] = { + .family = PLOT_FONT_FAMILY_SANS_SERIF, + .size = 14 * PLOT_STYLE_SCALE, + .flags = FONTF_NONE, + .weight = 400, + }, +}; + +/** + * Plot style for domain font. + */ +static plot_font_style_t pi__domain = { + .family = PLOT_FONT_FAMILY_SANS_SERIF, + .size = 8 * PLOT_STYLE_SCALE, + .flags = FONTF_NONE, + .weight = 700, +}; + +/** + * Plot style for item font. + */ +static plot_font_style_t pi__item = { + .family = PLOT_FONT_FAMILY_SANS_SERIF, + .size = 11 * PLOT_STYLE_SCALE, + .flags = FONTF_NONE, + .weight = 400, +}; + +/** + * Plot style for item detail font. + */ +static plot_font_style_t pi__item_detail = { + .family = PLOT_FONT_FAMILY_SANS_SERIF, + .size = 11 * PLOT_STYLE_SCALE, + .flags = FONTF_NONE, + .weight = 400, +}; + +/** + * Plot style for window background. + */ +static plot_style_t pi__bg = { + .fill_type = PLOT_OP_TYPE_SOLID, +}; + +/** + * Plot style for hover background. + */ +static plot_style_t pi__hover = { + .fill_type = PLOT_OP_TYPE_SOLID, +}; + +/** + * An "text" type page info entry. + */ +struct page_info_text { + const char *text; + const plot_font_style_t *style; + int width; + int height; + int padding_bottom; +}; + +/** + * An "item" type page info entry. + */ +struct page_info_item { + struct page_info_text item; + struct page_info_text detail; + const plot_style_t *hover_bg; + int padding_bottom; + int padding_top; + bool hover; +}; + +/** + * List of page info window entries. + */ +enum pi_entry { + PI_ENTRY_HEADER, + PI_ENTRY_DOMAIN, + PI_ENTRY_CERT, + PI_ENTRY_COOKIES, + PI_ENTRY__COUNT, +}; + +/** + * An entry on a page info window. + */ +struct page_info_entry { + /** + * List of page info entry types. + */ + enum page_info_entry_type { + PAGE_INFO_ENTRY_TYPE_TEXT, + PAGE_INFO_ENTRY_TYPE_ITEM, + } type; + /** + * Type-specific page info entry data. + */ + union { + struct page_info_text text; + struct page_info_item item; + } u; +}; + +/** + * The default page info window data. + */ +struct page_info_entry pi__entries[PI_ENTRY__COUNT] = { + [PI_ENTRY_HEADER] = { + .type = PAGE_INFO_ENTRY_TYPE_TEXT, + }, + [PI_ENTRY_DOMAIN] = { + .type = PAGE_INFO_ENTRY_TYPE_TEXT, + .u = { + .text = { + .style = &pi__domain, + }, + }, + }, + [PI_ENTRY_CERT] = { + .type = PAGE_INFO_ENTRY_TYPE_ITEM, + .u = { + .item = { + .item = { + .style = &pi__item, + }, + .detail = { + .style = &pi__item_detail, + }, + .hover_bg = &pi__hover, + }, + }, + }, + [PI_ENTRY_COOKIES] = { + .type = PAGE_INFO_ENTRY_TYPE_ITEM, + .u = { + .item = { + .item = { + .style = &pi__item, + }, + .detail = { + .style = &pi__item_detail, + }, + .hover_bg = &pi__hover, + }, + }, + }, +}; + +/** + * The page info window structure. + */ +struct page_info { + const struct core_window_callback_table *cw_t; + struct core_window *cw_h; + + struct browser_window *bw; + lwc_string *domain; + enum nsurl_scheme_type scheme; + + browser_window_page_info_state state; + unsigned cookies; + + char cookie_text[64]; + struct page_info_entry entries[PI_ENTRY__COUNT]; + + int width; + int height; + + int window_padding; +}; + +/* Exported interface documented in desktop/page_info.h */ +nserror page_info_init(void) +{ + pi__bg.fill_colour = nscolours[NSCOLOUR_WIN_EVEN_BG]; + pi__hover.fill_colour = nscolours[NSCOLOUR_WIN_EVEN_BG_HOVER]; + + pi__domain.background = nscolours[NSCOLOUR_WIN_EVEN_BG]; + pi__domain.foreground = nscolours[NSCOLOUR_WIN_EVEN_FG]; + + pi__item.background = nscolours[NSCOLOUR_WIN_EVEN_BG]; + pi__item.foreground = nscolours[NSCOLOUR_WIN_EVEN_FG]; + + pi__item_detail.background = nscolours[NSCOLOUR_WIN_EVEN_BG]; + pi__item_detail.foreground = nscolours[NSCOLOUR_WIN_EVEN_FG_FADED]; + + pi__heading[PAGE_STATE_UNKNOWN].background = nscolours[NSCOLOUR_WIN_EVEN_BG]; + pi__heading[PAGE_STATE_UNKNOWN].foreground = nscolours[NSCOLOUR_WIN_EVEN_FG_BAD]; + pi__heading[PAGE_STATE_INTERNAL].background = nscolours[NSCOLOUR_WIN_EVEN_BG]; + pi__heading[PAGE_STATE_INTERNAL].foreground = nscolours[NSCOLOUR_WIN_EVEN_FG]; + pi__heading[PAGE_STATE_LOCAL].background = nscolours[NSCOLOUR_WIN_EVEN_BG]; + pi__heading[PAGE_STATE_LOCAL].foreground = nscolours[NSCOLOUR_WIN_EVEN_FG]; + pi__heading[PAGE_STATE_INSECURE].background = nscolours[NSCOLOUR_WIN_EVEN_BG]; + pi__heading[PAGE_STATE_INSECURE].foreground = nscolours[NSCOLOUR_WIN_EVEN_FG_BAD]; + pi__heading[PAGE_STATE_SECURE_OVERRIDE].background = nscolours[NSCOLOUR_WIN_EVEN_BG]; + pi__heading[PAGE_STATE_SECURE_OVERRIDE].foreground = nscolours[NSCOLOUR_WIN_EVEN_FG_BAD]; + pi__heading[PAGE_STATE_SECURE_ISSUES].background = nscolours[NSCOLOUR_WIN_EVEN_BG]; + pi__heading[PAGE_STATE_SECURE_ISSUES].foreground = nscolours[NSCOLOUR_WIN_EVEN_FG_BAD]; + pi__heading[PAGE_STATE_SECURE].background = nscolours[NSCOLOUR_WIN_EVEN_BG]; + pi__heading[PAGE_STATE_SECURE].foreground = nscolours[NSCOLOUR_WIN_EVEN_FG_GOOD]; + + return NSERROR_OK; +} + +/* Exported interface documented in desktop/page_info.h */ +nserror page_info_fini(void) +{ + return NSERROR_OK; +} + +/** + * Measure the text in the page_info window. + * + * \param[in] pi The page info window handle. + * \return NSERROR_OK on success, appropriate error code otherwise. + */ +static nserror page_info__measure_text_entry( + struct page_info_text *pit) +{ + nserror err; + int height_px; + + err = guit->layout->width(pit->style, + pit->text, strlen(pit->text), + &pit->width); + if (err != NSERROR_OK) { + return err; + } + + /* \todo: This needs to be a helper in plot style or in nscss. */ + height_px = ((pit->style->size / PLOT_STYLE_SCALE) * + FIXTOINT(nscss_screen_dpi) + 36) / 72; + + pit->height = (height_px * 8 + 3) / 6; + + return NSERROR_OK; +} + +/** + * Measure the text in the page_info window. + * + * \param[in] pi The page info window handle. + * \return NSERROR_OK on success, appropriate error code otherwise. + */ +static nserror page_info__measure_text( + struct page_info *pi) +{ + nserror err; + + for (unsigned i = 0; i < PI_ENTRY__COUNT; i++) { + struct page_info_entry *entry = pi->entries + i; + int padding; + + switch (entry->type) { + case PAGE_INFO_ENTRY_TYPE_TEXT: + err = page_info__measure_text_entry( + &entry->u.text); + if (err != NSERROR_OK) { + return err; + } + if (i == PI_ENTRY_DOMAIN) { + entry->u.text.padding_bottom = + entry->u.text.height * 3 / 2; + } + break; + + case PAGE_INFO_ENTRY_TYPE_ITEM: + err = page_info__measure_text_entry( + &entry->u.item.item); + if (err != NSERROR_OK) { + return err; + } + err = page_info__measure_text_entry( + &entry->u.item.detail); + if (err != NSERROR_OK) { + return err; + } + padding = entry->u.item.item.height / 4; + entry->u.item.padding_top = padding; + entry->u.item.padding_bottom = padding; + + break; + } + } + + pi->window_padding = pi->entries[PI_ENTRY_DOMAIN] + .u.item.item.height / 2; + + return NSERROR_OK; +} + +/** + * Set the text for the page_info window. + * + * \todo Use messages for internationalisation. + * + * \param[in] pi The page info window handle. + * \return NSERROR_OK on success, appropriate error code otherwise. + */ +static nserror page_info__set_text( + struct page_info *pi) +{ + int printed; + static const char *header[PAGE_STATE__COUNT] = { + [PAGE_STATE_UNKNOWN] = "Provenance unknown", + [PAGE_STATE_INTERNAL] = "NetSurf data", + [PAGE_STATE_LOCAL] = "Local data", + [PAGE_STATE_INSECURE] = "Connection not secure", + [PAGE_STATE_SECURE_OVERRIDE] = "Connection not secure", + [PAGE_STATE_SECURE_ISSUES] = "Connection not secure", + [PAGE_STATE_SECURE] = "Connection is secure", + }; + static const char *certificate[PAGE_STATE__COUNT] = { + [PAGE_STATE_UNKNOWN] = "Missing", + [PAGE_STATE_INTERNAL] = "None", + [PAGE_STATE_LOCAL] = "None", + [PAGE_STATE_INSECURE] = "Not valid", + [PAGE_STATE_SECURE_OVERRIDE] = "Not valid", + [PAGE_STATE_SECURE_ISSUES] = "Not valid", + [PAGE_STATE_SECURE] = "Valid", + }; + + assert(pi != NULL); + assert(pi->state < PAGE_STATE__COUNT); + + pi->entries[PI_ENTRY_HEADER].u.text.style = &pi__heading[pi->state]; + pi->entries[PI_ENTRY_HEADER].u.text.text = header[pi->state]; + pi->entries[PI_ENTRY_DOMAIN].u.text.text = (pi->domain) ? + lwc_string_data(pi->domain) : "<No domain>"; + + pi->entries[PI_ENTRY_CERT].u.item.item.text = "Certificate: "; + pi->entries[PI_ENTRY_CERT].u.item.detail.text = certificate[pi->state]; + + printed = snprintf(pi->cookie_text, sizeof(pi->cookie_text), + "(%u in use)", pi->cookies); + if (printed < 0) { + return NSERROR_UNKNOWN; + + } else if ((unsigned) printed >= sizeof(pi->cookie_text)) { + return NSERROR_NOSPACE; + } + pi->entries[PI_ENTRY_COOKIES].u.item.item.text = "Cookies: "; + pi->entries[PI_ENTRY_COOKIES].u.item.detail.text = pi->cookie_text; + + return page_info__measure_text(pi); +} + +/** + * Create page info from a browser window. + * + * \param[in] pi The page info window handle. + * \param[in] bw Browser window to show page info for. + * \return NSERROR_OK on success, appropriate error code otherwise. + */ +static nserror page_info__create_from_bw( + struct page_info *pi, + struct browser_window *bw) +{ + nsurl *url = browser_window_access_url(bw); + + pi->bw = bw; + pi->state = browser_window_get_page_info_state(bw); + pi->cookies = browser_window_get_cookie_count(bw); + pi->domain = nsurl_get_component(url, NSURL_HOST); + pi->scheme = nsurl_get_scheme_type(url); + + return page_info__set_text(pi); +} + +/** + * Check whether an entry is relevant. + * + * \param[in] entry The page info entry to consider. + * \param[in] scheme URL scheme that the page info is for. + * \return true if the entry should be hidden, otherwise false. + */ +static inline bool page_info__hide_entry( + enum pi_entry entry, + enum nsurl_scheme_type scheme) +{ + switch (entry) { + case PI_ENTRY_CERT: + if (scheme != NSURL_SCHEME_HTTPS) { + return true; + } + break; + case PI_ENTRY_COOKIES: + if (scheme != NSURL_SCHEME_HTTP && + scheme != NSURL_SCHEME_HTTPS) { + return true; + } + break; + default: + break; + } + + return false; +} + +/** + * Lay out the page info window. + * + * \param[in] pi The page info window handle. + * \return NSERROR_OK on success, appropriate error code otherwise. + */ +static nserror page_info__layout( + struct page_info *pi) +{ + int cur_y = 0; + int max_x = 0; + + cur_y += pi->window_padding; + for (unsigned i = 0; i < PI_ENTRY__COUNT; i++) { + struct page_info_entry *entry = pi->entries + i; + + if (page_info__hide_entry(i, pi->scheme)) { + continue; + } + + switch (entry->type) { + case PAGE_INFO_ENTRY_TYPE_TEXT: + cur_y += entry->u.text.height; + if (max_x < entry->u.text.width) { + max_x = entry->u.text.width; + } + cur_y += entry->u.text.padding_bottom; + break; + + case PAGE_INFO_ENTRY_TYPE_ITEM: + { + int full_width = entry->u.item.item.width + + entry->u.item.detail.width; + cur_y += entry->u.item.padding_top; + cur_y += entry->u.item.item.height; + if (max_x < full_width) { + max_x = full_width; + } + cur_y += entry->u.item.padding_bottom; + } + break; + } + } + cur_y += pi->window_padding; + max_x += pi->window_padding * 2; + + pi->width = max_x; + pi->height = cur_y; + return pi->cw_t->update_size(pi->cw_h, max_x, cur_y); +} + +/* Exported interface documented in desktop/page_info.h */ +nserror page_info_create( + const struct core_window_callback_table *cw_t, + struct core_window *cw_h, + struct browser_window *bw, + struct page_info **pi_out) +{ + struct page_info *pi; + nserror err; + + pi = calloc(1, sizeof(*pi)); + if (pi == NULL) { + return NSERROR_NOMEM; + } + + pi->cw_t = cw_t; + pi->cw_h = cw_h; + + memcpy(pi->entries, pi__entries, sizeof(pi__entries)); + + err = page_info__create_from_bw(pi, bw); + if (err != NSERROR_OK) { + page_info_destroy(pi); + return err; + } + + err = page_info__layout(pi); + if (err != NSERROR_OK) { + page_info_destroy(pi); + return err; + } + + *pi_out = pi; + return NSERROR_OK; +} + +/* Exported interface documented in desktop/page_info.h */ +nserror page_info_destroy(struct page_info *pi) +{ + if (pi->domain != NULL) { + lwc_string_unref(pi->domain); + } + free(pi); + return NSERROR_OK; +} + +/* Exported interface documented in desktop/page_info.h */ +nserror page_info_set(struct page_info *pgi, struct browser_window *bw) +{ + nserror res; + + if (pgi->domain != NULL) { + lwc_string_unref(pgi->domain); + } + + res = page_info__create_from_bw(pgi, bw); + if (res == NSERROR_OK) { + res = page_info__layout(pgi); + } + + return res; +} + +/** + * Render a text entry. + * + * \param[in] pit The page info window handle. + * \param[in] x X-coordinate to plot at. + * \param[in] y Y-coordinate to plot at. + * \param[in] ctx Current redraw context. + * \return NSERROR_OK on success, appropriate error code otherwise. + */ +static nserror page_info__redraw_text_entry( + const struct page_info_text *pit, + int x, + int y, + const struct redraw_context *ctx) +{ + int baseline = (pit->height * 3 + 2) / 4; + + ctx->plot->text(ctx, pit->style, x, y + baseline, + pit->text, strlen(pit->text)); + + return NSERROR_OK; +} + +/* Exported interface documented in desktop/page_info.h */ +nserror page_info_redraw( + const struct page_info *pi, + int x, + int y, + const struct rect *clip, + const struct redraw_context *ctx) +{ + struct redraw_context new_ctx = *ctx; + struct rect r = { + .x0 = clip->x0 + x, + .y0 = clip->y0 + y, + .x1 = clip->x1 + x, + .y1 = clip->y1 + y, + }; + int cur_y = y; + nserror err; + + /* Start knockout rendering if it's available for this plotter. */ + if (ctx->plot->option_knockout) { + bool res = knockout_plot_start(ctx, &new_ctx); + if (res == false) { + return NSERROR_UNKNOWN; + } + } + + /* Set up clip rectangle and draw background. */ + new_ctx.plot->clip(&new_ctx, &r); + new_ctx.plot->rectangle(&new_ctx, &pi__bg, &r); + + cur_y += pi->window_padding; + for (unsigned i = 0; i < PI_ENTRY__COUNT; i++) { + const struct page_info_entry *entry = pi->entries + i; + int cur_x = x + pi->window_padding; + + if (page_info__hide_entry(i, pi->scheme)) { + continue; + } + + switch (entry->type) { + case PAGE_INFO_ENTRY_TYPE_TEXT: + err = page_info__redraw_text_entry( + &entry->u.text, + cur_x, cur_y, + &new_ctx); + if (err != NSERROR_OK) { + goto cleanup; + } + cur_y += entry->u.text.height; + cur_y += entry->u.text.padding_bottom; + break; + + case PAGE_INFO_ENTRY_TYPE_ITEM: + if (entry->u.item.hover) { + r.y0 = cur_y; + r.y1 = cur_y + entry->u.item.padding_top + + entry->u.item.item.height + + entry->u.item.padding_bottom; + new_ctx.plot->rectangle(&new_ctx, + &pi__hover, &r); + } + cur_y += entry->u.item.padding_top; + err = page_info__redraw_text_entry( + &entry->u.item.item, + cur_x, cur_y, + &new_ctx); + if (err != NSERROR_OK) { + goto cleanup; + } + cur_x += entry->u.item.item.width; + err = page_info__redraw_text_entry( + &entry->u.item.detail, + cur_x, cur_y, + &new_ctx); + if (err != NSERROR_OK) { + goto cleanup; + } + cur_y += entry->u.item.item.height; + cur_y += entry->u.item.padding_bottom; + break; + } + } + +cleanup: + /* Rendering complete */ + if (ctx->plot->option_knockout) { + bool res = knockout_plot_end(ctx); + if (res == false) { + return NSERROR_UNKNOWN; + } + } + + return NSERROR_OK; +} + +/** + * Handle any clicks on an item. + * + * \param[in] pi The page info window handle. + * \param[in] mouse The current mouse state. + * \param[in] clicked The page info window entry to consider clicks on. + * \param[out] did_something Set to true if this click did something + * \return NSERROR_OK on success, appropriate error code otherwise. + */ +static nserror page_info__handle_item_click( + struct page_info *pi, + enum browser_mouse_state mouse, + enum pi_entry clicked, + bool *did_something) +{ + nserror err; + + if (!(mouse & BROWSER_MOUSE_CLICK_1)) { + return NSERROR_OK; + } + + switch (clicked) { + case PI_ENTRY_CERT: + err = browser_window_show_certificates(pi->bw); + *did_something = true; + break; + case PI_ENTRY_COOKIES: + err = browser_window_show_cookies(pi->bw); + *did_something = true; + break; + default: + err = NSERROR_OK; + break; + } + + return err; +} + +/* Exported interface documented in desktop/page_info.h */ +nserror page_info_mouse_action( + struct page_info *pi, + enum browser_mouse_state mouse, + int x, + int y, + bool *did_something) +{ + int cur_y = 0; + nserror err; + + cur_y += pi->window_padding; + for (unsigned i = 0; i < PI_ENTRY__COUNT; i++) { + struct page_info_entry *entry = pi->entries + i; + bool hovering = false; + int height; + + if (page_info__hide_entry(i, pi->scheme)) { + continue; + } + + switch (entry->type) { + case PAGE_INFO_ENTRY_TYPE_TEXT: + cur_y += entry->u.text.height; + cur_y += entry->u.text.padding_bottom; + break; + + case PAGE_INFO_ENTRY_TYPE_ITEM: + height = entry->u.item.padding_top + + entry->u.item.item.height + + entry->u.item.padding_bottom; + + if (y >= cur_y && y < cur_y + height) { + hovering = true; + err = page_info__handle_item_click( + pi, mouse, i, did_something); + if (err != NSERROR_OK) { + return err; + } + } + if (entry->u.item.hover != hovering) { + int w, h; + struct rect r = { + .x0 = 0, + .y0 = cur_y, + .y1 = cur_y + height, + }; + pi->cw_t->get_window_dimensions( + pi->cw_h, &w, &h); + r.x1 = (pi->width > w) ? pi->width : w; + + pi->cw_t->invalidate(pi->cw_h, &r); + } + entry->u.item.hover = hovering; + cur_y += height; + break; + } + } + + return NSERROR_OK; +} + +/* Exported interface documented in desktop/page_info.h */ +bool page_info_keypress( + struct page_info *pi, + int32_t key) +{ + return NSERROR_OK; +} + +/* Exported interface documented in desktop/page_info.h */ +nserror page_info_get_size( + struct page_info *pi, + int *width, + int *height) +{ + *width = pi->width; + *height = pi->height; + + return NSERROR_OK; +} diff --git a/desktop/page-info.h b/desktop/page-info.h new file mode 100644 index 000000000..152a88496 --- /dev/null +++ b/desktop/page-info.h @@ -0,0 +1,151 @@ +/* + * Copyright 2020 Michael Drake <tlsa@netsurf-browser.org> + * + * This file is part of NetSurf, http://www.netsurf-browser.org/ + * + * NetSurf is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * NetSurf is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +/** + * \file + * Pave info viewer window interface + */ + +#ifndef NETSURF_DESKTOP_PAGE_INFO_H +#define NETSURF_DESKTOP_PAGE_INFO_H + +#include <stdint.h> +#include <stdbool.h> + +#include "utils/errors.h" +#include "netsurf/mouse.h" + +struct rect; +struct nsurl; +struct page_info; +struct core_window; +struct browser_window; +struct redraw_context; +struct core_window_callback_table; + +/** + * Initialise the page_info module. + * + * \return NSERROR_OK on success, appropriate error code otherwise. + */ +nserror page_info_init(void); + +/** + * Finalise the page_info module. + * + * \return NSERROR_OK on success, appropriate error code otherwise. + */ +nserror page_info_fini(void); + +/** + * Create a page info corewindow. + * + * The page info window is opened for a particular browser window. + * It can be destroyed before the browser window is destroyed by calling + * \ref page_info_destroy. + * + * \param[in] cw_t Callback table for the containing core_window. + * \param[in] cw_h Handle for the containing core_window. + * \param[in] bw Browser window to show page info for. + * \param[out] pi_out The created page info window handle. + * \return NSERROR_OK on success, appropriate error code otherwise. + */ +nserror page_info_create( + const struct core_window_callback_table *cw_t, + struct core_window *cw_h, + struct browser_window *bw, + struct page_info **pi_out); + +/** + * Destroy a page info corewindow. + * + * \param[in] pi The page info window handle. + */ +nserror page_info_destroy(struct page_info *pi); + +/** + * change the browser window the page information refers to + * + * \param[in] pgi The page info window context + * \param[in] bw The new browser window + * \return NSERROR_OK on sucess else error code. + */ +nserror page_info_set(struct page_info *pgi, struct browser_window *bw); + +/** + * Redraw the page info window. + * + * Causes the page info window to issue plot operations to redraw + * the specified area of the viewport. + * + * \param[in] pi The page info window handle. + * \param[in] x X coordinate to render page_info at. + * \param[in] y Y coordinate to render page_info at. + * \param[in] clip Current clip rectangle. + * \param[in] ctx Current redraw context. + * \return NSERROR_OK on success, appropriate error code otherwise. + */ +nserror page_info_redraw( + const struct page_info *pi, + int x, + int y, + const struct rect *clip, + const struct redraw_context *ctx); + +/** + * Mouse action handling. + * + * \param[in] pi The page info window handle. + * \param[in] mouse The current mouse state + * \param[in] x The current mouse X coordinate + * \param[in] y The current mouse Y coordinate + * \param[out] did_something Set to true if this resulted in some action + * \return NSERROR_OK on success, appropriate error code otherwise. + */ +nserror page_info_mouse_action( + struct page_info *pi, + enum browser_mouse_state mouse, + int x, + int y, + bool *did_something); + +/** + * Key press handling. + * + * \param[in] pi The page info window handle. + * \param[in] key The ucs4 character codepoint. + * \return true if the keypress is dealt with, false otherwise. + */ +bool page_info_keypress( + struct page_info *pi, + int32_t key); + +/** + * Get size of page info content area. + * + * \param[in] pi The page info window handle. + * \param[out] width On success, return the page info content width. + * \param[out] height On success, return the page info content height. + * \return NSERROR_OK on success, appropriate error code otherwise. + */ +nserror page_info_get_size( + struct page_info *pi, + int *width, + int *height); + +#endif diff --git a/desktop/print.c b/desktop/print.c index de579dcf2..e90e322ac 100644 --- a/desktop/print.c +++ b/desktop/print.c @@ -257,9 +257,9 @@ struct print_settings *print_make_settings(print_configuration configuration, struct print_settings *settings; css_fixed length = 0; css_unit unit = CSS_UNIT_MM; - nscss_len_ctx len_ctx = { - .vw = DEFAULT_PAGE_WIDTH, - .vh = DEFAULT_PAGE_HEIGHT, + css_unit_ctx unit_len_ctx = { + .viewport_width = DEFAULT_PAGE_WIDTH, + .viewport_height = DEFAULT_PAGE_HEIGHT, .root_style = NULL, }; @@ -277,17 +277,17 @@ struct print_settings *print_make_settings(print_configuration configuration, settings->scale = DEFAULT_EXPORT_SCALE; length = INTTOFIX(DEFAULT_MARGIN_LEFT_MM); - settings->margins[MARGINLEFT] = nscss_len2px( - &len_ctx, length, unit, NULL); + settings->margins[MARGINLEFT] = css_unit_len2device_px( + NULL, &unit_len_ctx, length, unit); length = INTTOFIX(DEFAULT_MARGIN_RIGHT_MM); - settings->margins[MARGINRIGHT] = nscss_len2px( - &len_ctx, length, unit, NULL); + settings->margins[MARGINRIGHT] = css_unit_len2device_px( + NULL, &unit_len_ctx, length, unit); length = INTTOFIX(DEFAULT_MARGIN_TOP_MM); - settings->margins[MARGINTOP] = nscss_len2px( - &len_ctx, length, unit, NULL); + settings->margins[MARGINTOP] = css_unit_len2device_px( + NULL, &unit_len_ctx, length, unit); length = INTTOFIX(DEFAULT_MARGIN_BOTTOM_MM); - settings->margins[MARGINBOTTOM] = nscss_len2px( - &len_ctx, length, unit, NULL); + settings->margins[MARGINBOTTOM] = css_unit_len2device_px( + NULL, &unit_len_ctx, length, unit); break; /* use settings from the Export options tab */ case PRINT_OPTIONS: @@ -303,17 +303,17 @@ struct print_settings *print_make_settings(print_configuration configuration, settings->scale = (float)nsoption_int(export_scale) / 100; length = INTTOFIX(nsoption_int(margin_left)); - settings->margins[MARGINLEFT] = nscss_len2px( - &len_ctx, length, unit, NULL); + settings->margins[MARGINLEFT] = css_unit_len2device_px( + NULL, &unit_len_ctx, length, unit); length = INTTOFIX(nsoption_int(margin_right)); - settings->margins[MARGINRIGHT] = nscss_len2px( - &len_ctx, length, unit, NULL); + settings->margins[MARGINRIGHT] = css_unit_len2device_px( + NULL, &unit_len_ctx, length, unit); length = INTTOFIX(nsoption_int(margin_top)); - settings->margins[MARGINTOP] = nscss_len2px( - &len_ctx, length, unit, NULL); + settings->margins[MARGINTOP] = css_unit_len2device_px( + NULL, &unit_len_ctx, length, unit); length = INTTOFIX(nsoption_int(margin_bottom)); - settings->margins[MARGINBOTTOM] = nscss_len2px( - &len_ctx, length, unit, NULL); + settings->margins[MARGINBOTTOM] = css_unit_len2device_px( + NULL, &unit_len_ctx, length, unit); break; default: return NULL; diff --git a/desktop/save_complete.c b/desktop/save_complete.c index c6f311acb..e4fadd274 100644 --- a/desktop/save_complete.c +++ b/desktop/save_complete.c @@ -28,10 +28,10 @@ #include <string.h> #include <strings.h> #include <sys/types.h> -#include <regex.h> #include <dom/dom.h> #include "utils/config.h" +#include "utils/regex.h" #include "utils/corestrings.h" #include "utils/log.h" #include "utils/nsurl.h" @@ -51,7 +51,7 @@ #include "desktop/gui_internal.h" #include "desktop/save_complete.h" -regex_t save_complete_import_re; +static regex_t save_complete_import_re; /** An entry in save_complete_list. */ typedef struct save_complete_entry { @@ -75,9 +75,8 @@ typedef enum { } save_complete_event_type; -static bool save_complete_save_html(save_complete_ctx *ctx, struct hlcache_handle *c, - bool index); -static bool save_complete_save_imported_sheets(save_complete_ctx *ctx, +static nserror save_complete_save_html(save_complete_ctx *ctx, struct hlcache_handle *c, bool index); +static nserror save_complete_save_imported_sheets(save_complete_ctx *ctx, struct nscss_import *imports, uint32_t import_count); @@ -100,20 +99,22 @@ static void save_complete_ctx_finalise(save_complete_ctx *ctx) } } -static bool save_complete_ctx_add_content(save_complete_ctx *ctx, - struct hlcache_handle *content) +static nserror +save_complete_ctx_add_content(save_complete_ctx *ctx, + struct hlcache_handle *content) { save_complete_entry *entry; entry = malloc(sizeof (*entry)); - if (entry == NULL) - return false; + if (entry == NULL) { + return NSERROR_NOMEM; + } entry->content = content; entry->next = ctx->list; ctx->list = entry; - return true; + return NSERROR_OK; } /** @@ -140,19 +141,22 @@ save_complete_ctx_find_content(save_complete_ctx *ctx, const nsurl *url) } -static bool save_complete_ctx_has_content(save_complete_ctx *ctx, - struct hlcache_handle *content) +static bool +save_complete_ctx_has_content(save_complete_ctx *ctx, + struct hlcache_handle *content) { save_complete_entry *entry; - for (entry = ctx->list; entry != NULL; entry = entry->next) - if (entry->content == content) + for (entry = ctx->list; entry != NULL; entry = entry->next) { + if (hlcache_handle_get_content(entry->content) == + hlcache_handle_get_content(content)) return true; + } return false; } -static bool +static nserror save_complete_save_buffer(save_complete_ctx *ctx, const char *leafname, const uint8_t *data, @@ -165,16 +169,14 @@ save_complete_save_buffer(save_complete_ctx *ctx, ret = netsurf_mkpath(&fname, NULL, 2, ctx->path, leafname); if (ret != NSERROR_OK) { - guit->misc->warning(messages_get_errorcode(ret), 0); - return false; + return ret; } fp = fopen(fname, "wb"); if (fp == NULL) { free(fname); - NSLOG(netsurf, INFO, "fopen(): errno = %i", errno); - guit->misc->warning("SaveError", strerror(errno)); - return false; + NSLOG(netsurf, INFO, "fopen(): %s", strerror(errno)); + return NSERROR_SAVE_FAILED; } fwrite(data, sizeof(*data), data_len, fp); @@ -186,9 +188,38 @@ save_complete_save_buffer(save_complete_ctx *ctx, } free(fname); - return true; + return NSERROR_OK; } + +/** + * perform a posix regexec on a string without a null terminator + */ +static int +snregexec(regex_t *preg, + const char *string, + size_t stringlen, + size_t nmatch, + regmatch_t pmatch[], + int eflags) +{ + char *strbuf; + int matches; + + strbuf = calloc(1, stringlen + 1); + if (strbuf == NULL) { + return -1; + } + memcpy(strbuf, string, stringlen); + + matches = regexec(preg, strbuf, nmatch, pmatch, eflags); + + free(strbuf); + + return matches; +} + + /** * Rewrite stylesheet \@import rules for save complete. * @@ -239,11 +270,14 @@ save_complete_rewrite_stylesheet_urls(save_complete_ctx *ctx, int import_url_len = 0; nsurl *url = NULL; regmatch_t match[11]; - int m = regexec(&save_complete_import_re, - (const char *)source + offset, - 11, - match, - 0); + int m; + + m = snregexec(&save_complete_import_re, + (const char *)source + offset, + size - offset, + 11, + match, + 0); if (m) break; @@ -322,7 +356,7 @@ save_complete_rewrite_stylesheet_urls(save_complete_ctx *ctx, return rewritten; } -static bool +static nserror save_complete_save_stylesheet(save_complete_ctx *ctx, hlcache_handle *css) { const uint8_t *css_data; @@ -333,20 +367,25 @@ save_complete_save_stylesheet(save_complete_ctx *ctx, hlcache_handle *css) uint32_t import_count; lwc_string *type; char filename[32]; - bool result; + nserror result; - if (save_complete_ctx_has_content(ctx, css)) - return true; + if (save_complete_ctx_find_content(ctx, + hlcache_handle_get_url(css)) != NULL) { + return NSERROR_OK; + } - if (save_complete_ctx_add_content(ctx, css) == false) { - guit->misc->warning("NoMemory", 0); - return false; + result = save_complete_ctx_add_content(ctx, css); + if (result != NSERROR_OK) { + return result; } imports = nscss_get_imports(css, &import_count); - if (save_complete_save_imported_sheets(ctx, - imports, import_count) == false) - return false; + result = save_complete_save_imported_sheets(ctx, + imports, + import_count); + if (result != NSERROR_OK) { + return result; + } css_data = content_get_source_data(css, &css_size); source = save_complete_rewrite_stylesheet_urls( @@ -356,14 +395,13 @@ save_complete_save_stylesheet(save_complete_ctx *ctx, hlcache_handle *css) hlcache_handle_get_url(css), &source_len); if (source == NULL) { - guit->misc->warning("NoMemory", 0); - return false; + return NSERROR_NOMEM; } type = content_get_mime_type(css); if (type == NULL) { free(source); - return false; + return NSERROR_NOMEM; } snprintf(filename, sizeof filename, "%p", css); @@ -377,70 +415,84 @@ save_complete_save_stylesheet(save_complete_ctx *ctx, hlcache_handle *css) return result; } -static bool save_complete_save_imported_sheets(save_complete_ctx *ctx, - struct nscss_import *imports, uint32_t import_count) +static nserror +save_complete_save_imported_sheets(save_complete_ctx *ctx, + struct nscss_import *imports, + uint32_t import_count) { + nserror res = NSERROR_OK; uint32_t i; for (i = 0; i < import_count; i++) { /* treat a valid content as a stylesheet to save */ - if ((imports[i].c != NULL) && - (save_complete_save_stylesheet(ctx, imports[i].c) == false)) { - return false; + if (imports[i].c != NULL) { + res = save_complete_save_stylesheet(ctx, imports[i].c); + if (res != NSERROR_OK) { + return res; + } } } - return true; + return res; } -static bool save_complete_save_html_stylesheet(save_complete_ctx *ctx, - struct html_stylesheet *sheet) +static nserror +save_complete_save_html_stylesheet(save_complete_ctx *ctx, + struct html_stylesheet *sheet) { - if (sheet->sheet == NULL) - return true; + if (sheet->sheet == NULL) { + return NSERROR_OK; + } return save_complete_save_stylesheet(ctx, sheet->sheet); } -static bool save_complete_save_html_stylesheets(save_complete_ctx *ctx, - hlcache_handle *c) +static nserror +save_complete_save_html_stylesheets(save_complete_ctx *ctx, + hlcache_handle *c) { struct html_stylesheet *sheets; unsigned int i, count; + nserror res; sheets = html_get_stylesheets(c, &count); for (i = STYLESHEET_START; i != count; i++) { - if (save_complete_save_html_stylesheet(ctx, - &sheets[i]) == false) - return false; + res = save_complete_save_html_stylesheet(ctx, &sheets[i]); + if (res != NSERROR_OK) { + return res; + } } - return true; + return NSERROR_OK; } -static bool +static nserror save_complete_save_html_object(save_complete_ctx *ctx, hlcache_handle *obj) { const uint8_t *obj_data; size_t obj_size; lwc_string *type; - bool result; + nserror result; char filename[32]; - if (content_get_type(obj) == CONTENT_NONE) - return true; + if (content_get_type(obj) == CONTENT_NONE) { + return NSERROR_OK; + } obj_data = content_get_source_data(obj, &obj_size); - if (obj_data == NULL) - return true; + if (obj_data == NULL) { + return NSERROR_OK; + } - if (save_complete_ctx_has_content(ctx, obj)) - return true; + if (save_complete_ctx_find_content(ctx, + hlcache_handle_get_url(obj)) != NULL) { + return NSERROR_OK; + } - if (save_complete_ctx_add_content(ctx, obj) == false) { - guit->misc->warning("NoMemory", 0); - return false; + result = save_complete_ctx_add_content(ctx, obj); + if (result != NSERROR_OK) { + return result; } if (content_get_type(obj) == CONTENT_HTML) { @@ -450,40 +502,46 @@ save_complete_save_html_object(save_complete_ctx *ctx, hlcache_handle *obj) snprintf(filename, sizeof filename, "%p", obj); type = content_get_mime_type(obj); - if (type == NULL) - return false; + if (type == NULL) { + return NSERROR_NOMEM; + } - result = save_complete_save_buffer(ctx, filename, - obj_data, obj_size, type); + result = save_complete_save_buffer(ctx, filename, obj_data, obj_size, type); lwc_string_unref(type); return result; } -static bool save_complete_save_html_objects(save_complete_ctx *ctx, - hlcache_handle *c) +static nserror +save_complete_save_html_objects(save_complete_ctx *ctx, + hlcache_handle *c) { struct content_html_object *object; unsigned int count; + nserror res; object = html_get_objects(c, &count); for (; object != NULL; object = object->next) { - if ((object->content != NULL) && (object->box != NULL)) { - if (save_complete_save_html_object(ctx, - object->content) == false) - return false; + if ((object->content != NULL) && + (object->box != NULL)) { + res = save_complete_save_html_object(ctx, object->content); + if (res != NSERROR_OK) { + return res; + } } } - return true; + return NSERROR_OK; } -static bool save_complete_libdom_treewalk(dom_node *root, - bool (*callback)(dom_node *node, - save_complete_event_type event_type, void *ctx), - void *ctx) +static bool +save_complete_libdom_treewalk(dom_node *root, + bool (*callback)(dom_node *node, + save_complete_event_type event_type, + void *ctx), + void *ctx) { dom_node *node; @@ -658,7 +716,7 @@ static bool save_complete_handle_attr_value(save_complete_ctx *ctx, * 4) background any (except those above) */ /* 1 */ - if (name_len == SLEN("data") && + if (name_len == SLEN("data") && strncasecmp(name_data, "data", name_len) == 0) { if (node_len == SLEN("object") && strncasecmp(node_data, @@ -673,13 +731,13 @@ static bool save_complete_handle_attr_value(save_complete_ctx *ctx, /* 2 */ else if (name_len == SLEN("href") && strncasecmp(name_data, "href", name_len) == 0) { - if ((node_len == SLEN("a") && + if ((node_len == SLEN("a") && strncasecmp(node_data, "a", node_len) == 0) || (node_len == SLEN("area") && - strncasecmp(node_data, "area", + strncasecmp(node_data, "area", node_len) == 0) || - (node_len == SLEN("link") && - strncasecmp(node_data, "link", + (node_len == SLEN("link") && + strncasecmp(node_data, "link", node_len) == 0)) { return save_complete_rewrite_url_value(ctx, value_data, value_len); @@ -687,7 +745,7 @@ static bool save_complete_handle_attr_value(save_complete_ctx *ctx, return save_complete_write_value(ctx, value_data, value_len); } - } + } /* 3 */ else if (name_len == SLEN("src") && strncasecmp(name_data, "src", name_len) == 0) { @@ -724,8 +782,10 @@ static bool save_complete_handle_attr_value(save_complete_ctx *ctx, } } -static bool save_complete_handle_attr(save_complete_ctx *ctx, - dom_string *node_name, dom_attr *attr) +static bool +save_complete_handle_attr(save_complete_ctx *ctx, + dom_string *node_name, + dom_attr *attr) { dom_string *name; const char *name_data; @@ -754,12 +814,13 @@ static bool save_complete_handle_attr(save_complete_ctx *ctx, if (value != NULL) { fputc('=', ctx->fp); - if (save_complete_handle_attr_value(ctx, node_name, + if (save_complete_handle_attr_value(ctx, node_name, name, value) == false) { dom_string_unref(value); dom_string_unref(name); return false; } + dom_string_unref(value); } dom_string_unref(name); @@ -767,8 +828,10 @@ static bool save_complete_handle_attr(save_complete_ctx *ctx, return true; } -static bool save_complete_handle_attrs(save_complete_ctx *ctx, - dom_string *node_name, dom_namednodemap *attrs) +static bool +save_complete_handle_attrs(save_complete_ctx *ctx, + dom_string *node_name, + dom_namednodemap *attrs) { uint32_t length, i; dom_exception error; @@ -798,8 +861,10 @@ static bool save_complete_handle_attrs(save_complete_ctx *ctx, return true; } -static bool save_complete_handle_element(save_complete_ctx *ctx, - dom_node *node, save_complete_event_type event_type) +static bool +save_complete_handle_element(save_complete_ctx *ctx, + dom_node *node, + save_complete_event_type event_type) { dom_string *name; dom_namednodemap *attrs; @@ -820,12 +885,12 @@ static bool save_complete_handle_element(save_complete_ctx *ctx, name_data = dom_string_data(name); name_len = dom_string_byte_length(name); - if (name_len == SLEN("base") && - strncasecmp(name_data, "base", name_len) == 0) { + if ((name_len == SLEN("base")) && + (strncasecmp(name_data, "base", name_len) == 0)) { /* Elide BASE elements from the output */ process = false; - } else if (name_len == SLEN("meta") && - strncasecmp(name_data, "meta", name_len) == 0) { + } else if ((name_len == SLEN("meta")) && + (strncasecmp(name_data, "meta", name_len) == 0)) { /* Don't emit close tags for META elements */ if (event_type == EVENT_LEAVE) { process = false; @@ -833,7 +898,8 @@ static bool save_complete_handle_element(save_complete_ctx *ctx, /* Elide meta charsets */ dom_string *value; error = dom_element_get_attribute(node, - corestring_dom_http_equiv, &value); + corestring_dom_http_equiv, + &value); if (error != DOM_NO_ERR) { dom_string_unref(name); return false; @@ -862,8 +928,8 @@ static bool save_complete_handle_element(save_complete_ctx *ctx, process = false; } } - } else if (event_type == EVENT_LEAVE && - ((name_len == SLEN("link") && + } else if (event_type == EVENT_LEAVE && + ((name_len == SLEN("link") && strncasecmp(name_data, "link", name_len) == 0))) { /* Don't emit close tags for void elements */ process = false; @@ -875,8 +941,9 @@ static bool save_complete_handle_element(save_complete_ctx *ctx, } fputc('<', ctx->fp); - if (event_type == EVENT_LEAVE) + if (event_type == EVENT_LEAVE) { fputc('/', ctx->fp); + } fwrite(name_data, sizeof(*name_data), name_len, ctx->fp); if (event_type == EVENT_ENTER) { @@ -946,8 +1013,10 @@ static bool save_complete_handle_element(save_complete_ctx *ctx, return true; } -static bool save_complete_node_handler(dom_node *node, - save_complete_event_type event_type, void *ctxin) +static bool +save_complete_node_handler(dom_node *node, + save_complete_event_type event_type, + void *ctxin) { save_complete_ctx *ctx = ctxin; dom_node_type type; @@ -989,7 +1058,7 @@ static bool save_complete_node_handler(dom_node *node, if (ret != NSERROR_OK) return false; - fwrite(escaped, sizeof(*escaped), + fwrite(escaped, sizeof(*escaped), strlen(escaped), ctx->fp); free(escaped); @@ -1065,8 +1134,10 @@ static bool save_complete_node_handler(dom_node *node, return true; } -static bool save_complete_save_html_document(save_complete_ctx *ctx, - hlcache_handle *c, bool index) +static nserror +save_complete_save_html_document(save_complete_ctx *ctx, + hlcache_handle *c, + bool index) { nserror ret; FILE *fp; @@ -1083,16 +1154,14 @@ static bool save_complete_save_html_document(save_complete_ctx *ctx, ret = netsurf_mkpath(&fname, NULL, 2, ctx->path, filename); if (ret != NSERROR_OK) { - guit->misc->warning(messages_get_errorcode(ret), NULL); - return false; + return ret; } fp = fopen(fname, "wb"); if (fp == NULL) { free(fname); - NSLOG(netsurf, INFO, "fopen(): errno = %i", errno); - guit->misc->warning("SaveError", strerror(errno)); - return false; + NSLOG(netsurf, INFO, "fopen(): %s", strerror(errno)); + return NSERROR_SAVE_FAILED; } ctx->base = html_get_base_url(c); @@ -1101,26 +1170,27 @@ static bool save_complete_save_html_document(save_complete_ctx *ctx, doc = html_get_document(c); - if (save_complete_libdom_treewalk((dom_node *) doc, - save_complete_node_handler, ctx) == false) { + if (save_complete_libdom_treewalk((dom_node *)doc, + save_complete_node_handler, + ctx) == false) { free(fname); - guit->misc->warning("NoMemory", 0); fclose(fp); - return false; + return NSERROR_NOMEM; } fclose(fp); mime_type = content_get_mime_type(c); if (mime_type != NULL) { - if (ctx->set_type != NULL) + if (ctx->set_type != NULL) { ctx->set_type(fname, mime_type); + } lwc_string_unref(mime_type); } free(fname); - return true; + return NSERROR_OK; } /** @@ -1131,20 +1201,30 @@ static bool save_complete_save_html_document(save_complete_ctx *ctx, * \param index true to save as "index" * \return true on success, false on error and error reported */ -static bool save_complete_save_html(save_complete_ctx *ctx, hlcache_handle *c, - bool index) +static nserror +save_complete_save_html(save_complete_ctx *ctx, + hlcache_handle *c, + bool index) { - if (content_get_type(c) != CONTENT_HTML) - return false; + nserror res; - if (save_complete_ctx_has_content(ctx, c)) - return true; + if (content_get_type(c) != CONTENT_HTML) { + return NSERROR_INVALID; + } - if (save_complete_save_html_stylesheets(ctx, c) == false) - return false; + if (save_complete_ctx_has_content(ctx, c)) { + return NSERROR_OK; + } - if (save_complete_save_html_objects(ctx, c) == false) - return false; + res = save_complete_save_html_stylesheets(ctx, c); + if (res != NSERROR_OK) { + return res; + } + + res = save_complete_save_html_objects(ctx, c); + if (res != NSERROR_OK) { + return res; + } return save_complete_save_html_document(ctx, c, index); } @@ -1154,7 +1234,7 @@ static bool save_complete_save_html(save_complete_ctx *ctx, hlcache_handle *c, * Create the inventory file listing original URLs. */ -static bool save_complete_inventory(save_complete_ctx *ctx) +static nserror save_complete_inventory(save_complete_ctx *ctx) { nserror ret; FILE *fp; @@ -1163,26 +1243,26 @@ static bool save_complete_inventory(save_complete_ctx *ctx) ret = netsurf_mkpath(&fname, NULL, 2, ctx->path, "Inventory"); if (ret != NSERROR_OK) { - return false; + return ret; } fp = fopen(fname, "w"); free(fname); if (fp == NULL) { - NSLOG(netsurf, INFO, "fopen(): errno = %i", errno); - guit->misc->warning("SaveError", strerror(errno)); - return false; + NSLOG(netsurf, INFO, "fopen(): %s", strerror(errno)); + return NSERROR_SAVE_FAILED; } for (entry = ctx->list; entry != NULL; entry = entry->next) { - fprintf(fp, "%p %s\n", entry->content, - nsurl_access(hlcache_handle_get_url( - entry->content))); + fprintf(fp, "%p %s\n", + entry->content, + nsurl_access(hlcache_handle_get_url( + entry->content))); } fclose(fp); - return true; + return NSERROR_OK; } /** @@ -1239,17 +1319,26 @@ void save_complete_init(void) } /* Documented in save_complete.h */ -bool save_complete(hlcache_handle *c, const char *path, - save_complete_set_type_cb set_type) +nserror save_complete_finalise(void) { - bool result; + regfree(&save_complete_import_re); + return NSERROR_OK; +} + +/* Documented in save_complete.h */ +nserror +save_complete(hlcache_handle *c, + const char *path, + save_complete_set_type_cb set_type) +{ + nserror result; save_complete_ctx ctx; save_complete_ctx_initialise(&ctx, path, set_type); - + result = save_complete_save_html(&ctx, c, true); - if (result) { + if (result == NSERROR_OK) { result = save_complete_inventory(&ctx); } @@ -1257,4 +1346,3 @@ bool save_complete(hlcache_handle *c, const char *path, return result; } - diff --git a/desktop/save_complete.h b/desktop/save_complete.h index 3c389a56f..7df89f0b5 100644 --- a/desktop/save_complete.h +++ b/desktop/save_complete.h @@ -21,8 +21,8 @@ * Save HTML document with dependencies (interface). */ -#ifndef _NETSURF_DESKTOP_SAVE_COMPLETE_H_ -#define _NETSURF_DESKTOP_SAVE_COMPLETE_H_ +#ifndef NETSURF_DESKTOP_SAVE_COMPLETE_H_ +#define NETSURF_DESKTOP_SAVE_COMPLETE_H_ #include <stdbool.h> @@ -44,15 +44,21 @@ typedef void (*save_complete_set_type_cb)(const char *path, */ void save_complete_init(void); + +/** + * Finalise save complete module. + */ +nserror save_complete_finalise(void); + /** * Save an HTML page with all dependencies. * * \param c CONTENT_HTML to save * \param path Native path to directory to save in to (must exist) * \param set_type Callback to set type of a file, or NULL - * \return true on success, false on error and error reported + * \return NSERROR_OK on success else error code */ -bool save_complete(struct hlcache_handle *c, const char *path, +nserror save_complete(struct hlcache_handle *c, const char *path, save_complete_set_type_cb set_type); #endif diff --git a/desktop/scrollbar.c b/desktop/scrollbar.c index af5536ba4..90ea924cc 100644 --- a/desktop/scrollbar.c +++ b/desktop/scrollbar.c @@ -29,6 +29,7 @@ #include "utils/log.h" #include "utils/messages.h" #include "utils/utils.h" +#include "utils/nscolour.h" #include "utils/nsoption.h" #include "netsurf/browser_window.h" #include "netsurf/mouse.h" @@ -250,29 +251,17 @@ scrollbar_redraw(struct scrollbar *s, plot_style_t bg_fill_style = { .fill_type = PLOT_OP_TYPE_SOLID, + .fill_colour = nscolours[NSCOLOUR_SCROLL_WELL], }; plot_style_t fg_fill_style = { .fill_type = PLOT_OP_TYPE_SOLID, + .fill_colour = nscolours[NSCOLOUR_BUTTON_BG], }; plot_style_t arrow_fill_style = { .fill_type = PLOT_OP_TYPE_SOLID, + .fill_colour = nscolours[NSCOLOUR_BUTTON_FG], }; - res = ns_system_colour_char("Scrollbar", &bg_fill_style.fill_colour); - if (res != NSERROR_OK) { - return res; - } - - res = ns_system_colour_char("ButtonFace", &fg_fill_style.fill_colour); - if (res != NSERROR_OK) { - return res; - } - - res = ns_system_colour_char("ButtonText", &arrow_fill_style.fill_colour); - if (res != NSERROR_OK) { - return res; - } - area.x0 = x; area.y0 = y; area.x1 = x + (s->horizontal ? s->length : SCROLLBAR_WIDTH) - 1; diff --git a/desktop/scrollbar.h b/desktop/scrollbar.h index fa5e167f2..796520724 100644 --- a/desktop/scrollbar.h +++ b/desktop/scrollbar.h @@ -38,6 +38,7 @@ #define SCROLL_BOTTOM INT_MAX struct scrollbar; +struct redraw_context; /** * scrollbar message types diff --git a/desktop/search.c b/desktop/search.c index 3d3e7704f..e21f520a6 100644 --- a/desktop/search.c +++ b/desktop/search.c @@ -23,7 +23,10 @@ * Free text search (core) */ -#include "content/content.h" +#include <stdbool.h> + +#include "utils/errors.h" +#include "content/textsearch.h" #include "netsurf/types.h" #include "netsurf/browser_window.h" @@ -36,7 +39,7 @@ void browser_window_search(struct browser_window *bw, void *context, { if ((bw != NULL) && (bw->current_content != NULL)) { - content_search(bw->current_content, context, flags, string); + content_textsearch(bw->current_content, context, flags, string); } } @@ -45,6 +48,6 @@ void browser_window_search_clear(struct browser_window *bw) { if ((bw != NULL) && (bw->current_content != NULL)) { - content_search_clear(bw->current_content); + content_textsearch_clear(bw->current_content); } } diff --git a/desktop/search.h b/desktop/search.h index baf382e77..c39d1d8a3 100644 --- a/desktop/search.h +++ b/desktop/search.h @@ -16,11 +16,13 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef _NETSURF_DESKTOP_SEARCH_H_ -#define _NETSURF_DESKTOP_SEARCH_H_ +/** + * \file + * Browseing window text search interface + */ -#include <ctype.h> -#include <string.h> +#ifndef NETSURF_DESKTOP_SEARCH_H_ +#define NETSURF_DESKTOP_SEARCH_H_ struct browser_window; diff --git a/desktop/searchweb.c b/desktop/searchweb.c index 91a8118ca..361860190 100644 --- a/desktop/searchweb.c +++ b/desktop/searchweb.c @@ -22,6 +22,7 @@ */ #include <stdlib.h> +#include <string.h> #include "utils/utils.h" #include "utils/log.h" @@ -52,7 +53,7 @@ static struct search_web_ctx_s { } search_web_ctx; -static const char *default_providers = "Google|www.google.com|http://www.google.com/search?q=%s|http://www.google.com/favicon.ico|\n"; +static const char *default_providers = "Google|www.google.com|https://www.google.com/search?q=%s|https://www.google.com/favicon.ico|\n"; static const char *default_search_icon_url = "resource:icons/search.png"; @@ -332,11 +333,11 @@ search_web_omni(const char *term, } /* try with adding default scheme */ - eterm = malloc(strlen(term) + SLEN("http://") + 1); + eterm = malloc(strlen(term) + SLEN("https://") + 1); if (eterm == NULL) { return NSERROR_NOMEM; } - sprintf(eterm, "http://%s", term); + sprintf(eterm, "https://%s", term); ret = nsurl_create(eterm, &url); free(eterm); if (ret == NSERROR_OK) { @@ -366,6 +367,33 @@ search_web_omni(const char *term, } /* exported interface documented in desktop/searchweb.h */ +nserror search_web_get_provider_bitmap(struct bitmap **bitmap_out) +{ + struct search_provider *provider; + struct bitmap *ico_bitmap = NULL; + + /* must be initialised */ + if (search_web_ctx.providers == NULL) { + return NSERROR_INIT_FAILED; + } + + provider = &search_web_ctx.providers[search_web_ctx.current]; + + /* set the icon now (if we can) at least to the default */ + if (provider->ico_handle != NULL) { + ico_bitmap = content_get_bitmap(provider->ico_handle); + } + if ((ico_bitmap == NULL) && + (search_web_ctx.default_ico_handle != NULL)) { + ico_bitmap = content_get_bitmap(search_web_ctx.default_ico_handle); + } + + *bitmap_out = ico_bitmap; + return NSERROR_OK; +} + + +/* exported interface documented in desktop/searchweb.h */ nserror search_web_select_provider(int selection) { struct search_provider *provider; @@ -520,10 +548,14 @@ nserror search_web_init(const char *provider_fname) } /* get default search icon */ - ret = hlcache_handle_retrieve(icon_nsurl, 0, NULL, NULL, + ret = hlcache_handle_retrieve(icon_nsurl, + 0, + NULL, + NULL, default_ico_callback, &search_web_ctx, - NULL, CONTENT_IMAGE, + NULL, + CONTENT_IMAGE, &search_web_ctx.default_ico_handle); nsurl_unref(icon_nsurl); if (ret != NSERROR_OK) { diff --git a/desktop/searchweb.h b/desktop/searchweb.h index a96e7787b..0712de9fe 100644 --- a/desktop/searchweb.h +++ b/desktop/searchweb.h @@ -62,7 +62,7 @@ enum search_web_omni_flags { * term. The flags allow control over the operation. By default the * operations are: * - interpret the \a term as a url - * - if missing a scheme as a http: url + * - if missing a scheme as a https: url * - combined with the search providers url into a url for that provider. * * \param term The search term. @@ -72,6 +72,17 @@ enum search_web_omni_flags { */ nserror search_web_omni(const char *term, enum search_web_omni_flags flags, struct nsurl **url_out); + +/** + * obtain the current providers bitmap + * + * obtain the icon representing the current web search provider + * + * \param bitmap_out recives the resulting bitmap which may be NULL + * \return NSERROR_OK on success or NSERROR_INIT_FAILED if not initialised + */ +nserror search_web_get_provider_bitmap(struct bitmap **bitmap_out); + /** * Change the currently selected web search provider. * diff --git a/desktop/selection.c b/desktop/selection.c index c8edda7b1..8b1f127c4 100644 --- a/desktop/selection.c +++ b/desktop/selection.c @@ -17,49 +17,25 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -/** \file - * Text selection within browser windows (implementation). - */ +/** + * \file + * implementation of text selection within browser windows. + */ -#include <assert.h> -#include <stdio.h> #include <stdlib.h> -#include <stdbool.h> #include <string.h> -#include <dom/dom.h> -#include "utils/log.h" -#include "utils/utf8.h" -#include "utils/utils.h" -#include "netsurf/form.h" -#include "html/box.h" -#include "html/html_internal.h" -#include "html/font.h" -#include "text/textplain.h" +#include "netsurf/clipboard.h" #include "netsurf/browser_window.h" +#include "netsurf/window.h" +#include "utils/utils.h" +#include "content/content_protected.h" -#include "netsurf/mouse.h" #include "desktop/browser_private.h" -#include "netsurf/plotters.h" -#include "desktop/save_text.h" -#include "desktop/selection.h" -#include "netsurf/clipboard.h" -#include "netsurf/window.h" #include "desktop/gui_internal.h" - -/** - * Text selection works by labelling each node in the box tree with its - * start index in the textual representation of the tree's content. - */ - -#define SPACE_LEN(b) ((b->space == 0) ? 0 : 1) +#include "desktop/selection.h" -struct rdw_info { - bool inited; - struct rect r; -}; - struct selection_string { char *buffer; size_t buffer_len; @@ -70,750 +46,414 @@ struct selection_string { }; -typedef bool (*seln_traverse_handler)(const char *text, size_t length, - struct box *box, const nscss_len_ctx *len_ctx, void *handle, - const char *whitespace_text, size_t whitespace_length); +typedef enum { + DRAG_NONE, + DRAG_START, + DRAG_END +} seln_drag_state; +struct selection { + struct content *c; -static bool redraw_handler(const char *text, size_t length, - struct box *box, const nscss_len_ctx *len_ctx, - void *handle, const char *whitespace_text, - size_t whitespace_length); -static void selection_redraw(struct selection *s, unsigned start_idx, - unsigned end_idx); -static bool selected_part(struct box *box, unsigned start_idx, unsigned end_idx, - unsigned *start_offset, unsigned *end_offset); -static bool traverse_tree(struct box *box, const nscss_len_ctx *len_ctx, - unsigned start_idx, unsigned end_idx, - seln_traverse_handler handler, - void *handle, save_text_whitespace *before, bool *first, - bool do_marker); -static unsigned selection_label_subtree(struct box *box, unsigned idx); + unsigned max_idx; /* total bytes in text representation */ -/** - * Get the browser window containing the content a selection object belongs to. - * - * \param s selection object - * \return the browser window - */ -static struct browser_window * selection_get_browser_window(struct selection *s) -{ - if (s->is_html) - return html_get_browser_window(s->c); - else - return textplain_get_browser_window(s->c); -} + unsigned start_idx; /* offset in bytes within text representation */ + unsigned end_idx; + + bool defined; + seln_drag_state drag_state; +}; /** - * Creates a new selection object associated with a browser window. + * Redraws the given range of text. * - * \return new selection context + * \param s selection object + * \param start_idx start offset (bytes) within the textual representation + * \param end_idx end offset (bytes) within the textual representation */ - -struct selection *selection_create(struct content *c, bool is_html) +static nserror +selection_redraw(struct selection *s, unsigned start_idx, unsigned end_idx) { - struct selection *s = calloc(1, sizeof(struct selection)); - if (s) { - selection_prepare(s, c, is_html); + nserror res; + + if (s->c->handler->textselection_redraw != NULL) { + res = s->c->handler->textselection_redraw(s->c, + start_idx, + end_idx); + } else { + res = NSERROR_NOT_IMPLEMENTED; } - return s; + return res; } + /** - * Prepare a newly created selection object for use. + * Set the start position of the current selection, updating the screen. * - * \param s selection object - * \param c content - * \param is_html true if content is html false if content is textplain + * \param s selection object + * \param offset byte offset within textual representation */ - -void selection_prepare(struct selection *s, struct content *c, bool is_html) +static void selection_set_start(struct selection *s, unsigned offset) { - if (s) { - s->c = c; - s->is_html = is_html; - s->root = NULL; - s->drag_state = DRAG_NONE; - s->max_idx = 0; - selection_clear(s, false); - } -} + bool was_defined; + unsigned old_start; + old_start = s->start_idx; + s->start_idx = offset; -/** - * Destroys a selection object, without updating the - * owning window (caller should call selection_clear() - * first if update is desired) - * - * \param s selection object - */ + was_defined = s->defined; + s->defined = (s->start_idx < s->end_idx); -void selection_destroy(struct selection *s) -{ - if (s != NULL) - free(s); + if (was_defined) { + if (offset < old_start) { + selection_redraw(s, s->start_idx, old_start); + } else { + selection_redraw(s, old_start, s->start_idx); + } + } else if (s->defined) { + selection_redraw(s, s->start_idx, s->end_idx); + } } /** - * Initialise the selection object to use the given box subtree as its root, - * ie. selections are confined to that subtree, whilst maintaining the current - * selection whenever possible because, for example, it's just the page being - * resized causing the layout to change. + * Set the end position of the current selection, updating the screen. * - * \param s selection object - * \param root the root box for html document or NULL for text/plain + * \param s selection object + * \param offset byte offset within textual representation */ - -void selection_reinit(struct selection *s, struct box *root) +static void selection_set_end(struct selection *s, unsigned offset) { - unsigned root_idx; - - assert(s); + bool was_defined; + unsigned old_end; - root_idx = 0; + old_end = s->end_idx; + s->end_idx = offset; - s->root = root; - if (root) { - s->max_idx = selection_label_subtree(root, root_idx); - } else { - if (s->is_html == false) - s->max_idx = textplain_size(s->c); - else - s->max_idx = 0; - } + was_defined = s->defined; + s->defined = (s->start_idx < s->end_idx); - if (s->defined) { - if (s->end_idx > s->max_idx) s->end_idx = s->max_idx; - if (s->start_idx > s->max_idx) s->start_idx = s->max_idx; - s->defined = (s->end_idx > s->start_idx); + if (was_defined) { + if (offset < old_end) { + selection_redraw(s, s->end_idx, old_end); + } else { + selection_redraw(s, old_end, s->end_idx); + } + } else if (s->defined) { + selection_redraw(s, s->start_idx, s->end_idx); } } /** - * Initialise the selection object to use the given box subtree as its root, - * ie. selections are confined to that subtree. + * Traverse the current selection, calling the handler function (with its + * handle) for all boxes that lie (partially) within the given range * - * \param s selection object - * \param root the root box for html document or NULL for text/plain + * \param s The selection context. + * \param handler handler function to call + * \param handle handle to pass + * \return false iff traversal abandoned part-way through */ - -void selection_init( - struct selection *s, - struct box *root, - const nscss_len_ctx *len_ctx) +static bool +selection_copy(struct selection *s, struct selection_string *selstr) { - if (s->defined) - selection_clear(s, true); + nserror res; - s->defined = false; - s->start_idx = 0; - s->end_idx = 0; - s->drag_state = DRAG_NONE; - if (len_ctx != NULL) { - s->len_ctx = *len_ctx; + if (s->c->handler->textselection_copy != NULL) { + res = s->c->handler->textselection_copy(s->c, + s->start_idx, + s->end_idx, + selstr); } else { - s->len_ctx.vw = 0; - s->len_ctx.vh = 0; - s->len_ctx.root_style = NULL; + res = NSERROR_NOT_IMPLEMENTED; } - selection_reinit(s, root); -} - - -/** - * Label each text box in the given box subtree with its position - * in a textual representation of the content. - * - * \param box The box at root of subtree - * \param idx current position within textual representation - * \return updated position - */ - -unsigned selection_label_subtree(struct box *box, unsigned idx) -{ - struct box *child = box->children; - - box->byte_offset = idx; - - if (box->text) - idx += box->length + SPACE_LEN(box); - - while (child) { - if (child->list_marker) - idx = selection_label_subtree(child->list_marker, idx); - - idx = selection_label_subtree(child, idx); - child = child->next; + if (res != NSERROR_OK) { + return false; } - - return idx; + return true; } /** - * Handles mouse clicks (including drag starts) in or near a selection - * - * \param s selection object - * \param mouse state of mouse buttons and modifier keys - * \param idx byte offset within textual representation + * Append text to selection string. * - * \return true iff the click has been handled by the selection code + * \param text text to be added + * \param length length of text in bytes + * \param space indicates whether a trailing space should be appended + * \param style The font style to use. + * \param sel_string string to append to, may be resized + * \return true iff successful */ - -bool selection_click(struct selection *s, browser_mouse_state mouse, - unsigned idx) +bool +selection_string_append(const char *text, + size_t length, + bool space, + plot_font_style_t *style, + struct selection_string *sel_string) { - browser_mouse_state modkeys = - (mouse & (BROWSER_MOUSE_MOD_1 | BROWSER_MOUSE_MOD_2)); - int pos = -1; /* 0 = inside selection, 1 = after it */ - struct browser_window *top = selection_get_browser_window(s); - top = browser_window_get_root(top); - - if (selection_defined(s)) { - if (idx > s->start_idx) { - if (idx <= s->end_idx) - pos = 0; - else - pos = 1; - } - } - - if (!pos && - ((mouse & BROWSER_MOUSE_DRAG_1) || - (modkeys && (mouse & BROWSER_MOUSE_DRAG_2)))) { - /* drag-saving selection */ - char *sel = selection_get_copy(s); - guit->window->drag_save_selection(top->window, sel); - free(sel); - } - else if (!modkeys) { - if (pos && (mouse & BROWSER_MOUSE_PRESS_1)) { - /* Clear the selection if mouse is pressed outside the - * selection, Otherwise clear on release (to allow for drags) */ - - selection_clear(s, true); - } else if (mouse & BROWSER_MOUSE_DRAG_1) { - /* start new selection drag */ + size_t new_length = sel_string->length + length + (space ? 1 : 0) + 1; - selection_clear(s, true); - - selection_set_start(s, idx); - selection_set_end(s, idx); + if (style != NULL) { + /* Add text run style */ + nsclipboard_styles *new_styles; - s->drag_state = DRAG_END; + if (sel_string->n_styles == 0) { + assert(sel_string->length == 0); + } - guit->window->event(top->window, GW_EVENT_START_SELECTION); + new_styles = realloc(sel_string->styles, + (sel_string->n_styles + 1) * + sizeof(nsclipboard_styles)); + if (new_styles == NULL) { + return false; } - else if (mouse & BROWSER_MOUSE_DRAG_2) { - /* adjust selection, but only if there is one */ - if (!selection_defined(s)) - return false; /* ignore Adjust drags */ + sel_string->styles = new_styles; - if (pos >= 0) { - selection_set_end(s, idx); + sel_string->styles[sel_string->n_styles].style = *style; + sel_string->styles[sel_string->n_styles].start = + sel_string->length; - s->drag_state = DRAG_END; - } - else { - selection_set_start(s, idx); + sel_string->n_styles++; + } - s->drag_state = DRAG_START; - } + if (new_length > sel_string->buffer_len) { + /* Need to extend buffer */ + size_t new_alloc = new_length + (new_length / 4); + char *new_buff; - guit->window->event(top->window, GW_EVENT_START_SELECTION); + new_buff = realloc(sel_string->buffer, new_alloc); + if (new_buff == NULL) { + return false; } - else if (mouse & BROWSER_MOUSE_CLICK_2) { - - /* ignore Adjust clicks when there's no selection */ - if (!selection_defined(s)) - return false; - if (pos >= 0) - selection_set_end(s, idx); - else - selection_set_start(s, idx); - s->drag_state = DRAG_NONE; - } - else - return false; - } - else { - /* not our problem */ - return false; + sel_string->buffer = new_buff; + sel_string->buffer_len = new_alloc; } - /* this mouse click is selection-related */ - return true; -} - - -/** - * Handles movements related to the selection, eg. dragging of start and - * end points. - * - * \param s selection object - * \param mouse state of mouse buttons and modifier keys - * \param idx byte offset within text representation - */ + /* Copy text onto end of existing text in buffer */ + memcpy(sel_string->buffer + sel_string->length, text, length); + sel_string->length += length; -void selection_track(struct selection *s, browser_mouse_state mouse, - unsigned idx) -{ - if (!mouse) { - s->drag_state = DRAG_NONE; + if (space) { + sel_string->buffer[sel_string->length++] = ' '; } - switch (s->drag_state) { - - case DRAG_START: - if (idx > s->end_idx) { - unsigned old_end = s->end_idx; - selection_set_end(s, idx); - selection_set_start(s, old_end); - s->drag_state = DRAG_END; - } - else - selection_set_start(s, idx); - break; - - case DRAG_END: - if (idx < s->start_idx) { - unsigned old_start = s->start_idx; - selection_set_start(s, idx); - selection_set_end(s, old_start); - s->drag_state = DRAG_START; - } - else - selection_set_end(s, idx); - break; + /* Ensure NULL termination */ + sel_string->buffer[sel_string->length] = '\0'; - default: - break; - } + return true; } -/** - * Tests whether a text box lies partially within the given range of - * byte offsets, returning the start and end indexes of the bytes - * that are enclosed. - * - * \param box box to be tested - * \param start_idx byte offset of start of range - * \param end_idx byte offset of end of range - * \param start_offset receives the start offset of the selected part - * \param end_offset receives the end offset of the selected part - * \return true iff the range encloses at least part of the box - */ - -bool selected_part(struct box *box, unsigned start_idx, unsigned end_idx, - unsigned *start_offset, unsigned *end_offset) +/* exported interface documented in desktop/selection.h */ +struct selection *selection_create(struct content *c) { - size_t box_length = box->length + SPACE_LEN(box); - - if (box_length > 0) { - if (box->byte_offset >= start_idx && - box->byte_offset + box_length <= end_idx) { - - /* fully enclosed */ - *start_offset = 0; - *end_offset = box_length; - return true; - } - else if (box->byte_offset + box_length > start_idx && - box->byte_offset < end_idx) { - /* partly enclosed */ - int offset = 0; - int len; - - if (box->byte_offset < start_idx) - offset = start_idx - box->byte_offset; - - len = box_length - offset; - - if (box->byte_offset + box_length > end_idx) - len = end_idx - (box->byte_offset + offset); - - *start_offset = offset; - *end_offset = offset + len; - - return true; - } + struct selection *sel; + sel = calloc(1, sizeof(struct selection)); + if (sel) { + sel->c = c; + sel->drag_state = DRAG_NONE; + sel->max_idx = 0; + selection_clear(sel, false); } - return false; + + return sel; } -/** - * Traverse the given box subtree, calling the handler function (with its handle) - * for all boxes that lie (partially) within the given range - * - * \param box box subtree - * \param len_ctx Length conversion context. - * \param start_idx start of range within textual representation (bytes) - * \param end_idx end of range - * \param handler handler function to call - * \param handle handle to pass - * \param before type of whitespace to place before next encountered text - * \param first whether this is the first box with text - * \param do_marker whether deal enter any marker box - * \return false iff traversal abandoned part-way through - */ - -bool traverse_tree( - struct box *box, const nscss_len_ctx *len_ctx, - unsigned start_idx, unsigned end_idx, - seln_traverse_handler handler, - void *handle, save_text_whitespace *before, bool *first, - bool do_marker) +/* exported interface documented in desktop/selection.h */ +void selection_destroy(struct selection *s) { - struct box *child; - const char *whitespace_text = ""; - size_t whitespace_length = 0; - - assert(box); - - /* If selection starts inside marker */ - if (box->parent && box->parent->list_marker == box && !do_marker) { - /* set box to main list element */ - box = box->parent; + if (s == NULL) { + return; } - /* If box has a list marker */ - if (box->list_marker) { - /* do the marker box before continuing with the rest of the - * list element */ - if (!traverse_tree(box->list_marker, len_ctx, - start_idx, end_idx, handler, handle, - before, first, true)) - return false; - } + selection_clear(s, true); + free(s); +} - /* we can prune this subtree, it's after the selection */ - if (box->byte_offset >= end_idx) - return true; - /* read before calling the handler in case it modifies the tree */ - child = box->children; +/* exported interface documented in desktop/selection.h */ +void selection_reinit(struct selection *s) +{ + s->max_idx = 0; - /* If nicely formatted output of the selected text is required, work - * out what whitespace should be placed before the next bit of text */ - if (before) { - save_text_solve_whitespace(box, first, before, &whitespace_text, - &whitespace_length); - } - else { - whitespace_text = NULL; - } - if (box->type != BOX_BR && - !((box->type == BOX_FLOAT_LEFT || - box->type == BOX_FLOAT_RIGHT) && - !box->text)) { - unsigned start_offset; - unsigned end_offset; - - if (selected_part(box, start_idx, end_idx, &start_offset, - &end_offset)) { - if (!handler(box->text + start_offset, min(box->length, - end_offset) - start_offset, - box, len_ctx, handle, whitespace_text, - whitespace_length)) - return false; - if (before) { - *first = false; - *before = WHITESPACE_NONE; - } - } + if (s->c->handler->textselection_get_end != NULL) { + s->c->handler->textselection_get_end(s->c, &s->max_idx); } - /* find the first child that could lie partially within the selection; - * this is important at the top-levels of the tree for pruning subtrees - * that lie entirely before the selection */ - - if (child) { - struct box *next = child->next; - - while (next && next->byte_offset < start_idx) { - child = next; - next = child->next; + if (s->defined) { + if (s->end_idx > s->max_idx) { + s->end_idx = s->max_idx; } - - while (child) { - /* read before calling the handler in case it modifies - * the tree */ - struct box *next = child->next; - - if (!traverse_tree(child, len_ctx, start_idx, end_idx, - handler, handle, before, first, false)) - return false; - - child = next; + if (s->start_idx > s->max_idx) { + s->start_idx = s->max_idx; } + s->defined = (s->end_idx > s->start_idx); } - - return true; } -/** - * Traverse the current selection, calling the handler function (with its - * handle) for all boxes that lie (partially) within the given range - * - * \param s The selection context. - * \param handler handler function to call - * \param handle handle to pass - * \return false iff traversal abandoned part-way through - */ - -static bool selection_traverse(struct selection *s, - seln_traverse_handler handler, void *handle) +/* exported interface documented in desktop/selection.h */ +void selection_init(struct selection *s) { - save_text_whitespace before = WHITESPACE_NONE; - bool first = true; - const char *text; - size_t length; - - if (!selection_defined(s)) - return true; /* easy case, nothing to do */ - - if (s->root) { - /* HTML */ - return traverse_tree(s->root, &s->len_ctx, - s->start_idx, s->end_idx, - handler, handle, - &before, &first, false); + if (s->defined) { + selection_clear(s, true); } - /* Text */ - text = textplain_get_raw_data(s->c, s->start_idx, s->end_idx, &length); - - if (text && !handler(text, length, NULL, NULL, handle, NULL, 0)) - return false; + s->defined = false; + s->start_idx = 0; + s->end_idx = 0; + s->drag_state = DRAG_NONE; - return true; + selection_reinit(s); } -/** - * Selection traversal handler for redrawing the screen when the selection - * has been altered. - * - * \param text pointer to text string - * \param length length of text to be appended (bytes) - * \param box pointer to text box being (partially) added - * \param handle unused handle, we don't need one - * \param whitespace_text whitespace to place before text for formatting - * may be NULL - * \param whitespace_length length of whitespace_text - * \return true iff successful and traversal should continue - */ - -bool redraw_handler(const char *text, size_t length, struct box *box, - const nscss_len_ctx *len_ctx, void *handle, - const char *whitespace_text, size_t whitespace_length) +/* exported interface documented in desktop/selection.h */ +bool +selection_click(struct selection *s, + struct browser_window *top, + browser_mouse_state mouse, + unsigned idx) { - if (box) { - struct rdw_info *r = (struct rdw_info*)handle; - int width, height; - int x, y; - plot_font_style_t fstyle; - - font_plot_style_from_css(len_ctx, box->style, &fstyle); - - /* \todo - it should be possible to reduce the redrawn area by - * considering the 'text', 'length' and 'space' parameters */ - box_coords(box, &x, &y); + browser_mouse_state modkeys; + int pos = -1; /* 0 = inside selection, 1 = after it */ - width = box->padding[LEFT] + box->width + box->padding[RIGHT]; - height = box->padding[TOP] + box->height + box->padding[BOTTOM]; + modkeys = (mouse & (BROWSER_MOUSE_MOD_1 | BROWSER_MOUSE_MOD_2)); - if (box->type == BOX_TEXT && box->space != 0) - width += box->space; + top = browser_window_get_root(top); - if (r->inited) { - if (x < r->r.x0) r->r.x0 = x; - if (y < r->r.y0) r->r.y0 = y; - if (x + width > r->r.x1) r->r.x1 = x + width; - if (y + height > r->r.y1) r->r.y1 = y + height; - } - else { - r->inited = true; - r->r.x0 = x; - r->r.y0 = y; - r->r.x1 = x + width; - r->r.y1 = y + height; + if (s->defined) { + if (idx > s->start_idx) { + if (idx <= s->end_idx) { + pos = 0; + } else { + pos = 1; + } } } - return true; -} + if (!pos && + ((mouse & BROWSER_MOUSE_DRAG_1) || + (modkeys && (mouse & BROWSER_MOUSE_DRAG_2)))) { + /* drag-saving selection */ + char *sel = selection_get_copy(s); + guit->window->drag_save_selection(top->window, sel); + free(sel); + } else if (!modkeys) { + if (pos && (mouse & BROWSER_MOUSE_PRESS_1)) { + /* Clear the selection if mouse is pressed + * outside the selection, Otherwise clear on + * release (to allow for drags) + */ -/** - * Redraws the given range of text. - * - * \param s selection object - * \param start_idx start offset (bytes) within the textual representation - * \param end_idx end offset (bytes) within the textual representation - */ + selection_clear(s, true); -void selection_redraw(struct selection *s, unsigned start_idx, unsigned end_idx) -{ - struct rdw_info rdw; + } else if (mouse & BROWSER_MOUSE_DRAG_1) { + /* start new selection drag */ - assert(end_idx >= start_idx); - rdw.inited = false; + selection_clear(s, true); - if (s->root) { - if (!traverse_tree(s->root, &s->len_ctx, start_idx, end_idx, - redraw_handler, &rdw, - NULL, NULL, false)) - return; - } - else { - if (s->is_html == false && end_idx > start_idx) { - textplain_coords_from_range(s->c, start_idx, - end_idx, &rdw.r); - rdw.inited = true; - } - } + selection_set_start(s, idx); + selection_set_end(s, idx); - if (rdw.inited) - content__request_redraw(s->c, rdw.r.x0, rdw.r.y0, - rdw.r.x1 - rdw.r.x0, rdw.r.y1 - rdw.r.y0); -} + s->drag_state = DRAG_END; + guit->window->event(top->window, + GW_EVENT_START_SELECTION); -/** - * Append text to selection string. - * - * \param text text to be added - * \param length length of text in bytes - * \param space indicates whether a trailing space should be appended - * \param style The font style to use. - * \param sel_string string to append to, may be resized - * \return true iff successful - */ + } else if (mouse & BROWSER_MOUSE_DRAG_2) { -static bool selection_string_append(const char *text, size_t length, bool space, - plot_font_style_t *style, struct selection_string *sel_string) -{ - size_t new_length = sel_string->length + length + (space ? 1 : 0) + 1; + /* adjust selection, but only if there is one */ + if (!s->defined) { + return false; /* ignore Adjust drags */ + } - if (style != NULL) { - /* Add text run style */ - nsclipboard_styles *new_styles; + if (pos >= 0) { + selection_set_end(s, idx); - if (sel_string->n_styles == 0) - assert(sel_string->length == 0); + s->drag_state = DRAG_END; + } else { + selection_set_start(s, idx); - new_styles = realloc(sel_string->styles, - (sel_string->n_styles + 1) * - sizeof(nsclipboard_styles)); - if (new_styles == NULL) - return false; + s->drag_state = DRAG_START; + } - sel_string->styles = new_styles; + guit->window->event(top->window, + GW_EVENT_START_SELECTION); - sel_string->styles[sel_string->n_styles].style = *style; - sel_string->styles[sel_string->n_styles].start = - sel_string->length; + } else if (mouse & BROWSER_MOUSE_CLICK_2) { - sel_string->n_styles++; - } + /* ignore Adjust clicks when there's no selection */ + if (!s->defined) { + return false; + } - if (new_length > sel_string->buffer_len) { - /* Need to extend buffer */ - size_t new_alloc = new_length + (new_length / 4); - char *new_buff; + if (pos >= 0) { + selection_set_end(s, idx); + } else { + selection_set_start(s, idx); + } + s->drag_state = DRAG_NONE; - new_buff = realloc(sel_string->buffer, new_alloc); - if (new_buff == NULL) + } else { return false; + } - sel_string->buffer = new_buff; - sel_string->buffer_len = new_alloc; + } else { + /* not our problem */ + return false; } - /* Copy text onto end of existing text in buffer */ - memcpy(sel_string->buffer + sel_string->length, text, length); - sel_string->length += length; - - if (space) - sel_string->buffer[sel_string->length++] = ' '; - - /* Ensure NULL termination */ - sel_string->buffer[sel_string->length] = '\0'; - + /* this mouse click is selection-related */ return true; } -/** - * Selection traversal routine for appending text to a string - * - * \param text pointer to text being added, or NULL for newline - * \param length length of text to be appended (bytes) - * \param box pointer to text box, or NULL if from textplain - * \param len_ctx Length conversion context - * \param handle selection string to append to - * \param whitespace_text whitespace to place before text for formatting - * may be NULL - * \param whitespace_length length of whitespace_text - * \return true iff successful and traversal should continue - */ - -static bool selection_copy_handler(const char *text, size_t length, - struct box *box, const nscss_len_ctx *len_ctx, - void *handle, const char *whitespace_text, - size_t whitespace_length) +/* exported interface documented in desktop/selection.h */ +void +selection_track(struct selection *s, browser_mouse_state mouse, unsigned idx) { - bool add_space = false; - plot_font_style_t style; - plot_font_style_t *pstyle = NULL; - - /* add any whitespace which precedes the text from this box */ - if (whitespace_text != NULL && whitespace_length > 0) { - if (!selection_string_append(whitespace_text, - whitespace_length, false, pstyle, handle)) { - return false; - } + if (!mouse) { + s->drag_state = DRAG_NONE; } - if (box != NULL) { - /* HTML */ - add_space = (box->space != 0); + switch (s->drag_state) { - if (box->style != NULL) { - /* Override default font style */ - font_plot_style_from_css(len_ctx, box->style, &style); - pstyle = &style; + case DRAG_START: + if (idx > s->end_idx) { + unsigned old_end = s->end_idx; + selection_set_end(s, idx); + selection_set_start(s, old_end); + s->drag_state = DRAG_END; } else { - /* If there's no style, there must be no text */ - assert(box->text == NULL); + selection_set_start(s, idx); } - } + break; - /* add the text from this box */ - if (!selection_string_append(text, length, add_space, pstyle, handle)) - return false; + case DRAG_END: + if (idx < s->start_idx) { + unsigned old_start = s->start_idx; + selection_set_start(s, idx); + selection_set_end(s, old_start); + s->drag_state = DRAG_START; + } else { + selection_set_end(s, idx); + } + break; - return true; + default: + break; + } } -/** - * Get copy of selection as string - * - * \param s selection - * \return string of selected text, or NULL. Ownership passed to caller. - */ - +/* exported interface documented in desktop/selection.h */ char *selection_get_copy(struct selection *s) { struct selection_string sel_string = { @@ -828,7 +468,7 @@ char *selection_get_copy(struct selection *s) if (s == NULL || !s->defined) return NULL; - if (!selection_traverse(s, selection_copy_handler, &sel_string)) { + if (!selection_copy(s, &sel_string)) { free(sel_string.buffer); free(sel_string.styles); return NULL; @@ -840,13 +480,7 @@ char *selection_get_copy(struct selection *s) } - -/** - * Copy the selected contents to the clipboard - * - * \param s selection - * \return true iff successful - */ +/* exported interface documented in desktop/selection.h */ bool selection_copy_to_clipboard(struct selection *s) { struct selection_string sel_string = { @@ -858,17 +492,20 @@ bool selection_copy_to_clipboard(struct selection *s) .styles = NULL }; - if (s == NULL || !s->defined) + if (s == NULL || !s->defined) { return false; + } - if (!selection_traverse(s, selection_copy_handler, &sel_string)) { + if (!selection_copy(s, &sel_string)) { free(sel_string.buffer); free(sel_string.styles); return false; } - guit->clipboard->set(sel_string.buffer, sel_string.length, - sel_string.styles, sel_string.n_styles); + guit->clipboard->set(sel_string.buffer, + sel_string.length, + sel_string.styles, + sel_string.n_styles); free(sel_string.buffer); free(sel_string.styles); @@ -877,21 +514,15 @@ bool selection_copy_to_clipboard(struct selection *s) } -/** - * Clears the current selection, optionally causing the screen to be updated. - * - * \param s selection object - * \param redraw true iff the previously selected region of the browser - * window should be redrawn - */ - -void selection_clear(struct selection *s, bool redraw) +/* exported interface documented in desktop/selection.h */ +bool selection_clear(struct selection *s, bool redraw) { int old_start, old_end; bool was_defined; assert(s); - was_defined = selection_defined(s); + + was_defined = s->defined; old_start = s->start_idx; old_end = s->end_idx; @@ -899,106 +530,75 @@ void selection_clear(struct selection *s, bool redraw) s->start_idx = 0; s->end_idx = 0; - if (redraw && was_defined) + if (redraw && was_defined) { selection_redraw(s, old_start, old_end); -} + } + return was_defined; +} -/** - * Selects all the text within the box subtree controlled by - * this selection object, updating the screen accordingly. - * - * \param s selection object - */ +/* exported interface documented in desktop/selection.h */ void selection_select_all(struct selection *s) { assert(s); s->defined = true; - + selection_set_start(s, 0); selection_set_end(s, s->max_idx); } -/** - * Set the start position of the current selection, updating the screen. - * - * \param s selection object - * \param offset byte offset within textual representation - */ - -void selection_set_start(struct selection *s, unsigned offset) +/* exported interface documented in desktop/selection.h */ +void selection_set_position(struct selection *s, unsigned start, unsigned end) { - bool was_defined = selection_defined(s); - unsigned old_start = s->start_idx; - - s->start_idx = offset; - s->defined = (s->start_idx < s->end_idx); - - if (was_defined) { - if (offset < old_start) - selection_redraw(s, s->start_idx, old_start); - else - selection_redraw(s, old_start, s->start_idx); - } - else if (selection_defined(s)) - selection_redraw(s, s->start_idx, s->end_idx); + selection_set_start(s, start); + selection_set_end(s, end); } -/** - * Set the end position of the current selection, updating the screen. - * - * \param s selection object - * \param offset byte offset within textual representation - */ - -void selection_set_end(struct selection *s, unsigned offset) +/* exported interface documented in desktop/selection.h */ +bool +selection_highlighted(const struct selection *s, + unsigned start, + unsigned end, + unsigned *start_idx, + unsigned *end_idx) { - bool was_defined = selection_defined(s); - unsigned old_end = s->end_idx; + assert(s); - s->end_idx = offset; - s->defined = (s->start_idx < s->end_idx); + if (!s->defined) { + return false; + } - if (was_defined) { - if (offset < old_end) - selection_redraw(s, s->end_idx, old_end); - else - selection_redraw(s, old_end, s->end_idx); + if ((end <= s->start_idx) || + (start >= s->end_idx)) { + return false; } - else if (selection_defined(s)) - selection_redraw(s, s->start_idx, s->end_idx); -} + *start_idx = (s->start_idx >= start) ? (s->start_idx - start) : 0; + *end_idx = min(end, s->end_idx) - start; -/** - * Tests whether a text range lies partially within the selection, if there is - * a selection defined, returning the start and end indexes of the bytes - * that should be selected. - * - * \param s the selection object - * \param start byte offset of start of text - * \param end byte offset of end of text - * \param start_idx receives the start index (in bytes) of the highlighted portion - * \param end_idx receives the end index (in bytes) - * \return true iff part of the given box lies within the selection - */ + return true; +} -bool selection_highlighted(const struct selection *s, - unsigned start, unsigned end, - unsigned *start_idx, unsigned *end_idx) +/* exported interface documented in desktop/selection.h */ +bool selection_active(struct selection *s) { - /* caller should have checked first for efficiency */ - assert(s); - assert(selection_defined(s)); + return s->defined; +} - if (end <= s->start_idx || start >= s->end_idx) - return false; +bool selection_dragging(struct selection *s) +{ + return s->drag_state != DRAG_NONE; +} - *start_idx = (s->start_idx >= start) ? (s->start_idx - start) : 0; - *end_idx = min(end, s->end_idx) - start; +bool selection_dragging_start(struct selection *s) +{ + return s->drag_state == DRAG_START; +} - return true; +void selection_drag_end(struct selection *s) +{ + s->drag_state = DRAG_NONE; } diff --git a/desktop/selection.h b/desktop/selection.h index 2f3f6dcfe..8cd523fd8 100644 --- a/desktop/selection.h +++ b/desktop/selection.h @@ -20,86 +20,172 @@ * Text selection within browser windows (interface). */ -#ifndef _NETSURF_DESKTOP_SELECTION_H_ -#define _NETSURF_DESKTOP_SELECTION_H_ +#ifndef NETSURF_DESKTOP_SELECTION_H_ +#define NETSURF_DESKTOP_SELECTION_H_ #include <stdbool.h> #include "netsurf/mouse.h" -#include "content/handlers/css/utils.h" struct box; +struct browser_window; +struct plot_font_style; +struct selection_string; +struct selection; +struct content; + +/** + * determine if a selecion is active + */ +bool selection_active(struct selection *s); -typedef enum { - DRAG_NONE, - DRAG_START, - DRAG_END -} seln_drag_state; - - -/* this structure should be treated as opaque outside selection.c - (it's defined here to accelerate selection_defined(s) for reduced - impact on redraw code) */ - -struct selection -{ - struct content *c; - struct box *root; - nscss_len_ctx len_ctx; - - unsigned max_idx; /* total bytes in text representation */ - - unsigned start_idx; /* offset in bytes within text representation */ - unsigned end_idx; +bool selection_dragging(struct selection *s); - bool defined; - bool is_html; +bool selection_dragging_start(struct selection *s); - seln_drag_state drag_state; -}; +/** + * Handles completion of a drag operation + */ +void selection_drag_end(struct selection *s); +/** + * Creates a new selection object associated with a browser window. + * + * Used from text and html content handlers + * + * \return new selection context + */ +struct selection *selection_create(struct content *c); -struct selection *selection_create(struct content *c, bool is_html); -void selection_prepare(struct selection *s, struct content *c, bool is_html); +/** + * Destroys a selection object clearing it if nesessary + * + * Used from content textsearch + * + * \param s selection object + */ void selection_destroy(struct selection *s); -void selection_init( - struct selection *s, - struct box *root, - const nscss_len_ctx *len_ctx); -void selection_reinit(struct selection *s, struct box *root); - -/* struct box *selection_root(struct selection *s); */ -#define selection_root(s) ((s)->root) - -/* bool selection_defined(struct selection *s); */ -#define selection_defined(s) ((s)->defined) +/** + * Initialise the selection object to use the given box subtree as its root, + * ie. selections are confined to that subtree. + * + * Used from text and html content handlers + * + * \param s selection object + */ +void selection_init(struct selection *s); -/* bool selection_dragging(struct selection *s); */ -#define selection_dragging(s) ((s)->drag_state != DRAG_NONE) +/** + * Initialise the selection object to use the given box subtree as its root, + * ie. selections are confined to that subtree, whilst maintaining the current + * selection whenever possible because, for example, it's just the page being + * resized causing the layout to change. + * + * Used from html content handler + * + * \param s selection object + */ +void selection_reinit(struct selection *s); -/* bool selection_dragging_start(struct selection *s); */ -#define selection_dragging_start(s) ((s)->drag_state == DRAG_START) +/** + * Clears the current selection, optionally causing the screen to be updated. + * + * Used from text and html content handlers + * + * \param s selection object + * \param redraw true iff the previously selected region of the browser + * window should be redrawn + * \return true if selection was cleared false if not + */ +bool selection_clear(struct selection *s, bool redraw); -void selection_clear(struct selection *s, bool redraw); +/** + * Selects all the text within the box subtree controlled by + * this selection object, updating the screen accordingly. + * + * Used from text and html content handlers + * + * \param s selection object + */ void selection_select_all(struct selection *s); -void selection_set_start(struct selection *s, unsigned idx); -void selection_set_end(struct selection *s, unsigned idx); +/** + * Set the position of the current selection, updating the screen. + * + * Used from content textsearch + * + * \param s selection object + * \param start byte offset within textual representation + * \param end byte offset within textual representation + */ +void selection_set_position(struct selection *s, unsigned start, unsigned end); -bool selection_click(struct selection *s, browser_mouse_state mouse, - unsigned idx); -void selection_track(struct selection *s, browser_mouse_state mouse, - unsigned idx); +/** + * Handles mouse clicks (including drag starts) in or near a selection + * + * Used from text and html content handlers + * + * \param s selection object + * \param mouse state of mouse buttons and modifier keys + * \param idx byte offset within textual representation + * \return true iff the click has been handled by the selection code + */ +bool selection_click(struct selection *s, struct browser_window *top, browser_mouse_state mouse, unsigned idx); +/** + * Handles movements related to the selection, eg. dragging of start and + * end points. + * + * Used from text and html content handlers + * + * \param s selection object + * \param mouse state of mouse buttons and modifier keys + * \param idx byte offset within text representation + */ +void selection_track(struct selection *s, browser_mouse_state mouse, unsigned idx); + +/** + * Copy the selected contents to the clipboard + * + * Used from text and html content handlers + * + * \param s selection + * \return true iff successful + */ bool selection_copy_to_clipboard(struct selection *s); -char * selection_get_copy(struct selection *s); -/** Handles completion of a drag operation */ -/* void selection_drag_end(struct selection *s); */ -#define selection_drag_end(s) ((s)->drag_state = DRAG_NONE) +/** + * Get copy of selection as string + * + * Used from text and html content handlers + * + * \param s selection + * \return string of selected text, or NULL. Ownership passed to caller. + */ +char *selection_get_copy(struct selection *s); + -bool selection_highlighted(const struct selection *s, - unsigned start, unsigned end, - unsigned *start_idx, unsigned *end_idx); +/** + * Tests whether a text range lies partially within the selection, if there is + * a selection defined, returning the start and end indexes of the bytes + * that should be selected. + * + * Used from text and html content handlers, content textsearch + * + * \param s the selection object + * \param start byte offset of start of text + * \param end byte offset of end of text + * \param start_idx receives the start index (in bytes) of the highlighted portion + * \param end_idx receives the end index (in bytes) + * \return true iff part of the given box lies within the selection + */ +bool selection_highlighted(const struct selection *s, unsigned start, unsigned end, unsigned *start_idx, unsigned *end_idx); + +bool +selection_string_append(const char *text, + size_t length, + bool space, + struct plot_font_style *style, + struct selection_string *sel_string); #endif diff --git a/desktop/sslcert_viewer.c b/desktop/sslcert_viewer.c deleted file mode 100644 index 4d8725757..000000000 --- a/desktop/sslcert_viewer.c +++ /dev/null @@ -1,570 +0,0 @@ -/* - * Copyright 2009 Paul Blokus <paul_pl@users.sourceforge.net> - * Copyright 2013 Michael Drake <tlsa@netsurf-browser.org> - * - * This file is part of NetSurf, http://www.netsurf-browser.org/ - * - * NetSurf is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * NetSurf is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -/** - * \file - * SSL Certificate verification UI implementation - */ - -#include <assert.h> -#include <stdlib.h> - -#include "content/fetch.h" -#include "content/urldb.h" -#include "content/hlcache.h" -#include "desktop/sslcert_viewer.h" -#include "desktop/treeview.h" -#include "utils/messages.h" -#include "utils/log.h" -#include "utils/utils.h" - -/** - * ssl certificate viewer data fields - */ -enum sslcert_viewer_field { - SSLCERT_V_SUBJECT, - SSLCERT_V_SERIAL, - SSLCERT_V_TYPE, - SSLCERT_V_VALID_UNTIL, - SSLCERT_V_VALID_FROM, - SSLCERT_V_VERSION, - SSLCERT_V_ISSUER, - SSLCERT_V_CERTIFICATES, - SSLCERT_V_N_FIELDS -}; - -typedef nserror (*response_cb)(bool proceed, void *pw); - -/** - * ssl certificate verification context. - */ -struct sslcert_session_data { - struct ssl_cert_info *certs; /**< Certificates */ - unsigned long num; /**< Number of certificates in chain */ - nsurl *url; /**< The url of the certificate */ - response_cb cb; /**< Cert accept/reject callback */ - void *cbpw; /**< Context passed to callback */ - - treeview *tree; /**< The treeview object */ - struct treeview_field_desc fields[SSLCERT_V_N_FIELDS]; -}; - - -/** - * ssl certificate tree entry - */ -struct sslcert_entry { - treeview_node *entry; - char version[24]; - char type[24]; - struct treeview_field_data data[SSLCERT_V_N_FIELDS - 1]; -}; - - -/** - * Free a ssl certificate viewer entry's treeview field data. - * - * \param e Entry to free data from - */ -static void sslcert_viewer_free_treeview_field_data(struct sslcert_entry *e) -{ -} - - -/** - * Build a sslcert viewer treeview field from given text - * - * \param field SSL certificate treeview field to build - * \param data SSL certificate entry field data to set - * \param value Text to set in field, ownership yielded - * \param ssl_d SSL certificate session data - * \return NSERROR_OK on success, appropriate error otherwise - */ -static inline nserror -sslcert_viewer_field_builder(enum sslcert_viewer_field field, - struct treeview_field_data *data, - const char *value, - struct sslcert_session_data *ssl_d) -{ - data->field = ssl_d->fields[field].field; - data->value = value; - data->value_len = (value != NULL) ? strlen(value) : 0; - - return NSERROR_OK; -} - - -/** - * Set a sslcert viewer entry's data from the certificate. - * - * \param e Entry to set up - * \param cert Data associated with entry's certificate - * \param ssl_d SSL certificate session data - * \return NSERROR_OK on success, appropriate error otherwise - */ -static nserror -sslcert_viewer_set_treeview_field_data(struct sslcert_entry *e, - const struct ssl_cert_info *cert, - struct sslcert_session_data *ssl_d) -{ - unsigned int written; - - assert(e != NULL); - assert(cert != NULL); - assert(ssl_d != NULL); - - /* Set the fields up */ - sslcert_viewer_field_builder(SSLCERT_V_SUBJECT, - &e->data[SSLCERT_V_SUBJECT], - cert->subject, ssl_d); - - sslcert_viewer_field_builder(SSLCERT_V_SERIAL, - &e->data[SSLCERT_V_SERIAL], - cert->serialnum, ssl_d); - - written = snprintf(e->type, sizeof(e->type), "%i", cert->cert_type); - assert(written < sizeof(e->type)); - sslcert_viewer_field_builder(SSLCERT_V_TYPE, - &e->data[SSLCERT_V_TYPE], - e->type, ssl_d); - - sslcert_viewer_field_builder(SSLCERT_V_VALID_UNTIL, - &e->data[SSLCERT_V_VALID_UNTIL], - cert->not_after, ssl_d); - - sslcert_viewer_field_builder(SSLCERT_V_VALID_FROM, - &e->data[SSLCERT_V_VALID_FROM], - cert->not_before, ssl_d); - - written = snprintf(e->version, sizeof(e->version), - "%li", cert->version); - assert(written < sizeof(e->version)); - sslcert_viewer_field_builder(SSLCERT_V_VERSION, - &e->data[SSLCERT_V_VERSION], - e->version, ssl_d); - - sslcert_viewer_field_builder(SSLCERT_V_ISSUER, - &e->data[SSLCERT_V_ISSUER], - cert->issuer, ssl_d); - - return NSERROR_OK; -} - - -/** - * Create a treeview node for a certificate - * - * \param ssl_d SSL certificate session data - * \param n Number of SSL certificate in chain, to make node for - * \return NSERROR_OK on success otherwise error code. - */ -static nserror -sslcert_viewer_create_node(struct sslcert_session_data *ssl_d, int n) -{ - struct sslcert_entry *e; - const struct ssl_cert_info *cert = &(ssl_d->certs[n]); - nserror err; - - /* Create new certificate viewer entry */ - e = malloc(sizeof(struct sslcert_entry)); - if (e == NULL) { - return NSERROR_NOMEM; - } - - err = sslcert_viewer_set_treeview_field_data(e, cert, ssl_d); - if (err != NSERROR_OK) { - free(e); - return err; - } - - /* Create the new treeview node */ - err = treeview_create_node_entry(ssl_d->tree, &(e->entry), - NULL, TREE_REL_FIRST_CHILD, - e->data, e, TREE_OPTION_NONE); - if (err != NSERROR_OK) { - sslcert_viewer_free_treeview_field_data(e); - free(e); - return err; - } - - return NSERROR_OK; -} - - -/** - * Initialise the treeview entry fields - * - * \param ssl_d SSL certificate session data - * \return NSERROR_OK on success otherwise error code. - */ -static nserror sslcert_init_entry_fields(struct sslcert_session_data *ssl_d) -{ - int i; - const char *label; - - for (i = 0; i < SSLCERT_V_N_FIELDS; i++) - ssl_d->fields[i].field = NULL; - - ssl_d->fields[SSLCERT_V_SUBJECT].flags = TREE_FLAG_DEFAULT; - label = "TreeviewLabelSubject"; - label = messages_get(label); - if (lwc_intern_string(label, strlen(label), - &ssl_d->fields[SSLCERT_V_SUBJECT].field) != - lwc_error_ok) { - goto error; - } - - ssl_d->fields[SSLCERT_V_SERIAL].flags = TREE_FLAG_SHOW_NAME; - label = "TreeviewLabelSerial"; - label = messages_get(label); - if (lwc_intern_string(label, strlen(label), - &ssl_d->fields[SSLCERT_V_SERIAL].field) != - lwc_error_ok) { - goto error; - } - - ssl_d->fields[SSLCERT_V_TYPE].flags = TREE_FLAG_SHOW_NAME; - label = "TreeviewLabelType"; - label = messages_get(label); - if (lwc_intern_string(label, strlen(label), - &ssl_d->fields[SSLCERT_V_TYPE].field) != - lwc_error_ok) { - goto error; - } - - ssl_d->fields[SSLCERT_V_VALID_UNTIL].flags = TREE_FLAG_SHOW_NAME; - label = "TreeviewLabelValidUntil"; - label = messages_get(label); - if (lwc_intern_string(label, strlen(label), - &ssl_d->fields[SSLCERT_V_VALID_UNTIL].field) != - lwc_error_ok) { - goto error; - } - - ssl_d->fields[SSLCERT_V_VALID_FROM].flags = TREE_FLAG_SHOW_NAME; - label = "TreeviewLabelValidFrom"; - label = messages_get(label); - if (lwc_intern_string(label, strlen(label), - &ssl_d->fields[SSLCERT_V_VALID_FROM].field) != - lwc_error_ok) { - goto error; - } - - ssl_d->fields[SSLCERT_V_VERSION].flags = TREE_FLAG_SHOW_NAME; - label = "TreeviewLabelVersion"; - label = messages_get(label); - if (lwc_intern_string(label, strlen(label), - &ssl_d->fields[SSLCERT_V_VERSION].field) != - lwc_error_ok) { - goto error; - } - - ssl_d->fields[SSLCERT_V_ISSUER].flags = TREE_FLAG_SHOW_NAME; - label = "TreeviewLabelIssuer"; - label = messages_get(label); - if (lwc_intern_string(label, strlen(label), - &ssl_d->fields[SSLCERT_V_ISSUER].field) != - lwc_error_ok) { - goto error; - } - - ssl_d->fields[SSLCERT_V_CERTIFICATES].flags = TREE_FLAG_DEFAULT; - label = "TreeviewLabelCertificates"; - label = messages_get(label); - if (lwc_intern_string(label, strlen(label), - &ssl_d->fields[SSLCERT_V_CERTIFICATES].field) != - lwc_error_ok) { - return false; - } - - return NSERROR_OK; - -error: - for (i = 0; i < SSLCERT_V_N_FIELDS; i++) - if (ssl_d->fields[i].field != NULL) - lwc_string_unref(ssl_d->fields[i].field); - - return NSERROR_UNKNOWN; -} - - -/** - * Delete ssl certificate viewer entries - * - * \param e Entry to delete. - */ -static void sslcert_viewer_delete_entry(struct sslcert_entry *e) -{ - sslcert_viewer_free_treeview_field_data(e); - free(e); -} - - -/** - * folder operation callback - * - * \param msg treeview message - * \param data message context - * \return NSERROR_OK on success - */ -static nserror -sslcert_viewer_tree_node_folder_cb(struct treeview_node_msg msg, void *data) -{ - switch (msg.msg) { - case TREE_MSG_NODE_DELETE: - case TREE_MSG_NODE_EDIT: - case TREE_MSG_NODE_LAUNCH: - break; - } - - return NSERROR_OK; -} - - -/** - * node entry callback - * - * \param msg treeview message - * \param data message context - * \return NSERROR_OK on success - */ -static nserror -sslcert_viewer_tree_node_entry_cb(struct treeview_node_msg msg, void *data) -{ - struct sslcert_entry *e = data; - - switch (msg.msg) { - case TREE_MSG_NODE_DELETE: - e->entry = NULL; - sslcert_viewer_delete_entry(e); - break; - - case TREE_MSG_NODE_EDIT: - case TREE_MSG_NODE_LAUNCH: - break; - } - - return NSERROR_OK; -} - - -/** - * ssl certificate treeview callbacks - */ -struct treeview_callback_table sslv_tree_cb_t = { - .folder = sslcert_viewer_tree_node_folder_cb, - .entry = sslcert_viewer_tree_node_entry_cb -}; - - -/* Exported interface, documented in sslcert_viewer.h */ -nserror -sslcert_viewer_init(struct core_window_callback_table *cw_t, - void *core_window_handle, - struct sslcert_session_data *ssl_d) -{ - nserror err; - int cert_loop; - - assert(ssl_d != NULL); - - err = treeview_init(); - if (err != NSERROR_OK) { - return err; - } - - NSLOG(netsurf, INFO, "Building certificate viewer"); - - /* Init. certificate chain treeview entry fields */ - err = sslcert_init_entry_fields(ssl_d); - if (err != NSERROR_OK) { - ssl_d->tree = NULL; - return err; - } - - /* Create the certificate treeview */ - err = treeview_create(&ssl_d->tree, &sslv_tree_cb_t, - SSLCERT_V_N_FIELDS, ssl_d->fields, - cw_t, core_window_handle, TREEVIEW_READ_ONLY); - if (err != NSERROR_OK) { - ssl_d->tree = NULL; - return err; - } - - /* Build treeview nodes from certificate chain */ - for (cert_loop = ssl_d->num - 1; cert_loop >= 0; cert_loop--) { - err = sslcert_viewer_create_node(ssl_d, cert_loop); - if (err != NSERROR_OK) { - return err; - } - } - - NSLOG(netsurf, INFO, "Built certificate viewer"); - - return NSERROR_OK; -} - - -/** - * Free SSL certificate session data - * - * \param ssl_d SSL certificate session data - */ -static void sslcert_cleanup_session(struct sslcert_session_data *ssl_d) -{ - assert(ssl_d != NULL); - - if (ssl_d->url) { - nsurl_unref(ssl_d->url); - ssl_d->url = NULL; - } - - if (ssl_d->certs) { - free(ssl_d->certs); - ssl_d->certs = NULL; - } - - free(ssl_d); -} - - -/* Exported interface, documented in sslcert_viewer.h */ -nserror sslcert_viewer_fini(struct sslcert_session_data *ssl_d) -{ - int i; - nserror err; - - NSLOG(netsurf, INFO, "Finalising ssl certificate viewer"); - - /* Destroy the treeview */ - err = treeview_destroy(ssl_d->tree); - - /* Free treeview entry fields */ - for (i = 0; i < SSLCERT_V_N_FIELDS; i++) - if (ssl_d->fields[i].field != NULL) - lwc_string_unref(ssl_d->fields[i].field); - - /* Destroy the sslcert_session_data */ - sslcert_cleanup_session(ssl_d); - - err = treeview_fini(); - if (err != NSERROR_OK) { - return err; - } - - NSLOG(netsurf, INFO, "Finalised ssl certificate viewer"); - - return err; -} - - -/* Exported interface, documented in sslcert_viewer.h */ -nserror -sslcert_viewer_create_session_data(unsigned long num, - struct nsurl *url, - nserror (*cb)(bool proceed, void *pw), - void *cbpw, - const struct ssl_cert_info *certs, - struct sslcert_session_data **ssl_d) -{ - struct sslcert_session_data *data; - - assert(url != NULL); - assert(certs != NULL); - - data = malloc(sizeof(struct sslcert_session_data)); - if (data == NULL) { - *ssl_d = NULL; - return NSERROR_NOMEM; - } - - /* copy certificate data */ - data->certs = malloc(num * sizeof(struct ssl_cert_info)); - if (data->certs == NULL) { - free(data); - *ssl_d = NULL; - return NSERROR_NOMEM; - } - memcpy(data->certs, certs, num * sizeof(struct ssl_cert_info)); - - data->url = nsurl_ref(url); - data->num = num; - data->cb = cb; - data->cbpw = cbpw; - - data->tree = NULL; - - *ssl_d = data; - return NSERROR_OK; -} - - -/* Exported interface, documented in sslcert_viewer.h */ -nserror sslcert_viewer_reject(struct sslcert_session_data *ssl_d) -{ - assert(ssl_d != NULL); - - ssl_d->cb(false, ssl_d->cbpw); - - return NSERROR_OK; -} - - -/* Exported interface, documented in sslcert_viewer.h */ -nserror sslcert_viewer_accept(struct sslcert_session_data *ssl_d) -{ - assert(ssl_d != NULL); - - urldb_set_cert_permissions(ssl_d->url, true); - - ssl_d->cb(true, ssl_d->cbpw); - - return NSERROR_OK; -} - - -/* Exported interface, documented in sslcert_viewer.h */ -void -sslcert_viewer_redraw(struct sslcert_session_data *ssl_d, - int x, int y, - struct rect *clip, - const struct redraw_context *ctx) -{ - assert(ssl_d != NULL && - "sslcert_viewer_redraw() given bad session data"); - - treeview_redraw(ssl_d->tree, x, y, clip, ctx); -} - - -/* Exported interface, documented in sslcert_viewer.h */ -void -sslcert_viewer_mouse_action(struct sslcert_session_data *ssl_d, - browser_mouse_state mouse, - int x, int y) -{ - treeview_mouse_action(ssl_d->tree, mouse, x, y); -} - - -/* Exported interface, documented in sslcert_viewer.h */ -bool sslcert_viewer_keypress(struct sslcert_session_data *ssl_d, uint32_t key) -{ - return treeview_keypress(ssl_d->tree, key); -} diff --git a/desktop/sslcert_viewer.h b/desktop/sslcert_viewer.h deleted file mode 100644 index 6955e0167..000000000 --- a/desktop/sslcert_viewer.h +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Copyright 2009 Paul Blokus <paul_pl@users.sourceforge.net> - * Copyright 2013 Michael Drake <tlsa@netsurf-browser.org> - * - * This file is part of NetSurf, http://www.netsurf-browser.org/ - * - * NetSurf is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * NetSurf is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -/** - * \file - * SSL Certificate verification UI interface - */ - -#ifndef NETSURF_DESKTOP_SSLCERT_VIEWER_H -#define NETSURF_DESKTOP_SSLCERT_VIEWER_H - -#include "netsurf/mouse.h" - -struct sslcert_session_data; -struct redraw_context; -struct core_window_callback_table; -struct rect; -struct nsurl; -struct ssl_cert_info; - -/** - * Create ssl certificate viewer session data. - * - * \param num The number of certificates in the chain - * \param url Address of the page we're inspecting certificates of - * \param cb Low level cache callback - * \param cbpw Low level cache private data - * \param certs The SSL certificates - * \param ssl_d Updated to SSL certificate session data - * \return NSERROR_OK on success, appropriate error otherwise - * - * Pass the session data to sslcert_viewer_init. - * sslcert_viewer_fini destroys the session data. - */ -nserror sslcert_viewer_create_session_data( - unsigned long num, struct nsurl *url, nserror (*cb)(bool proceed, void *pw), - void *cbpw, const struct ssl_cert_info *certs, - struct sslcert_session_data **ssl_d); - - -/** - * Initialise a ssl certificate viewer from session data. - * - * This iterates through the certificates, building a treeview. - * - * \param cw_t Callback table for cert viewer's core_window - * \param core_window_handle The core_window in which the cert viewer is shown - * \param ssl_d SSL certificate session data - * \return NSERROR_OK on success, appropriate error otherwise - */ -nserror sslcert_viewer_init(struct core_window_callback_table *cw_t, - void *core_window_handle, struct sslcert_session_data *ssl_d); - - -/** - * Finalise a ssl certificate viewer. - * - * This destroys the certificate treeview and the certificate viewer module's - * session data. - * - * \param ssl_d SSL certificate session data - * \return NSERROR_OK on success, appropriate error otherwise - */ -nserror sslcert_viewer_fini(struct sslcert_session_data *ssl_d); - - -/** - * Reject a certificate chain. - * - * \param ssl_d SSL certificate session data - * \return NSERROR_OK on success, appropriate error otherwise - */ -nserror sslcert_viewer_reject(struct sslcert_session_data *ssl_d); - - -/** - * Accept a certificate chain. - * - * \param ssl_d SSL certificate session data - * \return NSERROR_OK on success, appropriate error otherwise - */ -nserror sslcert_viewer_accept(struct sslcert_session_data *ssl_d); - - -/** - * Redraw the ssl certificate viewer. - * - * \param ssl_d SSL certificate session data - * \param x X coordinate to render treeview at - * \param y Y coordinate to render treeview at - * \param clip Current clip rectangle (wrt tree origin) - * \param ctx Current redraw context - */ -void sslcert_viewer_redraw(struct sslcert_session_data *ssl_d, - int x, int y, struct rect *clip, - const struct redraw_context *ctx); - - -/** - * Handles all kinds of mouse action - * - * \param ssl_d SSL certificate session data - * \param mouse The current mouse state - * \param x X coordinate - * \param y Y coordinate - */ -void sslcert_viewer_mouse_action(struct sslcert_session_data *ssl_d, - browser_mouse_state mouse, int x, int y); - - -/** - * Key press handling. - * - * \param ssl_d SSL certificate session data - * \param key The ucs4 character codepoint - * \return true if the keypress is dealt with, false otherwise. - */ -bool sslcert_viewer_keypress(struct sslcert_session_data *ssl_d, uint32_t key); - -#endif diff --git a/desktop/textarea.c b/desktop/textarea.c index 5bae27a5c..e0e87444c 100644 --- a/desktop/textarea.c +++ b/desktop/textarea.c @@ -58,7 +58,7 @@ struct line_info { struct textarea_drag { textarea_drag_type type; union { - struct scrollbar* scrollbar; + struct scrollbar *scrollbar; } data; }; @@ -625,7 +625,7 @@ static bool textarea_select(struct textarea *ta, int b_start, int b_end, * \param ta Text area * \return True on success, false otherwise */ -static bool textarea_select_fragment(struct textarea * ta) +static bool textarea_select_fragment(struct textarea *ta) { int caret_pos; size_t sel_start, sel_end; @@ -676,7 +676,7 @@ static bool textarea_select_fragment(struct textarea * ta) * \param ta textarea widget * \return True on success, false otherwise */ -static bool textarea_select_paragraph(struct textarea * ta) +static bool textarea_select_paragraph(struct textarea *ta) { int caret_pos; size_t sel_start, sel_end; @@ -1607,7 +1607,7 @@ static bool textarea_copy_to_undo_buffer(struct textarea *ta, * \param b_end End byte index of replaced section (exclusive) * \param rep Replacement UTF-8 text to insert * \param rep_len Byte length of replacement UTF-8 text - * \param add_to_clipboard True iff replaced text to be added to clipboard + * \param add_to_clipboard True if replaced text to be added to clipboard * \param byte_delta Updated to change in byte count in textarea (ta->show) * \param r Updated to area where redraw is required * \return false on memory exhaustion, true otherwise @@ -2452,7 +2452,7 @@ bool textarea_keypress(struct textarea *ta, uint32_t key) struct textarea_msg msg; struct rect r; /**< Redraw rectangle */ char utf8[6]; - unsigned int caret, length, b_off, b_len; + unsigned int caret, caret_copy, length, b_off, b_len; int h_extent = ta->h_extent; int v_extent = ta->v_extent; int line; @@ -2466,7 +2466,7 @@ bool textarea_keypress(struct textarea *ta, uint32_t key) /* Word separators */ static const char *sep = " .\n"; - caret = textarea_get_caret(ta); + caret = caret_copy = textarea_get_caret(ta); line = ta->caret_pos.line; readonly = (ta->flags & TEXTAREA_READONLY ? true : false); @@ -2743,6 +2743,9 @@ bool textarea_keypress(struct textarea *ta, uint32_t key) case NS_KEY_WORD_LEFT: if (readonly) break; + if (ta->sel_start != -1) { + textarea_clear_selection(ta); + } if (caret == 0) break; caret--; @@ -2756,13 +2759,59 @@ bool textarea_keypress(struct textarea *ta, uint32_t key) break; } } + break; + case NS_KEY_DELETE_WORD_LEFT: + if (readonly) + break; + + /* If there is a selection, remove the selected + * characters */ if (ta->sel_start != -1) { + if (!textarea_replace_text(ta, ta->sel_start, + ta->sel_end, "", 0, false, + &byte_delta, &r)) + return false; + caret = ta->sel_start; textarea_clear_selection(ta); + redraw = true; + break; + } + + if (caret == 0) + break; + + /* caret goes left until a non-separator is + * encountered */ + caret--; + while (strchr(sep, ta->show->data[caret]) != NULL && + caret > 0) + caret--; + + /* caret goes left until a separator is encountered */ + for (; caret > 0; caret--) { + if (strchr(sep, ta->show->data[caret]) != + NULL) { + caret++; + break; + } } + + /* Remove the characters from new caret position to + * original caret position */ + if (!textarea_replace_text(ta, caret, caret_copy, + "", 0, false, &byte_delta, &r)) + return false; + + redraw = true; break; case NS_KEY_WORD_RIGHT: if (readonly) break; + if (ta->sel_start != -1) { + textarea_clear_selection(ta); + } + if (caret == ta->show->len - 1) + break; if (strchr(sep, ta->show->data[caret]) != NULL && caret < ta->show->len - 1) { while (strchr(sep, ta->show->data[caret]) != @@ -2779,9 +2828,49 @@ bool textarea_keypress(struct textarea *ta, uint32_t key) while (strchr(sep, ta->show->data[caret]) != NULL && caret < ta->show->len - 1) caret++; + break; + case NS_KEY_DELETE_WORD_RIGHT: + if (readonly) + break; + + /* If there is a selection, remove the selected + * characters */ if (ta->sel_start != -1) { + if (!textarea_replace_text(ta, ta->sel_start, + ta->sel_end, "", 0, false, + &byte_delta, &r)) + return false; + caret = ta->sel_start; textarea_clear_selection(ta); + redraw = true; + break; } + + if (caret == ta->show->len - 1) + break; + + /* caret_copy goes right until a non-separator is + * encountered */ + while (strchr(sep, ta->show->data[caret_copy]) != NULL + && caret_copy < ta->show->len - 1) + caret_copy++; + + /* caret_copy goes right until a separator is + * encountered */ + for (; caret_copy < ta->show->len - 1; caret_copy++) { + if (strchr(sep, ta->show->data[caret_copy]) != + NULL) { + break; + } + } + + /* Remove all the characters from original caret + * position to caret_copy */ + if (!textarea_replace_text(ta, caret, caret_copy, + "", 0, false, &byte_delta, &r)) + return false; + + redraw = true; break; case NS_KEY_DELETE_LINE: if (readonly) diff --git a/desktop/textinput.c b/desktop/textinput.c index f8da3d86b..b8dced689 100644 --- a/desktop/textinput.c +++ b/desktop/textinput.c @@ -33,14 +33,13 @@ #include "utils/talloc.h" #include "utils/utf8.h" #include "utils/utils.h" +#include "netsurf/types.h" #include "netsurf/mouse.h" #include "netsurf/form.h" #include "netsurf/window.h" #include "netsurf/browser_window.h" #include "netsurf/keypress.h" -#include "html/box.h" -#include "html/html_internal.h" -#include "html/layout.h" +#include "content/content.h" #include "desktop/browser_private.h" #include "desktop/textinput.h" diff --git a/desktop/treeview.c b/desktop/treeview.c index 258b54a52..a65a37e72 100644 --- a/desktop/treeview.c +++ b/desktop/treeview.c @@ -22,15 +22,15 @@ * Treeview handling implementation. */ -#define _GNU_SOURCE /* strcasestr needs this for string.h */ +#include "utils/config.h" #include <string.h> #include "utils/utils.h" #include "utils/log.h" #include "utils/nsurl.h" +#include "utils/nscolour.h" #include "utils/nsoption.h" -#include "utils/config.h" #include "netsurf/bitmap.h" #include "netsurf/content.h" #include "netsurf/plotters.h" @@ -41,6 +41,7 @@ #include "content/hlcache.h" #include "css/utils.h" +#include "desktop/bitmap.h" #include "desktop/knockout.h" #include "desktop/textarea.h" #include "desktop/treeview.h" @@ -361,6 +362,26 @@ static inline void treeview__cw_invalidate_area( /** + * Corewindow callback wrapper: Request a full redraw of the window + * + * \param[in] tree The treeview to request redraw on. + */ +static inline void treeview__cw_full_redraw( + const struct treeview *tree) +{ + if (tree->cw_t != NULL) { + static const struct rect r = { + .x0 = 0, + .y0 = 0, + .x1 = REDRAW_MAX, + .y1 = REDRAW_MAX, + }; + tree->cw_t->invalidate(tree->cw_h, &r); + } +} + + +/** * Get height used by treeview's search bar (or 0 if not present). * * \param tree Treeview object to check. @@ -915,6 +936,12 @@ static void treeview__search_cancel(treeview *tree, bool drop_focus) return; } + if (textarea_get_text(tree->search.textarea, NULL, 0) == 1) { + // If there's no text in the search box, we drop focus on a + // cancel. Note '1' because it includes the trailing \0 + drop_focus = true; + } + if (drop_focus) { tree->search.active = false; textarea_set_caret(tree->search.textarea, -1); @@ -926,6 +953,34 @@ static void treeview__search_cancel(treeview *tree, bool drop_focus) treeview__cw_invalidate_area(tree, &r); } +/** + * Convert from treeview drag to core window drag type. + * + * \param[in] tree A treeview. + * \return Core window drag type. + */ +static core_window_drag_status treeview__get_cw_drag_type( + const treeview *tree) +{ + assert(tree != NULL); + + switch (tree->drag.type) { + case TV_DRAG_NONE: + return CORE_WINDOW_DRAG_NONE; + + case TV_DRAG_SELECTION: + return CORE_WINDOW_DRAG_SELECTION; + + case TV_DRAG_TEXTAREA: /* Fall through.*/ + case TV_DRAG_SEARCH: + return CORE_WINDOW_DRAG_TEXT_SELECTION; + + case TV_DRAG_MOVE: + return CORE_WINDOW_DRAG_MOVE; + } + + return CORE_WINDOW_DRAG_NONE; +} /** * Callback for textarea_create, in desktop/treeview.h @@ -952,7 +1007,8 @@ static void treeview_textarea_search_callback(void *data, /* Textarea drag started */ tree->drag.type = TV_DRAG_SEARCH; } - treeview__cw_drag_status(tree, tree->drag.type); + treeview__cw_drag_status(tree, + treeview__get_cw_drag_type(tree)); break; case TEXTAREA_MSG_REDRAW_REQUEST: @@ -2044,9 +2100,9 @@ treeview_create(treeview **tree, if (flags & TREEVIEW_SEARCHABLE) { (*tree)->search.textarea = treeview__create_textarea( *tree, 600, tree_g.line_height, - plot_style_even.text.background, - plot_style_even.text.background, - plot_style_even.text.foreground, + nscolours[NSCOLOUR_TEXT_INPUT_BG], + nscolours[NSCOLOUR_TEXT_INPUT_BG], + nscolours[NSCOLOUR_TEXT_INPUT_FG], plot_style_odd.text, treeview_textarea_search_callback); if ((*tree)->search.textarea == NULL) { @@ -2143,7 +2199,8 @@ treeview_node_expand_internal(treeview *tree, treeview_node *node) { treeview_node *child; struct treeview_node_entry *e; - int additional_height = 0; + int additional_height_folders = 0; + int additional_height_entries = 0; int i; assert(tree != NULL); @@ -2171,7 +2228,7 @@ treeview_node_expand_internal(treeview *tree, treeview_node *node) &(child->text.width)); } - additional_height += child->height; + additional_height_folders += child->height; child = child->next_sib; } while (child != NULL); @@ -2193,7 +2250,7 @@ treeview_node_expand_internal(treeview *tree, treeview_node *node) } /* Add height for field */ - additional_height += tree_g.line_height; + additional_height_entries += tree_g.line_height; } break; @@ -2212,17 +2269,18 @@ treeview_node_expand_internal(treeview *tree, treeview_node *node) for (struct treeview_node *n = node; (n != NULL) && (n->flags & TV_NFLAGS_EXPANDED); n = n->parent) { - n->height += additional_height; + n->height += additional_height_entries + + additional_height_folders; } if (tree->search.search && node->type == TREE_NODE_ENTRY && node->flags & TV_NFLAGS_MATCHED) { - tree->search.height += additional_height; + tree->search.height += additional_height_entries; } /* Inform front end of change in dimensions */ - if (additional_height != 0) { + if (additional_height_entries + additional_height_folders != 0) { treeview__cw_update_size(tree, -1, treeview__get_display_height(tree)); } @@ -2268,7 +2326,8 @@ struct treeview_contract_data { static nserror treeview_node_contract_cb(treeview_node *n, void *ctx, bool *end) { struct treeview_contract_data *data = ctx; - int h_reduction; + int h_reduction_folder = 0; + int h_reduction_entry = 0; assert(n != NULL); assert(n->type != TREE_NODE_ROOT); @@ -2281,17 +2340,30 @@ static nserror treeview_node_contract_cb(treeview_node *n, void *ctx, bool *end) return NSERROR_OK; } - h_reduction = n->height - tree_g.line_height; - assert(h_reduction >= 0); + switch (n->type) { + case TREE_NODE_FOLDER: + h_reduction_folder = n->height - tree_g.line_height; + break; + + case TREE_NODE_ENTRY: + h_reduction_entry = n->height - tree_g.line_height; + break; + + default: + break; + } + + + assert(h_reduction_folder + h_reduction_entry >= 0); for (struct treeview_node *node = n; (node != NULL) && (node->flags & TV_NFLAGS_EXPANDED); node = node->parent) { - node->height -= h_reduction; + node->height -= h_reduction_folder + h_reduction_entry; } if (data->tree->search.search) { - data->tree->search.height -= h_reduction; + data->tree->search.height -= h_reduction_entry; } n->flags ^= TV_NFLAGS_EXPANDED; @@ -2497,7 +2569,7 @@ static void treeview_redraw_tree( const int x, const int y, int *render_y_in_out, - struct rect *r, + const struct rect *r, struct content_redraw_data *data, const struct redraw_context *ctx) { @@ -2716,7 +2788,7 @@ static void treeview_redraw_search( const int x, const int y, int *render_y_in_out, - struct rect *r, + const struct rect *r, struct content_redraw_data *data, const struct redraw_context *ctx) { @@ -4175,7 +4247,8 @@ static void treeview_textarea_callback(void *data, struct textarea_msg *msg) /* Textarea drag started */ tree->drag.type = TV_DRAG_TEXTAREA; } - treeview__cw_drag_status(tree, tree->drag.type); + treeview__cw_drag_status(tree, + treeview__get_cw_drag_type(tree)); break; case TEXTAREA_MSG_REDRAW_REQUEST: @@ -4668,9 +4741,7 @@ treeview_mouse_action(treeview *tree, browser_mouse_state mouse, int x, int y) textarea_mouse_action(tree->edit.textarea, mouse, x - tree->edit.x, y - tree->edit.y); return; - } else if (tree->drag.type == TV_DRAG_SEARCH || - (y < search_height && - tree->drag.type == TV_DRAG_NONE)) { + } else if (tree->drag.type == TV_DRAG_SEARCH) { if (tree->search.active == false) { tree->search.active = true; if (treeview_clear_selection(tree, &r)) { @@ -4682,6 +4753,16 @@ treeview_mouse_action(treeview *tree, browser_mouse_state mouse, int x, int y) y); return; } else if (mouse & (BROWSER_MOUSE_PRESS_1 | BROWSER_MOUSE_PRESS_2) && + y < search_height && tree->search.active == false) { + tree->search.active = true; + if (treeview_clear_selection(tree, &r)) { + treeview__cw_invalidate_area(tree, &r); + } + textarea_mouse_action(tree->search.textarea, mouse, + x - tree_g.window_padding - tree_g.icon_size, + y); + return; + } else if (mouse & (BROWSER_MOUSE_PRESS_1 | BROWSER_MOUSE_PRESS_2) && tree->search.active == true) { tree->search.active = false; @@ -4839,6 +4920,31 @@ int treeview_get_height(treeview *tree) return height + search_height; } +/* Exported interface, documented in treeview.h */ +nserror treeview_set_search_string( + treeview *tree, + const char *string) +{ + if (!(tree->flags & TREEVIEW_SEARCHABLE)) { + return NSERROR_BAD_PARAMETER; + } + + if (string == NULL || strlen(string) == 0) { + tree->search.active = false; + tree->search.search = false; + return treeview__search(tree, "", 0); + } + + tree->search.active = true; + tree->search.search = true; + if (!textarea_set_text(tree->search.textarea, string)) { + return NSERROR_UNKNOWN; + } + + treeview__cw_full_redraw(tree); + + return NSERROR_OK; +} /** * Initialise the plot styles from CSS system colour values. @@ -4848,75 +4954,45 @@ int treeview_get_height(treeview *tree) */ static nserror treeview_init_plot_styles(int font_pt_size) { - nserror res; - /* Background colour */ plot_style_even.bg.stroke_type = PLOT_OP_TYPE_NONE; plot_style_even.bg.stroke_width = 0; plot_style_even.bg.stroke_colour = 0; plot_style_even.bg.fill_type = PLOT_OP_TYPE_SOLID; - res = ns_system_colour_char("Window", &plot_style_even.bg.fill_colour); - if (res != NSERROR_OK) { - return res; - } + plot_style_even.bg.fill_colour = nscolours[NSCOLOUR_WIN_EVEN_BG]; /* Text colour */ plot_style_even.text.family = PLOT_FONT_FAMILY_SANS_SERIF; plot_style_even.text.size = font_pt_size; plot_style_even.text.weight = 400; plot_style_even.text.flags = FONTF_NONE; - res = ns_system_colour_char("WindowText", &plot_style_even.text.foreground); - if (res != NSERROR_OK) { - return res; - } - res = ns_system_colour_char("Window", &plot_style_even.text.background); - if (res != NSERROR_OK) { - return res; - } + plot_style_even.text.foreground = nscolours[NSCOLOUR_WIN_EVEN_FG]; + plot_style_even.text.background = nscolours[NSCOLOUR_WIN_EVEN_BG]; /* Entry field text colour */ plot_style_even.itext = plot_style_even.text; - plot_style_even.itext.foreground = mix_colour( - plot_style_even.text.foreground, - plot_style_even.text.background, - 255 * 10 / 16); + plot_style_even.itext.foreground = nscolours[NSCOLOUR_WIN_EVEN_FG_FADED]; /* Selected background colour */ plot_style_even.sbg = plot_style_even.bg; - res = ns_system_colour_char("Highlight", &plot_style_even.sbg.fill_colour); - if (res != NSERROR_OK) { - return res; - } + plot_style_even.sbg.fill_colour = nscolours[NSCOLOUR_SEL_BG]; /* Selected text colour */ plot_style_even.stext = plot_style_even.text; - res = ns_system_colour_char("HighlightText", &plot_style_even.stext.foreground); - if (res != NSERROR_OK) { - return res; - } - res = ns_system_colour_char("Highlight", &plot_style_even.stext.background); - if (res != NSERROR_OK) { - return res; - } + plot_style_even.stext.foreground = nscolours[NSCOLOUR_SEL_FG]; + plot_style_even.stext.background = nscolours[NSCOLOUR_SEL_BG]; /* Selected entry field text colour */ plot_style_even.sitext = plot_style_even.stext; - plot_style_even.sitext.foreground = mix_colour( - plot_style_even.stext.foreground, - plot_style_even.stext.background, - 255 * 25 / 32); + plot_style_even.sitext.foreground = nscolours[NSCOLOUR_SEL_FG_SUBTLE]; /* Odd numbered node styles */ plot_style_odd.bg = plot_style_even.bg; - plot_style_odd.bg.fill_colour = mix_colour( - plot_style_even.bg.fill_colour, - plot_style_even.text.foreground, 255 * 15 / 16); + plot_style_odd.bg.fill_colour = nscolours[NSCOLOUR_WIN_ODD_BG]; plot_style_odd.text = plot_style_even.text; plot_style_odd.text.background = plot_style_odd.bg.fill_colour; plot_style_odd.itext = plot_style_odd.text; - plot_style_odd.itext.foreground = mix_colour( - plot_style_odd.text.foreground, - plot_style_odd.text.background, 255 * 10 / 16); + plot_style_odd.itext.foreground = nscolours[NSCOLOUR_WIN_EVEN_FG_FADED]; plot_style_odd.sbg = plot_style_even.sbg; plot_style_odd.stext = plot_style_even.stext; @@ -5002,7 +5078,7 @@ treeview_generate_triangle_bitmap(colour bg, colour fg, int size) colour colour4 = fg; /* Create the bitmap */ - b = guit->bitmap->create(size, size, BITMAP_NEW | BITMAP_OPAQUE); + b = guit->bitmap->create(size, size, BITMAP_OPAQUE); if (b == NULL) return NULL; @@ -5016,58 +5092,68 @@ treeview_generate_triangle_bitmap(colour bg, colour fg, int size) if (y < size / 2) { /* Top half */ for (x = 0; x < y * 2; x++) { - *(pos++) = red_from_colour(colour4); - *(pos++) = green_from_colour(colour4); - *(pos++) = blue_from_colour(colour4); - *(pos++) = 0xff; + pos[bitmap_layout.r] = red_from_colour(colour4); + pos[bitmap_layout.g] = green_from_colour(colour4); + pos[bitmap_layout.b] = blue_from_colour(colour4); + pos[bitmap_layout.a] = 0xff; + pos += 4; } - *(pos++) = red_from_colour(colour3); - *(pos++) = green_from_colour(colour3); - *(pos++) = blue_from_colour(colour3); - *(pos++) = 0xff; - *(pos++) = red_from_colour(colour1); - *(pos++) = green_from_colour(colour1); - *(pos++) = blue_from_colour(colour1); - *(pos++) = 0xff; + pos[bitmap_layout.r] = red_from_colour(colour3); + pos[bitmap_layout.g] = green_from_colour(colour3); + pos[bitmap_layout.b] = blue_from_colour(colour3); + pos[bitmap_layout.a] = 0xff; + pos += 4; + pos[bitmap_layout.r] = red_from_colour(colour1); + pos[bitmap_layout.g] = green_from_colour(colour1); + pos[bitmap_layout.b] = blue_from_colour(colour1); + pos[bitmap_layout.a] = 0xff; + pos += 4; for (x = y * 2 + 2; x < size ; x++) { - *(pos++) = red_from_colour(colour0); - *(pos++) = green_from_colour(colour0); - *(pos++) = blue_from_colour(colour0); - *(pos++) = 0xff; + pos[bitmap_layout.r] = red_from_colour(colour0); + pos[bitmap_layout.g] = green_from_colour(colour0); + pos[bitmap_layout.b] = blue_from_colour(colour0); + pos[bitmap_layout.a] = 0xff; + pos += 4; } } else if ((y == size / 2) && (size & 0x1)) { /* Middle row */ for (x = 0; x < size - 1; x++) { - *(pos++) = red_from_colour(colour4); - *(pos++) = green_from_colour(colour4); - *(pos++) = blue_from_colour(colour4); - *(pos++) = 0xff; + pos[bitmap_layout.r] = red_from_colour(colour4); + pos[bitmap_layout.g] = green_from_colour(colour4); + pos[bitmap_layout.b] = blue_from_colour(colour4); + pos[bitmap_layout.a] = 0xff; + pos += 4; } - *(pos++) = red_from_colour(colour2); - *(pos++) = green_from_colour(colour2); - *(pos++) = blue_from_colour(colour2); - *(pos++) = 0xff; + pos[bitmap_layout.r] = red_from_colour(colour2); + pos[bitmap_layout.g] = green_from_colour(colour2); + pos[bitmap_layout.b] = blue_from_colour(colour2); + pos[bitmap_layout.a] = 0xff; + pos += 4; } else { /* Bottom half */ for (x = 0; x < (size - y - 1) * 2; x++) { - *(pos++) = red_from_colour(colour4); - *(pos++) = green_from_colour(colour4); - *(pos++) = blue_from_colour(colour4); - *(pos++) = 0xff; + pos[bitmap_layout.r] = red_from_colour(colour4); + pos[bitmap_layout.g] = green_from_colour(colour4); + pos[bitmap_layout.b] = blue_from_colour(colour4); + pos[bitmap_layout.a] = 0xff; + pos += 4; } - *(pos++) = red_from_colour(colour3); - *(pos++) = green_from_colour(colour3); - *(pos++) = blue_from_colour(colour3); - *(pos++) = 0xff; - *(pos++) = red_from_colour(colour1); - *(pos++) = green_from_colour(colour1); - *(pos++) = blue_from_colour(colour1); - *(pos++) = 0xff; + pos[bitmap_layout.r] = red_from_colour(colour3); + pos[bitmap_layout.g] = green_from_colour(colour3); + pos[bitmap_layout.b] = blue_from_colour(colour3); + pos[bitmap_layout.a] = 0xff; + pos += 4; + pos[bitmap_layout.r] = red_from_colour(colour1); + pos[bitmap_layout.g] = green_from_colour(colour1); + pos[bitmap_layout.b] = blue_from_colour(colour1); + pos[bitmap_layout.a] = 0xff; + pos += 4; for (x = (size - y) * 2; x < size ; x++) { - *(pos++) = red_from_colour(colour0); - *(pos++) = green_from_colour(colour0); - *(pos++) = blue_from_colour(colour0); - *(pos++) = 0xff; + pos[bitmap_layout.r] = red_from_colour(colour0); + pos[bitmap_layout.g] = green_from_colour(colour0); + pos[bitmap_layout.b] = blue_from_colour(colour0); + pos[bitmap_layout.a] = 0xff; + pos += 4; } } @@ -5101,7 +5187,7 @@ treeview_generate_copy_bitmap(struct bitmap *orig, int size) assert(size == guit->bitmap->get_height(orig)); /* Create the bitmap */ - b = guit->bitmap->create(size, size, BITMAP_NEW | BITMAP_OPAQUE); + b = guit->bitmap->create(size, size, BITMAP_OPAQUE); if (b == NULL) return NULL; @@ -5149,7 +5235,7 @@ treeview_generate_rotate_bitmap(struct bitmap *orig, int size) assert(size == guit->bitmap->get_height(orig)); /* Create the bitmap */ - b = guit->bitmap->create(size, size, BITMAP_NEW | BITMAP_OPAQUE); + b = guit->bitmap->create(size, size, BITMAP_OPAQUE); if (b == NULL) return NULL; diff --git a/desktop/treeview.h b/desktop/treeview.h index a8cf29ac5..df9b4fb26 100644 --- a/desktop/treeview.h +++ b/desktop/treeview.h @@ -495,4 +495,15 @@ void treeview_edit_selection(treeview *tree); */ int treeview_get_height(treeview *tree); + +/** + * Set the search string for a treeview with \ref TREEVIEW_SEARCHABLE + * + * \param tree Tree to set the search string for. + * \return NSERROR_OK on success, appropriate error otherwise + */ +nserror treeview_set_search_string( + treeview *tree, + const char *string); + #endif diff --git a/desktop/version.c b/desktop/version.c index 92240cd07..91a7532f4 100644 --- a/desktop/version.c +++ b/desktop/version.c @@ -20,11 +20,11 @@ #include "desktop/version.h" -const char * const netsurf_version = "3.10 (Dev" +const char * const netsurf_version = "3.12 (Dev" #if defined(CI_BUILD) " CI #" CI_BUILD #endif ")" ; const int netsurf_version_major = 3; -const int netsurf_version_minor = 10; +const int netsurf_version_minor = 12; |