summaryrefslogtreecommitdiff
path: root/desktop
diff options
context:
space:
mode:
Diffstat (limited to 'desktop')
-rw-r--r--desktop/Makefile3
-rw-r--r--desktop/browser.c3475
-rw-r--r--desktop/browser_history.c3
-rw-r--r--desktop/browser_private.h9
-rw-r--r--desktop/browser_window.c3490
-rw-r--r--desktop/local_history.c1
-rw-r--r--desktop/search.c5
-rw-r--r--desktop/selection.c1
-rw-r--r--desktop/textinput.c1
9 files changed, 3509 insertions, 3479 deletions
diff --git a/desktop/Makefile b/desktop/Makefile
index ffd932177..c3c876642 100644
--- a/desktop/Makefile
+++ b/desktop/Makefile
@@ -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 := browser.c browser_window.c browser_history.c \
+ download.c frames.c netsurf.c \
save_complete.c save_text.c selection.c textinput.c gui_factory.c \
save_pdf.c font_haru.c
diff --git a/desktop/browser.c b/desktop/browser.c
index 663bfd01b..c04488063 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,3399 +19,14 @@
/**
* \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/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 "utils/errors.h"
+#include "netsurf/browser.h"
#include "css/utils.h"
-#include "html/form_internal.h"
-#include "html/html.h"
-#include "html/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_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);
-}
-
-/**
- * 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;
-
- /* Format the new content to the correct dimensions */
- browser_window_get_dimensions(bw, &width, &height, true);
- content_reformat(bw->current_content, false, width, height);
-
- /* history */
- if (bw->history_add && bw->history) {
- 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->new_content(bw->window);
-
- 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 */
- if ((content_get_type(bw->current_content) == CONTENT_HTML) &&
- (html_get_frameset(bw->current_content) != NULL)) {
- res = browser_window_create_frameset(bw, html_get_frameset(bw->current_content));
- }
-
- if (content_get_type(bw->current_content) == CONTENT_HTML &&
- html_get_iframe(bw->current_content) != NULL) {
- browser_window_create_iframes(bw, html_get_iframe(bw->current_content));
- }
-
- 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);
- }
- }
-
- 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;
-}
-
-/**
- * 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_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_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_GETDIMS:
- {
- int width;
- int height;
-
- browser_window_get_dimensions(bw, &width, &height, true);
-
- *(event->data.getdims.viewport_width) = width;
- *(event->data.getdims.viewport_height) = height;
- 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_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->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_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(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 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)
{
nscss_screen_dpi = INTTOFIX(dpi);
@@ -3425,80 +34,8 @@ nserror browser_set_dpi(int 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);
}
-
-/* exported interface documented in browser.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;
-}
diff --git a/desktop/browser_history.c b/desktop/browser_history.c
index 2a5fd63ba..6f807961b 100644
--- a/desktop/browser_history.c
+++ b/desktop/browser_history.c
@@ -33,13 +33,14 @@
#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 "desktop/gui_internal.h"
-#include "desktop/browser_history.h"
#include "desktop/browser_private.h"
+#include "desktop/browser_history.h"
#define WIDTH 100
#define HEIGHT 86
diff --git a/desktop/browser_private.h b/desktop/browser_private.h
index 192d22bf0..a5a6dcc9c 100644
--- a/desktop/browser_private.h
+++ b/desktop/browser_private.h
@@ -22,13 +22,8 @@
* 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 "desktop/frame_types.h"
diff --git a/desktop/browser_window.c b/desktop/browser_window.c
new file mode 100644
index 000000000..c53bd269f
--- /dev/null
+++ b/desktop/browser_window.c
@@ -0,0 +1,3490 @@
+/*
+ * 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>
+ *
+ * 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 <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/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 "css/utils.h"
+#include "html/form_internal.h"
+#include "html/html.h"
+#include "html/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_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);
+}
+
+/**
+ * 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;
+
+ /* Format the new content to the correct dimensions */
+ browser_window_get_dimensions(bw, &width, &height, true);
+ content_reformat(bw->current_content, false, width, height);
+
+ /* history */
+ if (bw->history_add && bw->history) {
+ 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->new_content(bw->window);
+
+ 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 */
+ if ((content_get_type(bw->current_content) == CONTENT_HTML) &&
+ (html_get_frameset(bw->current_content) != NULL)) {
+ res = browser_window_create_frameset(bw, html_get_frameset(bw->current_content));
+ }
+
+ if (content_get_type(bw->current_content) == CONTENT_HTML &&
+ html_get_iframe(bw->current_content) != NULL) {
+ browser_window_create_iframes(bw, html_get_iframe(bw->current_content));
+ }
+
+ 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);
+ }
+ }
+
+ 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;
+}
+
+/**
+ * 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_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_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_GETDIMS:
+ {
+ int width;
+ int height;
+
+ browser_window_get_dimensions(bw, &width, &height, true);
+
+ *(event->data.getdims.viewport_width) = width;
+ *(event->data.getdims.viewport_height) = height;
+ 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_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->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_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(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 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 */
+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;
+}
diff --git a/desktop/local_history.c b/desktop/local_history.c
index 75da4aff1..a97741682 100644
--- a/desktop/local_history.c
+++ b/desktop/local_history.c
@@ -28,6 +28,7 @@
#include "utils/nsurl.h"
#include "netsurf/types.h"
#include "netsurf/layout.h"
+#include "netsurf/browser_window.h"
#include "netsurf/core_window.h"
#include "netsurf/plotters.h"
diff --git a/desktop/search.c b/desktop/search.c
index 201d416dd..3d3e7704f 100644
--- a/desktop/search.c
+++ b/desktop/search.c
@@ -18,11 +18,14 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
- /** \file
+/**
+ * \file
* Free text search (core)
*/
#include "content/content.h"
+#include "netsurf/types.h"
+#include "netsurf/browser_window.h"
#include "desktop/browser_private.h"
#include "desktop/search.h"
diff --git a/desktop/selection.c b/desktop/selection.c
index 35eabb2a9..10aa4c7dd 100644
--- a/desktop/selection.c
+++ b/desktop/selection.c
@@ -36,6 +36,7 @@
#include "html/html_internal.h"
#include "html/font.h"
#include "text/textplain.h"
+#include "netsurf/browser_window.h"
#include "netsurf/mouse.h"
#include "desktop/browser_private.h"
diff --git a/desktop/textinput.c b/desktop/textinput.c
index 7fc95f792..660f01a4a 100644
--- a/desktop/textinput.c
+++ b/desktop/textinput.c
@@ -36,6 +36,7 @@
#include "netsurf/mouse.h"
#include "netsurf/form.h"
#include "netsurf/window.h"
+#include "netsurf/browser_window.h"
#include "netsurf/keypress.h"
#include "html/box.h"
#include "html/html_internal.h"