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