Subversion Repositories Kolibri OS

Rev

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

  1. /*
  2.  * Copyright 2006 James Bursa <bursa@users.sourceforge.net>
  3.  * Copyright 2006 Richard Wilson <info@tinct.net>
  4.  * Copyright 2008 Michael Drake <tlsa@netsurf-browser.org>
  5.  * Copyright 2009 Paul Blokus <paul_pl@users.sourceforge.net>  
  6.  *
  7.  * This file is part of NetSurf, http://www.netsurf-browser.org/
  8.  *
  9.  * NetSurf is free software; you can redistribute it and/or modify
  10.  * it under the terms of the GNU General Public License as published by
  11.  * the Free Software Foundation; version 2 of the License.
  12.  *
  13.  * NetSurf is distributed in the hope that it will be useful,
  14.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  16.  * GNU General Public License for more details.
  17.  *
  18.  * You should have received a copy of the GNU General Public License
  19.  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  20.  */
  21.  
  22. /** \file
  23.  * User interaction with a CONTENT_HTML (implementation).
  24.  */
  25.  
  26. #include <assert.h>
  27. #include <stdbool.h>
  28.  
  29. #include <dom/dom.h>
  30.  
  31. #include "content/content.h"
  32. #include "desktop/browser.h"
  33. #include "desktop/frames.h"
  34. #include "desktop/mouse.h"
  35. #include "desktop/options.h"
  36. #include "desktop/scrollbar.h"
  37. #include "desktop/selection.h"
  38. #include "desktop/textinput.h"
  39. #include "render/box.h"
  40. #include "render/font.h"
  41. #include "render/form.h"
  42. #include "render/html_internal.h"
  43. #include "render/imagemap.h"
  44. #include "render/textinput.h"
  45. #include "javascript/js.h"
  46. #include "utils/messages.h"
  47. #include "utils/utils.h"
  48.  
  49.  
  50. /**
  51.  * Get pointer shape for given box
  52.  *
  53.  * \param box       box in question
  54.  * \param imagemap  whether an imagemap applies to the box
  55.  */
  56.  
  57. static browser_pointer_shape get_pointer_shape(struct box *box, bool imagemap)
  58. {
  59.         browser_pointer_shape pointer;
  60.         css_computed_style *style;
  61.         enum css_cursor_e cursor;
  62.         lwc_string **cursor_uris;
  63.  
  64.         if (box->type == BOX_FLOAT_LEFT || box->type == BOX_FLOAT_RIGHT)
  65.                 style = box->children->style;
  66.         else
  67.                 style = box->style;
  68.  
  69.         if (style == NULL)
  70.                 return BROWSER_POINTER_DEFAULT;
  71.  
  72.         cursor = css_computed_cursor(style, &cursor_uris);
  73.  
  74.         switch (cursor) {
  75.         case CSS_CURSOR_AUTO:
  76.                 if (box->href || (box->gadget &&
  77.                                 (box->gadget->type == GADGET_IMAGE ||
  78.                                 box->gadget->type == GADGET_SUBMIT)) ||
  79.                                 imagemap) {
  80.                         /* link */
  81.                         pointer = BROWSER_POINTER_POINT;
  82.                 } else if (box->gadget &&
  83.                                 (box->gadget->type == GADGET_TEXTBOX ||
  84.                                 box->gadget->type == GADGET_PASSWORD ||
  85.                                 box->gadget->type == GADGET_TEXTAREA)) {
  86.                         /* text input */
  87.                         pointer = BROWSER_POINTER_CARET;
  88.                 } else {
  89.                         /* html content doesn't mind */
  90.                         pointer = BROWSER_POINTER_AUTO;
  91.                 }
  92.                 break;
  93.         case CSS_CURSOR_CROSSHAIR:
  94.                 pointer = BROWSER_POINTER_CROSS;
  95.                 break;
  96.         case CSS_CURSOR_POINTER:
  97.                 pointer = BROWSER_POINTER_POINT;
  98.                 break;
  99.         case CSS_CURSOR_MOVE:
  100.                 pointer = BROWSER_POINTER_MOVE;
  101.                 break;
  102.         case CSS_CURSOR_E_RESIZE:
  103.                 pointer = BROWSER_POINTER_RIGHT;
  104.                 break;
  105.         case CSS_CURSOR_W_RESIZE:
  106.                 pointer = BROWSER_POINTER_LEFT;
  107.                 break;
  108.         case CSS_CURSOR_N_RESIZE:
  109.                 pointer = BROWSER_POINTER_UP;
  110.                 break;
  111.         case CSS_CURSOR_S_RESIZE:
  112.                 pointer = BROWSER_POINTER_DOWN;
  113.                 break;
  114.         case CSS_CURSOR_NE_RESIZE:
  115.                 pointer = BROWSER_POINTER_RU;
  116.                 break;
  117.         case CSS_CURSOR_SW_RESIZE:
  118.                 pointer = BROWSER_POINTER_LD;
  119.                 break;
  120.         case CSS_CURSOR_SE_RESIZE:
  121.                 pointer = BROWSER_POINTER_RD;
  122.                 break;
  123.         case CSS_CURSOR_NW_RESIZE:
  124.                 pointer = BROWSER_POINTER_LU;
  125.                 break;
  126.         case CSS_CURSOR_TEXT:
  127.                 pointer = BROWSER_POINTER_CARET;
  128.                 break;
  129.         case CSS_CURSOR_WAIT:
  130.                 pointer = BROWSER_POINTER_WAIT;
  131.                 break;
  132.         case CSS_CURSOR_PROGRESS:
  133.                 pointer = BROWSER_POINTER_PROGRESS;
  134.                 break;
  135.         case CSS_CURSOR_HELP:
  136.                 pointer = BROWSER_POINTER_HELP;
  137.                 break;
  138.         default:
  139.                 pointer = BROWSER_POINTER_DEFAULT;
  140.                 break;
  141.         }
  142.  
  143.         return pointer;
  144. }
  145.  
  146.  
  147. /**
  148.  * Start drag scrolling the contents of a box
  149.  *
  150.  * \param box   the box to be scrolled
  151.  * \param x     x ordinate of initial mouse position
  152.  * \param y     y ordinate
  153.  */
  154.  
  155. static void html_box_drag_start(struct box *box, int x, int y)
  156. {
  157.         int box_x, box_y;
  158.         int scroll_mouse_x, scroll_mouse_y;
  159.  
  160.         box_coords(box, &box_x, &box_y);
  161.  
  162.         if (box->scroll_x != NULL) {
  163.                 scroll_mouse_x = x - box_x ;
  164.                 scroll_mouse_y = y - (box_y + box->padding[TOP] +
  165.                                 box->height + box->padding[BOTTOM] -
  166.                                 SCROLLBAR_WIDTH);
  167.                 scrollbar_start_content_drag(box->scroll_x,
  168.                                 scroll_mouse_x, scroll_mouse_y);
  169.         } else if (box->scroll_y != NULL) {
  170.                 scroll_mouse_x = x - (box_x + box->padding[LEFT] +
  171.                                 box->width + box->padding[RIGHT] -
  172.                                 SCROLLBAR_WIDTH);
  173.                 scroll_mouse_y = y - box_y;
  174.  
  175.                 scrollbar_start_content_drag(box->scroll_y,
  176.                                 scroll_mouse_x, scroll_mouse_y);
  177.         }
  178. }
  179.  
  180.  
  181. /**
  182.  * End overflow scroll scrollbar drags
  183.  *
  184.  * \param  h      html content's high level cache entry
  185.  * \param  mouse  state of mouse buttons and modifier keys
  186.  * \param  x      coordinate of mouse
  187.  * \param  y      coordinate of mouse
  188.  */
  189. static size_t html_selection_drag_end(struct html_content *html,
  190.                 browser_mouse_state mouse, int x, int y, int dir)
  191. {
  192.         int pixel_offset;
  193.         struct box *box;
  194.         int dx, dy;
  195.         size_t idx = 0;
  196.  
  197.         box = box_pick_text_box(html, x, y, dir, &dx, &dy);
  198.         if (box) {
  199.                 plot_font_style_t fstyle;
  200.  
  201.                 font_plot_style_from_css(box->style, &fstyle);
  202.  
  203.                 nsfont.font_position_in_string(&fstyle, box->text, box->length,
  204.                                 dx, &idx, &pixel_offset);
  205.  
  206.                 idx += box->byte_offset;
  207.         }
  208.  
  209.         return idx;
  210. }
  211.  
  212.  
  213. /**
  214.  * Handle mouse tracking (including drags) in an HTML content window.
  215.  *
  216.  * \param  c      content of type html
  217.  * \param  bw     browser window
  218.  * \param  mouse  state of mouse buttons and modifier keys
  219.  * \param  x      coordinate of mouse
  220.  * \param  y      coordinate of mouse
  221.  */
  222.  
  223. void html_mouse_track(struct content *c, struct browser_window *bw,
  224.                 browser_mouse_state mouse, int x, int y)
  225. {
  226.         html_content *html = (html_content*) c;
  227.         browser_drag_type drag_type = browser_window_get_drag_type(bw);
  228.  
  229.         if (drag_type == DRAGGING_SELECTION && !mouse) {
  230.                 int dir = -1;
  231.                 size_t idx;
  232.  
  233.                 if (selection_dragging_start(&html->sel))
  234.                         dir = 1;
  235.  
  236.                 idx = html_selection_drag_end(html, mouse, x, y, dir);
  237.  
  238.                 if (idx != 0)
  239.                         selection_track(&html->sel, mouse, idx);
  240.  
  241.                 browser_window_set_drag_type(bw, DRAGGING_NONE, NULL);
  242.         }
  243.  
  244.         switch (drag_type) {
  245.                 case DRAGGING_SELECTION: {
  246.                         struct box *box;
  247.                         int dir = -1;
  248.                         int dx, dy;
  249.  
  250.                         if (selection_dragging_start(&html->sel))
  251.                                 dir = 1;
  252.  
  253.                         box = box_pick_text_box(html, x, y, dir, &dx, &dy);
  254.  
  255.                         if (box) {
  256.                                 int pixel_offset;
  257.                                 size_t idx;
  258.                                 plot_font_style_t fstyle;
  259.  
  260.                                 font_plot_style_from_css(box->style, &fstyle);
  261.  
  262.                                 nsfont.font_position_in_string(&fstyle,
  263.                                                 box->text, box->length,
  264.                                                 dx, &idx, &pixel_offset);
  265.  
  266.                                 selection_track(&html->sel, mouse,
  267.                                                 box->byte_offset + idx);
  268.                         }
  269.                 }
  270.                 break;
  271.  
  272.                 default:
  273.                         html_mouse_action(c, bw, mouse, x, y);
  274.                         break;
  275.         }
  276. }
  277.  
  278.  
  279. /**
  280.  * Handle mouse clicks and movements in an HTML content window.
  281.  *
  282.  * \param  c      content of type html
  283.  * \param  bw     browser window
  284.  * \param  mouse  state of mouse buttons and modifier keys
  285.  * \param  x      coordinate of mouse
  286.  * \param  y      coordinate of mouse
  287.  *
  288.  * This function handles both hovering and clicking. It is important that the
  289.  * code path is identical (except that hovering doesn't carry out the action),
  290.  * so that the status bar reflects exactly what will happen. Having separate
  291.  * code paths opens the possibility that an attacker will make the status bar
  292.  * show some harmless action where clicking will be harmful.
  293.  */
  294.  
  295. void html_mouse_action(struct content *c, struct browser_window *bw,
  296.                 browser_mouse_state mouse, int x, int y)
  297. {
  298.         html_content *html = (html_content *) c;
  299.         enum { ACTION_NONE, ACTION_SUBMIT, ACTION_GO } action = ACTION_NONE;
  300.         const char *title = 0;
  301.         nsurl *url = 0;
  302.         const char *target = 0;
  303.         char status_buffer[200];
  304.         const char *status = 0;
  305.         browser_pointer_shape pointer = BROWSER_POINTER_DEFAULT;
  306.         bool imagemap = false;
  307.         int box_x = 0, box_y = 0;
  308.         int gadget_box_x = 0, gadget_box_y = 0;
  309.         int html_object_pos_x = 0, html_object_pos_y = 0;
  310.         int text_box_x = 0;
  311.         struct box *url_box = 0;
  312.         struct box *gadget_box = 0;
  313.         struct box *text_box = 0;
  314.         struct box *box;
  315.         struct form_control *gadget = 0;
  316.         hlcache_handle *object = NULL;
  317.         struct box *html_object_box = NULL;
  318.         struct browser_window *iframe = NULL;
  319.         struct box *next_box;
  320.         struct box *drag_candidate = NULL;
  321.         struct scrollbar *scrollbar = NULL;
  322.         plot_font_style_t fstyle;
  323.         int scroll_mouse_x = 0, scroll_mouse_y = 0;
  324.         int padding_left, padding_right, padding_top, padding_bottom;
  325.         browser_drag_type drag_type = browser_window_get_drag_type(bw);
  326.         union content_msg_data msg_data;
  327.         struct dom_node *node = NULL;
  328.  
  329.         if (drag_type != DRAGGING_NONE && !mouse &&
  330.                         html->visible_select_menu != NULL) {
  331.                 /* drag end: select menu */
  332.                 form_select_mouse_drag_end(html->visible_select_menu,
  333.                                 mouse, x, y);
  334.         }
  335.  
  336.         if (html->visible_select_menu != NULL) {
  337.                 box = html->visible_select_menu->box;
  338.                 box_coords(box, &box_x, &box_y);
  339.  
  340.                 box_x -= box->border[LEFT].width;
  341.                 box_y += box->height + box->border[BOTTOM].width +
  342.                                 box->padding[BOTTOM] + box->padding[TOP];
  343.                 status = form_select_mouse_action(html->visible_select_menu,
  344.                                 mouse, x - box_x, y - box_y);
  345.                 if (status != NULL) {
  346.                         msg_data.explicit_status_text = status;
  347.                         content_broadcast(c, CONTENT_MSG_STATUS, msg_data);
  348.                 } else {
  349.                         int width, height;
  350.                         form_select_get_dimensions(html->visible_select_menu,
  351.                                         &width, &height);
  352.                         html->visible_select_menu = NULL;
  353.                         browser_window_redraw_rect(bw, box_x, box_y,
  354.                                         width, height);                                
  355.                 }
  356.                 return;
  357.         }
  358.  
  359.         if (!mouse && html->scrollbar != NULL) {
  360.                 /* drag end: scrollbar */
  361.                 html_overflow_scroll_drag_end(html->scrollbar, mouse, x, y);
  362.         }
  363.  
  364.         if (html->scrollbar != NULL) {
  365.                 struct html_scrollbar_data *data =
  366.                                 scrollbar_get_data(html->scrollbar);
  367.                 box = data->box;
  368.                 box_coords(box, &box_x, &box_y);
  369.                 if (scrollbar_is_horizontal(html->scrollbar)) {
  370.                         scroll_mouse_x = x - box_x ;
  371.                         scroll_mouse_y = y - (box_y + box->padding[TOP] +
  372.                                         box->height + box->padding[BOTTOM] -
  373.                                         SCROLLBAR_WIDTH);
  374.                         status = scrollbar_mouse_action(html->scrollbar, mouse,
  375.                                         scroll_mouse_x, scroll_mouse_y);
  376.                 } else {
  377.                         scroll_mouse_x = x - (box_x + box->padding[LEFT] +
  378.                                         box->width + box->padding[RIGHT] -
  379.                                         SCROLLBAR_WIDTH);
  380.                         scroll_mouse_y = y - box_y;
  381.                         status = scrollbar_mouse_action(html->scrollbar, mouse,
  382.                                         scroll_mouse_x, scroll_mouse_y);
  383.                 }
  384.  
  385.                 msg_data.explicit_status_text = status;
  386.                 content_broadcast(c, CONTENT_MSG_STATUS, msg_data);
  387.                 return;
  388.         }
  389.  
  390.         /* Content related drags handled by now */
  391.         browser_window_set_drag_type(bw, DRAGGING_NONE, NULL);
  392.  
  393.         /* search the box tree for a link, imagemap, form control, or
  394.          * box with scrollbars
  395.          */
  396.  
  397.         box = html->layout;
  398.  
  399.         /* Consider the margins of the html page now */
  400.         box_x = box->margin[LEFT];
  401.         box_y = box->margin[TOP];
  402.  
  403.         /* descend through visible boxes setting more specific values for:
  404.          * box - deepest box at point
  405.          * html_object_box - html object
  406.          * html_object_pos_x - html object
  407.          * html_object_pos_y - html object
  408.          * object - non html object
  409.          * iframe - iframe
  410.          * url - href or imagemap
  411.          * target - href or imagemap or gadget
  412.          * url_box - href or imagemap
  413.          * imagemap - imagemap
  414.          * gadget - gadget
  415.          * gadget_box - gadget
  416.          * gadget_box_x - gadget
  417.          * gadget_box_y - gadget
  418.          * title - title
  419.          * pointer
  420.          *
  421.          * drag_candidate - first box with scroll
  422.          * padding_left - box with scroll
  423.          * padding_right
  424.          * padding_top
  425.          * padding_bottom
  426.          * scrollbar - inside padding box stops decent
  427.          * scroll_mouse_x - inside padding box stops decent
  428.          * scroll_mouse_y - inside padding box stops decent
  429.          *
  430.          * text_box - text box
  431.          * text_box_x - text_box
  432.          */
  433.         while ((next_box = box_at_point(box, x, y, &box_x, &box_y)) != NULL) {
  434.                 box = next_box;
  435.  
  436.                 if ((box->style != NULL) &&
  437.                     (css_computed_visibility(box->style) ==
  438.                      CSS_VISIBILITY_HIDDEN)) {
  439.                         continue;
  440.                 }
  441.  
  442.                 if (box->node != NULL) {
  443.                         node = box->node;
  444.                 }
  445.  
  446.                 if (box->object) {
  447.                         if (content_get_type(box->object) == CONTENT_HTML) {
  448.                                 html_object_box = box;
  449.                                 html_object_pos_x = box_x;
  450.                                 html_object_pos_y = box_y;
  451.                         } else {
  452.                                 object = box->object;
  453.                         }
  454.                 }
  455.  
  456.                 if (box->iframe) {
  457.                         iframe = box->iframe;
  458.                 }
  459.  
  460.                 if (box->href) {
  461.                         url = box->href;
  462.                         target = box->target;
  463.                         url_box = box;
  464.                 }
  465.  
  466.                 if (box->usemap) {
  467.                         url = imagemap_get(html, box->usemap,
  468.                                         box_x, box_y, x, y, &target);
  469.                         if (url) {
  470.                                 imagemap = true;
  471.                                 url_box = box;
  472.                         }
  473.                 }
  474.  
  475.                 if (box->gadget) {
  476.                         gadget = box->gadget;
  477.                         gadget_box = box;
  478.                         gadget_box_x = box_x;
  479.                         gadget_box_y = box_y;
  480.                         if (gadget->form)
  481.                                 target = gadget->form->target;
  482.                 }
  483.  
  484.                 if (box->title) {
  485.                         title = box->title;
  486.                 }
  487.  
  488.                 pointer = get_pointer_shape(box, false);
  489.                
  490.                 if ((box->scroll_x != NULL) ||
  491.                     (box->scroll_y != NULL)) {
  492.  
  493.                         if (drag_candidate == NULL) {
  494.                                 drag_candidate = box;
  495.                         }
  496.  
  497.                         padding_left = box_x +
  498.                                         scrollbar_get_offset(box->scroll_x);
  499.                         padding_right = padding_left + box->padding[LEFT] +
  500.                                         box->width + box->padding[RIGHT];
  501.                         padding_top = box_y +
  502.                                         scrollbar_get_offset(box->scroll_y);
  503.                         padding_bottom = padding_top + box->padding[TOP] +
  504.                                         box->height + box->padding[BOTTOM];
  505.                        
  506.                         if ((x > padding_left) &&
  507.                             (x < padding_right) &&
  508.                             (y > padding_top) &&
  509.                             (y < padding_bottom)) {
  510.                                 /* mouse inside padding box */
  511.                                
  512.                                 if ((box->scroll_y != NULL) &&
  513.                                     (x > (padding_right - SCROLLBAR_WIDTH))) {
  514.                                         /* mouse above vertical box scroll */
  515.                                        
  516.                                         scrollbar = box->scroll_y;
  517.                                         scroll_mouse_x = x - (padding_right -
  518.                                                              SCROLLBAR_WIDTH);
  519.                                         scroll_mouse_y = y - padding_top;
  520.                                         break;
  521.                                
  522.                                 } else if ((box->scroll_x != NULL) &&
  523.                                            (y > (padding_bottom - SCROLLBAR_WIDTH))) {
  524.                                         /* mouse above horizontal box scroll */
  525.                                                        
  526.                                         scrollbar = box->scroll_x;
  527.                                         scroll_mouse_x = x - padding_left;
  528.                                         scroll_mouse_y = y - (padding_bottom -
  529.                                                         SCROLLBAR_WIDTH);
  530.                                         break;
  531.                                 }
  532.                         }
  533.                 }
  534.  
  535.                 if (box->text && !box->object) {
  536.                         text_box = box;
  537.                         text_box_x = box_x;
  538.                 }
  539.         }
  540.  
  541.         /* use of box_x, box_y, or content below this point is probably a
  542.          * mistake; they will refer to the last box returned by box_at_point */
  543.  
  544.         if (scrollbar) {
  545.                 status = scrollbar_mouse_action(scrollbar, mouse,
  546.                                 scroll_mouse_x, scroll_mouse_y);
  547.                 pointer = BROWSER_POINTER_DEFAULT;
  548.         } else if (gadget) {
  549.                 switch (gadget->type) {
  550.                 case GADGET_SELECT:
  551.                         status = messages_get("FormSelect");
  552.                         pointer = BROWSER_POINTER_MENU;
  553.                         if (mouse & BROWSER_MOUSE_CLICK_1 &&
  554.                             nsoption_bool(core_select_menu)) {
  555.                                 html->visible_select_menu = gadget;
  556.                                 form_open_select_menu(c, gadget,
  557.                                                 form_select_menu_callback,
  558.                                                 c);
  559.                                 pointer =  BROWSER_POINTER_DEFAULT;
  560.                         } else if (mouse & BROWSER_MOUSE_CLICK_1)
  561.                                 gui_create_form_select_menu(bw, gadget);
  562.                         break;
  563.                 case GADGET_CHECKBOX:
  564.                         status = messages_get("FormCheckbox");
  565.                         if (mouse & BROWSER_MOUSE_CLICK_1) {
  566.                                 gadget->selected = !gadget->selected;
  567.                                 html__redraw_a_box(html, gadget_box);
  568.                         }
  569.                         break;
  570.                 case GADGET_RADIO:
  571.                         status = messages_get("FormRadio");
  572.                         if (mouse & BROWSER_MOUSE_CLICK_1)
  573.                                 form_radio_set(html, gadget);
  574.                         break;
  575.                 case GADGET_IMAGE:
  576.                         if (mouse & BROWSER_MOUSE_CLICK_1) {
  577.                                 gadget->data.image.mx = x - gadget_box_x;
  578.                                 gadget->data.image.my = y - gadget_box_y;
  579.                         }
  580.                         /* drop through */
  581.                 case GADGET_SUBMIT:
  582.                         if (gadget->form) {
  583.                                 snprintf(status_buffer, sizeof status_buffer,
  584.                                                 messages_get("FormSubmit"),
  585.                                                 gadget->form->action);
  586.                                 status = status_buffer;
  587.                                 pointer = get_pointer_shape(gadget_box, false);
  588.                                 if (mouse & (BROWSER_MOUSE_CLICK_1 |
  589.                                                 BROWSER_MOUSE_CLICK_2))
  590.                                         action = ACTION_SUBMIT;
  591.                         } else {
  592.                                 status = messages_get("FormBadSubmit");
  593.                         }
  594.                         break;
  595.                 case GADGET_TEXTAREA:
  596.                         status = messages_get("FormTextarea");
  597.                         pointer = get_pointer_shape(gadget_box, false);
  598.  
  599.                         if (mouse & (BROWSER_MOUSE_PRESS_1 |
  600.                                         BROWSER_MOUSE_PRESS_2)) {
  601.                                 if (text_box && selection_root(&html->sel) !=
  602.                                                 gadget_box)
  603.                                         selection_init(&html->sel, gadget_box);
  604.  
  605.                                 textinput_textarea_click(c, mouse,
  606.                                                 gadget_box,
  607.                                                 gadget_box_x,
  608.                                                 gadget_box_y,
  609.                                                 x - gadget_box_x,
  610.                                                 y - gadget_box_y);
  611.                         }
  612.  
  613.                         if (text_box) {
  614.                                 int pixel_offset;
  615.                                 size_t idx;
  616.  
  617.                                 font_plot_style_from_css(text_box->style,
  618.                                                 &fstyle);
  619.  
  620.                                 nsfont.font_position_in_string(&fstyle,
  621.                                         text_box->text,
  622.                                         text_box->length,
  623.                                         x - gadget_box_x - text_box->x,
  624.                                         &idx,
  625.                                         &pixel_offset);
  626.  
  627.                                 selection_click(&html->sel, mouse,
  628.                                                 text_box->byte_offset + idx);
  629.  
  630.                                 if (selection_dragging(&html->sel)) {
  631.                                         browser_window_set_drag_type(bw,
  632.                                                         DRAGGING_SELECTION,
  633.                                                         NULL);
  634.                                         status = messages_get("Selecting");
  635.                                 }
  636.                         }
  637.                         else if (mouse & BROWSER_MOUSE_PRESS_1)
  638.                                 selection_clear(&html->sel, true);
  639.                         break;
  640.                 case GADGET_TEXTBOX:
  641.                 case GADGET_PASSWORD:
  642.                         status = messages_get("FormTextbox");
  643.                         pointer = get_pointer_shape(gadget_box, false);
  644.  
  645.                         if ((mouse & BROWSER_MOUSE_PRESS_1) &&
  646.                                         !(mouse & (BROWSER_MOUSE_MOD_1 |
  647.                                         BROWSER_MOUSE_MOD_2))) {
  648.                                 textinput_input_click(c,
  649.                                                 gadget_box,
  650.                                                 gadget_box_x,
  651.                                                 gadget_box_y,
  652.                                                 x - gadget_box_x,
  653.                                                 y - gadget_box_y);
  654.                         }
  655.                         if (text_box) {
  656.                                 int pixel_offset;
  657.                                 size_t idx;
  658.  
  659.                                 if (mouse & (BROWSER_MOUSE_DRAG_1 |
  660.                                                 BROWSER_MOUSE_DRAG_2))
  661.                                         selection_init(&html->sel, gadget_box);
  662.  
  663.                                 font_plot_style_from_css(text_box->style,
  664.                                                 &fstyle);
  665.  
  666.                                 nsfont.font_position_in_string(&fstyle,
  667.                                         text_box->text,
  668.                                         text_box->length,
  669.                                         x - gadget_box_x - text_box->x,
  670.                                         &idx,
  671.                                         &pixel_offset);
  672.  
  673.                                 selection_click(&html->sel, mouse,
  674.                                                 text_box->byte_offset + idx);
  675.  
  676.                                 if (selection_dragging(&html->sel))
  677.                                         browser_window_set_drag_type(bw,
  678.                                                         DRAGGING_SELECTION,
  679.                                                         NULL);
  680.                         }
  681.                         else if (mouse & BROWSER_MOUSE_PRESS_1)
  682.                                 selection_clear(&html->sel, true);
  683.                         break;
  684.                 case GADGET_HIDDEN:
  685.                         /* not possible: no box generated */
  686.                         break;
  687.                 case GADGET_RESET:
  688.                         status = messages_get("FormReset");
  689.                         break;
  690.                 case GADGET_FILE:
  691.                         status = messages_get("FormFile");
  692.                         break;
  693.                 case GADGET_BUTTON:
  694.                         /* This gadget cannot be activated */
  695.                         status = messages_get("FormButton");
  696.                         break;
  697.                 }
  698.  
  699.         } else if (object && (mouse & BROWSER_MOUSE_MOD_2)) {
  700.  
  701.                 if (mouse & BROWSER_MOUSE_DRAG_2) {
  702.                         msg_data.dragsave.type = CONTENT_SAVE_NATIVE;
  703.                         msg_data.dragsave.content = object;
  704.                         content_broadcast(c, CONTENT_MSG_DRAGSAVE, msg_data);
  705.  
  706.                 } else if (mouse & BROWSER_MOUSE_DRAG_1) {
  707.                         msg_data.dragsave.type = CONTENT_SAVE_ORIG;
  708.                         msg_data.dragsave.content = object;
  709.                         content_broadcast(c, CONTENT_MSG_DRAGSAVE, msg_data);
  710.                 }
  711.  
  712.                 /* \todo should have a drag-saving object msg */
  713.  
  714.         } else if (iframe) {
  715.                 int pos_x, pos_y;
  716.                 float scale = browser_window_get_scale(bw);
  717.  
  718.                 browser_window_get_position(iframe, false, &pos_x, &pos_y);
  719.  
  720.                 pos_x /= scale;
  721.                 pos_y /= scale;
  722.  
  723.                 if (mouse & BROWSER_MOUSE_CLICK_1 ||
  724.                                 mouse & BROWSER_MOUSE_CLICK_2) {
  725.                         browser_window_mouse_click(iframe, mouse,
  726.                                         x - pos_x, y - pos_y);
  727.                 } else {
  728.                         browser_window_mouse_track(iframe, mouse,
  729.                                         x - pos_x, y - pos_y);
  730.                 }
  731.         } else if (html_object_box) {
  732.                 if (mouse & BROWSER_MOUSE_CLICK_1 ||
  733.                                 mouse & BROWSER_MOUSE_CLICK_2) {
  734.                         content_mouse_action(html_object_box->object,
  735.                                         bw, mouse,
  736.                                         x - html_object_pos_x,
  737.                                         y - html_object_pos_y);
  738.                 } else {
  739.                         content_mouse_track(html_object_box->object,
  740.                                         bw, mouse,
  741.                                         x - html_object_pos_x,
  742.                                         y - html_object_pos_y);
  743.                 }
  744.         } else if (url) {
  745.                 if (title) {
  746.                         snprintf(status_buffer, sizeof status_buffer, "%s: %s",
  747.                                         nsurl_access(url), title);
  748.                         status = status_buffer;
  749.                 } else
  750.                         status = nsurl_access(url);
  751.  
  752.                 pointer = get_pointer_shape(url_box, imagemap);
  753.  
  754.                 if (mouse & BROWSER_MOUSE_CLICK_1 &&
  755.                                 mouse & BROWSER_MOUSE_MOD_1) {
  756.                         /* force download of link */
  757.                         browser_window_go_post(bw, nsurl_access(url), 0, 0,
  758.                                         false,
  759.                                         nsurl_access(content_get_url(c)),
  760.                                         true, true, 0);
  761.  
  762.                 } else if (mouse & BROWSER_MOUSE_CLICK_2 &&
  763.                                 mouse & BROWSER_MOUSE_MOD_1) {
  764.                         msg_data.savelink.url = nsurl_access(url);
  765.                         msg_data.savelink.title = title;
  766.                         content_broadcast(c, CONTENT_MSG_SAVELINK, msg_data);
  767.  
  768.                 } else if (mouse & (BROWSER_MOUSE_CLICK_1 |
  769.                                 BROWSER_MOUSE_CLICK_2))
  770.                         action = ACTION_GO;
  771.         } else {
  772.                 bool done = false;
  773.  
  774.                 /* frame resizing */
  775.                 if (browser_window_frame_resize_start(bw, mouse, x, y,
  776.                                 &pointer)) {
  777.                         if (mouse & (BROWSER_MOUSE_DRAG_1 |
  778.                                         BROWSER_MOUSE_DRAG_2)) {
  779.                                 status = messages_get("FrameDrag");
  780.                         }
  781.                         done = true;
  782.                 }
  783.  
  784.                 /* if clicking in the main page, remove the selection from any
  785.                  * text areas */
  786.                 if (!done) {
  787.                         struct box *layout = html->layout;
  788.  
  789.                         if (mouse && (mouse < BROWSER_MOUSE_MOD_1) &&
  790.                                         selection_root(&html->sel) != layout) {
  791.                                 selection_init(&html->sel, layout);
  792.                         }
  793.  
  794.                         if (text_box) {
  795.                                 int pixel_offset;
  796.                                 size_t idx;
  797.  
  798.                                 font_plot_style_from_css(text_box->style,
  799.                                                 &fstyle);
  800.  
  801.                                 nsfont.font_position_in_string(&fstyle,
  802.                                         text_box->text,
  803.                                         text_box->length,
  804.                                         x - text_box_x,
  805.                                         &idx,
  806.                                         &pixel_offset);
  807.  
  808.                                 if (selection_click(&html->sel, mouse,
  809.                                                 text_box->byte_offset + idx)) {
  810.                                         /* key presses must be directed at the
  811.                                          * main browser window, paste text
  812.                                          * operations ignored */
  813.  
  814.                                         if (selection_dragging(&html->sel)) {
  815.                                                 browser_window_set_drag_type(bw,
  816.                                                         DRAGGING_SELECTION,
  817.                                                         NULL);
  818.                                                 status = messages_get(
  819.                                                                 "Selecting");
  820.                                         }
  821.  
  822.                                         done = true;
  823.                                 }
  824.  
  825.                         } else if (mouse & BROWSER_MOUSE_PRESS_1)
  826.                                 selection_clear(&html->sel, true);
  827.                 }
  828.  
  829.                 if (!done) {
  830.                         if (title)
  831.                                 status = title;
  832.  
  833.                         if (mouse & BROWSER_MOUSE_DRAG_1) {
  834.                                 if (mouse & BROWSER_MOUSE_MOD_2) {
  835.                                         msg_data.dragsave.type =
  836.                                                         CONTENT_SAVE_COMPLETE;
  837.                                         msg_data.dragsave.content = NULL;
  838.                                         content_broadcast(c,
  839.                                                         CONTENT_MSG_DRAGSAVE,
  840.                                                         msg_data);
  841.                                 } else {
  842.                                         if (drag_candidate == NULL) {
  843.                                                 browser_window_page_drag_start(
  844.                                                                 bw, x, y);
  845.                                         } else {
  846.                                                 html_box_drag_start(
  847.                                                                 drag_candidate,
  848.                                                                 x, y);
  849.                                         }
  850.                                         pointer = BROWSER_POINTER_MOVE;
  851.                                 }
  852.                         }
  853.                         else if (mouse & BROWSER_MOUSE_DRAG_2) {
  854.                                 if (mouse & BROWSER_MOUSE_MOD_2) {
  855.                                         msg_data.dragsave.type =
  856.                                                         CONTENT_SAVE_SOURCE;
  857.                                         msg_data.dragsave.content = NULL;
  858.                                         content_broadcast(c,
  859.                                                         CONTENT_MSG_DRAGSAVE,
  860.                                                         msg_data);
  861.                                 } else {
  862.                                         if (drag_candidate == NULL) {
  863.                                                 browser_window_page_drag_start(
  864.                                                                 bw, x, y);
  865.                                         } else {
  866.                                                 html_box_drag_start(
  867.                                                                 drag_candidate,
  868.                                                                 x, y);
  869.                                         }
  870.                                         pointer = BROWSER_POINTER_MOVE;
  871.                                 }
  872.                         }
  873.                 }
  874.                 if (mouse && mouse < BROWSER_MOUSE_MOD_1) {
  875.                         /* ensure key presses still act on the browser window */
  876.                         browser_window_remove_caret(bw);
  877.                 }
  878.         }
  879.  
  880.         if (!iframe && !html_object_box) {
  881.                 msg_data.explicit_status_text = status;
  882.                 content_broadcast(c, CONTENT_MSG_STATUS, msg_data);
  883.  
  884.                 msg_data.pointer = pointer;
  885.                 content_broadcast(c, CONTENT_MSG_POINTER, msg_data);
  886.         }
  887.  
  888.         /* fire dom click event */
  889.         if ((mouse & BROWSER_MOUSE_CLICK_1) ||
  890.             (mouse & BROWSER_MOUSE_CLICK_2)) {
  891.                 js_fire_event(html->jscontext, "click", html->document, node);
  892.         }
  893.  
  894.         /* deferred actions that can cause this browser_window to be destroyed
  895.          * and must therefore be done after set_status/pointer
  896.          */
  897.         switch (action) {
  898.         case ACTION_SUBMIT:
  899.                 form_submit(content_get_url(c),
  900.                                 browser_window_find_target(bw, target, mouse),
  901.                                 gadget->form, gadget);
  902.                 break;
  903.         case ACTION_GO:
  904.                 browser_window_go(browser_window_find_target(bw, target, mouse),
  905.                                 nsurl_access(url),
  906.                                 nsurl_access(content_get_url(c)), true);
  907.                 break;
  908.         case ACTION_NONE:
  909.                 break;
  910.         }
  911. }
  912.  
  913.  
  914. /**
  915.  * Callback for in-page scrollbars.
  916.  */
  917. void html_overflow_scroll_callback(void *client_data,
  918.                 struct scrollbar_msg_data *scrollbar_data)
  919. {
  920.         struct html_scrollbar_data *data = client_data;
  921.         html_content *html = (html_content *)data->c;
  922.         struct box *box = data->box;
  923.         union content_msg_data msg_data;
  924.        
  925.         switch(scrollbar_data->msg) {
  926.                 case SCROLLBAR_MSG_MOVED:
  927.                         html__redraw_a_box(html, box);
  928.                         break;
  929.                 case SCROLLBAR_MSG_SCROLL_START:
  930.                 {
  931.                         struct rect rect = {
  932.                                 .x0 = scrollbar_data->x0,
  933.                                 .y0 = scrollbar_data->y0,
  934.                                 .x1 = scrollbar_data->x1,
  935.                                 .y1 = scrollbar_data->y1
  936.                         };
  937.                         browser_window_set_drag_type(html->bw,
  938.                                         DRAGGING_CONTENT_SCROLLBAR, &rect);
  939.  
  940.                         html->scrollbar = scrollbar_data->scrollbar;
  941.                 }
  942.                         break;
  943.                 case SCROLLBAR_MSG_SCROLL_FINISHED:
  944.                         html->scrollbar = NULL;
  945.  
  946.                         browser_window_set_drag_type(html->bw,
  947.                                         DRAGGING_NONE, NULL);
  948.  
  949.                         msg_data.pointer = BROWSER_POINTER_AUTO;
  950.                         content_broadcast(data->c, CONTENT_MSG_POINTER,
  951.                                         msg_data);
  952.                         break;
  953.         }
  954. }
  955.  
  956.  
  957. /**
  958.  * End overflow scroll scrollbar drags
  959.  *
  960.  * \param  scroll  scrollbar widget
  961.  * \param  mouse   state of mouse buttons and modifier keys
  962.  * \param  x       coordinate of mouse
  963.  * \param  y       coordinate of mouse
  964.  */
  965. void html_overflow_scroll_drag_end(struct scrollbar *scrollbar,
  966.                 browser_mouse_state mouse, int x, int y)
  967. {
  968.         int scroll_mouse_x, scroll_mouse_y, box_x, box_y;
  969.         struct html_scrollbar_data *data = scrollbar_get_data(scrollbar);
  970.         struct box *box;
  971.  
  972.         box = data->box;
  973.         box_coords(box, &box_x, &box_y);
  974.  
  975.         if (scrollbar_is_horizontal(scrollbar)) {
  976.                 scroll_mouse_x = x - box_x;
  977.                 scroll_mouse_y = y - (box_y + box->padding[TOP] +
  978.                                 box->height + box->padding[BOTTOM] -
  979.                                 SCROLLBAR_WIDTH);
  980.                 scrollbar_mouse_drag_end(scrollbar, mouse,
  981.                                 scroll_mouse_x, scroll_mouse_y);
  982.         } else {
  983.                 scroll_mouse_x = x - (box_x + box->padding[LEFT] +
  984.                                 box->width + box->padding[RIGHT] -
  985.                                 SCROLLBAR_WIDTH);
  986.                 scroll_mouse_y = y - box_y;
  987.                 scrollbar_mouse_drag_end(scrollbar, mouse,
  988.                                 scroll_mouse_x, scroll_mouse_y);
  989.         }
  990. }
  991.