summaryrefslogtreecommitdiff
path: root/desktop
diff options
context:
space:
mode:
Diffstat (limited to 'desktop')
-rw-r--r--desktop/Makefile7
-rw-r--r--desktop/bitmap.c338
-rw-r--r--desktop/bitmap.h147
-rw-r--r--desktop/browser.c3348
-rw-r--r--desktop/browser_history.c99
-rw-r--r--desktop/browser_private.h88
-rw-r--r--desktop/browser_window.c4803
-rw-r--r--desktop/cookie_manager.c48
-rw-r--r--desktop/cookie_manager.h16
-rw-r--r--desktop/cw_helper.c86
-rw-r--r--desktop/cw_helper.h41
-rw-r--r--desktop/download.c20
-rw-r--r--desktop/download.h2
-rw-r--r--desktop/font_haru.c2
-rw-r--r--desktop/frames.c342
-rw-r--r--desktop/frames.h38
-rw-r--r--desktop/global_history.c27
-rw-r--r--desktop/global_history.h7
-rw-r--r--desktop/gui_factory.c200
-rw-r--r--desktop/gui_table.h9
-rw-r--r--desktop/hotlist.c66
-rw-r--r--desktop/hotlist.h11
-rw-r--r--desktop/knockout.c4
-rw-r--r--desktop/local_history.c267
-rw-r--r--desktop/local_history.h11
-rw-r--r--desktop/local_history_private.h38
-rw-r--r--desktop/netsurf.c80
-rw-r--r--desktop/options.h31
-rw-r--r--desktop/page-info.c831
-rw-r--r--desktop/page-info.h147
-rw-r--r--desktop/plot_style.c14
-rw-r--r--desktop/print.c39
-rw-r--r--desktop/save_complete.c428
-rw-r--r--desktop/save_complete.h14
-rw-r--r--desktop/save_pdf.c16
-rw-r--r--desktop/save_text.c4
-rw-r--r--desktop/scrollbar.c32
-rw-r--r--desktop/scrollbar.h1
-rw-r--r--desktop/search.c14
-rw-r--r--desktop/search.h10
-rw-r--r--desktop/searchweb.c94
-rw-r--r--desktop/searchweb.h29
-rw-r--r--desktop/selection.c1052
-rw-r--r--desktop/selection.h199
-rw-r--r--desktop/sslcert_viewer.c572
-rw-r--r--desktop/sslcert_viewer.h135
-rw-r--r--desktop/textarea.c157
-rw-r--r--desktop/textarea.h8
-rw-r--r--desktop/textinput.c13
-rw-r--r--desktop/treeview.c633
-rw-r--r--desktop/treeview.h23
-rw-r--r--desktop/version.c6
52 files changed, 8818 insertions, 5829 deletions
diff --git a/desktop/Makefile b/desktop/Makefile
index ffd932177..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,8 @@ 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_history.c download.c frames.c netsurf.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 19cfebb99..6968bf21b 100644
--- a/desktop/browser.c
+++ b/desktop/browser.c
@@ -1,11 +1,5 @@
/*
- * 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 2019 Vincent Sanders <vince@netsurf-browser.org>
*
* This file is part of NetSurf, http://www.netsurf-browser.org/
*
@@ -25,3349 +19,29 @@
/**
* \file
*
- * Browser window creation and manipulation implementation.
+ * Browser core functionality
*/
-#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/messages.h"
-#include "utils/nsurl.h"
#include "utils/utils.h"
-#include "utils/utf8.h"
-#include "utils/nsoption.h"
-#include "netsurf/misc.h"
-#include "netsurf/window.h"
-#include "netsurf/content.h"
-#include "netsurf/plotters.h"
-#include "content/content_debug.h"
-#include "content/fetch.h"
-#include "content/hlcache.h"
-#include "content/urldb.h"
+#include "netsurf/browser.h"
#include "css/utils.h"
-#include "render/form_internal.h"
-#include "render/html.h"
-#include "render/box.h"
-#include "javascript/js.h"
-
-#include "desktop/browser_history.h"
-#include "desktop/browser_private.h"
-#include "desktop/download.h"
-#include "desktop/frames.h"
-#include "desktop/global_history.h"
-#include "desktop/hotlist.h"
-#include "desktop/knockout.h"
-#include "desktop/scrollbar.h"
-#include "desktop/selection.h"
-#include "desktop/theme.h"
-#include "desktop/gui_internal.h"
-#include "desktop/textinput.h"
-
-
-/** maximum frame depth */
-#define FRAME_DEPTH 8
-
-
-/**
- * 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
- */
-static inline void browser_window_get_scrollbar_pos(struct browser_window *bw,
- bool horizontal, int *x, int *y)
-{
- if (horizontal) {
- *x = 0;
- *y = bw->height - SCROLLBAR_WIDTH;
- } else {
- *x = bw->width - SCROLLBAR_WIDTH;
- *y = 0;
- }
-}
-
-
-/**
- * Get browser window scrollbar widget length
- *
- * \param bw The browser window
- * \param horizontal Whether to get length of horizontal scrollbar
- * \return the scrollbar's length
- */
-static inline int browser_window_get_scrollbar_len(struct browser_window *bw,
- bool horizontal)
-{
- if (horizontal)
- return bw->width - (bw->scroll_y != NULL ? SCROLLBAR_WIDTH : 0);
- else
- return bw->height;
-}
-
-
-/* exported interface, documented in browser.h */
-nserror
-browser_window_get_name(struct browser_window *bw, const char **out_name)
-{
- assert(bw != NULL);
-
- *out_name = bw->name;
-
- return NSERROR_OK;
-}
-
-
-/* exported interface, documented in browser.h */
-nserror
-browser_window_set_name(struct browser_window *bw, const char *name)
-{
- char *nname = NULL;
-
- assert(bw != NULL);
-
- if (name != NULL) {
- nname = strdup(name);
- if (nname == NULL) {
- return NSERROR_NOMEM;
- }
- }
-
- if (bw->name != NULL) {
- free(bw->name);
- }
-
- bw->name = nname;
-
- return NSERROR_OK;
-}
-
-
-/* exported interface, documented in browser.h */
-bool
-browser_window_redraw(struct browser_window *bw,
- int x, int y,
- const struct rect *clip,
- const struct redraw_context *ctx)
-{
- struct redraw_context new_ctx = *ctx;
- int width = 0;
- int height = 0;
- bool plot_ok = true;
- content_type content_type;
- struct content_redraw_data data;
- struct rect content_clip;
- nserror res;
-
- if (bw == NULL) {
- NSLOG(netsurf, INFO, "NULL browser window");
- return false;
- }
-
- if ((bw->current_content == NULL) &&
- (bw->children == NULL)) {
- /* Browser window has no content, render blank fill */
- ctx->plot->clip(ctx, clip);
- return (ctx->plot->rectangle(ctx, plot_style_fill_white, clip) == NSERROR_OK);
- }
-
- /* Browser window has content OR children (frames) */
- if ((bw->window != NULL) &&
- (ctx->plot->option_knockout)) {
- /* Root browser window: start knockout */
- knockout_plot_start(ctx, &new_ctx);
- }
-
- new_ctx.plot->clip(ctx, clip);
-
- /* Handle redraw of any browser window children */
- if (bw->children) {
- struct browser_window *child;
- int cur_child;
- int children = bw->rows * bw->cols;
-
- if (bw->window != NULL) {
- /* Root browser window; start with blank fill */
- plot_ok &= (new_ctx.plot->rectangle(ctx,
- plot_style_fill_white,
- clip) == NSERROR_OK);
- }
-
- /* Loop through all children of bw */
- for (cur_child = 0; cur_child < children; cur_child++) {
- /* Set current child */
- child = &bw->children[cur_child];
-
- /* Get frame edge box 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 +
- child->width * child->scale;
- content_clip.y1 = content_clip.y0 +
- child->height * child->scale;
-
- /* Intersect it with clip rectangle */
- if (content_clip.x0 < clip->x0)
- content_clip.x0 = clip->x0;
- if (content_clip.y0 < clip->y0)
- content_clip.y0 = clip->y0;
- if (clip->x1 < content_clip.x1)
- content_clip.x1 = clip->x1;
- if (clip->y1 < content_clip.y1)
- content_clip.y1 = clip->y1;
-
- /* Skip this frame if it lies outside clip rectangle */
- if (content_clip.x0 >= content_clip.x1 ||
- content_clip.y0 >= content_clip.y1)
- continue;
-
- /* Redraw frame */
- plot_ok &= browser_window_redraw(child,
- x + child->x, y + child->y,
- &content_clip, &new_ctx);
- }
-
- /* Nothing else to redraw for browser windows with children;
- * cleanup and return
- */
- if (bw->window != NULL && ctx->plot->option_knockout) {
- /* Root browser window: knockout end */
- knockout_plot_end(ctx);
- }
-
- return plot_ok;
- }
-
- /* Handle browser windows with content to redraw */
-
- content_type = content_get_type(bw->current_content);
- if (content_type != CONTENT_HTML && content_type != CONTENT_TEXTPLAIN) {
- /* Set render area according to scale */
- width = content_get_width(bw->current_content) * bw->scale;
- height = content_get_height(bw->current_content) * bw->scale;
-
- /* Non-HTML may not fill viewport to extents, so plot white
- * background fill */
- plot_ok &= (new_ctx.plot->rectangle(&new_ctx,
- plot_style_fill_white,
- clip) == NSERROR_OK);
- }
-
- /* Set up content redraw data */
- data.x = x - scrollbar_get_offset(bw->scroll_x);
- data.y = y - scrollbar_get_offset(bw->scroll_y);
- data.width = width;
- data.height = height;
-
- data.background_colour = 0xFFFFFF;
- data.scale = bw->scale;
- data.repeat_x = false;
- data.repeat_y = false;
-
- content_clip = *clip;
-
- if (!bw->window) {
- int x0 = x * bw->scale;
- int y0 = y * bw->scale;
- int x1 = (x + bw->width - ((bw->scroll_y != NULL) ?
- SCROLLBAR_WIDTH : 0)) * bw->scale;
- int y1 = (y + bw->height - ((bw->scroll_x != NULL) ?
- SCROLLBAR_WIDTH : 0)) * bw->scale;
-
- if (content_clip.x0 < x0) content_clip.x0 = x0;
- if (content_clip.y0 < y0) content_clip.y0 = y0;
- if (x1 < content_clip.x1) content_clip.x1 = x1;
- if (y1 < content_clip.y1) content_clip.y1 = y1;
- }
-
- /* Render the content */
- plot_ok &= content_redraw(bw->current_content, &data,
- &content_clip, &new_ctx);
-
- /* Back to full clip rect */
- new_ctx.plot->clip(&new_ctx, clip);
-
- if (!bw->window) {
- /* Render scrollbars */
- int off_x, off_y;
- if (bw->scroll_x != NULL) {
- browser_window_get_scrollbar_pos(bw, true,
- &off_x, &off_y);
- res = scrollbar_redraw(bw->scroll_x,
- x + off_x, y + off_y, clip,
- bw->scale, &new_ctx);
- if (res != NSERROR_OK) {
- plot_ok = false;
- }
- }
- if (bw->scroll_y != NULL) {
- browser_window_get_scrollbar_pos(bw, false,
- &off_x, &off_y);
- res = scrollbar_redraw(bw->scroll_y,
- x + off_x, y + off_y, clip,
- bw->scale, &new_ctx);
- if (res != NSERROR_OK) {
- plot_ok = false;
- }
- }
- }
-
- if (bw->window != NULL && ctx->plot->option_knockout) {
- /* Root browser window: end knockout */
- knockout_plot_end(ctx);
- }
-
- return plot_ok;
-}
-
-
-/* exported interface, documented in browser.h */
-bool browser_window_redraw_ready(struct browser_window *bw)
-{
- if (bw == NULL) {
- NSLOG(netsurf, INFO, "NULL browser window");
- return false;
- } else if (bw->current_content != NULL) {
- /* Can't render locked contents */
- return !content_is_locked(bw->current_content);
- }
-
- return true;
-}
-
-
-/* exported interface, documented in browser_private.h */
-void browser_window_update_extent(struct browser_window *bw)
-{
- if (bw->window != NULL)
- /* Front end window */
- guit->window->update_extent(bw->window);
- else
- /* Core-managed browser window */
- browser_window_handle_scrollbars(bw);
-}
-
-
-/* exported interface, documented in browser.h */
-void
-browser_window_get_position(struct browser_window *bw,
- bool root,
- int *pos_x,
- int *pos_y)
-{
- *pos_x = 0;
- *pos_y = 0;
-
- assert(bw != NULL);
-
- while (bw) {
- switch (bw->browser_window_type) {
-
- case BROWSER_WINDOW_FRAMESET:
- *pos_x += bw->x * bw->scale;
- *pos_y += bw->y * bw->scale;
- break;
-
- case BROWSER_WINDOW_NORMAL:
- /* There is no offset to the root browser window */
- break;
-
- case BROWSER_WINDOW_FRAME:
- /* Iframe and Frame handling is identical;
- * fall though */
- case BROWSER_WINDOW_IFRAME:
- *pos_x += (bw->x - scrollbar_get_offset(bw->scroll_x)) *
- bw->scale;
- *pos_y += (bw->y - scrollbar_get_offset(bw->scroll_y)) *
- bw->scale;
- break;
- }
-
- bw = bw->parent;
-
- if (!root) {
- /* return if we just wanted the position in the parent
- * browser window. */
- return;
- }
- }
-}
-
-
-/* exported interface, documented in browser.h */
-void browser_window_set_position(struct browser_window *bw, int x, int y)
-{
- assert(bw != NULL);
-
- if (bw->window == NULL) {
- /* Core managed browser window */
- bw->x = x;
- bw->y = y;
- } else {
- NSLOG(netsurf, INFO,
- "Asked to set position of front end window.");
- assert(0);
- }
-}
-
-/* exported interface, documented in browser.h */
-void
-browser_window_set_drag_type(struct browser_window *bw,
- browser_drag_type type,
- const struct rect *rect)
-{
- struct browser_window *top_bw = browser_window_get_root(bw);
- gui_drag_type gtype;
-
- bw->drag.type = type;
-
- if (type == DRAGGING_NONE) {
- top_bw->drag.window = NULL;
- } else {
- top_bw->drag.window = bw;
-
- switch (type) {
- case DRAGGING_SELECTION:
- /** \todo tell front end */
- return;
- case DRAGGING_SCR_X:
- case DRAGGING_SCR_Y:
- case DRAGGING_CONTENT_SCROLLBAR:
- gtype = GDRAGGING_SCROLLBAR;
- break;
- default:
- gtype = GDRAGGING_OTHER;
- break;
- }
-
- guit->window->drag_start(top_bw->window, gtype, rect);
- }
-}
-
-/* exported interface, documented in browser.h */
-browser_drag_type browser_window_get_drag_type(struct browser_window *bw)
-{
- return bw->drag.type;
-}
-
-/* exported interface, documented in browser.h */
-struct browser_window * browser_window_get_root(struct browser_window *bw)
-{
- while (bw && bw->parent) {
- bw = bw->parent;
- }
- return bw;
-}
-
-/* exported interface, documented in browser.h */
-browser_editor_flags browser_window_get_editor_flags(struct browser_window *bw)
-{
- browser_editor_flags ed_flags = BW_EDITOR_NONE;
- assert(bw->window);
- assert(bw->parent == NULL);
-
- if (bw->selection.bw != NULL) {
- ed_flags |= BW_EDITOR_CAN_COPY;
-
- if (!bw->selection.read_only)
- ed_flags |= BW_EDITOR_CAN_CUT;
- }
-
- if (bw->can_edit)
- ed_flags |= BW_EDITOR_CAN_PASTE;
-
- return ed_flags;
-}
-
-/* exported interface, documented in browser.h */
-bool browser_window_can_select(struct browser_window *bw)
-{
- if (bw == NULL || bw->current_content == NULL)
- return false;
-
- /* TODO: We shouldn't have to know about specific content types
- * here. There should be a content_is_selectable() call. */
- if (content_get_type(bw->current_content) != CONTENT_HTML &&
- content_get_type(bw->current_content) !=
- CONTENT_TEXTPLAIN)
- return false;
-
- return true;
-}
-
-/* exported interface, documented in browser.h */
-char * browser_window_get_selection(struct browser_window *bw)
-{
- assert(bw->window);
- assert(bw->parent == NULL);
-
- if (bw->selection.bw == NULL ||
- bw->selection.bw->current_content == NULL)
- return NULL;
-
- return content_get_selection(bw->selection.bw->current_content);
-}
-
-/* exported interface, documented in netsurf/browser_window.h */
-bool browser_window_can_search(struct browser_window *bw)
-{
- if (bw == NULL || bw->current_content == NULL)
- return false;
-
- /** \todo We shouldn't have to know about specific content
- * types here. There should be a content_is_searchable() call.
- */
- if ((content_get_type(bw->current_content) != CONTENT_HTML) &&
- (content_get_type(bw->current_content) != CONTENT_TEXTPLAIN)) {
- return false;
- }
-
- return true;
-}
-
-
-/* exported interface, documented in netsurf/browser_window.h */
-bool browser_window_is_frameset(struct browser_window *bw)
-{
- return (bw->children != NULL);
-}
-
-
-/* exported interface, documented in netsurf/browser_window.h */
-nserror browser_window_get_scrollbar_type(struct browser_window *bw,
- browser_scrolling *h, browser_scrolling *v)
-{
- *h = bw->scrolling;
- *v = bw->scrolling;
-
- return NSERROR_OK;
-}
-
-/**
- * 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)
- */
-static void browser_window_set_selection(struct browser_window *bw,
- bool selection, bool read_only)
-{
- struct browser_window *top;
-
- assert(bw != NULL);
-
- top = browser_window_get_root(bw);
-
- assert(top != 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);
- }
-
- if (selection) {
- top->selection.bw = bw;
- } else {
- top->selection.bw = NULL;
- }
-
- top->selection.read_only = read_only;
-}
-
-
-/**
- * Set the scroll position of a browser window.
- *
- * scrolls the viewport to ensure the specified rectangle of the
- * content is shown.
- *
- * \param bw window to scroll
- * \param rect The rectangle to ensure is shown.
- * \return NSERROR_OK on success or apropriate error code.
- */
-static nserror
-browser_window_set_scroll(struct browser_window *bw,
- const struct rect *rect)
-{
- if (bw->window != NULL) {
- return guit->window->set_scroll(bw->window, rect);
- }
-
- if (bw->scroll_x != NULL) {
- scrollbar_set(bw->scroll_x, rect->x0, false);
- }
- if (bw->scroll_y != NULL) {
- scrollbar_set(bw->scroll_y, rect->y0, false);
- }
-
- return NSERROR_OK;
-}
-
-/**
- * Internal helper for getting the positional features
- *
- * \param[in] bw browser window to examine.
- * \param[in] x x-coordinate of point of interest
- * \param[in] y y-coordinate of point of interest
- * \param[out] data Feature structure to update.
- * \return NSERROR_OK or appropriate error code on faliure.
- */
-static nserror
-browser_window__get_contextual_content(struct browser_window *bw,
- int x, int y, struct browser_window_features *data)
-{
- nserror ret = NSERROR_OK;
-
- /* Handle (i)frame scroll offset (core-managed browser windows only) */
- x += scrollbar_get_offset(bw->scroll_x);
- y += scrollbar_get_offset(bw->scroll_y);
-
- if (bw->children) {
- /* Browser window has children, so pass request on to
- * appropriate child.
- */
- struct browser_window *bwc;
- int cur_child;
- int children = bw->rows * bw->cols;
-
- /* Loop through all children of bw */
- for (cur_child = 0; cur_child < children; cur_child++) {
- /* Set current child */
- bwc = &bw->children[cur_child];
-
- /* Skip this frame if (x, y) coord lies outside */
- if ((x < bwc->x) ||
- (bwc->x + bwc->width < x) ||
- (y < bwc->y) ||
- (bwc->y + bwc->height < y)) {
- continue;
- }
-
- /* Pass request into this child */
- return browser_window__get_contextual_content(bwc,
- (x - bwc->x), (y - bwc->y), data);
- }
-
- /* Coordinate not contained by any frame */
-
- } else if (bw->current_content != NULL) {
- /* Pass request to content */
- ret = content_get_contextual_content(bw->current_content,
- x, y, data);
- data->main = bw->current_content;
- }
-
- return ret;
-}
-
-/* exported interface, documented in browser.h */
-nserror browser_window_get_features(struct browser_window *bw,
- int x, int y, struct browser_window_features *data)
-{
- /* clear the features structure to empty values */
- data->link = NULL;
- data->object = NULL;
- data->main = NULL;
- data->form_features = CTX_FORM_NONE;
-
- return browser_window__get_contextual_content(bw, x, y, data);
-}
-
-/* exported interface, documented in browser.h */
-bool browser_window_scroll_at_point(struct browser_window *bw,
- int x, int y, int scrx, int scry)
-{
- bool handled_scroll = false;
- assert(bw != NULL);
-
- /* Handle (i)frame scroll offset (core-managed browser windows only) */
- x += scrollbar_get_offset(bw->scroll_x);
- y += scrollbar_get_offset(bw->scroll_y);
-
- if (bw->children) {
- /* Browser window has children, so pass request on to
- * appropriate child */
- struct browser_window *bwc;
- int cur_child;
- int children = bw->rows * bw->cols;
-
- /* Loop through all children of bw */
- for (cur_child = 0; cur_child < children; cur_child++) {
- /* Set current child */
- bwc = &bw->children[cur_child];
-
- /* Skip this frame if (x, y) coord lies outside */
- if (x < bwc->x || bwc->x + bwc->width < x ||
- y < bwc->y || bwc->y + bwc->height < y)
- continue;
-
- /* Pass request into this child */
- return browser_window_scroll_at_point(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)
- /* 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))
- handled_scroll = true;
-
- if (bw->scroll_x && scrollbar_scroll(bw->scroll_x, scrx))
- handled_scroll = true;
- }
-
- return handled_scroll;
-}
-
-/* exported interface, documented in browser.h */
-bool browser_window_drop_file_at_point(struct browser_window *bw,
- int x, int y, char *file)
-{
- assert(bw != NULL);
-
- /* Handle (i)frame scroll offset (core-managed browser windows only) */
- x += scrollbar_get_offset(bw->scroll_x);
- y += scrollbar_get_offset(bw->scroll_y);
-
- if (bw->children) {
- /* Browser window has children, so pass request on to
- * appropriate child */
- struct browser_window *bwc;
- int cur_child;
- int children = bw->rows * bw->cols;
-
- /* Loop through all children of bw */
- for (cur_child = 0; cur_child < children; cur_child++) {
- /* Set current child */
- bwc = &bw->children[cur_child];
-
- /* Skip this frame if (x, y) coord lies outside */
- if (x < bwc->x || bwc->x + bwc->width < x ||
- y < bwc->y || bwc->y + bwc->height < y)
- continue;
-
- /* Pass request into this child */
- return browser_window_drop_file_at_point(bwc,
- (x - bwc->x), (y - bwc->y),
- file);
- }
- }
-
- /* Pass file drop on to any content */
- if (bw->current_content != NULL)
- return content_drop_file_at_point(bw->current_content,
- x, y, file);
-
- return false;
-}
-
-/* exported interface, documented in netsurf/browser_window.h */
-void browser_window_set_gadget_filename(struct browser_window *bw,
- struct form_control *gadget, const char *fn)
-{
- html_set_file_gadget_filename(bw->current_content, gadget, fn);
-}
-
-/* exported interface, documented in browser.h */
-nserror browser_window_debug_dump(struct browser_window *bw,
- FILE *f, enum content_debug op)
-{
- if (bw->current_content != NULL) {
- return content_debug_dump(bw->current_content, f, op);
- }
- return NSERROR_OK;
-}
-
-/* exported interface, documented in browser.h */
-nserror browser_window_debug(struct browser_window *bw, enum content_debug op)
-{
- if (bw->current_content != NULL) {
- return content_debug(bw->current_content, op);
- }
- return NSERROR_OK;
-}
-
-/** 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;
-}
-
-/* exported interface, documented in netsurf/browser_window.h */
-nserror browser_window_create(enum browser_window_create_flags flags,
- nsurl *url, nsurl *referrer,
- struct browser_window *existing,
- struct browser_window **bw)
-{
- gui_window_create_flags gw_flags = GW_CREATE_NONE;
- struct browser_window *ret;
- nserror err;
-
- /* Check parameters */
- if (flags & BW_CREATE_CLONE) {
- if (existing == NULL) {
- assert(0 && "Failed: No existing window provided.");
- return NSERROR_BAD_PARAMETER;
- }
- }
- if (!(flags & BW_CREATE_HISTORY)) {
- if (!(flags & BW_CREATE_CLONE) || existing == NULL) {
- assert(0 && "Failed: Must have existing for history.");
- return NSERROR_BAD_PARAMETER;
- }
- }
-
-
- if ((ret = calloc(1, sizeof(struct browser_window))) == NULL) {
- return NSERROR_NOMEM;
- }
-
- /* Initialise common parts */
- err = browser_window_initialise_common(flags, ret, existing);
- if (err != NSERROR_OK) {
- browser_window_destroy(ret);
- return err;
- }
-
- /* window characteristics */
- ret->browser_window_type = BROWSER_WINDOW_NORMAL;
- ret->scrolling = BW_SCROLLING_YES;
- ret->border = true;
- ret->no_resize = true;
- ret->focus = ret;
-
- /* initialise last action with creation time */
- nsu_getmonotonic_ms(&ret->last_action);
-
- /* The existing gui_window is on the top-level existing
- * browser_window. */
- existing = browser_window_get_root(existing);
-
- /* Set up gui_window creation flags */
- if (flags & BW_CREATE_TAB)
- gw_flags |= GW_CREATE_TAB;
- if (flags & BW_CREATE_CLONE)
- gw_flags |= GW_CREATE_CLONE;
-
- ret->window = guit->window->create(ret,
- (existing != NULL) ? existing->window : NULL,
- gw_flags);
-
- if (ret->window == NULL) {
- browser_window_destroy(ret);
- return NSERROR_BAD_PARAMETER;
- }
-
- if (url != NULL) {
- enum browser_window_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)
- nav_flags |= BW_NAVIGATE_HISTORY;
- browser_window_navigate(ret, url, referrer, nav_flags, NULL,
- NULL, NULL);
- }
-
- if (bw != NULL) {
- *bw = ret;
- }
-
- return NSERROR_OK;
-}
-
-
-/* exported internal interface, documented in desktop/browser_private.h */
-nserror browser_window_initialise_common(enum browser_window_create_flags flags,
- struct browser_window *bw, 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);
- if (err != NSERROR_OK)
- return err;
-
- if (flags & BW_CREATE_CLONE) {
- assert(existing != NULL);
-
- /* clone history */
- err = browser_window_history_clone(existing, bw);
-
- /* copy the scale */
- bw->scale = existing->scale;
- } else {
- /* create history */
- err = browser_window_history_create(bw);
-
- /* default scale */
- bw->scale = (float) nsoption_int(scale) / 100.0;
- }
-
- if (err != NSERROR_OK)
- return err;
-
- /* window characteristics */
- bw->refresh_interval = -1;
-
- bw->drag.type = DRAGGING_NONE;
-
- bw->scroll_x = NULL;
- bw->scroll_y = NULL;
-
- bw->focus = NULL;
-
- /* initialise status text cache */
- bw->status.text = NULL;
- bw->status.text_len = 0;
- bw->status.match = 0;
- bw->status.miss = 0;
-
- return NSERROR_OK;
-}
-
-/**
- * implements the download operation of a window navigate
- */
-static nserror
-browser_window_download(struct browser_window *bw,
- nsurl *url,
- nsurl *nsref,
- uint32_t fetch_flags,
- bool fetch_is_post,
- llcache_post_data *post)
-{
- llcache_handle *l;
- struct browser_window *root;
- nserror error;
-
- root = browser_window_get_root(bw);
- assert(root != NULL);
-
- fetch_flags |= LLCACHE_RETRIEVE_FORCE_FETCH;
- fetch_flags |= LLCACHE_RETRIEVE_STREAM_DATA;
-
- error = llcache_handle_retrieve(url, fetch_flags, nsref,
- fetch_is_post ? post : NULL,
- NULL, NULL, &l);
- if (error == NSERROR_NO_FETCH_HANDLER) {
- /* no internal handler for this type, call out to frontend */
- error = guit->misc->launch_url(url);
- } else if (error != NSERROR_OK) {
- NSLOG(netsurf, INFO, "Failed to fetch download: %d", error);
- } else {
- error = download_context_create(l, root->window);
- if (error != NSERROR_OK) {
- NSLOG(netsurf, INFO,
- "Failed creating download context: %d", error);
- llcache_handle_abort(l);
- llcache_handle_release(l);
- }
- }
-
- return error;
-}
-
-
-static bool browser_window_check_throbber(struct browser_window *bw)
-{
- int children, index;
-
- if (bw->throbbing)
- return true;
-
- if (bw->children) {
- children = bw->rows * bw->cols;
- for (index = 0; index < children; index++) {
- if (browser_window_check_throbber(&bw->children[index]))
- return true;
- }
- }
- if (bw->iframes) {
- for (index = 0; index < bw->iframe_count; index++) {
- if (browser_window_check_throbber(&bw->iframes[index]))
- return true;
- }
- }
- return false;
-}
-
-
-/**
- * Start the busy indicator.
- *
- * \param bw browser window
- */
-
-static void browser_window_start_throbber(struct browser_window *bw)
-{
- bw->throbbing = true;
-
- while (bw->parent)
- bw = bw->parent;
-
- guit->window->start_throbber(bw->window);
-}
-
-
-/**
- * Stop the busy indicator.
- *
- * \param bw browser window
- */
-static void browser_window_stop_throbber(struct browser_window *bw)
-{
- bw->throbbing = false;
-
- while (bw->parent)
- bw = bw->parent;
-
- if (!browser_window_check_throbber(bw)) {
- guit->window->stop_throbber(bw->window);
- }
-}
-
-
-
-/**
- * Callback for fetchcache() for browser window favicon fetches.
- *
- * \param c content handle of favicon
- * \param event The event to process
- * \param pw a context containing the browser window
- * \return NSERROR_OK on success else appropriate error code.
- */
-static nserror
-browser_window_favicon_callback(hlcache_handle *c,
- const hlcache_event *event,
- void *pw)
-{
- struct browser_window *bw = pw;
-
- switch (event->type) {
- case CONTENT_MSG_DONE:
- if (bw->favicon.current != NULL) {
- content_status status =
- content_get_status(bw->favicon.current);
-
- if ((status == CONTENT_STATUS_READY) ||
- (status == CONTENT_STATUS_DONE))
- content_close(bw->favicon.current);
-
- hlcache_handle_release(bw->favicon.current);
- }
-
- bw->favicon.current = c;
- bw->favicon.loading = NULL;
-
- /* content_get_bitmap on the hlcache_handle should give
- * the favicon bitmap at this point
- */
- guit->window->set_icon(bw->window, c);
- break;
-
- case CONTENT_MSG_ERROR:
- case CONTENT_MSG_ERRORCODE:
-
- /* clean up after ourselves */
- if (c == bw->favicon.loading) {
- bw->favicon.loading = NULL;
- } else if (c == bw->favicon.current) {
- bw->favicon.current = NULL;
- }
-
- hlcache_handle_release(c);
-
- if (bw->favicon.failed == false) {
- nsurl *nsref = NULL;
- nsurl *nsurl;
- nserror error;
-
- bw->favicon.failed = true;
-
- error = nsurl_create("resource:favicon.ico", &nsurl);
- if (error != NSERROR_OK) {
- NSLOG(netsurf, INFO,
- "Unable to create default location url");
- } else {
- hlcache_handle_retrieve(nsurl,
- HLCACHE_RETRIEVE_SNIFF_TYPE,
- nsref, NULL,
- browser_window_favicon_callback,
- bw, NULL, CONTENT_IMAGE,
- &bw->favicon.loading);
-
- nsurl_unref(nsurl);
- }
-
- }
- break;
-
- default:
- break;
- }
- return NSERROR_OK;
-}
-
-
-/**
- * update the favicon associated with the browser window
- *
- * \param c the page content handle.
- * \param bw A top level browser window.
- * \param link A link context or NULL to attempt fallback scanning.
- */
-static void
-browser_window_update_favicon(hlcache_handle *c,
- struct browser_window *bw,
- struct content_rfc5988_link *link)
-{
- nsurl *nsref = NULL;
- nsurl *nsurl;
- nserror error;
-
- assert(c != NULL);
- assert(bw !=NULL);
-
- if (bw->window == NULL)
- /* Not top-level browser window; not interested */
- return;
-
- /* already fetching the favicon - use that */
- if (bw->favicon.loading != NULL)
- return;
-
- bw->favicon.failed = false;
-
- if (link == NULL) {
- /* Look for "icon" */
- link = content_find_rfc5988_link(c, corestring_lwc_icon);
- }
-
- if (link == NULL) {
- /* Look for "shortcut icon" */
- link = content_find_rfc5988_link(c,
- corestring_lwc_shortcut_icon);
- }
-
- if (link == NULL) {
- lwc_string *scheme;
- bool speculative_default = false;
- bool match;
-
- nsurl = hlcache_handle_get_url(c);
-
- scheme = nsurl_get_component(nsurl, NSURL_SCHEME);
-
- /* If the document was fetched over http(s), then speculate
- * that there's a favicon living at /favicon.ico */
- if ((lwc_string_caseless_isequal(scheme, corestring_lwc_http,
- &match) == lwc_error_ok && match) ||
- (lwc_string_caseless_isequal(scheme, corestring_lwc_https,
- &match) == lwc_error_ok && match)) {
- speculative_default = true;
- }
-
- lwc_string_unref(scheme);
-
- if (speculative_default) {
- /* no favicon via link, try for the default location */
- error = nsurl_join(nsurl, "/favicon.ico", &nsurl);
- } else {
- bw->favicon.failed = true;
- error = nsurl_create("resource:favicon.ico", &nsurl);
- }
- if (error != NSERROR_OK) {
- NSLOG(netsurf, INFO,
- "Unable to create default location url");
- return;
- }
- } else {
- nsurl = nsurl_ref(link->href);
- }
-
- if (link == NULL) {
- 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));
- }
-
- hlcache_handle_retrieve(nsurl, HLCACHE_RETRIEVE_SNIFF_TYPE,
- nsref, NULL, browser_window_favicon_callback,
- bw, NULL, CONTENT_IMAGE, &bw->favicon.loading);
-
- nsurl_unref(nsurl);
-}
-
-/**
- * window callback errorcode handling.
- */
-static void
-browser_window_callback_errorcode(hlcache_handle *c,
- struct browser_window *bw,
- nserror code)
-{
- const char* message;
-
- message = messages_get_errorcode(code);
-
- 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);
- }
-
- if (c == bw->loading_content) {
- bw->loading_content = NULL;
- } else if (c == bw->current_content) {
- bw->current_content = NULL;
- browser_window_remove_caret(bw, false);
- }
-
- hlcache_handle_release(c);
-
- browser_window_stop_throbber(bw);
-}
-
-
-/**
- * Handle meta http-equiv refresh time elapsing by loading a new page.
- *
- * \param p browser window to refresh with new page
- */
-static void browser_window_refresh(void *p)
-{
- struct browser_window *bw = p;
- nsurl *url;
- nsurl *refresh;
- hlcache_handle *parent = NULL;
- enum browser_window_nav_flags flags = BW_NAVIGATE_UNVERIFIABLE;
-
- assert(bw->current_content != NULL &&
- (content_get_status(bw->current_content) ==
- CONTENT_STATUS_READY ||
- content_get_status(bw->current_content) ==
- CONTENT_STATUS_DONE));
-
- /* Ignore if the refresh URL has gone
- * (may happen if a fetch error occurred) */
- refresh = content_get_refresh_url(bw->current_content);
- if (refresh == NULL)
- return;
-
- /* mark this content as invalid so it gets flushed from the cache */
- content_invalidate_reuse_data(bw->current_content);
-
- url = hlcache_handle_get_url(bw->current_content);
- if ((url == NULL) || (nsurl_compare(url, refresh, NSURL_COMPLETE))) {
- flags |= BW_NAVIGATE_HISTORY;
- }
-
- /* Treat an (almost) immediate refresh in a top-level browser window as
- * if it were an HTTP redirect, and thus make the resulting fetch
- * verifiable.
- *
- * See fetchcache.c for why redirected fetches should be verifiable at
- * all.
- */
- if (bw->refresh_interval <= 100 && bw->parent == NULL) {
- flags &= ~BW_NAVIGATE_UNVERIFIABLE;
- } else {
- parent = bw->current_content;
- }
-
- browser_window_navigate(bw,
- refresh,
- url,
- flags,
- NULL,
- NULL,
- parent);
-
-}
-
-
-/**
- * Transfer the loading_content to a new download window.
- */
-
-static void browser_window_convert_to_download(struct browser_window *bw,
- llcache_handle *stream)
-{
- struct browser_window *root = browser_window_get_root(bw);
- nserror error;
-
- assert(root != NULL);
-
- error = download_context_create(stream, root->window);
- if (error != NSERROR_OK) {
- llcache_handle_abort(stream);
- llcache_handle_release(stream);
- }
-
- /* remove content from browser window */
- hlcache_handle_release(bw->loading_content);
- bw->loading_content = NULL;
-
- browser_window_stop_throbber(bw);
-}
-
-
-/**
- * Browser window content event callback handler.
- */
-static nserror
-browser_window_callback(hlcache_handle *c,
- const hlcache_event *event,
- void *pw)
-{
- struct browser_window *bw = pw;
- nserror res = NSERROR_OK;
- float sx, sy;
-
- switch (event->type) {
- case CONTENT_MSG_DOWNLOAD:
- assert(bw->loading_content == c);
-
- browser_window_convert_to_download(bw, event->data.download);
-
- if (bw->current_content != NULL) {
- browser_window_refresh_url_bar(bw);
- }
- break;
-
- case CONTENT_MSG_LOADING:
- assert(bw->loading_content == c);
-
-#ifdef WITH_THEME_INSTALL
- if (content_get_type(c) == CONTENT_THEME) {
- theme_install_start(c);
- bw->loading_content = NULL;
- browser_window_stop_throbber(bw);
- } else
-#endif
- {
- bw->refresh_interval = -1;
- browser_window_set_status(bw,
- content_get_status_message(c));
- }
- break;
-
- case CONTENT_MSG_READY:
- {
- int width, height;
-
- assert(bw->loading_content == c);
-
- if (bw->current_content != NULL) {
- content_status status =
- content_get_status(bw->current_content);
-
- if (status == CONTENT_STATUS_READY ||
- status == CONTENT_STATUS_DONE)
- content_close(bw->current_content);
-
- hlcache_handle_release(bw->current_content);
- }
-
- bw->current_content = c;
- bw->loading_content = NULL;
-
- /* history */
- if (bw->history_add && bw->history) {
- nsurl *url = hlcache_handle_get_url(c);
-
- if (urldb_add_url(url)) {
- urldb_set_url_title(url, content_get_title(c));
- urldb_update_url_visit_data(url);
- urldb_set_url_content_type(url,
- content_get_type(c));
-
- /* This is safe as we've just added the URL */
- global_history_add(urldb_get_url(url));
- }
- /** \todo Urldb / Thumbnails / Local history brokenness
- *
- * We add to local history after calling urldb_add_url
- * rather than in the block above. If urldb_add_url
- * fails (as it will for urls like "about:about",
- * "about:config" etc), there would be no local history
- * node, and later calls to history_update will either
- * explode or overwrite the node for the previous URL.
- *
- * We call it after, rather than before urldb_add_url
- * because history_add calls bitmap render, which
- * tries to register the thumbnail with urldb. That
- * thumbnail registration fails if the url doesn't
- * exist in urldb already, and only urldb-registered
- * thumbnails get freed. So if we called history_add
- * before urldb_add_url we would leak thumbnails for
- * all newly visited URLs. With the history_add call
- * after, we only leak the thumbnails when urldb does
- * not add the URL.
- */
- browser_window_history_add(bw, c, bw->frag_id);
- }
-
- /* Format the new content to the correct dimensions */
- browser_window_get_dimensions(bw, &width, &height, true);
- content_reformat(c, false, width, height);
-
- browser_window_remove_caret(bw, false);
-
- if (bw->window != NULL) {
- guit->window->new_content(bw->window);
-
- browser_window_refresh_url_bar(bw);
- }
-
- /* new content; set scroll_to_top */
- browser_window_update(bw, true);
- content_open(c, bw, 0, 0);
- browser_window_set_status(bw, content_get_status_message(c));
-
- /* frames */
- if ((content_get_type(c) == CONTENT_HTML) &&
- (html_get_frameset(c) != NULL)) {
- res = browser_window_create_frameset(bw,
- html_get_frameset(c));
- }
- if (content_get_type(c) == CONTENT_HTML &&
- html_get_iframe(c) != NULL)
- browser_window_create_iframes(bw, html_get_iframe(c));
- }
- break;
-
- case CONTENT_MSG_DONE:
- assert(bw->current_content == c);
-
- if (bw->window == NULL) {
- /* Updated browser window's scrollbars.
- * TODO: do this before CONTENT_MSG_DONE */
- browser_window_reformat(bw, true,
- bw->width, bw->height);
- browser_window_handle_scrollbars(bw);
- }
-
- browser_window_update(bw, false);
- browser_window_set_status(bw, content_get_status_message(c));
- browser_window_stop_throbber(bw);
- browser_window_update_favicon(c, bw, NULL);
-
- if (browser_window_history_get_scroll(bw, &sx, &sy) == NSERROR_OK) {
- int scrollx = (int)((float)content_get_width(bw->current_content) * sx);
- int scrolly = (int)((float)content_get_height(bw->current_content) * sy);
- struct rect rect;
- rect.x0 = rect.x1 = scrollx;
- rect.y0 = rect.y1 = scrolly;
- if (browser_window_set_scroll(bw, &rect) != NSERROR_OK) {
- NSLOG(netsurf, WARNING,
- "Unable to set browser scroll offsets to %d by %d",
- scrollx, scrolly);
- }
- }
-
- browser_window_history_update(bw, c);
- hotlist_update_url(hlcache_handle_get_url(c));
-
- if (bw->refresh_interval != -1) {
- guit->misc->schedule(bw->refresh_interval * 10,
- browser_window_refresh, bw);
- }
- break;
-
- case CONTENT_MSG_ERRORCODE:
- browser_window_callback_errorcode(c, bw, event->data.errorcode);
- break;
-
- case CONTENT_MSG_ERROR:
- browser_window_set_status(bw, event->data.error);
-
- /* Only warn the user about errors in top-level windows */
- if (bw->browser_window_type == BROWSER_WINDOW_NORMAL) {
- guit->misc->warning(event->data.error, NULL);
- }
-
- if (c == bw->loading_content)
- bw->loading_content = NULL;
- else if (c == bw->current_content) {
- bw->current_content = NULL;
- browser_window_remove_caret(bw, false);
- }
-
- hlcache_handle_release(c);
-
- browser_window_stop_throbber(bw);
- break;
-
- case CONTENT_MSG_REDIRECT:
- if (urldb_add_url(event->data.redirect.from))
- urldb_update_url_visit_data(event->data.redirect.from);
- 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)
- /* Give preference to any loading content */
- status = content_get_status_message(
- bw->loading_content);
-
- if (status == NULL)
- status = content_get_status_message(c);
-
- 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);
- }
- 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);
- }
-
- /* Hide any caret, but don't remove it */
- browser_window_remove_caret(bw, true);
-
- if (!(event->data.background)) {
- /* Reformatted content should be redrawn */
- browser_window_update(bw, false);
- }
- 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;
-
- 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,
- event->data.rfc5988_link);
- }
- }
- 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;
- }
- 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;
- }
-
- 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_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 (save == NULL) {
- save = c;
- }
-
- 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_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;
-
- 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;
-
- case CONTENT_MSG_POINTER:
- /* Content wants to have specific mouse pointer */
- browser_window_set_pointer(bw, event->data.pointer);
- 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_window_set_drag_type(bw, bdt, event->data.drag.rect);
- }
- break;
-
- case CONTENT_MSG_CARET:
- switch (event->data.caret.type) {
- case CONTENT_CARET_REMOVE:
- browser_window_remove_caret(bw, false);
- break;
- case CONTENT_CARET_HIDE:
- browser_window_remove_caret(bw, true);
- break;
- case CONTENT_CARET_SET_POS:
- browser_window_place_caret(bw,
- event->data.caret.pos.x,
- event->data.caret.pos.y,
- event->data.caret.pos.height,
- event->data.caret.pos.clip);
- break;
- }
- break;
-
- case CONTENT_MSG_SELECTION:
- browser_window_set_selection(bw,
- event->data.selection.selection,
- event->data.selection.read_only);
- break;
-
- case CONTENT_MSG_SELECTMENU:
- if (event->data.select_menu.gadget->type == GADGET_SELECT) {
- struct browser_window *root =
- browser_window_get_root(bw);
- guit->window->create_form_select_menu(root->window,
- event->data.select_menu.gadget);
- }
-
- break;
-
- case CONTENT_MSG_GADGETCLICK:
- if (event->data.gadget_click.gadget->type == GADGET_FILE) {
- struct browser_window *root =
- browser_window_get_root(bw);
- guit->window->file_gadget_open(root->window, c,
- event->data.gadget_click.gadget);
- }
-
- break;
-
- default:
- break;
- }
- return res;
-}
-
-
-/* Have to forward declare browser_window_destroy_internal */
-static void browser_window_destroy_internal(struct browser_window *bw);
-
-/**
- * Close and destroy all child 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++)
- 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;
- }
-}
-
-
-/**
- * internal scheduled reformat callback.
- *
- * scheduled reformat callback to allow reformats from unthreaded context.
- *
- * \param vbw The browser window to be reformatted
- */
-static void scheduled_reformat(void *vbw)
-{
- struct browser_window *bw = vbw;
- int width;
- int height;
- nserror res;
-
- res = guit->window->get_dimensions(bw->window, &width, &height, false);
- if (res == NSERROR_OK) {
- browser_window_reformat(bw, false, width, height);
- }
-}
-
-
-/**
- * Release all memory associated with a browser window.
- *
- * \param bw browser window
- */
-static void 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);
- }
-
- /* Destroy scrollbars */
- if (bw->scroll_x != NULL) {
- scrollbar_destroy(bw->scroll_x);
- }
-
- if (bw->scroll_y != NULL) {
- scrollbar_destroy(bw->scroll_y);
- }
-
- /* clear any pending callbacks */
- guit->misc->schedule(-1, browser_window_refresh, bw);
- /* The ugly cast here is so the reformat function can be
- * passed a gui window pointer in its API rather than void*
- */
- NSLOG(netsurf, INFO,
- "Clearing reformat schedule for browser window %p", bw);
- guit->misc->schedule(-1, scheduled_reformat, bw);
-
- /* If this brower window is not the root window, and has focus, unset
- * the root browser window's focus pointer. */
- if (!bw->window) {
- struct browser_window *top = browser_window_get_root(bw);
-
- if (top->focus == bw)
- top->focus = top;
-
- if (top->selection.bw == bw) {
- browser_window_set_selection(top, false, false);
- }
- }
-
- /* Destruction order is important: we must ensure that the frontend
- * destroys any window(s) associated with this browser window before
- * we attempt any destructive cleanup.
- */
-
- if (bw->window) {
- /* Only the root window has a GUI window */
- guit->window->destroy(bw->window);
- }
-
- if (bw->loading_content != NULL) {
- hlcache_handle_abort(bw->loading_content);
- hlcache_handle_release(bw->loading_content);
- bw->loading_content = NULL;
- }
-
- if (bw->current_content != NULL) {
- content_status status = content_get_status(bw->current_content);
- if (status == CONTENT_STATUS_READY ||
- status == CONTENT_STATUS_DONE)
- content_close(bw->current_content);
-
- hlcache_handle_release(bw->current_content);
- bw->current_content = NULL;
- }
-
- if (bw->favicon.loading != NULL) {
- hlcache_handle_abort(bw->favicon.loading);
- hlcache_handle_release(bw->favicon.loading);
- bw->favicon.loading = NULL;
- }
-
- if (bw->favicon.current != NULL) {
- content_status status = content_get_status(bw->favicon.current);
-
- if (status == CONTENT_STATUS_READY ||
- status == CONTENT_STATUS_DONE) {
- content_close(bw->favicon.current);
- }
-
- hlcache_handle_release(bw->favicon.current);
- bw->favicon.current = NULL;
- }
-
- if (bw->box != NULL) {
- bw->box->iframe = NULL;
- bw->box = NULL;
- }
-
- if (bw->jsctx != NULL) {
- js_destroycontext(bw->jsctx);
- }
-
- /* These simply free memory, so are safe here */
-
- if (bw->frag_id != NULL) {
- lwc_string_unref(bw->frag_id);
- }
-
- browser_window_history_destroy(bw);
-
- free(bw->name);
- free(bw->status.text);
- bw->status.text = NULL;
- 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);
-}
-
-
-/* exported interface, documented in netsurf/browser_window.h */
-void browser_window_destroy(struct browser_window *bw)
-{
- /* can't destoy child windows on their own */
- assert(!bw->parent);
-
- /* destroy */
- browser_window_destroy_internal(bw);
- free(bw);
-}
-
-/* exported interface, documented in netsurf/browser_window.h */
-nserror browser_window_refresh_url_bar(struct browser_window *bw)
-{
- nserror ret;
- nsurl *display_url;
-
- assert(bw);
-
- if (bw->parent != NULL) {
- /* Not root window; don't set a URL in GUI URL bar */
- return NSERROR_OK;
- }
-
- 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->frag_id == NULL) {
- ret = browser_window_refresh_url_bar_internal(bw,
- hlcache_handle_get_url(bw->current_content));
- } else {
- /* Combine URL and Fragment */
- ret = nsurl_refragment(
- hlcache_handle_get_url(bw->current_content),
- bw->frag_id, &display_url);
- if (ret == NSERROR_OK) {
- ret = browser_window_refresh_url_bar_internal(bw,
- display_url);
- nsurl_unref(display_url);
- }
- }
-
- return ret;
-}
-
-
-/* exported interface documented in netsurf/browser_window.h */
-nserror
-browser_window_navigate(struct browser_window *bw,
- nsurl *url,
- nsurl *referrer,
- enum browser_window_nav_flags flags,
- char *post_urlenc,
- struct fetch_multipart_data *post_multipart,
- hlcache_handle *parent)
-{
- hlcache_handle *c;
- int depth = 0;
- struct browser_window *cur;
- uint32_t fetch_flags = 0;
- bool fetch_is_post = (post_urlenc != NULL || post_multipart != NULL);
- llcache_post_data post;
- hlcache_child_context child;
- nserror error;
-
- assert(bw);
- assert(url);
-
- NSLOG(netsurf, INFO, "bw %p, url %s", bw, nsurl_access(url));
-
- /* If we're navigating and we have a history entry and a content
- * then update the history entry before we navigate to save our
- * current state. However since history navigation pre-moves
- * the history state, we ensure that we only do this if we've not
- * been suppressed. In the suppressed case, the history code
- * updates the history itself before navigating.
- */
- if (bw->current_content != NULL &&
- bw->history != NULL &&
- bw->history->current != NULL &&
- !(flags & BW_NAVIGATE_NO_TERMINAL_HISTORY_UPDATE)) {
- browser_window_history_update(bw, bw->current_content);
- }
-
- /* don't allow massively nested framesets */
- for (cur = bw; cur->parent; cur = cur->parent) {
- depth++;
- }
- if (depth > FRAME_DEPTH) {
- NSLOG(netsurf, INFO, "frame depth too high.");
- return NSERROR_FRAME_DEPTH;
- }
-
- /* Set up retrieval parameters */
- if (!(flags & BW_NAVIGATE_UNVERIFIABLE)) {
- fetch_flags |= LLCACHE_RETRIEVE_VERIFIABLE;
- }
-
- if (post_multipart != NULL) {
- post.type = LLCACHE_POST_MULTIPART;
- post.data.multipart = post_multipart;
- } else if (post_urlenc != NULL) {
- post.type = LLCACHE_POST_URL_ENCODED;
- post.data.urlenc = post_urlenc;
- }
-
- child.charset = content_get_encoding(parent, CONTENT_ENCODING_NORMAL);
- if ((parent != NULL) && (content_get_type(parent) == CONTENT_HTML)) {
- child.quirks = content_get_quirks(parent);
- }
-
- url = nsurl_ref(url);
-
- if (referrer != NULL) {
- referrer = nsurl_ref(referrer);
- }
-
- /* Get download out of the way */
- if ((flags & BW_NAVIGATE_DOWNLOAD) != 0) {
- error = browser_window_download(bw,
- url,
- referrer,
- fetch_flags,
- fetch_is_post,
- &post);
- nsurl_unref(url);
- if (referrer != NULL) {
- nsurl_unref(referrer);
- }
- return error;
- }
-
- if (bw->frag_id != NULL) {
- lwc_string_unref(bw->frag_id);
- }
- bw->frag_id = NULL;
-
- if (nsurl_has_component(url, NSURL_FRAGMENT)) {
- bool same_url = false;
-
- bw->frag_id = nsurl_get_component(url, NSURL_FRAGMENT);
-
- /* 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);
- }
-
- /* if we're simply moving to another ID on the same page,
- * don't bother to fetch, just update the window.
- */
- if ((same_url) &&
- (fetch_is_post == false) &&
- (nsurl_has_component(url, NSURL_QUERY) == false)) {
- nsurl_unref(url);
-
- if (referrer != NULL) {
- nsurl_unref(referrer);
- }
-
- if ((flags & BW_NAVIGATE_HISTORY) != 0) {
- browser_window_history_add(bw,
- bw->current_content, bw->frag_id);
- }
-
- browser_window_update(bw, false);
-
- if (bw->current_content != NULL) {
- browser_window_refresh_url_bar(bw);
- }
- return NSERROR_OK;
- }
- }
-
- browser_window_stop(bw);
- browser_window_remove_caret(bw, false);
- browser_window_destroy_children(bw);
-
- NSLOG(netsurf, INFO, "Loading '%s'", nsurl_access(url));
-
- browser_window_set_status(bw, messages_get("Loading"));
- bw->history_add = (flags & BW_NAVIGATE_HISTORY);
-
- /* Verifiable fetches may trigger a download */
- if (!(flags & BW_NAVIGATE_UNVERIFIABLE)) {
- fetch_flags |= HLCACHE_RETRIEVE_MAY_DOWNLOAD;
- }
-
- error = hlcache_handle_retrieve(url,
- fetch_flags | HLCACHE_RETRIEVE_SNIFF_TYPE,
- referrer,
- fetch_is_post ? &post : NULL,
- browser_window_callback, bw,
- parent != NULL ? &child : NULL,
- CONTENT_ANY, &c);
-
- switch (error) {
- case NSERROR_OK:
- bw->loading_content = c;
- browser_window_start_throbber(bw);
- error = browser_window_refresh_url_bar_internal(bw, url);
- break;
-
- case NSERROR_NO_FETCH_HANDLER: /* no handler for this type */
- /** \todo does this always try and download even
- * unverifiable content?
- */
- error = guit->misc->launch_url(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);
- break;
-
- }
-
- nsurl_unref(url);
- if (referrer != NULL) {
- nsurl_unref(referrer);
- }
-
- /* Record time */
- nsu_getmonotonic_ms(&bw->last_action);
-
- return error;
-}
-
-
-/* Exported interface, documented in browser.h */
-bool browser_window_up_available(struct browser_window *bw)
-{
- bool result = false;
-
- if (bw != NULL && bw->current_content != NULL) {
- nsurl *parent;
- nserror 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), parent,
- NSURL_COMPLETE) == false;
- nsurl_unref(parent);
- }
- }
-
- return result;
-}
-
-
-/* Exported interface, documented in browser.h */
-nserror browser_window_navigate_up(struct browser_window *bw, bool new_window)
-{
- nsurl *current, *parent;
- nserror err;
-
- if (bw == NULL)
- return NSERROR_BAD_PARAMETER;
-
- current = browser_window_get_url(bw);
-
- err = nsurl_parent(current, &parent);
- if (err != NSERROR_OK) {
- return err;
- }
-
- if (nsurl_compare(current, parent, NSURL_COMPLETE) == true) {
- /* Can't go up to parent from here */
- nsurl_unref(parent);
- return NSERROR_OK;
- }
-
- if (new_window) {
- err = browser_window_create(BW_CREATE_CLONE,
- parent, NULL, bw, NULL);
- } else {
- err = browser_window_navigate(bw, parent, NULL,
- BW_NAVIGATE_HISTORY, NULL, NULL, NULL);
- }
-
- nsurl_unref(parent);
- return err;
-}
-
-
-/* Exported interface, documented in browser.h */
-nsurl* browser_window_get_url(struct browser_window *bw)
-{
- assert(bw != NULL);
-
- if (bw->current_content != NULL) {
- return hlcache_handle_get_url(bw->current_content);
-
- } else if (bw->loading_content != NULL) {
- /* TODO: should we return this? */
- return hlcache_handle_get_url(bw->loading_content);
- }
-
- return corestring_nsurl_about_blank;
-}
-
-/* Exported interface, documented in browser.h */
-const char* browser_window_get_title(struct browser_window *bw)
-{
- assert(bw != NULL);
-
- if (bw->current_content != NULL) {
- return content_get_title(bw->current_content);
- }
-
- /* no content so return about:blank */
- return nsurl_access(corestring_nsurl_about_blank);
-}
-
-/* Exported interface, documented in browser.h */
-struct history * browser_window_get_history(struct browser_window *bw)
-{
- assert(bw != NULL);
-
- return bw->history;
-}
-
-
-/* Exported interface, documented in browser.h */
-bool browser_window_has_content(struct browser_window *bw)
-{
- assert(bw != NULL);
-
- if (bw->current_content == NULL) {
- return false;
- }
-
- return true;
-}
-
-/* Exported interface, documented in browser.h */
-struct hlcache_handle *browser_window_get_content(struct browser_window *bw)
-{
- return bw->current_content;
-}
-
-/* Exported interface, documented in browser.h */
-nserror browser_window_get_extents(struct browser_window *bw, bool scaled,
- int *width, int *height)
-{
- assert(bw != NULL);
-
- if (bw->current_content == NULL) {
- *width = 0;
- *height = 0;
- return NSERROR_BAD_CONTENT;
- }
-
- *width = content_get_width(bw->current_content);
- *height = content_get_height(bw->current_content);
-
- if (scaled) {
- *width *= bw->scale;
- *height *= bw->scale;
- }
-
- return NSERROR_OK;
-}
-
-
-/* exported internal interface, documented in desktop/browser_private.h */
-void browser_window_get_dimensions(struct browser_window *bw,
- int *width, int *height, bool scaled)
-{
- assert(bw);
-
- if (bw->window == NULL) {
- /* Core managed browser window */
- *width = bw->width;
- *height = bw->height;
- } else {
- /* Front end window */
- guit->window->get_dimensions(bw->window, width, height, scaled);
- }
-}
-
-
-/* Exported interface, documented in browser.h */
-void browser_window_set_dimensions(struct browser_window *bw,
- int width, int height)
-{
- assert(bw);
-
- if (bw->window == NULL) {
- /* Core managed browser window */
- bw->width = width;
- bw->height = height;
- } else {
- NSLOG(netsurf, INFO,
- "Asked to set dimensions of front end window.");
- assert(0);
- }
-}
-
-
-/**
- * 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;
-}
-
-/* Exported interface, documented in browser.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)
-{
- int pos_x;
- int pos_y;
- struct browser_window *top;
-
- assert(bw);
-
- if (bw->window != NULL) {
- /* Front end window */
- guit->window->invalidate(bw->window, rect);
- } else {
- /* Core managed browser window */
- browser_window_get_position(bw, true, &pos_x, &pos_y);
-
- top = browser_window_get_root(bw);
-
- rect->x0 += pos_x / bw->scale;
- rect->y0 += pos_y / bw->scale;
- rect->x1 += pos_x / bw->scale;
- rect->y1 += pos_y / bw->scale;
-
- guit->window->invalidate(top->window, rect);
- }
-}
-
-/* Exported interface, documented in netsurf/browser_window.h */
-void browser_window_stop(struct browser_window *bw)
-{
- int children, index;
-
- if (bw->loading_content != NULL) {
- hlcache_handle_abort(bw->loading_content);
- hlcache_handle_release(bw->loading_content);
- bw->loading_content = NULL;
- }
-
- 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);
- error = hlcache_handle_abort(bw->current_content);
- assert(error == NSERROR_OK);
- }
-
- guit->misc->schedule(-1, browser_window_refresh, bw);
-
- if (bw->children) {
- children = bw->rows * bw->cols;
- for (index = 0; index < children; index++)
- browser_window_stop(&bw->children[index]);
- }
- if (bw->iframes) {
- children = bw->iframe_count;
- for (index = 0; index < children; index++)
- browser_window_stop(&bw->iframes[index]);
- }
-
- if (bw->current_content != NULL) {
- browser_window_refresh_url_bar(bw);
- }
-
- browser_window_stop_throbber(bw);
-}
-
-
-/* Exported interface, documented in netsurf/browser_window.h */
-void browser_window_reload(struct browser_window *bw, bool all)
-{
- hlcache_handle *c;
- unsigned int i;
-
- if (bw->current_content == NULL || bw->loading_content != NULL)
- return;
-
- if (all && content_get_type(bw->current_content) == CONTENT_HTML) {
- struct html_stylesheet *sheets;
- struct content_html_object *object;
- unsigned int count;
-
- c = bw->current_content;
-
- /* invalidate objects */
- object = html_get_objects(c, &count);
-
- for (; object != NULL; object = object->next) {
- if (object->content != NULL)
- content_invalidate_reuse_data(object->content);
- }
-
- /* invalidate stylesheets */
- sheets = html_get_stylesheets(c, &count);
-
- for (i = STYLESHEET_START; i != count; i++) {
- if (sheets[i].sheet != NULL) {
- content_invalidate_reuse_data(sheets[i].sheet);
- }
- }
- }
-
- 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);
-}
-
-
-/* Exported interface, documented in netsurf/browser_window.h */
-void browser_window_set_status(struct browser_window *bw, const char *text)
-{
- int text_len;
- /* find topmost window */
- while (bw->parent)
- bw = bw->parent;
-
- if ((bw->status.text != NULL) &&
- (strcmp(text, bw->status.text) == 0)) {
- /* status text is unchanged */
- bw->status.match++;
- return;
- }
-
- /* status text is changed */
-
- text_len = strlen(text);
-
- if ((bw->status.text == NULL) || (bw->status.text_len < text_len)) {
- /* no current string allocation or it is not long enough */
- free(bw->status.text);
- bw->status.text = strdup(text);
- bw->status.text_len = text_len;
- } else {
- /* current allocation has enough space */
- memcpy(bw->status.text, text, text_len + 1);
- }
-
- bw->status.miss++;
- guit->window->set_status(bw->window, bw->status.text);
-}
-
-
-/* Exported interface, documented in netsurf/browser_window.h */
-void browser_window_set_pointer(struct browser_window *bw,
- browser_pointer_shape shape)
-{
- struct browser_window *root = browser_window_get_root(bw);
- gui_pointer_shape gui_shape;
- bool loading;
- uint64_t ms_now;
-
- assert(root);
- assert(root->window);
-
- loading = ((bw->loading_content != NULL) ||
- ((bw->current_content != NULL) &&
- (content_get_status(bw->current_content) == CONTENT_STATUS_READY)));
-
- nsu_getmonotonic_ms(&ms_now);
-
- if (loading && ((ms_now - bw->last_action) < 1000)) {
- /* If loading and less than 1 second since last link followed,
- * force progress indicator pointer */
- gui_shape = GUI_POINTER_PROGRESS;
-
- } else if (shape == BROWSER_POINTER_AUTO) {
- /* Up to browser window to decide */
- if (loading) {
- gui_shape = GUI_POINTER_PROGRESS;
- } else {
- gui_shape = GUI_POINTER_DEFAULT;
- }
-
- } else {
- /* Use what we were told */
- gui_shape = (gui_pointer_shape)shape;
- }
-
- guit->window->set_pointer(root->window, gui_shape);
-}
-
-
-/* exported function documented in netsurf/browser_window.h */
-nserror browser_window_schedule_reformat(struct browser_window *bw)
-{
- if (bw->window == NULL) {
- return NSERROR_BAD_PARAMETER;
- }
-
- guit->misc->schedule(0, scheduled_reformat, bw);
-
- return NSERROR_OK;
-}
-
-
-/* exported function documented in netsurf/browser_window.h */
-void browser_window_reformat(struct browser_window *bw, bool background,
- int width, int height)
-{
- hlcache_handle *c = bw->current_content;
-
- if (c == NULL)
- return;
-
- if (bw->browser_window_type != BROWSER_WINDOW_IFRAME) {
- /* Iframe dimensions are already scaled in parent's layout */
- width /= bw->scale;
- height /= bw->scale;
- }
-
- if (bw->window == NULL) {
- /* Core managed browser window; subtract scrollbar width */
- width -= bw->scroll_y ? SCROLLBAR_WIDTH : 0;
- height -= bw->scroll_x ? SCROLLBAR_WIDTH : 0;
-
- width = width > 0 ? width : 0;
- height = height > 0 ? height : 0;
- }
-
- content_reformat(c, background, width, height);
-}
-
-/**
- * Set browser window scale.
- *
- * \param bw Browser window.
- * \param scale value.
- */
-static void browser_window_set_scale_internal(struct browser_window *bw,
- float scale)
-{
- int i;
- hlcache_handle *c;
-
- if (fabs(bw->scale-scale) < 0.0001)
- return;
-
- bw->scale = scale;
- c = bw->current_content;
-
- if (c != NULL) {
- if (content_can_reformat(c) == false) {
- browser_window_update(bw, false);
- } else {
- browser_window_schedule_reformat(bw);
- }
- }
-
- for (i = 0; i < (bw->cols * bw->rows); i++)
- browser_window_set_scale_internal(&bw->children[i], scale);
- for (i = 0; i < bw->iframe_count; i++)
- browser_window_set_scale_internal(&bw->iframes[i], scale);
-}
-
-
-/* exported interface documented in netsurf/browser_window.h */
-void browser_window_set_scale(struct browser_window *bw, float scale, bool all)
-{
- while (bw->parent && all)
- bw = bw->parent;
-
- browser_window_set_scale_internal(bw, scale);
-
- if (bw->parent)
- bw = bw->parent;
-
- browser_window_recalculate_frameset(bw);
-}
-
-
-/* exported interface documented in netsurf/browser_window.h */
-float browser_window_get_scale(struct browser_window *bw)
-{
- if (bw == NULL) {
- return 1.0;
- }
-
- return bw->scale;
-}
-
-/**
- * Find browser window.
- *
- * \param bw Browser window.
- * \param target Name of target.
- * \param depth Depth to scan.
- * \param page The browser window page.
- * \param rdepth The rdepth.
- * \param bw_target the output browser window.
- */
-static void browser_window_find_target_internal(struct browser_window *bw,
- const char *target, int depth, struct browser_window *page,
- int *rdepth, struct browser_window **bw_target)
-{
- int i;
-
- if ((bw->name) && (!strcasecmp(bw->name, target))) {
- if ((bw == page) || (depth > *rdepth)) {
- *rdepth = depth;
- *bw_target = bw;
- }
- }
-
- if ((!bw->children) && (!bw->iframes))
- return;
-
- depth++;
-
- if (bw->children != NULL) {
- for (i = 0; i < (bw->cols * bw->rows); i++) {
- if ((bw->children[i].name) &&
- (!strcasecmp(bw->children[i].name,
- target))) {
- if ((page == &bw->children[i]) ||
- (depth > *rdepth)) {
- *rdepth = depth;
- *bw_target = &bw->children[i];
- }
- }
- if (bw->children[i].children)
- browser_window_find_target_internal(
- &bw->children[i],
- target, depth, page,
- rdepth, bw_target);
- }
- }
-
- if (bw->iframes != NULL) {
- for (i = 0; i < bw->iframe_count; i++)
- browser_window_find_target_internal(&bw->iframes[i],
- target, depth, page, rdepth, bw_target);
- }
-}
-
-
-/* exported interface documented in netsurf/browser_window.h */
-struct browser_window *browser_window_find_target(struct browser_window *bw,
- const char *target, browser_mouse_state mouse)
-{
- struct browser_window *bw_target;
- struct browser_window *top;
- hlcache_handle *c;
- int rdepth;
- nserror error;
-
- /* 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)
- target = html_get_base_target(c);
- if (target == NULL)
- target = TARGET_SELF;
-
- /* allow the simple case of target="_blank" to be ignored if requested
- */
- if ((!(mouse & BROWSER_MOUSE_CLICK_2)) &&
- (!((mouse & BROWSER_MOUSE_CLICK_2) &&
- (mouse & BROWSER_MOUSE_MOD_2))) &&
- (!nsoption_bool(target_blank))) {
- /* 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")))
- return bw;
- }
-
- /* handle reserved keywords */
- if (((nsoption_bool(button_2_tab)) &&
- (mouse & BROWSER_MOUSE_CLICK_2))||
- ((!nsoption_bool(button_2_tab)) &&
- ((mouse & BROWSER_MOUSE_CLICK_1) &&
- (mouse & BROWSER_MOUSE_MOD_2))) ||
- ((nsoption_bool(button_2_tab)) &&
- ((target == TARGET_BLANK) ||
- (!strcasecmp(target, "_blank"))))) {
- /* open in new tab if:
- * - button_2 opens in new tab and button_2 was pressed
- * OR
- * - button_2 doesn't open in new tabs and button_1 was
- * pressed with ctrl held
- * OR
- * - button_2 opens in new tab and the link target is "_blank"
- */
- error = browser_window_create(BW_CREATE_TAB |
- BW_CREATE_HISTORY |
- BW_CREATE_CLONE,
- NULL,
- NULL,
- bw,
- &bw_target);
- if (error != NSERROR_OK) {
- return bw;
- }
- return bw_target;
- } else if (((!nsoption_bool(button_2_tab)) &&
- (mouse & BROWSER_MOUSE_CLICK_2)) ||
- ((nsoption_bool(button_2_tab)) &&
- ((mouse & BROWSER_MOUSE_CLICK_1) &&
- (mouse & BROWSER_MOUSE_MOD_2))) ||
- ((!nsoption_bool(button_2_tab)) &&
- ((target == TARGET_BLANK) ||
- (!strcasecmp(target, "_blank"))))) {
- /* open in new window if:
- * - button_2 doesn't open in new tabs and button_2 was pressed
- * OR
- * - button_2 opens in new tab and button_1 was pressed with
- * ctrl held
- * OR
- * - button_2 doesn't open in new tabs and the link target is
- * "_blank"
- */
- error = browser_window_create(BW_CREATE_HISTORY |
- BW_CREATE_CLONE,
- NULL,
- NULL,
- bw,
- &bw_target);
- if (error != NSERROR_OK) {
- return bw;
- }
- return bw_target;
- } else if ((target == TARGET_SELF) || (!strcasecmp(target, "_self"))) {
- return bw;
- } else if ((target == TARGET_PARENT) ||
- (!strcasecmp(target, "_parent"))) {
- if (bw->parent)
- return bw->parent;
- return bw;
- } else if ((target == TARGET_TOP) || (!strcasecmp(target, "_top"))) {
- while (bw->parent)
- bw = bw->parent;
- return bw;
- }
-
- /* find frame according to B.8, ie using the following priorities:
- *
- * 1) current frame
- * 2) closest to front
- */
- rdepth = -1;
- bw_target = NULL;
- for (top = bw; top->parent; top = top->parent);
- browser_window_find_target_internal(top, target, 0, bw, &rdepth,
- &bw_target);
- if (bw_target)
- return bw_target;
-
- /* we require a new window using the target name */
- if (!nsoption_bool(target_blank))
- return bw;
-
- error = browser_window_create(BW_CREATE_CLONE | BW_CREATE_HISTORY,
- NULL,
- NULL,
- bw,
- &bw_target);
- if (error != NSERROR_OK) {
- return bw;
- }
-
- /* frame names should begin with an alphabetic character (a-z,A-Z),
- * however in practice you get things such as '_new' and '2left'. The
- * only real effect this has is when giving out names as it can be
- * assumed that an author intended '_new' to create a new nameless
- * window (ie '_blank') whereas in the case of '2left' the intention
- * was for a new named window. As such we merely special case windows
- * 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;
-}
-
-
-/**
- * Handles the end of a drag operation in a browser window.
- *
- * \param bw browser window
- * \param mouse state of mouse buttons and modifier keys
- * \param x coordinate of mouse
- * \param y coordinate of mouse
- *
- * \todo Remove this function, once these things are associated with content,
- * rather than bw.
- */
-static void browser_window_mouse_drag_end(struct browser_window *bw,
- browser_mouse_state mouse, int x, int y)
-{
- int scr_x, scr_y;
-
- switch (bw->drag.type) {
- case DRAGGING_SELECTION:
- case DRAGGING_OTHER:
- case DRAGGING_CONTENT_SCROLLBAR:
- /* Drag handled by content handler */
- break;
-
- case DRAGGING_SCR_X:
-
- browser_window_get_scrollbar_pos(bw, true, &scr_x, &scr_y);
-
- scr_x = x - scr_x - scrollbar_get_offset(bw->scroll_x);
- scr_y = y - scr_y - scrollbar_get_offset(bw->scroll_y);
-
- scrollbar_mouse_drag_end(bw->scroll_x, mouse, scr_x, scr_y);
-
- bw->drag.type = DRAGGING_NONE;
- break;
-
- case DRAGGING_SCR_Y:
-
- browser_window_get_scrollbar_pos(bw, false, &scr_x, &scr_y);
-
- scr_x = x - scr_x - scrollbar_get_offset(bw->scroll_x);
- scr_y = y - scr_y - scrollbar_get_offset(bw->scroll_y);
-
- scrollbar_mouse_drag_end(bw->scroll_y, mouse, scr_x, scr_y);
-
- bw->drag.type = DRAGGING_NONE;
- break;
-
- default:
- browser_window_set_drag_type(bw, DRAGGING_NONE, NULL);
- break;
- }
-}
-
-
-/* exported interface documented in netsurf/browser_window.h */
-void browser_window_mouse_track(struct browser_window *bw,
- browser_mouse_state mouse, int x, int y)
-{
- hlcache_handle *c = bw->current_content;
- const char *status = NULL;
- browser_pointer_shape pointer = BROWSER_POINTER_DEFAULT;
-
- if (bw->window != NULL && bw->drag.window && bw != bw->drag.window) {
- /* This is the root browser window and there's an active drag
- * in a sub window.
- * Pass the mouse action straight on to that bw. */
- struct browser_window *drag_bw = bw->drag.window;
- int off_x = 0;
- int off_y = 0;
-
- browser_window_get_position(drag_bw, true, &off_x, &off_y);
-
- if (drag_bw->browser_window_type == BROWSER_WINDOW_FRAME) {
- browser_window_mouse_track(drag_bw, mouse,
- x - off_x, y - off_y);
-
- } else if (drag_bw->browser_window_type ==
- BROWSER_WINDOW_IFRAME) {
- browser_window_mouse_track(drag_bw, mouse,
- x - off_x / bw->scale,
- y - off_y / bw->scale);
- }
- return;
- }
-
- if (bw->children) {
- /* Browser window has children (frames) */
- struct browser_window *child;
- int cur_child;
- int children = bw->rows * bw->cols;
-
- for (cur_child = 0; cur_child < children; cur_child++) {
-
- child = &bw->children[cur_child];
-
- if (x < child->x || y < child->y ||
- child->x + child->width < x ||
- child->y + child->height < y) {
- /* Click not in this child */
- continue;
- }
-
- /* It's this child that contains the mouse; pass
- * mouse action on to child */
- browser_window_mouse_track(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;
- }
-
- /* Odd if we reached here, but nothing else can use the click
- * when there are children. */
- return;
- }
-
- if (c == NULL && bw->drag.type != DRAGGING_FRAME) {
- return;
- }
-
- if (bw->drag.type != DRAGGING_NONE && !mouse) {
- browser_window_mouse_drag_end(bw, mouse, x, y);
- }
-
- /* Browser window's horizontal scrollbar */
- if (bw->scroll_x != NULL && bw->drag.type != DRAGGING_SCR_Y) {
- int scr_x, scr_y;
- browser_window_get_scrollbar_pos(bw, true, &scr_x, &scr_y);
- scr_x = x - scr_x - scrollbar_get_offset(bw->scroll_x);
- scr_y = y - scr_y - scrollbar_get_offset(bw->scroll_y);
-
- if ((bw->drag.type == DRAGGING_SCR_X) ||
- (scr_x > 0 &&
- scr_x < browser_window_get_scrollbar_len(bw, true) &&
- scr_y > 0 &&
- scr_y < SCROLLBAR_WIDTH &&
- 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));
- pointer = BROWSER_POINTER_DEFAULT;
-
- if (status != NULL) {
- browser_window_set_status(bw, status);
- }
-
- browser_window_set_pointer(bw, pointer);
- return;
- }
- }
-
- /* Browser window's vertical scrollbar */
- if (bw->scroll_y != NULL) {
- int scr_x, scr_y;
- browser_window_get_scrollbar_pos(bw, false, &scr_x, &scr_y);
- scr_x = x - scr_x - scrollbar_get_offset(bw->scroll_x);
- scr_y = y - scr_y - scrollbar_get_offset(bw->scroll_y);
-
- if ((bw->drag.type == DRAGGING_SCR_Y) ||
- (scr_y > 0 &&
- scr_y < browser_window_get_scrollbar_len(bw, false) &&
- scr_x > 0 &&
- scr_x < SCROLLBAR_WIDTH &&
- 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));
- pointer = BROWSER_POINTER_DEFAULT;
-
- if (status != NULL) {
- browser_window_set_status(bw, status);
- }
-
- browser_window_set_pointer(bw, pointer);
- return;
- }
- }
-
- if (bw->drag.type == DRAGGING_FRAME) {
- browser_window_resize_frame(bw, bw->x + x, bw->y + y);
- } else if (bw->drag.type == DRAGGING_PAGE_SCROLL) {
- /* mouse movement since drag started */
- struct rect rect;
-
- rect.x0 = bw->drag.start_x - x;
- rect.y0 = bw->drag.start_y - y;
-
- /* new scroll offsets */
- rect.x0 += bw->drag.start_scroll_x;
- rect.y0 += bw->drag.start_scroll_y;
-
- bw->drag.start_scroll_x = rect.x1 = rect.x0;
- bw->drag.start_scroll_y = rect.y1 = rect.y0;
-
- browser_window_set_scroll(bw, &rect);
- } else {
- assert(c != NULL);
- content_mouse_track(c, bw, mouse, x, y);
- }
-}
-
-
-/* exported interface documented in netsurf/browser_window.h */
-void browser_window_mouse_click(struct browser_window *bw,
- browser_mouse_state mouse, int x, int y)
-{
- hlcache_handle *c = bw->current_content;
- const char *status = NULL;
- browser_pointer_shape pointer = BROWSER_POINTER_DEFAULT;
-
- if (bw->children) {
- /* Browser window has children (frames) */
- struct browser_window *child;
- int cur_child;
- int children = bw->rows * bw->cols;
-
- for (cur_child = 0; cur_child < children; cur_child++) {
-
- child = &bw->children[cur_child];
-
- if (x < child->x || y < child->y ||
- child->x + child->width < x ||
- child->y + child->height < y) {
- /* Click not in this child */
- continue;
- }
-
- /* It's this child that contains the click; pass it
- * on to child. */
- browser_window_mouse_click(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;
- }
-
- return;
- }
-
- if (!c)
- return;
-
- if (bw->scroll_x != NULL) {
- int scr_x, scr_y;
- browser_window_get_scrollbar_pos(bw, true, &scr_x, &scr_y);
- scr_x = x - scr_x - scrollbar_get_offset(bw->scroll_x);
- scr_y = y - scr_y - scrollbar_get_offset(bw->scroll_y);
-
- if (scr_x > 0 && scr_x < browser_window_get_scrollbar_len(bw,
- true) &&
- scr_y > 0 && scr_y < SCROLLBAR_WIDTH) {
- status = scrollbar_mouse_status_to_message(
- scrollbar_mouse_action(
- bw->scroll_x, mouse,
- scr_x, scr_y));
- pointer = BROWSER_POINTER_DEFAULT;
-
- if (status != NULL)
- browser_window_set_status(bw, status);
-
- browser_window_set_pointer(bw, pointer);
- return;
- }
- }
-
- if (bw->scroll_y != NULL) {
- int scr_x, scr_y;
- browser_window_get_scrollbar_pos(bw, false, &scr_x, &scr_y);
- scr_x = x - scr_x - scrollbar_get_offset(bw->scroll_x);
- scr_y = y - scr_y - scrollbar_get_offset(bw->scroll_y);
-
- if (scr_y > 0 && scr_y < browser_window_get_scrollbar_len(bw,
- false) &&
- scr_x > 0 && scr_x < SCROLLBAR_WIDTH) {
- status = scrollbar_mouse_status_to_message(
- scrollbar_mouse_action(
- bw->scroll_y, mouse,
- scr_x, scr_y));
- pointer = BROWSER_POINTER_DEFAULT;
-
- if (status != NULL)
- browser_window_set_status(bw, status);
-
- browser_window_set_pointer(bw, pointer);
- return;
- }
- }
-
- 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;
- }
-
- /* 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) {
- guit->window->drag_save_object(bw->window, c,
- GUI_SAVE_OBJECT_NATIVE);
- } else if (mouse & BROWSER_MOUSE_DRAG_1) {
- guit->window->drag_save_object(bw->window, c,
- GUI_SAVE_OBJECT_ORIG);
- }
- } else if (mouse & (BROWSER_MOUSE_DRAG_1 |
- BROWSER_MOUSE_DRAG_2)) {
- browser_window_page_drag_start(bw, x, y);
- browser_window_set_pointer(bw, BROWSER_POINTER_MOVE);
- }
- break;
- }
-}
-
-
-
-/* exported interface documented in netsurf/browser_window.h */
-void browser_window_redraw_rect(struct browser_window *bw, int x, int y,
- int width, int height)
-{
- content_request_redraw(bw->current_content, x, y, width, height);
-}
-
-
-/* exported interface documented in netsurf/browser_window.h */
-void browser_window_page_drag_start(struct browser_window *bw, int x, int y)
-{
- assert(bw != NULL);
-
- browser_window_set_drag_type(bw, DRAGGING_PAGE_SCROLL, NULL);
-
- bw->drag.start_x = x;
- bw->drag.start_y = y;
-
- if (bw->window != NULL) {
- /* Front end window */
- guit->window->get_scroll(bw->window,
- &bw->drag.start_scroll_x,
- &bw->drag.start_scroll_y);
-
- guit->window->scroll_start(bw->window);
- } else {
- /* Core managed browser window */
- bw->drag.start_scroll_x = scrollbar_get_offset(bw->scroll_x);
- bw->drag.start_scroll_y = scrollbar_get_offset(bw->scroll_y);
- }
-}
-
-
-
-/* exported interface documented in netsurf/browser_window.h */
-bool browser_window_back_available(struct browser_window *bw)
-{
- return (bw && bw->history &&
- browser_window_history_back_available(bw));
-}
-
-
-
-/* exported interface documented in netsurf/browser_window.h */
-bool browser_window_forward_available(struct browser_window *bw)
-{
- return (bw && bw->history &&
- browser_window_history_forward_available(bw));
-}
-
-/* exported interface documented in netsurf/browser_window.h */
-bool browser_window_reload_available(struct browser_window *bw)
-{
- return (bw && bw->current_content && !bw->loading_content);
-}
-
-
-/* exported interface documented in netsurf/browser_window.h */
-bool browser_window_stop_available(struct browser_window *bw)
-{
- return (bw && (bw->loading_content ||
- (bw->current_content &&
- (content_get_status(bw->current_content) !=
- CONTENT_STATUS_DONE))));
-}
-
-/* exported interface documented in browser.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;
}
-/* exported interface documented in browser.h */
+/* exported interface documented in netsurf/browser.h */
int browser_get_dpi(void)
{
return FIXTOINT(nscss_screen_dpi);
diff --git a/desktop/browser_history.c b/desktop/browser_history.c
index 640302773..ce9821af8 100644
--- a/desktop/browser_history.c
+++ b/desktop/browser_history.c
@@ -33,19 +33,16 @@
#include "netsurf/layout.h"
#include "netsurf/content.h"
#include "netsurf/window.h"
+#include "netsurf/browser_window.h"
#include "content/hlcache.h"
#include "content/urldb.h"
#include "netsurf/bitmap.h"
+#include "utils/corestrings.h"
#include "desktop/gui_internal.h"
-#include "desktop/browser_history.h"
#include "desktop/browser_private.h"
-
-#define WIDTH 100
-#define HEIGHT 86
-#define RIGHT_MARGIN 50
-#define BOTTOM_MARGIN 30
-
+#include "desktop/local_history_private.h"
+#include "desktop/browser_history.h"
/**
* Clone a history entry
@@ -106,8 +103,10 @@ browser_window_history__clone_entry(struct history *history,
unsigned char *bmdst_data;
size_t bmsize;
- new_entry->page.bitmap = guit->bitmap->create(WIDTH, HEIGHT,
- BITMAP_NEW | BITMAP_OPAQUE);
+ new_entry->page.bitmap = guit->bitmap->create(
+ LOCAL_HISTORY_WIDTH,
+ LOCAL_HISTORY_HEIGHT,
+ BITMAP_OPAQUE);
if (new_entry->page.bitmap != NULL) {
bmsrc_data = guit->bitmap->get_buffer(entry->page.bitmap);
@@ -201,26 +200,26 @@ static int browser_window_history__layout_subtree(struct history *history,
struct history_entry *child;
int y1 = y;
- if (history->width < x + WIDTH)
- history->width = x + WIDTH;
+ if (history->width < x + LOCAL_HISTORY_WIDTH)
+ history->width = x + LOCAL_HISTORY_WIDTH;
if (!entry->forward) {
entry->x = x;
entry->y = y;
- return y + HEIGHT;
+ return y + LOCAL_HISTORY_HEIGHT;
}
/* layout child subtrees below each other */
for (child = entry->forward; child; child = child->next) {
y1 = browser_window_history__layout_subtree(history, child,
- x + WIDTH + RIGHT_MARGIN, y1);
+ x + LOCAL_HISTORY_WIDTH + LOCAL_HISTORY_RIGHT_MARGIN, y1);
if (child->next)
- y1 += BOTTOM_MARGIN;
+ y1 += LOCAL_HISTORY_BOTTOM_MARGIN;
}
/* place ourselves in the middle */
entry->x = x;
- entry->y = (y + y1) / 2 - HEIGHT / 2;
+ entry->y = (y + y1) / 2 - LOCAL_HISTORY_HEIGHT / 2;
return y1;
}
@@ -243,12 +242,13 @@ static void browser_window_history__layout(struct history *history)
if (history->start)
history->height = browser_window_history__layout_subtree(
history, history->start,
- RIGHT_MARGIN / 2, BOTTOM_MARGIN / 2);
+ LOCAL_HISTORY_RIGHT_MARGIN / 2,
+ LOCAL_HISTORY_BOTTOM_MARGIN / 2);
else
history->height = 0;
- history->width += RIGHT_MARGIN / 2;
- history->height += BOTTOM_MARGIN / 2;
+ history->width += LOCAL_HISTORY_RIGHT_MARGIN / 2;
+ history->height += LOCAL_HISTORY_BOTTOM_MARGIN / 2;
}
@@ -274,7 +274,8 @@ static bool browser_window_history__enumerate_entry(
const struct history_entry *child;
if (!cb(bw, entry->x, entry->y,
- entry->x + WIDTH, entry->y + HEIGHT,
+ entry->x + LOCAL_HISTORY_WIDTH,
+ entry->y + LOCAL_HISTORY_HEIGHT,
entry, ud))
return false;
@@ -303,8 +304,8 @@ nserror browser_window_history_create(struct browser_window *bw)
return NSERROR_NOMEM;
}
- history->width = RIGHT_MARGIN / 2;
- history->height = BOTTOM_MARGIN / 2;
+ history->width = LOCAL_HISTORY_RIGHT_MARGIN / 2;
+ history->height = LOCAL_HISTORY_BOTTOM_MARGIN / 2;
bw->history = history;
@@ -385,8 +386,9 @@ browser_window_history_add(struct browser_window *bw,
NSLOG(netsurf, DEBUG,
"Creating thumbnail for %s", nsurl_access(entry->page.url));
- entry->page.bitmap = guit->bitmap->create(WIDTH, HEIGHT,
- BITMAP_NEW | BITMAP_CLEAR_MEMORY | BITMAP_OPAQUE);
+ entry->page.bitmap = guit->bitmap->create(
+ LOCAL_HISTORY_WIDTH, LOCAL_HISTORY_HEIGHT,
+ BITMAP_CLEAR | BITMAP_OPAQUE);
if (entry->page.bitmap != NULL) {
ret = guit->bitmap->render(entry->page.bitmap, content);
if (ret != NSERROR_OK) {
@@ -433,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;
}
@@ -454,13 +454,22 @@ 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);
+ /* clamp width and height values */
+ if (content_height < 1) {
+ content_height = 1;
+ }
+ if (content_width < 1) {
+ content_width = 1;
+ }
/* Successfully got scroll offsets, update the entry */
history->current->page.scroll_x = \
- (float)sx / (float)content_get_width(content);
+ (float)sx / (float)content_width;
history->current->page.scroll_y = \
- (float)sy / (float)content_get_height(content);
+ (float)sy / (float)content_height;
NSLOG(netsurf, INFO, "Updated scroll offsets to %g by %g",
history->current->page.scroll_x,
history->current->page.scroll_y);
@@ -479,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;
}
@@ -510,6 +517,24 @@ void browser_window_history_destroy(struct browser_window *bw)
/* exported interface documented in desktop/browser_history.h */
nserror browser_window_history_back(struct browser_window *bw, bool new_window)
{
+ if (bw != NULL && bw->internal_nav) {
+ /* All internal nav back operations ignore new_window */
+ if (bw->current_parameters.url != NULL) {
+ /* There are some internal parameters, restart from there */
+ return browser_window__reload_current_parameters(bw);
+ } else {
+ /* No internal parameters, just navigate to about:blank */
+ return browser_window_navigate(
+ bw,
+ corestring_nsurl_about_blank,
+ NULL, /* Referer */
+ BW_NAVIGATE_HISTORY,
+ NULL, /* Post */
+ NULL, /* Post */
+ NULL /* parent fetch */);
+ }
+ }
+
if (!bw || !bw->history || !bw->history->current ||
!bw->history->current->back) {
return NSERROR_BAD_PARAMETER;
@@ -601,7 +626,9 @@ nserror browser_window_history_go(struct browser_window *bw,
url, NULL, bw, NULL);
history->current = current;
} else {
- browser_window_history_update(bw, bw->current_content);
+ if (bw->current_content != NULL) {
+ browser_window_history_update(bw, bw->current_content);
+ }
history->current = entry;
error = browser_window_navigate(bw, url, NULL,
BW_NAVIGATE_NO_TERMINAL_HISTORY_UPDATE,
@@ -625,7 +652,8 @@ void browser_window_history_enumerate_forward(const struct browser_window *bw,
e = bw->history->current->forward_pref;
for (; e != NULL; e = e->forward_pref) {
- if (!cb(bw, e->x, e->y, e->x + WIDTH, e->y + HEIGHT,
+ if (!cb(bw, e->x, e->y, e->x + LOCAL_HISTORY_WIDTH,
+ e->y + LOCAL_HISTORY_HEIGHT,
e, user_data))
break;
}
@@ -642,7 +670,8 @@ void browser_window_history_enumerate_back(const struct browser_window *bw,
return;
for (e = bw->history->current->back; e != NULL; e = e->back) {
- if (!cb(bw, e->x, e->y, e->x + WIDTH, e->y + HEIGHT,
+ if (!cb(bw, e->x, e->y, e->x + LOCAL_HISTORY_WIDTH,
+ e->y + LOCAL_HISTORY_HEIGHT,
e, user_data))
break;
}
diff --git a/desktop/browser_private.h b/desktop/browser_private.h
index 192d22bf0..40c3b43ce 100644
--- a/desktop/browser_private.h
+++ b/desktop/browser_private.h
@@ -22,14 +22,10 @@
* Browser window private structure.
*/
-#ifndef _NETSURF_DESKTOP_BROWSER_PRIVATE_H_
-#define _NETSURF_DESKTOP_BROWSER_PRIVATE_H_
-
-#include <libwapcaplet/libwapcaplet.h>
-
-#include "netsurf/types.h"
-#include "netsurf/browser_window.h"
+#ifndef NETSURF_DESKTOP_BROWSER_PRIVATE_H_
+#define NETSURF_DESKTOP_BROWSER_PRIVATE_H_
+#include "content/fetch.h"
#include "desktop/frame_types.h"
struct box;
@@ -81,6 +77,20 @@ struct history {
};
/**
+ * The parameters for a fetch.
+ */
+struct browser_fetch_parameters {
+ struct nsurl *url; /**< The URL to fetch */
+ struct nsurl *referrer; /**< Optional refererer */
+ enum browser_window_nav_flags flags; /**< Navigation flags */
+ char *post_urlenc; /**< URL encoded post data */
+ struct fetch_multipart_data *post_multipart; /**< Multipart post data */
+ char *parent_charset; /**< Optional parent character set */
+ bool parent_quirks; /**< Optional parent quirks */
+};
+
+
+/**
* Browser window data.
*/
struct browser_window {
@@ -89,6 +99,17 @@ struct browser_window {
* READY or DONE status or NULL for no content.
*/
struct hlcache_handle *current_content;
+
+ /**
+ * The fetch parameters for the current content
+ */
+ struct browser_fetch_parameters current_parameters;
+
+ /**
+ * The certificate chain for the current content
+ */
+ struct cert_chain *current_cert_chain;
+
/**
* Content handle of page in process of being loaded or NULL
* if no page is being loaded.
@@ -96,6 +117,16 @@ struct browser_window {
struct hlcache_handle *loading_content;
/**
+ * The fetch parameters for the loading content
+ */
+ struct browser_fetch_parameters loading_parameters;
+
+ /**
+ * The certificate chain for the loading content
+ */
+ struct cert_chain *loading_cert_chain;
+
+ /**
* Favicon
*/
struct {
@@ -129,6 +160,8 @@ struct browser_window {
bool throbbing;
/** Add loading_content to the window history when it loads. */
bool history_add;
+ /** Internal navigation, do not update URL etc */
+ bool internal_nav;
/** Fragment identifier for current_content. */
lwc_string *frag_id;
@@ -230,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 {
@@ -250,19 +283,27 @@ 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
*
* \param bw The browser window to get dimensions of
* \param width Updated to the browser window viewport width
* \param height Updated to the browser window viewport height
- * \param scaled Whether we want the height with scale applied
+ * \return NSERROR_OK and width and height updated otherwise error code
*/
-void browser_window_get_dimensions(struct browser_window *bw,
- int *width, int *height, bool scaled);
+nserror browser_window_get_dimensions(struct browser_window *bw,
+ int *width, int *height);
/**
@@ -275,6 +316,15 @@ void browser_window_update_extent(struct browser_window *bw);
/**
+ * 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
+ */
+nserror browser_window_invalidate_rect(struct browser_window *bw, struct rect *rect);
+
+
+/**
* Change the status bar of a browser window.
*
* \param bw browser window
@@ -289,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);
/**
@@ -354,5 +405,16 @@ nserror browser_window_history_get_scroll(struct browser_window *bw,
*/
void browser_window_history_destroy(struct browser_window *bw);
+/**
+ * Type for handling query responses short-term
+ */
+typedef nserror (*browser_window_query_callback)(bool proceed, void *pw);
+
+/**
+ * Navigate a browser window to the current parameters
+ *
+ * \param bw The browser window to cause to navigate
+ */
+nserror browser_window__reload_current_parameters(struct browser_window *bw);
#endif
diff --git a/desktop/browser_window.c b/desktop/browser_window.c
new file mode 100644
index 000000000..55366a03e
--- /dev/null
+++ b/desktop/browser_window.c
@@ -0,0 +1,4803 @@
+/*
+ * Copyright 2008 Michael Drake <tlsa@netsurf-browser.org>
+ * 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/
+ *
+ * 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
+ *
+ * Browser window creation and manipulation implementation.
+ */
+
+#include "utils/config.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include <nsutils/time.h>
+
+#include "utils/errors.h"
+#include "utils/log.h"
+#include "utils/corestrings.h"
+#include "utils/messages.h"
+#include "utils/nsoption.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.h"
+#include "content/hlcache.h"
+#include "content/urldb.h"
+#include "content/content_debug.h"
+
+#include "html/html.h"
+#include "html/form_internal.h"
+#include "javascript/js.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/browser_history.h"
+#include "desktop/theme.h"
+
+#ifdef WITH_THEME_INSTALL
+#include "desktop/theme.h"
+#endif
+
+/**
+ * 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
+
+/**
+ * maximum frame depth
+ */
+#define FRAME_DEPTH 8
+
+/* Forward declare internal navigation function */
+static nserror browser_window__navigate_internal(
+ struct browser_window *bw, struct browser_fetch_parameters *params);
+
+
+/**
+ * Close and destroy all child 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++) {
+ browser_window_destroy_internal(&bw->children[i]);
+ }
+ free(bw->children);
+ bw->children = NULL;
+ bw->rows = 0;
+ bw->cols = 0;
+ }
+}
+
+
+/**
+ * Free the stored fetch parameters
+ *
+ * \param bw The browser window
+ */
+static void
+browser_window__free_fetch_parameters(struct browser_fetch_parameters *params)
+{
+ if (params->url != NULL) {
+ nsurl_unref(params->url);
+ params->url = NULL;
+ }
+ if (params->referrer != NULL) {
+ nsurl_unref(params->referrer);
+ params->referrer = NULL;
+ }
+ if (params->post_urlenc != NULL) {
+ free(params->post_urlenc);
+ params->post_urlenc = NULL;
+ }
+ if (params->post_multipart != NULL) {
+ fetch_multipart_data_destroy(params->post_multipart);
+ params->post_multipart = NULL;
+ }
+ if (params->parent_charset != NULL) {
+ free(params->parent_charset);
+ params->parent_charset = NULL;
+ }
+}
+
+
+/**
+ * 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
+ */
+static inline void
+browser_window_get_scrollbar_pos(struct browser_window *bw,
+ bool horizontal,
+ int *x, int *y)
+{
+ if (horizontal) {
+ *x = 0;
+ *y = bw->height - SCROLLBAR_WIDTH;
+ } else {
+ *x = bw->width - SCROLLBAR_WIDTH;
+ *y = 0;
+ }
+}
+
+
+/**
+ * Get browser window horizontal scrollbar widget length
+ *
+ * \param bw The browser window
+ * \return the scrollbar's length
+ */
+static inline int get_horz_scrollbar_len(struct browser_window *bw)
+{
+ if (bw->scroll_y == NULL) {
+ return bw->width;
+ }
+ return bw->width - SCROLLBAR_WIDTH;
+}
+
+
+/**
+ * Get browser window vertical scrollbar widget length
+ *
+ * \param bw The browser window
+ * \return the scrollbar's length
+ */
+static inline int get_vert_scrollbar_len(struct browser_window *bw)
+{
+ return bw->height;
+}
+
+
+/**
+ * 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)
+ */
+static void
+browser_window_set_selection(struct browser_window *bw,
+ bool selection,
+ bool read_only)
+{
+ struct browser_window *top;
+
+ assert(bw != NULL);
+
+ top = browser_window_get_root(bw);
+
+ assert(top != 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);
+ }
+
+ if (selection) {
+ top->selection.bw = bw;
+ } else {
+ top->selection.bw = NULL;
+ }
+
+ top->selection.read_only = read_only;
+}
+
+
+/**
+ * Set the scroll position of a browser window.
+ *
+ * scrolls the viewport to ensure the specified rectangle of the
+ * content is shown.
+ *
+ * \param bw window to scroll
+ * \param rect The rectangle to ensure is shown.
+ * \return NSERROR_OK on success or apropriate error code.
+ */
+static nserror
+browser_window_set_scroll(struct browser_window *bw, const struct rect *rect)
+{
+ if (bw->window != NULL) {
+ return guit->window->set_scroll(bw->window, rect);
+ }
+
+ if (bw->scroll_x != NULL) {
+ scrollbar_set(bw->scroll_x, rect->x0, false);
+ }
+ if (bw->scroll_y != NULL) {
+ scrollbar_set(bw->scroll_y, rect->y0, false);
+ }
+
+ return NSERROR_OK;
+}
+
+
+/**
+ * Internal helper for getting the positional features
+ *
+ * \param[in] bw browser window to examine.
+ * \param[in] x x-coordinate of point of interest
+ * \param[in] y y-coordinate of point of interest
+ * \param[out] data Feature structure to update.
+ * \return NSERROR_OK or appropriate error code on faliure.
+ */
+static nserror
+browser_window__get_contextual_content(struct browser_window *bw,
+ int x, int y,
+ struct browser_window_features *data)
+{
+ nserror ret = NSERROR_OK;
+
+ /* Handle (i)frame scroll offset (core-managed browser windows only) */
+ x += scrollbar_get_offset(bw->scroll_x);
+ y += scrollbar_get_offset(bw->scroll_y);
+
+ if (bw->children) {
+ /* Browser window has children, so pass request on to
+ * appropriate child.
+ */
+ struct browser_window *bwc;
+ int cur_child;
+ int children = bw->rows * bw->cols;
+
+ /* Loop through all children of bw */
+ for (cur_child = 0; cur_child < children; cur_child++) {
+ /* Set current child */
+ bwc = &bw->children[cur_child];
+
+ /* Skip this frame if (x, y) coord lies outside */
+ if ((x < bwc->x) ||
+ (bwc->x + bwc->width < x) ||
+ (y < bwc->y) ||
+ (bwc->y + bwc->height < y)) {
+ continue;
+ }
+
+ /* Pass request into this child */
+ return browser_window__get_contextual_content(bwc,
+ (x - bwc->x), (y - bwc->y), data);
+ }
+
+ /* Coordinate not contained by any frame */
+
+ } else if (bw->current_content != NULL) {
+ /* Pass request to content */
+ ret = content_get_contextual_content(bw->current_content,
+ x, y, data);
+ data->main = bw->current_content;
+ }
+
+ return ret;
+}
+
+
+/**
+ * implements the download operation of a window navigate
+ */
+static nserror
+browser_window_download(struct browser_window *bw,
+ nsurl *url,
+ nsurl *nsref,
+ uint32_t fetch_flags,
+ bool fetch_is_post,
+ llcache_post_data *post)
+{
+ llcache_handle *l;
+ struct browser_window *root;
+ nserror error;
+
+ root = browser_window_get_root(bw);
+ assert(root != NULL);
+
+ fetch_flags |= LLCACHE_RETRIEVE_FORCE_FETCH;
+ fetch_flags |= LLCACHE_RETRIEVE_STREAM_DATA;
+
+ error = llcache_handle_retrieve(url, fetch_flags, nsref,
+ fetch_is_post ? post : NULL,
+ NULL, NULL, &l);
+ if (error == NSERROR_NO_FETCH_HANDLER) {
+ /* no internal handler for this type, call out to frontend */
+ error = guit->misc->launch_url(url);
+ } else if (error != NSERROR_OK) {
+ NSLOG(netsurf, INFO, "Failed to fetch download: %d", error);
+ } else {
+ error = download_context_create(l, root->window);
+ if (error != NSERROR_OK) {
+ NSLOG(netsurf, INFO,
+ "Failed creating download context: %d", error);
+ llcache_handle_abort(l);
+ llcache_handle_release(l);
+ }
+ }
+
+ return error;
+}
+
+
+/**
+ * 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;
+
+ if (bw->throbbing)
+ return true;
+
+ if (bw->children) {
+ children = bw->rows * bw->cols;
+ for (index = 0; index < children; index++) {
+ if (browser_window_check_throbber(&bw->children[index]))
+ return true;
+ }
+ }
+
+ if (bw->iframes) {
+ for (index = 0; index < bw->iframe_count; index++) {
+ if (browser_window_check_throbber(&bw->iframes[index]))
+ return true;
+ }
+ }
+
+ return false;
+}
+
+
+/**
+ * Start the busy indicator.
+ *
+ * \param bw browser window
+ */
+static nserror browser_window_start_throbber(struct browser_window *bw)
+{
+ bw->throbbing = true;
+
+ while (bw->parent)
+ bw = bw->parent;
+
+ return guit->window->event(bw->window, GW_EVENT_START_THROBBER);
+}
+
+
+/**
+ * Stop the busy indicator.
+ *
+ * \param bw browser window
+ */
+static nserror browser_window_stop_throbber(struct browser_window *bw)
+{
+ nserror res = NSERROR_OK;
+
+ bw->throbbing = false;
+
+ while (bw->parent) {
+ bw = bw->parent;
+ }
+
+ if (!browser_window_check_throbber(bw)) {
+ res = guit->window->event(bw->window, GW_EVENT_STOP_THROBBER);
+ }
+ return res;
+}
+
+
+/**
+ * Callback for fetchcache() for browser window favicon fetches.
+ *
+ * \param c content handle of favicon
+ * \param event The event to process
+ * \param pw a context containing the browser window
+ * \return NSERROR_OK on success else appropriate error code.
+ */
+static nserror
+browser_window_favicon_callback(hlcache_handle *c,
+ const hlcache_event *event,
+ void *pw)
+{
+ struct browser_window *bw = pw;
+
+ switch (event->type) {
+ case CONTENT_MSG_DONE:
+ if (bw->favicon.current != NULL) {
+ content_close(bw->favicon.current);
+ hlcache_handle_release(bw->favicon.current);
+ }
+
+ bw->favicon.current = c;
+ bw->favicon.loading = NULL;
+
+ /* content_get_bitmap on the hlcache_handle should give
+ * the favicon bitmap at this point
+ */
+ guit->window->set_icon(bw->window, c);
+ break;
+
+ case CONTENT_MSG_ERROR:
+
+ /* clean up after ourselves */
+ if (c == bw->favicon.loading) {
+ bw->favicon.loading = NULL;
+ } else if (c == bw->favicon.current) {
+ bw->favicon.current = NULL;
+ }
+
+ hlcache_handle_release(c);
+
+ if (bw->favicon.failed == false) {
+ nsurl *nsref = NULL;
+ nsurl *nsurl;
+ nserror error;
+
+ bw->favicon.failed = true;
+
+ error = nsurl_create("resource:favicon.ico", &nsurl);
+ if (error != NSERROR_OK) {
+ NSLOG(netsurf, INFO,
+ "Unable to create default location url");
+ } else {
+ hlcache_handle_retrieve(nsurl,
+ HLCACHE_RETRIEVE_SNIFF_TYPE,
+ nsref, NULL,
+ browser_window_favicon_callback,
+ bw, NULL, CONTENT_IMAGE,
+ &bw->favicon.loading);
+
+ nsurl_unref(nsurl);
+ }
+
+ }
+ break;
+
+ default:
+ break;
+
+ }
+ return NSERROR_OK;
+}
+
+
+/**
+ * update the favicon associated with the browser window
+ *
+ * \param c the page content handle.
+ * \param bw A top level browser window.
+ * \param link A link context or NULL to attempt fallback scanning.
+ */
+static nserror
+browser_window_update_favicon(hlcache_handle *c,
+ struct browser_window *bw,
+ struct content_rfc5988_link *link)
+{
+ nsurl *nsref = NULL;
+ nsurl *nsurl;
+ nserror res;
+
+ assert(c != NULL);
+ assert(bw !=NULL);
+
+ if (bw->window == NULL) {
+ /* Not top-level browser window; not interested */
+ return NSERROR_OK;
+ }
+
+ /* already fetching the favicon - use that */
+ if (bw->favicon.loading != NULL) {
+ return NSERROR_OK;
+ }
+
+ bw->favicon.failed = false;
+
+ if (link == NULL) {
+ /* Look for "icon" */
+ link = content_find_rfc5988_link(c, corestring_lwc_icon);
+ }
+
+ if (link == NULL) {
+ /* Look for "shortcut icon" */
+ link = content_find_rfc5988_link(c, corestring_lwc_shortcut_icon);
+ }
+
+ if (link == NULL) {
+ lwc_string *scheme;
+ bool speculative_default = false;
+ bool match;
+
+ nsurl = hlcache_handle_get_url(c);
+
+ scheme = nsurl_get_component(nsurl, NSURL_SCHEME);
+
+ /* If the document was fetched over http(s), then speculate
+ * that there's a favicon living at /favicon.ico */
+ if ((lwc_string_caseless_isequal(scheme,
+ corestring_lwc_http,
+ &match) == lwc_error_ok &&
+ match) ||
+ (lwc_string_caseless_isequal(scheme,
+ corestring_lwc_https,
+ &match) == lwc_error_ok &&
+ match)) {
+ speculative_default = true;
+ }
+
+ lwc_string_unref(scheme);
+
+ if (speculative_default) {
+ /* no favicon via link, try for the default location */
+ res = nsurl_join(nsurl, "/favicon.ico", &nsurl);
+ } else {
+ bw->favicon.failed = true;
+ res = nsurl_create("resource:favicon.ico", &nsurl);
+ }
+ if (res != NSERROR_OK) {
+ NSLOG(netsurf, INFO,
+ "Unable to create default location url");
+ return res;
+ }
+ } else {
+ nsurl = nsurl_ref(link->href);
+ }
+
+ if (link == NULL) {
+ 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));
+ }
+
+ 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;
+}
+
+
+/**
+ * Handle meta http-equiv refresh time elapsing by loading a new page.
+ *
+ * \param p browser window to refresh with new page
+ */
+static void browser_window_refresh(void *p)
+{
+ struct browser_window *bw = p;
+ nsurl *url;
+ nsurl *refresh;
+ hlcache_handle *parent = NULL;
+ enum browser_window_nav_flags flags = BW_NAVIGATE_UNVERIFIABLE;
+
+ assert(bw->current_content != NULL &&
+ (content_get_status(bw->current_content) ==
+ CONTENT_STATUS_READY ||
+ content_get_status(bw->current_content) ==
+ CONTENT_STATUS_DONE));
+
+ /* Ignore if the refresh URL has gone
+ * (may happen if a fetch error occurred) */
+ refresh = content_get_refresh_url(bw->current_content);
+ if (refresh == NULL)
+ return;
+
+ /* mark this content as invalid so it gets flushed from the cache */
+ content_invalidate_reuse_data(bw->current_content);
+
+ url = hlcache_handle_get_url(bw->current_content);
+ if ((url == NULL) || (nsurl_compare(url, refresh, NSURL_COMPLETE))) {
+ flags |= BW_NAVIGATE_HISTORY;
+ }
+
+ /* Treat an (almost) immediate refresh in a top-level browser window as
+ * if it were an HTTP redirect, and thus make the resulting fetch
+ * verifiable.
+ *
+ * See fetchcache.c for why redirected fetches should be verifiable at
+ * all.
+ */
+ if (bw->refresh_interval <= 100 && bw->parent == NULL) {
+ flags &= ~BW_NAVIGATE_UNVERIFIABLE;
+ } else {
+ parent = bw->current_content;
+ }
+
+ browser_window_navigate(bw,
+ refresh,
+ url,
+ flags,
+ NULL,
+ NULL,
+ parent);
+
+}
+
+
+/**
+ * Transfer the loading_content to a new download window.
+ */
+static void
+browser_window_convert_to_download(struct browser_window *bw,
+ llcache_handle *stream)
+{
+ struct browser_window *root = browser_window_get_root(bw);
+ nserror error;
+
+ assert(root != NULL);
+
+ error = download_context_create(stream, root->window);
+ if (error != NSERROR_OK) {
+ llcache_handle_abort(stream);
+ llcache_handle_release(stream);
+ }
+
+ /* remove content from browser window */
+ hlcache_handle_release(bw->loading_content);
+ bw->loading_content = NULL;
+
+ browser_window_stop_throbber(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)
+{
+ int width, height;
+ nserror res = NSERROR_OK;
+
+ /* close and release the current window content */
+ if (bw->current_content != NULL) {
+ content_close(bw->current_content);
+ hlcache_handle_release(bw->current_content);
+ }
+
+ bw->current_content = bw->loading_content;
+ bw->loading_content = NULL;
+
+ if (!bw->internal_nav) {
+ /* Transfer the fetch parameters */
+ 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 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 */
+ browser_window_get_dimensions(bw, &width, &height);
+ width /= bw->scale;
+ height /= bw->scale;
+ content_reformat(bw->current_content, false, width, height);
+
+ /* history */
+ if (bw->history_add && bw->history && !bw->internal_nav) {
+ nsurl *url = hlcache_handle_get_url(bw->current_content);
+
+ if (urldb_add_url(url)) {
+ urldb_set_url_title(url, content_get_title(bw->current_content));
+ urldb_update_url_visit_data(url);
+ urldb_set_url_content_type(url,
+ content_get_type(bw->current_content));
+
+ /* This is safe as we've just added the URL */
+ global_history_add(urldb_get_url(url));
+ }
+ /**
+ * \todo Urldb / Thumbnails / Local history brokenness
+ *
+ * We add to local history after calling urldb_add_url rather
+ * than in the block above. If urldb_add_url fails (as it
+ * will for urls like "about:about", "about:config" etc),
+ * there would be no local history node, and later calls to
+ * history_update will either explode or overwrite the node
+ * for the previous URL.
+ *
+ * We call it after, rather than before urldb_add_url because
+ * history_add calls bitmap render, which tries to register
+ * the thumbnail with urldb. That thumbnail registration
+ * fails if the url doesn't exist in urldb already, and only
+ * urldb-registered thumbnails get freed. So if we called
+ * history_add before urldb_add_url we would leak thumbnails
+ * for all newly visited URLs. With the history_add call
+ * after, we only leak the thumbnails when urldb does not add
+ * the URL.
+ *
+ * Also, since browser_window_history_add can create a
+ * thumbnail (content_redraw), we need to do it after
+ * content_reformat.
+ */
+ browser_window_history_add(bw, bw->current_content, bw->frag_id);
+ }
+
+ browser_window_remove_caret(bw, false);
+
+ if (bw->window != NULL) {
+ guit->window->event(bw->window, GW_EVENT_NEW_CONTENT);
+
+ browser_window_refresh_url_bar(bw);
+ }
+
+ /* new content; set scroll_to_top */
+ browser_window_update(bw, true);
+ content_open(bw->current_content, bw, 0, 0);
+ browser_window_set_status(bw, content_get_status_message(bw->current_content));
+
+ /* frames */
+ res = browser_window_create_frameset(bw);
+
+ /* iframes */
+ res = browser_window_create_iframes(bw);
+
+ /* 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;
+}
+
+
+/**
+ * handle message for content done on browser window
+ */
+static nserror
+browser_window_content_done(struct browser_window *bw)
+{
+ float sx, sy;
+ struct rect rect;
+ int scrollx;
+ int scrolly;
+
+ if (bw->window == NULL) {
+ /* Updated browser window's scrollbars. */
+ /**
+ * \todo update browser window scrollbars before CONTENT_MSG_DONE
+ */
+ browser_window_reformat(bw, true, bw->width, bw->height);
+ browser_window_handle_scrollbars(bw);
+ }
+
+ browser_window_update(bw, false);
+ browser_window_set_status(bw, content_get_status_message(bw->current_content));
+ browser_window_stop_throbber(bw);
+ browser_window_update_favicon(bw->current_content, bw, NULL);
+
+ if (browser_window_history_get_scroll(bw, &sx, &sy) == NSERROR_OK) {
+ scrollx = (int)((float)content_get_width(bw->current_content) * sx);
+ scrolly = (int)((float)content_get_height(bw->current_content) * sy);
+ rect.x0 = rect.x1 = scrollx;
+ rect.y0 = rect.y1 = scrolly;
+ if (browser_window_set_scroll(bw, &rect) != NSERROR_OK) {
+ NSLOG(netsurf, WARNING,
+ "Unable to set browser scroll offsets to %d by %d",
+ scrollx, scrolly);
+ }
+ }
+
+ if (!bw->internal_nav) {
+ browser_window_history_update(bw, bw->current_content);
+ hotlist_update_url(hlcache_handle_get_url(bw->current_content));
+ }
+
+ if (bw->refresh_interval != -1) {
+ guit->misc->schedule(bw->refresh_interval * 10,
+ browser_window_refresh, bw);
+ }
+
+ return NSERROR_OK;
+}
+
+
+/**
+ * Handle query responses from SSL requests
+ */
+static nserror
+browser_window__handle_ssl_query_response(bool proceed, void *pw)
+{
+ struct browser_window *bw = (struct browser_window *)pw;
+
+ /* If we're in the process of loading, stop the load */
+ if (bw->loading_content != NULL) {
+ /* We had a loading content (maybe auth page?) */
+ browser_window_stop(bw);
+ browser_window_remove_caret(bw, false);
+ browser_window_destroy_children(bw);
+ browser_window_destroy_iframes(bw);
+ }
+
+ if (!proceed) {
+ /* We're processing a "back to safety", do a rough-and-ready
+ * nav to the old 'current' parameters, with any post data
+ * stripped away
+ */
+ return browser_window__reload_current_parameters(bw);
+ }
+
+ /* We're processing a "proceed" attempt from the form */
+ /* First, we permit the SSL */
+ urldb_set_cert_permissions(bw->loading_parameters.url, true);
+
+ /* 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.
+ *
+ * \param[in] userpass The input string to split.
+ * \param[in] username_out Returns username on success. Owned by caller.
+ * \param[out] password_out Returns password on success. Owned by caller.
+ * \return NSERROR_OK, or appropriate error code.
+ */
+static nserror
+browser_window__unpack_userpass(const char *userpass,
+ char **username_out,
+ char **password_out)
+{
+ const char *tmp;
+ char *username;
+ char *password;
+ size_t len;
+
+ if (userpass == NULL) {
+ username = malloc(1);
+ password = malloc(1);
+ if (username == NULL || password == NULL) {
+ free(username);
+ free(password);
+ return NSERROR_NOMEM;
+ }
+ username[0] = '\0';
+ password[0] = '\0';
+
+ *username_out = username;
+ *password_out = password;
+ return NSERROR_OK;
+ }
+
+ tmp = strchr(userpass, ':');
+ if (tmp == NULL) {
+ return NSERROR_BAD_PARAMETER;
+ } else {
+ size_t len2;
+ len = tmp - userpass;
+ len2 = strlen(++tmp);
+
+ username = malloc(len + 1);
+ password = malloc(len2 + 1);
+ if (username == NULL || password == NULL) {
+ free(username);
+ free(password);
+ return NSERROR_NOMEM;
+ }
+ memcpy(username, userpass, len);
+ username[len] = '\0';
+ memcpy(password, tmp, len2 + 1);
+ }
+
+ *username_out = username;
+ *password_out = password;
+ return NSERROR_OK;
+}
+
+
+/**
+ * Build a "username:password" from components.
+ *
+ * \param[in] username The username component.
+ * \param[in] password The password component.
+ * \param[out] userpass_out Returns combined string on success.
+ * Owned by caller.
+ * \return NSERROR_OK, or appropriate error code.
+ */
+static nserror
+browser_window__build_userpass(const char *username,
+ const char *password,
+ char **userpass_out)
+{
+ char *userpass;
+ size_t len;
+
+ len = strlen(username) + 1 + strlen(password) + 1;
+
+ userpass = malloc(len);
+ if (userpass == NULL) {
+ return NSERROR_NOMEM;
+ }
+
+ snprintf(userpass, len, "%s:%s", username, password);
+
+ *userpass_out = userpass;
+ return NSERROR_OK;
+}
+
+
+/**
+ * Handle a response from the UI when prompted for credentials
+ */
+static nserror
+browser_window__handle_userpass_response(nsurl *url,
+ const char *realm,
+ const char *username,
+ const char *password,
+ void *pw)
+{
+ struct browser_window *bw = (struct browser_window *)pw;
+ char *userpass;
+ nserror err;
+
+ err = browser_window__build_userpass(username, password, &userpass);
+ if (err != NSERROR_OK) {
+ return err;
+ }
+
+ urldb_set_auth_details(url, realm, userpass);
+
+ free(userpass);
+
+ /**
+ * \todo QUERY - Eventually this should fill out the form *NOT* nav
+ * to the original location
+ */
+ /* Finally navigate to the original loading parameters */
+ if (bw->loading_content != NULL) {
+ /* We had a loading content (maybe auth page?) */
+ 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
+ *
+ */
+static nserror
+browser_window__handle_login(struct browser_window *bw,
+ const char *realm,
+ nsurl *url) {
+ char *username = NULL, *password = NULL;
+ nserror err = NSERROR_OK;
+ struct browser_fetch_parameters params;
+
+ memset(&params, 0, sizeof(params));
+
+ /* Step one, retrieve what we have */
+ err = browser_window__unpack_userpass(
+ urldb_get_auth_details(url, realm),
+ &username, &password);
+ if (err != NSERROR_OK) {
+ goto out;
+ }
+
+ /* Step two, construct our fetch parameters */
+ params.url = nsurl_ref(corestring_nsurl_about_query_auth);
+ 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(&params.post_multipart,
+ "siteurl",
+ nsurl_access(url));
+ if (err != NSERROR_OK) {
+ goto out;
+ }
+
+ err = fetch_multipart_data_new_kv(&params.post_multipart,
+ "realm",
+ realm);
+ if (err != NSERROR_OK) {
+ goto out;
+ }
+
+ err = fetch_multipart_data_new_kv(&params.post_multipart,
+ "username",
+ username);
+ if (err != NSERROR_OK) {
+ goto out;
+ }
+
+ err = fetch_multipart_data_new_kv(&params.post_multipart,
+ "password",
+ password);
+ if (err != NSERROR_OK) {
+ goto out;
+ }
+
+ /* Now we issue the fetch */
+ bw->internal_nav = true;
+ err = browser_window__navigate_internal(bw, &params);
+
+ if (err != NSERROR_OK) {
+ goto out;
+ }
+
+ err = guit->misc->login(url, realm, username, password,
+ browser_window__handle_userpass_response, bw);
+
+ if (err == NSERROR_NOT_IMPLEMENTED) {
+ err = NSERROR_OK;
+ }
+ out:
+ if (username != NULL) {
+ free(username);
+ }
+ if (password != NULL) {
+ free(password);
+ }
+ browser_window__free_fetch_parameters(&params);
+ return err;
+}
+
+
+/**
+ * Handle a certificate verification request (BAD_CERTS) during a fetch
+ */
+static nserror
+browser_window__handle_bad_certs(struct browser_window *bw,
+ nsurl *url)
+{
+ struct browser_fetch_parameters params;
+ 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 depth;
+ nsurl *chainurl = NULL;
+
+ memset(&params, 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(&params.post_multipart,
+ "siteurl",
+ nsurl_access(url));
+ if (err != NSERROR_OK) {
+ goto out;
+ }
+
+ 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(&params.post_multipart,
+ "chainurl",
+ nsurl_access(chainurl));
+ if (err != NSERROR_OK) {
+ goto out;
+ }
+ }
+
+ err = fetch_multipart_data_new_kv(&params.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, &params);
+ if (err != NSERROR_OK) {
+ goto out;
+ }
+
+ out:
+ browser_window__free_fetch_parameters(&params);
+ if (chainurl != NULL)
+ nsurl_unref(chainurl);
+ return err;
+}
+
+
+/**
+ * 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(&params, 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(&params.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, &params);
+ if (err != NSERROR_OK) {
+ goto out;
+ }
+
+ out:
+ browser_window__free_fetch_parameters(&params);
+ 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(&params, 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(&params.post_multipart,
+ "siteurl",
+ nsurl_access(url));
+ if (err != NSERROR_OK) {
+ goto out;
+ }
+
+ err = fetch_multipart_data_new_kv(&params.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, &params);
+ if (err != NSERROR_OK) {
+ goto out;
+ }
+
+ out:
+ browser_window__free_fetch_parameters(&params);
+ return err;
+}
+
+
+/**
+ * Handle errors during content fetch
+ */
+static nserror
+browser_window__handle_error(struct browser_window *bw,
+ hlcache_handle *c,
+ const hlcache_event *event)
+{
+ const char *message = event->data.errordata.errormsg;
+ nserror code = event->data.errordata.errorcode;
+ nserror res;
+ nsurl *url = hlcache_handle_get_url(c);
+
+ /* Unexpected OK? */
+ assert(code != NSERROR_OK);
+
+ if (message == NULL) {
+ message = messages_get_errorcode(code);
+ } else {
+ message = messages_get(message);
+ }
+
+ if (c == bw->loading_content) {
+ bw->loading_content = NULL;
+ } else if (c == bw->current_content) {
+ bw->current_content = NULL;
+ browser_window_remove_caret(bw, false);
+ }
+
+ hlcache_handle_release(c);
+
+ switch (code) {
+ 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;
+ }
+
+ return res;
+}
+
+
+/**
+ * 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);
+}
+
+
+/**
+ * Browser window content event callback handler.
+ */
+static nserror
+browser_window_callback(hlcache_handle *c, const hlcache_event *event, void *pw)
+{
+ struct browser_window *bw = pw;
+ nserror res = NSERROR_OK;
+
+ switch (event->type) {
+ case CONTENT_MSG_SSL_CERTS:
+ /* SSL certificate information has arrived, store it */
+ 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,
+ event->data.log.msg,
+ event->data.log.msglen,
+ event->data.log.flags);
+ break;
+
+ case CONTENT_MSG_DOWNLOAD:
+ assert(bw->loading_content == c);
+
+ browser_window_convert_to_download(bw, event->data.download);
+
+ if (bw->current_content != NULL) {
+ browser_window_refresh_url_bar(bw);
+ }
+ break;
+
+ case CONTENT_MSG_LOADING:
+ assert(bw->loading_content == c);
+
+#ifdef WITH_THEME_INSTALL
+ if (content_get_type(c) == CONTENT_THEME) {
+ theme_install_start(c);
+ bw->loading_content = NULL;
+ browser_window_stop_throbber(bw);
+ } else
+#endif
+ {
+ bw->refresh_interval = -1;
+ browser_window_set_status(bw,
+ content_get_status_message(c));
+ }
+ break;
+
+ case CONTENT_MSG_READY:
+ assert(bw->loading_content == c);
+
+ res = browser_window_content_ready(bw);
+ break;
+
+ case CONTENT_MSG_DONE:
+ assert(bw->current_content == c);
+
+ res = browser_window_content_done(bw);
+ break;
+
+ case CONTENT_MSG_ERROR:
+ res = browser_window__handle_error(bw, c, event);
+ break;
+
+ case CONTENT_MSG_REDIRECT:
+ 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) {
+ /* Give preference to any loading content */
+ status = content_get_status_message(
+ bw->loading_content);
+ }
+
+ if (status == NULL) {
+ status = content_get_status_message(c);
+ }
+
+ 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);
+ }
+ break;
+
+ case CONTENT_MSG_REFORMAT:
+ 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 */
+ browser_window_remove_caret(bw, true);
+
+ if (!(event->data.background)) {
+ /* Reformatted content should be redrawn */
+ browser_window_update(bw, false);
+ }
+ 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_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,
+ event->data.rfc5988_link);
+ }
+ }
+ break;
+
+ 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);
+
+ *(event->data.getdims.viewport_width) = width / bw->scale;
+ *(event->data.getdims.viewport_height) = height / bw->scale;
+ 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;
+ }
+
+ 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_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 (save == NULL) {
+ save = c;
+ }
+
+ 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_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;
+
+ 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;
+
+ case CONTENT_MSG_POINTER:
+ /* Content wants to have specific mouse pointer */
+ browser_window_set_pointer(bw, event->data.pointer);
+ 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_window_set_drag_type(bw, bdt, event->data.drag.rect);
+ }
+ break;
+
+ case CONTENT_MSG_CARET:
+ switch (event->data.caret.type) {
+ case CONTENT_CARET_REMOVE:
+ browser_window_remove_caret(bw, false);
+ break;
+ case CONTENT_CARET_HIDE:
+ browser_window_remove_caret(bw, true);
+ break;
+ case CONTENT_CARET_SET_POS:
+ browser_window_place_caret(bw,
+ event->data.caret.pos.x,
+ event->data.caret.pos.y,
+ event->data.caret.pos.height,
+ event->data.caret.pos.clip);
+ break;
+ }
+ break;
+
+ case CONTENT_MSG_SELECTION:
+ browser_window_set_selection(bw,
+ event->data.selection.selection,
+ event->data.selection.read_only);
+ break;
+
+ case CONTENT_MSG_SELECTMENU:
+ if (event->data.select_menu.gadget->type == GADGET_SELECT) {
+ struct browser_window *root =
+ browser_window_get_root(bw);
+ guit->window->create_form_select_menu(root->window,
+ event->data.select_menu.gadget);
+ }
+
+ break;
+
+ case CONTENT_MSG_GADGETCLICK:
+ if (event->data.gadget_click.gadget->type == GADGET_FILE) {
+ struct browser_window *root =
+ browser_window_get_root(bw);
+ guit->window->file_gadget_open(root->window, c,
+ event->data.gadget_click.gadget);
+ }
+
+ 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;
+ }
+
+ return res;
+}
+
+
+/**
+ * internal scheduled reformat callback.
+ *
+ * scheduled reformat callback to allow reformats from unthreaded context.
+ *
+ * \param vbw The browser window to be reformatted
+ */
+static void scheduled_reformat(void *vbw)
+{
+ struct browser_window *bw = vbw;
+ int width;
+ int height;
+ nserror res;
+
+ res = guit->window->get_dimensions(bw->window, &width, &height);
+ if (res == NSERROR_OK) {
+ browser_window_reformat(bw, false, width, height);
+ }
+}
+
+/* exported interface documented in desktop/browser_private.h */
+nserror browser_window_destroy_internal(struct browser_window *bw)
+{
+ assert(bw);
+
+ browser_window_destroy_children(bw);
+ browser_window_destroy_iframes(bw);
+
+ /* Destroy scrollbars */
+ if (bw->scroll_x != NULL) {
+ scrollbar_destroy(bw->scroll_x);
+ }
+
+ if (bw->scroll_y != NULL) {
+ scrollbar_destroy(bw->scroll_y);
+ }
+
+ /* clear any pending callbacks */
+ guit->misc->schedule(-1, browser_window_refresh, bw);
+ NSLOG(netsurf, INFO,
+ "Clearing reformat schedule for browser window %p", bw);
+ guit->misc->schedule(-1, scheduled_reformat, bw);
+
+ /* If this brower window is not the root window, and has focus, unset
+ * the root browser window's focus pointer. */
+ if (!bw->window) {
+ struct browser_window *top = browser_window_get_root(bw);
+
+ if (top->focus == bw)
+ top->focus = top;
+
+ if (top->selection.bw == bw) {
+ browser_window_set_selection(top, false, false);
+ }
+ }
+
+ /* Destruction order is important: we must ensure that the frontend
+ * destroys any window(s) associated with this browser window before
+ * we attempt any destructive cleanup.
+ */
+
+ if (bw->window) {
+ /* Only the root window has a GUI window */
+ guit->window->destroy(bw->window);
+ }
+
+ if (bw->loading_content != NULL) {
+ hlcache_handle_abort(bw->loading_content);
+ hlcache_handle_release(bw->loading_content);
+ bw->loading_content = NULL;
+ }
+
+ if (bw->current_content != NULL) {
+ content_close(bw->current_content);
+ hlcache_handle_release(bw->current_content);
+ bw->current_content = NULL;
+ }
+
+ if (bw->favicon.loading != NULL) {
+ hlcache_handle_abort(bw->favicon.loading);
+ hlcache_handle_release(bw->favicon.loading);
+ bw->favicon.loading = NULL;
+ }
+
+ if (bw->favicon.current != NULL) {
+ content_close(bw->favicon.current);
+ hlcache_handle_release(bw->favicon.current);
+ bw->favicon.current = NULL;
+ }
+
+ if (bw->jsheap != NULL) {
+ js_destroyheap(bw->jsheap);
+ bw->jsheap = NULL;
+ }
+
+ /* These simply free memory, so are safe here */
+
+ if (bw->frag_id != NULL) {
+ lwc_string_unref(bw->frag_id);
+ }
+
+ 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;
+ browser_window__free_fetch_parameters(&bw->current_parameters);
+ browser_window__free_fetch_parameters(&bw->loading_parameters);
+ NSLOG(netsurf, INFO, "Status text cache match:miss %d:%d",
+ bw->status.match, bw->status.miss);
+
+ return NSERROR_OK;
+}
+
+
+/**
+ * Set browser window scale.
+ *
+ * \param bw Browser window.
+ * \param absolute scale value.
+ * \return NSERROR_OK on success else error code
+ */
+static nserror
+browser_window_set_scale_internal(struct browser_window *bw, float scale)
+{
+ int i;
+ nserror res = NSERROR_OK;
+
+ /* do not apply tiny changes in scale */
+ if (fabs(bw->scale - scale) < 0.0001)
+ return res;
+
+ bw->scale = scale;
+
+ if (bw->current_content != NULL) {
+ if (content_can_reformat(bw->current_content) == false) {
+ browser_window_update(bw, false);
+ } else {
+ res = browser_window_schedule_reformat(bw);
+ }
+ }
+
+ /* scale frames */
+ for (i = 0; i < (bw->cols * bw->rows); i++) {
+ res = browser_window_set_scale_internal(&bw->children[i], scale);
+ }
+
+ /* scale iframes */
+ for (i = 0; i < bw->iframe_count; i++) {
+ res = browser_window_set_scale_internal(&bw->iframes[i], scale);
+ }
+
+ return res;
+}
+
+
+/**
+ * Find browser window.
+ *
+ * \param bw Browser window.
+ * \param target Name of target.
+ * \param depth Depth to scan.
+ * \param page The browser window page.
+ * \param rdepth The rdepth.
+ * \param bw_target the output browser window.
+ */
+static void
+browser_window_find_target_internal(struct browser_window *bw,
+ const char *target,
+ int depth,
+ struct browser_window *page,
+ int *rdepth,
+ struct browser_window **bw_target)
+{
+ int i;
+
+ if ((bw->name) && (!strcasecmp(bw->name, target))) {
+ if ((bw == page) || (depth > *rdepth)) {
+ *rdepth = depth;
+ *bw_target = bw;
+ }
+ }
+
+ if ((!bw->children) && (!bw->iframes))
+ return;
+
+ depth++;
+
+ if (bw->children != NULL) {
+ for (i = 0; i < (bw->cols * bw->rows); i++) {
+ if ((bw->children[i].name) &&
+ (!strcasecmp(bw->children[i].name,
+ target))) {
+ if ((page == &bw->children[i]) ||
+ (depth > *rdepth)) {
+ *rdepth = depth;
+ *bw_target = &bw->children[i];
+ }
+ }
+ if (bw->children[i].children)
+ browser_window_find_target_internal(
+ &bw->children[i],
+ target, depth, page,
+ rdepth, bw_target);
+ }
+ }
+
+ if (bw->iframes != NULL) {
+ for (i = 0; i < bw->iframe_count; i++) {
+ browser_window_find_target_internal(&bw->iframes[i],
+ target,
+ depth,
+ page,
+ rdepth,
+ bw_target);
+ }
+ }
+}
+
+
+/**
+ * Handles the end of a drag operation in a browser window.
+ *
+ * \param bw browser window
+ * \param mouse state of mouse buttons and modifier keys
+ * \param x coordinate of mouse
+ * \param y coordinate of mouse
+ *
+ * \todo Remove this function, once these things are associated with content,
+ * rather than bw.
+ */
+static void
+browser_window_mouse_drag_end(struct browser_window *bw,
+ browser_mouse_state mouse,
+ int x, int y)
+{
+ int scr_x, scr_y;
+
+ switch (bw->drag.type) {
+ case DRAGGING_SELECTION:
+ case DRAGGING_OTHER:
+ case DRAGGING_CONTENT_SCROLLBAR:
+ /* Drag handled by content handler */
+ break;
+
+ case DRAGGING_SCR_X:
+
+ browser_window_get_scrollbar_pos(bw, true, &scr_x, &scr_y);
+
+ scr_x = x - scr_x - scrollbar_get_offset(bw->scroll_x);
+ scr_y = y - scr_y - scrollbar_get_offset(bw->scroll_y);
+
+ scrollbar_mouse_drag_end(bw->scroll_x, mouse, scr_x, scr_y);
+
+ bw->drag.type = DRAGGING_NONE;
+ break;
+
+ case DRAGGING_SCR_Y:
+
+ browser_window_get_scrollbar_pos(bw, false, &scr_x, &scr_y);
+
+ scr_x = x - scr_x - scrollbar_get_offset(bw->scroll_x);
+ scr_y = y - scr_y - scrollbar_get_offset(bw->scroll_y);
+
+ scrollbar_mouse_drag_end(bw->scroll_y, mouse, scr_x, scr_y);
+
+ bw->drag.type = DRAGGING_NONE;
+ break;
+
+ default:
+ browser_window_set_drag_type(bw, DRAGGING_NONE, NULL);
+ break;
+ }
+}
+
+/**
+ * Process mouse click event
+ *
+ * \param bw The browsing context receiving the event
+ * \param mouse The mouse event state
+ * \param x The scaled x co-ordinate of the event
+ * \param y The scaled y co-ordinate of the event
+ */
+static void
+browser_window_mouse_click_internal(struct browser_window *bw,
+ browser_mouse_state mouse,
+ int x, int y)
+{
+ hlcache_handle *c = bw->current_content;
+ const char *status = NULL;
+ browser_pointer_shape pointer = BROWSER_POINTER_DEFAULT;
+
+ if (bw->children) {
+ /* Browser window has children (frames) */
+ struct browser_window *child;
+ int cur_child;
+ int children = bw->rows * bw->cols;
+
+ for (cur_child = 0; cur_child < children; cur_child++) {
+
+ child = &bw->children[cur_child];
+
+ if ((x < child->x) ||
+ (y < child->y) ||
+ (child->x + child->width < x) ||
+ (child->y + child->height < y)) {
+ /* Click not in this child */
+ continue;
+ }
+
+ /* 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));
+
+ /* Mouse action was for this child, we're done */
+ return;
+ }
+
+ return;
+ }
+
+ if (!c)
+ return;
+
+ if (bw->scroll_x != NULL) {
+ int scr_x, scr_y;
+ browser_window_get_scrollbar_pos(bw, true, &scr_x, &scr_y);
+ scr_x = x - scr_x - scrollbar_get_offset(bw->scroll_x);
+ scr_y = y - scr_y - scrollbar_get_offset(bw->scroll_y);
+
+ 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));
+ pointer = BROWSER_POINTER_DEFAULT;
+
+ if (status != NULL)
+ browser_window_set_status(bw, status);
+
+ browser_window_set_pointer(bw, pointer);
+ return;
+ }
+ }
+
+ if (bw->scroll_y != NULL) {
+ int scr_x, scr_y;
+ browser_window_get_scrollbar_pos(bw, false, &scr_x, &scr_y);
+ scr_x = x - scr_x - scrollbar_get_offset(bw->scroll_x);
+ scr_y = y - scr_y - scrollbar_get_offset(bw->scroll_y);
+
+ 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));
+ pointer = BROWSER_POINTER_DEFAULT;
+
+ if (status != NULL) {
+ browser_window_set_status(bw, status);
+ }
+
+ browser_window_set_pointer(bw, pointer);
+ return;
+ }
+ }
+
+ 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;
+ }
+
+ /* 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) {
+ guit->window->drag_save_object(bw->window, c,
+ GUI_SAVE_OBJECT_NATIVE);
+ } else if (mouse & BROWSER_MOUSE_DRAG_1) {
+ guit->window->drag_save_object(bw->window, c,
+ GUI_SAVE_OBJECT_ORIG);
+ }
+ } else if (mouse & (BROWSER_MOUSE_DRAG_1 |
+ BROWSER_MOUSE_DRAG_2)) {
+ browser_window_page_drag_start(bw, x, y);
+ browser_window_set_pointer(bw, BROWSER_POINTER_MOVE);
+ }
+ break;
+ }
+}
+
+
+/**
+ * Process mouse movement event
+ *
+ * \param bw The browsing context receiving the event
+ * \param mouse The mouse event state
+ * \param x The scaled x co-ordinate of the event
+ * \param y The scaled y co-ordinate of the event
+ */
+static void
+browser_window_mouse_track_internal(struct browser_window *bw,
+ browser_mouse_state mouse,
+ int x, int y)
+{
+ hlcache_handle *c = bw->current_content;
+ const char *status = NULL;
+ browser_pointer_shape pointer = BROWSER_POINTER_DEFAULT;
+
+ if (bw->window != NULL && bw->drag.window && bw != bw->drag.window) {
+ /* This is the root browser window and there's an active drag
+ * in a sub window.
+ * Pass the mouse action straight on to that bw. */
+ struct browser_window *drag_bw = bw->drag.window;
+ int off_x = 0;
+ int off_y = 0;
+
+ 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);
+
+ } 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);
+ }
+ return;
+ }
+
+ if (bw->children) {
+ /* Browser window has children (frames) */
+ struct browser_window *child;
+ int cur_child;
+ int children = bw->rows * bw->cols;
+
+ for (cur_child = 0; cur_child < children; cur_child++) {
+
+ child = &bw->children[cur_child];
+
+ if ((x < child->x) ||
+ (y < child->y) ||
+ (child->x + child->width < x) ||
+ (child->y + child->height < y)) {
+ /* Click not in this child */
+ continue;
+ }
+
+ /* It's this child that contains the mouse; pass
+ * mouse action on to child */
+ browser_window_mouse_track_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;
+ }
+
+ /* Odd if we reached here, but nothing else can use the click
+ * when there are children. */
+ return;
+ }
+
+ if (c == NULL && bw->drag.type != DRAGGING_FRAME) {
+ return;
+ }
+
+ if (bw->drag.type != DRAGGING_NONE && !mouse) {
+ browser_window_mouse_drag_end(bw, mouse, x, y);
+ }
+
+ /* Browser window's horizontal scrollbar */
+ if (bw->scroll_x != NULL && bw->drag.type != DRAGGING_SCR_Y) {
+ int scr_x, scr_y;
+ browser_window_get_scrollbar_pos(bw, true, &scr_x, &scr_y);
+ scr_x = x - scr_x - scrollbar_get_offset(bw->scroll_x);
+ scr_y = y - scr_y - scrollbar_get_offset(bw->scroll_y);
+
+ if ((bw->drag.type == DRAGGING_SCR_X) ||
+ (scr_x > 0 &&
+ scr_x < get_horz_scrollbar_len(bw) &&
+ scr_y > 0 &&
+ scr_y < SCROLLBAR_WIDTH &&
+ 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));
+ pointer = BROWSER_POINTER_DEFAULT;
+
+ if (status != NULL) {
+ browser_window_set_status(bw, status);
+ }
+
+ browser_window_set_pointer(bw, pointer);
+ return;
+ }
+ }
+
+ /* Browser window's vertical scrollbar */
+ if (bw->scroll_y != NULL) {
+ int scr_x, scr_y;
+ browser_window_get_scrollbar_pos(bw, false, &scr_x, &scr_y);
+ scr_x = x - scr_x - scrollbar_get_offset(bw->scroll_x);
+ scr_y = y - scr_y - scrollbar_get_offset(bw->scroll_y);
+
+ if ((bw->drag.type == DRAGGING_SCR_Y) ||
+ (scr_y > 0 &&
+ scr_y < get_vert_scrollbar_len(bw) &&
+ scr_x > 0 &&
+ scr_x < SCROLLBAR_WIDTH &&
+ 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));
+ pointer = BROWSER_POINTER_DEFAULT;
+
+ if (status != NULL) {
+ browser_window_set_status(bw, status);
+ }
+
+ browser_window_set_pointer(bw, pointer);
+ return;
+ }
+ }
+
+ if (bw->drag.type == DRAGGING_FRAME) {
+ browser_window_resize_frame(bw, bw->x + x, bw->y + y);
+ } else if (bw->drag.type == DRAGGING_PAGE_SCROLL) {
+ /* mouse movement since drag started */
+ struct rect rect;
+
+ rect.x0 = bw->drag.start_x - x;
+ rect.y0 = bw->drag.start_y - y;
+
+ /* new scroll offsets */
+ rect.x0 += bw->drag.start_scroll_x;
+ rect.y0 += bw->drag.start_scroll_y;
+
+ bw->drag.start_scroll_x = rect.x1 = rect.x0;
+ bw->drag.start_scroll_y = rect.y1 = rect.y0;
+
+ browser_window_set_scroll(bw, &rect);
+ } else {
+ assert(c != NULL);
+ content_mouse_track(c, bw, mouse, x, y);
+ }
+}
+
+
+/**
+ * perform a scroll operation at a given coordinate
+ *
+ * \param bw The browsing context receiving the event
+ * \param x The scaled x co-ordinate of the event
+ * \param y The scaled y co-ordinate of the event
+ */
+static bool
+browser_window_scroll_at_point_internal(struct browser_window *bw,
+ int x, int y,
+ int scrx, int scry)
+{
+ assert(bw != NULL);
+
+ /* Handle (i)frame scroll offset (core-managed browser windows only) */
+ x += scrollbar_get_offset(bw->scroll_x);
+ y += scrollbar_get_offset(bw->scroll_y);
+
+ if (bw->children) {
+ /* Browser window has children, so pass request on to
+ * appropriate child */
+ struct browser_window *bwc;
+ int cur_child;
+ int children = bw->rows * bw->cols;
+
+ /* Loop through all children of bw */
+ for (cur_child = 0; cur_child < children; cur_child++) {
+ /* Set current child */
+ bwc = &bw->children[cur_child];
+
+ /* Skip this frame if (x, y) coord lies outside */
+ if (x < bwc->x || bwc->x + bwc->width < x ||
+ y < bwc->y || bwc->y + bwc->height < y)
+ continue;
+
+ /* Pass request into this child */
+ return browser_window_scroll_at_point_internal(
+ 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) {
+ /* Scroll handled by current content */
+ return true;
+ }
+
+ /* Try to scroll this window. */
+ return (int)scrollbar_scroll(bw->scroll_y, scry) |
+ (int)scrollbar_scroll(bw->scroll_x, scrx);
+}
+
+
+/**
+ * allows a dragged file to be dropped into a browser window at a position
+ *
+ * \param bw The browsing context receiving the event
+ * \param x The scaled x co-ordinate of the event
+ * \param y The scaled y co-ordinate of the event
+ * \param file filename to be put in the widget
+ */
+static bool
+browser_window_drop_file_at_point_internal(struct browser_window *bw,
+ int x, int y,
+ char *file)
+{
+ assert(bw != NULL);
+
+ /* Handle (i)frame scroll offset (core-managed browser windows only) */
+ x += scrollbar_get_offset(bw->scroll_x);
+ y += scrollbar_get_offset(bw->scroll_y);
+
+ if (bw->children) {
+ /* Browser window has children, so pass request on to
+ * appropriate child */
+ struct browser_window *bwc;
+ int cur_child;
+ int children = bw->rows * bw->cols;
+
+ /* Loop through all children of bw */
+ for (cur_child = 0; cur_child < children; cur_child++) {
+ /* Set current child */
+ bwc = &bw->children[cur_child];
+
+ /* Skip this frame if (x, y) coord lies outside */
+ if (x < bwc->x || bwc->x + bwc->width < x ||
+ y < bwc->y || bwc->y + bwc->height < y)
+ continue;
+
+ /* Pass request into this child */
+ return browser_window_drop_file_at_point_internal(
+ bwc,
+ (x - bwc->x),
+ (y - bwc->y),
+ file);
+ }
+ }
+
+ /* Pass file drop on to any content */
+ if (bw->current_content != NULL) {
+ return content_drop_file_at_point(bw->current_content,
+ x, y, file);
+ }
+
+ return false;
+}
+
+
+/**
+ * Check if this is an internal navigation URL.
+ *
+ * This safely checks if the given url is an internal navigation even
+ * for urls with no scheme or path.
+ *
+ * \param url The URL to check
+ * \return true if an internal navigation url else false
+ */
+static bool
+is_internal_navigate_url(nsurl *url)
+{
+ bool is_internal = false;
+ lwc_string *scheme, *path;
+
+ scheme = nsurl_get_component(url, NSURL_SCHEME);
+ if (scheme != NULL) {
+ path = nsurl_get_component(url, NSURL_PATH);
+ if (path != NULL) {
+ if (scheme == corestring_lwc_about) {
+ if (path == corestring_lwc_query_auth) {
+ 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);
+ }
+ lwc_string_unref(scheme);
+ }
+ return is_internal;
+}
+
+
+/* exported interface, documented in netsurf/browser_window.h */
+nserror
+browser_window_get_name(struct browser_window *bw, const char **out_name)
+{
+ assert(bw != NULL);
+
+ *out_name = bw->name;
+
+ return NSERROR_OK;
+}
+
+
+/* exported interface, documented in netsurf/browser_window.h */
+nserror
+browser_window_set_name(struct browser_window *bw, const char *name)
+{
+ char *nname = NULL;
+
+ assert(bw != NULL);
+
+ if (name != NULL) {
+ nname = strdup(name);
+ if (nname == NULL) {
+ return NSERROR_NOMEM;
+ }
+ }
+
+ if (bw->name != NULL) {
+ free(bw->name);
+ }
+
+ bw->name = nname;
+
+ return NSERROR_OK;
+}
+
+
+/* exported interface, documented in netsurf/browser_window.h */
+bool
+browser_window_redraw(struct browser_window *bw,
+ int x, int y,
+ const struct rect *clip,
+ const struct redraw_context *ctx)
+{
+ struct redraw_context new_ctx = *ctx;
+ int width = 0;
+ int height = 0;
+ bool plot_ok = true;
+ content_type content_type;
+ struct content_redraw_data data;
+ struct rect content_clip;
+ nserror res;
+
+ 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 */
+ ctx->plot->clip(ctx, clip);
+ return (ctx->plot->rectangle(ctx, plot_style_fill_white, clip) == NSERROR_OK);
+ }
+
+ /* Browser window has content OR children (frames) */
+ if ((bw->window != NULL) &&
+ (ctx->plot->option_knockout)) {
+ /* Root browser window: start knockout */
+ knockout_plot_start(ctx, &new_ctx);
+ }
+
+ new_ctx.plot->clip(ctx, clip);
+
+ /* Handle redraw of any browser window children */
+ if (bw->children) {
+ struct browser_window *child;
+ int cur_child;
+ int children = bw->rows * bw->cols;
+
+ if (bw->window != NULL) {
+ /* Root browser window; start with blank fill */
+ plot_ok &= (new_ctx.plot->rectangle(ctx,
+ plot_style_fill_white,
+ clip) == NSERROR_OK);
+ }
+
+ /* Loop through all children of bw */
+ for (cur_child = 0; cur_child < children; cur_child++) {
+ /* Set current child */
+ child = &bw->children[cur_child];
+
+ /* 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 +
+ child->width * child->scale;
+ content_clip.y1 = content_clip.y0 +
+ child->height * child->scale;
+
+ /* Intersect it with clip rectangle */
+ if (content_clip.x0 < clip->x0)
+ content_clip.x0 = clip->x0;
+ if (content_clip.y0 < clip->y0)
+ content_clip.y0 = clip->y0;
+ if (clip->x1 < content_clip.x1)
+ content_clip.x1 = clip->x1;
+ if (clip->y1 < content_clip.y1)
+ content_clip.y1 = clip->y1;
+
+ /* Skip this frame if it lies outside clip rectangle */
+ if (content_clip.x0 >= content_clip.x1 ||
+ content_clip.y0 >= content_clip.y1)
+ continue;
+
+ /* Redraw frame */
+ plot_ok &= browser_window_redraw(child,
+ x + child->x,
+ y + child->y,
+ &content_clip,
+ &new_ctx);
+ }
+
+ /* Nothing else to redraw for browser windows with children;
+ * cleanup and return
+ */
+ if (bw->window != NULL && ctx->plot->option_knockout) {
+ /* Root browser window: knockout end */
+ knockout_plot_end(ctx);
+ }
+
+ return plot_ok;
+ }
+
+ /* Handle browser windows with content to redraw */
+
+ content_type = content_get_type(bw->current_content);
+ if (content_type != CONTENT_HTML && content_type != CONTENT_TEXTPLAIN) {
+ /* Set render area according to scale */
+ width = content_get_width(bw->current_content) * bw->scale;
+ height = content_get_height(bw->current_content) * bw->scale;
+
+ /* Non-HTML may not fill viewport to extents, so plot white
+ * background fill */
+ plot_ok &= (new_ctx.plot->rectangle(&new_ctx,
+ plot_style_fill_white,
+ clip) == NSERROR_OK);
+ }
+
+ /* Set up content redraw data */
+ data.x = x - scrollbar_get_offset(bw->scroll_x);
+ data.y = y - scrollbar_get_offset(bw->scroll_y);
+ data.width = width;
+ data.height = height;
+
+ data.background_colour = 0xFFFFFF;
+ data.scale = bw->scale;
+ data.repeat_x = false;
+ data.repeat_y = false;
+
+ content_clip = *clip;
+
+ if (!bw->window) {
+ int x0 = x * bw->scale;
+ int y0 = y * bw->scale;
+ int x1 = (x + bw->width - ((bw->scroll_y != NULL) ?
+ SCROLLBAR_WIDTH : 0)) * bw->scale;
+ int y1 = (y + bw->height - ((bw->scroll_x != NULL) ?
+ SCROLLBAR_WIDTH : 0)) * bw->scale;
+
+ if (content_clip.x0 < x0) content_clip.x0 = x0;
+ if (content_clip.y0 < y0) content_clip.y0 = y0;
+ if (x1 < content_clip.x1) content_clip.x1 = x1;
+ if (y1 < content_clip.y1) content_clip.y1 = y1;
+ }
+
+ /* Render the content */
+ plot_ok &= content_redraw(bw->current_content, &data,
+ &content_clip, &new_ctx);
+
+ /* Back to full clip rect */
+ new_ctx.plot->clip(&new_ctx, clip);
+
+ if (!bw->window) {
+ /* Render scrollbars */
+ int off_x, off_y;
+ if (bw->scroll_x != NULL) {
+ browser_window_get_scrollbar_pos(bw, true,
+ &off_x, &off_y);
+ res = scrollbar_redraw(bw->scroll_x,
+ x + off_x, y + off_y, clip,
+ bw->scale, &new_ctx);
+ if (res != NSERROR_OK) {
+ plot_ok = false;
+ }
+ }
+ if (bw->scroll_y != NULL) {
+ browser_window_get_scrollbar_pos(bw, false,
+ &off_x, &off_y);
+ res = scrollbar_redraw(bw->scroll_y,
+ x + off_x, y + off_y, clip,
+ bw->scale, &new_ctx);
+ if (res != NSERROR_OK) {
+ plot_ok = false;
+ }
+ }
+ }
+
+ if (bw->window != NULL && ctx->plot->option_knockout) {
+ /* Root browser window: end knockout */
+ knockout_plot_end(ctx);
+ }
+
+ return plot_ok;
+}
+
+
+/* exported interface, documented in netsurf/browser_window.h */
+bool browser_window_redraw_ready(struct browser_window *bw)
+{
+ if (bw == NULL) {
+ NSLOG(netsurf, INFO, "NULL browser window");
+ return false;
+ } else if (bw->current_content != NULL) {
+ /* Can't render locked contents */
+ return !content_is_locked(bw->current_content);
+ }
+
+ return true;
+}
+
+
+/* exported interface, documented in browser_private.h */
+void browser_window_update_extent(struct browser_window *bw)
+{
+ if (bw->window != NULL) {
+ /* Front end window */
+ guit->window->event(bw->window, GW_EVENT_UPDATE_EXTENT);
+ } else {
+ /* Core-managed browser window */
+ browser_window_handle_scrollbars(bw);
+ }
+}
+
+
+/* exported interface, documented in netsurf/browser_window.h */
+void
+browser_window_get_position(struct browser_window *bw,
+ bool root,
+ int *pos_x,
+ int *pos_y)
+{
+ *pos_x = 0;
+ *pos_y = 0;
+
+ assert(bw != NULL);
+
+ while (bw) {
+ switch (bw->browser_window_type) {
+
+ case BROWSER_WINDOW_FRAMESET:
+ *pos_x += bw->x * bw->scale;
+ *pos_y += bw->y * bw->scale;
+ break;
+
+ case BROWSER_WINDOW_NORMAL:
+ /* There is no offset to the root browser window */
+ break;
+
+ case BROWSER_WINDOW_FRAME:
+ /* Iframe and Frame handling is identical;
+ * fall though */
+ case BROWSER_WINDOW_IFRAME:
+ *pos_x += (bw->x - scrollbar_get_offset(bw->scroll_x)) *
+ bw->scale;
+ *pos_y += (bw->y - scrollbar_get_offset(bw->scroll_y)) *
+ bw->scale;
+ break;
+ }
+
+ bw = bw->parent;
+
+ if (!root) {
+ /* return if we just wanted the position in the parent
+ * browser window. */
+ return;
+ }
+ }
+}
+
+
+/* exported interface, documented in netsurf/browser_window.h */
+void browser_window_set_position(struct browser_window *bw, int x, int y)
+{
+ assert(bw != NULL);
+
+ if (bw->window == NULL) {
+ /* Core managed browser window */
+ bw->x = x;
+ bw->y = y;
+ } else {
+ NSLOG(netsurf, INFO,
+ "Asked to set position of front end window.");
+ assert(0);
+ }
+}
+
+
+/* exported interface, documented in netsurf/browser_window.h */
+void
+browser_window_set_drag_type(struct browser_window *bw,
+ browser_drag_type type,
+ const struct rect *rect)
+{
+ struct browser_window *top_bw = browser_window_get_root(bw);
+ gui_drag_type gtype;
+
+ bw->drag.type = type;
+
+ if (type == DRAGGING_NONE) {
+ top_bw->drag.window = NULL;
+ } else {
+ top_bw->drag.window = bw;
+
+ switch (type) {
+ case DRAGGING_SELECTION:
+ /** \todo tell front end */
+ return;
+ case DRAGGING_SCR_X:
+ case DRAGGING_SCR_Y:
+ case DRAGGING_CONTENT_SCROLLBAR:
+ gtype = GDRAGGING_SCROLLBAR;
+ break;
+ default:
+ gtype = GDRAGGING_OTHER;
+ break;
+ }
+
+ guit->window->drag_start(top_bw->window, gtype, rect);
+ }
+}
+
+
+/* exported interface, documented in netsurf/browser_window.h */
+browser_drag_type browser_window_get_drag_type(struct browser_window *bw)
+{
+ return bw->drag.type;
+}
+
+
+/* exported interface, documented in netsurf/browser_window.h */
+struct browser_window * browser_window_get_root(struct browser_window *bw)
+{
+ while (bw && bw->parent) {
+ bw = bw->parent;
+ }
+ return bw;
+}
+
+
+/* exported interface, documented in netsurf/browser_window.h */
+browser_editor_flags browser_window_get_editor_flags(struct browser_window *bw)
+{
+ browser_editor_flags ed_flags = BW_EDITOR_NONE;
+ assert(bw->window);
+ assert(bw->parent == NULL);
+
+ if (bw->selection.bw != NULL) {
+ ed_flags |= BW_EDITOR_CAN_COPY;
+
+ if (!bw->selection.read_only)
+ ed_flags |= BW_EDITOR_CAN_CUT;
+ }
+
+ if (bw->can_edit)
+ ed_flags |= BW_EDITOR_CAN_PASTE;
+
+ return ed_flags;
+}
+
+
+/* exported interface, documented in netsurf/browser_window.h */
+bool browser_window_can_select(struct browser_window *bw)
+{
+ if (bw == NULL || bw->current_content == NULL)
+ return false;
+
+ /* TODO: We shouldn't have to know about specific content types
+ * here. There should be a content_is_selectable() call. */
+ if (content_get_type(bw->current_content) != CONTENT_HTML &&
+ content_get_type(bw->current_content) !=
+ CONTENT_TEXTPLAIN)
+ return false;
+
+ return true;
+}
+
+
+/* exported interface, documented in netsurf/browser_window.h */
+char * browser_window_get_selection(struct browser_window *bw)
+{
+ assert(bw->window);
+ assert(bw->parent == NULL);
+
+ if (bw->selection.bw == NULL ||
+ bw->selection.bw->current_content == NULL)
+ return NULL;
+
+ return content_get_selection(bw->selection.bw->current_content);
+}
+
+
+/* exported interface, documented in netsurf/browser_window.h */
+bool browser_window_can_search(struct browser_window *bw)
+{
+ if (bw == NULL || bw->current_content == NULL)
+ return false;
+
+ /** \todo We shouldn't have to know about specific content
+ * types here. There should be a content_is_searchable() call.
+ */
+ if ((content_get_type(bw->current_content) != CONTENT_HTML) &&
+ (content_get_type(bw->current_content) != CONTENT_TEXTPLAIN)) {
+ return false;
+ }
+
+ return true;
+}
+
+
+/* exported interface, documented in netsurf/browser_window.h */
+bool browser_window_is_frameset(struct browser_window *bw)
+{
+ return (bw->children != NULL);
+}
+
+
+/* exported interface, documented in netsurf/browser_window.h */
+nserror
+browser_window_get_scrollbar_type(struct browser_window *bw,
+ browser_scrolling *h,
+ browser_scrolling *v)
+{
+ *h = bw->scrolling;
+ *v = bw->scrolling;
+
+ return NSERROR_OK;
+}
+
+
+/* exported interface, documented in netsurf/browser_window.h */
+nserror
+browser_window_get_features(struct browser_window *bw,
+ int x, int y,
+ struct browser_window_features *data)
+{
+ /* clear the features structure to empty values */
+ data->link = NULL;
+ data->object = NULL;
+ data->main = NULL;
+ data->form_features = CTX_FORM_NONE;
+
+ return browser_window__get_contextual_content(bw,
+ x / bw->scale,
+ y / bw->scale,
+ data);
+}
+
+
+/* exported interface, documented in netsurf/browser_window.h */
+bool
+browser_window_scroll_at_point(struct browser_window *bw,
+ int x, int y,
+ int scrx, int scry)
+{
+ return browser_window_scroll_at_point_internal(bw,
+ x / bw->scale,
+ y / bw->scale,
+ scrx,
+ scry);
+}
+
+
+/* exported interface, documented in netsurf/browser_window.h */
+bool
+browser_window_drop_file_at_point(struct browser_window *bw,
+ int x, int y,
+ char *file)
+{
+ return browser_window_drop_file_at_point_internal(bw,
+ x / bw->scale,
+ y / bw->scale,
+ file);
+}
+
+
+/* exported interface, documented in netsurf/browser_window.h */
+void
+browser_window_set_gadget_filename(struct browser_window *bw,
+ struct form_control *gadget,
+ const char *fn)
+{
+ html_set_file_gadget_filename(bw->current_content, gadget, fn);
+}
+
+
+/* exported interface, documented in netsurf/browser_window.h */
+nserror
+browser_window_debug_dump(struct browser_window *bw,
+ FILE *f,
+ enum content_debug op)
+{
+ if (bw->current_content != NULL) {
+ return content_debug_dump(bw->current_content, f, op);
+ }
+ return NSERROR_OK;
+}
+
+
+/* exported interface, documented in netsurf/browser_window.h */
+nserror browser_window_debug(struct browser_window *bw, enum content_debug op)
+{
+ if (bw->current_content != NULL) {
+ return content_debug(bw->current_content, op);
+ }
+ return NSERROR_OK;
+}
+
+
+/* exported interface, documented in netsurf/browser_window.h */
+nserror
+browser_window_create(enum browser_window_create_flags flags,
+ nsurl *url,
+ nsurl *referrer,
+ struct browser_window *existing,
+ struct browser_window **bw)
+{
+ gui_window_create_flags gw_flags = GW_CREATE_NONE;
+ struct browser_window *ret;
+ nserror err;
+
+ /* Check parameters */
+ if (flags & BW_CREATE_CLONE) {
+ if (existing == NULL) {
+ assert(0 && "Failed: No existing window provided.");
+ return NSERROR_BAD_PARAMETER;
+ }
+ }
+
+ if (!(flags & BW_CREATE_HISTORY)) {
+ if (!(flags & BW_CREATE_CLONE) || existing == NULL) {
+ assert(0 && "Failed: Must have existing for history.");
+ return NSERROR_BAD_PARAMETER;
+ }
+ }
+
+ ret = calloc(1, sizeof(struct browser_window));
+ if (ret == NULL) {
+ return NSERROR_NOMEM;
+ }
+
+ /* Initialise common parts */
+ err = browser_window_initialise_common(flags, ret, existing);
+ if (err != NSERROR_OK) {
+ browser_window_destroy(ret);
+ return err;
+ }
+
+ /* window characteristics */
+ ret->browser_window_type = BROWSER_WINDOW_NORMAL;
+ ret->scrolling = BW_SCROLLING_YES;
+ ret->border = true;
+ ret->no_resize = true;
+ ret->focus = ret;
+
+ /* initialise last action with creation time */
+ nsu_getmonotonic_ms(&ret->last_action);
+
+ /* The existing gui_window is on the top-level existing
+ * browser_window. */
+ existing = browser_window_get_root(existing);
+
+ /* Set up gui_window creation flags */
+ if (flags & BW_CREATE_TAB)
+ 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,
+ gw_flags);
+
+ if (ret->window == NULL) {
+ browser_window_destroy(ret);
+ return NSERROR_BAD_PARAMETER;
+ }
+
+ if (url != NULL) {
+ 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) {
+ nav_flags |= BW_NAVIGATE_HISTORY;
+ }
+ browser_window_navigate(ret,
+ url,
+ referrer,
+ nav_flags,
+ NULL,
+ NULL,
+ NULL);
+ }
+
+ if (bw != NULL) {
+ *bw = ret;
+ }
+
+ return NSERROR_OK;
+}
+
+
+/* exported internal interface, documented in desktop/browser_private.h */
+nserror
+browser_window_initialise_common(enum browser_window_create_flags flags,
+ struct browser_window *bw,
+ const struct browser_window *existing)
+{
+ nserror err;
+ assert(bw);
+
+ /* new javascript context for each window/(i)frame */
+ err = js_newheap(nsoption_int(script_timeout), &bw->jsheap);
+ if (err != NSERROR_OK)
+ return err;
+
+ if (flags & BW_CREATE_CLONE) {
+ assert(existing != NULL);
+
+ /* clone history */
+ err = browser_window_history_clone(existing, bw);
+
+ /* copy the scale */
+ bw->scale = existing->scale;
+ } else {
+ /* create history */
+ err = browser_window_history_create(bw);
+
+ /* default scale */
+ bw->scale = (float) nsoption_int(scale) / 100.0;
+ }
+
+ if (err != NSERROR_OK)
+ return err;
+
+ /* window characteristics */
+ bw->refresh_interval = -1;
+
+ bw->drag.type = DRAGGING_NONE;
+
+ bw->scroll_x = NULL;
+ bw->scroll_y = NULL;
+
+ bw->focus = NULL;
+
+ /* initialise status text cache */
+ bw->status.text = NULL;
+ bw->status.text_len = 0;
+ bw->status.match = 0;
+ bw->status.miss = 0;
+
+ return NSERROR_OK;
+}
+
+
+/* exported interface, documented in netsurf/browser_window.h */
+void browser_window_destroy(struct browser_window *bw)
+{
+ /* can't destoy child windows on their own */
+ assert(!bw->parent);
+
+ /* destroy */
+ browser_window_destroy_internal(bw);
+ free(bw);
+}
+
+
+/* exported interface, documented in netsurf/browser_window.h */
+nserror browser_window_refresh_url_bar(struct browser_window *bw)
+{
+ nserror ret;
+ nsurl *display_url, *url;
+
+ assert(bw);
+
+ if (bw->parent != NULL) {
+ /* Not root window; don't set a URL in GUI URL bar */
+ return NSERROR_OK;
+ }
+
+ 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 && 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) {
+ url = bw->loading_parameters.url;
+ } else {
+ url = hlcache_handle_get_url(bw->current_content);
+ }
+ ret = browser_window_refresh_url_bar_internal(bw, url);
+ } else {
+ /* Combine URL and Fragment */
+ if (bw->internal_nav) {
+ url = bw->loading_parameters.url;
+ } else {
+ url = hlcache_handle_get_url(bw->current_content);
+ }
+ ret = nsurl_refragment(
+ url,
+ bw->frag_id, &display_url);
+ if (ret == NSERROR_OK) {
+ ret = browser_window_refresh_url_bar_internal(bw,
+ display_url);
+ nsurl_unref(display_url);
+ }
+ }
+
+ return ret;
+}
+
+
+/* exported interface documented in netsurf/browser_window.h */
+nserror
+browser_window_navigate(struct browser_window *bw,
+ nsurl *url,
+ nsurl *referrer,
+ enum browser_window_nav_flags flags,
+ char *post_urlenc,
+ struct fetch_multipart_data *post_multipart,
+ hlcache_handle *parent)
+{
+ int depth = 0;
+ struct browser_window *cur;
+ uint32_t fetch_flags = 0;
+ bool fetch_is_post = (post_urlenc != NULL || post_multipart != NULL);
+ llcache_post_data post;
+ hlcache_child_context child;
+ nserror error;
+ bool is_internal = false;
+ struct browser_fetch_parameters params, *pass_params = NULL;
+
+ assert(bw);
+ assert(url);
+
+ NSLOG(netsurf, INFO, "bw %p, url %s", bw, nsurl_access(url));
+
+ /*
+ * determine if navigation is internal url, if so, we do not
+ * do certain things during the load.
+ */
+ is_internal = is_internal_navigate_url(url);
+
+ if (is_internal &&
+ !(flags & BW_NAVIGATE_INTERNAL)) {
+ /* Internal navigation detected, but flag not set, only allow
+ * this is there's a fetch multipart
+ */
+ if (post_multipart == NULL) {
+ return NSERROR_NEED_DATA;
+ }
+ /* It *is* internal, set it as such */
+ flags |= BW_NAVIGATE_INTERNAL | BW_NAVIGATE_HISTORY;
+ /* If we were previously internal, don't update again */
+ if (bw->internal_nav) {
+ flags |= BW_NAVIGATE_NO_TERMINAL_HISTORY_UPDATE;
+ }
+ }
+
+ /* If we're navigating and we have a history entry and a content
+ * then update the history entry before we navigate to save our
+ * current state. However since history navigation pre-moves
+ * the history state, we ensure that we only do this if we've not
+ * been suppressed. In the suppressed case, the history code
+ * updates the history itself before navigating.
+ */
+ if (bw->current_content != NULL &&
+ bw->history != NULL &&
+ bw->history->current != NULL &&
+ !is_internal &&
+ !(flags & BW_NAVIGATE_NO_TERMINAL_HISTORY_UPDATE)) {
+ browser_window_history_update(bw, bw->current_content);
+ }
+
+ /* don't allow massively nested framesets */
+ for (cur = bw; cur->parent; cur = cur->parent) {
+ depth++;
+ }
+ if (depth > FRAME_DEPTH) {
+ NSLOG(netsurf, INFO, "frame depth too high.");
+ return NSERROR_FRAME_DEPTH;
+ }
+
+ /* Set up retrieval parameters */
+ if (!(flags & BW_NAVIGATE_UNVERIFIABLE)) {
+ fetch_flags |= LLCACHE_RETRIEVE_VERIFIABLE;
+ }
+
+ if (post_multipart != NULL) {
+ post.type = LLCACHE_POST_MULTIPART;
+ post.data.multipart = post_multipart;
+ } else if (post_urlenc != NULL) {
+ post.type = LLCACHE_POST_URL_ENCODED;
+ post.data.urlenc = post_urlenc;
+ }
+
+ child.charset = content_get_encoding(parent, CONTENT_ENCODING_NORMAL);
+ if ((parent != NULL) && (content_get_type(parent) == CONTENT_HTML)) {
+ child.quirks = content_get_quirks(parent);
+ } else {
+ child.quirks = false;
+ }
+
+ url = nsurl_ref(url);
+
+ if (referrer != NULL) {
+ referrer = nsurl_ref(referrer);
+ }
+
+ /* Get download out of the way */
+ if ((flags & BW_NAVIGATE_DOWNLOAD) != 0) {
+ error = browser_window_download(bw,
+ url,
+ referrer,
+ fetch_flags,
+ fetch_is_post,
+ &post);
+ nsurl_unref(url);
+ if (referrer != NULL) {
+ nsurl_unref(referrer);
+ }
+ return error;
+ }
+
+ if (bw->frag_id != NULL) {
+ lwc_string_unref(bw->frag_id);
+ }
+ bw->frag_id = NULL;
+
+ if (nsurl_has_component(url, NSURL_FRAGMENT)) {
+ bool same_url = false;
+
+ bw->frag_id = nsurl_get_component(url, NSURL_FRAGMENT);
+
+ /* 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);
+ }
+
+ /* if we're simply moving to another ID on the same page,
+ * don't bother to fetch, just update the window.
+ */
+ if ((same_url) &&
+ (fetch_is_post == false) &&
+ (nsurl_has_component(url, NSURL_QUERY) == false)) {
+ nsurl_unref(url);
+
+ if (referrer != NULL) {
+ nsurl_unref(referrer);
+ }
+
+ if ((flags & BW_NAVIGATE_HISTORY) != 0) {
+ browser_window_history_add(bw,
+ bw->current_content,
+ bw->frag_id);
+ }
+
+ browser_window_update(bw, false);
+
+ if (bw->current_content != NULL) {
+ browser_window_refresh_url_bar(bw);
+ }
+ return NSERROR_OK;
+ }
+ }
+
+ 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(&params, 0, sizeof(params));
+
+ params.url = nsurl_ref(url);
+
+ if (referrer != NULL) {
+ params.referrer = nsurl_ref(referrer);
+ }
+
+ params.flags = flags;
+
+ if (post_urlenc != NULL) {
+ params.post_urlenc = strdup(post_urlenc);
+ }
+
+ if (post_multipart != NULL) {
+ params.post_multipart = fetch_multipart_data_clone(post_multipart);
+ }
+
+ if (parent != NULL) {
+ params.parent_charset = strdup(child.charset);
+ params.parent_quirks = child.quirks;
+ }
+
+ bw->internal_nav = is_internal;
+
+ if (is_internal) {
+ pass_params = &params;
+ } else {
+ /* At this point, we're navigating, so store the fetch parameters */
+ browser_window__free_fetch_parameters(&bw->loading_parameters);
+ memcpy(&bw->loading_parameters, &params, sizeof(params));
+ memset(&params, 0, sizeof(params));
+ pass_params = &bw->loading_parameters;
+ }
+
+ error = browser_window__navigate_internal(bw, pass_params);
+
+ nsurl_unref(url);
+
+ if (referrer != NULL) {
+ nsurl_unref(referrer);
+ }
+
+ if (is_internal) {
+ browser_window__free_fetch_parameters(&params);
+ }
+
+ return error;
+}
+
+
+/**
+ * Internal navigation handler for normal fetches
+ */
+static nserror
+navigate_internal_real(struct browser_window *bw,
+ struct browser_fetch_parameters *params)
+{
+ uint32_t fetch_flags = 0;
+ bool fetch_is_post;
+ llcache_post_data post;
+ hlcache_child_context child;
+ 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 */
+ cert_chain_free(bw->loading_cert_chain);
+ bw->loading_cert_chain = NULL;
+
+ /* Set up retrieval parameters */
+ if (!(params->flags & BW_NAVIGATE_UNVERIFIABLE)) {
+ fetch_flags |= LLCACHE_RETRIEVE_VERIFIABLE;
+ }
+
+ if (params->post_multipart != NULL) {
+ post.type = LLCACHE_POST_MULTIPART;
+ post.data.multipart = params->post_multipart;
+ } else if (params->post_urlenc != NULL) {
+ post.type = LLCACHE_POST_URL_ENCODED;
+ post.data.urlenc = params->post_urlenc;
+ }
+
+ if (params->parent_charset != NULL) {
+ child.charset = params->parent_charset;
+ child.quirks = params->parent_quirks;
+ }
+
+ browser_window_set_status(bw, messages_get("Loading"));
+ bw->history_add = (params->flags & BW_NAVIGATE_HISTORY);
+
+ /* Verifiable fetches may trigger a download */
+ if (!(params->flags & BW_NAVIGATE_UNVERIFIABLE)) {
+ fetch_flags |= HLCACHE_RETRIEVE_MAY_DOWNLOAD;
+ }
+
+ 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 (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) {
+ res = browser_window_refresh_url_bar_internal(bw,
+ params->url);
+ }
+ break;
+
+ case NSERROR_NO_FETCH_HANDLER: /* no handler for this type */
+ /** \todo does this always try and download even
+ * unverifiable content?
+ */
+ res = guit->misc->launch_url(params->url);
+ break;
+
+ default: /* report error to user */
+ browser_window_set_status(bw, messages_get_errorcode(res));
+ break;
+
+ }
+
+ /* Record time */
+ nsu_getmonotonic_ms(&bw->last_action);
+
+ return res;
+}
+
+
+/**
+ * Internal navigation handler for the authentication query handler
+ *
+ * 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_auth(struct browser_window *bw,
+ struct browser_fetch_parameters *params)
+{
+ char *userpass = NULL;
+ const char *username, *password, *realm, *siteurl;
+ nsurl *sitensurl;
+ nserror res;
+ bool is_login = false, is_cancel = false;
+
+ assert(params->post_multipart != NULL);
+
+ is_login = fetch_multipart_data_find(params->post_multipart, "login") != NULL;
+ is_cancel = fetch_multipart_data_find(params->post_multipart, "cancel") != NULL;
+
+ if (!(is_login || is_cancel)) {
+ /* This is a request, so pass it on */
+ return navigate_internal_real(bw, params);
+ }
+
+ if (is_cancel) {
+ /* We're processing a cancel, do a rough-and-ready nav to
+ * about:blank
+ */
+ browser_window__free_fetch_parameters(&bw->loading_parameters);
+ bw->loading_parameters.url = nsurl_ref(corestring_nsurl_about_blank);
+ bw->loading_parameters.flags = BW_NAVIGATE_NO_TERMINAL_HISTORY_UPDATE | BW_NAVIGATE_INTERNAL;
+ bw->internal_nav = true;
+ return browser_window__navigate_internal(bw, &bw->loading_parameters);
+ }
+
+ /* We're processing a "login" attempt from the form */
+
+ /* Retrieve the data */
+ username = fetch_multipart_data_find(params->post_multipart, "username");
+ password = fetch_multipart_data_find(params->post_multipart, "password");
+ realm = fetch_multipart_data_find(params->post_multipart, "realm");
+ siteurl = fetch_multipart_data_find(params->post_multipart, "siteurl");
+
+ if (username == NULL || password == NULL ||
+ realm == NULL || siteurl == NULL) {
+ /* Bad inputs, simply fail */
+ return NSERROR_INVALID;
+ }
+
+ /* Parse the URL */
+ res = nsurl_create(siteurl, &sitensurl);
+ if (res != NSERROR_OK) {
+ return res;
+ }
+
+ /* Construct the username/password */
+ res = browser_window__build_userpass(username, password, &userpass);
+ if (res != NSERROR_OK) {
+ nsurl_unref(sitensurl);
+ return res;
+ }
+
+ /* And let urldb know */
+ urldb_set_auth_details(sitensurl, realm, userpass);
+
+ /* Clean up */
+ free(userpass);
+ nsurl_unref(sitensurl);
+
+ /* Finally navigate to the original loading parameters */
+ bw->internal_nav = false;
+ return navigate_internal_real(bw, &bw->loading_parameters);
+}
+
+
+/**
+ * Internal navigation handler for the SSL/privacy 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_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) || siteurl == NULL) {
+ /* This is a request, so pass it on */
+ 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;
+
+ /* All our special URIs are in the about: scheme */
+ scheme = nsurl_get_component(params->url, NSURL_SCHEME);
+ if (scheme != corestring_lwc_about) {
+ lwc_string_unref(scheme);
+ goto normal_fetch;
+ }
+ lwc_string_unref(scheme);
+
+ /* Is it the auth query handler? */
+ path = nsurl_get_component(params->url, NSURL_PATH);
+ if (path == corestring_lwc_query_auth) {
+ lwc_string_unref(path);
+ return navigate_internal_query_auth(bw, params);
+ }
+ if (path == corestring_lwc_query_ssl) {
+ lwc_string_unref(path);
+ 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);
+ }
+
+ /* Fall through to a normal about: fetch */
+
+ normal_fetch:
+ return navigate_internal_real(bw, params);
+}
+
+
+/* Exported interface, documented in netsurf/browser_window.h */
+bool browser_window_up_available(struct browser_window *bw)
+{
+ bool result = false;
+
+ if (bw != NULL && bw->current_content != NULL) {
+ nsurl *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),
+ parent,
+ NSURL_COMPLETE) == false;
+ nsurl_unref(parent);
+ }
+ }
+
+ return result;
+}
+
+
+/* Exported interface, documented in netsurf/browser_window.h */
+nserror browser_window_navigate_up(struct browser_window *bw, bool new_window)
+{
+ nsurl *current, *parent;
+ nserror err;
+
+ if (bw == NULL)
+ return NSERROR_BAD_PARAMETER;
+
+ current = browser_window_access_url(bw);
+
+ err = nsurl_parent(current, &parent);
+ if (err != NSERROR_OK) {
+ return err;
+ }
+
+ if (nsurl_compare(current, parent, NSURL_COMPLETE) == true) {
+ /* Can't go up to parent from here */
+ nsurl_unref(parent);
+ return NSERROR_OK;
+ }
+
+ if (new_window) {
+ err = browser_window_create(BW_CREATE_CLONE,
+ parent, NULL, bw, NULL);
+ } else {
+ err = browser_window_navigate(bw, parent, NULL,
+ BW_NAVIGATE_HISTORY,
+ NULL, NULL, NULL);
+ }
+
+ nsurl_unref(parent);
+ return err;
+}
+
+
+/* Exported interface, documented in include/netsurf/browser_window.h */
+nsurl* browser_window_access_url(const struct browser_window *bw)
+{
+ assert(bw != NULL);
+
+ if (bw->current_content != NULL) {
+ return hlcache_handle_get_url(bw->current_content);
+
+ } else if (bw->loading_content != NULL) {
+ /* TODO: should we return this? */
+ return hlcache_handle_get_url(bw->loading_content);
+ }
+
+ return corestring_nsurl_about_blank;
+}
+
+
+/* Exported interface, documented in include/netsurf/browser_window.h */
+nserror
+browser_window_get_url(struct browser_window *bw, bool fragment,nsurl** url_out)
+{
+ nserror err;
+ nsurl *url;
+
+ assert(bw != NULL);
+
+ if (!fragment || bw->frag_id == NULL || bw->loading_content != NULL) {
+ /* If there's a loading content, then the bw->frag_id will have
+ * been trampled, possibly with a new frag_id, but we will
+ * still be returning the current URL, so in this edge case
+ * we just drop any fragment. */
+ url = nsurl_ref(browser_window_access_url(bw));
+
+ } else {
+ err = nsurl_refragment(browser_window_access_url(bw),
+ bw->frag_id, &url);
+ if (err != NSERROR_OK) {
+ return err;
+ }
+ }
+
+ *url_out = url;
+ return NSERROR_OK;
+}
+
+
+/* Exported interface, documented in netsurf/browser_window.h */
+const char* browser_window_get_title(struct browser_window *bw)
+{
+ assert(bw != NULL);
+
+ if (bw->current_content != NULL) {
+ return content_get_title(bw->current_content);
+ }
+
+ /* no content so return about:blank */
+ return nsurl_access(corestring_nsurl_about_blank);
+}
+
+
+/* Exported interface, documented in netsurf/browser_window.h */
+struct history * browser_window_get_history(struct browser_window *bw)
+{
+ assert(bw != NULL);
+
+ return bw->history;
+}
+
+
+/* Exported interface, documented in netsurf/browser_window.h */
+bool browser_window_has_content(struct browser_window *bw)
+{
+ assert(bw != NULL);
+
+ if (bw->current_content == NULL) {
+ return false;
+ }
+
+ return true;
+}
+
+
+/* Exported interface, documented in netsurf/browser_window.h */
+struct hlcache_handle *browser_window_get_content(struct browser_window *bw)
+{
+ return bw->current_content;
+}
+
+
+/* Exported interface, documented in netsurf/browser_window.h */
+nserror browser_window_get_extents(struct browser_window *bw, bool scaled,
+ int *width, int *height)
+{
+ assert(bw != NULL);
+
+ if (bw->current_content == NULL) {
+ *width = 0;
+ *height = 0;
+ return NSERROR_BAD_CONTENT;
+ }
+
+ *width = content_get_width(bw->current_content);
+ *height = content_get_height(bw->current_content);
+
+ if (scaled) {
+ *width *= bw->scale;
+ *height *= bw->scale;
+ }
+
+ return NSERROR_OK;
+}
+
+
+/* exported internal interface, documented in desktop/browser_private.h */
+nserror
+browser_window_get_dimensions(struct browser_window *bw,
+ int *width,
+ int *height)
+{
+ nserror res;
+ assert(bw);
+
+ if (bw->window == NULL) {
+ /* Core managed browser window */
+ *width = bw->width;
+ *height = bw->height;
+ res = NSERROR_OK;
+ } else {
+ /* Front end window */
+ res = guit->window->get_dimensions(bw->window, width, height);
+ }
+ return res;
+}
+
+
+/* Exported interface, documented in netsurf/browser_window.h */
+void
+browser_window_set_dimensions(struct browser_window *bw, int width, int height)
+{
+ assert(bw);
+
+ if (bw->window == NULL) {
+ /* Core managed browser window */
+ bw->width = width;
+ bw->height = height;
+ } else {
+ NSLOG(netsurf, INFO,
+ "Asked to set dimensions of front end window.");
+ assert(0);
+ }
+}
+
+
+/* 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;
+ struct browser_window *top = bw;
+
+ assert(bw);
+
+ if (bw->window == NULL) {
+ /* Core managed browser window */
+ browser_window_get_position(bw, true, &pos_x, &pos_y);
+
+ top = browser_window_get_root(bw);
+
+ rect->x0 += pos_x / bw->scale;
+ rect->y0 += pos_y / bw->scale;
+ rect->x1 += pos_x / bw->scale;
+ rect->y1 += pos_y / bw->scale;
+ }
+
+ rect->x0 *= top->scale;
+ rect->y0 *= top->scale;
+ rect->x1 *= top->scale;
+ rect->y1 *= top->scale;
+
+ return guit->window->invalidate(top->window, rect);
+}
+
+
+/* Exported interface, documented in netsurf/browser_window.h */
+void browser_window_stop(struct browser_window *bw)
+{
+ int children, index;
+
+ if (bw->loading_content != NULL) {
+ hlcache_handle_abort(bw->loading_content);
+ hlcache_handle_release(bw->loading_content);
+ bw->loading_content = NULL;
+ }
+
+ 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);
+ error = hlcache_handle_abort(bw->current_content);
+ assert(error == NSERROR_OK);
+ }
+
+ guit->misc->schedule(-1, browser_window_refresh, bw);
+
+ if (bw->children) {
+ children = bw->rows * bw->cols;
+ for (index = 0; index < children; index++)
+ browser_window_stop(&bw->children[index]);
+ }
+ if (bw->iframes) {
+ children = bw->iframe_count;
+ for (index = 0; index < children; index++)
+ browser_window_stop(&bw->iframes[index]);
+ }
+
+ if (bw->current_content != NULL) {
+ browser_window_refresh_url_bar(bw);
+ }
+
+ browser_window_stop_throbber(bw);
+}
+
+
+/* Exported interface, documented in netsurf/browser_window.h */
+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 NSERROR_INVALID;
+ }
+
+ if (all && content_get_type(bw->current_content) == CONTENT_HTML) {
+ struct html_stylesheet *sheets;
+ struct content_html_object *object;
+ unsigned int count;
+
+ c = bw->current_content;
+
+ /* invalidate objects */
+ object = html_get_objects(c, &count);
+
+ for (; object != NULL; object = object->next) {
+ if (object->content != NULL)
+ content_invalidate_reuse_data(object->content);
+ }
+
+ /* invalidate stylesheets */
+ sheets = html_get_stylesheets(c, &count);
+
+ for (i = STYLESHEET_START; i != count; i++) {
+ if (sheets[i].sheet != NULL) {
+ content_invalidate_reuse_data(sheets[i].sheet);
+ }
+ }
+ }
+
+ content_invalidate_reuse_data(bw->current_content);
+
+ reload_url = hlcache_handle_get_url(bw->current_content);
+
+ return browser_window_navigate(bw,
+ reload_url,
+ NULL,
+ BW_NAVIGATE_NONE,
+ NULL,
+ NULL,
+ NULL);
+}
+
+
+/* Exported interface, documented in netsurf/browser_window.h */
+void browser_window_set_status(struct browser_window *bw, const char *text)
+{
+ int text_len;
+ /* find topmost window */
+ while (bw->parent)
+ bw = bw->parent;
+
+ if ((bw->status.text != NULL) &&
+ (strcmp(text, bw->status.text) == 0)) {
+ /* status text is unchanged */
+ bw->status.match++;
+ return;
+ }
+
+ /* status text is changed */
+
+ text_len = strlen(text);
+
+ if ((bw->status.text == NULL) || (bw->status.text_len < text_len)) {
+ /* no current string allocation or it is not long enough */
+ free(bw->status.text);
+ bw->status.text = strdup(text);
+ bw->status.text_len = text_len;
+ } else {
+ /* current allocation has enough space */
+ memcpy(bw->status.text, text, text_len + 1);
+ }
+
+ bw->status.miss++;
+ guit->window->set_status(bw->window, bw->status.text);
+}
+
+
+/* Exported interface, documented in netsurf/browser_window.h */
+void browser_window_set_pointer(struct browser_window *bw,
+ browser_pointer_shape shape)
+{
+ struct browser_window *root = browser_window_get_root(bw);
+ gui_pointer_shape gui_shape;
+ bool loading;
+ uint64_t ms_now;
+
+ assert(root);
+ assert(root->window);
+
+ loading = ((bw->loading_content != NULL) ||
+ ((bw->current_content != NULL) &&
+ (content_get_status(bw->current_content) == CONTENT_STATUS_READY)));
+
+ nsu_getmonotonic_ms(&ms_now);
+
+ if (loading && ((ms_now - bw->last_action) < 1000)) {
+ /* If loading and less than 1 second since last link followed,
+ * force progress indicator pointer */
+ gui_shape = GUI_POINTER_PROGRESS;
+
+ } else if (shape == BROWSER_POINTER_AUTO) {
+ /* Up to browser window to decide */
+ if (loading) {
+ gui_shape = GUI_POINTER_PROGRESS;
+ } else {
+ gui_shape = GUI_POINTER_DEFAULT;
+ }
+
+ } else {
+ /* Use what we were told */
+ gui_shape = (gui_pointer_shape)shape;
+ }
+
+ guit->window->set_pointer(root->window, gui_shape);
+}
+
+
+/* exported function documented in netsurf/browser_window.h */
+nserror browser_window_schedule_reformat(struct browser_window *bw)
+{
+ if (bw->window == NULL) {
+ return NSERROR_BAD_PARAMETER;
+ }
+
+ return guit->misc->schedule(0, scheduled_reformat, bw);
+}
+
+
+/* exported function documented in netsurf/browser_window.h */
+void
+browser_window_reformat(struct browser_window *bw,
+ bool background,
+ int width,
+ int height)
+{
+ hlcache_handle *c = bw->current_content;
+
+ if (c == NULL)
+ return;
+
+ if (bw->browser_window_type != BROWSER_WINDOW_IFRAME) {
+ /* Iframe dimensions are already scaled in parent's layout */
+ width /= bw->scale;
+ height /= bw->scale;
+ }
+
+ if (bw->window == NULL) {
+ /* Core managed browser window; subtract scrollbar width */
+ width -= bw->scroll_y ? SCROLLBAR_WIDTH : 0;
+ height -= bw->scroll_x ? SCROLLBAR_WIDTH : 0;
+
+ width = width > 0 ? width : 0;
+ height = height > 0 ? height : 0;
+ }
+
+ content_reformat(c, background, width, height);
+}
+
+
+/* exported interface documented in netsurf/browser_window.h */
+nserror
+browser_window_set_scale(struct browser_window *bw, float scale, bool absolute)
+{
+ nserror res;
+
+ /* get top browser window */
+ while (bw->parent) {
+ bw = bw->parent;
+ }
+
+ if (!absolute) {
+ /* snap small values around 1.0 */
+ if ((scale + bw->scale) > (1.01 - scale) &&
+ (scale + bw->scale) < (0.99 + scale)) {
+ scale = 1.0;
+ } else {
+ scale += bw->scale;
+ }
+ }
+
+ /* clamp range between 0.1 and 10 (10% and 1000%) */
+ if (scale < SCALE_MINIMUM) {
+ scale = SCALE_MINIMUM;
+ } else if (scale > SCALE_MAXIMUM) {
+ scale = SCALE_MAXIMUM;
+ }
+
+ res = browser_window_set_scale_internal(bw, scale);
+ if (res == NSERROR_OK) {
+ browser_window_recalculate_frameset(bw);
+ }
+
+ return res;
+}
+
+
+/* exported interface documented in netsurf/browser_window.h */
+float browser_window_get_scale(struct browser_window *bw)
+{
+ if (bw == NULL) {
+ return 1.0;
+ }
+
+ return bw->scale;
+}
+
+
+/* exported interface documented in netsurf/browser_window.h */
+struct browser_window *
+browser_window_find_target(struct browser_window *bw,
+ const char *target,
+ browser_mouse_state mouse)
+{
+ struct browser_window *bw_target;
+ struct browser_window *top;
+ hlcache_handle *c;
+ int rdepth;
+ nserror error;
+
+ /* 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) {
+ target = html_get_base_target(c);
+ }
+ if (target == NULL) {
+ target = "_self";
+ }
+
+ /* allow the simple case of target="_blank" to be ignored if requested
+ */
+ if ((!(mouse & BROWSER_MOUSE_CLICK_2)) &&
+ (!((mouse & BROWSER_MOUSE_CLICK_2) &&
+ (mouse & BROWSER_MOUSE_MOD_2))) &&
+ (!nsoption_bool(target_blank))) {
+ /* not a mouse button 2 click
+ * not a mouse button 1 click with ctrl pressed
+ * configured to ignore target="_blank" */
+ if (!strcasecmp(target, "_blank"))
+ return bw;
+ }
+
+ /* handle reserved keywords */
+ if (((nsoption_bool(button_2_tab)) &&
+ (mouse & BROWSER_MOUSE_CLICK_2))||
+ ((!nsoption_bool(button_2_tab)) &&
+ ((mouse & BROWSER_MOUSE_CLICK_1) &&
+ (mouse & BROWSER_MOUSE_MOD_2))) ||
+ ((nsoption_bool(button_2_tab)) &&
+ (!strcasecmp(target, "_blank")))) {
+ /* open in new tab if:
+ * - button_2 opens in new tab and button_2 was pressed
+ * OR
+ * - button_2 doesn't open in new tabs and button_1 was
+ * pressed with ctrl held
+ * OR
+ * - button_2 opens in new tab and the link target is "_blank"
+ */
+ error = browser_window_create(BW_CREATE_TAB |
+ BW_CREATE_HISTORY |
+ BW_CREATE_CLONE,
+ NULL,
+ NULL,
+ bw,
+ &bw_target);
+ if (error != NSERROR_OK) {
+ return bw;
+ }
+ return bw_target;
+ } else if (((!nsoption_bool(button_2_tab)) &&
+ (mouse & BROWSER_MOUSE_CLICK_2)) ||
+ ((nsoption_bool(button_2_tab)) &&
+ ((mouse & BROWSER_MOUSE_CLICK_1) &&
+ (mouse & BROWSER_MOUSE_MOD_2))) ||
+ ((!nsoption_bool(button_2_tab)) &&
+ (!strcasecmp(target, "_blank")))) {
+ /* open in new window if:
+ * - button_2 doesn't open in new tabs and button_2 was pressed
+ * OR
+ * - button_2 opens in new tab and button_1 was pressed with
+ * ctrl held
+ * OR
+ * - button_2 doesn't open in new tabs and the link target is
+ * "_blank"
+ */
+ error = browser_window_create(BW_CREATE_HISTORY |
+ BW_CREATE_CLONE,
+ NULL,
+ NULL,
+ bw,
+ &bw_target);
+ if (error != NSERROR_OK) {
+ return bw;
+ }
+ return bw_target;
+ } else if (!strcasecmp(target, "_self")) {
+ return bw;
+ } else if (!strcasecmp(target, "_parent")) {
+ if (bw->parent)
+ return bw->parent;
+ return bw;
+ } else if (!strcasecmp(target, "_top")) {
+ while (bw->parent)
+ bw = bw->parent;
+ return bw;
+ }
+
+ /* find frame according to B.8, ie using the following priorities:
+ *
+ * 1) current frame
+ * 2) closest to front
+ */
+ rdepth = -1;
+ bw_target = NULL;
+ for (top = bw; top->parent; top = top->parent);
+ browser_window_find_target_internal(top, target, 0, bw, &rdepth,
+ &bw_target);
+ if (bw_target)
+ return bw_target;
+
+ /* we require a new window using the target name */
+ if (!nsoption_bool(target_blank))
+ return bw;
+
+ error = browser_window_create(BW_CREATE_CLONE | BW_CREATE_HISTORY,
+ NULL,
+ NULL,
+ bw,
+ &bw_target);
+ if (error != NSERROR_OK) {
+ return bw;
+ }
+
+ /* frame names should begin with an alphabetic character (a-z,A-Z),
+ * however in practice you get things such as '_new' and '2left'. The
+ * only real effect this has is when giving out names as it can be
+ * assumed that an author intended '_new' to create a new nameless
+ * window (ie '_blank') whereas in the case of '2left' the intention
+ * was for a new named window. As such we merely special case windows
+ * that begin with an underscore. */
+ if (target[0] != '_') {
+ bw_target->name = strdup(target);
+ }
+ return bw_target;
+}
+
+
+/* exported interface documented in netsurf/browser_window.h */
+void
+browser_window_mouse_track(struct browser_window *bw,
+ browser_mouse_state mouse,
+ int x, int y)
+{
+ browser_window_mouse_track_internal(bw,
+ mouse,
+ x / bw->scale,
+ y / bw->scale);
+}
+
+/* exported interface documented in netsurf/browser_window.h */
+void
+browser_window_mouse_click(struct browser_window *bw,
+ browser_mouse_state mouse,
+ int x, int y)
+{
+ browser_window_mouse_click_internal(bw,
+ mouse,
+ x / bw->scale,
+ y / bw->scale);
+}
+
+
+/* exported interface documented in netsurf/browser_window.h */
+void browser_window_page_drag_start(struct browser_window *bw, int x, int y)
+{
+ assert(bw != NULL);
+
+ browser_window_set_drag_type(bw, DRAGGING_PAGE_SCROLL, NULL);
+
+ bw->drag.start_x = x;
+ bw->drag.start_y = y;
+
+ if (bw->window != NULL) {
+ /* Front end window */
+ guit->window->get_scroll(bw->window,
+ &bw->drag.start_scroll_x,
+ &bw->drag.start_scroll_y);
+
+ guit->window->event(bw->window, GW_EVENT_SCROLL_START);
+ } else {
+ /* Core managed browser window */
+ bw->drag.start_scroll_x = scrollbar_get_offset(bw->scroll_x);
+ bw->drag.start_scroll_y = scrollbar_get_offset(bw->scroll_y);
+ }
+}
+
+
+/* exported interface documented in netsurf/browser_window.h */
+bool browser_window_back_available(struct browser_window *bw)
+{
+ if (bw != NULL && bw->internal_nav) {
+ /* Internal nav, back is possible */
+ return true;
+ }
+ return (bw && bw->history && browser_window_history_back_available(bw));
+}
+
+
+/* exported interface documented in netsurf/browser_window.h */
+bool browser_window_forward_available(struct browser_window *bw)
+{
+ return (bw && bw->history && browser_window_history_forward_available(bw));
+}
+
+/* exported interface documented in netsurf/browser_window.h */
+bool browser_window_reload_available(struct browser_window *bw)
+{
+ return (bw && bw->current_content && !bw->loading_content);
+}
+
+
+/* exported interface documented in netsurf/browser_window.h */
+bool browser_window_stop_available(struct browser_window *bw)
+{
+ return (bw && (bw->loading_content ||
+ (bw->current_content &&
+ (content_get_status(bw->current_content) !=
+ CONTENT_STATUS_DONE))));
+}
+
+/* exported interface documented in netsurf/browser_window.h */
+bool
+browser_window_exec(struct browser_window *bw, const char *src, size_t srclen)
+{
+ assert(bw != NULL);
+
+ if (!bw->current_content) {
+ NSLOG(netsurf, DEEPDEBUG, "Unable to exec, no content");
+ return false;
+ }
+
+ if (content_get_status(bw->current_content) != CONTENT_STATUS_DONE) {
+ NSLOG(netsurf, DEEPDEBUG, "Unable to exec, content not done");
+ return false;
+ }
+
+ /* Okay it should be safe, forward the request through to the content
+ * itself. Only HTML contents currently support executing code
+ */
+ return content_exec(bw->current_content, src, srclen);
+}
+
+
+/* exported interface documented in browser_window.h */
+nserror
+browser_window_console_log(struct browser_window *bw,
+ browser_window_console_source src,
+ const char *msg,
+ size_t msglen,
+ browser_window_console_flags flags)
+{
+ browser_window_console_flags log_level = flags & BW_CS_FLAG_LEVEL_MASK;
+ struct browser_window *root = browser_window_get_root(bw);
+
+ assert(msg != NULL);
+ /* We don't assert msglen > 0, if someone wants to log a real empty
+ * string then we won't stop them. It does sometimes happen from
+ * JavaScript for example.
+ */
+
+ /* bw is the target of the log, but root is where we log it */
+
+ NSLOG(netsurf, DEEPDEBUG, "Logging message in %p targetted at %p", root, bw);
+ NSLOG(netsurf, DEEPDEBUG, "Log came from %s",
+ ((src == BW_CS_INPUT) ? "user input" :
+ (src == BW_CS_SCRIPT_ERROR) ? "script error" :
+ (src == BW_CS_SCRIPT_CONSOLE) ? "script console" :
+ "unknown input location"));
+
+ switch (log_level) {
+ case BW_CS_FLAG_LEVEL_DEBUG:
+ NSLOG(netsurf, DEBUG, "%.*s", (int)msglen, msg);
+ break;
+ case BW_CS_FLAG_LEVEL_LOG:
+ NSLOG(netsurf, VERBOSE, "%.*s", (int)msglen, msg);
+ break;
+ case BW_CS_FLAG_LEVEL_INFO:
+ NSLOG(netsurf, INFO, "%.*s", (int)msglen, msg);
+ break;
+ case BW_CS_FLAG_LEVEL_WARN:
+ NSLOG(netsurf, WARNING, "%.*s", (int)msglen, msg);
+ break;
+ case BW_CS_FLAG_LEVEL_ERROR:
+ NSLOG(netsurf, ERROR, "%.*s", (int)msglen, msg);
+ break;
+ default:
+ /* Unreachable */
+ break;
+ }
+
+ guit->window->console_log(root->window, src, msg, msglen, flags);
+
+ return NSERROR_OK;
+}
+
+
+/* Exported interface, documented in browser_private.h */
+nserror
+browser_window__reload_current_parameters(struct browser_window *bw)
+{
+ assert(bw != NULL);
+
+ if (bw->current_parameters.post_urlenc != NULL) {
+ free(bw->current_parameters.post_urlenc);
+ bw->current_parameters.post_urlenc = NULL;
+ }
+
+ if (bw->current_parameters.post_multipart != NULL) {
+ fetch_multipart_data_destroy(bw->current_parameters.post_multipart);
+ bw->current_parameters.post_multipart = NULL;
+ }
+
+ if (bw->current_parameters.url == NULL) {
+ /* We have never navigated so go to about:blank */
+ bw->current_parameters.url = nsurl_ref(corestring_nsurl_about_blank);
+ }
+
+ 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..580750805 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
*
@@ -781,8 +788,7 @@ struct treeview_callback_table cm_tree_cb_t = {
/* Exported interface, documented in cookie_manager.h */
-nserror cookie_manager_init(struct core_window_callback_table *cw_t,
- void *core_window_handle)
+nserror cookie_manager_init(void *core_window_handle)
{
nserror err;
@@ -810,7 +816,7 @@ nserror cookie_manager_init(struct core_window_callback_table *cw_t,
/* Create the cookie manager treeview */
err = treeview_create(&cm_ctx.tree, &cm_tree_cb_t,
COOKIE_M_N_FIELDS, cm_ctx.fields,
- cw_t, core_window_handle,
+ core_window_handle,
TREEVIEW_NO_MOVES |
TREEVIEW_DEL_EMPTY_DIRS |
TREEVIEW_SEARCHABLE);
diff --git a/desktop/cookie_manager.h b/desktop/cookie_manager.h
index 4ae74a25f..532030b03 100644
--- a/desktop/cookie_manager.h
+++ b/desktop/cookie_manager.h
@@ -31,7 +31,6 @@
struct redraw_context;
struct cookie_data;
-struct core_window_callback_table;
struct rect;
/**
@@ -42,12 +41,10 @@ struct rect;
*
* This must be called before any other cookie_manager_* function.
*
- * \param cw_t Callback table for core_window containing the treeview
- * \param core_window_handle The core_window in which the treeview is shown
+ * \param core_window_handle The core_window in which the cookie view is shown
* \return NSERROR_OK on success, appropriate error otherwise
*/
-nserror cookie_manager_init(struct core_window_callback_table *cw_t,
- void *core_window_handle);
+nserror cookie_manager_init(void *core_window_handle);
/**
* Finalise the cookie manager.
@@ -76,6 +73,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/cw_helper.c b/desktop/cw_helper.c
new file mode 100644
index 000000000..0e3cef74e
--- /dev/null
+++ b/desktop/cw_helper.c
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2019 Daniel Silverstone <dsilvers@netsurf-browser.org>
+ * Copyright 2019 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
+ *
+ * Helpers to simplify core use of corewindow.
+ */
+
+#include "utils/errors.h"
+#include "netsurf/browser.h"
+#include "netsurf/core_window.h"
+#include "netsurf/types.h"
+#include "css/utils.h"
+#include "desktop/cw_helper.h"
+#include "desktop/gui_internal.h"
+
+/* exported interface documented in cw_helper.h */
+nserror cw_helper_scroll_visible(struct core_window *cw_h, const struct rect *r)
+{
+ nserror err;
+ int height;
+ int width;
+ int x0;
+ int y0;
+ int x1;
+ int y1;
+
+ assert(cw_h != NULL);
+
+ err = guit->corewindow->get_dimensions(cw_h, &width, &height);
+ if (err != NSERROR_OK) {
+ return err;
+ }
+
+ guit->corewindow->get_scroll(cw_h, &x0, &y0);
+ if (err != NSERROR_OK) {
+ return err;
+ }
+
+ y1 = y0 + height;
+ x1 = x0 + width;
+
+ if (r->y1 > y1) {
+ /* The bottom of the rectangle is off the bottom of the
+ * window, so scroll down to fit it
+ */
+ y0 = r->y1 - height;
+ }
+ if (r->y0 < y0) {
+ /* The top of the rectangle is off the top of the window,
+ * so scroll up to fit it
+ */
+ y0 = r->y0;
+ }
+ if (r->x1 > x1) {
+ /* The right of the rectangle is off the right of the window
+ * so scroll right to fit it
+ */
+ x0 = r->x1 - width;
+ }
+ if (r->x0 < x0) {
+ /* The left of the rectangle is off the left of the window
+ * so scroll left to fit it
+ */
+ x0 = r->x0;
+ }
+
+ return guit->corewindow->set_scroll(cw_h, x0, y0);
+}
diff --git a/desktop/cw_helper.h b/desktop/cw_helper.h
new file mode 100644
index 000000000..e8070ab06
--- /dev/null
+++ b/desktop/cw_helper.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2019 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
+ *
+ * Helpers to simplify core use of corewindow.
+ */
+
+#ifndef NETSURF_DESKTOP_CW_HELPER_H_
+#define NETSURF_DESKTOP_CW_HELPER_H_
+
+struct rect;
+struct core_window;
+
+/**
+ * Scroll a core window to make the given rectangle visible.
+ *
+ * \param[in] cw_t The core window callback table to use.
+ * \param[in] cw_h The core window's handle.
+ * \param[in] r The rectangle to make visisble by scrolling.
+ * \return NSERROR_OK on success or appropriate error code
+ */
+nserror cw_helper_scroll_visible(struct core_window *cw_h, const struct rect *r);
+
+#endif
diff --git a/desktop/download.c b/desktop/download.c
index 76c294977..4931371fb 100644
--- a/desktop/download.c
+++ b/desktop/download.c
@@ -29,6 +29,7 @@
#include "utils/corestrings.h"
#include "utils/http.h"
#include "utils/utils.h"
+#include "utils/log.h"
#include "desktop/download.h"
#include "netsurf/download.h"
#include "desktop/gui_internal.h"
@@ -41,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 */
@@ -93,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 */
@@ -107,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,
@@ -180,6 +182,9 @@ static nserror download_callback(llcache_handle *handle,
nserror error = NSERROR_OK;
switch (event->type) {
+ case LLCACHE_EVENT_GOT_CERTS:
+ /* Nominally not interested in these */
+ break;
case LLCACHE_EVENT_HAD_HEADERS:
error = download_context_process_headers(ctx);
if (error != NSERROR_OK) {
@@ -223,7 +228,7 @@ static nserror download_callback(llcache_handle *handle,
case LLCACHE_EVENT_ERROR:
if (ctx->window != NULL)
- guit->download->error(ctx->window, event->data.msg);
+ guit->download->error(ctx->window, event->data.error.msg);
else
download_context_destroy(ctx);
@@ -295,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/font_haru.c b/desktop/font_haru.c
index f92d89197..4ee9824f0 100644
--- a/desktop/font_haru.c
+++ b/desktop/font_haru.c
@@ -352,7 +352,7 @@ bool haru_nsfont_apply_style(const plot_font_style_t *fstyle,
if (size <= 0)
return true;
- size /= FONT_SIZE_SCALE;
+ size /= PLOT_STYLE_SCALE;
if (size > HPDF_MAX_FONTSIZE)
size = HPDF_MAX_FONTSIZE;
diff --git a/desktop/frames.c b/desktop/frames.c
index e22287630..85f18793e 100644
--- a/desktop/frames.c
+++ b/desktop/frames.c
@@ -33,8 +33,9 @@
#include "utils/utils.h"
#include "netsurf/content.h"
#include "content/hlcache.h"
-#include "render/html.h"
-#include "render/box.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];
@@ -436,7 +346,9 @@ void browser_window_recalculate_frameset(struct browser_window *bw)
/* window dimensions */
if (!bw->parent) {
- browser_window_get_dimensions(bw, &bw_width, &bw_height, true);
+ browser_window_get_dimensions(bw, &bw_width, &bw_height);
+ bw_width /= bw->scale;
+ bw_height /= bw->scale;
bw->x = 0;
bw->y = 0;
bw->width = bw_width;
@@ -643,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);
}
@@ -700,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..b64648e67 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) {
@@ -721,8 +723,7 @@ struct treeview_callback_table gh_tree_cb_t = {
/* Exported interface, documented in global_history.h */
-nserror global_history_init(struct core_window_callback_table *cw_t,
- void *core_window_handle)
+nserror global_history_init(void *core_window_handle)
{
nserror err;
@@ -753,7 +754,7 @@ nserror global_history_init(struct core_window_callback_table *cw_t,
/* Create the global history treeview */
err = treeview_create(&gh_ctx.tree, &gh_tree_cb_t,
N_FIELDS, gh_ctx.fields,
- cw_t, core_window_handle,
+ core_window_handle,
TREEVIEW_NO_MOVES | TREEVIEW_DEL_EMPTY_DIRS |
TREEVIEW_SEARCHABLE);
if (err != NSERROR_OK) {
diff --git a/desktop/global_history.h b/desktop/global_history.h
index 213a014f3..13e1cf398 100644
--- a/desktop/global_history.h
+++ b/desktop/global_history.h
@@ -25,7 +25,6 @@
#include "utils/errors.h"
#include "netsurf/mouse.h"
-struct core_window_callback_table;
struct redraw_context;
struct nsurl;
struct rect;
@@ -38,12 +37,10 @@ struct rect;
*
* This must be called before any other global_history_* function.
*
- * \param cw_t Callback table for core_window containing the treeview.
- * \param core_window_handle The core_window in which the treeview is shown.
+ * \param core_window_handle The core_window in which the global history is shown.
* \return NSERROR_OK on success, appropriate error otherwise
*/
-nserror global_history_init(struct core_window_callback_table *cw_t,
- void *core_window_handle);
+nserror global_history_init(void *core_window_handle);
/**
* Finalise the global history.
diff --git a/desktop/gui_factory.c b/desktop/gui_factory.c
index ca9eff1da..27365254a 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"
@@ -34,6 +37,7 @@
#include "netsurf/fetch.h"
#include "netsurf/misc.h"
#include "netsurf/window.h"
+#include "netsurf/core_window.h"
#include "netsurf/search.h"
#include "netsurf/clipboard.h"
#include "netsurf/utf8.h"
@@ -55,14 +59,6 @@ static nserror gui_default_window_set_url(struct gui_window *g, struct nsurl *ur
return NSERROR_OK;
}
-static void gui_default_window_start_throbber(struct gui_window *g)
-{
-}
-
-static void gui_default_window_stop_throbber(struct gui_window *g)
-{
-}
-
static bool gui_default_window_drag_start(struct gui_window *g,
gui_drag_type type,
const struct rect *rect)
@@ -82,17 +78,6 @@ static void gui_default_window_set_icon(struct gui_window *g,
{
}
-
-static void gui_default_window_new_content(struct gui_window *g)
-{
-}
-
-
-static bool gui_default_window_scroll_start(struct gui_window *g)
-{
- return true;
-}
-
static void gui_default_window_set_pointer(struct gui_window *g,
gui_pointer_shape shape)
{
@@ -109,10 +94,6 @@ static void gui_default_window_place_caret(struct gui_window *g,
{
}
-static void gui_default_window_remove_caret(struct gui_window *g)
-{
-}
-
static void gui_default_window_create_form_select_menu(struct gui_window *g,
struct form_control *control)
{
@@ -135,7 +116,13 @@ static void gui_default_window_drag_save_selection(struct gui_window *g,
{
}
-static void gui_default_window_start_selection(struct gui_window *g)
+
+static void
+gui_default_console_log(struct gui_window *gw,
+ browser_window_console_source src,
+ const char *msg,
+ size_t msglen,
+ browser_window_console_flags flags)
{
}
@@ -167,7 +154,7 @@ static nserror verify_window_register(struct gui_window_table *gwt)
if (gwt->get_dimensions == NULL) {
return NSERROR_BAD_PARAMETER;
}
- if (gwt->update_extent == NULL) {
+ if (gwt->event == NULL) {
return NSERROR_BAD_PARAMETER;
}
@@ -191,27 +178,12 @@ static nserror verify_window_register(struct gui_window_table *gwt)
if (gwt->place_caret == NULL) {
gwt->place_caret = gui_default_window_place_caret;
}
- if (gwt->remove_caret == NULL) {
- gwt->remove_caret = gui_default_window_remove_caret;
- }
- if (gwt->start_throbber == NULL) {
- gwt->start_throbber = gui_default_window_start_throbber;
- }
- if (gwt->stop_throbber == NULL) {
- gwt->stop_throbber = gui_default_window_stop_throbber;
- }
if (gwt->drag_start == NULL) {
gwt->drag_start = gui_default_window_drag_start;
}
if (gwt->save_link == NULL) {
gwt->save_link = gui_default_window_save_link;
}
- if (gwt->new_content == NULL) {
- gwt->new_content = gui_default_window_new_content;
- }
- if (gwt->scroll_start == NULL) {
- gwt->scroll_start = gui_default_window_scroll_start;
- }
if (gwt->create_form_select_menu == NULL) {
gwt->create_form_select_menu =
gui_default_window_create_form_select_menu;
@@ -225,14 +197,83 @@ static nserror verify_window_register(struct gui_window_table *gwt)
if (gwt->drag_save_selection == NULL) {
gwt->drag_save_selection = gui_default_window_drag_save_selection;
}
- if (gwt->start_selection == NULL) {
- gwt->start_selection = gui_default_window_start_selection;
+ if (gwt->console_log == NULL) {
+ gwt->console_log = gui_default_console_log;
}
return NSERROR_OK;
}
+static nserror gui_default_corewindow_invalidate(struct core_window *cw, const struct rect *rect)
+{
+ return NSERROR_OK;
+}
+
+static nserror gui_default_corewindow_set_extent(struct core_window *cw, int width, int height)
+{
+ return NSERROR_OK;
+}
+
+static nserror gui_default_corewindow_set_scroll(struct core_window *cw, int x, int y)
+{
+ return NSERROR_OK;
+}
+
+static nserror gui_default_corewindow_get_scroll(const struct core_window *cw, int *x, int *y)
+{
+ return NSERROR_OK;
+}
+
+static nserror gui_default_corewindow_get_dimensions(const struct core_window *cw, int *width, int *height)
+{
+ return NSERROR_OK;
+}
+
+static nserror gui_default_corewindow_dragstatus(struct core_window *cw, core_window_drag_status ds)
+{
+ return NSERROR_OK;
+}
+
+static struct core_window_table default_corewindow_table = {
+ .invalidate = gui_default_corewindow_invalidate,
+ .set_extent = gui_default_corewindow_set_extent,
+ .set_scroll = gui_default_corewindow_set_scroll,
+ .get_scroll = gui_default_corewindow_get_scroll,
+ .get_dimensions = gui_default_corewindow_get_dimensions,
+ .drag_status = gui_default_corewindow_dragstatus,
+};
+
+/** verify corewindow window table is valid */
+static nserror verify_corewindow_register(struct core_window_table *cwt)
+{
+ /* check table is present */
+ if (cwt == NULL) {
+ return NSERROR_BAD_PARAMETER;
+ }
+
+ /* all enties are mandantory */
+ if (cwt->invalidate == NULL) {
+ return NSERROR_BAD_PARAMETER;
+ }
+ if (cwt->set_extent == NULL) {
+ return NSERROR_BAD_PARAMETER;
+ }
+ if (cwt->set_scroll == NULL) {
+ return NSERROR_BAD_PARAMETER;
+ }
+ if (cwt->get_scroll == NULL) {
+ return NSERROR_BAD_PARAMETER;
+ }
+ if (cwt->get_dimensions == NULL) {
+ return NSERROR_BAD_PARAMETER;
+ }
+ if (cwt->drag_status == NULL) {
+ return NSERROR_BAD_PARAMETER;
+ }
+
+ return NSERROR_OK;
+}
static struct gui_download_window *
gui_default_download_create(download_context *ctx, struct gui_window *parent)
@@ -503,6 +544,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)
{
@@ -529,6 +580,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;
}
@@ -565,7 +622,7 @@ static nserror verify_file_register(struct gui_file_table *gft)
* verify bitmap table is valid
*
* \param gbt The bitmap table to verify.
- * \return NSERROR_OK if teh table is valid else NSERROR_BAD_PARAMETER.
+ * \return NSERROR_OK if the table is valid else NSERROR_BAD_PARAMETER.
*/
static nserror verify_bitmap_register(struct gui_bitmap_table *gbt)
{
@@ -591,10 +648,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;
}
@@ -611,14 +664,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;
}
@@ -670,21 +715,18 @@ 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)
+static nserror gui_default_401login_open(
+ nsurl *url, const char *realm,
+ const char *username, const char *password,
+ nserror (*cb)(nsurl *url, const char * realm,
+ const char *username,
+ const char *password,
+ void *pw),
+ void *cbpw)
{
return NSERROR_NOT_IMPLEMENTED;
}
-static void gui_default_401login_open(nsurl *url, const char *realm,
- nserror (*cb)(bool proceed, void *pw), void *cbpw)
-{
- cb(false, cbpw);
-}
-
static void
gui_default_pdf_password(char **owner_pass, char **user_pass, char *path)
{
@@ -692,6 +734,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)
{
@@ -704,9 +752,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) {
@@ -715,15 +760,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;
}
@@ -777,6 +822,17 @@ nserror netsurf_register(struct netsurf_table *gt)
/* optional tables */
+ /* core window table */
+ if (gt->corewindow == NULL) {
+ /* set default core window table */
+ gt->corewindow = &default_corewindow_table;
+ }
+ err = verify_corewindow_register(gt->corewindow);
+ if (err != NSERROR_OK) {
+ return err;
+ }
+
+
/* file table */
if (gt->file == NULL) {
gt->file = default_file_table;
diff --git a/desktop/gui_table.h b/desktop/gui_table.h
index 0ac9f0417..2a7c648bb 100644
--- a/desktop/gui_table.h
+++ b/desktop/gui_table.h
@@ -66,6 +66,15 @@ struct netsurf_table {
struct gui_window_table *window;
/**
+ * Core window table.
+ *
+ * Provides all operations which affect core ui windows of a frontend.
+ *
+ * The table is optional and may be NULL which does nothing.
+ */
+ struct core_window_table *corewindow;
+
+ /**
* Download table.
*
* operations table for the download windows.
diff --git a/desktop/hotlist.c b/desktop/hotlist.c
index 4bdd7c8cb..57c582d21 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) /
@@ -1314,7 +1324,7 @@ nserror hotlist_init(
/* Create the hotlist treeview */
err = treeview_create(&hl_ctx.tree, &hl_tree_cb_t,
- HL_N_FIELDS, hl_ctx.fields, NULL, NULL,
+ HL_N_FIELDS, hl_ctx.fields, NULL,
TREEVIEW_SEARCHABLE);
if (err != NSERROR_OK) {
free(hl_ctx.save_path);
@@ -1341,13 +1351,12 @@ nserror hotlist_init(
/* Exported interface, documented in hotlist.h */
-nserror hotlist_manager_init(struct core_window_callback_table *cw_t,
- void *core_window_handle)
+nserror hotlist_manager_init(void *core_window_handle)
{
nserror err;
/* Create the hotlist treeview */
- err = treeview_cw_attach(hl_ctx.tree, cw_t, core_window_handle);
+ err = treeview_cw_attach(hl_ctx.tree, core_window_handle);
if (err != NSERROR_OK) {
return err;
}
@@ -1396,6 +1405,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 +1628,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/hotlist.h b/desktop/hotlist.h
index 571f4de97..aa9a57137 100644
--- a/desktop/hotlist.h
+++ b/desktop/hotlist.h
@@ -25,7 +25,6 @@
#include "utils/errors.h"
#include "netsurf/mouse.h"
-struct core_window_callback_table;
struct redraw_context;
struct nsurl;
struct rect;
@@ -47,9 +46,7 @@ struct rect;
* \param save_path The path to save hotlist to, or NULL for read-only mode.
* \return NSERROR_OK on success, appropriate error otherwise
*/
-nserror hotlist_init(
- const char *load_path,
- const char *save_path);
+nserror hotlist_init(const char *load_path, const char *save_path);
/**
* Initialise the hotlist manager.
@@ -58,12 +55,10 @@ nserror hotlist_init(
*
* The provided core window handle must be valid until hotlist_fini is called.
*
- * \param cw_t Callback table for core_window containing the treeview
- * \param core_window_handle The handle in which the treeview is shown
+ * \param core_window_handle The handle in which the hotlist is shown
* \return NSERROR_OK on success, appropriate error otherwise
*/
-nserror hotlist_manager_init(struct core_window_callback_table *cw_t,
- void *core_window_handle);
+nserror hotlist_manager_init(void *core_window_handle);
/**
diff --git a/desktop/knockout.c b/desktop/knockout.c
index 6dbf4ebcf..127a48cdb 100644
--- a/desktop/knockout.c
+++ b/desktop/knockout.c
@@ -671,7 +671,6 @@ knockout_plot_polygon(const struct redraw_context *ctx,
* \param pstyle Style controlling the path plot.
* \param p elements of path
* \param n nunber of elements on path
- * \param width The width of the path
* \param transform A transform to apply to the path.
* \return NSERROR_OK on success else error code.
*/
@@ -680,14 +679,13 @@ knockout_plot_path(const struct redraw_context *ctx,
const plot_style_t *pstyle,
const float *p,
unsigned int n,
- float width,
const float transform[6])
{
nserror res;
nserror ffres;
ffres = knockout_plot_flush(ctx);
- res = real_plot.path(ctx, pstyle, p, n, width, transform);
+ res = real_plot.path(ctx, pstyle, p, n, transform);
/* return the first error */
if ((res != NSERROR_OK) && (ffres == NSERROR_OK)) {
diff --git a/desktop/local_history.c b/desktop/local_history.c
index 3219de90c..4f1299879 100644
--- a/desktop/local_history.c
+++ b/desktop/local_history.c
@@ -24,29 +24,33 @@
#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"
#include "netsurf/core_window.h"
#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"
-#define WIDTH 100
-#define HEIGHT 86
-
/**
* local history viewer context
*/
struct local_history_session {
struct browser_window *bw;
- struct core_window_callback_table *cw_t;
void *core_window_handle;
+ struct history_entry *cursor;
};
@@ -55,7 +59,7 @@ struct local_history_session {
*/
static plot_style_t pstyle_line = {
.stroke_type = PLOT_OP_TYPE_SOLID,
- .stroke_width = 2,
+ .stroke_width = plot_style_int_to_fixed(2),
};
@@ -72,7 +76,7 @@ static plot_style_t pstyle_bg = {
*/
static plot_style_t pstyle_rect = {
.stroke_type = PLOT_OP_TYPE_SOLID,
- .stroke_width = 1,
+ .stroke_width = plot_style_int_to_fixed(1),
};
@@ -81,7 +85,16 @@ static plot_style_t pstyle_rect = {
*/
static plot_style_t pstyle_rect_sel = {
.stroke_type = PLOT_OP_TYPE_SOLID,
- .stroke_width = 3,
+ .stroke_width = plot_style_int_to_fixed(3),
+};
+
+
+/**
+ * plot style for drawing rectangle round the cursor node
+ */
+static plot_style_t pstyle_rect_cursor = {
+ .stroke_type = PLOT_OP_TYPE_DASH,
+ .stroke_width = plot_style_int_to_fixed(3),
};
@@ -90,7 +103,7 @@ static plot_style_t pstyle_rect_sel = {
*/
static plot_font_style_t pfstyle_node = {
.family = PLOT_FONT_FAMILY_SANS_SERIF,
- .size = 8 * FONT_SIZE_SCALE,
+ .size = 8 * PLOT_STYLE_SCALE,
.weight = 400,
.flags = FONTF_NONE,
};
@@ -101,7 +114,7 @@ static plot_font_style_t pfstyle_node = {
*/
static plot_font_style_t pfstyle_node_sel = {
.family = PLOT_FONT_FAMILY_SANS_SERIF,
- .size = 8 * FONT_SIZE_SCALE,
+ .size = 8 * PLOT_STYLE_SCALE,
.weight = 900,
.flags = FONTF_NONE,
};
@@ -120,6 +133,7 @@ static plot_font_style_t pfstyle_node_sel = {
static nserror
redraw_entry(struct history *history,
struct history_entry *entry,
+ struct history_entry *cursor,
struct rect *clip,
int x, int y,
const struct redraw_context *ctx)
@@ -149,7 +163,8 @@ redraw_entry(struct history *history,
entry->page.bitmap,
entry->x + x,
entry->y + y,
- WIDTH, HEIGHT,
+ LOCAL_HISTORY_WIDTH,
+ LOCAL_HISTORY_HEIGHT,
0xffffff,
0);
if (res != NSERROR_OK) {
@@ -159,15 +174,27 @@ redraw_entry(struct history *history,
rect.x0 = entry->x - 1 + x;
rect.y0 = entry->y - 1 + y;
- rect.x1 = entry->x + x + WIDTH;
- rect.y1 = entry->y + y + HEIGHT;
- res = ctx->plot->rectangle(ctx, pstyle, &rect);
- if (res != NSERROR_OK) {
- return res;
+ rect.x1 = entry->x + x + LOCAL_HISTORY_WIDTH;
+ rect.y1 = entry->y + y + LOCAL_HISTORY_HEIGHT;
+
+ /* 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 += 1;
+ rect.y1 += 1;
+ ctx->plot->rectangle(ctx, &pstyle_rect_cursor, &rect);
}
res = guit->layout->position(plot_style_font, entry->page.title,
- strlen(entry->page.title), WIDTH,
+ strlen(entry->page.title), LOCAL_HISTORY_WIDTH,
&char_offset, &actual_x);
if (res != NSERROR_OK) {
return res;
@@ -176,7 +203,7 @@ redraw_entry(struct history *history,
res = ctx->plot->text(ctx,
pfstyle,
entry->x + x,
- entry->y + HEIGHT + 12 + y,
+ entry->y + LOCAL_HISTORY_HEIGHT + 12 + y,
entry->page.title,
char_offset);
if (res != NSERROR_OK) {
@@ -185,34 +212,34 @@ redraw_entry(struct history *history,
/* for each child node draw a line and recurse redraw into it */
for (child = entry->forward; child; child = child->next) {
- rect.x0 = entry->x + WIDTH + x;
- rect.y0 = entry->y + HEIGHT / 2 + y;
- rect.x1 = entry->x + WIDTH + tailsize + x;
- rect.y1 = entry->y + HEIGHT / 2 + y;
+ rect.x0 = entry->x + LOCAL_HISTORY_WIDTH + x;
+ rect.y0 = entry->y + LOCAL_HISTORY_HEIGHT / 2 + y;
+ rect.x1 = entry->x + LOCAL_HISTORY_WIDTH + tailsize + x;
+ rect.y1 = entry->y + LOCAL_HISTORY_HEIGHT / 2 + y;
res = ctx->plot->line(ctx, &pstyle_line, &rect);
if (res != NSERROR_OK) {
return res;
}
- rect.x0 = entry->x + WIDTH + tailsize + x;
- rect.y0 = entry->y + HEIGHT / 2 + y;
+ rect.x0 = entry->x + LOCAL_HISTORY_WIDTH + tailsize + x;
+ rect.y0 = entry->y + LOCAL_HISTORY_HEIGHT / 2 + y;
rect.x1 = child->x - tailsize + x;
- rect.y1 = child->y + HEIGHT / 2 + y;
+ rect.y1 = child->y + LOCAL_HISTORY_HEIGHT / 2 + y;
res = ctx->plot->line(ctx, &pstyle_line, &rect);
if (res != NSERROR_OK) {
return res;
}
rect.x0 = child->x - tailsize + x;
- rect.y0 = child->y + HEIGHT / 2 + y;
+ rect.y0 = child->y + LOCAL_HISTORY_HEIGHT / 2 + y;
rect.x1 = child->x + x;
- rect.y1 = child->y + HEIGHT / 2 + y;
+ rect.y1 = child->y + LOCAL_HISTORY_HEIGHT / 2 + y;
res = ctx->plot->line(ctx, &pstyle_line, &rect);
if (res != NSERROR_OK) {
return res;
}
- res = redraw_entry(history, child, clip, x, y, ctx);
+ res = redraw_entry(history, child, cursor, clip, x, y, ctx);
if (res != NSERROR_OK) {
return res;
}
@@ -241,9 +268,9 @@ find_entry_position(struct history_entry *entry, int x, int y)
}
if ((entry->x <= x) &&
- (x <= entry->x + WIDTH) &&
+ (x <= entry->x + LOCAL_HISTORY_WIDTH) &&
(entry->y <= y) &&
- (y <= entry->y + HEIGHT)) {
+ (y <= entry->y + LOCAL_HISTORY_HEIGHT)) {
return entry;
}
@@ -257,43 +284,51 @@ find_entry_position(struct history_entry *entry, int x, int y)
return NULL;
}
+/* exported interface documented in desktop/local_history.h */
+nserror
+local_history_scroll_to_cursor(struct local_history_session *session)
+{
+ rect cursor;
+
+ if (session->cursor == NULL) {
+ return NSERROR_OK;
+ }
+
+ cursor.x0 = session->cursor->x - LOCAL_HISTORY_RIGHT_MARGIN / 2;
+ cursor.y0 = session->cursor->y - LOCAL_HISTORY_BOTTOM_MARGIN / 2;
+ cursor.x1 = cursor.x0 + LOCAL_HISTORY_WIDTH +
+ LOCAL_HISTORY_RIGHT_MARGIN / 2;
+ cursor.y1 = cursor.y0 + LOCAL_HISTORY_HEIGHT +
+ LOCAL_HISTORY_BOTTOM_MARGIN / 2;
+
+ return cw_helper_scroll_visible(session->core_window_handle, &cursor);
+}
/* exported interface documented in desktop/local_history.h */
nserror
-local_history_init(struct core_window_callback_table *cw_t,
- void *core_window_handle,
+local_history_init(void *core_window_handle,
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;
+ 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_sel.stroke_colour);
- if (res != NSERROR_OK) {
- return res;
- }
- pfstyle_node_sel.foreground = pstyle_rect_sel.stroke_colour;
+ 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) {
return NSERROR_NOMEM;
}
- nses->cw_t = cw_t;
nses->core_window_handle = core_window_handle;
local_history_set(nses, bw);
@@ -338,11 +373,13 @@ local_history_redraw(struct local_history_session *session,
ctx->plot->clip(ctx, &r);
ctx->plot->rectangle(ctx, &pstyle_bg, &r);
- return redraw_entry(session->bw->history,
- session->bw->history->start,
- clip,
- x, y,
- ctx);
+ return redraw_entry(
+ session->bw->history,
+ session->bw->history->start,
+ session->cursor,
+ clip,
+ x, y,
+ ctx);
}
/* exported interface documented in desktop/local_history.h */
@@ -385,10 +422,120 @@ local_history_mouse_action(struct local_history_session *session,
return NSERROR_OK;
}
+/**
+ * Determine the point on the parent line where this history line branches.
+ *
+ * If `branch_point` gets set then there is a guarantee that (a) `ent` is
+ * a transitive child (forward) of that point. and (b) `branch_point` has a
+ * parent.
+ *
+ * \param[in] ent The entry to work backward from
+ * \param[out] branch_point The entry to set to the branch point if one is found
+ */
+static void
+_local_history_find_branch_point(struct history_entry *ent,
+ struct history_entry **branch_point)
+{
+ if (ent->back == NULL) {
+ /* We're at the root, nothing to do */
+ return;
+ }
+ /* Start from our immediate parent */
+ ent = ent->back;
+ while (ent->back != NULL) {
+ if (ent->back->forward != ent->back->forward_last) {
+ /* This point is a branch */
+ *branch_point = ent;
+ break;
+ }
+ ent = ent->back;
+ }
+}
+
/* exported interface documented in desktop/local_history.h */
bool
local_history_keypress(struct local_history_session *session, uint32_t key)
{
+ switch (key) {
+ case NS_KEY_NL:
+ case NS_KEY_CR:
+ /* pressed enter */
+ if (session->cursor != session->bw->history->current) {
+ browser_window_history_go(session->bw, session->cursor,
+ false);
+ local_history_scroll_to_cursor(session);
+ guit->corewindow->invalidate(session->core_window_handle, NULL);
+ }
+ /* We have handled this keypress */
+ return true;
+ case NS_KEY_LEFT:
+ /* Go to parent */
+ if (session->cursor->back != NULL) {
+ session->cursor = session->cursor->back;
+ local_history_scroll_to_cursor(session);
+ guit->corewindow->invalidate(session->core_window_handle, NULL);
+ }
+ /* We have handled this keypress */
+ return true;
+ case NS_KEY_RIGHT:
+ /* Go to preferred child if there is one */
+ if (session->cursor->forward_pref != NULL) {
+ session->cursor = session->cursor->forward_pref;
+ local_history_scroll_to_cursor(session);
+ guit->corewindow->invalidate(session->core_window_handle, NULL);
+ }
+ /* We have handled this keypress */
+ return true;
+ case NS_KEY_DOWN:
+ /* Go to next sibling down, if there is one */
+ if (session->cursor->next != NULL) {
+ session->cursor = session->cursor->next;
+ } else {
+ struct history_entry *branch_point = NULL;
+ _local_history_find_branch_point(
+ session->cursor,
+ &branch_point);
+ if (branch_point != NULL) {
+ if (branch_point->next != NULL) {
+ branch_point = branch_point->next;
+ }
+ session->cursor = branch_point;
+ }
+ }
+ /* We have handled this keypress */
+ local_history_scroll_to_cursor(session);
+ guit->corewindow->invalidate(session->core_window_handle, NULL);
+ return true;
+ case NS_KEY_UP:
+ /* Go to next sibling up, if there is one */
+ if (session->cursor->back != NULL) {
+ struct history_entry *ent = session->cursor->back->forward;
+ while (ent != session->cursor &&
+ ent->next != NULL &&
+ ent->next != session->cursor) {
+ ent = ent->next;
+ }
+ if (session->cursor != ent) {
+ session->cursor = ent;
+ } else {
+ struct history_entry *branch_point = NULL;
+ _local_history_find_branch_point(
+ session->cursor,
+ &branch_point);
+ if (branch_point != NULL) {
+ struct history_entry *ent = branch_point->back->forward;
+ while (ent->next != NULL && ent->next != branch_point) {
+ ent = ent->next;
+ }
+ session->cursor = ent;
+ }
+ }
+ }
+ /* We have handled this keypress */
+ local_history_scroll_to_cursor(session);
+ guit->corewindow->invalidate(session->core_window_handle, NULL);
+ return true;
+ }
return false;
}
@@ -398,12 +545,16 @@ local_history_set(struct local_history_session *session,
struct browser_window *bw)
{
session->bw = bw;
+ session->cursor = NULL;
+
if (bw != NULL) {
assert(session->bw->history != NULL);
+ session->cursor = bw->history->current;
- session->cw_t->update_size(session->core_window_handle,
- session->bw->history->width,
- session->bw->history->height);
+ guit->corewindow->set_extent(session->core_window_handle,
+ session->bw->history->width,
+ session->bw->history->height);
+ local_history_scroll_to_cursor(session);
}
return NSERROR_OK;
diff --git a/desktop/local_history.h b/desktop/local_history.h
index 7f85a633e..7f1986c11 100644
--- a/desktop/local_history.h
+++ b/desktop/local_history.h
@@ -25,7 +25,6 @@
#include "utils/errors.h"
#include "netsurf/mouse.h"
-struct core_window_callback_table;
struct redraw_context;
struct nsurl;
struct rect;
@@ -47,8 +46,7 @@ struct browser_window;
* \param[out] session The created local history session context.
* \return NSERROR_OK on success and session set, appropriate error code otherwise
*/
-nserror local_history_init(struct core_window_callback_table *cw_t,
- void *core_window_handle,
+nserror local_history_init(void *core_window_handle,
struct browser_window *bw,
struct local_history_session **session);
@@ -140,5 +138,12 @@ nserror local_history_get_size(struct local_history_session *session, int *width
*/
nserror local_history_get_url(struct local_history_session *session, int x, int y, struct nsurl **url_out);
+/**
+ * Scroll the local history window to ensure the current cursor is shown.
+ *
+ * \param session The local history session
+ */
+nserror local_history_scroll_to_cursor(struct local_history_session *session);
+
#endif
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 8aa949a5a..bd785898f 100644
--- a/desktop/netsurf.c
+++ b/desktop/netsurf.c
@@ -24,14 +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"
@@ -41,16 +46,21 @@
#include "image/image.h"
#include "image/image_cache.h"
#include "javascript/js.h"
-#include "render/html.h"
-#include "render/textplain.h"
+#include "html/html.h"
+#include "text/textplain.h"
#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"
#include "netsurf/netsurf.h"
+
+/** \todo QUERY - Remove this import later */
+#include "desktop/browser_private.h"
+
/** speculative pre-conversion small image size
*
* Experimenting by visiting every page from default page in order and
@@ -89,43 +99,10 @@
static void netsurf_lwc_iterator(lwc_string *str, void *pw)
{
- NSLOG(netsurf, INFO, "[%3u] %.*s", str->refcnt,
+ NSLOG(netsurf, WARNING, "[%3"PRIu32"] %.*s", str->refcnt,
(int)lwc_string_length(str), lwc_string_data(str));
}
-/**
- * Dispatch a low-level cache query to the frontend
- *
- * \param query Query descriptor
- * \param pw Private data
- * \param cb Continuation callback
- * \param cbpw Private data for continuation
- * \return NSERROR_OK
- */
-static nserror netsurf_llcache_query_handler(const llcache_query *query,
- void *pw, llcache_query_response cb, void *cbpw)
-{
- nserror res = NSERROR_OK;
-
- switch (query->type) {
- case LLCACHE_QUERY_AUTH:
- guit->misc->login(query->url, query->data.auth.realm, cb, cbpw);
- break;
-
- case LLCACHE_QUERY_REDIRECT:
- /** \todo Need redirect query dialog */
- /* For now, do nothing, as this query type isn't emitted yet */
- break;
-
- case LLCACHE_QUERY_SSL:
- res = guit->misc->cert_verify(query->url, query->data.ssl.certs,
- query->data.ssl.num, cb, cbpw);
- break;
- }
-
- return res;
-}
-
/* exported interface documented in netsurf/netsurf.h */
nserror netsurf_init(const char *store_path)
{
@@ -133,7 +110,6 @@ nserror netsurf_init(const char *store_path)
struct hlcache_parameters hlcache_parameters = {
.bg_clean_time = HL_CACHE_CLEAN_TIME,
.llcache = {
- .cb = netsurf_llcache_query_handler,
.minimum_lifetime = LLCACHE_STORE_MIN_LIFETIME,
.minimum_bandwidth = LLCACHE_STORE_MIN_BANDWIDTH,
.maximum_bandwidth = LLCACHE_STORE_MAX_BANDWIDTH,
@@ -161,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);
@@ -175,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;
@@ -187,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);
@@ -233,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;
}
@@ -247,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();
@@ -259,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();
@@ -279,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 9b7064efa..70c6253f8 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)
@@ -150,7 +154,7 @@ NSOPTION_STRING(homepage_url, NULL)
NSOPTION_BOOL(search_url_bar, false)
/** default web search provider */
-NSOPTION_INTEGER(search_provider, 0)
+NSOPTION_STRING(search_web_provider, NULL)
/** URL completion in url bar */
NSOPTION_BOOL(url_suggestion, true)
@@ -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)
@@ -291,6 +292,6 @@ NSOPTION_COLOUR(sys_colour_WindowFrame, 0x004e4e4e)
NSOPTION_COLOUR(sys_colour_WindowText, 0x00000000)
/** Filter for non-verbose logging */
-NSOPTION_STRING(log_filter, "level:WARNING")
+NSOPTION_STRING(log_filter, NETSURF_BUILTIN_LOG_FILTER)
/** Filter for verbose logging */
-NSOPTION_STRING(verbose_filter, "level:VERBOSE")
+NSOPTION_STRING(verbose_filter, NETSURF_BUILTIN_VERBOSE_FILTER)
diff --git a/desktop/page-info.c b/desktop/page-info.c
new file mode 100644
index 000000000..c428a34be
--- /dev/null
+++ b/desktop/page-info.c
@@ -0,0 +1,831 @@
+/*
+ * 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 {
+ 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 guit->corewindow->set_extent(pi->cw_h, max_x, cur_y);
+}
+
+/* Exported interface documented in desktop/page_info.h */
+nserror page_info_create(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_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,
+ };
+ guit->corewindow->get_dimensions(pi->cw_h, &w, &h);
+ r.x1 = (pi->width > w) ? pi->width : w;
+
+ guit->corewindow->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..1b05177a3
--- /dev/null
+++ b/desktop/page-info.h
@@ -0,0 +1,147 @@
+/*
+ * 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;
+
+/**
+ * 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_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(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/plot_style.c b/desktop/plot_style.c
index 1f0ac39cf..05954144a 100644
--- a/desktop/plot_style.c
+++ b/desktop/plot_style.c
@@ -47,7 +47,7 @@ plot_style_t *plot_style_fill_red = &plot_style_fill_red_static;
static const plot_style_t plot_style_content_edge_static = {
.stroke_type = PLOT_OP_TYPE_SOLID,
.stroke_colour = 0x00ff0000,
- .stroke_width = 1,
+ .stroke_width = plot_style_int_to_fixed(1),
};
plot_style_t const * const plot_style_content_edge =
&plot_style_content_edge_static;
@@ -55,7 +55,7 @@ plot_style_t const * const plot_style_content_edge =
static const plot_style_t plot_style_padding_edge_static = {
.stroke_type = PLOT_OP_TYPE_SOLID,
.stroke_colour = 0x000000ff,
- .stroke_width = 1,
+ .stroke_width = plot_style_int_to_fixed(1),
};
plot_style_t const * const plot_style_padding_edge =
&plot_style_padding_edge_static;
@@ -63,7 +63,7 @@ plot_style_t const * const plot_style_padding_edge =
static const plot_style_t plot_style_margin_edge_static = {
.stroke_type = PLOT_OP_TYPE_SOLID,
.stroke_colour = 0x0000ffff,
- .stroke_width = 1,
+ .stroke_width = plot_style_int_to_fixed(1),
};
plot_style_t const * const plot_style_margin_edge =
&plot_style_margin_edge_static;
@@ -74,14 +74,14 @@ static const plot_style_t plot_style_broken_object_static = {
.fill_colour = 0x008888ff,
.stroke_type = PLOT_OP_TYPE_SOLID,
.stroke_colour = 0x000000ff,
- .stroke_width = 1,
+ .stroke_width = plot_style_int_to_fixed(1),
};
plot_style_t const * const plot_style_broken_object =
&plot_style_broken_object_static;
static const plot_font_style_t plot_fstyle_broken_object_static = {
.family = PLOT_FONT_FAMILY_SANS_SERIF,
- .size = 16 * FONT_SIZE_SCALE,
+ .size = 16 * PLOT_STYLE_SCALE,
.weight = 400,
.flags = FONTF_NONE,
.background = 0x8888ff,
@@ -134,7 +134,7 @@ plot_style_t *plot_style_fill_wblobc = &plot_style_fill_wblobc_static;
static plot_style_t plot_style_stroke_wblobc_static = {
.stroke_type = PLOT_OP_TYPE_SOLID,
.stroke_colour = WIDGET_BLOBC,
- .stroke_width = 2,
+ .stroke_width = plot_style_int_to_fixed(2),
};
plot_style_t *plot_style_stroke_wblobc = &plot_style_stroke_wblobc_static;
@@ -156,7 +156,7 @@ plot_style_t *plot_style_stroke_lightwbasec = &plot_style_stroke_lightwbasec_sta
/* Generic font style */
static const plot_font_style_t plot_style_font_static = {
.family = PLOT_FONT_FAMILY_SANS_SERIF,
- .size = 8 * FONT_SIZE_SCALE,
+ .size = 8 * PLOT_STYLE_SCALE,
.weight = 400,
.flags = FONTF_NONE,
.background = 0xffffff,
diff --git a/desktop/print.c b/desktop/print.c
index 54cc5451a..e90e322ac 100644
--- a/desktop/print.c
+++ b/desktop/print.c
@@ -34,7 +34,7 @@
#include "netsurf/plotters.h"
#include "content/hlcache.h"
#include "css/utils.h"
-#include "render/box.h"
+#include "html/box.h"
#include "desktop/print.h"
#include "desktop/printer.h"
@@ -257,6 +257,11 @@ struct print_settings *print_make_settings(print_configuration configuration,
struct print_settings *settings;
css_fixed length = 0;
css_unit unit = CSS_UNIT_MM;
+ css_unit_ctx unit_len_ctx = {
+ .viewport_width = DEFAULT_PAGE_WIDTH,
+ .viewport_height = DEFAULT_PAGE_HEIGHT,
+ .root_style = NULL,
+ };
switch (configuration){
case PRINT_DEFAULT:
@@ -272,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(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(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(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(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:
@@ -298,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(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(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(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(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 9a88ad180..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"
@@ -43,14 +43,15 @@
#include "netsurf/content.h"
#include "content/hlcache.h"
#include "css/css.h"
-#include "render/box.h"
-#include "render/html.h"
+#include "html/box.h"
+#include "html/html_save.h"
+#include "html/html.h"
#include "netsurf/misc.h"
#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 {
@@ -74,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);
@@ -99,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;
}
/**
@@ -139,21 +141,27 @@ 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 save_complete_save_buffer(save_complete_ctx *ctx,
- const char *leafname, const char *data, size_t data_len,
- lwc_string *mime_type)
+static nserror
+save_complete_save_buffer(save_complete_ctx *ctx,
+ const char *leafname,
+ const uint8_t *data,
+ size_t data_len,
+ lwc_string *mime_type)
{
nserror ret;
FILE *fp;
@@ -161,16 +169,14 @@ static bool 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);
@@ -182,9 +188,38 @@ static bool 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.
*
@@ -195,14 +230,14 @@ static bool save_complete_save_buffer(save_complete_ctx *ctx,
* \param osize updated with the size of the result.
* \return converted source, or NULL on out of memory.
*/
-static char *
+static uint8_t *
save_complete_rewrite_stylesheet_urls(save_complete_ctx *ctx,
- const char *source,
- unsigned long size,
+ const uint8_t *source,
+ size_t size,
const nsurl *base,
- unsigned long *osize)
+ size_t *osize)
{
- char *rewritten;
+ uint8_t *rewritten;
unsigned long offset = 0;
unsigned int imports = 0;
nserror error;
@@ -230,13 +265,19 @@ save_complete_rewrite_stylesheet_urls(save_complete_ctx *ctx,
offset = 0;
while (offset < size) {
- const char *import_url = NULL;
+ const uint8_t *import_url = NULL;
char *import_url_copy;
int import_url_len = 0;
nsurl *url = NULL;
regmatch_t match[11];
- int m = regexec(&save_complete_import_re, 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;
@@ -258,7 +299,8 @@ save_complete_rewrite_stylesheet_urls(save_complete_ctx *ctx,
}
assert(import_url != NULL);
- import_url_copy = strndup(import_url, import_url_len);
+ import_url_copy = strndup((const char *)import_url,
+ import_url_len);
if (import_url_copy == NULL) {
free(rewritten);
return NULL;
@@ -314,44 +356,52 @@ save_complete_rewrite_stylesheet_urls(save_complete_ctx *ctx,
return rewritten;
}
-static bool save_complete_save_stylesheet(save_complete_ctx *ctx,
- hlcache_handle *css)
+static nserror
+save_complete_save_stylesheet(save_complete_ctx *ctx, hlcache_handle *css)
{
- const char *css_data;
- unsigned long css_size;
- char *source;
- unsigned long source_len;
+ const uint8_t *css_data;
+ size_t css_size;
+ uint8_t *source;
+ size_t source_len;
struct nscss_import *imports;
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(ctx, css_data, css_size,
- hlcache_handle_get_url(css), &source_len);
+ source = save_complete_rewrite_stylesheet_urls(
+ ctx,
+ css_data,
+ css_size,
+ 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);
@@ -365,70 +415,84 @@ static bool save_complete_save_stylesheet(save_complete_ctx *ctx,
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 save_complete_save_html_object(save_complete_ctx *ctx,
- hlcache_handle *obj)
+static nserror
+save_complete_save_html_object(save_complete_ctx *ctx, hlcache_handle *obj)
{
- const char *obj_data;
- unsigned long obj_size;
+ 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) {
@@ -438,40 +502,46 @@ static bool save_complete_save_html_object(save_complete_ctx *ctx,
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;
@@ -646,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,
@@ -661,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);
@@ -675,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) {
@@ -712,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;
@@ -742,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);
@@ -755,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;
@@ -786,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;
@@ -808,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;
@@ -821,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;
@@ -850,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;
@@ -863,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) {
@@ -897,13 +976,13 @@ static bool save_complete_handle_element(save_complete_ctx *ctx,
}
if (content != NULL) {
- char *rewritten;
- unsigned long len;
+ uint8_t *rewritten;
+ size_t len;
/* Rewrite @import rules */
rewritten = save_complete_rewrite_stylesheet_urls(
ctx,
- dom_string_data(content),
+ (const uint8_t *)dom_string_data(content),
dom_string_byte_length(content),
ctx->base,
&len);
@@ -934,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;
@@ -977,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);
@@ -1053,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;
@@ -1071,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);
@@ -1089,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;
}
/**
@@ -1119,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);
}
@@ -1142,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;
@@ -1151,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;
}
/**
@@ -1227,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)
+{
+ 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)
{
- bool result;
+ 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);
}
@@ -1245,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/save_pdf.c b/desktop/save_pdf.c
index 83e3d4f31..889190089 100644
--- a/desktop/save_pdf.c
+++ b/desktop/save_pdf.c
@@ -213,10 +213,10 @@ bool pdf_plot_rectangle(int x0, int y0, int x1, int y1, const plot_style_t *psty
}
apply_clip_and_mode(false,
- NS_TRANSPARENT,
- pstyle->stroke_colour,
- pstyle->stroke_width,
- dash);
+ NS_TRANSPARENT,
+ pstyle->stroke_colour,
+ plot_style_int_to_fixed(pstyle->stroke_width),
+ dash);
HPDF_Page_Rectangle(pdf_page, x0, page_height - y0, x1 - x0, -(y1 - y0));
HPDF_Page_Stroke(pdf_page);
@@ -245,10 +245,10 @@ bool pdf_plot_line(int x0, int y0, int x1, int y1, const plot_style_t *pstyle)
}
apply_clip_and_mode(false,
- NS_TRANSPARENT,
- pstyle->stroke_colour,
- pstyle->stroke_width,
- dash);
+ NS_TRANSPARENT,
+ pstyle->stroke_colour,
+ plot_style_int_to_fixed(pstyle->stroke_width),
+ dash);
HPDF_Page_MoveTo(pdf_page, x0, page_height - y0);
HPDF_Page_LineTo(pdf_page, x1, page_height - y1);
diff --git a/desktop/save_text.c b/desktop/save_text.c
index 791ae9201..a86f173b0 100644
--- a/desktop/save_text.c
+++ b/desktop/save_text.c
@@ -32,8 +32,8 @@
#include "utils/utf8.h"
#include "utils/utils.h"
#include "netsurf/content.h"
-#include "render/box.h"
-#include "render/html.h"
+#include "html/box.h"
+#include "html/html_save.h"
#include "netsurf/utf8.h"
#include "desktop/gui_internal.h"
diff --git a/desktop/scrollbar.c b/desktop/scrollbar.c
index 5a7420ae1..35daef107 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"
@@ -164,17 +165,17 @@ scrollbar_rectangle(const struct redraw_context *ctx,
static plot_style_t c0 = {
.stroke_type = PLOT_OP_TYPE_SOLID,
- .stroke_width = 1,
+ .stroke_width = plot_style_int_to_fixed(1),
};
static plot_style_t c1 = {
.stroke_type = PLOT_OP_TYPE_SOLID,
- .stroke_width = 1,
+ .stroke_width = plot_style_int_to_fixed(1),
};
static plot_style_t c2 = {
.stroke_type = PLOT_OP_TYPE_SOLID,
- .stroke_width = 1,
+ .stroke_width = plot_style_int_to_fixed(1),
};
if (inset) {
@@ -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;
@@ -573,11 +562,11 @@ void scrollbar_set(struct scrollbar *s, int value, bool bar_pos)
bool scrollbar_scroll(struct scrollbar *s, int change)
{
int well_length;
- int old_offset = s->offset;
+ int old_offset;
struct scrollbar_msg_data msg;
- if (change == 0 || s->full_size <= s->visible_size) {
- /* zero scroll step, or unscrollable */
+ if (s == NULL || change == 0 || s->full_size <= s->visible_size) {
+ /* scrollbar not present, zero scroll step, or unscrollable */
return false;
}
@@ -605,6 +594,7 @@ bool scrollbar_scroll(struct scrollbar *s, int change)
}
/* Get new offset */
+ old_offset = s->offset;
if (s->offset + change > s->full_size - s->visible_size) {
s->offset = s->full_size - s->visible_size;
} else if (s->offset + change < 0) {
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 201d416dd..e21f520a6 100644
--- a/desktop/search.c
+++ b/desktop/search.c
@@ -18,11 +18,17 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
- /** \file
+/**
+ * \file
* 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"
#include "desktop/browser_private.h"
#include "desktop/search.h"
@@ -33,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);
}
}
@@ -42,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 c07cac9d5..b78c43f69 100644
--- a/desktop/searchweb.c
+++ b/desktop/searchweb.c
@@ -17,11 +17,12 @@
*/
/**
- * \file desktop/searchweb.c
+ * \file
* \brief core web search facilities implementation.
*/
#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";
@@ -298,10 +299,8 @@ search_web_ico_callback(hlcache_handle *ico,
case CONTENT_MSG_ERROR:
NSLOG(netsurf, INFO, "icon %s error: %s",
nsurl_access(hlcache_handle_get_url(ico)),
- event->data.error);
- /* fall through */
+ event->data.errordata.errormsg);
- case CONTENT_MSG_ERRORCODE:
hlcache_handle_release(ico);
/* clear reference to released handle */
provider->ico_handle = NULL;
@@ -334,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) {
@@ -368,7 +367,7 @@ search_web_omni(const char *term,
}
/* exported interface documented in desktop/searchweb.h */
-nserror search_web_select_provider(int selection)
+nserror search_web_get_provider_bitmap(struct bitmap **bitmap_out)
{
struct search_provider *provider;
struct bitmap *ico_bitmap = NULL;
@@ -378,13 +377,44 @@ nserror search_web_select_provider(int selection)
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(const char *selection)
+{
+ struct search_provider *provider;
+ struct bitmap *ico_bitmap = NULL;
+ size_t pidx;
+
+ /* must be initialised */
+ if (search_web_ctx.providers == NULL) {
+ return NSERROR_INIT_FAILED;
+ }
+
/* negative value just selects whatevers current */
- if (selection >= 0) {
- /* ensure selection lies within acceptable range */
- if ((size_t)selection < search_web_ctx.providers_count) {
- search_web_ctx.current = selection;
- } else {
- /* out of range */
+ if (selection != NULL) {
+ for (pidx=0; pidx < search_web_ctx.providers_count;pidx++) {
+ if (strcmp(search_web_ctx.providers[pidx].name, selection)==0) {
+ search_web_ctx.current = pidx;
+ break;
+ }
+ }
+ if (pidx == search_web_ctx.providers_count) {
+ /* selected provider not found */
search_web_ctx.current = 0;
}
}
@@ -399,12 +429,12 @@ nserror search_web_select_provider(int selection)
(search_web_ctx.default_ico_handle != NULL)) {
ico_bitmap = content_get_bitmap(search_web_ctx.default_ico_handle);
}
- /* update the callback with the provider change. Bitmap may
+
+ /* signal the frontend with the provider change. Bitmap may
* be NULL at this point.
*/
guit->search_web->provider_update(provider->name, ico_bitmap);
-
/* if the providers icon has not been retrieved get it now */
if (provider->ico_handle == NULL) {
nsurl *icon_nsurl;
@@ -458,10 +488,8 @@ default_ico_callback(hlcache_handle *ico,
case CONTENT_MSG_ERROR:
NSLOG(netsurf, INFO, "icon %s error: %s",
nsurl_access(hlcache_handle_get_url(ico)),
- event->data.error);
- /* fall through */
+ event->data.errordata.errormsg);
- case CONTENT_MSG_ERRORCODE:
hlcache_handle_release(ico);
/* clear reference to released handle */
ctx->default_ico_handle = NULL;
@@ -475,17 +503,21 @@ default_ico_callback(hlcache_handle *ico,
}
/* exported interface documented in desktop/searchweb.h */
-ssize_t search_web_iterate_providers(ssize_t from, const char **name)
+ssize_t search_web_iterate_providers(ssize_t iter, const char **name)
{
- if (from < 0)
- return -1;
-
- if ((size_t)from >= search_web_ctx.providers_count)
- return -1;
+ if (iter < 0) {
+ iter=0;
+ } else {
+ iter++;
+ }
- *name = search_web_ctx.providers[from].name;
+ if ((size_t)iter >= search_web_ctx.providers_count) {
+ iter = -1;
+ } else {
+ *name = search_web_ctx.providers[iter].name;
+ }
- return from + 1;
+ return iter;
}
@@ -524,10 +556,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..d7e6a1c65 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,37 +72,48 @@ 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.
*
- * \param selection Index of the search provider to select or -1 to
+ * \param selection Name of the search provider to select or NULL to
* reselect the current provider
* \return NSERROR_OK on success or appropriate error code.
*/
-nserror search_web_select_provider(int selection);
+nserror search_web_select_provider(const char *selection);
/**
* Iterate the search providers, returning their names.
*
- * \param from Index to start iteration from. Use 0 to begin iteration.
+ * \param from Index to start iteration from. Use -1 to begin iteration.
* Use the value returned from search_web_iterate_providers to
* continue an iteration.
* \param name Pointer to fill in with the search provider name requested.
* \return -1 if there are no more, otherwise the iterator for the next item.
*
* \verbatim
- * ssize_t iter;
+ * ssize_t iter = -1;
* const char *name;
* ...
- * for (iter = search_web_iterate_providers(0, &name);
- * iter != -1;
- * iter = search_web_iterate_providers(iter, &name)) {
+ * iter = search_web_iterate_providers(iter, &name);
+ * while(iter !=-1) {
* do_something_with(name);
+ * iter = search_web_iterate_providers(iter, &name);
* }
* \endverbatim
*/
-ssize_t search_web_iterate_providers(ssize_t from, const char **name);
+ssize_t search_web_iterate_providers(ssize_t iter, const char **name);
/**
diff --git a/desktop/selection.c b/desktop/selection.c
index 7506af0ef..8b1f127c4 100644
--- a/desktop/selection.c
+++ b/desktop/selection.c
@@ -17,47 +17,24 @@
* 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 "netsurf/clipboard.h"
+#include "netsurf/browser_window.h"
+#include "netsurf/window.h"
#include "utils/utils.h"
-#include "netsurf/form.h"
-#include "render/box.h"
-#include "render/html_internal.h"
-#include "render/font.h"
-#include "render/textplain.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"
+#include "desktop/selection.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)
-
-
-struct rdw_info {
- bool inited;
- struct rect r;
-};
struct selection_string {
char *buffer;
@@ -69,731 +46,414 @@ struct selection_string {
};
-typedef bool (*seln_traverse_handler)(const char *text, size_t length,
- struct box *box, 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,
- 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, 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);
-}
-
-
-/**
- * Creates a new selection object associated with a browser window.
- *
- * \return new selection context
- */
+ unsigned start_idx; /* offset in bytes within text representation */
+ unsigned end_idx;
-struct selection *selection_create(struct content *c, bool is_html)
-{
- struct selection *s = calloc(1, sizeof(struct selection));
- if (s) {
- selection_prepare(s, c, is_html);
- }
+ bool defined;
- return s;
-}
+ seln_drag_state drag_state;
+};
/**
- * Prepare a newly created selection object for use.
+ * Redraws the given range of text.
*
- * \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 start_idx start offset (bytes) within the textual representation
+ * \param end_idx end offset (bytes) within the textual representation
*/
-
-void selection_prepare(struct selection *s, struct content *c, bool is_html)
+static nserror
+selection_redraw(struct selection *s, unsigned start_idx, unsigned end_idx)
{
- 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);
- }
-}
+ 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;
+ }
-/**
- * Destroys a selection object, without updating the
- * owning window (caller should call selection_clear()
- * first if update is desired)
- *
- * \param s selection object
- */
-
-void selection_destroy(struct selection *s)
-{
- if (s != NULL)
- free(s);
+ return res;
}
/**
- * 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 start 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_start(struct selection *s, unsigned offset)
{
- unsigned root_idx;
-
- assert(s);
+ bool was_defined;
+ unsigned old_start;
- root_idx = 0;
+ old_start = s->start_idx;
+ s->start_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_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.
+ * 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_init(struct selection *s, struct box *root)
+static void selection_set_end(struct selection *s, unsigned offset)
{
- if (s->defined)
- selection_clear(s, true);
+ bool was_defined;
+ unsigned old_end;
- s->defined = false;
- s->start_idx = 0;
- s->end_idx = 0;
- s->drag_state = DRAG_NONE;
+ old_end = s->end_idx;
+ s->end_idx = offset;
+
+ was_defined = s->defined;
+ s->defined = (s->start_idx < s->end_idx);
- selection_reinit(s, root);
+ 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);
+ }
}
/**
- * Label each text box in the given box subtree with its position
- * in a textual representation of the content.
+ * Traverse the current selection, calling the handler function (with its
+ * handle) for all boxes that lie (partially) within the given range
*
- * \param box The box at root of subtree
- * \param idx current position within textual representation
- * \return updated position
+ * \param s The selection context.
+ * \param handler handler function to call
+ * \param handle handle to pass
+ * \return false iff traversal abandoned part-way through
*/
-
-unsigned selection_label_subtree(struct box *box, unsigned idx)
+static bool
+selection_copy(struct selection *s, struct selection_string *selstr)
{
- struct box *child = box->children;
-
- box->byte_offset = idx;
+ nserror res;
- 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 (s->c->handler->textselection_copy != NULL) {
+ res = s->c->handler->textselection_copy(s->c,
+ s->start_idx,
+ s->end_idx,
+ selstr);
+ } else {
+ res = NSERROR_NOT_IMPLEMENTED;
}
- return idx;
+ if (res != NSERROR_OK) {
+ return false;
+ }
+ 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->start_selection(top->window);
+ 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->start_selection(top->window);
+ 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 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, 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, 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, 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, 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->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, 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,
- 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(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, 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 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, 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(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 = {
@@ -808,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;
@@ -820,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 = {
@@ -838,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);
@@ -857,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;
@@ -879,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 e2bc3b31d..8cd523fd8 100644
--- a/desktop/selection.h
+++ b/desktop/selection.h
@@ -20,81 +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"
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;
-
- 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);
-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 f40af5968..000000000
--- a/desktop/sslcert_viewer.c
+++ /dev/null
@@ -1,572 +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
-};
-
-
-/**
- * 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 */
- llcache_query_response 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 serial[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);
-
- written = snprintf(e->serial, sizeof(e->serial), "%li", cert->serial);
- assert(written < sizeof(e->serial));
- sslcert_viewer_field_builder(SSLCERT_V_SERIAL,
- &e->data[SSLCERT_V_SERIAL],
- e->serial, 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,
- nsurl *url,
- llcache_query_response cb,
- 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 9a57b965c..000000000
--- a/desktop/sslcert_viewer.h
+++ /dev/null
@@ -1,135 +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 "content/llcache.h"
-#include "netsurf/mouse.h"
-
-struct sslcert_session_data;
-struct redraw_context;
-struct core_window_callback_table;
-struct rect;
-
-/**
- * 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, nsurl *url, llcache_query_response cb,
- 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 149ca26b1..20f7d163b 100644
--- a/desktop/textarea.c
+++ b/desktop/textarea.c
@@ -45,9 +45,9 @@
#define TA_ALLOC_STEP 512
static plot_style_t pstyle_stroke_caret = {
- .stroke_type = PLOT_OP_TYPE_SOLID,
- .stroke_colour = CARET_COLOR,
- .stroke_width = 1,
+ .stroke_type = PLOT_OP_TYPE_SOLID,
+ .stroke_colour = CARET_COLOR,
+ .stroke_width = plot_style_int_to_fixed(1),
};
struct line_info {
@@ -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;
@@ -934,7 +934,7 @@ static bool textarea_reflow_multiline(struct textarea *ta,
assert(ta->flags & TEXTAREA_MULTILINE);
if (ta->lines == NULL) {
- ta->lines = calloc(sizeof(struct line_info), LINE_CHUNK_SIZE);
+ ta->lines = calloc(LINE_CHUNK_SIZE, sizeof(struct line_info));
if (ta->lines == NULL) {
NSLOG(netsurf, INFO,
"Failed to allocate memory for textarea lines");
@@ -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
@@ -1803,6 +1803,10 @@ static void textarea_setup_text_offsets(struct textarea *ta)
{
int text_y_offset, text_y_offset_baseline;
+ ta->line_height = FIXTOINT(FMUL(FLTTOFIX(1.3), FDIV(FMUL(
+ nscss_screen_dpi, FDIV(INTTOFIX(ta->fstyle.size),
+ INTTOFIX(PLOT_STYLE_SCALE))), F_72)));
+
text_y_offset = text_y_offset_baseline = ta->border_width;
if (ta->flags & TEXTAREA_MULTILINE) {
/* Multiline textarea */
@@ -1822,6 +1826,27 @@ static void textarea_setup_text_offsets(struct textarea *ta)
}
+/**
+ * Set font styles up for a textarea.
+ *
+ * \param[in] ta Textarea to update.
+ * \param[in] fstyle Font style to set in textarea.
+ * \param[in] selected_text Textarea selected text colour.
+ * \param[in] selected_bg Textarea selection background colour.
+ */
+static void textarea_set_text_style(
+ struct textarea *ta,
+ const plot_font_style_t *fstyle,
+ colour selected_text,
+ colour selected_bg)
+{
+ ta->fstyle = *fstyle;
+
+ ta->sel_fstyle = *fstyle;
+ ta->sel_fstyle.foreground = selected_text;
+ ta->sel_fstyle.background = selected_bg;
+}
+
/* exported interface, documented in textarea.h */
struct textarea *textarea_create(const textarea_flags flags,
@@ -1861,11 +1886,10 @@ struct textarea *textarea_create(const textarea_flags flags,
ret->border_width = setup->border_width;
ret->border_col = setup->border_col;
- ret->fstyle = setup->text;
-
- ret->sel_fstyle = setup->text;
- ret->sel_fstyle.foreground = setup->selected_text;
- ret->sel_fstyle.background = setup->selected_bg;
+ textarea_set_text_style(ret,
+ &setup->text,
+ setup->selected_text,
+ setup->selected_bg);
ret->scroll_x = 0;
ret->scroll_y = 0;
@@ -1924,7 +1948,7 @@ struct textarea *textarea_create(const textarea_flags flags,
ret->line_height = FIXTOINT(FMUL(FLTTOFIX(1.3), FDIV(FMUL(
nscss_screen_dpi, FDIV(INTTOFIX(setup->text.size),
- INTTOFIX(FONT_SIZE_SCALE))), F_72)));
+ INTTOFIX(PLOT_STYLE_SCALE))), F_72)));
ret->caret_pos.line = ret->caret_pos.byte_off = -1;
ret->caret_x = 0;
@@ -2428,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;
@@ -2442,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);
@@ -2719,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--;
@@ -2732,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]) !=
@@ -2755,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)
@@ -3220,8 +3333,12 @@ void textarea_set_dimensions(struct textarea *ta, int width, int height)
/* exported interface, documented in textarea.h */
-void textarea_set_layout(struct textarea *ta, int width, int height,
- int top, int right, int bottom, int left)
+void textarea_set_layout(
+ struct textarea *ta,
+ const plot_font_style_t *fstyle,
+ int width, int height,
+ int top, int right,
+ int bottom, int left)
{
struct rect r = {0, 0, 0, 0};
@@ -3232,6 +3349,10 @@ void textarea_set_layout(struct textarea *ta, int width, int height,
ta->pad_bottom = bottom + ((ta->bar_x == NULL) ? 0 : SCROLLBAR_WIDTH);
ta->pad_left = left;
+ textarea_set_text_style(ta, fstyle,
+ ta->sel_fstyle.foreground,
+ ta->sel_fstyle.background);
+
textarea_setup_text_offsets(ta);
if (ta->flags & TEXTAREA_MULTILINE) {
diff --git a/desktop/textarea.h b/desktop/textarea.h
index b386e50e8..82e0de95b 100644
--- a/desktop/textarea.h
+++ b/desktop/textarea.h
@@ -329,8 +329,12 @@ void textarea_set_dimensions(struct textarea *ta, int width, int height);
* \param bottom the new bottom padding of the textarea
* \param left the new left padding of the textarea
*/
-void textarea_set_layout(struct textarea *ta, int width, int height,
- int top, int right, int bottom, int left);
+void textarea_set_layout(
+ struct textarea *ta,
+ const plot_font_style_t *fstyle,
+ int width, int height,
+ int top, int right,
+ int bottom, int left);
/**
diff --git a/desktop/textinput.c b/desktop/textinput.c
index c0e0ba8f7..b8dced689 100644
--- a/desktop/textinput.c
+++ b/desktop/textinput.c
@@ -33,13 +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 "render/box.h"
-#include "render/html_internal.h"
-#include "render/layout.h"
+#include "content/content.h"
#include "desktop/browser_private.h"
#include "desktop/textinput.h"
@@ -92,13 +92,14 @@ void browser_window_remove_caret(struct browser_window *bw, bool only_hide)
root_bw = browser_window_get_root(bw);
assert(root_bw != NULL);
- if (only_hide)
+ if (only_hide) {
root_bw->can_edit = true;
- else
+ } else {
root_bw->can_edit = false;
+ }
if (root_bw->window) {
- guit->window->remove_caret(root_bw->window);
+ guit->window->event(root_bw->window, GW_EVENT_REMOVE_CARET);
}
}
diff --git a/desktop/treeview.c b/desktop/treeview.c
index 1651ff5ef..107f490e0 100644
--- a/desktop/treeview.c
+++ b/desktop/treeview.c
@@ -22,13 +22,15 @@
* Treeview handling implementation.
*/
+#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"
@@ -39,11 +41,13 @@
#include "content/hlcache.h"
#include "css/utils.h"
-#include "desktop/system_colour.h"
+#include "desktop/bitmap.h"
#include "desktop/knockout.h"
#include "desktop/textarea.h"
#include "desktop/treeview.h"
+#include "desktop/cw_helper.h"
#include "desktop/gui_internal.h"
+#include "desktop/system_colour.h"
/**
* The maximum horizontal size a treeview can possibly be.
@@ -244,7 +248,6 @@ struct treeview {
const struct treeview_callback_table *callbacks; /**< For node events */
- const struct core_window_callback_table *cw_t; /**< Window cb table */
struct core_window *cw_h; /**< Core window handle */
};
@@ -333,7 +336,7 @@ static struct treeview_resource treeview_res[TREE_RES_LAST] = {
* \param[in] tree Treeview to get the height of.
* \return the display height in pixels.
*/
-static inline int treeview__get_display_height(treeview *tree)
+static inline int treeview__get_display_height(const treeview *tree)
{
return (tree->search.search == false) ?
tree->root->height :
@@ -351,13 +354,47 @@ static inline void treeview__cw_invalidate_area(
const struct treeview *tree,
const struct rect *r)
{
- if (tree->cw_t != NULL) {
- tree->cw_t->invalidate(tree->cw_h, r);
+ if (tree->cw_h != NULL) {
+ guit->corewindow->invalidate(tree->cw_h, r);
}
}
/**
+ * 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_h != NULL) {
+ static const struct rect r = {
+ .x0 = 0,
+ .y0 = 0,
+ .x1 = REDRAW_MAX,
+ .y1 = REDRAW_MAX,
+ };
+ guit->corewindow->invalidate(tree->cw_h, &r);
+ }
+}
+
+
+/**
+ * Get height used by treeview's search bar (or 0 if not present).
+ *
+ * \param tree Treeview object to check.
+ * \return height used by search bar in pixels.
+ */
+static inline unsigned treeview__get_search_height(
+ const treeview *tree)
+{
+ return (tree->flags & TREEVIEW_SEARCHABLE) ?
+ tree_g.line_height : 0;
+}
+
+
+/**
* Corewindow callback wrapper: Update the limits of the window
*
* \param[in] tree The treeview to update size for.
@@ -368,12 +405,10 @@ static inline void treeview__cw_update_size(
const struct treeview *tree,
int width, int height)
{
- int search_height = (tree->flags & TREEVIEW_SEARCHABLE) ?
- tree_g.line_height : 0;
-
- if (tree->cw_t != NULL) {
- tree->cw_t->update_size(tree->cw_h, width,
- height + search_height);
+ if (tree->cw_h != NULL) {
+ guit->corewindow->set_extent(tree->cw_h,
+ width,
+ height + treeview__get_search_height(tree));
}
}
@@ -393,12 +428,9 @@ static inline void treeview__cw_scroll_top(
.y1 = tree_g.line_height,
};
- if (tree->cw_t != NULL) {
- tree->cw_t->scroll_visible(tree->cw_h, &r);
- }
+ cw_helper_scroll_visible(tree->cw_h, &r);
}
-
/**
* Corewindow callback wrapper: Get window viewport dimensions
*
@@ -410,8 +442,8 @@ static inline void treeview__cw_get_window_dimensions(
const struct treeview *tree,
int *width, int *height)
{
- if (tree->cw_t != NULL) {
- tree->cw_t->get_window_dimensions(tree->cw_h, width, height);
+ if (tree->cw_h != NULL) {
+ guit->corewindow->get_dimensions(tree->cw_h, width, height);
}
}
@@ -426,8 +458,8 @@ static inline void treeview__cw_drag_status(
const struct treeview *tree,
core_window_drag_status ds)
{
- if (tree->cw_t != NULL) {
- tree->cw_t->drag_status(tree->cw_h, ds);
+ if (tree->cw_h != NULL) {
+ guit->corewindow->drag_status(tree->cw_h, ds);
}
}
@@ -501,9 +533,8 @@ static inline treeview_node * treeview_node_next(treeview_node *node, bool full)
*/
static treeview_node * treeview_y_node(treeview *tree, int target_y)
{
+ int y = treeview__get_search_height(tree);
treeview_node *n;
- int y = 0;
- int h;
assert(tree != NULL);
assert(tree->root != NULL);
@@ -511,7 +542,7 @@ static treeview_node * treeview_y_node(treeview *tree, int target_y)
n = treeview_node_next(tree->root, false);
while (n != NULL) {
- h = (n->type == TREE_NODE_ENTRY) ?
+ int h = (n->type == TREE_NODE_ENTRY) ?
n->height : tree_g.line_height;
if (target_y >= y && target_y < y + h)
return n;
@@ -531,10 +562,12 @@ static treeview_node * treeview_y_node(treeview *tree, int target_y)
* \param node Node to get position of
* \return node's y position
*/
-static int treeview_node_y(treeview *tree, treeview_node *node)
+static int treeview_node_y(
+ const treeview *tree,
+ const treeview_node *node)
{
treeview_node *n;
- int y = 0;
+ int y = treeview__get_search_height(tree);
assert(tree != NULL);
assert(tree->root != NULL);
@@ -553,6 +586,54 @@ static int treeview_node_y(treeview *tree, treeview_node *node)
/**
+ * Corewindow callback_wrapper: Scroll to make node visible
+ *
+ * \param[in] tree The treeview to scroll.
+ * \param[in] node The treeview node to scroll to visibility.
+ */
+static inline void treeview__cw_scroll_to_node(
+ const struct treeview *tree,
+ const struct treeview_node *node)
+{
+ struct rect r = {
+ .x0 = 0,
+ .y0 = treeview_node_y(tree, node),
+ .x1 = 1,
+ .y1 = ((node->type == TREE_NODE_ENTRY) ?
+ node->height : tree_g.line_height),
+ };
+
+ r.y1 += r.y0; /* Apply the Y offset to the second Y coordinate */
+
+ cw_helper_scroll_visible(tree->cw_h, &r);
+}
+
+
+/**
+ * Redraw tree from given node to the bottom.
+ *
+ * \param[in] tree Tree to redraw from node in.
+ * \param[in] node Node to redraw from.
+ */
+static void treeview__redraw_from_node(
+ const treeview *tree,
+ const treeview_node *node)
+{
+ struct rect r = {
+ .x0 = 0,
+ .y0 = treeview_node_y(tree, node),
+ .x1 = REDRAW_MAX,
+ .y1 = treeview__get_display_height(tree) +
+ treeview__get_search_height(tree),
+ };
+
+ assert(tree != NULL);
+
+ treeview__cw_invalidate_area(tree, &r);
+}
+
+
+/**
* The treeview walk mode. Controls which nodes are visited in a walk.
*/
enum treeview_walk_mode {
@@ -790,8 +871,7 @@ static nserror treeview__search(
nserror err;
uint32_t height;
uint32_t prev_height = treeview__get_display_height(tree);
- int search_height = (tree->flags & TREEVIEW_SEARCHABLE) ?
- tree_g.line_height : 0;
+ int search_height = treeview__get_search_height(tree);
struct treeview_search_walk_data sw = {
.len = len,
.text = text,
@@ -855,6 +935,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);
@@ -866,6 +952,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
@@ -892,7 +1006,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:
@@ -1904,18 +2019,17 @@ static struct textarea *treeview__create_textarea(
/* Exported interface, documented in treeview.h */
nserror
-treeview_create(treeview **tree,
+treeview_create(treeview **treeout,
const struct treeview_callback_table *callbacks,
int n_fields,
struct treeview_field_desc fields[],
- const struct core_window_callback_table *cw_t,
struct core_window *cw,
treeview_flags flags)
{
+ treeview *tree;
nserror error;
- int i;
+ int fldidx;
- assert((cw_t == NULL && cw == NULL) || (cw_t != NULL && cw != NULL));
assert(callbacks != NULL);
assert(fields != NULL);
@@ -1923,86 +2037,91 @@ treeview_create(treeview **tree,
assert(fields[n_fields - 1].flags & TREE_FLAG_DEFAULT);
assert(n_fields >= 2);
- *tree = malloc(sizeof(struct treeview));
- if (*tree == NULL) {
+ tree = malloc(sizeof(struct treeview));
+ if (tree == NULL) {
return NSERROR_NOMEM;
}
- (*tree)->fields = malloc(sizeof(struct treeview_field) * n_fields);
- if ((*tree)->fields == NULL) {
+ tree->fields = malloc(sizeof(struct treeview_field) * n_fields);
+ if (tree->fields == NULL) {
free(tree);
return NSERROR_NOMEM;
}
- error = treeview_create_node_root(&((*tree)->root));
+ error = treeview_create_node_root(&(tree->root));
if (error != NSERROR_OK) {
- free((*tree)->fields);
- free(*tree);
+ free(tree->fields);
+ free(tree);
return error;
}
- (*tree)->field_width = 0;
- for (i = 0; i < n_fields; i++) {
- struct treeview_field *f = &((*tree)->fields[i]);
+ tree->field_width = 0;
+ for (fldidx = 0; fldidx < n_fields; fldidx++) {
+ struct treeview_field *f = &(tree->fields[fldidx]);
- f->flags = fields[i].flags;
- f->field = lwc_string_ref(fields[i].field);
- f->value.data = lwc_string_data(fields[i].field);
- f->value.len = lwc_string_length(fields[i].field);
+ f->flags = fields[fldidx].flags;
+ f->field = lwc_string_ref(fields[fldidx].field);
+ f->value.data = lwc_string_data(fields[fldidx].field);
+ f->value.len = lwc_string_length(fields[fldidx].field);
- guit->layout->width(&plot_style_odd.text, f->value.data,
- f->value.len, &(f->value.width));
+ guit->layout->width(&plot_style_odd.text,
+ f->value.data,
+ f->value.len,
+ &(f->value.width));
- if (f->flags & TREE_FLAG_SHOW_NAME)
- if ((*tree)->field_width < f->value.width)
- (*tree)->field_width = f->value.width;
+ if (f->flags & TREE_FLAG_SHOW_NAME) {
+ if (tree->field_width < f->value.width) {
+ tree->field_width = f->value.width;
+ }
+ }
}
- (*tree)->field_width += tree_g.step_width;
+ tree->field_width += tree_g.step_width;
- (*tree)->callbacks = callbacks;
- (*tree)->n_fields = n_fields - 1;
+ tree->callbacks = callbacks;
+ tree->n_fields = n_fields - 1;
- (*tree)->drag.type = TV_DRAG_NONE;
- (*tree)->drag.start_node = NULL;
- (*tree)->drag.start.x = 0;
- (*tree)->drag.start.y = 0;
- (*tree)->drag.start.node_y = 0;
- (*tree)->drag.start.node_h = 0;
- (*tree)->drag.prev.x = 0;
- (*tree)->drag.prev.y = 0;
- (*tree)->drag.prev.node_y = 0;
- (*tree)->drag.prev.node_h = 0;
+ tree->drag.type = TV_DRAG_NONE;
+ tree->drag.start_node = NULL;
+ tree->drag.start.x = 0;
+ tree->drag.start.y = 0;
+ tree->drag.start.node_y = 0;
+ tree->drag.start.node_h = 0;
+ tree->drag.prev.x = 0;
+ tree->drag.prev.y = 0;
+ tree->drag.prev.node_y = 0;
+ tree->drag.prev.node_h = 0;
- (*tree)->move.root = NULL;
- (*tree)->move.target = NULL;
- (*tree)->move.target_pos = TV_TARGET_NONE;
+ tree->move.root = NULL;
+ tree->move.target = NULL;
+ tree->move.target_pos = TV_TARGET_NONE;
- (*tree)->edit.textarea = NULL;
- (*tree)->edit.node = NULL;
+ tree->edit.textarea = NULL;
+ tree->edit.node = NULL;
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,
+ tree->search.textarea = treeview__create_textarea(
+ tree, 600, tree_g.line_height,
+ 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) {
- treeview_destroy(*tree);
+ if (tree->search.textarea == NULL) {
+ treeview_destroy(tree);
return NSERROR_NOMEM;
}
} else {
- (*tree)->search.textarea = NULL;
+ tree->search.textarea = NULL;
}
- (*tree)->search.active = false;
- (*tree)->search.search = false;
+ tree->search.active = false;
+ tree->search.search = false;
- (*tree)->flags = flags;
+ tree->flags = flags;
- (*tree)->cw_t = cw_t;
- (*tree)->cw_h = cw;
+ tree->cw_h = cw;
+
+ *treeout = tree;
return NSERROR_OK;
}
@@ -2010,18 +2129,14 @@ treeview_create(treeview **tree,
/* Exported interface, documented in treeview.h */
nserror
-treeview_cw_attach(treeview *tree,
- const struct core_window_callback_table *cw_t,
- struct core_window *cw)
+treeview_cw_attach(treeview *tree, struct core_window *cw)
{
- assert(cw_t != NULL);
assert(cw != NULL);
- if (tree->cw_t != NULL || tree->cw_h != NULL) {
+ if (tree->cw_h != NULL) {
NSLOG(netsurf, INFO, "Treeview already attached.");
return NSERROR_UNKNOWN;
}
- tree->cw_t = cw_t;
tree->cw_h = cw;
return NSERROR_OK;
@@ -2031,7 +2146,6 @@ treeview_cw_attach(treeview *tree,
/* Exported interface, documented in treeview.h */
nserror treeview_cw_detach(treeview *tree)
{
- tree->cw_t = NULL;
tree->cw_h = NULL;
treeview__search_cancel(tree, true);
@@ -2083,7 +2197,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);
@@ -2111,7 +2226,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);
@@ -2133,7 +2248,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;
@@ -2152,17 +2267,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));
}
@@ -2175,17 +2291,13 @@ treeview_node_expand_internal(treeview *tree, treeview_node *node)
nserror treeview_node_expand(treeview *tree, treeview_node *node)
{
nserror res;
- struct rect r;
res = treeview_node_expand_internal(tree, node);
+ NSLOG(netsurf, INFO, "Expanding!");
if (res == NSERROR_OK) {
/* expansion was successful, attempt redraw */
- r.x0 = 0;
- r.y0 = treeview_node_y(tree, node);
- r.x1 = REDRAW_MAX;
- r.y1 = treeview__get_display_height(tree);
-
- treeview__cw_invalidate_area(tree, &r);
+ treeview__redraw_from_node(tree, node);
+ NSLOG(netsurf, INFO, "Expanded!");
}
return res;
@@ -2212,7 +2324,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);
@@ -2225,17 +2338,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;
@@ -2289,19 +2415,15 @@ treeview_node_contract_internal(treeview *tree, treeview_node *node)
nserror treeview_node_contract(treeview *tree, treeview_node *node)
{
nserror res;
- struct rect r;
assert(tree != NULL);
res = treeview_node_contract_internal(tree, node);
+ NSLOG(netsurf, INFO, "Contracting!");
if (res == NSERROR_OK) {
/* successful contraction, request redraw */
- r.x0 = 0;
- r.y0 = treeview_node_y(tree, node);
- r.x1 = REDRAW_MAX;
- r.y1 = tree->root->height;
-
- treeview__cw_invalidate_area(tree, &r);
+ treeview__redraw_from_node(tree, node);
+ NSLOG(netsurf, INFO, "Contracted!");
}
return res;
@@ -2311,6 +2433,7 @@ nserror treeview_node_contract(treeview *tree, treeview_node *node)
/* Exported interface, documented in treeview.h */
nserror treeview_contract(treeview *tree, bool all)
{
+ int search_height = treeview__get_search_height(tree);
struct treeview_contract_data data;
bool selected;
treeview_node *n;
@@ -2322,7 +2445,7 @@ nserror treeview_contract(treeview *tree, bool all)
r.x0 = 0;
r.y0 = 0;
r.x1 = REDRAW_MAX;
- r.y1 = tree->root->height;
+ r.y1 = tree->root->height + search_height;
data.tree = tree;
data.only_entries = !all;
@@ -2444,7 +2567,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)
{
@@ -2663,7 +2786,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)
{
@@ -3010,6 +3133,7 @@ struct treeview_selection_walk_data {
} drag;
struct {
treeview_node *prev;
+ treeview_node *fixed;
} yank;
struct {
treeview_node *n;
@@ -3112,6 +3236,10 @@ treeview_node_selection_walk_cb(treeview_node *n,
treeview_node *p = n->parent;
int h = 0;
+ if (n == sw->data.yank.fixed) {
+ break;
+ }
+
if (treeview_unlink_node(n))
h = n->height;
@@ -3270,8 +3398,7 @@ static bool treeview_clear_selection(treeview *tree, struct rect *rect)
sw.purpose = TREEVIEW_WALK_CLEAR_SELECTION;
sw.data.redraw.required = false;
sw.data.redraw.rect = rect;
- sw.current_y = (tree->flags & TREEVIEW_SEARCHABLE) ?
- tree_g.line_height : 0;
+ sw.current_y = treeview__get_search_height(tree);
treeview_walk_internal(tree, tree->root,
TREEVIEW_WALK_MODE_DISPLAY, NULL,
@@ -3300,8 +3427,7 @@ static bool treeview_select_all(treeview *tree, struct rect *rect)
sw.purpose = TREEVIEW_WALK_SELECT_ALL;
sw.data.redraw.required = false;
sw.data.redraw.rect = rect;
- sw.current_y = (tree->flags & TREEVIEW_SEARCHABLE) ?
- tree_g.line_height : 0;
+ sw.current_y = treeview__get_search_height(tree);
treeview_walk_internal(tree, tree->root,
TREEVIEW_WALK_MODE_DISPLAY, NULL,
@@ -3321,8 +3447,7 @@ static void treeview_commit_selection_drag(treeview *tree)
struct treeview_selection_walk_data sw;
sw.purpose = TREEVIEW_WALK_COMMIT_SELECT_DRAG;
- sw.current_y = (tree->flags & TREEVIEW_SEARCHABLE) ?
- tree_g.line_height : 0;
+ sw.current_y = treeview__get_search_height(tree);
if (tree->drag.start.y > tree->drag.prev.y) {
sw.data.drag.sel_min = tree->drag.prev.y;
@@ -3341,13 +3466,15 @@ static void treeview_commit_selection_drag(treeview *tree)
/**
* Yank a selection to the node move list.
*
- * \param tree Treeview object to yank selection from
+ * \param tree Treeview object to yank selection from
+ * \param fixed Treeview node that should not be yanked
*/
-static void treeview_move_yank_selection(treeview *tree)
+static void treeview_move_yank_selection(treeview *tree, treeview_node *fixed)
{
struct treeview_selection_walk_data sw;
sw.purpose = TREEVIEW_WALK_YANK_SELECTION;
+ sw.data.yank.fixed = fixed;
sw.data.yank.prev = NULL;
sw.tree = tree;
@@ -3404,12 +3531,12 @@ static bool treeview_delete_selection(treeview *tree, struct rect *rect)
rect->x0 = 0;
rect->y0 = 0;
rect->x1 = REDRAW_MAX;
- rect->y1 = tree->root->height;
+ rect->y1 = treeview__get_display_height(tree);
sw.purpose = TREEVIEW_WALK_DELETE_SELECTION;
sw.data.redraw.required = false;
sw.data.redraw.rect = rect;
- sw.current_y = 0;
+ sw.current_y = treeview__get_search_height(tree);
sw.tree = tree;
treeview_walk_internal(tree, tree->root,
@@ -3442,7 +3569,7 @@ static bool treeview_propagate_selection(treeview *tree, struct rect *rect)
sw.purpose = TREEVIEW_WALK_PROPAGATE_SELECTION;
sw.data.redraw.required = false;
sw.data.redraw.rect = rect;
- sw.current_y = 0;
+ sw.current_y = treeview__get_search_height(tree);
sw.tree = tree;
treeview_walk_internal(tree, tree->root,
@@ -3521,16 +3648,17 @@ static nserror treeview_move_selection(treeview *tree, struct rect *rect)
parent = relation->parent;
}
- /* The node that we're moving selection to can't itself be selected */
- assert(!(relation->flags & TV_NFLAGS_SELECTED));
-
/* Move all selected nodes from treeview to tree->move.root */
- treeview_move_yank_selection(tree);
+ treeview_move_yank_selection(tree, relation);
/* Move all nodes on tree->move.root to target location */
for (node = tree->move.root; node != NULL; node = next) {
next = node->next_sib;
+ if (node == relation) {
+ continue;
+ }
+
if (!(parent->flags & TV_NFLAGS_EXPANDED)) {
if (node->flags & TV_NFLAGS_EXPANDED)
treeview_node_contract_internal(tree, node);
@@ -3769,8 +3897,10 @@ treeview_keyboard_navigation(treeview *tree, uint32_t key, struct rect *rect)
.n_selected = 0,
.prev_n_selected = 0
};
- int h = tree->root->height;
+ int search_height = treeview__get_search_height(tree);
+ int h = treeview__get_display_height(tree) + search_height;
bool redraw = false;
+ struct treeview_node *scroll_to_node = NULL;
/* Fill out the nav. state struct, by examining the current selection
* state */
@@ -3778,6 +3908,8 @@ treeview_keyboard_navigation(treeview *tree, uint32_t key, struct rect *rect)
TREEVIEW_WALK_MODE_DISPLAY, NULL,
treeview_node_nav_cb, &ns);
+ scroll_to_node = ns.curr;
+
if (tree->search.search == false) {
if (ns.next == NULL)
ns.next = tree->root->children;
@@ -3798,10 +3930,12 @@ treeview_keyboard_navigation(treeview *tree, uint32_t key, struct rect *rect)
ns.curr->parent->type != TREE_NODE_ROOT) {
/* Step to parent */
ns.curr->parent->flags |= TV_NFLAGS_SELECTED;
+ scroll_to_node = ns.curr->parent;
} else if (ns.curr != NULL && tree->root->children != NULL) {
/* Select first node in tree */
tree->root->children->flags |= TV_NFLAGS_SELECTED;
+ scroll_to_node = tree->root->children;
}
break;
@@ -3814,6 +3948,7 @@ treeview_keyboard_navigation(treeview *tree, uint32_t key, struct rect *rect)
/* Step to first child */
ns.curr->children->flags |=
TV_NFLAGS_SELECTED;
+ scroll_to_node = ns.curr->children;
} else {
/* Retain current node selection */
ns.curr->flags |= TV_NFLAGS_SELECTED;
@@ -3835,6 +3970,7 @@ treeview_keyboard_navigation(treeview *tree, uint32_t key, struct rect *rect)
if (ns.prev != NULL) {
/* Step to previous node */
ns.prev->flags |= TV_NFLAGS_SELECTED;
+ scroll_to_node = ns.prev;
}
break;
@@ -3842,6 +3978,7 @@ treeview_keyboard_navigation(treeview *tree, uint32_t key, struct rect *rect)
if (ns.next != NULL) {
/* Step to next node */
ns.next->flags |= TV_NFLAGS_SELECTED;
+ scroll_to_node = ns.next;
}
break;
@@ -3849,12 +3986,14 @@ treeview_keyboard_navigation(treeview *tree, uint32_t key, struct rect *rect)
break;
}
+ treeview__cw_scroll_to_node(tree, scroll_to_node);
+
/* TODO: Deal with redraw area properly */
rect->x0 = 0;
rect->y0 = 0;
rect->x1 = REDRAW_MAX;
- if (tree->root->height > h)
- rect->y1 = tree->root->height;
+ if (treeview__get_display_height(tree) + search_height > h)
+ rect->y1 = treeview__get_display_height(tree) + search_height;
else
rect->y1 = h;
redraw = true;
@@ -4106,7 +4245,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:
@@ -4290,6 +4430,7 @@ struct treeview_mouse_action {
int x;
int y;
int current_y; /* Y coordinate value of top of current node */
+ int search_height;
};
@@ -4477,7 +4618,8 @@ treeview_node_mouse_action_cb(treeview_node *node,
if (((node->type == TREE_NODE_FOLDER) &&
(ma->mouse & BROWSER_MOUSE_DOUBLE_CLICK) && click) ||
(part == TV_NODE_PART_TOGGLE && click)) {
- int h = ma->tree->root->height;
+ int h = treeview__get_display_height(ma->tree) +
+ ma->search_height;
/* Clear any existing selection */
redraw |= treeview_clear_selection(ma->tree, &r);
@@ -4495,7 +4637,13 @@ treeview_node_mouse_action_cb(treeview_node *node,
/* Set up redraw */
if (!redraw || r.y0 > ma->current_y)
r.y0 = ma->current_y;
- r.y1 = h > ma->tree->root->height ? h : ma->tree->root->height;
+ if (h > treeview__get_display_height(ma->tree) +
+ ma->search_height) {
+ r.y1 = h;
+ } else {
+ r.y1 = treeview__get_display_height(ma->tree) +
+ ma->search_height;
+ }
redraw = true;
} else if ((node->type == TREE_NODE_ENTRY) &&
@@ -4576,8 +4724,7 @@ treeview_mouse_action(treeview *tree, browser_mouse_state mouse, int x, int y)
{
struct rect r;
bool redraw = false;
- int search_height = (tree->flags & TREEVIEW_SEARCHABLE) ?
- tree_g.line_height : 0;
+ int search_height = treeview__get_search_height(tree);
assert(tree != NULL);
assert(tree->root != NULL);
@@ -4592,9 +4739,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)) {
@@ -4606,6 +4751,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;
@@ -4734,13 +4889,14 @@ treeview_mouse_action(treeview *tree, browser_mouse_state mouse, int x, int y)
} else {
/* On tree */
- struct treeview_mouse_action ma;
-
- ma.tree = tree;
- ma.mouse = mouse;
- ma.x = x;
- ma.y = y;
- ma.current_y = search_height;
+ struct treeview_mouse_action ma = {
+ .tree = tree,
+ .mouse = mouse,
+ .x = x,
+ .y = y,
+ .current_y = search_height,
+ .search_height = search_height,
+ };
treeview_walk_internal(tree, tree->root,
TREEVIEW_WALK_MODE_DISPLAY, NULL,
@@ -4748,12 +4904,10 @@ treeview_mouse_action(treeview *tree, browser_mouse_state mouse, int x, int y)
}
}
-
/* Exported interface, documented in treeview.h */
int treeview_get_height(treeview *tree)
{
- int search_height = (tree->flags & TREEVIEW_SEARCHABLE) ?
- tree_g.line_height : 0;
+ int search_height = treeview__get_search_height(tree);
int height = treeview__get_display_height(tree);
assert(tree != NULL);
@@ -4764,6 +4918,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.
@@ -4773,75 +4952,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;
@@ -4927,7 +5076,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;
@@ -4941,58 +5090,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;
}
}
@@ -5026,7 +5185,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;
@@ -5074,7 +5233,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;
@@ -5195,7 +5354,7 @@ nserror treeview_init(void)
10 + 36) / 72;
tree_g.line_height = (font_px_size * 8 + 3) / 6;
- res = treeview_init_plot_styles(font_pt_size * FONT_SIZE_SCALE / 10);
+ res = treeview_init_plot_styles(font_pt_size * PLOT_STYLE_SCALE / 10);
if (res != NSERROR_OK) {
return res;
}
diff --git a/desktop/treeview.h b/desktop/treeview.h
index a8cf29ac5..2cf6902a8 100644
--- a/desktop/treeview.h
+++ b/desktop/treeview.h
@@ -32,7 +32,6 @@
struct redraw_context;
struct core_window;
-struct core_window_callback_table;
typedef struct treeview treeview;
typedef struct treeview_node treeview_node;
@@ -189,9 +188,10 @@ nserror treeview_fini(void);
*/
nserror treeview_create(treeview **tree,
const struct treeview_callback_table *callbacks,
- int n_fields, struct treeview_field_desc fields[],
- const struct core_window_callback_table *cw_t,
- struct core_window *cw, treeview_flags flags);
+ int n_fields,
+ struct treeview_field_desc fields[],
+ struct core_window *cw,
+ treeview_flags flags);
/**
@@ -204,9 +204,7 @@ nserror treeview_create(treeview **tree,
* \param cw The core_window in which the treeview is shown
* \return NSERROR_OK on success, appropriate error otherwise
*/
-nserror treeview_cw_attach(treeview *tree,
- const struct core_window_callback_table *cw_t,
- struct core_window *cw);
+nserror treeview_cw_attach(treeview *tree, struct core_window *cw);
/**
@@ -495,4 +493,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 bbe759f2e..91a7532f4 100644
--- a/desktop/version.c
+++ b/desktop/version.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 Vincent Sanders <vince@netsurf-browser.org>
+ * Copyright 2019 Vincent Sanders <vince@netsurf-browser.org>
*
* This file is part of NetSurf, http://www.netsurf-browser.org/
*
@@ -20,11 +20,11 @@
#include "desktop/version.h"
-const char * const netsurf_version = "3.8 (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 = 8;
+const int netsurf_version_minor = 12;