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 /**
23 * \file
24 * implementation of user interaction with a CONTENT_HTML.
25 */
26
27 #include <assert.h>
28 #include <stdbool.h>
29
30 #include <dom/dom.h>
31
32 #include "utils/corestrings.h"
33 #include "utils/messages.h"
34 #include "utils/utils.h"
35 #include "utils/log.h"
36 #include "utils/nsoption.h"
37 #include "netsurf/content.h"
38 #include "netsurf/browser_window.h"
39 #include "netsurf/mouse.h"
40 #include "netsurf/misc.h"
41 #include "netsurf/layout.h"
42 #include "netsurf/keypress.h"
43 #include "content/hlcache.h"
44 #include "content/textsearch.h"
45 #include "desktop/frames.h"
46 #include "desktop/scrollbar.h"
47 #include "desktop/selection.h"
48 #include "desktop/textarea.h"
49 #include "javascript/js.h"
50 #include "desktop/gui_internal.h"
51
52 #include "html/box.h"
53 #include "html/box_textarea.h"
54 #include "html/box_inspect.h"
55 #include "html/font.h"
56 #include "html/form_internal.h"
57 #include "html/private.h"
58 #include "html/imagemap.h"
59 #include "html/interaction.h"
60
61 /**
62 * Get pointer shape for given box
63 *
64 * \param box box in question
65 * \param imagemap whether an imagemap applies to the box
66 */
67
get_pointer_shape(struct box * box,bool imagemap)68 static browser_pointer_shape get_pointer_shape(struct box *box, bool imagemap)
69 {
70 browser_pointer_shape pointer;
71 css_computed_style *style;
72 enum css_cursor_e cursor;
73 lwc_string **cursor_uris;
74
75 if (box->type == BOX_FLOAT_LEFT || box->type == BOX_FLOAT_RIGHT)
76 style = box->children->style;
77 else
78 style = box->style;
79
80 if (style == NULL)
81 return BROWSER_POINTER_DEFAULT;
82
83 cursor = css_computed_cursor(style, &cursor_uris);
84
85 switch (cursor) {
86 case CSS_CURSOR_AUTO:
87 if (box->href || (box->gadget &&
88 (box->gadget->type == GADGET_IMAGE ||
89 box->gadget->type == GADGET_SUBMIT)) ||
90 imagemap) {
91 /* link */
92 pointer = BROWSER_POINTER_POINT;
93 } else if (box->gadget &&
94 (box->gadget->type == GADGET_TEXTBOX ||
95 box->gadget->type == GADGET_PASSWORD ||
96 box->gadget->type == GADGET_TEXTAREA)) {
97 /* text input */
98 pointer = BROWSER_POINTER_CARET;
99 } else {
100 /* html content doesn't mind */
101 pointer = BROWSER_POINTER_AUTO;
102 }
103 break;
104 case CSS_CURSOR_CROSSHAIR:
105 pointer = BROWSER_POINTER_CROSS;
106 break;
107 case CSS_CURSOR_POINTER:
108 pointer = BROWSER_POINTER_POINT;
109 break;
110 case CSS_CURSOR_MOVE:
111 pointer = BROWSER_POINTER_MOVE;
112 break;
113 case CSS_CURSOR_E_RESIZE:
114 pointer = BROWSER_POINTER_RIGHT;
115 break;
116 case CSS_CURSOR_W_RESIZE:
117 pointer = BROWSER_POINTER_LEFT;
118 break;
119 case CSS_CURSOR_N_RESIZE:
120 pointer = BROWSER_POINTER_UP;
121 break;
122 case CSS_CURSOR_S_RESIZE:
123 pointer = BROWSER_POINTER_DOWN;
124 break;
125 case CSS_CURSOR_NE_RESIZE:
126 pointer = BROWSER_POINTER_RU;
127 break;
128 case CSS_CURSOR_SW_RESIZE:
129 pointer = BROWSER_POINTER_LD;
130 break;
131 case CSS_CURSOR_SE_RESIZE:
132 pointer = BROWSER_POINTER_RD;
133 break;
134 case CSS_CURSOR_NW_RESIZE:
135 pointer = BROWSER_POINTER_LU;
136 break;
137 case CSS_CURSOR_TEXT:
138 pointer = BROWSER_POINTER_CARET;
139 break;
140 case CSS_CURSOR_WAIT:
141 pointer = BROWSER_POINTER_WAIT;
142 break;
143 case CSS_CURSOR_PROGRESS:
144 pointer = BROWSER_POINTER_PROGRESS;
145 break;
146 case CSS_CURSOR_HELP:
147 pointer = BROWSER_POINTER_HELP;
148 break;
149 default:
150 pointer = BROWSER_POINTER_DEFAULT;
151 break;
152 }
153
154 return pointer;
155 }
156
157
158 /**
159 * Start drag scrolling the contents of a box
160 *
161 * \param box the box to be scrolled
162 * \param x x ordinate of initial mouse position
163 * \param y y ordinate
164 */
165
html_box_drag_start(struct box * box,int x,int y)166 static void html_box_drag_start(struct box *box, int x, int y)
167 {
168 int box_x, box_y;
169 int scroll_mouse_x, scroll_mouse_y;
170
171 box_coords(box, &box_x, &box_y);
172
173 if (box->scroll_x != NULL) {
174 scroll_mouse_x = x - box_x ;
175 scroll_mouse_y = y - (box_y + box->padding[TOP] +
176 box->height + box->padding[BOTTOM] -
177 SCROLLBAR_WIDTH);
178 scrollbar_start_content_drag(box->scroll_x,
179 scroll_mouse_x, scroll_mouse_y);
180 } else if (box->scroll_y != NULL) {
181 scroll_mouse_x = x - (box_x + box->padding[LEFT] +
182 box->width + box->padding[RIGHT] -
183 SCROLLBAR_WIDTH);
184 scroll_mouse_y = y - box_y;
185
186 scrollbar_start_content_drag(box->scroll_y,
187 scroll_mouse_x, scroll_mouse_y);
188 }
189 }
190
191
192 /**
193 * End overflow scroll scrollbar drags
194 *
195 * \param html html content
196 * \param mouse state of mouse buttons and modifier keys
197 * \param x coordinate of mouse
198 * \param y coordinate of mouse
199 * \param dir Direction of drag
200 */
html_selection_drag_end(struct html_content * html,browser_mouse_state mouse,int x,int y,int dir)201 static size_t html_selection_drag_end(struct html_content *html,
202 browser_mouse_state mouse, int x, int y, int dir)
203 {
204 int pixel_offset;
205 struct box *box;
206 int dx, dy;
207 size_t idx = 0;
208
209 box = box_pick_text_box(html, x, y, dir, &dx, &dy);
210 if (box) {
211 plot_font_style_t fstyle;
212
213 font_plot_style_from_css(&html->len_ctx, box->style, &fstyle);
214
215 guit->layout->position(&fstyle, box->text, box->length,
216 dx, &idx, &pixel_offset);
217
218 idx += box->byte_offset;
219 }
220
221 return idx;
222 }
223
224
225 /**
226 * Helper for file gadgets to store their filename.
227 *
228 * Stores the filename unencoded on the dom node associated with the
229 * gadget.
230 *
231 * \todo Get rid of this crap eventually
232 *
233 * \param operation DOM operation
234 * \param key DOM node key being considerd
235 * \param _data The data assocated with the key
236 * \param src The source DOM node.
237 * \param dst The destination DOM node.
238 */
239 static void
html__image_coords_dom_user_data_handler(dom_node_operation operation,dom_string * key,void * _data,struct dom_node * src,struct dom_node * dst)240 html__image_coords_dom_user_data_handler(dom_node_operation operation,
241 dom_string *key,
242 void *_data,
243 struct dom_node *src,
244 struct dom_node *dst)
245 {
246 struct image_input_coords *oldcoords, *coords = _data, *newcoords;
247
248 if (!dom_string_isequal(corestring_dom___ns_key_image_coords_node_data,
249 key) || coords == NULL) {
250 return;
251 }
252
253 switch (operation) {
254 case DOM_NODE_CLONED:
255 newcoords = calloc(1, sizeof(*newcoords));
256 if (newcoords != NULL) {
257 *newcoords = *coords;
258 if (dom_node_set_user_data(dst,
259 corestring_dom___ns_key_image_coords_node_data,
260 newcoords,
261 html__image_coords_dom_user_data_handler,
262 &oldcoords) == DOM_NO_ERR) {
263 free(oldcoords);
264 }
265 }
266 break;
267
268 case DOM_NODE_DELETED:
269 free(coords);
270 break;
271
272 case DOM_NODE_RENAMED:
273 case DOM_NODE_IMPORTED:
274 case DOM_NODE_ADOPTED:
275 break;
276
277 default:
278 NSLOG(netsurf, INFO, "User data operation not handled.");
279 assert(0);
280 }
281 }
282
283
284 /**
285 * End overflow scroll scrollbar drags
286 *
287 * \param scrollbar scrollbar widget
288 * \param mouse state of mouse buttons and modifier keys
289 * \param x coordinate of mouse
290 * \param y coordinate of mouse
291 */
292 static void
html_overflow_scroll_drag_end(struct scrollbar * scrollbar,browser_mouse_state mouse,int x,int y)293 html_overflow_scroll_drag_end(struct scrollbar *scrollbar,
294 browser_mouse_state mouse,
295 int x, int y)
296 {
297 int scroll_mouse_x, scroll_mouse_y, box_x, box_y;
298 struct html_scrollbar_data *data = scrollbar_get_data(scrollbar);
299 struct box *box;
300
301 box = data->box;
302 box_coords(box, &box_x, &box_y);
303
304 if (scrollbar_is_horizontal(scrollbar)) {
305 scroll_mouse_x = x - box_x;
306 scroll_mouse_y = y - (box_y + box->padding[TOP] +
307 box->height + box->padding[BOTTOM] -
308 SCROLLBAR_WIDTH);
309 scrollbar_mouse_drag_end(scrollbar, mouse,
310 scroll_mouse_x, scroll_mouse_y);
311 } else {
312 scroll_mouse_x = x - (box_x + box->padding[LEFT] +
313 box->width + box->padding[RIGHT] -
314 SCROLLBAR_WIDTH);
315 scroll_mouse_y = y - box_y;
316 scrollbar_mouse_drag_end(scrollbar, mouse,
317 scroll_mouse_x, scroll_mouse_y);
318 }
319 }
320
321
322 /**
323 * handle html mouse action when select menu is open
324 *
325 */
326 static nserror
mouse_action_select_menu(html_content * html,struct browser_window * bw,browser_mouse_state mouse,int x,int y)327 mouse_action_select_menu(html_content *html,
328 struct browser_window *bw,
329 browser_mouse_state mouse,
330 int x, int y)
331 {
332 struct box *box;
333 int box_x = 0;
334 int box_y = 0;
335 const char *status;
336 int width, height;
337 struct hlcache_handle *bw_content;
338 browser_drag_type bw_drag_type;
339
340 assert(html->visible_select_menu != NULL);
341
342 bw_drag_type = browser_window_get_drag_type(bw);
343 if (bw_drag_type != DRAGGING_NONE && !mouse) {
344 /* drag end: select menu */
345 form_select_mouse_drag_end(html->visible_select_menu, mouse, x, y);
346 }
347
348 box = html->visible_select_menu->box;
349 box_coords(box, &box_x, &box_y);
350
351 box_x -= box->border[LEFT].width;
352 box_y += box->height + box->border[BOTTOM].width +
353 box->padding[BOTTOM] + box->padding[TOP];
354
355 status = form_select_mouse_action(html->visible_select_menu,
356 mouse,
357 x - box_x,
358 y - box_y);
359 if (status != NULL) {
360 /* set status if menu still open */
361 union content_msg_data msg_data;
362 msg_data.explicit_status_text = status;
363 content_broadcast((struct content *)html,
364 CONTENT_MSG_STATUS,
365 &msg_data);
366 return NSERROR_OK;
367 }
368
369 /* close menu and redraw where it was */
370 form_select_get_dimensions(html->visible_select_menu, &width, &height);
371
372 html->visible_select_menu = NULL;
373
374 bw_content = browser_window_get_content(bw);
375 content_request_redraw(bw_content,
376 box_x,
377 box_y,
378 width,
379 height);
380 return NSERROR_OK;
381 }
382
383
384 /**
385 * handle html mouse action when a selection drag is being performed
386 *
387 */
388 static nserror
mouse_action_drag_selection(html_content * html,struct browser_window * bw,browser_mouse_state mouse,int x,int y)389 mouse_action_drag_selection(html_content *html,
390 struct browser_window *bw,
391 browser_mouse_state mouse,
392 int x, int y)
393 {
394 struct box *box;
395 int dir = -1;
396 int dx, dy;
397 size_t idx;
398 union html_drag_owner drag_owner;
399 int pixel_offset;
400 plot_font_style_t fstyle;
401
402 if (!mouse) {
403 /* End of selection drag */
404 if (selection_dragging_start(html->sel)) {
405 dir = 1;
406 }
407
408 idx = html_selection_drag_end(html, mouse, x, y, dir);
409
410 if (idx != 0) {
411 selection_track(html->sel, mouse, idx);
412 }
413
414 drag_owner.no_owner = true;
415 html_set_drag_type(html, HTML_DRAG_NONE, drag_owner, NULL);
416
417 return NSERROR_OK;
418 }
419
420 if (selection_dragging_start(html->sel)) {
421 dir = 1;
422 }
423
424 box = box_pick_text_box(html, x, y, dir, &dx, &dy);
425 if (box != NULL) {
426 font_plot_style_from_css(&html->len_ctx, box->style, &fstyle);
427
428 guit->layout->position(&fstyle,
429 box->text,
430 box->length,
431 dx,
432 &idx,
433 &pixel_offset);
434
435 selection_track(html->sel, mouse, box->byte_offset + idx);
436 }
437 return NSERROR_OK;
438 }
439
440
441 /**
442 * handle html mouse action when a scrollbar drag is being performed
443 *
444 */
445 static nserror
mouse_action_drag_scrollbar(html_content * html,struct browser_window * bw,browser_mouse_state mouse,int x,int y)446 mouse_action_drag_scrollbar(html_content *html,
447 struct browser_window *bw,
448 browser_mouse_state mouse,
449 int x, int y)
450 {
451 struct scrollbar *scr;
452 struct html_scrollbar_data *data;
453 struct box *box;
454 int box_x = 0;
455 int box_y = 0;
456 const char *status;
457 int scroll_mouse_x = 0, scroll_mouse_y = 0;
458 scrollbar_mouse_status scrollbar_status;
459
460 scr = html->drag_owner.scrollbar;
461
462 if (!mouse) {
463 /* drag end: scrollbar */
464 html_overflow_scroll_drag_end(scr, mouse, x, y);
465 }
466
467 data = scrollbar_get_data(scr);
468
469 box = data->box;
470
471 box_coords(box, &box_x, &box_y);
472
473 if (scrollbar_is_horizontal(scr)) {
474 scroll_mouse_x = x - box_x ;
475 scroll_mouse_y = y - (box_y + box->padding[TOP] +
476 box->height + box->padding[BOTTOM] -
477 SCROLLBAR_WIDTH);
478 scrollbar_status = scrollbar_mouse_action(scr,
479 mouse,
480 scroll_mouse_x,
481 scroll_mouse_y);
482 } else {
483 scroll_mouse_x = x - (box_x + box->padding[LEFT] +
484 box->width + box->padding[RIGHT] -
485 SCROLLBAR_WIDTH);
486 scroll_mouse_y = y - box_y;
487
488 scrollbar_status = scrollbar_mouse_action(scr,
489 mouse,
490 scroll_mouse_x,
491 scroll_mouse_y);
492 }
493 status = scrollbar_mouse_status_to_message(scrollbar_status);
494
495 if (status != NULL) {
496 union content_msg_data msg_data;
497
498 msg_data.explicit_status_text = status;
499 content_broadcast((struct content *)html,
500 CONTENT_MSG_STATUS,
501 &msg_data);
502 }
503
504 return NSERROR_OK;
505 }
506
507
508 /**
509 * handle mouse actions while dragging in a text area
510 */
511 static nserror
mouse_action_drag_textarea(html_content * html,struct browser_window * bw,browser_mouse_state mouse,int x,int y)512 mouse_action_drag_textarea(html_content *html,
513 struct browser_window *bw,
514 browser_mouse_state mouse,
515 int x, int y)
516 {
517 struct box *box;
518 int box_x = 0;
519 int box_y = 0;
520
521 box = html->drag_owner.textarea;
522
523 assert(box->gadget != NULL);
524 assert(box->gadget->type == GADGET_TEXTAREA ||
525 box->gadget->type == GADGET_PASSWORD ||
526 box->gadget->type == GADGET_TEXTBOX);
527
528 box_coords(box, &box_x, &box_y);
529 textarea_mouse_action(box->gadget->data.text.ta,
530 mouse,
531 x - box_x,
532 y - box_y);
533
534 /* TODO: Set appropriate statusbar message */
535 return NSERROR_OK;
536 }
537
538
539 /**
540 * handle mouse actions while dragging in a content
541 */
542 static nserror
mouse_action_drag_content(html_content * html,struct browser_window * bw,browser_mouse_state mouse,int x,int y)543 mouse_action_drag_content(html_content *html,
544 struct browser_window *bw,
545 browser_mouse_state mouse,
546 int x, int y)
547 {
548 struct box *box;
549 int box_x = 0;
550 int box_y = 0;
551
552 box = html->drag_owner.content;
553 assert(box->object != NULL);
554
555 box_coords(box, &box_x, &box_y);
556 content_mouse_track(box->object,
557 bw, mouse,
558 x - box_x,
559 y - box_y);
560 return NSERROR_OK;
561 }
562
563
564 /**
565 * local structure containing all the mouse action state information
566 */
567 struct mouse_action_state {
568 struct {
569 const char *status; /**< status text */
570 browser_pointer_shape pointer; /**< pointer shape */
571 enum {
572 ACTION_NONE,
573 ACTION_NOSEND, /**< do not send status and pointer message */
574 ACTION_SUBMIT,
575 ACTION_GO,
576 ACTION_JS,
577 } action;
578 } result;
579
580 /** dom node */
581 struct dom_node *node;
582
583 /** html object */
584 struct {
585 struct box *box;
586 int pos_x;
587 int pos_y;
588 } html_object;
589
590 /** non html object */
591 hlcache_handle *object;
592
593 /** iframe */
594 struct browser_window *iframe;
595
596 /** link either from href or imagemap */
597 struct {
598 struct box *box;
599 nsurl *url;
600 const char *target;
601 bool is_imagemap;
602 } link;
603
604 /** gadget */
605 struct {
606 struct form_control *control;
607 struct box *box;
608 int box_x;
609 int box_y;
610 const char *target;
611 } gadget;
612
613 /** title */
614 const char *title;
615
616 /** candidate box for drag operation */
617 struct box *drag_candidate;
618
619 /** scrollbar */
620 struct {
621 struct scrollbar *bar;
622 int mouse_x;
623 int mouse_y;
624 } scroll;
625
626 /** text in box */
627 struct {
628 struct box *box;
629 int box_x;
630 } text;
631 };
632
633
634 /**
635 * iterate the box tree for deepest node at coordinates
636 *
637 * extracts mouse action node information by descending through
638 * visible boxes setting more specific values for:
639 *
640 * box - deepest box at point
641 * html_object_box - html object
642 * html_object_pos_x - html object
643 * html_object_pos_y - html object
644 * object - non html object
645 * iframe - iframe
646 * url - href or imagemap
647 * target - href or imagemap or gadget
648 * url_box - href or imagemap
649 * imagemap - imagemap
650 * gadget - gadget
651 * gadget_box - gadget
652 * gadget_box_x - gadget
653 * gadget_box_y - gadget
654 * title - title
655 * pointer
656 *
657 * drag_candidate - first box with scroll
658 * padding_left - box with scroll
659 * padding_right
660 * padding_top
661 * padding_bottom
662 * scrollbar - inside padding box stops decent
663 * scroll_mouse_x - inside padding box stops decent
664 * scroll_mouse_y - inside padding box stops decent
665 *
666 * text_box - text box
667 * text_box_x - text_box
668 */
669 static nserror
get_mouse_action_node(html_content * html,int x,int y,struct mouse_action_state * man)670 get_mouse_action_node(html_content *html,
671 int x, int y,
672 struct mouse_action_state *man)
673 {
674 struct box *box;
675 int box_x = 0;
676 int box_y = 0;
677
678 /* initialise the mouse action state data */
679 memset(man, 0, sizeof(struct mouse_action_state));
680 man->node = html->layout->node; /* Default dom node to the <HTML> */
681 man->result.pointer = BROWSER_POINTER_DEFAULT;
682
683 /* search the box tree for a link, imagemap, form control, or
684 * box with scrollbars
685 */
686 box = html->layout;
687
688 /* Consider the margins of the html page now */
689 box_x = box->margin[LEFT];
690 box_y = box->margin[TOP];
691
692 do {
693 /* skip hidden boxes */
694 if ((box->style != NULL) &&
695 (css_computed_visibility(box->style) ==
696 CSS_VISIBILITY_HIDDEN)) {
697 goto next_box;
698 }
699
700 if (box->node != NULL) {
701 man->node = box->node;
702 }
703
704 if (box->object) {
705 if (content_get_type(box->object) == CONTENT_HTML) {
706 man->html_object.box = box;
707 man->html_object.pos_x = box_x;
708 man->html_object.pos_y = box_y;
709 } else {
710 man->object = box->object;
711 }
712 }
713
714 if (box->iframe) {
715 man->iframe = box->iframe;
716 }
717
718 if (box->href) {
719 man->link.url = box->href;
720 man->link.target = box->target;
721 man->link.box = box;
722 man->link.is_imagemap = false;
723 }
724
725 if (box->usemap) {
726 man->link.url = imagemap_get(html,
727 box->usemap,
728 box_x,
729 box_y,
730 x, y,
731 &man->link.target);
732 man->link.box = box;
733 man->link.is_imagemap = true;
734 }
735
736 if (box->gadget) {
737 man->gadget.control = box->gadget;
738 man->gadget.box = box;
739 man->gadget.box_x = box_x;
740 man->gadget.box_y = box_y;
741 if (box->gadget->form) {
742 man->gadget.target = box->gadget->form->target;
743 }
744 }
745
746 if (box->title) {
747 man->title = box->title;
748 }
749
750 man->result.pointer = get_pointer_shape(box, false);
751
752 if ((box->scroll_x != NULL) ||
753 (box->scroll_y != NULL)) {
754 int padding_left;
755 int padding_right;
756 int padding_top;
757 int padding_bottom;
758
759 if (man->drag_candidate == NULL) {
760 man->drag_candidate = box;
761 }
762
763 padding_left = box_x +
764 scrollbar_get_offset(box->scroll_x);
765 padding_right = padding_left + box->padding[LEFT] +
766 box->width + box->padding[RIGHT];
767 padding_top = box_y +
768 scrollbar_get_offset(box->scroll_y);
769 padding_bottom = padding_top + box->padding[TOP] +
770 box->height + box->padding[BOTTOM];
771
772 if ((x > padding_left) &&
773 (x < padding_right) &&
774 (y > padding_top) &&
775 (y < padding_bottom)) {
776 /* mouse inside padding box */
777
778 if ((box->scroll_y != NULL) &&
779 (x > (padding_right - SCROLLBAR_WIDTH))) {
780 /* mouse above vertical box scroll */
781
782 man->scroll.bar = box->scroll_y;
783 man->scroll.mouse_x = x - (padding_right - SCROLLBAR_WIDTH);
784 man->scroll.mouse_y = y - padding_top;
785 break;
786
787 } else if ((box->scroll_x != NULL) &&
788 (y > (padding_bottom -
789 SCROLLBAR_WIDTH))) {
790 /* mouse above horizontal box scroll */
791
792 man->scroll.bar = box->scroll_x;
793 man->scroll.mouse_x = x - padding_left;
794 man->scroll.mouse_y = y - (padding_bottom - SCROLLBAR_WIDTH);
795 break;
796 }
797 }
798 }
799
800 if (box->text && !box->object) {
801 man->text.box = box;
802 man->text.box_x = box_x;
803 }
804
805 next_box:
806 /* iterate to next box */
807 box = box_at_point(&html->len_ctx, box, x, y, &box_x, &box_y);
808 } while (box != NULL);
809
810 /* use of box_x, box_y, or content below this point is probably a
811 * mistake; they will refer to the last box returned by box_at_point */
812
813 assert(man->node != NULL);
814
815 return NSERROR_OK;
816 }
817
818
819 /**
820 * process mouse activity on a form gadget
821 */
822 static nserror
gadget_mouse_action(html_content * html,browser_mouse_state mouse,int x,int y,struct mouse_action_state * mas)823 gadget_mouse_action(html_content *html,
824 browser_mouse_state mouse,
825 int x, int y,
826 struct mouse_action_state *mas)
827 {
828 struct content *c = (struct content *)html;
829 textarea_mouse_status ta_status;
830 union content_msg_data msg_data;
831 nserror res;
832 bool click;
833 click = mouse & (BROWSER_MOUSE_PRESS_1 | BROWSER_MOUSE_PRESS_2 |
834 BROWSER_MOUSE_CLICK_1 | BROWSER_MOUSE_CLICK_2 |
835 BROWSER_MOUSE_DRAG_1 | BROWSER_MOUSE_DRAG_2);
836
837 switch (mas->gadget.control->type) {
838 case GADGET_SELECT:
839 mas->result.status = messages_get("FormSelect");
840 mas->result.pointer = BROWSER_POINTER_MENU;
841 if (mouse & BROWSER_MOUSE_CLICK_1 &&
842 nsoption_bool(core_select_menu)) {
843 html->visible_select_menu = mas->gadget.control;
844 res = form_open_select_menu(c,
845 mas->gadget.control,
846 form_select_menu_callback,
847 c);
848 if (res != NSERROR_OK) {
849 NSLOG(netsurf, ERROR, "%s",
850 messages_get_errorcode(res));
851 html->visible_select_menu = NULL;
852 }
853 mas->result.pointer = BROWSER_POINTER_DEFAULT;
854 } else if (mouse & BROWSER_MOUSE_CLICK_1) {
855 msg_data.select_menu.gadget = mas->gadget.control;
856 content_broadcast(c,
857 CONTENT_MSG_SELECTMENU,
858 &msg_data);
859 }
860 break;
861
862 case GADGET_CHECKBOX:
863 mas->result.status = messages_get("FormCheckbox");
864 if (mouse & BROWSER_MOUSE_CLICK_1) {
865 mas->gadget.control->selected = !mas->gadget.control->selected;
866 dom_html_input_element_set_checked(
867 (dom_html_input_element *)(mas->gadget.control->node),
868 mas->gadget.control->selected);
869 html__redraw_a_box(html, mas->gadget.box);
870 }
871 break;
872
873 case GADGET_RADIO:
874 mas->result.status = messages_get("FormRadio");
875 if (mouse & BROWSER_MOUSE_CLICK_1) {
876 form_radio_set(mas->gadget.control);
877 }
878 break;
879
880 case GADGET_IMAGE:
881 /* This falls through to SUBMIT */
882 if (mouse & BROWSER_MOUSE_CLICK_1) {
883 struct image_input_coords *coords, *oldcoords;
884 /** \todo Find a way to not ignore errors */
885 coords = calloc(1, sizeof(*coords));
886 if (coords == NULL) {
887 return NSERROR_OK;
888 }
889 coords->x = x - mas->gadget.box_x;
890 coords->y = y - mas->gadget.box_y;
891 if (dom_node_set_user_data(
892 mas->gadget.control->node,
893 corestring_dom___ns_key_image_coords_node_data,
894 coords,
895 html__image_coords_dom_user_data_handler,
896 &oldcoords) != DOM_NO_ERR) {
897 return NSERROR_OK;
898 }
899 free(oldcoords);
900 }
901 /* Fall through */
902
903 case GADGET_SUBMIT:
904 if (mas->gadget.control->form) {
905 static char status_buffer[200];
906
907 snprintf(status_buffer,
908 sizeof status_buffer,
909 messages_get("FormSubmit"),
910 mas->gadget.control->form->action);
911 mas->result.status = status_buffer;
912 mas->result.pointer = get_pointer_shape(mas->gadget.box,
913 false);
914 if (mouse & (BROWSER_MOUSE_CLICK_1 |
915 BROWSER_MOUSE_CLICK_2)) {
916 mas->result.action = ACTION_SUBMIT;
917 }
918 } else {
919 mas->result.status = messages_get("FormBadSubmit");
920 }
921 break;
922
923 case GADGET_TEXTBOX:
924 case GADGET_PASSWORD:
925 case GADGET_TEXTAREA:
926 if (mas->gadget.control->type == GADGET_TEXTAREA) {
927 mas->result.status = messages_get("FormTextarea");
928 } else {
929 mas->result.status = messages_get("FormTextbox");
930 }
931
932 if (click &&
933 (html->selection_type != HTML_SELECTION_TEXTAREA ||
934 html->selection_owner.textarea != mas->gadget.box)) {
935 union html_selection_owner sel_owner;
936 sel_owner.none = true;
937 html_set_selection(html,
938 HTML_SELECTION_NONE,
939 sel_owner,
940 true);
941 }
942
943 ta_status = textarea_mouse_action(mas->gadget.control->data.text.ta,
944 mouse,
945 x - mas->gadget.box_x,
946 y - mas->gadget.box_y);
947
948 if (ta_status & TEXTAREA_MOUSE_EDITOR) {
949 mas->result.pointer = get_pointer_shape(mas->gadget.box, false);
950 } else {
951 mas->result.pointer = BROWSER_POINTER_DEFAULT;
952 mas->result.status = scrollbar_mouse_status_to_message(ta_status >> 3);
953 }
954 break;
955
956 case GADGET_HIDDEN:
957 /* not possible: no box generated */
958 break;
959
960 case GADGET_RESET:
961 mas->result.status = messages_get("FormReset");
962 break;
963
964 case GADGET_FILE:
965 mas->result.status = messages_get("FormFile");
966 if (mouse & BROWSER_MOUSE_CLICK_1) {
967 msg_data.gadget_click.gadget = mas->gadget.control;
968 content_broadcast(c,
969 CONTENT_MSG_GADGETCLICK,
970 &msg_data);
971 }
972 break;
973
974 case GADGET_BUTTON:
975 /* This gadget cannot be activated */
976 mas->result.status = messages_get("FormButton");
977 break;
978 }
979
980 return NSERROR_OK;
981 }
982
983
984 /**
985 * process mouse activity on an iframe
986 */
987 static nserror
iframe_mouse_action(struct browser_window * bw,browser_mouse_state mouse,int x,int y,struct mouse_action_state * mas)988 iframe_mouse_action(struct browser_window *bw,
989 browser_mouse_state mouse,
990 int x, int y,
991 struct mouse_action_state *mas)
992 {
993 int pos_x, pos_y;
994 float scale;
995
996 scale = browser_window_get_scale(bw);
997
998 browser_window_get_position(mas->iframe, false, &pos_x, &pos_y);
999
1000 if (mouse & BROWSER_MOUSE_CLICK_1 ||
1001 mouse & BROWSER_MOUSE_CLICK_2) {
1002 browser_window_mouse_click(mas->iframe,
1003 mouse,
1004 (x * scale) - pos_x,
1005 (y * scale) - pos_y);
1006 } else {
1007 browser_window_mouse_track(mas->iframe,
1008 mouse,
1009 (x * scale) - pos_x,
1010 (y * scale) - pos_y);
1011 }
1012 mas->result.action = ACTION_NOSEND;
1013
1014 return NSERROR_OK;
1015 }
1016
1017
1018 /**
1019 * process mouse activity on an html object
1020 */
1021 static nserror
html_object_mouse_action(html_content * html,struct browser_window * bw,browser_mouse_state mouse,int x,int y,struct mouse_action_state * mas)1022 html_object_mouse_action(html_content *html,
1023 struct browser_window *bw,
1024 browser_mouse_state mouse,
1025 int x, int y,
1026 struct mouse_action_state *mas)
1027 {
1028 bool click;
1029 click = mouse & (BROWSER_MOUSE_PRESS_1 | BROWSER_MOUSE_PRESS_2 |
1030 BROWSER_MOUSE_CLICK_1 | BROWSER_MOUSE_CLICK_2 |
1031 BROWSER_MOUSE_DRAG_1 | BROWSER_MOUSE_DRAG_2);
1032
1033 if (click &&
1034 (html->selection_type != HTML_SELECTION_CONTENT ||
1035 html->selection_owner.content != mas->html_object.box)) {
1036 union html_selection_owner sel_owner;
1037 sel_owner.none = true;
1038 html_set_selection(html, HTML_SELECTION_NONE, sel_owner, true);
1039 }
1040
1041 if (mouse & BROWSER_MOUSE_CLICK_1 ||
1042 mouse & BROWSER_MOUSE_CLICK_2) {
1043 content_mouse_action(mas->html_object.box->object,
1044 bw,
1045 mouse,
1046 x - mas->html_object.pos_x,
1047 y - mas->html_object.pos_y);
1048 } else {
1049 content_mouse_track(mas->html_object.box->object,
1050 bw,
1051 mouse,
1052 x - mas->html_object.pos_x,
1053 y - mas->html_object.pos_y);
1054 }
1055
1056 mas->result.action = ACTION_NOSEND;
1057 return NSERROR_OK;
1058 }
1059
1060
1061 /**
1062 * determine if a url has a javascript scheme
1063 *
1064 * \param urm The url to check.
1065 * \return true if the url is a javascript scheme else false
1066 */
is_javascript_navigate_url(nsurl * url)1067 static bool is_javascript_navigate_url(nsurl *url)
1068 {
1069 bool is_js = false;
1070 lwc_string *scheme;
1071
1072 scheme = nsurl_get_component(url, NSURL_SCHEME);
1073 if (scheme != NULL) {
1074 if (scheme == corestring_lwc_javascript) {
1075 is_js = true;
1076 }
1077 lwc_string_unref(scheme);
1078 }
1079 return is_js;
1080 }
1081
1082
1083 /**
1084 * process mouse activity on a link
1085 */
1086 static nserror
link_mouse_action(html_content * html,struct browser_window * bw,browser_mouse_state mouse,int x,int y,struct mouse_action_state * mas)1087 link_mouse_action(html_content *html,
1088 struct browser_window *bw,
1089 browser_mouse_state mouse,
1090 int x, int y,
1091 struct mouse_action_state *mas)
1092 {
1093 nserror res;
1094 char *url_s = NULL;
1095 size_t url_l = 0;
1096 static char status_buffer[200];
1097 union content_msg_data msg_data;
1098
1099 if (nsoption_bool(display_decoded_idn) == true) {
1100 res = nsurl_get_utf8(mas->link.url, &url_s, &url_l);
1101 if (res != NSERROR_OK) {
1102 /* Unable to obtain a decoded IDN. This is not
1103 * a fatal error. Ensure the string pointer
1104 * is NULL so we use the encoded version.
1105 */
1106 url_s = NULL;
1107 }
1108 }
1109
1110 if (mas->title) {
1111 snprintf(status_buffer,
1112 sizeof status_buffer,
1113 "%s: %s",
1114 url_s ? url_s : nsurl_access(mas->link.url),
1115 mas->title);
1116 } else {
1117 snprintf(status_buffer,
1118 sizeof status_buffer,
1119 "%s",
1120 url_s ? url_s : nsurl_access(mas->link.url));
1121 }
1122
1123 if (url_s != NULL) {
1124 free(url_s);
1125 }
1126
1127 mas->result.status = status_buffer;
1128
1129 mas->result.pointer = get_pointer_shape(mas->link.box,
1130 mas->link.is_imagemap);
1131
1132 if (mouse & BROWSER_MOUSE_CLICK_1 &&
1133 mouse & BROWSER_MOUSE_MOD_1) {
1134 /* force download of link */
1135 browser_window_navigate(bw,
1136 mas->link.url,
1137 content_get_url((struct content *)html),
1138 BW_NAVIGATE_DOWNLOAD,
1139 NULL,
1140 NULL,
1141 NULL);
1142
1143 } else if (mouse & BROWSER_MOUSE_CLICK_2 &&
1144 mouse & BROWSER_MOUSE_MOD_1) {
1145 msg_data.savelink.url = mas->link.url;
1146 msg_data.savelink.title = mas->title;
1147 content_broadcast((struct content *)html,
1148 CONTENT_MSG_SAVELINK,
1149 &msg_data);
1150
1151 } else if (mouse & (BROWSER_MOUSE_CLICK_1 | BROWSER_MOUSE_CLICK_2)) {
1152 if (is_javascript_navigate_url(mas->link.url)) {
1153 mas->result.action = ACTION_JS;
1154 } else {
1155 mas->result.action = ACTION_GO;
1156 }
1157 }
1158
1159 return NSERROR_OK;
1160 }
1161
1162
1163
1164 /**
1165 * process mouse activity if it is not anything else
1166 */
1167 static nserror
default_mouse_action(html_content * html,struct browser_window * bw,browser_mouse_state mouse,int x,int y,struct mouse_action_state * mas)1168 default_mouse_action(html_content *html,
1169 struct browser_window *bw,
1170 browser_mouse_state mouse,
1171 int x, int y,
1172 struct mouse_action_state *mas)
1173 {
1174 struct content *c = (struct content *)html;
1175 bool done = false;
1176
1177 /* frame resizing */
1178 if (browser_window_frame_resize_start(bw, mouse, x, y, &mas->result.pointer)) {
1179 if (mouse & (BROWSER_MOUSE_DRAG_1 | BROWSER_MOUSE_DRAG_2)) {
1180 mas->result.status = messages_get("FrameDrag");
1181 }
1182 done = true;
1183 }
1184
1185 /* if clicking in the main page, remove the selection from any
1186 * text areas */
1187 if (!done) {
1188 union html_selection_owner sel_owner;
1189 bool click;
1190 click = mouse & (BROWSER_MOUSE_PRESS_1 | BROWSER_MOUSE_PRESS_2 |
1191 BROWSER_MOUSE_CLICK_1 | BROWSER_MOUSE_CLICK_2 |
1192 BROWSER_MOUSE_DRAG_1 | BROWSER_MOUSE_DRAG_2);
1193
1194 if (click && html->focus_type != HTML_FOCUS_SELF) {
1195 union html_focus_owner fo;
1196 fo.self = true;
1197 html_set_focus(html, HTML_FOCUS_SELF, fo,
1198 true, 0, 0, 0, NULL);
1199 }
1200 if (click && html->selection_type != HTML_SELECTION_SELF) {
1201 sel_owner.none = true;
1202 html_set_selection(html, HTML_SELECTION_NONE,
1203 sel_owner, true);
1204 }
1205
1206 if (mas->text.box) {
1207 int pixel_offset;
1208 size_t idx;
1209 plot_font_style_t fstyle;
1210
1211 font_plot_style_from_css(&html->len_ctx,
1212 mas->text.box->style,
1213 &fstyle);
1214
1215 guit->layout->position(&fstyle,
1216 mas->text.box->text,
1217 mas->text.box->length,
1218 x - mas->text.box_x,
1219 &idx,
1220 &pixel_offset);
1221
1222 if (selection_click(html->sel,
1223 html->bw,
1224 mouse,
1225 mas->text.box->byte_offset + idx)) {
1226 /* key presses must be directed at the
1227 * main browser window, paste text
1228 * operations ignored */
1229 html_drag_type drag_type;
1230 union html_drag_owner drag_owner;
1231
1232 if (selection_dragging(html->sel)) {
1233 drag_type = HTML_DRAG_SELECTION;
1234 drag_owner.no_owner = true;
1235 html_set_drag_type(html,
1236 drag_type,
1237 drag_owner,
1238 NULL);
1239 mas->result.status = messages_get("Selecting");
1240 }
1241
1242 done = true;
1243 }
1244
1245 } else if (mouse & BROWSER_MOUSE_PRESS_1) {
1246 sel_owner.none = true;
1247 selection_clear(html->sel, true);
1248 }
1249
1250 if (selection_active(html->sel)) {
1251 sel_owner.none = false;
1252 html_set_selection(html,
1253 HTML_SELECTION_SELF,
1254 sel_owner,
1255 true);
1256 } else if (click &&
1257 html->selection_type != HTML_SELECTION_NONE) {
1258 sel_owner.none = true;
1259 html_set_selection(html,
1260 HTML_SELECTION_NONE,
1261 sel_owner,
1262 true);
1263 }
1264 }
1265
1266 if (!done) {
1267 union content_msg_data msg_data;
1268 if (mas->title) {
1269 mas->result.status = mas->title;
1270 }
1271
1272 if (mouse & BROWSER_MOUSE_DRAG_1) {
1273 if (mouse & BROWSER_MOUSE_MOD_2) {
1274 msg_data.dragsave.type = CONTENT_SAVE_COMPLETE;
1275 msg_data.dragsave.content = NULL;
1276 content_broadcast(c,
1277 CONTENT_MSG_DRAGSAVE,
1278 &msg_data);
1279 } else {
1280 if (mas->drag_candidate == NULL) {
1281 browser_window_page_drag_start(bw,
1282 x, y);
1283 } else {
1284 html_box_drag_start(mas->drag_candidate,
1285 x, y);
1286 }
1287 mas->result.pointer = BROWSER_POINTER_MOVE;
1288 }
1289 } else if (mouse & BROWSER_MOUSE_DRAG_2) {
1290 if (mouse & BROWSER_MOUSE_MOD_2) {
1291 msg_data.dragsave.type = CONTENT_SAVE_SOURCE;
1292 msg_data.dragsave.content = NULL;
1293 content_broadcast(c,
1294 CONTENT_MSG_DRAGSAVE,
1295 &msg_data);
1296 } else {
1297 if (mas->drag_candidate == NULL) {
1298 browser_window_page_drag_start(bw,
1299 x, y);
1300 } else {
1301 html_box_drag_start(mas->drag_candidate,
1302 x, y);
1303 }
1304 mas->result.pointer = BROWSER_POINTER_MOVE;
1305 }
1306 }
1307 }
1308
1309 if (mouse && mouse < BROWSER_MOUSE_MOD_1) {
1310 /* ensure key presses still act on the browser window */
1311 union html_focus_owner fo;
1312 fo.self = true;
1313 html_set_focus(html, HTML_FOCUS_SELF, fo, true, 0, 0, 0, NULL);
1314 }
1315
1316 return NSERROR_OK;
1317 }
1318
1319
1320 /**
1321 * handle non dragging mouse actions
1322 */
1323 static nserror
mouse_action_drag_none(html_content * html,struct browser_window * bw,browser_mouse_state mouse,int x,int y)1324 mouse_action_drag_none(html_content *html,
1325 struct browser_window *bw,
1326 browser_mouse_state mouse,
1327 int x, int y)
1328 {
1329 nserror res;
1330 struct content *c = (struct content *)html;
1331 union content_msg_data msg_data;
1332 lwc_string *path;
1333
1334 /**
1335 * computed state
1336 *
1337 * not on heap to avoid allocation or stack because it is large
1338 */
1339 static struct mouse_action_state mas;
1340
1341 res = get_mouse_action_node(html, x, y, &mas);
1342 if (res != NSERROR_OK) {
1343 return res;
1344 }
1345
1346 if (mas.scroll.bar) {
1347 mas.result.status = scrollbar_mouse_status_to_message(
1348 scrollbar_mouse_action(mas.scroll.bar,
1349 mouse,
1350 mas.scroll.mouse_x,
1351 mas.scroll.mouse_y));
1352 mas.result.pointer = BROWSER_POINTER_DEFAULT;
1353
1354 } else if (mas.gadget.control) {
1355 res = gadget_mouse_action(html, mouse, x, y, &mas);
1356
1357 } else if ((mas.object != NULL) && (mouse & BROWSER_MOUSE_MOD_2)) {
1358
1359 if (mouse & BROWSER_MOUSE_DRAG_2) {
1360 msg_data.dragsave.type = CONTENT_SAVE_NATIVE;
1361 msg_data.dragsave.content = mas.object;
1362 content_broadcast(c, CONTENT_MSG_DRAGSAVE, &msg_data);
1363
1364 } else if (mouse & BROWSER_MOUSE_DRAG_1) {
1365 msg_data.dragsave.type = CONTENT_SAVE_ORIG;
1366 msg_data.dragsave.content = mas.object;
1367 content_broadcast(c, CONTENT_MSG_DRAGSAVE, &msg_data);
1368 }
1369
1370 /* \todo should have a drag-saving object msg */
1371
1372 } else if (mas.iframe != NULL) {
1373 res = iframe_mouse_action(bw, mouse, x, y, &mas);
1374
1375 } else if (mas.html_object.box != NULL) {
1376 res = html_object_mouse_action(html, bw, mouse, x, y, &mas);
1377
1378 } else if (mas.link.url != NULL) {
1379 res = link_mouse_action(html, bw, mouse, x, y, &mas);
1380
1381 } else {
1382 res = default_mouse_action(html, bw, mouse, x, y, &mas);
1383
1384 }
1385 if (res != NSERROR_OK) {
1386 return res;
1387 }
1388
1389 /* send status and pointer message */
1390 if (mas.result.action != ACTION_NOSEND) {
1391 msg_data.explicit_status_text = mas.result.status;
1392 content_broadcast(c, CONTENT_MSG_STATUS, &msg_data);
1393
1394 msg_data.pointer = mas.result.pointer;
1395 content_broadcast(c, CONTENT_MSG_POINTER, &msg_data);
1396 }
1397
1398 /* fire dom click event */
1399 if (mouse & BROWSER_MOUSE_CLICK_1) {
1400 fire_generic_dom_event(corestring_dom_click, mas.node, true, true);
1401 }
1402
1403 /* deferred actions that can cause this browser_window to be destroyed
1404 * and must therefore be done after set_status/pointer
1405 */
1406 switch (mas.result.action) {
1407 case ACTION_SUBMIT:
1408 res = form_submit(content_get_url(c),
1409 browser_window_find_target(bw,
1410 mas.gadget.target,
1411 mouse),
1412 mas.gadget.control->form,
1413 mas.gadget.control);
1414 break;
1415
1416 case ACTION_GO:
1417 res = browser_window_navigate(
1418 browser_window_find_target(bw,
1419 mas.link.target,
1420 mouse),
1421 mas.link.url,
1422 content_get_url(c),
1423 BW_NAVIGATE_HISTORY,
1424 NULL,
1425 NULL,
1426 NULL);
1427 break;
1428
1429 case ACTION_JS:
1430 path = nsurl_get_component(mas.link.url, NSURL_PATH);
1431 if (path != NULL) {
1432 html_exec(c,
1433 lwc_string_data(path),
1434 lwc_string_length(path));
1435 lwc_string_unref(path);
1436 }
1437 break;
1438
1439 case ACTION_NOSEND:
1440 case ACTION_NONE:
1441 res = NSERROR_OK;
1442 break;
1443 }
1444
1445 return res;
1446 }
1447
1448
1449 /* exported interface documented in html/interaction.h */
html_mouse_track(struct content * c,struct browser_window * bw,browser_mouse_state mouse,int x,int y)1450 nserror html_mouse_track(struct content *c,
1451 struct browser_window *bw,
1452 browser_mouse_state mouse,
1453 int x, int y)
1454 {
1455 return html_mouse_action(c, bw, mouse, x, y);
1456 }
1457
1458
1459 /* exported interface documented in html/interaction.h */
1460 nserror
html_mouse_action(struct content * c,struct browser_window * bw,browser_mouse_state mouse,int x,int y)1461 html_mouse_action(struct content *c,
1462 struct browser_window *bw,
1463 browser_mouse_state mouse,
1464 int x, int y)
1465 {
1466 html_content *html = (html_content *)c;
1467 nserror res;
1468
1469 /* handle open select menu */
1470 if (html->visible_select_menu != NULL) {
1471 return mouse_action_select_menu(html, bw, mouse, x, y);
1472 }
1473
1474 /* handle content drag */
1475 switch (html->drag_type) {
1476 case HTML_DRAG_SELECTION:
1477 res = mouse_action_drag_selection(html, bw, mouse, x, y);
1478 break;
1479
1480 case HTML_DRAG_SCROLLBAR:
1481 res = mouse_action_drag_scrollbar(html, bw, mouse, x, y);
1482 break;
1483
1484 case HTML_DRAG_TEXTAREA_SELECTION:
1485 case HTML_DRAG_TEXTAREA_SCROLLBAR:
1486 res = mouse_action_drag_textarea(html, bw, mouse, x, y);
1487 break;
1488
1489 case HTML_DRAG_CONTENT_SELECTION:
1490 case HTML_DRAG_CONTENT_SCROLL:
1491 res = mouse_action_drag_content(html, bw, mouse, x, y);
1492 break;
1493
1494 case HTML_DRAG_NONE:
1495 res = mouse_action_drag_none(html, bw, mouse, x, y);
1496 break;
1497
1498 default:
1499 /* Unknown content related drag type */
1500 assert(0 && "Unknown content related drag type");
1501 }
1502
1503 if (res != NSERROR_OK) {
1504 NSLOG(netsurf, ERROR, "%s", messages_get_errorcode(res));
1505 }
1506
1507 return res;
1508 }
1509
1510
1511 /**
1512 * Handle keypresses.
1513 *
1514 * \param c content of type HTML
1515 * \param key The UCS4 character codepoint
1516 * \return true if key handled, false otherwise
1517 */
html_keypress(struct content * c,uint32_t key)1518 bool html_keypress(struct content *c, uint32_t key)
1519 {
1520 html_content *html = (html_content *) c;
1521 struct selection *sel = html->sel;
1522
1523 /** \todo
1524 * At the moment, the front end interface for keypress only gives
1525 * us a UCS4 key value. This doesn't doesn't have all the information
1526 * we need to fill out the event properly. We don't get to know about
1527 * modifier keys, and things like CTRL+C are passed in as
1528 * \ref NS_KEY_COPY_SELECTION, a magic value outside the valid Unicode
1529 * range.
1530 *
1531 * We need to:
1532 *
1533 * 1. Update the front end interface so that both press and release
1534 * events reach the core.
1535 * 2. Stop encoding the special keys like \ref NS_KEY_COPY_SELECTION as
1536 * magic values in the front ends, so we just get the events, e.g.:
1537 * 1. Press ctrl
1538 * 2. Press c
1539 * 3. Release c
1540 * 4. Release ctrl
1541 * 3. Pass all the new info to the DOM KeyboardEvent events.
1542 * 4. If there is a focused element, fire the event at that, instead of
1543 * `html->layout->node`.
1544 * 5. Rebuild the \ref NS_KEY_COPY_SELECTION values from the info we
1545 * now get given, and use that for the code below this
1546 * \ref fire_dom_keyboard_event call.
1547 * 6. Move the code after this \ref fire_dom_keyboard_event call into
1548 * the default action handler for DOM events.
1549 *
1550 * This will mean that if the JavaScript event listener does
1551 * `event.preventDefault()` then we won't handle the event when
1552 * we're not supposed to.
1553 */
1554 if (html->layout != NULL && html->layout->node != NULL) {
1555 fire_dom_keyboard_event(corestring_dom_keydown,
1556 html->layout->node, true, true, key);
1557 }
1558
1559 switch (html->focus_type) {
1560 case HTML_FOCUS_CONTENT:
1561 return content_keypress(html->focus_owner.content->object, key);
1562
1563 case HTML_FOCUS_TEXTAREA:
1564 if (box_textarea_keypress(html, html->focus_owner.textarea, key) == NSERROR_OK) {
1565 return true;
1566 } else {
1567 return false;
1568 }
1569
1570 default:
1571 /* Deal with it below */
1572 break;
1573 }
1574
1575 switch (key) {
1576 case NS_KEY_COPY_SELECTION:
1577 selection_copy_to_clipboard(sel);
1578 return true;
1579
1580 case NS_KEY_CLEAR_SELECTION:
1581 selection_clear(sel, true);
1582 return true;
1583
1584 case NS_KEY_SELECT_ALL:
1585 selection_select_all(sel);
1586 return true;
1587
1588 case NS_KEY_ESCAPE:
1589 /* if there's no selection, leave Escape for the caller */
1590 return selection_clear(sel, true);
1591 }
1592
1593 return false;
1594 }
1595
1596
1597 /**
1598 * Callback for in-page scrollbars.
1599 */
html_overflow_scroll_callback(void * client_data,struct scrollbar_msg_data * scrollbar_data)1600 void html_overflow_scroll_callback(void *client_data,
1601 struct scrollbar_msg_data *scrollbar_data)
1602 {
1603 struct html_scrollbar_data *data = client_data;
1604 html_content *html = (html_content *)data->c;
1605 struct box *box = data->box;
1606 union content_msg_data msg_data;
1607 html_drag_type drag_type;
1608 union html_drag_owner drag_owner;
1609
1610 switch(scrollbar_data->msg) {
1611 case SCROLLBAR_MSG_MOVED:
1612
1613 if (html->reflowing == true) {
1614 /* Can't redraw during layout, and it will
1615 * be redrawn after layout anyway. */
1616 break;
1617 }
1618
1619 html__redraw_a_box(html, box);
1620 break;
1621 case SCROLLBAR_MSG_SCROLL_START:
1622 {
1623 struct rect rect = {
1624 .x0 = scrollbar_data->x0,
1625 .y0 = scrollbar_data->y0,
1626 .x1 = scrollbar_data->x1,
1627 .y1 = scrollbar_data->y1
1628 };
1629 drag_type = HTML_DRAG_SCROLLBAR;
1630 drag_owner.scrollbar = scrollbar_data->scrollbar;
1631 html_set_drag_type(html, drag_type, drag_owner, &rect);
1632 }
1633 break;
1634 case SCROLLBAR_MSG_SCROLL_FINISHED:
1635 drag_type = HTML_DRAG_NONE;
1636 drag_owner.no_owner = true;
1637 html_set_drag_type(html, drag_type, drag_owner, NULL);
1638
1639 msg_data.pointer = BROWSER_POINTER_AUTO;
1640 content_broadcast(data->c, CONTENT_MSG_POINTER, &msg_data);
1641 break;
1642 }
1643 }
1644
1645
1646 /* Documented in html_internal.h */
html_set_drag_type(html_content * html,html_drag_type drag_type,union html_drag_owner drag_owner,const struct rect * rect)1647 void html_set_drag_type(html_content *html, html_drag_type drag_type,
1648 union html_drag_owner drag_owner, const struct rect *rect)
1649 {
1650 union content_msg_data msg_data;
1651
1652 assert(html != NULL);
1653
1654 html->drag_type = drag_type;
1655 html->drag_owner = drag_owner;
1656
1657 switch (drag_type) {
1658 case HTML_DRAG_NONE:
1659 assert(drag_owner.no_owner == true);
1660 msg_data.drag.type = CONTENT_DRAG_NONE;
1661 break;
1662
1663 case HTML_DRAG_SCROLLBAR:
1664 case HTML_DRAG_TEXTAREA_SCROLLBAR:
1665 case HTML_DRAG_CONTENT_SCROLL:
1666 msg_data.drag.type = CONTENT_DRAG_SCROLL;
1667 break;
1668
1669 case HTML_DRAG_SELECTION:
1670 assert(drag_owner.no_owner == true);
1671 /* Fall through */
1672 case HTML_DRAG_TEXTAREA_SELECTION:
1673 case HTML_DRAG_CONTENT_SELECTION:
1674 msg_data.drag.type = CONTENT_DRAG_SELECTION;
1675 break;
1676 }
1677 msg_data.drag.rect = rect;
1678
1679 /* Inform of the content's drag status change */
1680 content_broadcast((struct content *)html, CONTENT_MSG_DRAG, &msg_data);
1681 }
1682
1683 /* Documented in html_internal.h */
html_set_focus(html_content * html,html_focus_type focus_type,union html_focus_owner focus_owner,bool hide_caret,int x,int y,int height,const struct rect * clip)1684 void html_set_focus(html_content *html, html_focus_type focus_type,
1685 union html_focus_owner focus_owner, bool hide_caret,
1686 int x, int y, int height, const struct rect *clip)
1687 {
1688 union content_msg_data msg_data;
1689 int x_off = 0;
1690 int y_off = 0;
1691 struct rect cr;
1692 bool textarea_lost_focus = html->focus_type == HTML_FOCUS_TEXTAREA &&
1693 focus_type != HTML_FOCUS_TEXTAREA;
1694
1695 assert(html != NULL);
1696
1697 switch (focus_type) {
1698 case HTML_FOCUS_SELF:
1699 assert(focus_owner.self == true);
1700 if (html->focus_type == HTML_FOCUS_SELF)
1701 /* Don't need to tell anyone anything */
1702 return;
1703 break;
1704
1705 case HTML_FOCUS_CONTENT:
1706 box_coords(focus_owner.content, &x_off, &y_off);
1707 break;
1708
1709 case HTML_FOCUS_TEXTAREA:
1710 box_coords(focus_owner.textarea, &x_off, &y_off);
1711 break;
1712 }
1713
1714 html->focus_type = focus_type;
1715 html->focus_owner = focus_owner;
1716
1717 if (textarea_lost_focus) {
1718 msg_data.caret.type = CONTENT_CARET_REMOVE;
1719 } else if (focus_type != HTML_FOCUS_SELF && hide_caret) {
1720 msg_data.caret.type = CONTENT_CARET_HIDE;
1721 } else {
1722 if (clip != NULL) {
1723 cr = *clip;
1724 cr.x0 += x_off;
1725 cr.y0 += y_off;
1726 cr.x1 += x_off;
1727 cr.y1 += y_off;
1728 }
1729
1730 msg_data.caret.type = CONTENT_CARET_SET_POS;
1731 msg_data.caret.pos.x = x + x_off;
1732 msg_data.caret.pos.y = y + y_off;
1733 msg_data.caret.pos.height = height;
1734 msg_data.caret.pos.clip = (clip == NULL) ? NULL : &cr;
1735 }
1736
1737 /* Inform of the content's drag status change */
1738 content_broadcast((struct content *)html, CONTENT_MSG_CARET, &msg_data);
1739 }
1740
1741 /* Documented in html_internal.h */
html_set_selection(html_content * html,html_selection_type selection_type,union html_selection_owner selection_owner,bool read_only)1742 void html_set_selection(html_content *html, html_selection_type selection_type,
1743 union html_selection_owner selection_owner, bool read_only)
1744 {
1745 union content_msg_data msg_data;
1746 struct box *box;
1747 bool changed = false;
1748 bool same_type = html->selection_type == selection_type;
1749
1750 assert(html != NULL);
1751
1752 if ((selection_type == HTML_SELECTION_NONE &&
1753 html->selection_type != HTML_SELECTION_NONE) ||
1754 (selection_type != HTML_SELECTION_NONE &&
1755 html->selection_type == HTML_SELECTION_NONE))
1756 /* Existance of selection has changed, and we'll need to
1757 * inform our owner */
1758 changed = true;
1759
1760 /* Clear any existing selection */
1761 if (html->selection_type != HTML_SELECTION_NONE) {
1762 switch (html->selection_type) {
1763 case HTML_SELECTION_SELF:
1764 if (same_type)
1765 break;
1766 selection_clear(html->sel, true);
1767 break;
1768 case HTML_SELECTION_TEXTAREA:
1769 if (same_type && html->selection_owner.textarea ==
1770 selection_owner.textarea)
1771 break;
1772 box = html->selection_owner.textarea;
1773 textarea_clear_selection(box->gadget->data.text.ta);
1774 break;
1775 case HTML_SELECTION_CONTENT:
1776 if (same_type && html->selection_owner.content ==
1777 selection_owner.content)
1778 break;
1779 box = html->selection_owner.content;
1780 content_clear_selection(box->object);
1781 break;
1782 default:
1783 break;
1784 }
1785 }
1786
1787 html->selection_type = selection_type;
1788 html->selection_owner = selection_owner;
1789
1790 if (!changed)
1791 /* Don't need to report lack of change to owner */
1792 return;
1793
1794 /* Prepare msg */
1795 switch (selection_type) {
1796 case HTML_SELECTION_NONE:
1797 assert(selection_owner.none == true);
1798 msg_data.selection.selection = false;
1799 break;
1800 case HTML_SELECTION_SELF:
1801 assert(selection_owner.none == false);
1802 /* fall through */
1803 case HTML_SELECTION_TEXTAREA:
1804 case HTML_SELECTION_CONTENT:
1805 msg_data.selection.selection = true;
1806 break;
1807 default:
1808 break;
1809 }
1810 msg_data.selection.read_only = read_only;
1811
1812 /* Inform of the content's selection status change */
1813 content_broadcast((struct content *)html, CONTENT_MSG_SELECTION,
1814 &msg_data);
1815 }
1816