Subversion Repositories Kolibri OS

Rev

Go to most recent revision | Blame | Last modification | View Log | Download | RSS feed

  1. /*
  2.  * Copyright 2003 Phil Mellor <monkeyson@users.sourceforge.net>
  3.  * Copyright 2006 James Bursa <bursa@users.sourceforge.net>
  4.  * Copyright 2004 Andrew Timmins <atimmins@blueyonder.co.uk>
  5.  * Copyright 2004 John Tytgat <joty@netsurf-browser.org>
  6.  * Copyright 2006 Richard Wilson <info@tinct.net>
  7.  * Copyright 2008 Michael Drake <tlsa@netsurf-browser.org>
  8.  * Copyright 2009 Paul Blokus <paul_pl@users.sourceforge.net>  
  9.  *
  10.  * This file is part of NetSurf, http://www.netsurf-browser.org/
  11.  *
  12.  * NetSurf is free software; you can redistribute it and/or modify
  13.  * it under the terms of the GNU General Public License as published by
  14.  * the Free Software Foundation; version 2 of the License.
  15.  *
  16.  * NetSurf is distributed in the hope that it will be useful,
  17.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  18.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  19.  * GNU General Public License for more details.
  20.  *
  21.  * You should have received a copy of the GNU General Public License
  22.  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  23.  */
  24.  
  25. /** \file
  26.  * Browser window creation and manipulation (implementation).
  27.  */
  28.  
  29. #include <assert.h>
  30. #include <limits.h>
  31. #include <stdbool.h>
  32. #include <stdint.h>
  33. #include <stdlib.h>
  34. #include <string.h>
  35. #include <strings.h>
  36. #include <math.h>
  37.  
  38. #include "curl/curl.h"
  39. #include "utils/config.h"
  40. #include "content/content.h"
  41. #include "content/fetch.h"
  42. #include "content/hlcache.h"
  43. #include "content/urldb.h"
  44. #include "desktop/401login.h"
  45. #include "desktop/browser_private.h"
  46. #include "desktop/download.h"
  47. #include "desktop/frames.h"
  48. #include "desktop/history_core.h"
  49. #include "desktop/hotlist.h"
  50. #include "desktop/gui.h"
  51. #include "desktop/knockout.h"
  52. #include "desktop/options.h"
  53. #include "desktop/scrollbar.h"
  54. #include "desktop/selection.h"
  55. #include "desktop/textinput.h"
  56. #include "desktop/plotters.h"
  57.  
  58. #include "javascript/js.h"
  59.  
  60. #include "render/form.h"
  61. #include "render/textplain.h"
  62. #include "render/html.h"
  63. #include "render/box.h"
  64. #include "utils/log.h"
  65. #include "utils/messages.h"
  66. #include "utils/nsurl.h"
  67. #include "utils/schedule.h"
  68. #include "utils/url.h"
  69. #include "utils/utils.h"
  70. #include "utils/utf8.h"
  71.  
  72. /** one or more windows require a reformat */
  73. bool browser_reformat_pending;
  74.  
  75. /** maximum frame depth */
  76. #define FRAME_DEPTH 8
  77.  
  78. static nserror browser_window_callback(hlcache_handle *c,
  79.                 const hlcache_event *event, void *pw);
  80. static void browser_window_refresh(void *p);
  81. static bool browser_window_check_throbber(struct browser_window *bw);
  82. static void browser_window_convert_to_download(struct browser_window *bw,
  83.                 llcache_handle *stream);
  84. static void browser_window_start_throbber(struct browser_window *bw);
  85. static void browser_window_stop_throbber(struct browser_window *bw);
  86. static void browser_window_destroy_children(struct browser_window *bw);
  87. static void browser_window_destroy_internal(struct browser_window *bw);
  88. static void browser_window_set_scale_internal(struct browser_window *bw,
  89.                 float scale);
  90. static void browser_window_find_target_internal(struct browser_window *bw,
  91.                 const char *target, int depth, struct browser_window *page,
  92.                 int *rdepth, struct browser_window **bw_target);
  93. static void browser_window_mouse_drag_end(struct browser_window *bw,
  94.                 browser_mouse_state mouse, int x, int y);
  95.  
  96.  
  97. /**
  98.  * Get position of scrollbar widget within browser window.
  99.  *
  100.  * \param  bw           The browser window
  101.  * \param  horizontal   Whether to get position of horizontal scrollbar
  102.  * \param  x            Updated to x-coord of top left of scrollbar widget
  103.  * \param  y            Updated to y-coord of top left of scrollbar widget
  104.  */
  105.  
  106. static inline void browser_window_get_scrollbar_pos(struct browser_window *bw,
  107.                 bool horizontal, int *x, int *y)
  108. {
  109.         if (horizontal) {
  110.                 *x = 0;
  111.                 *y = bw->height - SCROLLBAR_WIDTH;
  112.         } else {
  113.                 *x = bw->width - SCROLLBAR_WIDTH;
  114.                 *y = 0;
  115.         }
  116. }
  117.  
  118.  
  119. /**
  120.  * Get browser window scrollbar widget length
  121.  *
  122.  * \param  bw           The browser window
  123.  * \param  horizontal   Whether to get length of horizontal scrollbar
  124.  * \return the scrollbar's length
  125.  */
  126.  
  127. static inline int browser_window_get_scrollbar_len(struct browser_window *bw,
  128.                 bool horizontal)
  129. {
  130.         if (horizontal)
  131.                 return bw->width - (bw->scroll_y != NULL ? SCROLLBAR_WIDTH : 0);
  132.         else
  133.                 return bw->height;
  134. }
  135.  
  136. /* exported interface, documented in browser.h */
  137. bool browser_window_redraw(struct browser_window *bw, int x, int y,
  138.                 const struct rect *clip, const struct redraw_context *ctx)
  139. {
  140.         struct redraw_context new_ctx = *ctx;
  141.         int width = 0;
  142.         int height = 0;
  143.         bool plot_ok = true;
  144.         content_type content_type;
  145.         struct content_redraw_data data;
  146.         struct rect content_clip;
  147.  
  148.         if (bw == NULL) {
  149.                 LOG(("NULL browser window"));
  150.                 return false;
  151.         }
  152.  
  153.         if (bw->current_content == NULL && bw->children == NULL) {
  154.                 /* Browser window has no content, render blank fill */
  155.                 ctx->plot->clip(clip);
  156.                 return ctx->plot->rectangle(clip->x0, clip->y0,
  157.                                 clip->x1, clip->y1,
  158.                                 plot_style_fill_white);
  159.         }
  160.  
  161.         /* Browser window has content OR children (frames) */
  162.  
  163.         if ((bw->window != NULL) &&
  164.             (ctx->plot->option_knockout)) {
  165.                 /* Root browser window: start knockout */
  166.                 knockout_plot_start(ctx, &new_ctx);
  167.         }
  168.  
  169.         new_ctx.plot->clip(clip);
  170.  
  171.         /* Handle redraw of any browser window children */
  172.         if (bw->children) {
  173.                 struct browser_window *child;
  174.                 int cur_child;
  175.                 int children = bw->rows * bw->cols;
  176.  
  177.                 if (bw->window != NULL)
  178.                         /* Root browser window; start with blank fill */
  179.                         plot_ok &= new_ctx.plot->rectangle(clip->x0, clip->y0,
  180.                                         clip->x1, clip->y1,
  181.                                         plot_style_fill_white);
  182.  
  183.                 /* Loop through all children of bw */
  184.                 for (cur_child = 0; cur_child < children; cur_child++) {
  185.                         /* Set current child */
  186.                         child = &bw->children[cur_child];
  187.  
  188.                         /* Get frame edge box in global coordinates */
  189.                         content_clip.x0 = (x + child->x) * child->scale;
  190.                         content_clip.y0 = (y + child->y) * child->scale;
  191.                         content_clip.x1 = content_clip.x0 +
  192.                                         child->width * child->scale;
  193.                         content_clip.y1 = content_clip.y0 +
  194.                                         child->height * child->scale;
  195.  
  196.                         /* Intersect it with clip rectangle */
  197.                         if (content_clip.x0 < clip->x0)
  198.                                 content_clip.x0 = clip->x0;
  199.                         if (content_clip.y0 < clip->y0)
  200.                                 content_clip.y0 = clip->y0;
  201.                         if (clip->x1 < content_clip.x1)
  202.                                 content_clip.x1 = clip->x1;
  203.                         if (clip->y1 < content_clip.y1)
  204.                                 content_clip.y1 = clip->y1;
  205.  
  206.                         /* Skip this frame if it lies outside clip rectangle */
  207.                         if (content_clip.x0 >= content_clip.x1 ||
  208.                                         content_clip.y0 >= content_clip.y1)
  209.                                 continue;
  210.  
  211.                         /* Redraw frame */
  212.                         plot_ok &= browser_window_redraw(child,
  213.                                         x + child->x, y + child->y,
  214.                                         &content_clip, &new_ctx);
  215.                 }
  216.  
  217.                 /* Nothing else to redraw for browser windows with children;
  218.                  * cleanup and return */
  219.                 if (bw->window != NULL && ctx->plot->option_knockout) {
  220.                         /* Root browser window: knockout end */
  221.                         knockout_plot_end();
  222.                 }
  223.  
  224.                 return plot_ok;
  225.         }
  226.  
  227.         /* Handle browser windows with content to redraw */
  228.  
  229.         content_type = content_get_type(bw->current_content);
  230.         if (content_type != CONTENT_HTML && content_type != CONTENT_TEXTPLAIN) {
  231.                 /* Set render area according to scale */
  232.                 width = content_get_width(bw->current_content) * bw->scale;
  233.                 height = content_get_height(bw->current_content) * bw->scale;
  234.  
  235.                 /* Non-HTML may not fill viewport to extents, so plot white
  236.                  * background fill */
  237.                 plot_ok &= new_ctx.plot->rectangle(clip->x0, clip->y0,
  238.                                 clip->x1, clip->y1, plot_style_fill_white);
  239.         }
  240.  
  241.         /* Set up content redraw data */
  242.         data.x = x - scrollbar_get_offset(bw->scroll_x);
  243.         data.y = y - scrollbar_get_offset(bw->scroll_y);
  244.         data.width = width;
  245.         data.height = height;
  246.  
  247.         data.background_colour = 0xFFFFFF;
  248.         data.scale = bw->scale;
  249.         data.repeat_x = false;
  250.         data.repeat_y = false;
  251.  
  252.         content_clip = *clip;
  253.  
  254.         if (!bw->window) {
  255.                 int x0 = x * bw->scale;
  256.                 int y0 = y * bw->scale;
  257.                 int x1 = (x + bw->width - ((bw->scroll_y != NULL) ?
  258.                                 SCROLLBAR_WIDTH : 0)) * bw->scale;
  259.                 int y1 = (y + bw->height - ((bw->scroll_x != NULL) ?
  260.                                 SCROLLBAR_WIDTH : 0)) * bw->scale;
  261.  
  262.                 if (content_clip.x0 < x0) content_clip.x0 = x0;
  263.                 if (content_clip.y0 < y0) content_clip.y0 = y0;
  264.                 if (x1 < content_clip.x1) content_clip.x1 = x1;
  265.                 if (y1 < content_clip.y1) content_clip.y1 = y1;
  266.         }
  267.  
  268.         /* Render the content */
  269.         plot_ok &= content_redraw(bw->current_content, &data,
  270.                         &content_clip, &new_ctx);
  271.  
  272.         /* Back to full clip rect */
  273.         new_ctx.plot->clip(clip);
  274.  
  275.         if (!bw->window) {
  276.                 /* Render scrollbars */
  277.                 int off_x, off_y;
  278.                 if (bw->scroll_x != NULL) {
  279.                         browser_window_get_scrollbar_pos(bw, true,
  280.                                         &off_x, &off_y);
  281.                         plot_ok &= scrollbar_redraw(bw->scroll_x,
  282.                                         x + off_x, y + off_y, clip,
  283.                                         bw->scale, &new_ctx);
  284.                 }
  285.                 if (bw->scroll_y != NULL) {
  286.                         browser_window_get_scrollbar_pos(bw, false,
  287.                                         &off_x, &off_y);
  288.                         plot_ok &= scrollbar_redraw(bw->scroll_y,
  289.                                         x + off_x, y + off_y, clip,
  290.                                         bw->scale, &new_ctx);
  291.                 }
  292.         }
  293.        
  294.         if (bw->window != NULL && ctx->plot->option_knockout) {
  295.                 /* Root browser window: end knockout */
  296.                 knockout_plot_end();
  297.         }
  298.  
  299.         return plot_ok;
  300. }
  301.  
  302. /* exported interface, documented in browser.h */
  303. bool browser_window_redraw_ready(struct browser_window *bw)
  304. {
  305.         if (bw == NULL) {
  306.                 LOG(("NULL browser window"));
  307.                 return false;
  308.         } else if (bw->current_content != NULL) {
  309.                 /* Can't render locked contents */
  310.                 return !content_is_locked(bw->current_content);
  311.         }
  312.  
  313.         return true;
  314. }
  315.  
  316. /* exported interface, documented in browser.h */
  317. void browser_window_update_extent(struct browser_window *bw)
  318. {
  319.         if (bw->window != NULL)
  320.                 /* Front end window */
  321.                 gui_window_update_extent(bw->window);
  322.         else
  323.                 /* Core-managed browser window */
  324.                 browser_window_handle_scrollbars(bw);
  325. }
  326.  
  327. /* exported interface, documented in browser.h */
  328. void browser_window_get_position(struct browser_window *bw, bool root,
  329.                 int *pos_x, int *pos_y)
  330. {
  331.         *pos_x = 0;
  332.         *pos_y = 0;
  333.  
  334.         assert(bw != NULL);
  335.  
  336.         while (bw) {
  337.                 switch (bw->browser_window_type) {
  338.  
  339.                 case BROWSER_WINDOW_FRAMESET:
  340.                         *pos_x += bw->x * bw->scale;
  341.                         *pos_y += bw->y * bw->scale;
  342.                         break;
  343.  
  344.                 case BROWSER_WINDOW_NORMAL:
  345.                         /* There is no offset to the root browser window */
  346.                         break;
  347.  
  348.                 case BROWSER_WINDOW_FRAME:
  349.                         /* Iframe and Frame handling is identical;
  350.                          * fall though */
  351.                 case BROWSER_WINDOW_IFRAME:
  352.                         *pos_x += (bw->x - scrollbar_get_offset(bw->scroll_x)) *
  353.                                         bw->scale;
  354.                         *pos_y += (bw->y - scrollbar_get_offset(bw->scroll_y)) *
  355.                                         bw->scale;
  356.                         break;
  357.                 }
  358.  
  359.                 bw = bw->parent;
  360.  
  361.                 if (!root) {
  362.                         /* return if we just wanted the position in the parent
  363.                          * browser window. */
  364.                         return;
  365.                 }
  366.         }
  367. }
  368.  
  369.  
  370. /* exported interface, documented in browser.h */
  371. void browser_window_set_position(struct browser_window *bw, int x, int y)
  372. {
  373.         assert(bw != NULL);
  374.  
  375.         if (bw->window == NULL) {
  376.                 /* Core managed browser window */
  377.                 bw->x = x;
  378.                 bw->y = y;
  379.         } else {
  380.                 LOG(("Asked to set position of front end window."));
  381.                 assert(0);
  382.         }
  383. }
  384.  
  385. /* exported interface, documented in browser.h */
  386. void browser_window_set_drag_type(struct browser_window *bw,
  387.                 browser_drag_type type, const struct rect *rect)
  388. {
  389.         struct browser_window *top_bw = browser_window_get_root(bw);
  390.         gui_drag_type gtype;
  391.  
  392.         bw->drag_type = type;
  393.  
  394.         if (type == DRAGGING_NONE) {
  395.                 top_bw->drag_window = NULL;
  396.         } else {
  397.                 top_bw->drag_window = bw;
  398.  
  399.                 switch (type) {
  400.                 case DRAGGING_SCR_X:
  401.                 case DRAGGING_SCR_Y:
  402.                 case DRAGGING_CONTENT_SCROLLBAR:
  403.                         gtype = GDRAGGING_SCROLLBAR;
  404.                         break;
  405.                 default:
  406.                         gtype = GDRAGGING_OTHER;
  407.                         break;
  408.                 }
  409.  
  410.                 gui_window_drag_start(top_bw->window, gtype, rect);
  411.         }
  412. }
  413.  
  414. /* exported interface, documented in browser.h */
  415. browser_drag_type browser_window_get_drag_type(struct browser_window *bw)
  416. {
  417.         return bw->drag_type;
  418. }
  419.  
  420. /* exported interface, documented in browser.h */
  421. struct browser_window * browser_window_get_root(struct browser_window *bw)
  422. {
  423.         while (bw && bw->parent) {
  424.                 bw = bw->parent;
  425.         }
  426.         return bw;
  427. }
  428.  
  429. /* exported interface, documented in browser.h */
  430. bool browser_window_has_selection(struct browser_window *bw)
  431. {
  432.         assert(bw->window);
  433.  
  434.         if (bw->cur_sel != NULL && selection_defined(bw->cur_sel)) {
  435.                 return true;
  436.         } else {
  437.                 return false;
  438.         }
  439. }
  440.  
  441. /* exported interface, documented in browser.h */
  442. void browser_window_set_selection(struct browser_window *bw,
  443.                 struct selection *s)
  444. {
  445.         assert(bw->window);
  446.  
  447.         if (bw->cur_sel != s && bw->cur_sel != NULL) {
  448.                 /* Clear any existing selection */
  449.                 selection_clear(bw->cur_sel, true);
  450.         }
  451.  
  452.         /* Replace current selection pointer */
  453.         if (s == NULL && bw->current_content != NULL) {
  454.                 bw->cur_sel = content_get_selection(bw->current_content);
  455.         } else {
  456.                 bw->cur_sel = s;
  457.         }
  458. }
  459.  
  460. /* exported interface, documented in browser.h */
  461. struct selection *browser_window_get_selection(struct browser_window *bw)
  462. {
  463.         assert(bw->window);
  464.  
  465.         return bw->cur_sel;
  466. }
  467.  
  468. /* exported interface, documented in browser.h */
  469. void browser_window_scroll_visible(struct browser_window *bw,
  470.                 const struct rect *rect)
  471. {
  472.         assert(bw != NULL);
  473.  
  474.         if (bw->window != NULL) {
  475.                 /* Front end window */
  476.                 gui_window_scroll_visible(bw->window,
  477.                                 rect->x0, rect->y0, rect->x1, rect->y1);
  478.         } else {
  479.                 /* Core managed browser window */
  480.                 if (bw->scroll_x != NULL)
  481.                         scrollbar_set(bw->scroll_x, rect->x0, false);
  482.                 if (bw->scroll_y != NULL)
  483.                         scrollbar_set(bw->scroll_y, rect->y0, false);
  484.         }
  485. }
  486.  
  487. /* exported interface, documented in browser.h */
  488. void browser_window_set_scroll(struct browser_window *bw, int x, int y)
  489. {
  490.         if (bw->window != NULL) {
  491.                 gui_window_set_scroll(bw->window, x, y);
  492.         } else {
  493.                 if (bw->scroll_x != NULL)
  494.                         scrollbar_set(bw->scroll_x, x, false);
  495.                 if (bw->scroll_y != NULL)
  496.                         scrollbar_set(bw->scroll_y, y, false);
  497.         }
  498. }
  499.  
  500. /**
  501.  * Internal helper for browser_window_get_contextual_content
  502.  */
  503. static void browser_window__get_contextual_content(struct browser_window *bw,
  504.                 int x, int y, struct contextual_content *data)
  505. {
  506.         /* Handle (i)frame scroll offset (core-managed browser windows only) */
  507.         x += scrollbar_get_offset(bw->scroll_x);
  508.         y += scrollbar_get_offset(bw->scroll_y);
  509.  
  510.         if (bw->children) {
  511.                 /* Browser window has children, so pass request on to
  512.                  * appropriate child */
  513.                 struct browser_window *bwc;
  514.                 int cur_child;
  515.                 int children = bw->rows * bw->cols;
  516.  
  517.                 /* Loop through all children of bw */
  518.                 for (cur_child = 0; cur_child < children; cur_child++) {
  519.                         /* Set current child */
  520.                         bwc = &bw->children[cur_child];
  521.  
  522.                         /* Skip this frame if (x, y) coord lies outside */
  523.                         if (x < bwc->x || bwc->x + bwc->width < x ||
  524.                                         y < bwc->y || bwc->y + bwc->height < y)
  525.                                 continue;
  526.  
  527.                         /* Pass request into this child */
  528.                         browser_window__get_contextual_content(bwc,
  529.                                         (x - bwc->x), (y - bwc->y), data);
  530.                         return;
  531.                 }
  532.  
  533.                 /* Coordinate not contained by any frame */
  534.                 return;
  535.         }
  536.  
  537.         if (bw->current_content == NULL)
  538.                 /* No content; nothing to set */
  539.                 return;
  540.  
  541.         /* Pass request to content */
  542.         content_get_contextual_content(bw->current_content, x, y, data);
  543.         data->main = bw->current_content;
  544. }
  545.  
  546. /* exported interface, documented in browser.h */
  547. void browser_window_get_contextual_content(struct browser_window *bw,
  548.                 int x, int y, struct contextual_content *data)
  549. {
  550.         data->link_url = NULL;
  551.         data->object = NULL;
  552.         data->main = NULL;
  553.         data->form_features = CTX_FORM_NONE;
  554.  
  555.         browser_window__get_contextual_content(bw, x, y, data);
  556. }
  557.  
  558. /* exported interface, documented in browser.h */
  559. bool browser_window_scroll_at_point(struct browser_window *bw,
  560.                 int x, int y, int scrx, int scry)
  561. {
  562.         bool handled_scroll = false;
  563.         assert(bw != NULL);
  564.  
  565.         /* Handle (i)frame scroll offset (core-managed browser windows only) */
  566.         x += scrollbar_get_offset(bw->scroll_x);
  567.         y += scrollbar_get_offset(bw->scroll_y);
  568.  
  569.         if (bw->children) {
  570.                 /* Browser window has children, so pass request on to
  571.                  * appropriate child */
  572.                 struct browser_window *bwc;
  573.                 int cur_child;
  574.                 int children = bw->rows * bw->cols;
  575.  
  576.                 /* Loop through all children of bw */
  577.                 for (cur_child = 0; cur_child < children; cur_child++) {
  578.                         /* Set current child */
  579.                         bwc = &bw->children[cur_child];
  580.  
  581.                         /* Skip this frame if (x, y) coord lies outside */
  582.                         if (x < bwc->x || bwc->x + bwc->width < x ||
  583.                                         y < bwc->y || bwc->y + bwc->height < y)
  584.                                 continue;
  585.  
  586.                         /* Pass request into this child */
  587.                         return browser_window_scroll_at_point(bwc,
  588.                                         (x - bwc->x), (y - bwc->y),
  589.                                         scrx, scry);
  590.                 }
  591.         }
  592.  
  593.         /* Try to scroll any current content */
  594.         if (bw->current_content != NULL && content_scroll_at_point(
  595.                         bw->current_content, x, y, scrx, scry) == true)
  596.                 /* Scroll handled by current content */
  597.                 return true;
  598.  
  599.         /* Try to scroll this window, if scroll not already handled */
  600.         if (handled_scroll == false) {
  601.                 if (bw->scroll_y && scrollbar_scroll(bw->scroll_y, scry))
  602.                         handled_scroll = true;
  603.  
  604.                 if (bw->scroll_x && scrollbar_scroll(bw->scroll_x, scrx))
  605.                         handled_scroll = true;
  606.         }
  607.  
  608.         return handled_scroll;
  609. }
  610.  
  611. /* exported interface, documented in browser.h */
  612. bool browser_window_drop_file_at_point(struct browser_window *bw,
  613.                 int x, int y, char *file)
  614. {
  615.         assert(bw != NULL);
  616.  
  617.         /* Handle (i)frame scroll offset (core-managed browser windows only) */
  618.         x += scrollbar_get_offset(bw->scroll_x);
  619.         y += scrollbar_get_offset(bw->scroll_y);
  620.  
  621.         if (bw->children) {
  622.                 /* Browser window has children, so pass request on to
  623.                  * appropriate child */
  624.                 struct browser_window *bwc;
  625.                 int cur_child;
  626.                 int children = bw->rows * bw->cols;
  627.  
  628.                 /* Loop through all children of bw */
  629.                 for (cur_child = 0; cur_child < children; cur_child++) {
  630.                         /* Set current child */
  631.                         bwc = &bw->children[cur_child];
  632.  
  633.                         /* Skip this frame if (x, y) coord lies outside */
  634.                         if (x < bwc->x || bwc->x + bwc->width < x ||
  635.                                         y < bwc->y || bwc->y + bwc->height < y)
  636.                                 continue;
  637.  
  638.                         /* Pass request into this child */
  639.                         return browser_window_drop_file_at_point(bwc,
  640.                                         (x - bwc->x), (y - bwc->y),
  641.                                         file);
  642.                 }
  643.         }
  644.  
  645.         /* Pass file drop on to any content */
  646.         if (bw->current_content != NULL)
  647.                 return content_drop_file_at_point(bw->current_content,
  648.                                 x, y, file);
  649.  
  650.         return false;
  651. }
  652.  
  653. /* exported interface, documented in browser.h */
  654. void browser_window_debug_dump(struct browser_window *bw, FILE *f)
  655. {
  656.         if (bw->current_content != NULL)
  657.                 content_debug_dump(bw->current_content, f);
  658. }
  659.  
  660.  
  661. /**
  662.  * Create and open a new root browser window with the given page.
  663.  *
  664.  * \param  url      URL to start fetching in the new window (copied)
  665.  * \param  clone    The browser window to clone
  666.  * \param  referer  The referring uri (copied), or 0 if none
  667.  * \param history_add add to history
  668.  * \param new_tab create a new tab
  669.  * \return new browser window or NULL on error
  670.  */
  671.  
  672. struct browser_window *browser_window_create(const char *url,
  673.                 struct browser_window *clone,
  674.                 const char *referer, bool history_add, bool new_tab)
  675. {
  676.         struct browser_window *bw;
  677.         struct browser_window *top;
  678.  
  679.         LOG(("start bwc"));
  680.        
  681.         assert(clone || history_add);
  682.  
  683.         if ((bw = calloc(1, sizeof *bw)) == NULL) {
  684.                 warn_user("NoMemory", 0);
  685.                 return NULL;
  686.         }
  687.  
  688.         LOG(("new js..."));
  689.        
  690.  
  691.         bw->jsctx = js_newcontext();
  692.  
  693.         LOG(("start bw init common"));
  694.        
  695.         /* Initialise common parts */
  696.         browser_window_initialise_common(bw, clone);
  697.  
  698.         LOG(("set props"));
  699.        
  700.         /* window characteristics */
  701.         bw->browser_window_type = BROWSER_WINDOW_NORMAL;
  702.         bw->scrolling = SCROLLING_YES;
  703.         bw->border = true;
  704.         bw->no_resize = true;
  705.         LOG(("wallclock!!!"));
  706.        
  707.         bw->last_action = wallclock();
  708.         bw->focus = bw;
  709.  
  710.         /* gui window */
  711.         /* from the front end's pov, it clones the top level browser window,
  712.          * so find that. */
  713.        
  714.         LOG(("get root"));
  715.        
  716.        
  717.         top = browser_window_get_root(clone);
  718.  
  719.         LOG(("start bw gui create"));
  720.        
  721.  
  722.         bw->window = gui_create_browser_window(bw, top, new_tab);
  723.  
  724.         if (bw->window == NULL) {
  725.                 LOG(("destroy window"));
  726.        
  727.                 browser_window_destroy(bw);
  728.                 return NULL;
  729.         }
  730.  
  731.         LOG(("url?"));
  732.        
  733.         if (url) {
  734.                 LOG(("bw go go go"));
  735.        
  736.                 browser_window_go(bw, url, referer, history_add);
  737.         }
  738.  
  739.         LOG(("success bwc"));
  740.        
  741.         return bw;
  742. }
  743.  
  744.  
  745. /**
  746.  * Initialise common parts of a browser window
  747.  *
  748.  * \param bw     The window to initialise
  749.  * \param clone  The window to clone, or NULL if none
  750.  */
  751. void browser_window_initialise_common(struct browser_window *bw,
  752.                 struct browser_window *clone)
  753. {
  754.         LOG(("bwc init common IN"));
  755.         assert(bw);
  756.  
  757.         LOG(("check clone..."));
  758.  
  759.         if (!clone){
  760.                 bw->history = history_create();
  761.                 LOG(("hist create"));
  762.                 }
  763.         else {
  764.                 bw->history = history_clone(clone->history);
  765.         LOG(("hist clone"));}
  766.  
  767.  
  768.         LOG(("winchar"));
  769.         /* window characteristics */
  770.         bw->cur_sel = NULL;
  771.         bw->cur_search = NULL;
  772.         bw->refresh_interval = -1;
  773.  
  774.         bw->reformat_pending = false;
  775.         bw->drag_type = DRAGGING_NONE;
  776.         LOG(("nsoption int scale"));
  777.         bw->scale = (float) nsoption_int(scale) / 100.0;
  778.  
  779.  
  780.         bw->scroll_x = NULL;
  781.         bw->scroll_y = NULL;
  782.  
  783.         bw->focus = NULL;
  784.  
  785.         /* initialise status text cache */
  786.         bw->status_text = NULL;
  787.         bw->status_text_len = 0;
  788.         bw->status_match = 0;
  789.         bw->status_miss = 0;
  790. LOG(("fine bwc init OK"));
  791.  
  792. }
  793.  
  794.  
  795. /**
  796.  * Start fetching a page in a browser window.
  797.  *
  798.  * \param  bw       browser window
  799.  * \param  url      URL to start fetching (copied)
  800.  * \param  referer  the referring uri (copied), or 0 if none
  801.  * \param history_add Add to history
  802.  *
  803.  * Any existing fetches in the window are aborted.
  804.  */
  805.  
  806. void browser_window_go(struct browser_window *bw, const char *url,
  807.                 const char *referer, bool history_add)
  808. {
  809.         /* All fetches passing through here are verifiable
  810.          * (i.e are the result of user action) */
  811.         browser_window_go_post(bw, url, 0, 0, history_add, referer,
  812.                         false, true, NULL);
  813. }
  814.  
  815.  
  816. /**
  817.  * Start a download of the given URL from a browser window.
  818.  *
  819.  * \param  bw       browser window
  820.  * \param  url      URL to start downloading (copied)
  821.  * \param  referer  the referring uri (copied), or 0 if none
  822.  */
  823.  
  824. void browser_window_download(struct browser_window *bw, const char *url,
  825.                 const char *referer)
  826. {
  827.         browser_window_go_post(bw, url, 0, 0, false, referer,
  828.                         true, true, NULL);
  829. }
  830.  
  831.  
  832. /**
  833.  * Start fetching a page in a browser window.
  834.  *
  835.  * \param  bw       browser window
  836.  * \param  url      URL to start fetching (copied)
  837.  * \param  referer  the referring uri (copied), or 0 if none
  838.  * \param history_add add to history
  839.  * \param parent parent handle
  840.  *
  841.  * Any existing fetches in the window are aborted.
  842.  */
  843.  
  844. void browser_window_go_unverifiable(struct browser_window *bw,
  845.                 const char *url, const char *referer, bool history_add,
  846.                 hlcache_handle *parent)
  847. {
  848.         /* All fetches passing through here are unverifiable
  849.          * (i.e are not the result of user action) */
  850.         browser_window_go_post(bw, url, 0, 0, history_add, referer,
  851.                         false, false, parent);
  852. }
  853.  
  854. /**
  855.  * Start fetching a page in a browser window, POSTing form data.
  856.  *
  857.  * \param  bw              browser window
  858.  * \param  url             URL to start fetching (copied)
  859.  * \param  post_urlenc     url encoded post data, or 0 if none
  860.  * \param  post_multipart  multipart post data, or 0 if none
  861.  * \param  add_to_history  add to window history
  862.  * \param  referer         the referring uri (copied), or 0 if none
  863.  * \param  download        download, rather than render the uri
  864.  * \param  verifiable      this transaction is verifiable
  865.  * \param  parent          Parent content, or NULL
  866.  *
  867.  * Any existing fetches in the window are aborted.
  868.  *
  869.  * If post_urlenc and post_multipart are 0 the url is fetched using GET.
  870.  *
  871.  * The page is not added to the window history if add_to_history is false.
  872.  * This should be used when returning to a page in the window history.
  873.  */
  874.  
  875. void browser_window_go_post(struct browser_window *bw, const char *url,
  876.                 char *post_urlenc,
  877.                 struct fetch_multipart_data *post_multipart,
  878.                 bool add_to_history, const char *referer, bool download,
  879.                 bool verifiable, hlcache_handle *parent)
  880. {
  881.        
  882.         LOG(("GO POST"));
  883.         hlcache_handle *c;
  884.         int depth = 0;
  885.         struct browser_window *cur;
  886.         uint32_t fetch_flags = 0;
  887.         bool fetch_is_post = (post_urlenc != NULL || post_multipart != NULL);
  888.         llcache_post_data post;
  889.         hlcache_child_context child;
  890.         nserror error;
  891.  
  892.         nsurl *nsref = NULL;
  893.         nsurl *nsurl;
  894.  
  895.         LOG(("bw %p, url %s", bw, url));
  896.         assert(bw);
  897.         assert(url);
  898.  
  899.         /* don't allow massively nested framesets */
  900.         for (cur = bw; cur->parent; cur = cur->parent)
  901.                 depth++;
  902.         if (depth > FRAME_DEPTH) {
  903.                 LOG(("frame depth too high."));
  904.                 return;
  905.         }
  906.  
  907.         /* Set up retrieval parameters */
  908.         if (verifiable)
  909.                 fetch_flags |= LLCACHE_RETRIEVE_VERIFIABLE;
  910.  
  911.         if (post_multipart != NULL) {
  912.                 post.type = LLCACHE_POST_MULTIPART;
  913.                 post.data.multipart = post_multipart;
  914.         } else if (post_urlenc != NULL) {
  915.                 post.type = LLCACHE_POST_URL_ENCODED;
  916.                 post.data.urlenc = post_urlenc;
  917.         }
  918.  
  919.         if (parent != NULL && content_get_type(parent) == CONTENT_HTML) {
  920.                 child.charset = html_get_encoding(parent);
  921.                 child.quirks = content_get_quirks(parent);
  922.         }
  923.  
  924.         error = nsurl_create(url, &nsurl);
  925.         if (error != NSERROR_OK) {
  926.                 return;
  927.         }
  928.  
  929.         if (referer != NULL) {
  930.                 error = nsurl_create(referer, &nsref);
  931.                 if (error != NSERROR_OK) {
  932.                         nsurl_unref(nsurl);
  933.                         return;
  934.                 }
  935.         }
  936.  
  937.         /* Get download out of the way */
  938.         if (download) {
  939.                 llcache_handle *l;
  940.                 struct browser_window *root;
  941.  
  942.                 root = browser_window_get_root(bw);
  943.                 assert(root != NULL);
  944.  
  945.                 fetch_flags |= LLCACHE_RETRIEVE_FORCE_FETCH;
  946.                 fetch_flags |= LLCACHE_RETRIEVE_STREAM_DATA;
  947.  
  948.                 error = llcache_handle_retrieve(nsurl, fetch_flags, nsref,
  949.                                 fetch_is_post ? &post : NULL,
  950.                                 NULL, NULL, &l);
  951.                 if (error == NSERROR_NO_FETCH_HANDLER) {
  952.                         gui_launch_url(nsurl_access(nsurl));
  953.                 } else if (error != NSERROR_OK) {
  954.                         LOG(("Failed to fetch download: %d", error));
  955.                 } else {
  956.                         error = download_context_create(l, root->window);
  957.                         if (error != NSERROR_OK) {
  958.                                 LOG(("Failed creating download context: %d",
  959.                                                 error));
  960.                                 llcache_handle_abort(l);
  961.                                 llcache_handle_release(l);
  962.                         }
  963.                 }
  964.  
  965.                 nsurl_unref(nsurl);
  966.                 if (nsref != NULL)
  967.                         nsurl_unref(nsref);
  968.  
  969.                 return;
  970.         }
  971.  
  972.         if (bw->frag_id != NULL)
  973.                 lwc_string_unref(bw->frag_id);
  974.         bw->frag_id = NULL;
  975.  
  976.         if (nsurl_has_component(nsurl, NSURL_FRAGMENT)) {
  977.                 bool same_url = false;
  978.  
  979.                 bw->frag_id = nsurl_get_component(nsurl, NSURL_FRAGMENT);
  980.  
  981.                 /* Compare new URL with existing one (ignoring fragments) */
  982.                 if (bw->current_content != NULL &&
  983.                                 hlcache_handle_get_url(bw->current_content) != NULL) {
  984.                         same_url = nsurl_compare(nsurl,
  985.                                         hlcache_handle_get_url(bw->current_content),
  986.                                         NSURL_COMPLETE);
  987.                 }
  988.  
  989.                 /* if we're simply moving to another ID on the same page,
  990.                  * don't bother to fetch, just update the window.
  991.                  */
  992.                 if (same_url && fetch_is_post == false &&
  993.                                 nsurl_has_component(nsurl, NSURL_QUERY) ==
  994.                                                 false) {
  995.                         nsurl_unref(nsurl);
  996.                         if (nsref != NULL)
  997.                                 nsurl_unref(nsref);
  998.                         if (add_to_history)
  999.                                 history_add(bw->history, bw->current_content,
  1000.                                                 bw->frag_id == NULL ? NULL :
  1001.                                                 lwc_string_data(bw->frag_id));
  1002.                         browser_window_update(bw, false);
  1003.                         if (bw->current_content != NULL) {
  1004.                                 browser_window_refresh_url_bar(bw,
  1005.                                         hlcache_handle_get_url(bw->current_content),
  1006.                                         bw->frag_id);
  1007.                         }
  1008.                         return;
  1009.                 }
  1010.         }
  1011.  
  1012.         browser_window_stop(bw);
  1013.         browser_window_remove_caret(bw);
  1014.         browser_window_destroy_children(bw);
  1015.  
  1016.         LOG(("Loading '%s'", nsurl_access(nsurl)));
  1017.  
  1018.         LOG(("Set status loading"));
  1019.         browser_window_set_status(bw, messages_get("Loading"));
  1020.         bw->history_add = add_to_history;
  1021.  
  1022.         /* Verifiable fetches may trigger a download */
  1023.         if (verifiable)
  1024.                 fetch_flags |= HLCACHE_RETRIEVE_MAY_DOWNLOAD;
  1025.  
  1026.  
  1027.         LOG(("hlcache retr"));
  1028.         error = hlcache_handle_retrieve(nsurl,
  1029.                         fetch_flags | HLCACHE_RETRIEVE_SNIFF_TYPE,
  1030.                         nsref,
  1031.                         fetch_is_post ? &post : NULL,
  1032.                         browser_window_callback, bw,
  1033.                         parent != NULL ? &child : NULL,
  1034.                         CONTENT_ANY, &c);
  1035.  
  1036.         switch (error) {
  1037.         case NSERROR_NO_FETCH_HANDLER: /* no handler for this type */
  1038.                 LOG(("no fetcher"));
  1039.                 gui_launch_url(nsurl_access(nsurl));
  1040.                 nsurl_unref(nsurl);
  1041.                 if (nsref != NULL)
  1042.                         nsurl_unref(nsref);
  1043.                 break;
  1044.  
  1045.         case NSERROR_OK:
  1046.                 LOG(("generic error"));
  1047.                 bw->loading_content = c;
  1048.                 browser_window_start_throbber(bw);
  1049.                 browser_window_refresh_url_bar(bw, nsurl, NULL);
  1050.  
  1051.                 nsurl_unref(nsurl);
  1052.                 if (nsref != NULL)
  1053.                         nsurl_unref(nsref);
  1054.                 break;
  1055.  
  1056.         default: /* assume out of memory */
  1057.                 LOG(("SUKAAA don't know why"));
  1058.                 /* TODO: fix all fetcher errors being reported as OOM? */
  1059.                 nsurl_unref(nsurl);
  1060.                 if (nsref != NULL)
  1061.                         nsurl_unref(nsref);
  1062.                 browser_window_set_status(bw, messages_get("NoMemory"));
  1063.                 warn_user("NoMemory", 0);
  1064.  
  1065.         }
  1066.  
  1067.         LOG(("record time"));
  1068.         /* Record time */
  1069.         bw->last_action = wallclock();
  1070.        
  1071.         LOG(("That's fine"));
  1072. }
  1073.  
  1074.  
  1075.  
  1076. /**
  1077.  * Callback for fetchcache() for browser window favicon fetches.
  1078.  */
  1079.  
  1080. static nserror browser_window_favicon_callback(hlcache_handle *c,
  1081.                 const hlcache_event *event, void *pw)
  1082. {
  1083.         struct browser_window *bw = pw;
  1084.  
  1085.         switch (event->type) {
  1086.         case CONTENT_MSG_DONE:
  1087.                 if (bw->current_favicon != NULL) {
  1088.                         content_status status =
  1089.                                         content_get_status(bw->current_favicon);
  1090.  
  1091.                         if ((status == CONTENT_STATUS_READY) ||
  1092.                                         (status == CONTENT_STATUS_DONE))
  1093.                                 content_close(bw->current_favicon);
  1094.  
  1095.                         hlcache_handle_release(bw->current_favicon);
  1096.                 }
  1097.  
  1098.                 bw->current_favicon = c;
  1099.                 bw->loading_favicon = NULL;
  1100.  
  1101.                 /* content_get_bitmap on the hlcache_handle should give
  1102.                  *   us the favicon bitmap at this point
  1103.                  */
  1104.                 gui_window_set_icon(bw->window, c);
  1105.                 break;
  1106.  
  1107.         case CONTENT_MSG_ERROR:
  1108.  
  1109.                 /* clean up after ourselves */
  1110.                 if (c == bw->loading_favicon)
  1111.                         bw->loading_favicon = NULL;
  1112.                 else if (c == bw->current_favicon) {
  1113.                         bw->current_favicon = NULL;
  1114.                 }
  1115.  
  1116.                 hlcache_handle_release(c);
  1117.  
  1118.                 if (bw->failed_favicon == false) {
  1119.                         nsurl *nsref = NULL;
  1120.                         nsurl *nsurl;
  1121.                         nserror error;
  1122.  
  1123.                         bw->failed_favicon = true;
  1124.  
  1125.                         error = nsurl_create("resource:favicon.ico", &nsurl);
  1126.                         if (error != NSERROR_OK) {
  1127.                                 LOG(("Unable to create default location url"));
  1128.                         } else {
  1129.  
  1130.                                 hlcache_handle_retrieve(nsurl,
  1131.                                                 HLCACHE_RETRIEVE_SNIFF_TYPE,
  1132.                                                 nsref, NULL,
  1133.                                                 browser_window_favicon_callback,
  1134.                                                 bw, NULL, CONTENT_IMAGE,
  1135.                                                 &bw->loading_favicon);
  1136.  
  1137.                                 nsurl_unref(nsurl);
  1138.                         }
  1139.  
  1140.                 }
  1141.                 break;
  1142.  
  1143.         default:
  1144.                 break;
  1145.         }
  1146.         return NSERROR_OK;
  1147. }
  1148.  
  1149. static void browser_window_update_favicon(hlcache_handle *c,
  1150.                 struct browser_window *bw, struct content_rfc5988_link *link)
  1151. {
  1152.         lwc_string *icon_str;
  1153.         nsurl *nsref = NULL;
  1154.         nsurl *nsurl;
  1155.         nserror error;
  1156.  
  1157.         assert(c != NULL);
  1158.         assert(bw !=NULL);
  1159.  
  1160.         if (bw->window == NULL)
  1161.                 /* Not top-level browser window; not interested */
  1162.                 return;
  1163.        
  1164.         /* already fetching the favicon - use that */
  1165.         if (bw->loading_favicon != NULL)
  1166.                 return;
  1167.  
  1168.         bw->failed_favicon = false;
  1169.  
  1170.         if (link == NULL) {
  1171.                 /* look for favicon metadata link */
  1172.                 if (lwc_intern_string("icon", SLEN("icon"),
  1173.                                 &icon_str) == lwc_error_ok) {
  1174.                         link = content_find_rfc5988_link(c, icon_str);
  1175.                         lwc_string_unref(icon_str);
  1176.                 }
  1177.         }
  1178.  
  1179.         if (link == NULL) {
  1180.                 if (lwc_intern_string("shortcut icon", SLEN("shortcut icon"),
  1181.                                 &icon_str) == lwc_error_ok) {
  1182.                         link = content_find_rfc5988_link(c, icon_str);
  1183.                         lwc_string_unref(icon_str);
  1184.                 }
  1185.         }
  1186.  
  1187.         if (link == NULL) {
  1188.                 lwc_string *scheme;
  1189.                 bool speculative_default = false;
  1190.  
  1191.                 nsurl = hlcache_handle_get_url(c);
  1192.  
  1193.                 scheme = nsurl_get_component(nsurl, NSURL_SCHEME);
  1194.  
  1195.                 /* If the document was fetched over http(s), then speculate
  1196.                  * that there's a favicon living at /favicon.ico */
  1197.                 if ((lwc_string_length(scheme) == SLEN("HTTP") &&
  1198.                                 strcasecmp(lwc_string_data(scheme),
  1199.                                                 "http") == 0) ||
  1200.                     (lwc_string_length(scheme) == SLEN("HTTPS") &&
  1201.                                 strcasecmp(lwc_string_data(scheme),
  1202.                                                 "https") == 0)) {
  1203.                         speculative_default = true;
  1204.                 }
  1205.  
  1206.                 lwc_string_unref(scheme);
  1207.  
  1208.                 if (speculative_default) {
  1209.                         /* no favicon via link, try for the default location */
  1210.                         error = nsurl_join(nsurl, "/favicon.ico", &nsurl);
  1211.                 } else {
  1212.                         bw->failed_favicon = true;
  1213.                         error = nsurl_create("resource:favicon.ico", &nsurl);
  1214.                 }
  1215.                 if (error != NSERROR_OK) {
  1216.                         LOG(("Unable to create default location url"));
  1217.                         return;
  1218.                 }
  1219.         } else {
  1220.                 nsurl = nsurl_ref(link->href);
  1221.         }
  1222.  
  1223.         if (link == NULL) {
  1224.                 LOG(("fetching general favicon from '%s'",
  1225.                      nsurl_access(nsurl)));
  1226.         } else {
  1227.                 LOG(("fetching favicon rel:%s '%s'",
  1228.                                 lwc_string_data(link->rel),
  1229.                                 nsurl_access(nsurl)));
  1230.         }
  1231.  
  1232.         hlcache_handle_retrieve(nsurl, HLCACHE_RETRIEVE_SNIFF_TYPE,
  1233.                         nsref, NULL, browser_window_favicon_callback,
  1234.                         bw, NULL, CONTENT_IMAGE, &bw->loading_favicon);
  1235.  
  1236.         nsurl_unref(nsurl);
  1237. }
  1238.  
  1239. /** window callback errorcode handling */
  1240. static void
  1241. browser_window_callback_errorcode(hlcache_handle *c,
  1242.                                   struct browser_window *bw,
  1243.                                   nserror code)
  1244. {
  1245.         const char* message;
  1246.  
  1247.         message = messages_get_errorcode(code);
  1248.  
  1249.         browser_window_set_status(bw, message);
  1250.  
  1251.         /* Only warn the user about errors in top-level windows */
  1252.         if (bw->browser_window_type == BROWSER_WINDOW_NORMAL) {
  1253.                 warn_user(message, 0);
  1254.         }
  1255.  
  1256.         if (c == bw->loading_content) {
  1257.                 bw->loading_content = NULL;
  1258.         } else if (c == bw->current_content) {
  1259.                 bw->current_content = NULL;
  1260.                 browser_window_remove_caret(bw);
  1261.         }
  1262.  
  1263.         hlcache_handle_release(c);
  1264.  
  1265.         browser_window_stop_throbber(bw);
  1266. }
  1267.  
  1268. /**
  1269.  * Callback for fetchcache() for browser window fetches.
  1270.  */
  1271.  
  1272. nserror browser_window_callback(hlcache_handle *c,
  1273.                 const hlcache_event *event, void *pw)
  1274. {
  1275.         struct browser_window *bw = pw;
  1276.  
  1277.         switch (event->type) {
  1278.         case CONTENT_MSG_DOWNLOAD:
  1279.                 assert(bw->loading_content == c);
  1280.  
  1281.                 browser_window_convert_to_download(bw, event->data.download);
  1282.  
  1283.                 if (bw->current_content != NULL) {
  1284.                         browser_window_refresh_url_bar(bw,
  1285.                                         hlcache_handle_get_url(bw->current_content),
  1286.                                         bw->frag_id);
  1287.                 }
  1288.                 break;
  1289.  
  1290.         case CONTENT_MSG_LOADING:
  1291.                 assert(bw->loading_content == c);
  1292.  
  1293. #ifdef WITH_THEME_INSTALL
  1294.                 if (content_get_type(c) == CONTENT_THEME) {
  1295.                         theme_install_start(c);
  1296.                         bw->loading_content = NULL;
  1297.                         browser_window_stop_throbber(bw);
  1298.                 } else
  1299. #endif
  1300.                 {
  1301.                         bw->refresh_interval = -1;
  1302.                         browser_window_set_status(bw,
  1303.                                         content_get_status_message(c));
  1304.                 }
  1305.                 break;
  1306.  
  1307.         case CONTENT_MSG_READY:
  1308.         {
  1309.                 int width, height;
  1310.  
  1311.                 assert(bw->loading_content == c);
  1312.  
  1313.                 if (bw->current_content != NULL) {
  1314.                         content_status status =
  1315.                                         content_get_status(bw->current_content);
  1316.  
  1317.                         if (status == CONTENT_STATUS_READY ||
  1318.                                         status == CONTENT_STATUS_DONE)
  1319.                                 content_close(bw->current_content);
  1320.  
  1321.                         hlcache_handle_release(bw->current_content);
  1322.                 }
  1323.  
  1324.                 bw->current_content = c;
  1325.                 bw->loading_content = NULL;
  1326.  
  1327.                 /* Format the new content to the correct dimensions */
  1328.                 browser_window_get_dimensions(bw, &width, &height, true);
  1329.                 content_reformat(c, false, width, height);
  1330.  
  1331.                 browser_window_remove_caret(bw);
  1332.  
  1333.                 if (bw->window)
  1334.                         gui_window_new_content(bw->window);
  1335.  
  1336.                 browser_window_refresh_url_bar(bw,
  1337.                                 hlcache_handle_get_url(bw->current_content),
  1338.                                 bw->frag_id);
  1339.  
  1340.                 /* new content; set scroll_to_top */
  1341.                 browser_window_update(bw, true);
  1342.                 content_open(c, bw, 0, 0);
  1343.                 browser_window_set_status(bw, content_get_status_message(c));
  1344.  
  1345.                 /* history */
  1346.                 if (bw->history_add && bw->history) {
  1347.                         nsurl *url = hlcache_handle_get_url(c);
  1348.  
  1349.                         history_add(bw->history, c, bw->frag_id == NULL ? NULL :
  1350.                                         lwc_string_data(bw->frag_id));
  1351.                         if (urldb_add_url(url)) {
  1352.                                 urldb_set_url_title(url, content_get_title(c));
  1353.                                 urldb_update_url_visit_data(url);
  1354.                                 urldb_set_url_content_type(url,
  1355.                                                 content_get_type(c));
  1356.                                 /* This is safe as we've just added the URL */
  1357.                                 global_history_add(urldb_get_url(url));
  1358.                         }
  1359.                 }
  1360.  
  1361.                 if (bw->window != NULL) {
  1362.                         browser_window_set_selection(bw,
  1363.                                         content_get_selection(c));
  1364.                 }
  1365.  
  1366.                 /* frames */
  1367.                 if (content_get_type(c) == CONTENT_HTML &&
  1368.                                 html_get_frameset(c) != NULL)
  1369.                         browser_window_create_frameset(bw,
  1370.                                         html_get_frameset(c));
  1371.                 if (content_get_type(c) == CONTENT_HTML &&
  1372.                                 html_get_iframe(c) != NULL)
  1373.                         browser_window_create_iframes(bw, html_get_iframe(c));
  1374.         }
  1375.                 break;
  1376.  
  1377.         case CONTENT_MSG_DONE:
  1378.                 assert(bw->current_content == c);
  1379.  
  1380.                 if (bw->window == NULL) {
  1381.                         /* Updated browser window's scrollbars.
  1382.                          * TODO: do this before CONTENT_MSG_DONE */
  1383.                         browser_window_reformat(bw, true,
  1384.                                         bw->width, bw->height);
  1385.                         browser_window_handle_scrollbars(bw);
  1386.                 }
  1387.  
  1388.                 browser_window_update(bw, false);
  1389.                 browser_window_set_status(bw, content_get_status_message(c));
  1390.                 browser_window_stop_throbber(bw);
  1391.                 browser_window_update_favicon(c, bw, NULL);
  1392.  
  1393.                 history_update(bw->history, c);
  1394.                 hotlist_visited(c);
  1395.  
  1396.                 if (bw->refresh_interval != -1)
  1397.                         schedule(bw->refresh_interval,
  1398.                                         browser_window_refresh, bw);
  1399.                 break;
  1400.  
  1401.         case CONTENT_MSG_ERRORCODE:
  1402.                 browser_window_callback_errorcode(c, bw, event->data.errorcode);
  1403.                 break;
  1404.  
  1405.         case CONTENT_MSG_ERROR:
  1406.                 browser_window_set_status(bw, event->data.error);
  1407.  
  1408.                 /* Only warn the user about errors in top-level windows */
  1409.                 if (bw->browser_window_type == BROWSER_WINDOW_NORMAL)
  1410.                         warn_user(event->data.error, 0);
  1411.  
  1412.                 if (c == bw->loading_content)
  1413.                         bw->loading_content = NULL;
  1414.                 else if (c == bw->current_content) {
  1415.                         bw->current_content = NULL;
  1416.                         browser_window_remove_caret(bw);
  1417.                 }
  1418.  
  1419.                 hlcache_handle_release(c);
  1420.  
  1421.                 browser_window_stop_throbber(bw);
  1422.                 break;
  1423.  
  1424.         case CONTENT_MSG_STATUS:
  1425.                 if (event->data.explicit_status_text == NULL) {
  1426.                         /* Object content's status text updated */
  1427.                         const char *status = NULL;
  1428.                         if (bw->loading_content != NULL)
  1429.                                 /* Give preference to any loading content */
  1430.                                 status = content_get_status_message(
  1431.                                                 bw->loading_content);
  1432.  
  1433.                         if (status == NULL)
  1434.                                 status = content_get_status_message(c);
  1435.  
  1436.                         if (status != NULL)
  1437.                                 browser_window_set_status(bw, status);
  1438.                 } else {
  1439.                         /* Object content wants to set explicit message */
  1440.                         browser_window_set_status(bw,
  1441.                                         event->data.explicit_status_text);
  1442.                 }
  1443.                 break;
  1444.  
  1445.         case CONTENT_MSG_REFORMAT:
  1446.                 if (c == bw->current_content &&
  1447.                         content_get_type(c) == CONTENT_HTML) {
  1448.                         /* reposition frames */
  1449.                         if (html_get_frameset(c) != NULL)
  1450.                                 browser_window_recalculate_frameset(bw);
  1451.                         /* reflow iframe positions */
  1452.                         if (html_get_iframe(c) != NULL)
  1453.                                 browser_window_recalculate_iframes(bw);
  1454.                 }
  1455.  
  1456.                 if (bw->move_callback)
  1457.                         bw->move_callback(bw, bw->caret_p1, bw->caret_p2);
  1458.  
  1459.                 if (!(event->data.background)) {
  1460.                         /* Reformatted content should be redrawn */
  1461.                         browser_window_update(bw, false);
  1462.                 }
  1463.                 break;
  1464.  
  1465.         case CONTENT_MSG_REDRAW:
  1466.         {
  1467.                 struct rect rect = {
  1468.                         .x0 = event->data.redraw.x,
  1469.                         .y0 = event->data.redraw.y,
  1470.                         .x1 = event->data.redraw.x + event->data.redraw.width,
  1471.                         .y1 = event->data.redraw.y + event->data.redraw.height
  1472.                 };
  1473.  
  1474.                 browser_window_update_box(bw, &rect);
  1475.         }
  1476.                 break;
  1477.  
  1478.         case CONTENT_MSG_REFRESH:
  1479.                 bw->refresh_interval = event->data.delay * 100;
  1480.                 break;
  1481.                
  1482.         case CONTENT_MSG_LINK: /* content has an rfc5988 link element */
  1483.         {
  1484.                 lwc_string *icon_str;
  1485.                 lwc_string *shortcut_icon_str;
  1486.                 bool icon_match = false;
  1487.                 bool shortcut_icon_match = false;
  1488.  
  1489.                 if (lwc_intern_string("icon", SLEN("icon"),
  1490.                                 &icon_str) == lwc_error_ok) {
  1491.                         if (lwc_string_caseless_isequal(
  1492.                                         event->data.rfc5988_link->rel,
  1493.                                         icon_str, &icon_match) != lwc_error_ok) {
  1494.                                 icon_match = false;
  1495.                         }
  1496.                         lwc_string_unref(icon_str);
  1497.                 }
  1498.  
  1499.                 if (lwc_intern_string("shortcut icon", SLEN("shortcut icon"),
  1500.                                 &shortcut_icon_str) == lwc_error_ok) {
  1501.                         if (lwc_string_caseless_isequal(
  1502.                                         event->data.rfc5988_link->rel,
  1503.                                         shortcut_icon_str,
  1504.                                         &shortcut_icon_match) != lwc_error_ok) {
  1505.                                 shortcut_icon_match = false;
  1506.                         }
  1507.                         lwc_string_unref(shortcut_icon_str);
  1508.                 }
  1509.  
  1510.                 if (icon_match || shortcut_icon_match) {
  1511.                         /* it's a favicon perhaps start a fetch for it */
  1512.                         browser_window_update_favicon(c, bw,
  1513.                                         event->data.rfc5988_link);
  1514.                 }
  1515.         }
  1516.                 break;
  1517.  
  1518.         case CONTENT_MSG_GETCTX:
  1519.                 /* only the content object created by the browser
  1520.                  * window requires a new global compartment object
  1521.                  */
  1522.                 assert(bw->loading_content == c);
  1523.                 if (js_newcompartment(bw->jsctx,
  1524.                                       bw,
  1525.                                       hlcache_handle_get_content(c)) != NULL) {
  1526.                         *(event->data.jscontext) = bw->jsctx;
  1527.                 }
  1528.                 break;
  1529.  
  1530.         case CONTENT_MSG_SCROLL:
  1531.                 /* Content wants to be scrolled */
  1532.                 if (bw->current_content != c)
  1533.                         break;
  1534.  
  1535.                 if (event->data.scroll.area) {
  1536.                         struct rect rect = {
  1537.                                 .x0 = event->data.scroll.x0,
  1538.                                 .y0 = event->data.scroll.y0,
  1539.                                 .x1 = event->data.scroll.x1,
  1540.                                 .y1 = event->data.scroll.y1
  1541.                         };
  1542.                         browser_window_scroll_visible(bw, &rect);
  1543.                 } else {
  1544.                         browser_window_set_scroll(bw,
  1545.                                         event->data.scroll.x0,
  1546.                                         event->data.scroll.y0);
  1547.                 }
  1548.  
  1549.                 break;
  1550.  
  1551.         case CONTENT_MSG_DRAGSAVE:
  1552.         {
  1553.                 /* Content wants drag save of a content */
  1554.                 struct browser_window *root = browser_window_get_root(bw);
  1555.                 hlcache_handle *save = event->data.dragsave.content;
  1556.  
  1557.                 if (save == NULL) {
  1558.                         save = c;
  1559.                 }
  1560.  
  1561.                 switch(event->data.dragsave.type) {
  1562.                 case CONTENT_SAVE_ORIG:
  1563.                         gui_drag_save_object(GUI_SAVE_OBJECT_ORIG, save,
  1564.                                         root->window);
  1565.                         break;
  1566.                 case CONTENT_SAVE_NATIVE:
  1567.                         gui_drag_save_object(GUI_SAVE_OBJECT_NATIVE, save,
  1568.                                         root->window);
  1569.                         break;
  1570.                 case CONTENT_SAVE_COMPLETE:
  1571.                         gui_drag_save_object(GUI_SAVE_COMPLETE, save,
  1572.                                         root->window);
  1573.                         break;
  1574.                 case CONTENT_SAVE_SOURCE:
  1575.                         gui_drag_save_object(GUI_SAVE_SOURCE, save,
  1576.                                         root->window);
  1577.                         break;
  1578.                 }
  1579.         }
  1580.                 break;
  1581.  
  1582.         case CONTENT_MSG_SAVELINK:
  1583.         {
  1584.                 /* Content wants a link to be saved */
  1585.                 struct browser_window *root = browser_window_get_root(bw);
  1586.                 gui_window_save_link(root->window,
  1587.                                 event->data.savelink.url,
  1588.                                 event->data.savelink.title);
  1589.         }
  1590.                 break;
  1591.  
  1592.         case CONTENT_MSG_POINTER:
  1593.                 /* Content wants to have specific mouse pointer */
  1594.                 browser_window_set_pointer(bw, event->data.pointer);
  1595.                 break;
  1596.  
  1597.         default:
  1598.                 assert(0);
  1599.         }
  1600.  
  1601.         return NSERROR_OK;
  1602. }
  1603.  
  1604.  
  1605. /*
  1606.  * Get the dimensions of the area a browser window occupies
  1607.  *
  1608.  * \param  bw      The browser window to get dimensions of
  1609.  * \param  width   Updated to the browser window viewport width
  1610.  * \param  height  Updated to the browser window viewport height
  1611.  * \param  scaled  Whether we want the height with scale applied
  1612.  */
  1613.  
  1614. void browser_window_get_dimensions(struct browser_window *bw,
  1615.                 int *width, int *height, bool scaled)
  1616. {
  1617.         assert(bw);
  1618.  
  1619.         if (bw->window == NULL) {
  1620.                 /* Core managed browser window */
  1621.                 *width = bw->width;
  1622.                 *height = bw->height;
  1623.         } else {
  1624.                 /* Front end window */
  1625.                 gui_window_get_dimensions(bw->window, width, height, scaled);
  1626.         }
  1627. }
  1628.  
  1629.  
  1630. /*
  1631.  * Set the dimensions of the area a browser window occupies
  1632.  *
  1633.  * \param  bw      The browser window to set dimensions of
  1634.  * \param  width   Width in pixels
  1635.  * \param  height  Height in pixels
  1636.  */
  1637.  
  1638. void browser_window_set_dimensions(struct browser_window *bw,
  1639.                 int width, int height)
  1640. {
  1641.         assert(bw);
  1642.  
  1643.         if (bw->window == NULL) {
  1644.                 /* Core managed browser window */
  1645.                 bw->width = width;
  1646.                 bw->height = height;
  1647.         } else {
  1648.                 LOG(("Asked to set dimensions of front end window."));
  1649.                 assert(0);
  1650.         }
  1651. }
  1652.  
  1653.  
  1654. /**
  1655.  * Transfer the loading_content to a new download window.
  1656.  */
  1657.  
  1658. void browser_window_convert_to_download(struct browser_window *bw,
  1659.                 llcache_handle *stream)
  1660. {
  1661.         struct browser_window *root = browser_window_get_root(bw);
  1662.         nserror error;
  1663.  
  1664.         assert(root != NULL);
  1665.  
  1666.         error = download_context_create(stream, root->window);
  1667.         if (error != NSERROR_OK) {
  1668.                 llcache_handle_abort(stream);
  1669.                 llcache_handle_release(stream);
  1670.         }
  1671.  
  1672.         /* remove content from browser window */
  1673.         hlcache_handle_release(bw->loading_content);
  1674.         bw->loading_content = NULL;
  1675.  
  1676.         browser_window_stop_throbber(bw);
  1677. }
  1678.  
  1679.  
  1680. /**
  1681.  * Handle meta http-equiv refresh time elapsing by loading a new page.
  1682.  *
  1683.  * \param  p  browser window to refresh with new page
  1684.  */
  1685.  
  1686. void browser_window_refresh(void *p)
  1687. {
  1688.         struct browser_window *bw = p;
  1689.         bool history_add = true;
  1690.         const char *url;
  1691.         const char *refresh;
  1692.  
  1693.         assert(bw->current_content != NULL &&
  1694.                 (content_get_status(bw->current_content) ==
  1695.                                 CONTENT_STATUS_READY ||
  1696.                 content_get_status(bw->current_content) ==
  1697.                                 CONTENT_STATUS_DONE));
  1698.  
  1699.         /* Ignore if the refresh URL has gone
  1700.          * (may happen if a fetch error occurred) */
  1701.         refresh = nsurl_access(content_get_refresh_url(bw->current_content));
  1702.         if (refresh == NULL)
  1703.                 return;
  1704.  
  1705.         /* mark this content as invalid so it gets flushed from the cache */
  1706.         content_invalidate_reuse_data(bw->current_content);
  1707.  
  1708.         url = nsurl_access(hlcache_handle_get_url(bw->current_content));
  1709.         if (url != NULL && strcmp(url, refresh) == 0)
  1710.                 history_add = false;
  1711.  
  1712.         /* Treat an (almost) immediate refresh in a top-level browser window as
  1713.          * if it were an HTTP redirect, and thus make the resulting fetch
  1714.          * verifiable.
  1715.          *
  1716.          * See fetchcache.c for why redirected fetches should be verifiable at
  1717.          * all.
  1718.          */
  1719.         if (bw->refresh_interval <= 100 && bw->parent == NULL) {
  1720.                 browser_window_go(bw, refresh, url, history_add);
  1721.         } else {
  1722.                 browser_window_go_unverifiable(bw, refresh, url, history_add,
  1723.                                 bw->current_content);
  1724.         }
  1725. }
  1726.  
  1727.  
  1728. /**
  1729.  * Start the busy indicator.
  1730.  *
  1731.  * \param  bw  browser window
  1732.  */
  1733.  
  1734. void browser_window_start_throbber(struct browser_window *bw)
  1735. {
  1736.         bw->throbbing = true;
  1737.  
  1738.         while (bw->parent)
  1739.                 bw = bw->parent;
  1740.  
  1741.         gui_window_start_throbber(bw->window);
  1742. }
  1743.  
  1744.  
  1745. /**
  1746.  * Stop the busy indicator.
  1747.  *
  1748.  * \param  bw  browser window
  1749.  */
  1750.  
  1751. void browser_window_stop_throbber(struct browser_window *bw)
  1752. {
  1753.         bw->throbbing = false;
  1754.  
  1755.         while (bw->parent)
  1756.                 bw = bw->parent;
  1757.  
  1758.         if (!browser_window_check_throbber(bw))
  1759.                 gui_window_stop_throbber(bw->window);
  1760. }
  1761.  
  1762. bool browser_window_check_throbber(struct browser_window *bw)
  1763. {
  1764.         int children, index;
  1765.  
  1766.         if (bw->throbbing)
  1767.                 return true;
  1768.  
  1769.         if (bw->children) {
  1770.                 children = bw->rows * bw->cols;
  1771.                 for (index = 0; index < children; index++) {
  1772.                         if (browser_window_check_throbber(&bw->children[index]))
  1773.                                 return true;
  1774.                 }
  1775.         }
  1776.         if (bw->iframes) {
  1777.                 for (index = 0; index < bw->iframe_count; index++) {
  1778.                         if (browser_window_check_throbber(&bw->iframes[index]))
  1779.                                 return true;
  1780.                 }
  1781.         }
  1782.         return false;
  1783. }
  1784.  
  1785. /**
  1786.  * Redraw browser window, set extent to content, and update title.
  1787.  *
  1788.  * \param  bw             browser_window
  1789.  * \param  scroll_to_top  move view to top of page
  1790.  */
  1791.  
  1792. void browser_window_update(struct browser_window *bw, bool scroll_to_top)
  1793. {
  1794.         int x, y;
  1795.  
  1796.         if (bw->current_content == NULL)
  1797.                 return;
  1798.  
  1799.         switch (bw->browser_window_type) {
  1800.  
  1801.         case BROWSER_WINDOW_NORMAL:
  1802.                 /* Root browser window, constituting a front end window/tab */
  1803.                 gui_window_set_title(bw->window,
  1804.                                 content_get_title(bw->current_content));
  1805.  
  1806.                 browser_window_update_extent(bw);
  1807.  
  1808.                 if (scroll_to_top)
  1809.                         browser_window_set_scroll(bw, 0, 0);
  1810.  
  1811.                 /* if frag_id exists, then try to scroll to it */
  1812.                 /** @todo don't do this if the user has scrolled */
  1813.                 if (bw->frag_id && html_get_id_offset(bw->current_content,
  1814.                                 bw->frag_id, &x, &y)) {
  1815.                         browser_window_set_scroll(bw, x, y);
  1816.                 }
  1817.  
  1818.                 gui_window_redraw_window(bw->window);
  1819.  
  1820.                 break;
  1821.  
  1822.         case BROWSER_WINDOW_IFRAME:
  1823.                 /* Internal iframe browser window */
  1824.                 assert(bw->parent != NULL);
  1825.                 assert(bw->parent->current_content != NULL);
  1826.  
  1827.                 browser_window_update_extent(bw);
  1828.  
  1829.                 if (scroll_to_top)
  1830.                         browser_window_set_scroll(bw, 0, 0);
  1831.  
  1832.                 /* if frag_id exists, then try to scroll to it */
  1833.                 /** @todo don't do this if the user has scrolled */
  1834.                 if (bw->frag_id && html_get_id_offset(bw->current_content,
  1835.                                 bw->frag_id, &x, &y)) {
  1836.                         browser_window_set_scroll(bw, x, y);
  1837.                 }
  1838.  
  1839.                 html_redraw_a_box(bw->parent->current_content, bw->box);
  1840.                 break;
  1841.  
  1842.         case BROWSER_WINDOW_FRAME:
  1843.         {
  1844.                 struct rect rect;
  1845.                 browser_window_update_extent(bw);
  1846.  
  1847.                 if (scroll_to_top)
  1848.                         browser_window_set_scroll(bw, 0, 0);
  1849.  
  1850.                 /* if frag_id exists, then try to scroll to it */
  1851.                 /** @todo don't do this if the user has scrolled */
  1852.                 if (bw->frag_id && html_get_id_offset(bw->current_content,
  1853.                                 bw->frag_id, &x, &y)) {
  1854.                         browser_window_set_scroll(bw, x, y);
  1855.                 }
  1856.  
  1857.                 rect.x0 = scrollbar_get_offset(bw->scroll_x);
  1858.                 rect.y0 = scrollbar_get_offset(bw->scroll_y);
  1859.                 rect.x1 = rect.x0 + bw->width;
  1860.                 rect.y1 = rect.y0 + bw->height;
  1861.  
  1862.                 browser_window_update_box(bw, &rect);
  1863.         }
  1864.                 break;
  1865.  
  1866.         default:
  1867.         case BROWSER_WINDOW_FRAMESET:
  1868.                 /* Nothing to do */
  1869.                 break;
  1870.         }
  1871. }
  1872.  
  1873.  
  1874. void browser_window_update_box(struct browser_window *bw, struct rect *rect)
  1875. {
  1876.         int pos_x;
  1877.         int pos_y;
  1878.         struct browser_window *top;
  1879.  
  1880.         assert(bw);
  1881.  
  1882.         if (bw->window != NULL) {
  1883.                 /* Front end window */
  1884.                 gui_window_update_box(bw->window, rect);
  1885.         } else {
  1886.                 /* Core managed browser window */
  1887.                 browser_window_get_position(bw, true, &pos_x, &pos_y);
  1888.  
  1889.                 top = browser_window_get_root(bw);
  1890.  
  1891.                 rect->x0 += pos_x / bw->scale;
  1892.                 rect->y0 += pos_y / bw->scale;
  1893.                 rect->x1 += pos_x / bw->scale;
  1894.                 rect->y1 += pos_y / bw->scale;
  1895.  
  1896.                 gui_window_update_box(top->window, rect);
  1897.         }
  1898. }
  1899.  
  1900.  
  1901. /**
  1902.  * Stop all fetching activity in a browser window.
  1903.  *
  1904.  * \param  bw  browser window
  1905.  */
  1906.  
  1907. void browser_window_stop(struct browser_window *bw)
  1908. {
  1909.         int children, index;
  1910.  
  1911.         if (bw->loading_content != NULL) {
  1912.                 hlcache_handle_abort(bw->loading_content);
  1913.                 hlcache_handle_release(bw->loading_content);
  1914.                 bw->loading_content = NULL;
  1915.         }
  1916.  
  1917.         if (bw->current_content != NULL && content_get_status(
  1918.                         bw->current_content) != CONTENT_STATUS_DONE) {
  1919.                 nserror error;
  1920.                 assert(content_get_status(bw->current_content) ==
  1921.                                 CONTENT_STATUS_READY);
  1922.                 error = hlcache_handle_abort(bw->current_content);
  1923.                 assert(error == NSERROR_OK);
  1924.         }
  1925.  
  1926.         schedule_remove(browser_window_refresh, bw);
  1927.  
  1928.         if (bw->children) {
  1929.                 children = bw->rows * bw->cols;
  1930.                 for (index = 0; index < children; index++)
  1931.                         browser_window_stop(&bw->children[index]);
  1932.         }
  1933.         if (bw->iframes) {
  1934.                 children = bw->iframe_count;
  1935.                 for (index = 0; index < children; index++)
  1936.                         browser_window_stop(&bw->iframes[index]);
  1937.         }
  1938.  
  1939.         if (bw->current_content != NULL) {
  1940.                 browser_window_refresh_url_bar(bw,
  1941.                                 hlcache_handle_get_url(bw->current_content),
  1942.                                 bw->frag_id);
  1943.         }
  1944.  
  1945.         browser_window_stop_throbber(bw);
  1946. }
  1947.  
  1948.  
  1949. /**
  1950.  * Reload the page in a browser window.
  1951.  *
  1952.  * \param  bw  browser window
  1953.  * \param  all whether to reload all objects associated with the page
  1954.  */
  1955.  
  1956. void browser_window_reload(struct browser_window *bw, bool all)
  1957. {
  1958.         hlcache_handle *c;
  1959.         unsigned int i;
  1960.  
  1961.         if (bw->current_content == NULL || bw->loading_content != NULL)
  1962.                 return;
  1963.  
  1964.         if (all && content_get_type(bw->current_content) == CONTENT_HTML) {
  1965.                 struct html_stylesheet *sheets;
  1966.                 struct content_html_object *object;
  1967.                 unsigned int count;
  1968.  
  1969.                 c = bw->current_content;
  1970.  
  1971.                 /* invalidate objects */
  1972.                 object = html_get_objects(c, &count);
  1973.  
  1974.                 for (; object != NULL; object = object->next) {
  1975.                         if (object->content != NULL)
  1976.                                 content_invalidate_reuse_data(object->content);
  1977.                 }
  1978.  
  1979.                 /* invalidate stylesheets */
  1980.                 sheets = html_get_stylesheets(c, &count);
  1981.  
  1982.                 for (i = STYLESHEET_START; i != count; i++) {
  1983.                         if (sheets[i].type == HTML_STYLESHEET_EXTERNAL &&
  1984.                                         sheets[i].data.external != NULL) {
  1985.                                 content_invalidate_reuse_data(
  1986.                                                 sheets[i].data.external);
  1987.                         }
  1988.                 }
  1989.         }
  1990.  
  1991.         content_invalidate_reuse_data(bw->current_content);
  1992.  
  1993.         browser_window_go(bw, nsurl_access(
  1994.                         hlcache_handle_get_url(bw->current_content)), 0, false);
  1995. }
  1996.  
  1997.  
  1998. /**
  1999.  * Change the status bar of a browser window.
  2000.  *
  2001.  * \param  bw    browser window
  2002.  * \param  text  new status text (copied)
  2003.  */
  2004.  
  2005. void browser_window_set_status(struct browser_window *bw, const char *text)
  2006. {
  2007.         int text_len;
  2008.         /* find topmost window */
  2009.         while (bw->parent)
  2010.                 bw = bw->parent;
  2011.  
  2012.         if ((bw->status_text != NULL) &&
  2013.             (strcmp(text, bw->status_text) == 0)) {
  2014.                 /* status text is unchanged */
  2015.                 bw->status_match++;
  2016.                 return;
  2017.         }
  2018.  
  2019.         /* status text is changed */
  2020.  
  2021.         text_len = strlen(text);
  2022.  
  2023.         if ((bw->status_text == NULL) || (bw->status_text_len < text_len)) {
  2024.                 /* no current string allocation or it is not long enough */
  2025.                 free(bw->status_text);
  2026.                 bw->status_text = strdup(text);
  2027.                 bw->status_text_len = text_len;
  2028.         } else {
  2029.                 /* current allocation has enough space */
  2030.                 memcpy(bw->status_text, text, text_len + 1);
  2031.         }
  2032.  
  2033.         bw->status_miss++;
  2034.         gui_window_set_status(bw->window, bw->status_text);
  2035. }
  2036.  
  2037.  
  2038. /**
  2039.  * Change the shape of the mouse pointer
  2040.  *
  2041.  * \param bw Browser window to set shape in
  2042.  * \param shape The pointer shape to use
  2043.  */
  2044.  
  2045. void browser_window_set_pointer(struct browser_window *bw,
  2046.                 browser_pointer_shape shape)
  2047. {
  2048.         struct browser_window *root = browser_window_get_root(bw);
  2049.         gui_pointer_shape gui_shape;
  2050.         bool loading;
  2051.  
  2052.         assert(root);
  2053.         assert(root->window);
  2054.  
  2055.         loading = (bw->loading_content != NULL || (bw->current_content &&
  2056.                         content_get_status(bw->current_content) ==
  2057.                         CONTENT_STATUS_READY));
  2058.  
  2059.         if (wallclock() - bw->last_action < 100 && loading) {
  2060.                 /* If loading and less than 1 second since last link followed,
  2061.                  * force progress indicator pointer */
  2062.                 gui_shape = GUI_POINTER_PROGRESS;
  2063.  
  2064.         } else if (shape == BROWSER_POINTER_AUTO) {
  2065.                 /* Up to browser window to decide */
  2066.                 if (loading)
  2067.                         gui_shape = GUI_POINTER_PROGRESS;
  2068.                 else
  2069.                         gui_shape = GUI_POINTER_DEFAULT;
  2070.  
  2071.         } else {
  2072.                 /* Use what we were told */
  2073.                 gui_shape = (gui_pointer_shape)shape;
  2074.         }
  2075.  
  2076.         gui_window_set_pointer(root->window, gui_shape);
  2077. }
  2078.  
  2079.  
  2080. /**
  2081.  * Close and destroy a browser window.
  2082.  *
  2083.  * \param  bw  browser window
  2084.  */
  2085.  
  2086. void browser_window_destroy(struct browser_window *bw)
  2087. {
  2088.         /* can't destoy child windows on their own */
  2089.         assert(!bw->parent);
  2090.  
  2091.         /* destroy */
  2092.         browser_window_destroy_internal(bw);
  2093.         free(bw);
  2094. }
  2095.  
  2096.  
  2097. /**
  2098.  * Close and destroy all child browser window.
  2099.  *
  2100.  * \param  bw  browser window
  2101.  */
  2102.  
  2103. void browser_window_destroy_children(struct browser_window *bw)
  2104. {
  2105.         int i;
  2106.  
  2107.         if (bw->children) {
  2108.                 for (i = 0; i < (bw->rows * bw->cols); i++)
  2109.                         browser_window_destroy_internal(&bw->children[i]);
  2110.                 free(bw->children);
  2111.                 bw->children = NULL;
  2112.                 bw->rows = 0;
  2113.                 bw->cols = 0;
  2114.         }
  2115.         if (bw->iframes) {
  2116.                 for (i = 0; i < bw->iframe_count; i++)
  2117.                         browser_window_destroy_internal(&bw->iframes[i]);
  2118.                 free(bw->iframes);
  2119.                 bw->iframes = NULL;
  2120.                 bw->iframe_count = 0;
  2121.         }
  2122. }
  2123.  
  2124.  
  2125. /**
  2126.  * Release all memory associated with a browser window.
  2127.  *
  2128.  * \param  bw  browser window
  2129.  */
  2130.  
  2131. void browser_window_destroy_internal(struct browser_window *bw)
  2132. {
  2133.         assert(bw);
  2134.  
  2135.         LOG(("Destroying window"));
  2136.  
  2137.         if (bw->children != NULL || bw->iframes != NULL)
  2138.                 browser_window_destroy_children(bw);
  2139.  
  2140.         schedule_remove(browser_window_refresh, bw);
  2141.  
  2142.         /* If this brower window is not the root window, and has focus, unset
  2143.          * the root browser window's focus pointer. */
  2144.         if (!bw->window) {
  2145.                 struct browser_window *top = browser_window_get_root(bw);
  2146.  
  2147.                 if (top->focus == bw)
  2148.                         top->focus = top;
  2149.  
  2150.                 if (bw->current_content != NULL &&
  2151.                                 top->cur_sel == content_get_selection(
  2152.                                                 bw->current_content)) {
  2153.                         browser_window_set_selection(top, NULL);
  2154.                 }
  2155.         }
  2156.  
  2157.         /* Destroying a search context causes it to redraw any deselected,
  2158.          * content areas, so do this first */
  2159.         browser_window_search_destroy_context(bw);
  2160.  
  2161.         /* Destruction order is important: we must ensure that the frontend
  2162.          * destroys any window(s) associated with this browser window before
  2163.          * we attempt any destructive cleanup.
  2164.          */
  2165.  
  2166.         if (bw->window) {
  2167.                 /* Only the root window has a GUI window */
  2168.                 gui_window_destroy(bw->window);
  2169.         }
  2170.  
  2171.         if (bw->loading_content != NULL) {
  2172.                 hlcache_handle_abort(bw->loading_content);
  2173.                 hlcache_handle_release(bw->loading_content);
  2174.                 bw->loading_content = NULL;
  2175.         }
  2176.  
  2177.         if (bw->current_content != NULL) {
  2178.                 content_status status = content_get_status(bw->current_content);
  2179.                 if (status == CONTENT_STATUS_READY ||
  2180.                                 status == CONTENT_STATUS_DONE)
  2181.                         content_close(bw->current_content);
  2182.  
  2183.                 hlcache_handle_release(bw->current_content);
  2184.                 bw->current_content = NULL;
  2185.         }
  2186.  
  2187.         if (bw->loading_favicon != NULL) {
  2188.                 hlcache_handle_abort(bw->loading_favicon);
  2189.                 hlcache_handle_release(bw->loading_favicon);
  2190.                 bw->loading_favicon = NULL;
  2191.         }
  2192.  
  2193.         if (bw->current_favicon != NULL) {
  2194.                 content_status status = content_get_status(bw->current_favicon);
  2195.  
  2196.                 if (status == CONTENT_STATUS_READY ||
  2197.                     status == CONTENT_STATUS_DONE)
  2198.                         content_close(bw->current_favicon);
  2199.  
  2200.                 hlcache_handle_release(bw->current_favicon);
  2201.                 bw->current_favicon = NULL;
  2202.         }
  2203.  
  2204.         if (bw->box != NULL) {
  2205.                 bw->box->iframe = NULL;
  2206.                 bw->box = NULL;
  2207.         }
  2208.  
  2209.         if (bw->jsctx != NULL) {
  2210.                 js_destroycontext(bw->jsctx);
  2211.         }
  2212.  
  2213.         /* These simply free memory, so are safe here */
  2214.  
  2215.         if (bw->frag_id != NULL)
  2216.                 lwc_string_unref(bw->frag_id);
  2217.  
  2218.         history_destroy(bw->history);
  2219.  
  2220.         free(bw->name);
  2221.         free(bw->status_text);
  2222.         bw->status_text = NULL;
  2223.         LOG(("Status text cache match:miss %d:%d",
  2224.                         bw->status_match, bw->status_miss));
  2225. }
  2226.  
  2227.  
  2228. /**
  2229.  * Reformat a browser window contents to a new width or height.
  2230.  *
  2231.  * \param  bw      the browser window to reformat
  2232.  * \param  width   new width
  2233.  * \param  height  new height
  2234.  */
  2235.  
  2236. void browser_window_reformat(struct browser_window *bw, bool background,
  2237.                 int width, int height)
  2238. {
  2239.         hlcache_handle *c = bw->current_content;
  2240.  
  2241.         if (c == NULL)
  2242.                 return;
  2243.  
  2244.         if (bw->browser_window_type != BROWSER_WINDOW_IFRAME) {
  2245.                 /* Iframe dimensions are already scaled in parent's layout */
  2246.                 width  /= bw->scale;
  2247.                 height /= bw->scale;
  2248.         }
  2249.  
  2250.         if (bw->window == NULL) {
  2251.                 /* Core managed browser window; subtract scrollbar width */
  2252.                 width  -= bw->scroll_y ? SCROLLBAR_WIDTH : 0;
  2253.                 height -= bw->scroll_x ? SCROLLBAR_WIDTH : 0;
  2254.  
  2255.                 width  = width  > 0 ? width  : 0;
  2256.                 height = height > 0 ? height : 0;
  2257.         }
  2258.  
  2259.         content_reformat(c, background, width, height);
  2260. }
  2261.  
  2262.  
  2263. /**
  2264.  * Sets the scale of a browser window
  2265.  *
  2266.  * \param bw    The browser window to scale
  2267.  * \param scale The new scale
  2268.  * \param all   Scale all windows in the tree (ie work up aswell as down)
  2269.  */
  2270.  
  2271. void browser_window_set_scale(struct browser_window *bw, float scale, bool all)
  2272. {
  2273.         while (bw->parent && all)
  2274.                 bw = bw->parent;
  2275.  
  2276.         browser_window_set_scale_internal(bw, scale);
  2277.  
  2278.         if (bw->parent)
  2279.                 bw = bw->parent;
  2280.  
  2281.         browser_window_recalculate_frameset(bw);
  2282. }
  2283.  
  2284. void browser_window_set_scale_internal(struct browser_window *bw, float scale)
  2285. {
  2286.         int i;
  2287.         hlcache_handle *c;
  2288.  
  2289.         if (fabs(bw->scale-scale) < 0.0001)
  2290.                 return;
  2291.  
  2292.         bw->scale = scale;
  2293.         c = bw->current_content;
  2294.  
  2295.         if (c != NULL) {
  2296.                 if (content_can_reformat(c) == false) {
  2297.                         browser_window_update(bw, false);
  2298.                 } else {
  2299.                         bw->reformat_pending = true;
  2300.                         browser_reformat_pending = true;
  2301.                 }
  2302.         }
  2303.  
  2304.         for (i = 0; i < (bw->cols * bw->rows); i++)
  2305.                 browser_window_set_scale_internal(&bw->children[i], scale);
  2306.         for (i = 0; i < bw->iframe_count; i++)
  2307.                 browser_window_set_scale_internal(&bw->iframes[i], scale);
  2308. }
  2309.  
  2310.  
  2311. /**
  2312.  * Gets the scale of a browser window
  2313.  *
  2314.  * \param bw    The browser window to scale
  2315.  * \return
  2316.  */
  2317.  
  2318. float browser_window_get_scale(struct browser_window *bw)
  2319. {
  2320.         return bw->scale;
  2321. }
  2322.  
  2323.  
  2324. /**
  2325.  * Update URL bar for a given browser window to given URL
  2326.  *
  2327.  * \param bw    Browser window to update URL bar for.
  2328.  * \param url   URL for content displayed by bw, excluding any fragment.
  2329.  * \param frag  Additional fragment. May be NULL if none.
  2330.  */
  2331.  
  2332. void browser_window_refresh_url_bar(struct browser_window *bw, nsurl *url,
  2333.                 lwc_string *frag)
  2334. {
  2335.         assert(bw);
  2336.         assert(url);
  2337.  
  2338.         if (bw->parent != NULL) {
  2339.                 /* Not root window; don't set a URL in GUI URL bar */
  2340.                 return;
  2341.         }
  2342.  
  2343.         if (frag == NULL) {
  2344.                 /* With no fragment, we may as well pass url straight through
  2345.                  * saving a malloc, copy, free cycle.
  2346.                  */
  2347.                 gui_window_set_url(bw->window, nsurl_access(url));
  2348.         } else {
  2349.                 nsurl *display_url;
  2350.                 nserror error;
  2351.  
  2352.                 error = nsurl_refragment(url, frag, &display_url);
  2353.                 if (error != NSERROR_OK) {
  2354.                         warn_user("NoMemory", 0);
  2355.                         return;
  2356.                 }
  2357.  
  2358.                 gui_window_set_url(bw->window, nsurl_access(display_url));
  2359.                 nsurl_unref(display_url);
  2360.         }
  2361. }
  2362.  
  2363. /**
  2364.  * Locate a browser window in the specified stack according.
  2365.  *
  2366.  * \param bw  the browser_window to search all relatives of
  2367.  * \param target  the target to locate
  2368.  * \param mouse The current mouse state
  2369.  * \return The browser window the mouse is in
  2370.  */
  2371.  
  2372. struct browser_window *browser_window_find_target(struct browser_window *bw,
  2373.                 const char *target, browser_mouse_state mouse)
  2374. {
  2375.         struct browser_window *bw_target;
  2376.         struct browser_window *top;
  2377.         hlcache_handle *c;
  2378.         int rdepth;
  2379.  
  2380.         /* use the base target if we don't have one */
  2381.         c = bw->current_content;
  2382.         if (target == NULL && c != NULL && content_get_type(c) == CONTENT_HTML)
  2383.                 target = html_get_base_target(c);
  2384.         if (target == NULL)
  2385.                 target = TARGET_SELF;
  2386.  
  2387.         /* allow the simple case of target="_blank" to be ignored if requested
  2388.          */
  2389.         if ((!(mouse & BROWSER_MOUSE_CLICK_2)) &&
  2390.                         (!((mouse & BROWSER_MOUSE_CLICK_2) &&
  2391.                         (mouse & BROWSER_MOUSE_MOD_2))) &&
  2392.             (!nsoption_bool(target_blank))) {
  2393.                 /* not a mouse button 2 click
  2394.                  * not a mouse button 1 click with ctrl pressed
  2395.                  * configured to ignore target="_blank" */
  2396.                 if ((target == TARGET_BLANK) || (!strcasecmp(target, "_blank")))
  2397.                         return bw;
  2398.         }
  2399.  
  2400.         /* handle reserved keywords */
  2401.         if (((nsoption_bool(button_2_tab)) &&
  2402.              (mouse & BROWSER_MOUSE_CLICK_2))||
  2403.             ((!nsoption_bool(button_2_tab)) &&
  2404.              ((mouse & BROWSER_MOUSE_CLICK_1) &&
  2405.               (mouse & BROWSER_MOUSE_MOD_2))) ||
  2406.             ((nsoption_bool(button_2_tab)) &&
  2407.              ((target == TARGET_BLANK) ||
  2408.               (!strcasecmp(target, "_blank"))))) {
  2409.                 /* open in new tab if:
  2410.                  * - button_2 opens in new tab and button_2 was pressed
  2411.                  * OR
  2412.                  * - button_2 doesn't open in new tabs and button_1 was
  2413.                  *   pressed with ctrl held
  2414.                  * OR
  2415.                  * - button_2 opens in new tab and the link target is "_blank"
  2416.                  */
  2417.                 bw_target = browser_window_create(NULL, bw, NULL, false, true);
  2418.                 if (!bw_target)
  2419.                         return bw;
  2420.                 return bw_target;
  2421.         } else if (((!nsoption_bool(button_2_tab)) &&
  2422.                     (mouse & BROWSER_MOUSE_CLICK_2)) ||
  2423.                    ((nsoption_bool(button_2_tab)) &&
  2424.                     ((mouse & BROWSER_MOUSE_CLICK_1) &&
  2425.                      (mouse & BROWSER_MOUSE_MOD_2))) ||
  2426.                    ((!nsoption_bool(button_2_tab)) &&
  2427.                     ((target == TARGET_BLANK) ||
  2428.                      (!strcasecmp(target, "_blank"))))) {
  2429.                 /* open in new window if:
  2430.                  * - button_2 doesn't open in new tabs and button_2 was pressed
  2431.                  * OR
  2432.                  * - button_2 opens in new tab and button_1 was pressed with
  2433.                  *   ctrl held
  2434.                  * OR
  2435.                  * - button_2 doesn't open in new tabs and the link target is
  2436.                  *   "_blank"
  2437.                  */
  2438.                 bw_target = browser_window_create(NULL, bw, NULL, false, false);
  2439.                 if (!bw_target)
  2440.                         return bw;
  2441.                 return bw_target;
  2442.         } else if ((target == TARGET_SELF) || (!strcasecmp(target, "_self"))) {
  2443.                 return bw;
  2444.         } else if ((target == TARGET_PARENT) ||
  2445.                         (!strcasecmp(target, "_parent"))) {
  2446.                 if (bw->parent)
  2447.                         return bw->parent;
  2448.                 return bw;
  2449.         } else if ((target == TARGET_TOP) || (!strcasecmp(target, "_top"))) {
  2450.                 while (bw->parent)
  2451.                         bw = bw->parent;
  2452.                 return bw;
  2453.         }
  2454.  
  2455.         /* find frame according to B.8, ie using the following priorities:
  2456.          *
  2457.          *  1) current frame
  2458.          *  2) closest to front
  2459.          */
  2460.         rdepth = -1;
  2461.         bw_target = NULL;
  2462.         for (top = bw; top->parent; top = top->parent);
  2463.         browser_window_find_target_internal(top, target, 0, bw, &rdepth,
  2464.                         &bw_target);
  2465.         if (bw_target)
  2466.                 return bw_target;
  2467.  
  2468.         /* we require a new window using the target name */
  2469.         if (!nsoption_bool(target_blank))
  2470.                 return bw;
  2471.  
  2472.         bw_target = browser_window_create(NULL, bw, NULL, false, false);
  2473.         if (!bw_target)
  2474.                 return bw;
  2475.  
  2476.         /* frame names should begin with an alphabetic character (a-z,A-Z),
  2477.          * however in practice you get things such as '_new' and '2left'. The
  2478.          * only real effect this has is when giving out names as it can be
  2479.          * assumed that an author intended '_new' to create a new nameless
  2480.          * window (ie '_blank') whereas in the case of '2left' the intention
  2481.          * was for a new named window. As such we merely special case windows
  2482.          * that begin with an underscore. */
  2483.         if (target[0] != '_') {
  2484.                 bw_target->name = strdup(target);
  2485.                 if (!bw_target->name)
  2486.                         warn_user("NoMemory", 0);
  2487.         }
  2488.         return bw_target;
  2489. }
  2490.  
  2491. void browser_window_find_target_internal(struct browser_window *bw,
  2492.                 const char *target, int depth, struct browser_window *page,
  2493.                 int *rdepth, struct browser_window **bw_target)
  2494. {
  2495.         int i;
  2496.  
  2497.         if ((bw->name) && (!strcasecmp(bw->name, target))) {
  2498.                 if ((bw == page) || (depth > *rdepth)) {
  2499.                         *rdepth = depth;
  2500.                         *bw_target = bw;
  2501.                 }
  2502.         }
  2503.  
  2504.         if ((!bw->children) && (!bw->iframes))
  2505.                 return;
  2506.  
  2507.         depth++;
  2508.  
  2509.         if (bw->children != NULL) {
  2510.                 for (i = 0; i < (bw->cols * bw->rows); i++) {
  2511.                         if ((bw->children[i].name) &&
  2512.                                         (!strcasecmp(bw->children[i].name,
  2513.                                         target))) {
  2514.                                 if ((page == &bw->children[i]) ||
  2515.                                                 (depth > *rdepth)) {
  2516.                                         *rdepth = depth;
  2517.                                         *bw_target = &bw->children[i];
  2518.                                 }
  2519.                         }
  2520.                         if (bw->children[i].children)
  2521.                                 browser_window_find_target_internal(
  2522.                                                 &bw->children[i],
  2523.                                                 target, depth, page,
  2524.                                                 rdepth, bw_target);
  2525.                 }
  2526.         }
  2527.  
  2528.         if (bw->iframes != NULL) {
  2529.                 for (i = 0; i < bw->iframe_count; i++)
  2530.                         browser_window_find_target_internal(&bw->iframes[i],
  2531.                                         target, depth, page, rdepth, bw_target);
  2532.         }
  2533. }
  2534.  
  2535.  
  2536. /**
  2537.  * Handle non-click mouse action in a browser window. (drag ends, movements)
  2538.  *
  2539.  * \param  bw     browser window
  2540.  * \param  mouse  state of mouse buttons and modifier keys
  2541.  * \param  x      coordinate of mouse
  2542.  * \param  y      coordinate of mouse
  2543.  */
  2544.  
  2545. void browser_window_mouse_track(struct browser_window *bw,
  2546.                 browser_mouse_state mouse, int x, int y)
  2547. {
  2548.         hlcache_handle *c = bw->current_content;
  2549.         const char *status = NULL;
  2550.         browser_pointer_shape pointer = BROWSER_POINTER_DEFAULT;
  2551.  
  2552.         if (bw->window != NULL && bw->drag_window && bw != bw->drag_window) {
  2553.                 /* This is the root browser window and there's an active drag
  2554.                  * in a sub window.
  2555.                  * Pass the mouse action straight on to that bw. */
  2556.                 struct browser_window *drag_bw = bw->drag_window;
  2557.                 int off_x = 0;
  2558.                 int off_y = 0;
  2559.  
  2560.                 browser_window_get_position(drag_bw, true, &off_x, &off_y);
  2561.  
  2562.                 if (drag_bw->browser_window_type == BROWSER_WINDOW_FRAME) {
  2563.                         browser_window_mouse_track(drag_bw, mouse,
  2564.                                         x - off_x, y - off_y);
  2565.  
  2566.                 } else if (drag_bw->browser_window_type ==
  2567.                                 BROWSER_WINDOW_IFRAME) {
  2568.                         browser_window_mouse_track(drag_bw, mouse,
  2569.                                         x - off_x / bw->scale,
  2570.                                         y - off_y / bw->scale);
  2571.                 }
  2572.                 return;
  2573.         }
  2574.  
  2575.         if (bw->children) {
  2576.                 /* Browser window has children (frames) */
  2577.                 struct browser_window *child;
  2578.                 int cur_child;
  2579.                 int children = bw->rows * bw->cols;
  2580.  
  2581.                 for (cur_child = 0; cur_child < children; cur_child++) {
  2582.  
  2583.                         child = &bw->children[cur_child];
  2584.  
  2585.                         if (x < child->x || y < child->y ||
  2586.                                         child->x + child->width < x ||
  2587.                                         child->y + child->height < y) {
  2588.                                 /* Click not in this child */
  2589.                                 continue;
  2590.                         }
  2591.  
  2592.                         /* It's this child that contains the mouse; pass
  2593.                          * mouse action on to child */
  2594.                         browser_window_mouse_track(child, mouse,
  2595.                                         x - child->x + scrollbar_get_offset(
  2596.                                                         child->scroll_x),
  2597.                                         y - child->y + scrollbar_get_offset(
  2598.                                                         child->scroll_y));
  2599.  
  2600.                         /* Mouse action was for this child, we're done */
  2601.                         return;
  2602.                 }
  2603.  
  2604.                 /* Odd if we reached here, but nothing else can use the click
  2605.                  * when there are children. */
  2606.                 return;
  2607.         }
  2608.  
  2609.         if (c == NULL && bw->drag_type != DRAGGING_FRAME)
  2610.                 return;
  2611.  
  2612.         if (bw->drag_type != DRAGGING_NONE && !mouse) {
  2613.                 browser_window_mouse_drag_end(bw, mouse, x, y);
  2614.         }
  2615.  
  2616.         /* Browser window's horizontal scrollbar */
  2617.         if (bw->scroll_x != NULL && bw->drag_type != DRAGGING_SCR_Y) {
  2618.                 int scr_x, scr_y;
  2619.                 browser_window_get_scrollbar_pos(bw, true, &scr_x, &scr_y);
  2620.                 scr_x = x - scr_x - scrollbar_get_offset(bw->scroll_x);
  2621.                 scr_y = y - scr_y - scrollbar_get_offset(bw->scroll_y);
  2622.  
  2623.                 if ((scr_x > 0 && scr_x < browser_window_get_scrollbar_len(bw,
  2624.                                                 true) &&
  2625.                                 scr_y > 0 && scr_y < SCROLLBAR_WIDTH &&
  2626.                                 bw->drag_type == DRAGGING_NONE) ||
  2627.                                 bw->drag_type == DRAGGING_SCR_X) {
  2628.                         /* Start a scrollbar drag, or continue existing drag */
  2629.                         status = scrollbar_mouse_action(bw->scroll_x, mouse,
  2630.                                         scr_x, scr_y);
  2631.                         pointer = BROWSER_POINTER_DEFAULT;
  2632.  
  2633.                         if (status != NULL)
  2634.                                 browser_window_set_status(bw, status);
  2635.  
  2636.                         browser_window_set_pointer(bw, pointer);
  2637.                         return;
  2638.                 }
  2639.         }
  2640.  
  2641.         /* Browser window's vertical scrollbar */
  2642.         if (bw->scroll_y != NULL) {
  2643.                 int scr_x, scr_y;
  2644.                 browser_window_get_scrollbar_pos(bw, false, &scr_x, &scr_y);
  2645.                 scr_x = x - scr_x - scrollbar_get_offset(bw->scroll_x);
  2646.                 scr_y = y - scr_y - scrollbar_get_offset(bw->scroll_y);
  2647.  
  2648.                 if ((scr_y > 0 && scr_y < browser_window_get_scrollbar_len(bw,
  2649.                                                 false) &&
  2650.                                 scr_x > 0 && scr_x < SCROLLBAR_WIDTH &&
  2651.                                 bw->drag_type == DRAGGING_NONE) ||
  2652.                                 bw->drag_type == DRAGGING_SCR_Y) {
  2653.                         /* Start a scrollbar drag, or continue existing drag */
  2654.                         status = scrollbar_mouse_action(bw->scroll_y, mouse,
  2655.                                         scr_x, scr_y);
  2656.                         pointer = BROWSER_POINTER_DEFAULT;
  2657.  
  2658.                         if (status != NULL)
  2659.                                 browser_window_set_status(bw, status);
  2660.  
  2661.                         browser_window_set_pointer(bw, pointer);
  2662.                         return;
  2663.                 }
  2664.         }
  2665.  
  2666.         if (bw->drag_type == DRAGGING_FRAME) {
  2667.                 browser_window_resize_frame(bw, bw->x + x, bw->y + y);
  2668.         } else if (bw->drag_type == DRAGGING_PAGE_SCROLL) {
  2669.                 /* mouse movement since drag started */
  2670.                 int scrollx = bw->drag_start_x - x;
  2671.                 int scrolly = bw->drag_start_y - y;
  2672.  
  2673.                 /* new scroll offsets */
  2674.                 scrollx += bw->drag_start_scroll_x;
  2675.                 scrolly += bw->drag_start_scroll_y;
  2676.  
  2677.                 bw->drag_start_scroll_x = scrollx;
  2678.                 bw->drag_start_scroll_y = scrolly;
  2679.  
  2680.                 browser_window_set_scroll(bw, scrollx, scrolly);
  2681.         } else {
  2682.                 assert(c != NULL);
  2683.                 content_mouse_track(c, bw, mouse, x, y);
  2684.         }
  2685. }
  2686.  
  2687.  
  2688. /**
  2689.  * Handle mouse clicks in a browser window.
  2690.  *
  2691.  * \param  bw     browser window
  2692.  * \param  mouse  state of mouse buttons and modifier keys
  2693.  * \param  x      coordinate of mouse
  2694.  * \param  y      coordinate of mouse
  2695.  */
  2696.  
  2697. void browser_window_mouse_click(struct browser_window *bw,
  2698.                 browser_mouse_state mouse, int x, int y)
  2699. {
  2700.         hlcache_handle *c = bw->current_content;
  2701.         const char *status = NULL;
  2702.         browser_pointer_shape pointer = BROWSER_POINTER_DEFAULT;
  2703.  
  2704.         if (bw->children) {
  2705.                 /* Browser window has children (frames) */
  2706.                 struct browser_window *child;
  2707.                 int cur_child;
  2708.                 int children = bw->rows * bw->cols;
  2709.  
  2710.                 for (cur_child = 0; cur_child < children; cur_child++) {
  2711.  
  2712.                         child = &bw->children[cur_child];
  2713.  
  2714.                         if (x < child->x || y < child->y ||
  2715.                                         child->x + child->width < x ||
  2716.                                         child->y + child->height < y) {
  2717.                                 /* Click not in this child */
  2718.                                 continue;
  2719.                         }
  2720.  
  2721.                         /* It's this child that contains the click; pass it
  2722.                          * on to child. */
  2723.                         browser_window_mouse_click(child, mouse,
  2724.                                         x - child->x + scrollbar_get_offset(
  2725.                                                         child->scroll_x),
  2726.                                         y - child->y + scrollbar_get_offset(
  2727.                                                         child->scroll_y));
  2728.  
  2729.                         /* Mouse action was for this child, we're done */
  2730.                         return;
  2731.                 }
  2732.  
  2733.                 return;
  2734.         }
  2735.  
  2736.         if (!c)
  2737.                 return;
  2738.  
  2739.         if (bw->scroll_x != NULL) {
  2740.                 int scr_x, scr_y;
  2741.                 browser_window_get_scrollbar_pos(bw, true, &scr_x, &scr_y);
  2742.                 scr_x = x - scr_x - scrollbar_get_offset(bw->scroll_x);
  2743.                 scr_y = y - scr_y - scrollbar_get_offset(bw->scroll_y);
  2744.  
  2745.                 if (scr_x > 0 && scr_x < browser_window_get_scrollbar_len(bw,
  2746.                                                 true) &&
  2747.                                 scr_y > 0 && scr_y < SCROLLBAR_WIDTH) {
  2748.                         status = scrollbar_mouse_action(bw->scroll_x, mouse,
  2749.                                         scr_x, scr_y);
  2750.                         pointer = BROWSER_POINTER_DEFAULT;
  2751.  
  2752.                         if (status != NULL)
  2753.                                 browser_window_set_status(bw, status);
  2754.  
  2755.                         browser_window_set_pointer(bw, pointer);
  2756.                         return;
  2757.                 }
  2758.         }
  2759.  
  2760.         if (bw->scroll_y != NULL) {
  2761.                 int scr_x, scr_y;
  2762.                 browser_window_get_scrollbar_pos(bw, false, &scr_x, &scr_y);
  2763.                 scr_x = x - scr_x - scrollbar_get_offset(bw->scroll_x);
  2764.                 scr_y = y - scr_y - scrollbar_get_offset(bw->scroll_y);
  2765.  
  2766.                 if (scr_y > 0 && scr_y < browser_window_get_scrollbar_len(bw,
  2767.                                                 false) &&
  2768.                                 scr_x > 0 && scr_x < SCROLLBAR_WIDTH) {
  2769.                         status = scrollbar_mouse_action(bw->scroll_y, mouse,
  2770.                                         scr_x, scr_y);
  2771.                         pointer = BROWSER_POINTER_DEFAULT;
  2772.  
  2773.                         if (status != NULL)
  2774.                                 browser_window_set_status(bw, status);
  2775.  
  2776.                         browser_window_set_pointer(bw, pointer);
  2777.                         return;
  2778.                 }
  2779.         }
  2780.  
  2781.         switch (content_get_type(c)) {
  2782.         case CONTENT_HTML:
  2783.         case CONTENT_TEXTPLAIN:
  2784.                 content_mouse_action(c, bw, mouse, x, y);
  2785.                 break;
  2786.         default:
  2787.                 if (mouse & BROWSER_MOUSE_MOD_2) {
  2788.                         if (mouse & BROWSER_MOUSE_DRAG_2)
  2789.                                 gui_drag_save_object(GUI_SAVE_OBJECT_NATIVE, c,
  2790.                                                 bw->window);
  2791.                         else if (mouse & BROWSER_MOUSE_DRAG_1)
  2792.                                 gui_drag_save_object(GUI_SAVE_OBJECT_ORIG, c,
  2793.                                                 bw->window);
  2794.                 }
  2795.                 else if (mouse & (BROWSER_MOUSE_DRAG_1 |
  2796.                                 BROWSER_MOUSE_DRAG_2)) {
  2797.                         browser_window_page_drag_start(bw, x, y);
  2798.                         browser_window_set_pointer(bw, BROWSER_POINTER_MOVE);
  2799.                 }
  2800.                 break;
  2801.         }
  2802. }
  2803.  
  2804.  
  2805. /**
  2806.  * Handles the end of a drag operation in a browser window.
  2807.  *
  2808.  * \param  bw     browser window
  2809.  * \param  mouse  state of mouse buttons and modifier keys
  2810.  * \param  x      coordinate of mouse
  2811.  * \param  y      coordinate of mouse
  2812.  *
  2813.  * TODO: Remove this function, once these things are associated with content,
  2814.  *       rather than bw.
  2815.  */
  2816.  
  2817. void browser_window_mouse_drag_end(struct browser_window *bw,
  2818.                 browser_mouse_state mouse, int x, int y)
  2819. {
  2820.         int scr_x, scr_y;
  2821.  
  2822.         switch (bw->drag_type) {
  2823.         case DRAGGING_SELECTION:
  2824.         case DRAGGING_OTHER:
  2825.         case DRAGGING_CONTENT_SCROLLBAR:
  2826.                 /* Drag handled by content handler */
  2827.                 break;
  2828.  
  2829.         case DRAGGING_SCR_X:
  2830.  
  2831.                 browser_window_get_scrollbar_pos(bw, true, &scr_x, &scr_y);
  2832.  
  2833.                 scr_x = x - scr_x - scrollbar_get_offset(bw->scroll_x);
  2834.                 scr_y = y - scr_y - scrollbar_get_offset(bw->scroll_y);
  2835.  
  2836.                 scrollbar_mouse_drag_end(bw->scroll_x, mouse, scr_x, scr_y);
  2837.  
  2838.                 bw->drag_type = DRAGGING_NONE;
  2839.                 break;
  2840.  
  2841.         case DRAGGING_SCR_Y:
  2842.  
  2843.                 browser_window_get_scrollbar_pos(bw, false, &scr_x, &scr_y);
  2844.  
  2845.                 scr_x = x - scr_x - scrollbar_get_offset(bw->scroll_x);
  2846.                 scr_y = y - scr_y - scrollbar_get_offset(bw->scroll_y);
  2847.  
  2848.                 scrollbar_mouse_drag_end(bw->scroll_y, mouse, scr_x, scr_y);
  2849.  
  2850.                 bw->drag_type = DRAGGING_NONE;
  2851.                 break;
  2852.  
  2853.         default:
  2854.                 browser_window_set_drag_type(bw, DRAGGING_NONE, NULL);
  2855.                 break;
  2856.         }
  2857. }
  2858.  
  2859.  
  2860. /**
  2861.  * Redraw a rectangular region of a browser window
  2862.  *
  2863.  * \param  bw     browser window to be redrawn
  2864.  * \param  x      x co-ord of top-left
  2865.  * \param  y      y co-ord of top-left
  2866.  * \param  width  width of rectangle
  2867.  * \param  height height of rectangle
  2868.  */
  2869.  
  2870. void browser_window_redraw_rect(struct browser_window *bw, int x, int y,
  2871.                 int width, int height)
  2872. {
  2873.         content_request_redraw(bw->current_content, x, y, width, height);
  2874. }
  2875.  
  2876.  
  2877. /**
  2878.  * Start drag scrolling the contents of the browser window
  2879.  *
  2880.  * \param bw  browser window
  2881.  * \param x   x ordinate of initial mouse position
  2882.  * \param y   y ordinate
  2883.  */
  2884.  
  2885. void browser_window_page_drag_start(struct browser_window *bw, int x, int y)
  2886. {
  2887.         browser_window_set_drag_type(bw, DRAGGING_PAGE_SCROLL, NULL);
  2888.  
  2889.         bw->drag_start_x = x;
  2890.         bw->drag_start_y = y;
  2891.  
  2892.         if (bw->window != NULL) {
  2893.                 /* Front end window */
  2894.                 gui_window_get_scroll(bw->window, &bw->drag_start_scroll_x,
  2895.                                 &bw->drag_start_scroll_y);
  2896.  
  2897.                 gui_window_scroll_start(bw->window);
  2898.         } else {
  2899.                 /* Core managed browser window */
  2900.                 bw->drag_start_scroll_x = scrollbar_get_offset(bw->scroll_x);
  2901.                 bw->drag_start_scroll_y = scrollbar_get_offset(bw->scroll_y);
  2902.         }
  2903. }
  2904.  
  2905.  
  2906. /**
  2907.  * Check availability of Back action for a given browser window
  2908.  *
  2909.  * \param bw  browser window
  2910.  * \return true if Back action is available
  2911.  */
  2912.  
  2913. bool browser_window_back_available(struct browser_window *bw)
  2914. {
  2915.         return (bw && bw->history && history_back_available(bw->history));
  2916. }
  2917.  
  2918.  
  2919. /**
  2920.  * Check availability of Forward action for a given browser window
  2921.  *
  2922.  * \param bw  browser window
  2923.  * \return true if Forward action is available
  2924.  */
  2925.  
  2926. bool browser_window_forward_available(struct browser_window *bw)
  2927. {
  2928.         return (bw && bw->history && history_forward_available(bw->history));
  2929. }
  2930.  
  2931.  
  2932. /**
  2933.  * Check availability of Reload action for a given browser window
  2934.  *
  2935.  * \param bw  browser window
  2936.  * \return true if Reload action is available
  2937.  */
  2938.  
  2939. bool browser_window_reload_available(struct browser_window *bw)
  2940. {
  2941.         return (bw && bw->current_content && !bw->loading_content);
  2942. }
  2943.  
  2944.  
  2945. /**
  2946.  * Check availability of Stop action for a given browser window
  2947.  *
  2948.  * \param bw  browser window
  2949.  * \return true if Stop action is available
  2950.  */
  2951.  
  2952. bool browser_window_stop_available(struct browser_window *bw)
  2953. {
  2954.         return (bw && (bw->loading_content ||
  2955.                         (bw->current_content &&
  2956.                         (content_get_status(bw->current_content) !=
  2957.                         CONTENT_STATUS_DONE))));
  2958. }
  2959.