1 /*
2  * Copyright 2008 Michael Drake <tlsa@netsurf-browser.org>
3  * Copyright 2010 Daniel Silverstone <dsilvers@digital-scurf.org>
4  * Copyright 2010 Vincent Sanders <vince@netsurf-browser.org>
5  *
6  * This file is part of NetSurf, http://www.netsurf-browser.org/
7  *
8  * NetSurf is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; version 2 of the License.
11  *
12  * NetSurf is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
19  */
20 
21 /**
22  * \file
23  *
24  * Browser window creation and manipulation implementation.
25  */
26 
27 #include "utils/config.h"
28 
29 #include <assert.h>
30 #include <limits.h>
31 #include <stdbool.h>
32 #include <stdint.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <strings.h>
36 #include <math.h>
37 #include <nsutils/time.h>
38 #include <nsutils/base64.h>
39 
40 #include "utils/corestrings.h"
41 #include "utils/log.h"
42 #include "utils/messages.h"
43 #include "utils/nsurl.h"
44 #include "utils/utils.h"
45 #include "utils/utf8.h"
46 #include "utils/nsoption.h"
47 #include "netsurf/misc.h"
48 #include "netsurf/window.h"
49 #include "netsurf/search.h"
50 #include "netsurf/content.h"
51 #include "netsurf/plotters.h"
52 #include "content/content_debug.h"
53 #include "content/fetch.h"
54 #include "content/hlcache.h"
55 #include "content/urldb.h"
56 #include "css/utils.h"
57 #include "html/form_internal.h"
58 #include "html/html.h"
59 #include "html/box.h"
60 #include "javascript/js.h"
61 
62 #include "desktop/cookie_manager.h"
63 #include "desktop/browser_history.h"
64 #include "desktop/browser_private.h"
65 #include "desktop/download.h"
66 #include "desktop/frames.h"
67 #include "desktop/global_history.h"
68 #include "desktop/hotlist.h"
69 #include "desktop/knockout.h"
70 #include "desktop/scrollbar.h"
71 #include "desktop/selection.h"
72 #include "desktop/theme.h"
73 #include "desktop/gui_internal.h"
74 #include "desktop/textinput.h"
75 
76 /**
77  * smallest scale that can be applied to a browser window
78  */
79 #define SCALE_MINIMUM 0.2
80 
81 /**
82  * largests scale that can be applied to a browser window
83  */
84 #define SCALE_MAXIMUM 10.0
85 
86 /**
87  * maximum frame depth
88  */
89 #define FRAME_DEPTH 8
90 
91 /* Have to forward declare browser_window_destroy_internal */
92 static void browser_window_destroy_internal(struct browser_window *bw);
93 
94 /* Forward declare internal navigation function */
95 static nserror browser_window__navigate_internal(
96 	struct browser_window *bw, struct browser_fetch_parameters *params);
97 
98 
99 /**
100  * Close and destroy all child browser window.
101  *
102  * \param bw browser window
103  */
browser_window_destroy_children(struct browser_window * bw)104 static void browser_window_destroy_children(struct browser_window *bw)
105 {
106 	int i;
107 
108 	if (bw->children) {
109 		for (i = 0; i < (bw->rows * bw->cols); i++) {
110 			browser_window_destroy_internal(&bw->children[i]);
111 		}
112 		free(bw->children);
113 		bw->children = NULL;
114 		bw->rows = 0;
115 		bw->cols = 0;
116 	}
117 	if (bw->iframes) {
118 		for (i = 0; i < bw->iframe_count; i++) {
119 			browser_window_destroy_internal(&bw->iframes[i]);
120 		}
121 		free(bw->iframes);
122 		bw->iframes = NULL;
123 		bw->iframe_count = 0;
124 	}
125 }
126 
127 
128 /**
129  * Free the stored fetch parameters
130  *
131  * \param bw The browser window
132  */
133 static void
browser_window__free_fetch_parameters(struct browser_fetch_parameters * params)134 browser_window__free_fetch_parameters(struct browser_fetch_parameters *params)
135 {
136 	if (params->url != NULL) {
137 		nsurl_unref(params->url);
138 		params->url = NULL;
139 	}
140 	if (params->referrer != NULL) {
141 		nsurl_unref(params->referrer);
142 		params->referrer = NULL;
143 	}
144 	if (params->post_urlenc != NULL) {
145 		free(params->post_urlenc);
146 		params->post_urlenc = NULL;
147 	}
148 	if (params->post_multipart != NULL) {
149 		fetch_multipart_data_destroy(params->post_multipart);
150 		params->post_multipart = NULL;
151 	}
152 	if (params->parent_charset != NULL) {
153 		free(params->parent_charset);
154 		params->parent_charset = NULL;
155 	}
156 }
157 
158 
159 /**
160  * Get position of scrollbar widget within browser window.
161  *
162  * \param bw The browser window
163  * \param horizontal Whether to get position of horizontal scrollbar
164  * \param x Updated to x-coord of top left of scrollbar widget
165  * \param y Updated to y-coord of top left of scrollbar widget
166  */
167 static inline void
browser_window_get_scrollbar_pos(struct browser_window * bw,bool horizontal,int * x,int * y)168 browser_window_get_scrollbar_pos(struct browser_window *bw,
169 				 bool horizontal,
170 				 int *x, int *y)
171 {
172 	if (horizontal) {
173 		*x = 0;
174 		*y = bw->height - SCROLLBAR_WIDTH;
175 	} else {
176 		*x = bw->width - SCROLLBAR_WIDTH;
177 		*y = 0;
178 	}
179 }
180 
181 
182 /**
183  * Get browser window horizontal scrollbar widget length
184  *
185  * \param bw The browser window
186  * \return the scrollbar's length
187  */
get_horz_scrollbar_len(struct browser_window * bw)188 static inline int get_horz_scrollbar_len(struct browser_window *bw)
189 {
190 	if (bw->scroll_y == NULL) {
191 		return bw->width;
192 	}
193 	return bw->width - SCROLLBAR_WIDTH;
194 }
195 
196 
197 /**
198  * Get browser window vertical scrollbar widget length
199  *
200  * \param bw The browser window
201  * \return the scrollbar's length
202  */
get_vert_scrollbar_len(struct browser_window * bw)203 static inline int get_vert_scrollbar_len(struct browser_window *bw)
204 {
205 	return bw->height;
206 }
207 
208 
209 /**
210  * Set or remove a selection.
211  *
212  * \param bw browser window with selection
213  * \param selection true if bw has a selection, false if removing selection
214  * \param read_only true iff selection is read only (e.g. can't cut it)
215  */
216 static void
browser_window_set_selection(struct browser_window * bw,bool selection,bool read_only)217 browser_window_set_selection(struct browser_window *bw,
218 			     bool selection,
219 			     bool read_only)
220 {
221 	struct browser_window *top;
222 
223 	assert(bw != NULL);
224 
225 	top = browser_window_get_root(bw);
226 
227 	assert(top != NULL);
228 
229 	if (bw != top->selection.bw &&
230 	    top->selection.bw != NULL &&
231 	    top->selection.bw->current_content != NULL) {
232 		/* clear old selection */
233 		content_clear_selection(top->selection.bw->current_content);
234 	}
235 
236 	if (selection) {
237 		top->selection.bw = bw;
238 	} else {
239 		top->selection.bw = NULL;
240 	}
241 
242 	top->selection.read_only = read_only;
243 }
244 
245 
246 /**
247  * Set the scroll position of a browser window.
248  *
249  * scrolls the viewport to ensure the specified rectangle of the
250  *   content is shown.
251  *
252  * \param bw window to scroll
253  * \param rect The rectangle to ensure is shown.
254  * \return NSERROR_OK on success or apropriate error code.
255  */
256 static nserror
browser_window_set_scroll(struct browser_window * bw,const struct rect * rect)257 browser_window_set_scroll(struct browser_window *bw, const struct rect *rect)
258 {
259 	if (bw->window != NULL) {
260 		return guit->window->set_scroll(bw->window, rect);
261 	}
262 
263 	if (bw->scroll_x != NULL) {
264 		scrollbar_set(bw->scroll_x, rect->x0, false);
265 	}
266 	if (bw->scroll_y != NULL) {
267 		scrollbar_set(bw->scroll_y, rect->y0, false);
268 	}
269 
270 	return NSERROR_OK;
271 }
272 
273 
274 /**
275  * Internal helper for getting the positional features
276  *
277  * \param[in] bw browser window to examine.
278  * \param[in] x x-coordinate of point of interest
279  * \param[in] y y-coordinate of point of interest
280  * \param[out] data Feature structure to update.
281  * \return NSERROR_OK or appropriate error code on faliure.
282  */
283 static nserror
browser_window__get_contextual_content(struct browser_window * bw,int x,int y,struct browser_window_features * data)284 browser_window__get_contextual_content(struct browser_window *bw,
285 				       int x, int y,
286 				       struct browser_window_features *data)
287 {
288 	nserror ret = NSERROR_OK;
289 
290 	/* Handle (i)frame scroll offset (core-managed browser windows only) */
291 	x += scrollbar_get_offset(bw->scroll_x);
292 	y += scrollbar_get_offset(bw->scroll_y);
293 
294 	if (bw->children) {
295 		/* Browser window has children, so pass request on to
296 		 * appropriate child.
297 		 */
298 		struct browser_window *bwc;
299 		int cur_child;
300 		int children = bw->rows * bw->cols;
301 
302 		/* Loop through all children of bw */
303 		for (cur_child = 0; cur_child < children; cur_child++) {
304 			/* Set current child */
305 			bwc = &bw->children[cur_child];
306 
307 			/* Skip this frame if (x, y) coord lies outside */
308 			if ((x < bwc->x) ||
309 			    (bwc->x + bwc->width < x) ||
310 			    (y < bwc->y) ||
311 			    (bwc->y + bwc->height < y)) {
312 				continue;
313 			}
314 
315 			/* Pass request into this child */
316 			return browser_window__get_contextual_content(bwc,
317 					      (x - bwc->x), (y - bwc->y), data);
318 		}
319 
320 		/* Coordinate not contained by any frame */
321 
322 	} else if (bw->current_content != NULL) {
323 		/* Pass request to content */
324 		ret = content_get_contextual_content(bw->current_content,
325 						     x, y, data);
326 		data->main = bw->current_content;
327 	}
328 
329 	return ret;
330 }
331 
332 
333 /**
334  * implements the download operation of a window navigate
335  */
336 static nserror
browser_window_download(struct browser_window * bw,nsurl * url,nsurl * nsref,uint32_t fetch_flags,bool fetch_is_post,llcache_post_data * post)337 browser_window_download(struct browser_window *bw,
338 			nsurl *url,
339 			nsurl *nsref,
340 			uint32_t fetch_flags,
341 			bool fetch_is_post,
342 			llcache_post_data *post)
343 {
344 	llcache_handle *l;
345 	struct browser_window *root;
346 	nserror error;
347 
348 	root = browser_window_get_root(bw);
349 	assert(root != NULL);
350 
351 	fetch_flags |= LLCACHE_RETRIEVE_FORCE_FETCH;
352 	fetch_flags |= LLCACHE_RETRIEVE_STREAM_DATA;
353 
354 	error = llcache_handle_retrieve(url, fetch_flags, nsref,
355 					fetch_is_post ? post : NULL,
356 					NULL, NULL, &l);
357 	if (error == NSERROR_NO_FETCH_HANDLER) {
358 		/* no internal handler for this type, call out to frontend */
359 		error = guit->misc->launch_url(url);
360 	} else if (error != NSERROR_OK) {
361 		NSLOG(netsurf, INFO, "Failed to fetch download: %d", error);
362 	} else {
363 		error = download_context_create(l, root->window);
364 		if (error != NSERROR_OK) {
365 			NSLOG(netsurf, INFO,
366 			      "Failed creating download context: %d", error);
367 			llcache_handle_abort(l);
368 			llcache_handle_release(l);
369 		}
370 	}
371 
372 	return error;
373 }
374 
375 
376 /**
377  * recursively check browser windows for activity
378  *
379  * \param bw browser window to start checking from.
380  */
browser_window_check_throbber(struct browser_window * bw)381 static bool browser_window_check_throbber(struct browser_window *bw)
382 {
383 	int children, index;
384 
385 	if (bw->throbbing)
386 		return true;
387 
388 	if (bw->children) {
389 		children = bw->rows * bw->cols;
390 		for (index = 0; index < children; index++) {
391 			if (browser_window_check_throbber(&bw->children[index]))
392 				return true;
393 		}
394 	}
395 
396 	if (bw->iframes) {
397 		for (index = 0; index < bw->iframe_count; index++) {
398 			if (browser_window_check_throbber(&bw->iframes[index]))
399 				return true;
400 		}
401 	}
402 
403 	return false;
404 }
405 
406 
407 /**
408  * Start the busy indicator.
409  *
410  * \param bw browser window
411  */
browser_window_start_throbber(struct browser_window * bw)412 static nserror browser_window_start_throbber(struct browser_window *bw)
413 {
414 	bw->throbbing = true;
415 
416 	while (bw->parent)
417 		bw = bw->parent;
418 
419 	return guit->window->event(bw->window, GW_EVENT_START_THROBBER);
420 }
421 
422 
423 /**
424  * Stop the busy indicator.
425  *
426  * \param bw browser window
427  */
browser_window_stop_throbber(struct browser_window * bw)428 static nserror browser_window_stop_throbber(struct browser_window *bw)
429 {
430 	nserror res = NSERROR_OK;
431 
432 	bw->throbbing = false;
433 
434 	while (bw->parent) {
435 		bw = bw->parent;
436 	}
437 
438 	if (!browser_window_check_throbber(bw)) {
439 		res = guit->window->event(bw->window, GW_EVENT_STOP_THROBBER);
440 	}
441 	return res;
442 }
443 
444 
445 /**
446  * Callback for fetchcache() for browser window favicon fetches.
447  *
448  * \param c content handle of favicon
449  * \param event The event to process
450  * \param pw a context containing the browser window
451  * \return NSERROR_OK on success else appropriate error code.
452  */
453 static nserror
browser_window_favicon_callback(hlcache_handle * c,const hlcache_event * event,void * pw)454 browser_window_favicon_callback(hlcache_handle *c,
455 				const hlcache_event *event,
456 				void *pw)
457 {
458 	struct browser_window *bw = pw;
459 
460 	switch (event->type) {
461 	case CONTENT_MSG_DONE:
462 		if (bw->favicon.current != NULL) {
463 			content_close(bw->favicon.current);
464 			hlcache_handle_release(bw->favicon.current);
465 		}
466 
467 		bw->favicon.current = c;
468 		bw->favicon.loading = NULL;
469 
470 		/* content_get_bitmap on the hlcache_handle should give
471 		 *   the favicon bitmap at this point
472 		 */
473 		guit->window->set_icon(bw->window, c);
474 		break;
475 
476 	case CONTENT_MSG_ERROR:
477 
478 		/* clean up after ourselves */
479 		if (c == bw->favicon.loading) {
480 			bw->favicon.loading = NULL;
481 		} else if (c == bw->favicon.current) {
482 			bw->favicon.current = NULL;
483 		}
484 
485 		hlcache_handle_release(c);
486 
487 		if (bw->favicon.failed == false) {
488 			nsurl *nsref = NULL;
489 			nsurl *nsurl;
490 			nserror error;
491 
492 			bw->favicon.failed = true;
493 
494 			error = nsurl_create("resource:favicon.ico", &nsurl);
495 			if (error != NSERROR_OK) {
496 				NSLOG(netsurf, INFO,
497 				      "Unable to create default location url");
498 			} else {
499 				hlcache_handle_retrieve(nsurl,
500 							HLCACHE_RETRIEVE_SNIFF_TYPE,
501 							nsref, NULL,
502 							browser_window_favicon_callback,
503 							bw, NULL, CONTENT_IMAGE,
504 							&bw->favicon.loading);
505 
506 				nsurl_unref(nsurl);
507 			}
508 
509 		}
510 		break;
511 
512 	default:
513 		break;
514 
515 	}
516 	return NSERROR_OK;
517 }
518 
519 
520 /**
521  * update the favicon associated with the browser window
522  *
523  * \param c the page content handle.
524  * \param bw A top level browser window.
525  * \param link A link context or NULL to attempt fallback scanning.
526  */
527 static nserror
browser_window_update_favicon(hlcache_handle * c,struct browser_window * bw,struct content_rfc5988_link * link)528 browser_window_update_favicon(hlcache_handle *c,
529 			      struct browser_window *bw,
530 			      struct content_rfc5988_link *link)
531 {
532 	nsurl *nsref = NULL;
533 	nsurl *nsurl;
534 	nserror res;
535 
536 	assert(c != NULL);
537 	assert(bw !=NULL);
538 
539 	if (bw->window == NULL) {
540 		/* Not top-level browser window; not interested */
541 		return NSERROR_OK;
542 	}
543 
544 	/* already fetching the favicon - use that */
545 	if (bw->favicon.loading != NULL) {
546 		return NSERROR_OK;
547 	}
548 
549 	bw->favicon.failed = false;
550 
551 	if (link == NULL) {
552 		/* Look for "icon" */
553 		link = content_find_rfc5988_link(c, corestring_lwc_icon);
554 	}
555 
556 	if (link == NULL) {
557 		/* Look for "shortcut icon" */
558 		link = content_find_rfc5988_link(c, corestring_lwc_shortcut_icon);
559 	}
560 
561 	if (link == NULL) {
562 		lwc_string *scheme;
563 		bool speculative_default = false;
564 		bool match;
565 
566 		nsurl = hlcache_handle_get_url(c);
567 
568 		scheme = nsurl_get_component(nsurl, NSURL_SCHEME);
569 
570 		/* If the document was fetched over http(s), then speculate
571 		 * that there's a favicon living at /favicon.ico */
572 		if ((lwc_string_caseless_isequal(scheme,
573 						 corestring_lwc_http,
574 						 &match) == lwc_error_ok &&
575 		     match) ||
576 		    (lwc_string_caseless_isequal(scheme,
577 						 corestring_lwc_https,
578 						 &match) == lwc_error_ok &&
579 		     match)) {
580 			speculative_default = true;
581 		}
582 
583 		lwc_string_unref(scheme);
584 
585 		if (speculative_default) {
586 			/* no favicon via link, try for the default location */
587 			res = nsurl_join(nsurl, "/favicon.ico", &nsurl);
588 		} else {
589 			bw->favicon.failed = true;
590 			res = nsurl_create("resource:favicon.ico", &nsurl);
591 		}
592 		if (res != NSERROR_OK) {
593 			NSLOG(netsurf, INFO,
594 			      "Unable to create default location url");
595 			return res;
596 		}
597 	} else {
598 		nsurl = nsurl_ref(link->href);
599 	}
600 
601 	if (link == NULL) {
602 		NSLOG(netsurf, INFO,
603 		      "fetching general favicon from '%s'",
604 		      nsurl_access(nsurl));
605 	} else {
606 		NSLOG(netsurf, INFO,
607 		      "fetching favicon rel:%s '%s'",
608 		      lwc_string_data(link->rel),
609 		      nsurl_access(nsurl));
610 	}
611 
612 	res = hlcache_handle_retrieve(nsurl,
613 				      HLCACHE_RETRIEVE_SNIFF_TYPE,
614 				      nsref,
615 				      NULL,
616 				      browser_window_favicon_callback,
617 				      bw,
618 				      NULL,
619 				      CONTENT_IMAGE,
620 				      &bw->favicon.loading);
621 
622 	nsurl_unref(nsurl);
623 
624 	return res;
625 }
626 
627 
628 /**
629  * Handle meta http-equiv refresh time elapsing by loading a new page.
630  *
631  * \param p browser window to refresh with new page
632  */
browser_window_refresh(void * p)633 static void browser_window_refresh(void *p)
634 {
635 	struct browser_window *bw = p;
636 	nsurl *url;
637 	nsurl *refresh;
638 	hlcache_handle *parent = NULL;
639 	enum browser_window_nav_flags flags = BW_NAVIGATE_UNVERIFIABLE;
640 
641 	assert(bw->current_content != NULL &&
642 	       (content_get_status(bw->current_content) ==
643 		CONTENT_STATUS_READY ||
644 		content_get_status(bw->current_content) ==
645 		CONTENT_STATUS_DONE));
646 
647 	/* Ignore if the refresh URL has gone
648 	 * (may happen if a fetch error occurred) */
649 	refresh = content_get_refresh_url(bw->current_content);
650 	if (refresh == NULL)
651 		return;
652 
653 	/* mark this content as invalid so it gets flushed from the cache */
654 	content_invalidate_reuse_data(bw->current_content);
655 
656 	url = hlcache_handle_get_url(bw->current_content);
657 	if ((url == NULL) || (nsurl_compare(url, refresh, NSURL_COMPLETE))) {
658 		flags |= BW_NAVIGATE_HISTORY;
659 	}
660 
661 	/* Treat an (almost) immediate refresh in a top-level browser window as
662 	 * if it were an HTTP redirect, and thus make the resulting fetch
663 	 * verifiable.
664 	 *
665 	 * See fetchcache.c for why redirected fetches should be verifiable at
666 	 * all.
667 	 */
668 	if (bw->refresh_interval <= 100 && bw->parent == NULL) {
669 		flags &= ~BW_NAVIGATE_UNVERIFIABLE;
670 	} else {
671 		parent = bw->current_content;
672 	}
673 
674 	browser_window_navigate(bw,
675 				refresh,
676 				url,
677 				flags,
678 				NULL,
679 				NULL,
680 				parent);
681 
682 }
683 
684 
685 /**
686  * Transfer the loading_content to a new download window.
687  */
688 static void
browser_window_convert_to_download(struct browser_window * bw,llcache_handle * stream)689 browser_window_convert_to_download(struct browser_window *bw,
690 				   llcache_handle *stream)
691 {
692 	struct browser_window *root = browser_window_get_root(bw);
693 	nserror error;
694 
695 	assert(root != NULL);
696 
697 	error = download_context_create(stream, root->window);
698 	if (error != NSERROR_OK) {
699 		llcache_handle_abort(stream);
700 		llcache_handle_release(stream);
701 	}
702 
703 	/* remove content from browser window */
704 	hlcache_handle_release(bw->loading_content);
705 	bw->loading_content = NULL;
706 
707 	browser_window_stop_throbber(bw);
708 }
709 
710 
711 /**
712  * handle message for content ready on browser window
713  */
browser_window_content_ready(struct browser_window * bw)714 static nserror browser_window_content_ready(struct browser_window *bw)
715 {
716 	int width, height;
717 	nserror res = NSERROR_OK;
718 
719 	/* close and release the current window content */
720 	if (bw->current_content != NULL) {
721 		content_close(bw->current_content);
722 		hlcache_handle_release(bw->current_content);
723 	}
724 
725 	bw->current_content = bw->loading_content;
726 	bw->loading_content = NULL;
727 
728 	if (!bw->internal_nav) {
729 		/* Transfer the fetch parameters */
730 		browser_window__free_fetch_parameters(&bw->current_parameters);
731 		bw->current_parameters = bw->loading_parameters;
732 		memset(&bw->loading_parameters, 0, sizeof(bw->loading_parameters));
733 		/* Transfer the certificate chain */
734 		cert_chain_free(bw->current_cert_chain);
735 		bw->current_cert_chain = bw->loading_cert_chain;
736 		bw->loading_cert_chain = NULL;
737 	}
738 
739 	/* Format the new content to the correct dimensions */
740 	browser_window_get_dimensions(bw, &width, &height);
741 	width /= bw->scale;
742 	height /= bw->scale;
743 	content_reformat(bw->current_content, false, width, height);
744 
745 	/* history */
746 	if (bw->history_add && bw->history && !bw->internal_nav) {
747 		nsurl *url = hlcache_handle_get_url(bw->current_content);
748 
749 		if (urldb_add_url(url)) {
750 			urldb_set_url_title(url, content_get_title(bw->current_content));
751 			urldb_update_url_visit_data(url);
752 			urldb_set_url_content_type(url,
753 						   content_get_type(bw->current_content));
754 
755 			/* This is safe as we've just added the URL */
756 			global_history_add(urldb_get_url(url));
757 		}
758 		/**
759 		 * \todo Urldb / Thumbnails / Local history brokenness
760 		 *
761 		 * We add to local history after calling urldb_add_url rather
762 		 *  than in the block above.  If urldb_add_url fails (as it
763 		 *  will for urls like "about:about", "about:config" etc),
764 		 *  there would be no local history node, and later calls to
765 		 *  history_update will either explode or overwrite the node
766 		 *  for the previous URL.
767 		 *
768 		 * We call it after, rather than before urldb_add_url because
769 		 *  history_add calls bitmap render, which tries to register
770 		 *  the thumbnail with urldb.  That thumbnail registration
771 		 *  fails if the url doesn't exist in urldb already, and only
772 		 *  urldb-registered thumbnails get freed.  So if we called
773 		 *  history_add before urldb_add_url we would leak thumbnails
774 		 *  for all newly visited URLs.  With the history_add call
775 		 *  after, we only leak the thumbnails when urldb does not add
776 		 *  the URL.
777 		 *
778 		 * Also, since browser_window_history_add can create a
779 		 *  thumbnail (content_redraw), we need to do it after
780 		 *  content_reformat.
781 		 */
782 		browser_window_history_add(bw, bw->current_content, bw->frag_id);
783 	}
784 
785 	browser_window_remove_caret(bw, false);
786 
787 	if (bw->window != NULL) {
788 		guit->window->event(bw->window, GW_EVENT_NEW_CONTENT);
789 
790 		browser_window_refresh_url_bar(bw);
791 	}
792 
793 	/* new content; set scroll_to_top */
794 	browser_window_update(bw, true);
795 	content_open(bw->current_content, bw, 0, 0);
796 	browser_window_set_status(bw, content_get_status_message(bw->current_content));
797 
798 	/* frames */
799 	if ((content_get_type(bw->current_content) == CONTENT_HTML) &&
800 	    (html_get_frameset(bw->current_content) != NULL)) {
801 		res = browser_window_create_frameset(bw, html_get_frameset(bw->current_content));
802 	}
803 
804 	if (content_get_type(bw->current_content) == CONTENT_HTML &&
805 	    html_get_iframe(bw->current_content) != NULL) {
806 		browser_window_create_iframes(bw, html_get_iframe(bw->current_content));
807 	}
808 
809 	/* Indicate page status may have changed */
810 	if (res == NSERROR_OK) {
811 		struct browser_window *root = browser_window_get_root(bw);
812 		res = guit->window->event(root->window, GW_EVENT_PAGE_INFO_CHANGE);
813 	}
814 
815 	return res;
816 }
817 
818 
819 /**
820  * handle message for content done on browser window
821  */
822 static nserror
browser_window_content_done(struct browser_window * bw)823 browser_window_content_done(struct browser_window *bw)
824 {
825 	float sx, sy;
826 	struct rect rect;
827 	int scrollx;
828 	int scrolly;
829 
830 	if (bw->window == NULL) {
831 		/* Updated browser window's scrollbars. */
832 		/**
833 		 * \todo update browser window scrollbars before CONTENT_MSG_DONE
834 		 */
835 		browser_window_reformat(bw, true, bw->width, bw->height);
836 		browser_window_handle_scrollbars(bw);
837 	}
838 
839 	browser_window_update(bw, false);
840 	browser_window_set_status(bw, content_get_status_message(bw->current_content));
841 	browser_window_stop_throbber(bw);
842 	browser_window_update_favicon(bw->current_content, bw, NULL);
843 
844 	if (browser_window_history_get_scroll(bw, &sx, &sy) == NSERROR_OK) {
845 		scrollx = (int)((float)content_get_width(bw->current_content) * sx);
846 		scrolly = (int)((float)content_get_height(bw->current_content) * sy);
847 		rect.x0 = rect.x1 = scrollx;
848 		rect.y0 = rect.y1 = scrolly;
849 		if (browser_window_set_scroll(bw, &rect) != NSERROR_OK) {
850 			NSLOG(netsurf, WARNING,
851 			      "Unable to set browser scroll offsets to %d by %d",
852 			      scrollx, scrolly);
853 		}
854 	}
855 
856 	if (!bw->internal_nav) {
857 		browser_window_history_update(bw, bw->current_content);
858 		hotlist_update_url(hlcache_handle_get_url(bw->current_content));
859 	}
860 
861 	if (bw->refresh_interval != -1) {
862 		guit->misc->schedule(bw->refresh_interval * 10,
863 				     browser_window_refresh, bw);
864 	}
865 
866 	return NSERROR_OK;
867 }
868 
869 
870 /**
871  * Handle query responses from SSL requests
872  */
873 static nserror
browser_window__handle_ssl_query_response(bool proceed,void * pw)874 browser_window__handle_ssl_query_response(bool proceed, void *pw)
875 {
876 	struct browser_window *bw = (struct browser_window *)pw;
877 
878 	/* If we're in the process of loading, stop the load */
879 	if (bw->loading_content != NULL) {
880 		/* We had a loading content (maybe auth page?) */
881 		browser_window_stop(bw);
882 		browser_window_remove_caret(bw, false);
883 		browser_window_destroy_children(bw);
884 	}
885 
886 	if (!proceed) {
887 		/* We're processing a "back to safety", do a rough-and-ready
888 		 * nav to the old 'current' parameters, with any post data
889 		 * stripped away
890 		 */
891 		return browser_window__reload_current_parameters(bw);
892 	}
893 
894 	/* We're processing a "proceed" attempt from the form */
895 	/* First, we permit the SSL */
896 	urldb_set_cert_permissions(bw->loading_parameters.url, true);
897 
898 	/* And then we navigate to the original loading parameters */
899 	bw->internal_nav = false;
900 
901 	return browser_window__navigate_internal(bw, &bw->loading_parameters);
902 }
903 
904 
905 /**
906  * Unpack a "username:password" to components.
907  *
908  * \param[in]  userpass      The input string to split.
909  * \param[in]  username_out  Returns username on success.  Owned by caller.
910  * \param[out] password_out  Returns password on success.  Owned by caller.
911  * \return NSERROR_OK, or appropriate error code.
912  */
913 static nserror
browser_window__unpack_userpass(const char * userpass,char ** username_out,char ** password_out)914 browser_window__unpack_userpass(const char *userpass,
915 				char **username_out,
916 				char **password_out)
917 {
918 	const char *tmp;
919 	char *username;
920 	char *password;
921 	size_t len;
922 
923 	if (userpass == NULL) {
924 		username = malloc(1);
925 		password = malloc(1);
926 		if (username == NULL || password == NULL) {
927 			free(username);
928 			free(password);
929 			return NSERROR_NOMEM;
930 		}
931 		username[0] = '\0';
932 		password[0] = '\0';
933 
934 		*username_out = username;
935 		*password_out = password;
936 		return NSERROR_OK;
937 	}
938 
939 	tmp = strchr(userpass, ':');
940 	if (tmp == NULL) {
941 		return NSERROR_BAD_PARAMETER;
942 	} else {
943 		size_t len2;
944 		len = tmp - userpass;
945 		len2 = strlen(++tmp);
946 
947 		username = malloc(len + 1);
948 		password = malloc(len2 + 1);
949 		if (username == NULL || password == NULL) {
950 			free(username);
951 			free(password);
952 			return NSERROR_NOMEM;
953 		}
954 		memcpy(username, userpass, len);
955 		username[len] = '\0';
956 		memcpy(password, tmp, len2 + 1);
957 	}
958 
959 	*username_out = username;
960 	*password_out = password;
961 	return NSERROR_OK;
962 }
963 
964 
965 /**
966  * Build a "username:password" from components.
967  *
968  * \param[in]  username      The username component.
969  * \param[in]  password      The password component.
970  * \param[out] userpass_out  Returns combined string on success.
971  *                           Owned by caller.
972  * \return NSERROR_OK, or appropriate error code.
973  */
974 static nserror
browser_window__build_userpass(const char * username,const char * password,char ** userpass_out)975 browser_window__build_userpass(const char *username,
976 			       const char *password,
977 			       char **userpass_out)
978 {
979 	char *userpass;
980 	size_t len;
981 
982 	len = strlen(username) + 1 + strlen(password) + 1;
983 
984 	userpass = malloc(len);
985 	if (userpass == NULL) {
986 		return NSERROR_NOMEM;
987 	}
988 
989 	snprintf(userpass, len, "%s:%s", username, password);
990 
991 	*userpass_out = userpass;
992 	return NSERROR_OK;
993 }
994 
995 
996 /**
997  * Handle a response from the UI when prompted for credentials
998  */
999 static nserror
browser_window__handle_userpass_response(nsurl * url,const char * realm,const char * username,const char * password,void * pw)1000 browser_window__handle_userpass_response(nsurl *url,
1001 					 const char *realm,
1002 					 const char *username,
1003 					 const char *password,
1004 					 void *pw)
1005 {
1006 	struct browser_window *bw = (struct browser_window *)pw;
1007 	char *userpass;
1008 	nserror err;
1009 
1010 	err = browser_window__build_userpass(username, password, &userpass);
1011 	if (err != NSERROR_OK) {
1012 		return err;
1013 	}
1014 
1015 	urldb_set_auth_details(url, realm, userpass);
1016 
1017 	free(userpass);
1018 
1019 	/**
1020 	 * \todo QUERY - Eventually this should fill out the form *NOT* nav
1021 	 *               to the original location
1022 	 */
1023 	/* Finally navigate to the original loading parameters */
1024 	if (bw->loading_content != NULL) {
1025 		/* We had a loading content (maybe auth page?) */
1026 		browser_window_stop(bw);
1027 		browser_window_remove_caret(bw, false);
1028 		browser_window_destroy_children(bw);
1029 	}
1030 	bw->internal_nav = false;
1031 	return browser_window__navigate_internal(bw, &bw->loading_parameters);
1032 }
1033 
1034 
1035 /**
1036  * Handle login request (BAD_AUTH) during fetch
1037  *
1038  */
1039 static nserror
browser_window__handle_login(struct browser_window * bw,const char * realm,nsurl * url)1040 browser_window__handle_login(struct browser_window *bw,
1041 			     const char *realm,
1042 			     nsurl *url) {
1043 	char *username = NULL, *password = NULL;
1044 	nserror err = NSERROR_OK;
1045 	struct browser_fetch_parameters params;
1046 
1047 	memset(&params, 0, sizeof(params));
1048 
1049 	/* Step one, retrieve what we have */
1050 	err = browser_window__unpack_userpass(
1051 				      urldb_get_auth_details(url, realm),
1052 				      &username, &password);
1053 	if (err != NSERROR_OK) {
1054 		goto out;
1055 	}
1056 
1057 	/* Step two, construct our fetch parameters */
1058 	params.url = nsurl_ref(corestring_nsurl_about_query_auth);
1059 	params.referrer = nsurl_ref(url);
1060 	params.flags = BW_NAVIGATE_HISTORY | BW_NAVIGATE_NO_TERMINAL_HISTORY_UPDATE | BW_NAVIGATE_INTERNAL;
1061 
1062 	err = fetch_multipart_data_new_kv(&params.post_multipart,
1063 					  "siteurl",
1064 					  nsurl_access(url));
1065 	if (err != NSERROR_OK) {
1066 		goto out;
1067 	}
1068 
1069 	err = fetch_multipart_data_new_kv(&params.post_multipart,
1070 					  "realm",
1071 					  realm);
1072 	if (err != NSERROR_OK) {
1073 		goto out;
1074 	}
1075 
1076 	err = fetch_multipart_data_new_kv(&params.post_multipart,
1077 					  "username",
1078 					  username);
1079 	if (err != NSERROR_OK) {
1080 		goto out;
1081 	}
1082 
1083 	err = fetch_multipart_data_new_kv(&params.post_multipart,
1084 					  "password",
1085 					  password);
1086 	if (err != NSERROR_OK) {
1087 		goto out;
1088 	}
1089 
1090 	/* Now we issue the fetch */
1091 	bw->internal_nav = true;
1092 	err = browser_window__navigate_internal(bw, &params);
1093 
1094 	if (err != NSERROR_OK) {
1095 		goto out;
1096 	}
1097 
1098 	err = guit->misc->login(url, realm, username, password,
1099 				browser_window__handle_userpass_response, bw);
1100 
1101 	if (err == NSERROR_NOT_IMPLEMENTED) {
1102 		err = NSERROR_OK;
1103 	}
1104  out:
1105 	if (username != NULL) {
1106 		free(username);
1107 	}
1108 	if (password != NULL) {
1109 		free(password);
1110 	}
1111 	browser_window__free_fetch_parameters(&params);
1112 	return err;
1113 }
1114 
1115 
1116 /**
1117  * Handle a certificate verification request (BAD_CERTS) during a fetch
1118  */
1119 static nserror
browser_window__handle_bad_certs(struct browser_window * bw,nsurl * url)1120 browser_window__handle_bad_certs(struct browser_window *bw,
1121 				 nsurl *url)
1122 {
1123 	struct browser_fetch_parameters params;
1124 	nserror err;
1125 	/* Initially we don't know WHY the SSL cert was bad */
1126 	const char *reason = messages_get_sslcode(SSL_CERT_ERR_UNKNOWN);
1127 	size_t depth;
1128 	nsurl *chainurl = NULL;
1129 
1130 	memset(&params, 0, sizeof(params));
1131 
1132 	params.url = nsurl_ref(corestring_nsurl_about_query_ssl);
1133 	params.referrer = nsurl_ref(url);
1134 	params.flags = BW_NAVIGATE_HISTORY | BW_NAVIGATE_NO_TERMINAL_HISTORY_UPDATE | BW_NAVIGATE_INTERNAL;
1135 
1136 	err = fetch_multipart_data_new_kv(&params.post_multipart,
1137 					  "siteurl",
1138 					  nsurl_access(url));
1139 	if (err != NSERROR_OK) {
1140 		goto out;
1141 	}
1142 
1143 	if (bw->loading_cert_chain != NULL) {
1144 		for (depth = 0; depth < bw->loading_cert_chain->depth; ++depth) {
1145 			size_t idx = bw->loading_cert_chain->depth - (depth + 1);
1146 			ssl_cert_err err = bw->loading_cert_chain->certs[idx].err;
1147 			if (err != SSL_CERT_ERR_OK) {
1148 				reason = messages_get_sslcode(err);
1149 				break;
1150 			}
1151 		}
1152 
1153 		err = cert_chain_to_query(bw->loading_cert_chain, &chainurl);
1154 		if (err != NSERROR_OK) {
1155 			goto out;
1156 		}
1157 
1158 		err = fetch_multipart_data_new_kv(&params.post_multipart,
1159 						  "chainurl",
1160 						  nsurl_access(chainurl));
1161 		if (err != NSERROR_OK) {
1162 			goto out;
1163 		}
1164 	}
1165 
1166 	err = fetch_multipart_data_new_kv(&params.post_multipart,
1167 					  "reason",
1168 					  reason);
1169 	if (err != NSERROR_OK) {
1170 		goto out;
1171 	}
1172 
1173 	/* Now we issue the fetch */
1174 	bw->internal_nav = true;
1175 	err = browser_window__navigate_internal(bw, &params);
1176 	if (err != NSERROR_OK) {
1177 		goto out;
1178 	}
1179 
1180  out:
1181 	browser_window__free_fetch_parameters(&params);
1182 	if (chainurl != NULL)
1183 		nsurl_unref(chainurl);
1184 	return err;
1185 }
1186 
1187 
1188 /**
1189  * Handle a timeout during a fetch
1190  */
1191 static nserror
browser_window__handle_timeout(struct browser_window * bw,nsurl * url)1192 browser_window__handle_timeout(struct browser_window *bw, nsurl *url)
1193 {
1194 	struct browser_fetch_parameters params;
1195 	nserror err;
1196 
1197 	memset(&params, 0, sizeof(params));
1198 
1199 	params.url = nsurl_ref(corestring_nsurl_about_query_timeout);
1200 	params.referrer = nsurl_ref(url);
1201 	params.flags = BW_NAVIGATE_HISTORY | BW_NAVIGATE_NO_TERMINAL_HISTORY_UPDATE | BW_NAVIGATE_INTERNAL;
1202 
1203 	err = fetch_multipart_data_new_kv(&params.post_multipart,
1204 					  "siteurl",
1205 					  nsurl_access(url));
1206 	if (err != NSERROR_OK) {
1207 		goto out;
1208 	}
1209 
1210 	/* Now we issue the fetch */
1211 	bw->internal_nav = true;
1212 	err = browser_window__navigate_internal(bw, &params);
1213 	if (err != NSERROR_OK) {
1214 		goto out;
1215 	}
1216 
1217  out:
1218 	browser_window__free_fetch_parameters(&params);
1219 	return err;
1220 }
1221 
1222 
1223 /**
1224  * Handle non specific errors during a fetch
1225  */
1226 static nserror
browser_window__handle_fetcherror(struct browser_window * bw,const char * reason,nsurl * url)1227 browser_window__handle_fetcherror(struct browser_window *bw,
1228 				  const char *reason,
1229 				  nsurl *url)
1230 {
1231 	struct browser_fetch_parameters params;
1232 	nserror err;
1233 
1234 	memset(&params, 0, sizeof(params));
1235 
1236 	params.url = nsurl_ref(corestring_nsurl_about_query_fetcherror);
1237 	params.referrer = nsurl_ref(url);
1238 	params.flags = BW_NAVIGATE_HISTORY | BW_NAVIGATE_NO_TERMINAL_HISTORY_UPDATE | BW_NAVIGATE_INTERNAL;
1239 
1240 	err = fetch_multipart_data_new_kv(&params.post_multipart,
1241 					  "siteurl",
1242 					  nsurl_access(url));
1243 	if (err != NSERROR_OK) {
1244 		goto out;
1245 	}
1246 
1247 	err = fetch_multipart_data_new_kv(&params.post_multipart,
1248 					  "reason",
1249 					  reason);
1250 	if (err != NSERROR_OK) {
1251 		goto out;
1252 	}
1253 
1254 	/* Now we issue the fetch */
1255 	bw->internal_nav = true;
1256 	err = browser_window__navigate_internal(bw, &params);
1257 	if (err != NSERROR_OK) {
1258 		goto out;
1259 	}
1260 
1261  out:
1262 	browser_window__free_fetch_parameters(&params);
1263 	return err;
1264 }
1265 
1266 
1267 /**
1268  * Handle errors during content fetch
1269  */
1270 static nserror
browser_window__handle_error(struct browser_window * bw,hlcache_handle * c,const hlcache_event * event)1271 browser_window__handle_error(struct browser_window *bw,
1272 			     hlcache_handle *c,
1273 			     const hlcache_event *event)
1274 {
1275 	const char *message = event->data.errordata.errormsg;
1276 	nserror code = event->data.errordata.errorcode;
1277 	nserror res;
1278 	nsurl *url = hlcache_handle_get_url(c);
1279 
1280 	/* Unexpected OK? */
1281 	assert(code != NSERROR_OK);
1282 
1283 	if (message == NULL) {
1284 		message = messages_get_errorcode(code);
1285 	} else {
1286 		message = messages_get(message);
1287 	}
1288 
1289 	if (c == bw->loading_content) {
1290 		bw->loading_content = NULL;
1291 	} else if (c == bw->current_content) {
1292 		bw->current_content = NULL;
1293 		browser_window_remove_caret(bw, false);
1294 	}
1295 
1296 	hlcache_handle_release(c);
1297 
1298 	switch (code) {
1299 	case NSERROR_BAD_AUTH:
1300 		res = browser_window__handle_login(bw, message, url);
1301 		break;
1302 
1303 	case NSERROR_BAD_CERTS:
1304 		res = browser_window__handle_bad_certs(bw, url);
1305 		break;
1306 
1307 	case NSERROR_TIMEOUT:
1308 		res = browser_window__handle_timeout(bw, url);
1309 		break;
1310 
1311 	default:
1312 		res = browser_window__handle_fetcherror(bw, message, url);
1313 		break;
1314 	}
1315 
1316 	return res;
1317 }
1318 
1319 
1320 /**
1321  * Update URL bar for a given browser window to given URL
1322  *
1323  * \param bw	Browser window to update URL bar for.
1324  * \param url	URL for content displayed by bw including any fragment.
1325  */
1326 static inline nserror
browser_window_refresh_url_bar_internal(struct browser_window * bw,nsurl * url)1327 browser_window_refresh_url_bar_internal(struct browser_window *bw, nsurl *url)
1328 {
1329 	assert(bw);
1330 	assert(url);
1331 
1332 	if ((bw->parent != NULL) || (bw->window == NULL)) {
1333 		/* Not root window or no gui window so do not set a URL */
1334 		return NSERROR_OK;
1335 	}
1336 
1337 	return guit->window->set_url(bw->window, url);
1338 }
1339 
1340 
1341 /**
1342  * Browser window content event callback handler.
1343  */
1344 static nserror
browser_window_callback(hlcache_handle * c,const hlcache_event * event,void * pw)1345 browser_window_callback(hlcache_handle *c, const hlcache_event *event, void *pw)
1346 {
1347 	struct browser_window *bw = pw;
1348 	nserror res = NSERROR_OK;
1349 
1350 	switch (event->type) {
1351 	case CONTENT_MSG_SSL_CERTS:
1352 		/* SSL certificate information has arrived, store it */
1353 		cert_chain_free(bw->loading_cert_chain);
1354 		cert_chain_dup(event->data.chain, &bw->loading_cert_chain);
1355 		break;
1356 
1357 	case CONTENT_MSG_LOG:
1358 		browser_window_console_log(bw,
1359 					   event->data.log.src,
1360 					   event->data.log.msg,
1361 					   event->data.log.msglen,
1362 					   event->data.log.flags);
1363 		break;
1364 
1365 	case CONTENT_MSG_DOWNLOAD:
1366 		assert(bw->loading_content == c);
1367 
1368 		browser_window_convert_to_download(bw, event->data.download);
1369 
1370 		if (bw->current_content != NULL) {
1371 			browser_window_refresh_url_bar(bw);
1372 		}
1373 		break;
1374 
1375 	case CONTENT_MSG_LOADING:
1376 		assert(bw->loading_content == c);
1377 
1378 #ifdef WITH_THEME_INSTALL
1379 		if (content_get_type(c) == CONTENT_THEME) {
1380 			theme_install_start(c);
1381 			bw->loading_content = NULL;
1382 			browser_window_stop_throbber(bw);
1383 		} else
1384 #endif
1385 		{
1386 			bw->refresh_interval = -1;
1387 			browser_window_set_status(bw,
1388 						  content_get_status_message(c));
1389 		}
1390 		break;
1391 
1392 	case CONTENT_MSG_READY:
1393 		assert(bw->loading_content == c);
1394 
1395 		res = browser_window_content_ready(bw);
1396 		break;
1397 
1398 	case CONTENT_MSG_DONE:
1399 		assert(bw->current_content == c);
1400 
1401 		res = browser_window_content_done(bw);
1402 		break;
1403 
1404 	case CONTENT_MSG_ERROR:
1405 		res = browser_window__handle_error(bw, c, event);
1406 		break;
1407 
1408 	case CONTENT_MSG_REDIRECT:
1409 		if (urldb_add_url(event->data.redirect.from)) {
1410 			urldb_update_url_visit_data(event->data.redirect.from);
1411 		}
1412 		browser_window_refresh_url_bar_internal(bw, event->data.redirect.to);
1413 		break;
1414 
1415 	case CONTENT_MSG_STATUS:
1416 		if (event->data.explicit_status_text == NULL) {
1417 			/* Object content's status text updated */
1418 			const char *status = NULL;
1419 			if (bw->loading_content != NULL) {
1420 				/* Give preference to any loading content */
1421 				status = content_get_status_message(
1422 							bw->loading_content);
1423 			}
1424 
1425 			if (status == NULL) {
1426 				status = content_get_status_message(c);
1427 			}
1428 
1429 			if (status != NULL) {
1430 				browser_window_set_status(bw, status);
1431 			}
1432 		} else {
1433 			/* Object content wants to set explicit message */
1434 			browser_window_set_status(bw,
1435 					event->data.explicit_status_text);
1436 		}
1437 		break;
1438 
1439 	case CONTENT_MSG_REFORMAT:
1440 		if (c == bw->current_content &&
1441 		    content_get_type(c) == CONTENT_HTML) {
1442 			/* reposition frames */
1443 			if (html_get_frameset(c) != NULL)
1444 				browser_window_recalculate_frameset(bw);
1445 			/* reflow iframe positions */
1446 			if (html_get_iframe(c) != NULL)
1447 				browser_window_recalculate_iframes(bw);
1448 		}
1449 
1450 		/* Hide any caret, but don't remove it */
1451 		browser_window_remove_caret(bw, true);
1452 
1453 		if (!(event->data.background)) {
1454 			/* Reformatted content should be redrawn */
1455 			browser_window_update(bw, false);
1456 		}
1457 		break;
1458 
1459 	case CONTENT_MSG_REDRAW:
1460 		{
1461 			struct rect rect = {
1462 					    .x0 = event->data.redraw.x,
1463 					    .y0 = event->data.redraw.y,
1464 					    .x1 = event->data.redraw.x + event->data.redraw.width,
1465 					    .y1 = event->data.redraw.y + event->data.redraw.height
1466 			};
1467 
1468 			browser_window_update_box(bw, &rect);
1469 		}
1470 		break;
1471 
1472 	case CONTENT_MSG_REFRESH:
1473 		bw->refresh_interval = event->data.delay * 100;
1474 		break;
1475 
1476 	case CONTENT_MSG_LINK: /* content has an rfc5988 link element */
1477 		{
1478 			bool match;
1479 
1480 			/* Handle "icon" and "shortcut icon" */
1481 			if ((lwc_string_caseless_isequal(
1482 							 event->data.rfc5988_link->rel,
1483 							 corestring_lwc_icon,
1484 							 &match) == lwc_error_ok && match) ||
1485 			    (lwc_string_caseless_isequal(
1486 							 event->data.rfc5988_link->rel,
1487 							 corestring_lwc_shortcut_icon,
1488 							 &match) == lwc_error_ok && match)) {
1489 				/* it's a favicon perhaps start a fetch for it */
1490 				browser_window_update_favicon(c, bw,
1491 						      event->data.rfc5988_link);
1492 			}
1493 		}
1494 		break;
1495 
1496 	case CONTENT_MSG_GETTHREAD:
1497 		{
1498 			/* only the content object created by the browser
1499 			 * window requires a new javascript thread object
1500 			 */
1501 			jsthread *thread;
1502 			assert(bw->loading_content == c);
1503 
1504 			if (js_newthread(bw->jsheap,
1505 					 bw,
1506 					 hlcache_handle_get_content(c),
1507 					 &thread) == NSERROR_OK) {
1508 				/* The content which is requesting the thread
1509 				 * is required to keep hold of it and
1510 				 * to destroy it when it is finished with it.
1511 				 */
1512 				*(event->data.jsthread) = thread;
1513 			}
1514 		}
1515 		break;
1516 
1517 	case CONTENT_MSG_GETDIMS:
1518 		{
1519 			int width;
1520 			int height;
1521 
1522 			browser_window_get_dimensions(bw, &width, &height);
1523 
1524 			*(event->data.getdims.viewport_width) = width / bw->scale;
1525 			*(event->data.getdims.viewport_height) = height / bw->scale;
1526 			break;
1527 		}
1528 
1529 	case CONTENT_MSG_SCROLL:
1530 		{
1531 			struct rect rect = {
1532 					    .x0 = event->data.scroll.x0,
1533 					    .y0 = event->data.scroll.y0,
1534 			};
1535 
1536 			/* Content wants to be scrolled */
1537 			if (bw->current_content != c) {
1538 				break;
1539 			}
1540 
1541 			if (event->data.scroll.area) {
1542 				rect.x1 = event->data.scroll.x1;
1543 				rect.y1 = event->data.scroll.y1;
1544 			} else {
1545 				rect.x1 = event->data.scroll.x0;
1546 				rect.y1 = event->data.scroll.y0;
1547 			}
1548 			browser_window_set_scroll(bw, &rect);
1549 
1550 			break;
1551 		}
1552 
1553 	case CONTENT_MSG_DRAGSAVE:
1554 		{
1555 			/* Content wants drag save of a content */
1556 			struct browser_window *root = browser_window_get_root(bw);
1557 			hlcache_handle *save = event->data.dragsave.content;
1558 
1559 			if (save == NULL) {
1560 				save = c;
1561 			}
1562 
1563 			switch(event->data.dragsave.type) {
1564 			case CONTENT_SAVE_ORIG:
1565 				guit->window->drag_save_object(root->window,
1566 							       save,
1567 							       GUI_SAVE_OBJECT_ORIG);
1568 				break;
1569 
1570 			case CONTENT_SAVE_NATIVE:
1571 				guit->window->drag_save_object(root->window,
1572 							       save,
1573 							       GUI_SAVE_OBJECT_NATIVE);
1574 				break;
1575 
1576 			case CONTENT_SAVE_COMPLETE:
1577 				guit->window->drag_save_object(root->window,
1578 							       save,
1579 							       GUI_SAVE_COMPLETE);
1580 				break;
1581 
1582 			case CONTENT_SAVE_SOURCE:
1583 				guit->window->drag_save_object(root->window,
1584 							       save,
1585 							       GUI_SAVE_SOURCE);
1586 				break;
1587 			}
1588 		}
1589 		break;
1590 
1591 	case CONTENT_MSG_SAVELINK:
1592 		{
1593 			/* Content wants a link to be saved */
1594 			struct browser_window *root = browser_window_get_root(bw);
1595 			guit->window->save_link(root->window,
1596 						event->data.savelink.url,
1597 						event->data.savelink.title);
1598 		}
1599 		break;
1600 
1601 	case CONTENT_MSG_POINTER:
1602 		/* Content wants to have specific mouse pointer */
1603 		browser_window_set_pointer(bw, event->data.pointer);
1604 		break;
1605 
1606 	case CONTENT_MSG_DRAG:
1607 		{
1608 			browser_drag_type bdt = DRAGGING_NONE;
1609 
1610 			switch (event->data.drag.type) {
1611 			case CONTENT_DRAG_NONE:
1612 				bdt = DRAGGING_NONE;
1613 				break;
1614 			case CONTENT_DRAG_SCROLL:
1615 				bdt = DRAGGING_CONTENT_SCROLLBAR;
1616 				break;
1617 			case CONTENT_DRAG_SELECTION:
1618 				bdt = DRAGGING_SELECTION;
1619 				break;
1620 			}
1621 			browser_window_set_drag_type(bw, bdt, event->data.drag.rect);
1622 		}
1623 		break;
1624 
1625 	case CONTENT_MSG_CARET:
1626 		switch (event->data.caret.type) {
1627 		case CONTENT_CARET_REMOVE:
1628 			browser_window_remove_caret(bw, false);
1629 			break;
1630 		case CONTENT_CARET_HIDE:
1631 			browser_window_remove_caret(bw, true);
1632 			break;
1633 		case CONTENT_CARET_SET_POS:
1634 			browser_window_place_caret(bw,
1635 						   event->data.caret.pos.x,
1636 						   event->data.caret.pos.y,
1637 						   event->data.caret.pos.height,
1638 						   event->data.caret.pos.clip);
1639 			break;
1640 		}
1641 		break;
1642 
1643 	case CONTENT_MSG_SELECTION:
1644 		browser_window_set_selection(bw,
1645 					     event->data.selection.selection,
1646 					     event->data.selection.read_only);
1647 		break;
1648 
1649 	case CONTENT_MSG_SELECTMENU:
1650 		if (event->data.select_menu.gadget->type == GADGET_SELECT) {
1651 			struct browser_window *root =
1652 				browser_window_get_root(bw);
1653 			guit->window->create_form_select_menu(root->window,
1654 							      event->data.select_menu.gadget);
1655 		}
1656 
1657 		break;
1658 
1659 	case CONTENT_MSG_GADGETCLICK:
1660 		if (event->data.gadget_click.gadget->type == GADGET_FILE) {
1661 			struct browser_window *root =
1662 				browser_window_get_root(bw);
1663 			guit->window->file_gadget_open(root->window, c,
1664 						       event->data.gadget_click.gadget);
1665 		}
1666 
1667 		break;
1668 
1669 
1670 	case CONTENT_MSG_TEXTSEARCH:
1671 		switch (event->data.textsearch.type) {
1672 		case CONTENT_TEXTSEARCH_FIND:
1673 			guit->search->hourglass(event->data.textsearch.state,
1674 						event->data.textsearch.ctx);
1675 			break;
1676 
1677 		case CONTENT_TEXTSEARCH_MATCH:
1678 			guit->search->status(event->data.textsearch.state,
1679 					     event->data.textsearch.ctx);
1680 			break;
1681 
1682 		case CONTENT_TEXTSEARCH_BACK:
1683 			guit->search->back_state(event->data.textsearch.state,
1684 						 event->data.textsearch.ctx);
1685 			break;
1686 
1687 		case CONTENT_TEXTSEARCH_FORWARD:
1688 			guit->search->forward_state(event->data.textsearch.state,
1689 						    event->data.textsearch.ctx);
1690 			break;
1691 
1692 		case CONTENT_TEXTSEARCH_RECENT:
1693 			guit->search->add_recent(event->data.textsearch.string,
1694 						 event->data.textsearch.ctx);
1695 
1696 			break;
1697 		}
1698 		break;
1699 
1700 	default:
1701 		break;
1702 	}
1703 
1704 	return res;
1705 }
1706 
1707 
1708 /**
1709  * internal scheduled reformat callback.
1710  *
1711  * scheduled reformat callback to allow reformats from unthreaded context.
1712  *
1713  * \param vbw The browser window to be reformatted
1714  */
scheduled_reformat(void * vbw)1715 static void scheduled_reformat(void *vbw)
1716 {
1717 	struct browser_window *bw = vbw;
1718 	int width;
1719 	int height;
1720 	nserror res;
1721 
1722 	res = guit->window->get_dimensions(bw->window, &width, &height);
1723 	if (res == NSERROR_OK) {
1724 		browser_window_reformat(bw, false, width, height);
1725 	}
1726 }
1727 
1728 
1729 /**
1730  * Release all memory associated with a browser window.
1731  *
1732  * \param bw browser window
1733  */
browser_window_destroy_internal(struct browser_window * bw)1734 static void browser_window_destroy_internal(struct browser_window *bw)
1735 {
1736 	assert(bw);
1737 
1738 	NSLOG(netsurf, INFO, "Destroying window");
1739 
1740 	if (bw->children != NULL || bw->iframes != NULL) {
1741 		browser_window_destroy_children(bw);
1742 	}
1743 
1744 	/* Destroy scrollbars */
1745 	if (bw->scroll_x != NULL) {
1746 		scrollbar_destroy(bw->scroll_x);
1747 	}
1748 
1749 	if (bw->scroll_y != NULL) {
1750 		scrollbar_destroy(bw->scroll_y);
1751 	}
1752 
1753 	/* clear any pending callbacks */
1754 	guit->misc->schedule(-1, browser_window_refresh, bw);
1755 	NSLOG(netsurf, INFO,
1756 	      "Clearing reformat schedule for browser window %p", bw);
1757 	guit->misc->schedule(-1, scheduled_reformat, bw);
1758 
1759 	/* If this brower window is not the root window, and has focus, unset
1760 	 * the root browser window's focus pointer. */
1761 	if (!bw->window) {
1762 		struct browser_window *top = browser_window_get_root(bw);
1763 
1764 		if (top->focus == bw)
1765 			top->focus = top;
1766 
1767 		if (top->selection.bw == bw) {
1768 			browser_window_set_selection(top, false, false);
1769 		}
1770 	}
1771 
1772 	/* Destruction order is important: we must ensure that the frontend
1773 	 * destroys any window(s) associated with this browser window before
1774 	 * we attempt any destructive cleanup.
1775 	 */
1776 
1777 	if (bw->window) {
1778 		/* Only the root window has a GUI window */
1779 		guit->window->destroy(bw->window);
1780 	}
1781 
1782 	if (bw->loading_content != NULL) {
1783 		hlcache_handle_abort(bw->loading_content);
1784 		hlcache_handle_release(bw->loading_content);
1785 		bw->loading_content = NULL;
1786 	}
1787 
1788 	if (bw->current_content != NULL) {
1789 		content_close(bw->current_content);
1790 		hlcache_handle_release(bw->current_content);
1791 		bw->current_content = NULL;
1792 	}
1793 
1794 	if (bw->favicon.loading != NULL) {
1795 		hlcache_handle_abort(bw->favicon.loading);
1796 		hlcache_handle_release(bw->favicon.loading);
1797 		bw->favicon.loading = NULL;
1798 	}
1799 
1800 	if (bw->favicon.current != NULL) {
1801 		content_close(bw->favicon.current);
1802 		hlcache_handle_release(bw->favicon.current);
1803 		bw->favicon.current = NULL;
1804 	}
1805 
1806 	if (bw->box != NULL) {
1807 		bw->box->iframe = NULL;
1808 		bw->box = NULL;
1809 	}
1810 
1811 	if (bw->jsheap != NULL) {
1812 		js_destroyheap(bw->jsheap);
1813 		bw->jsheap = NULL;
1814 	}
1815 
1816 	/* These simply free memory, so are safe here */
1817 
1818 	if (bw->frag_id != NULL) {
1819 		lwc_string_unref(bw->frag_id);
1820 	}
1821 
1822 	browser_window_history_destroy(bw);
1823 
1824 	cert_chain_free(bw->current_cert_chain);
1825 	cert_chain_free(bw->loading_cert_chain);
1826 	bw->current_cert_chain = NULL;
1827 	bw->loading_cert_chain = NULL;
1828 
1829 	free(bw->name);
1830 	free(bw->status.text);
1831 	bw->status.text = NULL;
1832 	browser_window__free_fetch_parameters(&bw->current_parameters);
1833 	browser_window__free_fetch_parameters(&bw->loading_parameters);
1834 	NSLOG(netsurf, INFO, "Status text cache match:miss %d:%d",
1835 	      bw->status.match, bw->status.miss);
1836 }
1837 
1838 
1839 /**
1840  * scroll to a fragment if present
1841  *
1842  * \param bw browser window
1843  * \return true if the scroll was sucessful
1844  */
frag_scroll(struct browser_window * bw)1845 static bool frag_scroll(struct browser_window *bw)
1846 {
1847 	struct rect rect;
1848 
1849 	if (bw->frag_id == NULL) {
1850 		return false;
1851 	}
1852 
1853 	if (!html_get_id_offset(bw->current_content,
1854 				bw->frag_id,
1855 				&rect.x0,
1856 				&rect.y0)) {
1857 		return false;
1858 	}
1859 
1860 	rect.x1 = rect.x0;
1861 	rect.y1 = rect.y0;
1862 	if (browser_window_set_scroll(bw, &rect) == NSERROR_OK) {
1863 		if (bw->current_content != NULL &&
1864 		    bw->history != NULL &&
1865 		    bw->history->current != NULL) {
1866 			browser_window_history_update(bw, bw->current_content);
1867 		}
1868 		return true;
1869 	}
1870 	return false;
1871 }
1872 
1873 
1874 /**
1875  * Set browser window scale.
1876  *
1877  * \param bw Browser window.
1878  * \param absolute scale value.
1879  * \return NSERROR_OK on success else error code
1880  */
1881 static nserror
browser_window_set_scale_internal(struct browser_window * bw,float scale)1882 browser_window_set_scale_internal(struct browser_window *bw, float scale)
1883 {
1884 	int i;
1885 	nserror res = NSERROR_OK;
1886 
1887 	/* do not apply tiny changes in scale */
1888 	if (fabs(bw->scale - scale) < 0.0001)
1889 		return res;
1890 
1891 	bw->scale = scale;
1892 
1893 	if (bw->current_content != NULL) {
1894 		if (content_can_reformat(bw->current_content) == false) {
1895 			browser_window_update(bw, false);
1896 		} else {
1897 			res = browser_window_schedule_reformat(bw);
1898 		}
1899 	}
1900 
1901 	/* scale frames */
1902 	for (i = 0; i < (bw->cols * bw->rows); i++) {
1903 		res = browser_window_set_scale_internal(&bw->children[i], scale);
1904 	}
1905 
1906 	/* sale iframes */
1907 	for (i = 0; i < bw->iframe_count; i++) {
1908 		res = browser_window_set_scale_internal(&bw->iframes[i], scale);
1909 	}
1910 
1911 	return res;
1912 }
1913 
1914 
1915 /**
1916  * Find browser window.
1917  *
1918  * \param bw Browser window.
1919  * \param target Name of target.
1920  * \param depth Depth to scan.
1921  * \param page The browser window page.
1922  * \param rdepth The rdepth.
1923  * \param bw_target the output browser window.
1924  */
1925 static void
browser_window_find_target_internal(struct browser_window * bw,const char * target,int depth,struct browser_window * page,int * rdepth,struct browser_window ** bw_target)1926 browser_window_find_target_internal(struct browser_window *bw,
1927 				    const char *target,
1928 				    int depth,
1929 				    struct browser_window *page,
1930 				    int *rdepth,
1931 				    struct browser_window **bw_target)
1932 {
1933 	int i;
1934 
1935 	if ((bw->name) && (!strcasecmp(bw->name, target))) {
1936 		if ((bw == page) || (depth > *rdepth)) {
1937 			*rdepth = depth;
1938 			*bw_target = bw;
1939 		}
1940 	}
1941 
1942 	if ((!bw->children) && (!bw->iframes))
1943 		return;
1944 
1945 	depth++;
1946 
1947 	if (bw->children != NULL) {
1948 		for (i = 0; i < (bw->cols * bw->rows); i++) {
1949 			if ((bw->children[i].name) &&
1950 			    (!strcasecmp(bw->children[i].name,
1951 					 target))) {
1952 				if ((page == &bw->children[i]) ||
1953 				    (depth > *rdepth)) {
1954 					*rdepth = depth;
1955 					*bw_target = &bw->children[i];
1956 				}
1957 			}
1958 			if (bw->children[i].children)
1959 				browser_window_find_target_internal(
1960 							    &bw->children[i],
1961 							    target, depth, page,
1962 							    rdepth, bw_target);
1963 		}
1964 	}
1965 
1966 	if (bw->iframes != NULL) {
1967 		for (i = 0; i < bw->iframe_count; i++) {
1968 			browser_window_find_target_internal(&bw->iframes[i],
1969 							    target,
1970 							    depth,
1971 							    page,
1972 							    rdepth,
1973 							    bw_target);
1974 		}
1975 	}
1976 }
1977 
1978 
1979 /**
1980  * Handles the end of a drag operation in a browser window.
1981  *
1982  * \param  bw	  browser window
1983  * \param  mouse  state of mouse buttons and modifier keys
1984  * \param  x	  coordinate of mouse
1985  * \param  y	  coordinate of mouse
1986  *
1987  * \todo Remove this function, once these things are associated with content,
1988  *       rather than bw.
1989  */
1990 static void
browser_window_mouse_drag_end(struct browser_window * bw,browser_mouse_state mouse,int x,int y)1991 browser_window_mouse_drag_end(struct browser_window *bw,
1992 			      browser_mouse_state mouse,
1993 			      int x, int y)
1994 {
1995 	int scr_x, scr_y;
1996 
1997 	switch (bw->drag.type) {
1998 	case DRAGGING_SELECTION:
1999 	case DRAGGING_OTHER:
2000 	case DRAGGING_CONTENT_SCROLLBAR:
2001 		/* Drag handled by content handler */
2002 		break;
2003 
2004 	case DRAGGING_SCR_X:
2005 
2006 		browser_window_get_scrollbar_pos(bw, true, &scr_x, &scr_y);
2007 
2008 		scr_x = x - scr_x - scrollbar_get_offset(bw->scroll_x);
2009 		scr_y = y - scr_y - scrollbar_get_offset(bw->scroll_y);
2010 
2011 		scrollbar_mouse_drag_end(bw->scroll_x, mouse, scr_x, scr_y);
2012 
2013 		bw->drag.type = DRAGGING_NONE;
2014 		break;
2015 
2016 	case DRAGGING_SCR_Y:
2017 
2018 		browser_window_get_scrollbar_pos(bw, false, &scr_x, &scr_y);
2019 
2020 		scr_x = x - scr_x - scrollbar_get_offset(bw->scroll_x);
2021 		scr_y = y - scr_y - scrollbar_get_offset(bw->scroll_y);
2022 
2023 		scrollbar_mouse_drag_end(bw->scroll_y, mouse, scr_x, scr_y);
2024 
2025 		bw->drag.type = DRAGGING_NONE;
2026 		break;
2027 
2028 	default:
2029 		browser_window_set_drag_type(bw, DRAGGING_NONE, NULL);
2030 		break;
2031 	}
2032 }
2033 
2034 /**
2035  * Process mouse click event
2036  *
2037  * \param bw The browsing context receiving the event
2038  * \param mouse The mouse event state
2039  * \param x The scaled x co-ordinate of the event
2040  * \param y The scaled y co-ordinate of the event
2041  */
2042 static void
browser_window_mouse_click_internal(struct browser_window * bw,browser_mouse_state mouse,int x,int y)2043 browser_window_mouse_click_internal(struct browser_window *bw,
2044 				    browser_mouse_state mouse,
2045 				    int x, int y)
2046 {
2047 	hlcache_handle *c = bw->current_content;
2048 	const char *status = NULL;
2049 	browser_pointer_shape pointer = BROWSER_POINTER_DEFAULT;
2050 
2051 	if (bw->children) {
2052 		/* Browser window has children (frames) */
2053 		struct browser_window *child;
2054 		int cur_child;
2055 		int children = bw->rows * bw->cols;
2056 
2057 		for (cur_child = 0; cur_child < children; cur_child++) {
2058 
2059 			child = &bw->children[cur_child];
2060 
2061 			if ((x < child->x) ||
2062 			    (y < child->y) ||
2063 			    (child->x + child->width < x) ||
2064 			    (child->y + child->height < y)) {
2065 				/* Click not in this child */
2066 				continue;
2067 			}
2068 
2069 			/* It's this child that contains the click; pass it
2070 			 * on to child. */
2071 			browser_window_mouse_click_internal(
2072 				child,
2073 				mouse,
2074 				x - child->x + scrollbar_get_offset(child->scroll_x),
2075 				y - child->y + scrollbar_get_offset(child->scroll_y));
2076 
2077 			/* Mouse action was for this child, we're done */
2078 			return;
2079 		}
2080 
2081 		return;
2082 	}
2083 
2084 	if (!c)
2085 		return;
2086 
2087 	if (bw->scroll_x != NULL) {
2088 		int scr_x, scr_y;
2089 		browser_window_get_scrollbar_pos(bw, true, &scr_x, &scr_y);
2090 		scr_x = x - scr_x - scrollbar_get_offset(bw->scroll_x);
2091 		scr_y = y - scr_y - scrollbar_get_offset(bw->scroll_y);
2092 
2093 		if (scr_x > 0 && scr_x < get_horz_scrollbar_len(bw) &&
2094 		    scr_y > 0 && scr_y < SCROLLBAR_WIDTH) {
2095 			status = scrollbar_mouse_status_to_message(
2096 					   scrollbar_mouse_action(
2097 						  bw->scroll_x, mouse,
2098 						  scr_x, scr_y));
2099 			pointer = BROWSER_POINTER_DEFAULT;
2100 
2101 			if (status != NULL)
2102 				browser_window_set_status(bw, status);
2103 
2104 			browser_window_set_pointer(bw, pointer);
2105 			return;
2106 		}
2107 	}
2108 
2109 	if (bw->scroll_y != NULL) {
2110 		int scr_x, scr_y;
2111 		browser_window_get_scrollbar_pos(bw, false, &scr_x, &scr_y);
2112 		scr_x = x - scr_x - scrollbar_get_offset(bw->scroll_x);
2113 		scr_y = y - scr_y - scrollbar_get_offset(bw->scroll_y);
2114 
2115 		if (scr_y > 0 && scr_y < get_vert_scrollbar_len(bw) &&
2116 		    scr_x > 0 && scr_x < SCROLLBAR_WIDTH) {
2117 			status = scrollbar_mouse_status_to_message(
2118 						scrollbar_mouse_action(
2119 							bw->scroll_y,
2120 							mouse,
2121 							scr_x,
2122 							scr_y));
2123 			pointer = BROWSER_POINTER_DEFAULT;
2124 
2125 			if (status != NULL) {
2126 				browser_window_set_status(bw, status);
2127 			}
2128 
2129 			browser_window_set_pointer(bw, pointer);
2130 			return;
2131 		}
2132 	}
2133 
2134 	switch (content_get_type(c)) {
2135 	case CONTENT_HTML:
2136 	case CONTENT_TEXTPLAIN:
2137 		{
2138 			/* Give bw focus */
2139 			struct browser_window *root_bw = browser_window_get_root(bw);
2140 			if (bw != root_bw->focus) {
2141 				browser_window_remove_caret(bw, false);
2142 				browser_window_set_selection(bw, false, true);
2143 				root_bw->focus = bw;
2144 			}
2145 
2146 			/* Pass mouse action to content */
2147 			content_mouse_action(c, bw, mouse, x, y);
2148 		}
2149 		break;
2150 	default:
2151 		if (mouse & BROWSER_MOUSE_MOD_2) {
2152 			if (mouse & BROWSER_MOUSE_DRAG_2) {
2153 				guit->window->drag_save_object(bw->window, c,
2154 							       GUI_SAVE_OBJECT_NATIVE);
2155 			} else if (mouse & BROWSER_MOUSE_DRAG_1) {
2156 				guit->window->drag_save_object(bw->window, c,
2157 							       GUI_SAVE_OBJECT_ORIG);
2158 			}
2159 		} else if (mouse & (BROWSER_MOUSE_DRAG_1 |
2160 				    BROWSER_MOUSE_DRAG_2)) {
2161 			browser_window_page_drag_start(bw, x, y);
2162 			browser_window_set_pointer(bw, BROWSER_POINTER_MOVE);
2163 		}
2164 		break;
2165 	}
2166 }
2167 
2168 
2169 /**
2170  * Process mouse movement event
2171  *
2172  * \param bw The browsing context receiving the event
2173  * \param mouse The mouse event state
2174  * \param x The scaled x co-ordinate of the event
2175  * \param y The scaled y co-ordinate of the event
2176  */
2177 static void
browser_window_mouse_track_internal(struct browser_window * bw,browser_mouse_state mouse,int x,int y)2178 browser_window_mouse_track_internal(struct browser_window *bw,
2179 				    browser_mouse_state mouse,
2180 				    int x, int y)
2181 {
2182 	hlcache_handle *c = bw->current_content;
2183 	const char *status = NULL;
2184 	browser_pointer_shape pointer = BROWSER_POINTER_DEFAULT;
2185 
2186 	if (bw->window != NULL && bw->drag.window && bw != bw->drag.window) {
2187 		/* This is the root browser window and there's an active drag
2188 		 * in a sub window.
2189 		 * Pass the mouse action straight on to that bw. */
2190 		struct browser_window *drag_bw = bw->drag.window;
2191 		int off_x = 0;
2192 		int off_y = 0;
2193 
2194 		browser_window_get_position(drag_bw, true, &off_x, &off_y);
2195 
2196 		if (drag_bw->browser_window_type == BROWSER_WINDOW_FRAME) {
2197 			browser_window_mouse_track_internal(drag_bw,
2198 							    mouse,
2199 							    x - off_x,
2200 							    y - off_y);
2201 
2202 		} else if (drag_bw->browser_window_type == BROWSER_WINDOW_IFRAME) {
2203 			browser_window_mouse_track_internal(drag_bw, mouse,
2204 							    x - off_x / bw->scale,
2205 							    y - off_y / bw->scale);
2206 		}
2207 		return;
2208 	}
2209 
2210 	if (bw->children) {
2211 		/* Browser window has children (frames) */
2212 		struct browser_window *child;
2213 		int cur_child;
2214 		int children = bw->rows * bw->cols;
2215 
2216 		for (cur_child = 0; cur_child < children; cur_child++) {
2217 
2218 			child = &bw->children[cur_child];
2219 
2220 			if ((x < child->x) ||
2221 			    (y < child->y) ||
2222 			    (child->x + child->width < x) ||
2223 			    (child->y + child->height < y)) {
2224 				/* Click not in this child */
2225 				continue;
2226 			}
2227 
2228 			/* It's this child that contains the mouse; pass
2229 			 * mouse action on to child */
2230 			browser_window_mouse_track_internal(
2231 				child,
2232 				mouse,
2233 				x - child->x + scrollbar_get_offset(child->scroll_x),
2234 				y - child->y + scrollbar_get_offset(child->scroll_y));
2235 
2236 			/* Mouse action was for this child, we're done */
2237 			return;
2238 		}
2239 
2240 		/* Odd if we reached here, but nothing else can use the click
2241 		 * when there are children. */
2242 		return;
2243 	}
2244 
2245 	if (c == NULL && bw->drag.type != DRAGGING_FRAME) {
2246 		return;
2247 	}
2248 
2249 	if (bw->drag.type != DRAGGING_NONE && !mouse) {
2250 		browser_window_mouse_drag_end(bw, mouse, x, y);
2251 	}
2252 
2253 	/* Browser window's horizontal scrollbar */
2254 	if (bw->scroll_x != NULL && bw->drag.type != DRAGGING_SCR_Y) {
2255 		int scr_x, scr_y;
2256 		browser_window_get_scrollbar_pos(bw, true, &scr_x, &scr_y);
2257 		scr_x = x - scr_x - scrollbar_get_offset(bw->scroll_x);
2258 		scr_y = y - scr_y - scrollbar_get_offset(bw->scroll_y);
2259 
2260 		if ((bw->drag.type == DRAGGING_SCR_X) ||
2261 		    (scr_x > 0 &&
2262 		     scr_x < get_horz_scrollbar_len(bw) &&
2263 		     scr_y > 0 &&
2264 		     scr_y < SCROLLBAR_WIDTH &&
2265 		     bw->drag.type == DRAGGING_NONE)) {
2266 			/* Start a scrollbar drag, or continue existing drag */
2267 			status = scrollbar_mouse_status_to_message(
2268 					scrollbar_mouse_action(bw->scroll_x,
2269 							       mouse,
2270 							       scr_x,
2271 							       scr_y));
2272 			pointer = BROWSER_POINTER_DEFAULT;
2273 
2274 			if (status != NULL) {
2275 				browser_window_set_status(bw, status);
2276 			}
2277 
2278 			browser_window_set_pointer(bw, pointer);
2279 			return;
2280 		}
2281 	}
2282 
2283 	/* Browser window's vertical scrollbar */
2284 	if (bw->scroll_y != NULL) {
2285 		int scr_x, scr_y;
2286 		browser_window_get_scrollbar_pos(bw, false, &scr_x, &scr_y);
2287 		scr_x = x - scr_x - scrollbar_get_offset(bw->scroll_x);
2288 		scr_y = y - scr_y - scrollbar_get_offset(bw->scroll_y);
2289 
2290 		if ((bw->drag.type == DRAGGING_SCR_Y) ||
2291 		    (scr_y > 0 &&
2292 		     scr_y < get_vert_scrollbar_len(bw) &&
2293 		     scr_x > 0 &&
2294 		     scr_x < SCROLLBAR_WIDTH &&
2295 		     bw->drag.type == DRAGGING_NONE)) {
2296 			/* Start a scrollbar drag, or continue existing drag */
2297 			status = scrollbar_mouse_status_to_message(
2298 					scrollbar_mouse_action(bw->scroll_y,
2299 							       mouse,
2300 							       scr_x,
2301 							       scr_y));
2302 			pointer = BROWSER_POINTER_DEFAULT;
2303 
2304 			if (status != NULL) {
2305 				browser_window_set_status(bw, status);
2306 			}
2307 
2308 			browser_window_set_pointer(bw, pointer);
2309 			return;
2310 		}
2311 	}
2312 
2313 	if (bw->drag.type == DRAGGING_FRAME) {
2314 		browser_window_resize_frame(bw, bw->x + x, bw->y + y);
2315 	} else if (bw->drag.type == DRAGGING_PAGE_SCROLL) {
2316 		/* mouse movement since drag started */
2317 		struct rect rect;
2318 
2319 		rect.x0 = bw->drag.start_x - x;
2320 		rect.y0 = bw->drag.start_y - y;
2321 
2322 		/* new scroll offsets */
2323 		rect.x0 += bw->drag.start_scroll_x;
2324 		rect.y0 += bw->drag.start_scroll_y;
2325 
2326 		bw->drag.start_scroll_x = rect.x1 = rect.x0;
2327 		bw->drag.start_scroll_y = rect.y1 = rect.y0;
2328 
2329 		browser_window_set_scroll(bw, &rect);
2330 	} else {
2331 		assert(c != NULL);
2332 		content_mouse_track(c, bw, mouse, x, y);
2333 	}
2334 }
2335 
2336 
2337 /**
2338  * perform a scroll operation at a given coordinate
2339  *
2340  * \param bw The browsing context receiving the event
2341  * \param x The scaled x co-ordinate of the event
2342  * \param y The scaled y co-ordinate of the event
2343  */
2344 static bool
browser_window_scroll_at_point_internal(struct browser_window * bw,int x,int y,int scrx,int scry)2345 browser_window_scroll_at_point_internal(struct browser_window *bw,
2346 					int x, int y,
2347 					int scrx, int scry)
2348 {
2349 	bool handled_scroll = false;
2350 	assert(bw != NULL);
2351 
2352 	/* Handle (i)frame scroll offset (core-managed browser windows only) */
2353 	x += scrollbar_get_offset(bw->scroll_x);
2354 	y += scrollbar_get_offset(bw->scroll_y);
2355 
2356 	if (bw->children) {
2357 		/* Browser window has children, so pass request on to
2358 		 * appropriate child */
2359 		struct browser_window *bwc;
2360 		int cur_child;
2361 		int children = bw->rows * bw->cols;
2362 
2363 		/* Loop through all children of bw */
2364 		for (cur_child = 0; cur_child < children; cur_child++) {
2365 			/* Set current child */
2366 			bwc = &bw->children[cur_child];
2367 
2368 			/* Skip this frame if (x, y) coord lies outside */
2369 			if (x < bwc->x || bwc->x + bwc->width < x ||
2370 			    y < bwc->y || bwc->y + bwc->height < y)
2371 				continue;
2372 
2373 			/* Pass request into this child */
2374 			return browser_window_scroll_at_point_internal(
2375 								bwc,
2376 								(x - bwc->x),
2377 								(y - bwc->y),
2378 								scrx, scry);
2379 		}
2380 	}
2381 
2382 	/* Try to scroll any current content */
2383 	if (bw->current_content != NULL &&
2384 	    content_scroll_at_point(bw->current_content, x, y, scrx, scry) == true) {
2385 		/* Scroll handled by current content */
2386 		return true;
2387 	}
2388 
2389 	/* Try to scroll this window, if scroll not already handled */
2390 	if (handled_scroll == false) {
2391 		if (bw->scroll_y && scrollbar_scroll(bw->scroll_y, scry)) {
2392 			handled_scroll = true;
2393 		}
2394 
2395 		if (bw->scroll_x && scrollbar_scroll(bw->scroll_x, scrx)) {
2396 			handled_scroll = true;
2397 		}
2398 	}
2399 
2400 	return handled_scroll;
2401 }
2402 
2403 
2404 /**
2405  * allows a dragged file to be dropped into a browser window at a position
2406  *
2407  * \param bw The browsing context receiving the event
2408  * \param x The scaled x co-ordinate of the event
2409  * \param y The scaled y co-ordinate of the event
2410  * \param file filename to be put in the widget
2411  */
2412 static bool
browser_window_drop_file_at_point_internal(struct browser_window * bw,int x,int y,char * file)2413 browser_window_drop_file_at_point_internal(struct browser_window *bw,
2414 					   int x, int y,
2415 					   char *file)
2416 {
2417 	assert(bw != NULL);
2418 
2419 	/* Handle (i)frame scroll offset (core-managed browser windows only) */
2420 	x += scrollbar_get_offset(bw->scroll_x);
2421 	y += scrollbar_get_offset(bw->scroll_y);
2422 
2423 	if (bw->children) {
2424 		/* Browser window has children, so pass request on to
2425 		 * appropriate child */
2426 		struct browser_window *bwc;
2427 		int cur_child;
2428 		int children = bw->rows * bw->cols;
2429 
2430 		/* Loop through all children of bw */
2431 		for (cur_child = 0; cur_child < children; cur_child++) {
2432 			/* Set current child */
2433 			bwc = &bw->children[cur_child];
2434 
2435 			/* Skip this frame if (x, y) coord lies outside */
2436 			if (x < bwc->x || bwc->x + bwc->width < x ||
2437 			    y < bwc->y || bwc->y + bwc->height < y)
2438 				continue;
2439 
2440 			/* Pass request into this child */
2441 			return browser_window_drop_file_at_point_internal(
2442 								bwc,
2443 								(x - bwc->x),
2444 								(y - bwc->y),
2445 								file);
2446 		}
2447 	}
2448 
2449 	/* Pass file drop on to any content */
2450 	if (bw->current_content != NULL) {
2451 		return content_drop_file_at_point(bw->current_content,
2452 						  x, y, file);
2453 	}
2454 
2455 	return false;
2456 }
2457 
2458 
2459 /**
2460  * Check if this is an internal navigation URL.
2461  *
2462  * This safely checks if the given url is an internal navigation even
2463  *  for urls with no scheme or path.
2464  *
2465  * \param url The URL to check
2466  * \return true if an internal navigation url else false
2467  */
2468 static bool
is_internal_navigate_url(nsurl * url)2469 is_internal_navigate_url(nsurl *url)
2470 {
2471 	bool is_internal = false;
2472 	lwc_string *scheme, *path;
2473 
2474 	scheme = nsurl_get_component(url, NSURL_SCHEME);
2475 	if (scheme != NULL) {
2476 		path = nsurl_get_component(url, NSURL_PATH);
2477 		if (path != NULL) {
2478 			if (scheme == corestring_lwc_about) {
2479 				if (path == corestring_lwc_query_auth) {
2480 					is_internal = true;
2481 				} else if (path == corestring_lwc_query_ssl) {
2482 					is_internal = true;
2483 				} else if (path == corestring_lwc_query_timeout) {
2484 					is_internal = true;
2485 				} else if (path == corestring_lwc_query_fetcherror) {
2486 					is_internal = true;
2487 				}
2488 			}
2489 			lwc_string_unref(path);
2490 		}
2491 		lwc_string_unref(scheme);
2492 	}
2493 	return is_internal;
2494 }
2495 
2496 
2497 /* exported interface, documented in netsurf/browser_window.h */
2498 nserror
browser_window_get_name(struct browser_window * bw,const char ** out_name)2499 browser_window_get_name(struct browser_window *bw, const char **out_name)
2500 {
2501 	assert(bw != NULL);
2502 
2503 	*out_name = bw->name;
2504 
2505 	return NSERROR_OK;
2506 }
2507 
2508 
2509 /* exported interface, documented in netsurf/browser_window.h */
2510 nserror
browser_window_set_name(struct browser_window * bw,const char * name)2511 browser_window_set_name(struct browser_window *bw, const char *name)
2512 {
2513 	char *nname = NULL;
2514 
2515 	assert(bw != NULL);
2516 
2517 	if (name != NULL) {
2518 		nname = strdup(name);
2519 		if (nname == NULL) {
2520 			return NSERROR_NOMEM;
2521 		}
2522 	}
2523 
2524 	if (bw->name != NULL) {
2525 		free(bw->name);
2526 	}
2527 
2528 	bw->name = nname;
2529 
2530 	return NSERROR_OK;
2531 }
2532 
2533 
2534 /* exported interface, documented in netsurf/browser_window.h */
2535 bool
browser_window_redraw(struct browser_window * bw,int x,int y,const struct rect * clip,const struct redraw_context * ctx)2536 browser_window_redraw(struct browser_window *bw,
2537 		      int x, int y,
2538 		      const struct rect *clip,
2539 		      const struct redraw_context *ctx)
2540 {
2541 	struct redraw_context new_ctx = *ctx;
2542 	int width = 0;
2543 	int height = 0;
2544 	bool plot_ok = true;
2545 	content_type content_type;
2546 	struct content_redraw_data data;
2547 	struct rect content_clip;
2548 	nserror res;
2549 
2550 	x /= bw->scale;
2551 	y /= bw->scale;
2552 
2553 	if (bw == NULL) {
2554 		NSLOG(netsurf, INFO, "NULL browser window");
2555 		return false;
2556 	}
2557 
2558 	if ((bw->current_content == NULL) &&
2559 	    (bw->children == NULL)) {
2560 		/* Browser window has no content, render blank fill */
2561 		ctx->plot->clip(ctx, clip);
2562 		return (ctx->plot->rectangle(ctx, plot_style_fill_white, clip) == NSERROR_OK);
2563 	}
2564 
2565 	/* Browser window has content OR children (frames) */
2566 	if ((bw->window != NULL) &&
2567 	    (ctx->plot->option_knockout)) {
2568 		/* Root browser window: start knockout */
2569 		knockout_plot_start(ctx, &new_ctx);
2570 	}
2571 
2572 	new_ctx.plot->clip(ctx, clip);
2573 
2574 	/* Handle redraw of any browser window children */
2575 	if (bw->children) {
2576 		struct browser_window *child;
2577 		int cur_child;
2578 		int children = bw->rows * bw->cols;
2579 
2580 		if (bw->window != NULL) {
2581 			/* Root browser window; start with blank fill */
2582 			plot_ok &= (new_ctx.plot->rectangle(ctx,
2583 							    plot_style_fill_white,
2584 							    clip) == NSERROR_OK);
2585 		}
2586 
2587 		/* Loop through all children of bw */
2588 		for (cur_child = 0; cur_child < children; cur_child++) {
2589 			/* Set current child */
2590 			child = &bw->children[cur_child];
2591 
2592 			/* Get frame edge box in global coordinates */
2593 			content_clip.x0 = (x + child->x) * child->scale;
2594 			content_clip.y0 = (y + child->y) * child->scale;
2595 			content_clip.x1 = content_clip.x0 +
2596 				child->width * child->scale;
2597 			content_clip.y1 = content_clip.y0 +
2598 				child->height * child->scale;
2599 
2600 			/* Intersect it with clip rectangle */
2601 			if (content_clip.x0 < clip->x0)
2602 				content_clip.x0 = clip->x0;
2603 			if (content_clip.y0 < clip->y0)
2604 				content_clip.y0 = clip->y0;
2605 			if (clip->x1 < content_clip.x1)
2606 				content_clip.x1 = clip->x1;
2607 			if (clip->y1 < content_clip.y1)
2608 				content_clip.y1 = clip->y1;
2609 
2610 			/* Skip this frame if it lies outside clip rectangle */
2611 			if (content_clip.x0 >= content_clip.x1 ||
2612 			    content_clip.y0 >= content_clip.y1)
2613 				continue;
2614 
2615 			/* Redraw frame */
2616 			plot_ok &= browser_window_redraw(child,
2617 							 x + child->x,
2618 							 y + child->y,
2619 							 &content_clip,
2620 							 &new_ctx);
2621 		}
2622 
2623 		/* Nothing else to redraw for browser windows with children;
2624 		 * cleanup and return
2625 		 */
2626 		if (bw->window != NULL && ctx->plot->option_knockout) {
2627 			/* Root browser window: knockout end */
2628 			knockout_plot_end(ctx);
2629 		}
2630 
2631 		return plot_ok;
2632 	}
2633 
2634 	/* Handle browser windows with content to redraw */
2635 
2636 	content_type = content_get_type(bw->current_content);
2637 	if (content_type != CONTENT_HTML && content_type != CONTENT_TEXTPLAIN) {
2638 		/* Set render area according to scale */
2639 		width = content_get_width(bw->current_content) * bw->scale;
2640 		height = content_get_height(bw->current_content) * bw->scale;
2641 
2642 		/* Non-HTML may not fill viewport to extents, so plot white
2643 		 * background fill */
2644 		plot_ok &= (new_ctx.plot->rectangle(&new_ctx,
2645 						    plot_style_fill_white,
2646 						    clip) == NSERROR_OK);
2647 	}
2648 
2649 	/* Set up content redraw data */
2650 	data.x = x - scrollbar_get_offset(bw->scroll_x);
2651 	data.y = y - scrollbar_get_offset(bw->scroll_y);
2652 	data.width = width;
2653 	data.height = height;
2654 
2655 	data.background_colour = 0xFFFFFF;
2656 	data.scale = bw->scale;
2657 	data.repeat_x = false;
2658 	data.repeat_y = false;
2659 
2660 	content_clip = *clip;
2661 
2662 	if (!bw->window) {
2663 		int x0 = x * bw->scale;
2664 		int y0 = y * bw->scale;
2665 		int x1 = (x + bw->width - ((bw->scroll_y != NULL) ?
2666 					   SCROLLBAR_WIDTH : 0)) * bw->scale;
2667 		int y1 = (y + bw->height - ((bw->scroll_x != NULL) ?
2668 					    SCROLLBAR_WIDTH : 0)) * bw->scale;
2669 
2670 		if (content_clip.x0 < x0) content_clip.x0 = x0;
2671 		if (content_clip.y0 < y0) content_clip.y0 = y0;
2672 		if (x1 < content_clip.x1) content_clip.x1 = x1;
2673 		if (y1 < content_clip.y1) content_clip.y1 = y1;
2674 	}
2675 
2676 	/* Render the content */
2677 	plot_ok &= content_redraw(bw->current_content, &data,
2678 				  &content_clip, &new_ctx);
2679 
2680 	/* Back to full clip rect */
2681 	new_ctx.plot->clip(&new_ctx, clip);
2682 
2683 	if (!bw->window) {
2684 		/* Render scrollbars */
2685 		int off_x, off_y;
2686 		if (bw->scroll_x != NULL) {
2687 			browser_window_get_scrollbar_pos(bw, true,
2688 							 &off_x, &off_y);
2689 			res = scrollbar_redraw(bw->scroll_x,
2690 					       x + off_x, y + off_y, clip,
2691 					       bw->scale, &new_ctx);
2692 			if (res != NSERROR_OK) {
2693 				plot_ok = false;
2694 			}
2695 		}
2696 		if (bw->scroll_y != NULL) {
2697 			browser_window_get_scrollbar_pos(bw, false,
2698 							 &off_x, &off_y);
2699 			res = scrollbar_redraw(bw->scroll_y,
2700 					       x + off_x, y + off_y, clip,
2701 					       bw->scale, &new_ctx);
2702 			if (res != NSERROR_OK) {
2703 				plot_ok = false;
2704 			}
2705 		}
2706 	}
2707 
2708 	if (bw->window != NULL && ctx->plot->option_knockout) {
2709 		/* Root browser window: end knockout */
2710 		knockout_plot_end(ctx);
2711 	}
2712 
2713 	return plot_ok;
2714 }
2715 
2716 
2717 /* exported interface, documented in netsurf/browser_window.h */
browser_window_redraw_ready(struct browser_window * bw)2718 bool browser_window_redraw_ready(struct browser_window *bw)
2719 {
2720 	if (bw == NULL) {
2721 		NSLOG(netsurf, INFO, "NULL browser window");
2722 		return false;
2723 	} else if (bw->current_content != NULL) {
2724 		/* Can't render locked contents */
2725 		return !content_is_locked(bw->current_content);
2726 	}
2727 
2728 	return true;
2729 }
2730 
2731 
2732 /* exported interface, documented in browser_private.h */
browser_window_update_extent(struct browser_window * bw)2733 void browser_window_update_extent(struct browser_window *bw)
2734 {
2735 	if (bw->window != NULL) {
2736 		/* Front end window */
2737 		guit->window->event(bw->window, GW_EVENT_UPDATE_EXTENT);
2738 	} else {
2739 		/* Core-managed browser window */
2740 		browser_window_handle_scrollbars(bw);
2741 	}
2742 }
2743 
2744 
2745 /* exported interface, documented in netsurf/browser_window.h */
2746 void
browser_window_get_position(struct browser_window * bw,bool root,int * pos_x,int * pos_y)2747 browser_window_get_position(struct browser_window *bw,
2748 			    bool root,
2749 			    int *pos_x,
2750 			    int *pos_y)
2751 {
2752 	*pos_x = 0;
2753 	*pos_y = 0;
2754 
2755 	assert(bw != NULL);
2756 
2757 	while (bw) {
2758 		switch (bw->browser_window_type) {
2759 
2760 		case BROWSER_WINDOW_FRAMESET:
2761 			*pos_x += bw->x * bw->scale;
2762 			*pos_y += bw->y * bw->scale;
2763 			break;
2764 
2765 		case BROWSER_WINDOW_NORMAL:
2766 			/* There is no offset to the root browser window */
2767 			break;
2768 
2769 		case BROWSER_WINDOW_FRAME:
2770 			/* Iframe and Frame handling is identical;
2771 			 * fall though */
2772 		case BROWSER_WINDOW_IFRAME:
2773 			*pos_x += (bw->x - scrollbar_get_offset(bw->scroll_x)) *
2774 				bw->scale;
2775 			*pos_y += (bw->y - scrollbar_get_offset(bw->scroll_y)) *
2776 				bw->scale;
2777 			break;
2778 		}
2779 
2780 		bw = bw->parent;
2781 
2782 		if (!root) {
2783 			/* return if we just wanted the position in the parent
2784 			 * browser window. */
2785 			return;
2786 		}
2787 	}
2788 }
2789 
2790 
2791 /* exported interface, documented in netsurf/browser_window.h */
browser_window_set_position(struct browser_window * bw,int x,int y)2792 void browser_window_set_position(struct browser_window *bw, int x, int y)
2793 {
2794 	assert(bw != NULL);
2795 
2796 	if (bw->window == NULL) {
2797 		/* Core managed browser window */
2798 		bw->x = x;
2799 		bw->y = y;
2800 	} else {
2801 		NSLOG(netsurf, INFO,
2802 		      "Asked to set position of front end window.");
2803 		assert(0);
2804 	}
2805 }
2806 
2807 
2808 /* exported interface, documented in netsurf/browser_window.h */
2809 void
browser_window_set_drag_type(struct browser_window * bw,browser_drag_type type,const struct rect * rect)2810 browser_window_set_drag_type(struct browser_window *bw,
2811 			     browser_drag_type type,
2812 			     const struct rect *rect)
2813 {
2814 	struct browser_window *top_bw = browser_window_get_root(bw);
2815 	gui_drag_type gtype;
2816 
2817 	bw->drag.type = type;
2818 
2819 	if (type == DRAGGING_NONE) {
2820 		top_bw->drag.window = NULL;
2821 	} else {
2822 		top_bw->drag.window = bw;
2823 
2824 		switch (type) {
2825 		case DRAGGING_SELECTION:
2826 			/** \todo tell front end */
2827 			return;
2828 		case DRAGGING_SCR_X:
2829 		case DRAGGING_SCR_Y:
2830 		case DRAGGING_CONTENT_SCROLLBAR:
2831 			gtype = GDRAGGING_SCROLLBAR;
2832 			break;
2833 		default:
2834 			gtype = GDRAGGING_OTHER;
2835 			break;
2836 		}
2837 
2838 		guit->window->drag_start(top_bw->window, gtype, rect);
2839 	}
2840 }
2841 
2842 
2843 /* exported interface, documented in netsurf/browser_window.h */
browser_window_get_drag_type(struct browser_window * bw)2844 browser_drag_type browser_window_get_drag_type(struct browser_window *bw)
2845 {
2846 	return bw->drag.type;
2847 }
2848 
2849 
2850 /* exported interface, documented in netsurf/browser_window.h */
browser_window_get_root(struct browser_window * bw)2851 struct browser_window * browser_window_get_root(struct browser_window *bw)
2852 {
2853 	while (bw && bw->parent) {
2854 		bw = bw->parent;
2855 	}
2856 	return bw;
2857 }
2858 
2859 
2860 /* exported interface, documented in netsurf/browser_window.h */
browser_window_get_editor_flags(struct browser_window * bw)2861 browser_editor_flags browser_window_get_editor_flags(struct browser_window *bw)
2862 {
2863 	browser_editor_flags ed_flags = BW_EDITOR_NONE;
2864 	assert(bw->window);
2865 	assert(bw->parent == NULL);
2866 
2867 	if (bw->selection.bw != NULL) {
2868 		ed_flags |= BW_EDITOR_CAN_COPY;
2869 
2870 		if (!bw->selection.read_only)
2871 			ed_flags |= BW_EDITOR_CAN_CUT;
2872 	}
2873 
2874 	if (bw->can_edit)
2875 		ed_flags |= BW_EDITOR_CAN_PASTE;
2876 
2877 	return ed_flags;
2878 }
2879 
2880 
2881 /* exported interface, documented in netsurf/browser_window.h */
browser_window_can_select(struct browser_window * bw)2882 bool browser_window_can_select(struct browser_window *bw)
2883 {
2884 	if (bw == NULL || bw->current_content == NULL)
2885 		return false;
2886 
2887 	/* TODO: We shouldn't have to know about specific content types
2888 	 *       here.  There should be a content_is_selectable() call. */
2889 	if (content_get_type(bw->current_content) != CONTENT_HTML &&
2890 	    content_get_type(bw->current_content) !=
2891 	    CONTENT_TEXTPLAIN)
2892 		return false;
2893 
2894 	return true;
2895 }
2896 
2897 
2898 /* exported interface, documented in netsurf/browser_window.h */
browser_window_get_selection(struct browser_window * bw)2899 char * browser_window_get_selection(struct browser_window *bw)
2900 {
2901 	assert(bw->window);
2902 	assert(bw->parent == NULL);
2903 
2904 	if (bw->selection.bw == NULL ||
2905 	    bw->selection.bw->current_content == NULL)
2906 		return NULL;
2907 
2908 	return content_get_selection(bw->selection.bw->current_content);
2909 }
2910 
2911 
2912 /* exported interface, documented in netsurf/browser_window.h */
browser_window_can_search(struct browser_window * bw)2913 bool browser_window_can_search(struct browser_window *bw)
2914 {
2915 	if (bw == NULL || bw->current_content == NULL)
2916 		return false;
2917 
2918 	/** \todo We shouldn't have to know about specific content
2919 	 * types here. There should be a content_is_searchable() call.
2920 	 */
2921 	if ((content_get_type(bw->current_content) != CONTENT_HTML) &&
2922 	    (content_get_type(bw->current_content) != CONTENT_TEXTPLAIN)) {
2923 		return false;
2924 	}
2925 
2926 	return true;
2927 }
2928 
2929 
2930 /* exported interface, documented in netsurf/browser_window.h */
browser_window_is_frameset(struct browser_window * bw)2931 bool browser_window_is_frameset(struct browser_window *bw)
2932 {
2933 	return (bw->children != NULL);
2934 }
2935 
2936 
2937 /* exported interface, documented in netsurf/browser_window.h */
2938 nserror
browser_window_get_scrollbar_type(struct browser_window * bw,browser_scrolling * h,browser_scrolling * v)2939 browser_window_get_scrollbar_type(struct browser_window *bw,
2940 				  browser_scrolling *h,
2941 				  browser_scrolling *v)
2942 {
2943 	*h = bw->scrolling;
2944 	*v = bw->scrolling;
2945 
2946 	return NSERROR_OK;
2947 }
2948 
2949 
2950 /* exported interface, documented in netsurf/browser_window.h */
2951 nserror
browser_window_get_features(struct browser_window * bw,int x,int y,struct browser_window_features * data)2952 browser_window_get_features(struct browser_window *bw,
2953 			    int x, int y,
2954 			    struct browser_window_features *data)
2955 {
2956 	/* clear the features structure to empty values */
2957 	data->link = NULL;
2958 	data->object = NULL;
2959 	data->main = NULL;
2960 	data->form_features = CTX_FORM_NONE;
2961 
2962 	return browser_window__get_contextual_content(bw,
2963 						      x / bw->scale,
2964 						      y / bw->scale,
2965 						      data);
2966 }
2967 
2968 
2969 /* exported interface, documented in netsurf/browser_window.h */
2970 bool
browser_window_scroll_at_point(struct browser_window * bw,int x,int y,int scrx,int scry)2971 browser_window_scroll_at_point(struct browser_window *bw,
2972 			       int x, int y,
2973 			       int scrx, int scry)
2974 {
2975 	return browser_window_scroll_at_point_internal(bw,
2976 						       x / bw->scale,
2977 						       y / bw->scale,
2978 						       scrx,
2979 						       scry);
2980 }
2981 
2982 
2983 /* exported interface, documented in netsurf/browser_window.h */
2984 bool
browser_window_drop_file_at_point(struct browser_window * bw,int x,int y,char * file)2985 browser_window_drop_file_at_point(struct browser_window *bw,
2986 				  int x, int y,
2987 				  char *file)
2988 {
2989 	return browser_window_drop_file_at_point_internal(bw,
2990 							  x / bw->scale,
2991 							  y / bw->scale,
2992 							  file);
2993 }
2994 
2995 
2996 /* exported interface, documented in netsurf/browser_window.h */
2997 void
browser_window_set_gadget_filename(struct browser_window * bw,struct form_control * gadget,const char * fn)2998 browser_window_set_gadget_filename(struct browser_window *bw,
2999 				   struct form_control *gadget,
3000 				   const char *fn)
3001 {
3002 	html_set_file_gadget_filename(bw->current_content, gadget, fn);
3003 }
3004 
3005 
3006 /* exported interface, documented in netsurf/browser_window.h */
3007 nserror
browser_window_debug_dump(struct browser_window * bw,FILE * f,enum content_debug op)3008 browser_window_debug_dump(struct browser_window *bw,
3009 			  FILE *f,
3010 			  enum content_debug op)
3011 {
3012 	if (bw->current_content != NULL) {
3013 		return content_debug_dump(bw->current_content, f, op);
3014 	}
3015 	return NSERROR_OK;
3016 }
3017 
3018 
3019 /* exported interface, documented in netsurf/browser_window.h */
browser_window_debug(struct browser_window * bw,enum content_debug op)3020 nserror browser_window_debug(struct browser_window *bw, enum content_debug op)
3021 {
3022 	if (bw->current_content != NULL) {
3023 		return content_debug(bw->current_content, op);
3024 	}
3025 	return NSERROR_OK;
3026 }
3027 
3028 
3029 /* exported interface, documented in netsurf/browser_window.h */
3030 nserror
browser_window_create(enum browser_window_create_flags flags,nsurl * url,nsurl * referrer,struct browser_window * existing,struct browser_window ** bw)3031 browser_window_create(enum browser_window_create_flags flags,
3032 		      nsurl *url,
3033 		      nsurl *referrer,
3034 		      struct browser_window *existing,
3035 		      struct browser_window **bw)
3036 {
3037 	gui_window_create_flags gw_flags = GW_CREATE_NONE;
3038 	struct browser_window *ret;
3039 	nserror err;
3040 
3041 	/* Check parameters */
3042 	if (flags & BW_CREATE_CLONE) {
3043 		if (existing == NULL) {
3044 			assert(0 && "Failed: No existing window provided.");
3045 			return NSERROR_BAD_PARAMETER;
3046 		}
3047 	}
3048 
3049 	if (!(flags & BW_CREATE_HISTORY)) {
3050 		if (!(flags & BW_CREATE_CLONE) || existing == NULL) {
3051 			assert(0 && "Failed: Must have existing for history.");
3052 			return NSERROR_BAD_PARAMETER;
3053 		}
3054 	}
3055 
3056 	ret = calloc(1, sizeof(struct browser_window));
3057 	if (ret == NULL) {
3058 		return NSERROR_NOMEM;
3059 	}
3060 
3061 	/* Initialise common parts */
3062 	err = browser_window_initialise_common(flags, ret, existing);
3063 	if (err != NSERROR_OK) {
3064 		browser_window_destroy(ret);
3065 		return err;
3066 	}
3067 
3068 	/* window characteristics */
3069 	ret->browser_window_type = BROWSER_WINDOW_NORMAL;
3070 	ret->scrolling = BW_SCROLLING_YES;
3071 	ret->border = true;
3072 	ret->no_resize = true;
3073 	ret->focus = ret;
3074 
3075 	/* initialise last action with creation time */
3076 	nsu_getmonotonic_ms(&ret->last_action);
3077 
3078 	/* The existing gui_window is on the top-level existing
3079 	 * browser_window. */
3080 	existing = browser_window_get_root(existing);
3081 
3082 	/* Set up gui_window creation flags */
3083 	if (flags & BW_CREATE_TAB)
3084 		gw_flags |= GW_CREATE_TAB;
3085 	if (flags & BW_CREATE_CLONE)
3086 		gw_flags |= GW_CREATE_CLONE;
3087 	if (flags & BW_CREATE_FOREGROUND)
3088 		gw_flags |= GW_CREATE_FOREGROUND;
3089 	if (flags & BW_CREATE_FOCUS_LOCATION)
3090 		gw_flags |= GW_CREATE_FOCUS_LOCATION;
3091 
3092 	ret->window = guit->window->create(ret,
3093 					   (existing != NULL) ? existing->window : NULL,
3094 					   gw_flags);
3095 
3096 	if (ret->window == NULL) {
3097 		browser_window_destroy(ret);
3098 		return NSERROR_BAD_PARAMETER;
3099 	}
3100 
3101 	if (url != NULL) {
3102 		enum browser_window_nav_flags nav_flags;
3103 		nav_flags = BW_NAVIGATE_NO_TERMINAL_HISTORY_UPDATE;
3104 		if (flags & BW_CREATE_UNVERIFIABLE) {
3105 			nav_flags |= BW_NAVIGATE_UNVERIFIABLE;
3106 		}
3107 		if (flags & BW_CREATE_HISTORY) {
3108 			nav_flags |= BW_NAVIGATE_HISTORY;
3109 		}
3110 		browser_window_navigate(ret,
3111 					url,
3112 					referrer,
3113 					nav_flags,
3114 					NULL,
3115 					NULL,
3116 					NULL);
3117 	}
3118 
3119 	if (bw != NULL) {
3120 		*bw = ret;
3121 	}
3122 
3123 	return NSERROR_OK;
3124 }
3125 
3126 
3127 /* exported internal interface, documented in desktop/browser_private.h */
3128 nserror
browser_window_initialise_common(enum browser_window_create_flags flags,struct browser_window * bw,const struct browser_window * existing)3129 browser_window_initialise_common(enum browser_window_create_flags flags,
3130 				 struct browser_window *bw,
3131 				 const struct browser_window *existing)
3132 {
3133 	nserror err;
3134 	assert(bw);
3135 
3136 	/* new javascript context for each window/(i)frame */
3137 	err = js_newheap(nsoption_int(script_timeout), &bw->jsheap);
3138 	if (err != NSERROR_OK)
3139 		return err;
3140 
3141 	if (flags & BW_CREATE_CLONE) {
3142 		assert(existing != NULL);
3143 
3144 		/* clone history */
3145 		err = browser_window_history_clone(existing, bw);
3146 
3147 		/* copy the scale */
3148 		bw->scale = existing->scale;
3149 	} else {
3150 		/* create history */
3151 		err = browser_window_history_create(bw);
3152 
3153 		/* default scale */
3154 		bw->scale = (float) nsoption_int(scale) / 100.0;
3155 	}
3156 
3157 	if (err != NSERROR_OK)
3158 		return err;
3159 
3160 	/* window characteristics */
3161 	bw->refresh_interval = -1;
3162 
3163 	bw->drag.type = DRAGGING_NONE;
3164 
3165 	bw->scroll_x = NULL;
3166 	bw->scroll_y = NULL;
3167 
3168 	bw->focus = NULL;
3169 
3170 	/* initialise status text cache */
3171 	bw->status.text = NULL;
3172 	bw->status.text_len = 0;
3173 	bw->status.match = 0;
3174 	bw->status.miss = 0;
3175 
3176 	return NSERROR_OK;
3177 }
3178 
3179 
3180 /* exported interface, documented in netsurf/browser_window.h */
browser_window_destroy(struct browser_window * bw)3181 void browser_window_destroy(struct browser_window *bw)
3182 {
3183 	/* can't destoy child windows on their own */
3184 	assert(!bw->parent);
3185 
3186 	/* destroy */
3187 	browser_window_destroy_internal(bw);
3188 	free(bw);
3189 }
3190 
3191 
3192 /* exported interface, documented in netsurf/browser_window.h */
browser_window_refresh_url_bar(struct browser_window * bw)3193 nserror browser_window_refresh_url_bar(struct browser_window *bw)
3194 {
3195 	nserror ret;
3196 	nsurl *display_url, *url;
3197 
3198 	assert(bw);
3199 
3200 	if (bw->parent != NULL) {
3201 		/* Not root window; don't set a URL in GUI URL bar */
3202 		return NSERROR_OK;
3203 	}
3204 
3205 	if (bw->current_content == NULL) {
3206 		/* no content so return about:blank */
3207 		ret = browser_window_refresh_url_bar_internal(bw,
3208 						corestring_nsurl_about_blank);
3209 	} else if (bw->throbbing && bw->loading_parameters.url != NULL) {
3210 		/* Throbbing and we have loading parameters, use those */
3211 		url = bw->loading_parameters.url;
3212 		ret = browser_window_refresh_url_bar_internal(bw, url);
3213 	} else if (bw->frag_id == NULL) {
3214 		if (bw->internal_nav) {
3215 			url = bw->loading_parameters.url;
3216 		} else {
3217 			url = hlcache_handle_get_url(bw->current_content);
3218 		}
3219 		ret = browser_window_refresh_url_bar_internal(bw, url);
3220 	} else {
3221 		/* Combine URL and Fragment */
3222 		if (bw->internal_nav) {
3223 			url = bw->loading_parameters.url;
3224 		} else {
3225 			url = hlcache_handle_get_url(bw->current_content);
3226 		}
3227 		ret = nsurl_refragment(
3228 				       url,
3229 				       bw->frag_id, &display_url);
3230 		if (ret == NSERROR_OK) {
3231 			ret = browser_window_refresh_url_bar_internal(bw,
3232 								display_url);
3233 			nsurl_unref(display_url);
3234 		}
3235 	}
3236 
3237 	return ret;
3238 }
3239 
3240 
3241 /* exported interface documented in netsurf/browser_window.h */
3242 nserror
browser_window_navigate(struct browser_window * bw,nsurl * url,nsurl * referrer,enum browser_window_nav_flags flags,char * post_urlenc,struct fetch_multipart_data * post_multipart,hlcache_handle * parent)3243 browser_window_navigate(struct browser_window *bw,
3244 			nsurl *url,
3245 			nsurl *referrer,
3246 			enum browser_window_nav_flags flags,
3247 			char *post_urlenc,
3248 			struct fetch_multipart_data *post_multipart,
3249 			hlcache_handle *parent)
3250 {
3251 	int depth = 0;
3252 	struct browser_window *cur;
3253 	uint32_t fetch_flags = 0;
3254 	bool fetch_is_post = (post_urlenc != NULL || post_multipart != NULL);
3255 	llcache_post_data post;
3256 	hlcache_child_context child;
3257 	nserror error;
3258 	bool is_internal = false;
3259 	struct browser_fetch_parameters params, *pass_params = NULL;
3260 
3261 	assert(bw);
3262 	assert(url);
3263 
3264 	NSLOG(netsurf, INFO, "bw %p, url %s", bw, nsurl_access(url));
3265 
3266 	/*
3267 	 * determine if navigation is internal url, if so, we do not
3268 	 * do certain things during the load.
3269 	 */
3270 	is_internal = is_internal_navigate_url(url);
3271 
3272 	if (is_internal &&
3273 	    !(flags & BW_NAVIGATE_INTERNAL)) {
3274 		/* Internal navigation detected, but flag not set, only allow
3275 		 * this is there's a fetch multipart
3276 		 */
3277 		if (post_multipart == NULL) {
3278 			return NSERROR_NEED_DATA;
3279 		}
3280 		/* It *is* internal, set it as such */
3281 		flags |= BW_NAVIGATE_INTERNAL | BW_NAVIGATE_HISTORY;
3282 		/* If we were previously internal, don't update again */
3283 		if (bw->internal_nav) {
3284 			flags |= BW_NAVIGATE_NO_TERMINAL_HISTORY_UPDATE;
3285 		}
3286 	}
3287 
3288 	/* If we're navigating and we have a history entry and a content
3289 	 * then update the history entry before we navigate to save our
3290 	 * current state.  However since history navigation pre-moves
3291 	 * the history state, we ensure that we only do this if we've not
3292 	 * been suppressed.  In the suppressed case, the history code
3293 	 * updates the history itself before navigating.
3294 	 */
3295 	if (bw->current_content != NULL &&
3296 	    bw->history != NULL &&
3297 	    bw->history->current != NULL &&
3298 	    !is_internal &&
3299 	    !(flags & BW_NAVIGATE_NO_TERMINAL_HISTORY_UPDATE)) {
3300 		browser_window_history_update(bw, bw->current_content);
3301 	}
3302 
3303 	/* don't allow massively nested framesets */
3304 	for (cur = bw; cur->parent; cur = cur->parent) {
3305 		depth++;
3306 	}
3307 	if (depth > FRAME_DEPTH) {
3308 		NSLOG(netsurf, INFO, "frame depth too high.");
3309 		return NSERROR_FRAME_DEPTH;
3310 	}
3311 
3312 	/* Set up retrieval parameters */
3313 	if (!(flags & BW_NAVIGATE_UNVERIFIABLE)) {
3314 		fetch_flags |= LLCACHE_RETRIEVE_VERIFIABLE;
3315 	}
3316 
3317 	if (post_multipart != NULL) {
3318 		post.type = LLCACHE_POST_MULTIPART;
3319 		post.data.multipart = post_multipart;
3320 	} else if (post_urlenc != NULL) {
3321 		post.type = LLCACHE_POST_URL_ENCODED;
3322 		post.data.urlenc = post_urlenc;
3323 	}
3324 
3325 	child.charset = content_get_encoding(parent, CONTENT_ENCODING_NORMAL);
3326 	if ((parent != NULL) && (content_get_type(parent) == CONTENT_HTML)) {
3327 		child.quirks = content_get_quirks(parent);
3328 	} else {
3329 		child.quirks = false;
3330 	}
3331 
3332 	url = nsurl_ref(url);
3333 
3334 	if (referrer != NULL) {
3335 		referrer = nsurl_ref(referrer);
3336 	}
3337 
3338 	/* Get download out of the way */
3339 	if ((flags & BW_NAVIGATE_DOWNLOAD) != 0) {
3340 		error = browser_window_download(bw,
3341 						url,
3342 						referrer,
3343 						fetch_flags,
3344 						fetch_is_post,
3345 						&post);
3346 		nsurl_unref(url);
3347 		if (referrer != NULL) {
3348 			nsurl_unref(referrer);
3349 		}
3350 		return error;
3351 	}
3352 
3353 	if (bw->frag_id != NULL) {
3354 		lwc_string_unref(bw->frag_id);
3355 	}
3356 	bw->frag_id = NULL;
3357 
3358 	if (nsurl_has_component(url, NSURL_FRAGMENT)) {
3359 		bool same_url = false;
3360 
3361 		bw->frag_id = nsurl_get_component(url, NSURL_FRAGMENT);
3362 
3363 		/* Compare new URL with existing one (ignoring fragments) */
3364 		if ((bw->current_content != NULL) &&
3365 		    (hlcache_handle_get_url(bw->current_content) != NULL)) {
3366 			same_url = nsurl_compare(
3367 				url,
3368 				hlcache_handle_get_url(bw->current_content),
3369 				NSURL_COMPLETE);
3370 		}
3371 
3372 		/* if we're simply moving to another ID on the same page,
3373 		 * don't bother to fetch, just update the window.
3374 		 */
3375 		if ((same_url) &&
3376 		    (fetch_is_post == false) &&
3377 		    (nsurl_has_component(url, NSURL_QUERY) == false)) {
3378 			nsurl_unref(url);
3379 
3380 			if (referrer != NULL) {
3381 				nsurl_unref(referrer);
3382 			}
3383 
3384 			if ((flags & BW_NAVIGATE_HISTORY) != 0) {
3385 				browser_window_history_add(bw,
3386 							   bw->current_content,
3387 							   bw->frag_id);
3388 			}
3389 
3390 			browser_window_update(bw, false);
3391 
3392 			if (bw->current_content != NULL) {
3393 				browser_window_refresh_url_bar(bw);
3394 			}
3395 			return NSERROR_OK;
3396 		}
3397 	}
3398 
3399 	browser_window_stop(bw);
3400 	browser_window_remove_caret(bw, false);
3401 	browser_window_destroy_children(bw);
3402 
3403 	/* Set up the fetch parameters */
3404 	memset(&params, 0, sizeof(params));
3405 
3406 	params.url = nsurl_ref(url);
3407 
3408 	if (referrer != NULL) {
3409 		params.referrer = nsurl_ref(referrer);
3410 	}
3411 
3412 	params.flags = flags;
3413 
3414 	if (post_urlenc != NULL) {
3415 		params.post_urlenc = strdup(post_urlenc);
3416 	}
3417 
3418 	if (post_multipart != NULL) {
3419 		params.post_multipart = fetch_multipart_data_clone(post_multipart);
3420 	}
3421 
3422 	if (parent != NULL) {
3423 		params.parent_charset = strdup(child.charset);
3424 		params.parent_quirks = child.quirks;
3425 	}
3426 
3427 	bw->internal_nav = is_internal;
3428 
3429 	if (is_internal) {
3430 		pass_params = &params;
3431 	} else {
3432 		/* At this point, we're navigating, so store the fetch parameters */
3433 		browser_window__free_fetch_parameters(&bw->loading_parameters);
3434 		memcpy(&bw->loading_parameters, &params, sizeof(params));
3435 		memset(&params, 0, sizeof(params));
3436 		pass_params = &bw->loading_parameters;
3437 	}
3438 
3439 	error = browser_window__navigate_internal(bw, pass_params);
3440 
3441 	nsurl_unref(url);
3442 
3443 	if (referrer != NULL) {
3444 		nsurl_unref(referrer);
3445 	}
3446 
3447 	if (is_internal) {
3448 		browser_window__free_fetch_parameters(&params);
3449 	}
3450 
3451 	return error;
3452 }
3453 
3454 
3455 /**
3456  * Internal navigation handler for normal fetches
3457  */
3458 static nserror
navigate_internal_real(struct browser_window * bw,struct browser_fetch_parameters * params)3459 navigate_internal_real(struct browser_window *bw,
3460 		       struct browser_fetch_parameters *params)
3461 {
3462 	uint32_t fetch_flags = 0;
3463 	bool fetch_is_post;
3464 	llcache_post_data post;
3465 	hlcache_child_context child;
3466 	nserror res;
3467 	hlcache_handle *c;
3468 
3469 	NSLOG(netsurf, INFO, "Loading '%s'", nsurl_access(params->url));
3470 
3471 	fetch_is_post = (params->post_urlenc != NULL || params->post_multipart != NULL);
3472 
3473 	/* Clear SSL info for load */
3474 	cert_chain_free(bw->loading_cert_chain);
3475 	bw->loading_cert_chain = NULL;
3476 
3477 	/* Set up retrieval parameters */
3478 	if (!(params->flags & BW_NAVIGATE_UNVERIFIABLE)) {
3479 		fetch_flags |= LLCACHE_RETRIEVE_VERIFIABLE;
3480 	}
3481 
3482 	if (params->post_multipart != NULL) {
3483 		post.type = LLCACHE_POST_MULTIPART;
3484 		post.data.multipart = params->post_multipart;
3485 	} else if (params->post_urlenc != NULL) {
3486 		post.type = LLCACHE_POST_URL_ENCODED;
3487 		post.data.urlenc = params->post_urlenc;
3488 	}
3489 
3490 	if (params->parent_charset != NULL) {
3491 		child.charset = params->parent_charset;
3492 		child.quirks = params->parent_quirks;
3493 	}
3494 
3495 	browser_window_set_status(bw, messages_get("Loading"));
3496 	bw->history_add = (params->flags & BW_NAVIGATE_HISTORY);
3497 
3498 	/* Verifiable fetches may trigger a download */
3499 	if (!(params->flags & BW_NAVIGATE_UNVERIFIABLE)) {
3500 		fetch_flags |= HLCACHE_RETRIEVE_MAY_DOWNLOAD;
3501 	}
3502 
3503 	res = hlcache_handle_retrieve(params->url,
3504 				      fetch_flags | HLCACHE_RETRIEVE_SNIFF_TYPE,
3505 				      params->referrer,
3506 				      fetch_is_post ? &post : NULL,
3507 				      browser_window_callback,
3508 				      bw,
3509 				      params->parent_charset != NULL ? &child : NULL,
3510 				      CONTENT_ANY,
3511 				      &c);
3512 
3513 	switch (res) {
3514 	case NSERROR_OK:
3515 		bw->loading_content = c;
3516 		browser_window_start_throbber(bw);
3517 		if (bw->window != NULL) {
3518 			guit->window->set_icon(bw->window, NULL);
3519 		}
3520 		if (bw->internal_nav == false) {
3521 			res = browser_window_refresh_url_bar_internal(bw,
3522 								      params->url);
3523 		}
3524 		break;
3525 
3526 	case NSERROR_NO_FETCH_HANDLER: /* no handler for this type */
3527 		/** \todo does this always try and download even
3528 		 * unverifiable content?
3529 		 */
3530 		res = guit->misc->launch_url(params->url);
3531 		break;
3532 
3533 	default: /* report error to user */
3534 		browser_window_set_status(bw, messages_get_errorcode(res));
3535 		break;
3536 
3537 	}
3538 
3539 	/* Record time */
3540 	nsu_getmonotonic_ms(&bw->last_action);
3541 
3542 	return res;
3543 }
3544 
3545 
3546 /**
3547  * Internal navigation handler for the authentication query handler
3548  *
3549  * If the parameters indicate we're processing a *response* from the handler
3550  * then we deal with that, otherwise we pass it on to the about: handler
3551  */
3552 static nserror
navigate_internal_query_auth(struct browser_window * bw,struct browser_fetch_parameters * params)3553 navigate_internal_query_auth(struct browser_window *bw,
3554 			     struct browser_fetch_parameters *params)
3555 {
3556 	char *userpass = NULL;
3557 	const char *username, *password, *realm, *siteurl;
3558 	nsurl *sitensurl;
3559 	nserror res;
3560 	bool is_login = false, is_cancel = false;
3561 
3562 	assert(params->post_multipart != NULL);
3563 
3564 	is_login = fetch_multipart_data_find(params->post_multipart, "login") != NULL;
3565 	is_cancel = fetch_multipart_data_find(params->post_multipart, "cancel") != NULL;
3566 
3567 	if (!(is_login || is_cancel)) {
3568 		/* This is a request, so pass it on */
3569 		return navigate_internal_real(bw, params);
3570 	}
3571 
3572 	if (is_cancel) {
3573 		/* We're processing a cancel, do a rough-and-ready nav to
3574 		 * about:blank
3575 		 */
3576 		browser_window__free_fetch_parameters(&bw->loading_parameters);
3577 		bw->loading_parameters.url = nsurl_ref(corestring_nsurl_about_blank);
3578 		bw->loading_parameters.flags = BW_NAVIGATE_NO_TERMINAL_HISTORY_UPDATE | BW_NAVIGATE_INTERNAL;
3579 		bw->internal_nav = true;
3580 		return browser_window__navigate_internal(bw, &bw->loading_parameters);
3581 	}
3582 
3583 	/* We're processing a "login" attempt from the form */
3584 
3585 	/* Retrieve the data */
3586 	username = fetch_multipart_data_find(params->post_multipart, "username");
3587 	password = fetch_multipart_data_find(params->post_multipart, "password");
3588 	realm = fetch_multipart_data_find(params->post_multipart, "realm");
3589 	siteurl = fetch_multipart_data_find(params->post_multipart, "siteurl");
3590 
3591 	if (username == NULL || password == NULL ||
3592 	    realm == NULL || siteurl == NULL) {
3593 		/* Bad inputs, simply fail */
3594 		return NSERROR_INVALID;
3595 	}
3596 
3597 	/* Parse the URL */
3598 	res = nsurl_create(siteurl, &sitensurl);
3599 	if (res != NSERROR_OK) {
3600 		return res;
3601 	}
3602 
3603 	/* Construct the username/password */
3604 	res = browser_window__build_userpass(username, password, &userpass);
3605 	if (res != NSERROR_OK) {
3606 		nsurl_unref(sitensurl);
3607 		return res;
3608 	}
3609 
3610 	/* And let urldb know */
3611 	urldb_set_auth_details(sitensurl, realm, userpass);
3612 
3613 	/* Clean up */
3614 	free(userpass);
3615 	nsurl_unref(sitensurl);
3616 
3617 	/* Finally navigate to the original loading parameters */
3618 	bw->internal_nav = false;
3619 	return navigate_internal_real(bw, &bw->loading_parameters);
3620 }
3621 
3622 
3623 /**
3624  * Internal navigation handler for the SSL/privacy query page.
3625  *
3626  * If the parameters indicate we're processing a *response* from the handler
3627  * then we deal with that, otherwise we pass it on to the about: handler
3628  */
3629 static nserror
navigate_internal_query_ssl(struct browser_window * bw,struct browser_fetch_parameters * params)3630 navigate_internal_query_ssl(struct browser_window *bw,
3631 			    struct browser_fetch_parameters *params)
3632 {
3633 	bool is_proceed = false, is_back = false;
3634 	const char *siteurl = NULL;
3635 	nsurl *siteurl_ns;
3636 
3637 	assert(params->post_multipart != NULL);
3638 
3639 	is_proceed = fetch_multipart_data_find(params->post_multipart, "proceed") != NULL;
3640 	is_back = fetch_multipart_data_find(params->post_multipart, "back") != NULL;
3641 	siteurl = fetch_multipart_data_find(params->post_multipart, "siteurl");
3642 
3643 	if (!(is_proceed || is_back) || siteurl == NULL) {
3644 		/* This is a request, so pass it on */
3645 		return navigate_internal_real(bw, params);
3646 	}
3647 
3648 	if (nsurl_create(siteurl, &siteurl_ns) != NSERROR_OK) {
3649 		NSLOG(netsurf, ERROR, "Unable to reset ssl loading parameters");
3650 	} else {
3651 		/* In order that we may proceed, replace the loading parameters */
3652 		nsurl_unref(bw->loading_parameters.url);
3653 		bw->loading_parameters.url = siteurl_ns;
3654 	}
3655 
3656 	return browser_window__handle_ssl_query_response(is_proceed, bw);
3657 }
3658 
3659 
3660 /**
3661  * Internal navigation handler for the timeout query page.
3662  *
3663  * If the parameters indicate we're processing a *response* from the handler
3664  * then we deal with that, otherwise we pass it on to the about: handler
3665  */
3666 static nserror
navigate_internal_query_timeout(struct browser_window * bw,struct browser_fetch_parameters * params)3667 navigate_internal_query_timeout(struct browser_window *bw,
3668 				struct browser_fetch_parameters *params)
3669 {
3670 	bool is_retry = false, is_back = false;
3671 
3672 	NSLOG(netsurf, INFO, "bw:%p params:%p", bw, params);
3673 
3674 	assert(params->post_multipart != NULL);
3675 
3676 	is_retry = fetch_multipart_data_find(params->post_multipart, "retry") != NULL;
3677 	is_back = fetch_multipart_data_find(params->post_multipart, "back") != NULL;
3678 
3679 	if (is_back) {
3680 		/* do a rough-and-ready nav to the old 'current'
3681 		 * parameters, with any post data stripped away
3682 		 */
3683 		return browser_window__reload_current_parameters(bw);
3684 	}
3685 
3686 	if (is_retry) {
3687 		/* Finally navigate to the original loading parameters */
3688 		bw->internal_nav = false;
3689 		return navigate_internal_real(bw, &bw->loading_parameters);
3690 	}
3691 
3692 	return navigate_internal_real(bw, params);
3693 }
3694 
3695 
3696 /**
3697  * Internal navigation handler for the fetch error query page.
3698  *
3699  * If the parameters indicate we're processing a *response* from the handler
3700  * then we deal with that, otherwise we pass it on to the about: handler
3701  */
3702 static nserror
navigate_internal_query_fetcherror(struct browser_window * bw,struct browser_fetch_parameters * params)3703 navigate_internal_query_fetcherror(struct browser_window *bw,
3704 				   struct browser_fetch_parameters *params)
3705 {
3706 	bool is_retry = false, is_back = false;
3707 
3708 	NSLOG(netsurf, INFO, "bw:%p params:%p", bw, params);
3709 
3710 	assert(params->post_multipart != NULL);
3711 
3712 	is_retry = fetch_multipart_data_find(params->post_multipart, "retry") != NULL;
3713 	is_back = fetch_multipart_data_find(params->post_multipart, "back") != NULL;
3714 
3715 	if (is_back) {
3716 		/* do a rough-and-ready nav to the old 'current'
3717 		 * parameters, with any post data stripped away
3718 		 */
3719 		return browser_window__reload_current_parameters(bw);
3720 	}
3721 
3722 	if (is_retry) {
3723 		/* Finally navigate to the original loading parameters */
3724 		bw->internal_nav = false;
3725 		return navigate_internal_real(bw, &bw->loading_parameters);
3726 	}
3727 
3728 	return navigate_internal_real(bw, params);
3729 }
3730 
3731 
3732 /**
3733  * dispatch to internal query handlers or normal navigation
3734  *
3735  * Here we determine if we're navigating to an internal query URI and
3736  * if so, what we need to do about it.
3737  *
3738  * \note these check must match those in is_internal_navigate_url()
3739  *
3740  * If we're not, then we just move on to the real navigate.
3741  */
3742 nserror
browser_window__navigate_internal(struct browser_window * bw,struct browser_fetch_parameters * params)3743 browser_window__navigate_internal(struct browser_window *bw,
3744 				  struct browser_fetch_parameters *params)
3745 {
3746 	lwc_string *scheme, *path;
3747 
3748 	/* All our special URIs are in the about: scheme */
3749 	scheme = nsurl_get_component(params->url, NSURL_SCHEME);
3750 	if (scheme != corestring_lwc_about) {
3751 		lwc_string_unref(scheme);
3752 		goto normal_fetch;
3753 	}
3754 	lwc_string_unref(scheme);
3755 
3756 	/* Is it the auth query handler? */
3757 	path = nsurl_get_component(params->url, NSURL_PATH);
3758 	if (path == corestring_lwc_query_auth) {
3759 		lwc_string_unref(path);
3760 		return navigate_internal_query_auth(bw, params);
3761 	}
3762 	if (path == corestring_lwc_query_ssl) {
3763 		lwc_string_unref(path);
3764 		return navigate_internal_query_ssl(bw, params);
3765 	}
3766 	if (path == corestring_lwc_query_timeout) {
3767 		lwc_string_unref(path);
3768 		return navigate_internal_query_timeout(bw, params);
3769 	}
3770 	if (path == corestring_lwc_query_fetcherror) {
3771 		lwc_string_unref(path);
3772 		return navigate_internal_query_fetcherror(bw, params);
3773 	}
3774 	if (path != NULL) {
3775 		lwc_string_unref(path);
3776 	}
3777 
3778 	/* Fall through to a normal about: fetch */
3779 
3780  normal_fetch:
3781 	return navigate_internal_real(bw, params);
3782 }
3783 
3784 
3785 /* Exported interface, documented in netsurf/browser_window.h */
browser_window_up_available(struct browser_window * bw)3786 bool browser_window_up_available(struct browser_window *bw)
3787 {
3788 	bool result = false;
3789 
3790 	if (bw != NULL && bw->current_content != NULL) {
3791 		nsurl *parent;
3792 		nserror	err;
3793 		err = nsurl_parent(hlcache_handle_get_url(bw->current_content),
3794 				   &parent);
3795 		if (err == NSERROR_OK) {
3796 			result = nsurl_compare(hlcache_handle_get_url(
3797 						      bw->current_content),
3798 					       parent,
3799 					       NSURL_COMPLETE) == false;
3800 			nsurl_unref(parent);
3801 		}
3802 	}
3803 
3804 	return result;
3805 }
3806 
3807 
3808 /* Exported interface, documented in netsurf/browser_window.h */
browser_window_navigate_up(struct browser_window * bw,bool new_window)3809 nserror browser_window_navigate_up(struct browser_window *bw, bool new_window)
3810 {
3811 	nsurl *current, *parent;
3812 	nserror err;
3813 
3814 	if (bw == NULL)
3815 		return NSERROR_BAD_PARAMETER;
3816 
3817 	current = browser_window_access_url(bw);
3818 
3819 	err = nsurl_parent(current, &parent);
3820 	if (err != NSERROR_OK) {
3821 		return err;
3822 	}
3823 
3824 	if (nsurl_compare(current, parent, NSURL_COMPLETE) == true) {
3825 		/* Can't go up to parent from here */
3826 		nsurl_unref(parent);
3827 		return NSERROR_OK;
3828 	}
3829 
3830 	if (new_window) {
3831 		err = browser_window_create(BW_CREATE_CLONE,
3832 					    parent, NULL, bw, NULL);
3833 	} else {
3834 		err = browser_window_navigate(bw, parent, NULL,
3835 					      BW_NAVIGATE_HISTORY,
3836 					      NULL, NULL, NULL);
3837 	}
3838 
3839 	nsurl_unref(parent);
3840 	return err;
3841 }
3842 
3843 
3844 /* Exported interface, documented in include/netsurf/browser_window.h */
browser_window_access_url(const struct browser_window * bw)3845 nsurl* browser_window_access_url(const struct browser_window *bw)
3846 {
3847 	assert(bw != NULL);
3848 
3849 	if (bw->current_content != NULL) {
3850 		return hlcache_handle_get_url(bw->current_content);
3851 
3852 	} else if (bw->loading_content != NULL) {
3853 		/* TODO: should we return this? */
3854 		return hlcache_handle_get_url(bw->loading_content);
3855 	}
3856 
3857 	return corestring_nsurl_about_blank;
3858 }
3859 
3860 
3861 /* Exported interface, documented in include/netsurf/browser_window.h */
3862 nserror
browser_window_get_url(struct browser_window * bw,bool fragment,nsurl ** url_out)3863 browser_window_get_url(struct browser_window *bw, bool fragment,nsurl** url_out)
3864 {
3865 	nserror err;
3866 	nsurl *url;
3867 
3868 	assert(bw != NULL);
3869 
3870 	if (!fragment || bw->frag_id == NULL || bw->loading_content != NULL) {
3871 		/* If there's a loading content, then the bw->frag_id will have
3872 		 * been trampled, possibly with a new frag_id, but we will
3873 		 * still be returning the current URL, so in this edge case
3874 		 * we just drop any fragment. */
3875 		url = nsurl_ref(browser_window_access_url(bw));
3876 
3877 	} else {
3878 		err = nsurl_refragment(browser_window_access_url(bw),
3879 				       bw->frag_id, &url);
3880 		if (err != NSERROR_OK) {
3881 			return err;
3882 		}
3883 	}
3884 
3885 	*url_out = url;
3886 	return NSERROR_OK;
3887 }
3888 
3889 
3890 /* Exported interface, documented in netsurf/browser_window.h */
browser_window_get_title(struct browser_window * bw)3891 const char* browser_window_get_title(struct browser_window *bw)
3892 {
3893 	assert(bw != NULL);
3894 
3895 	if (bw->current_content != NULL) {
3896 		return content_get_title(bw->current_content);
3897 	}
3898 
3899 	/* no content so return about:blank */
3900 	return nsurl_access(corestring_nsurl_about_blank);
3901 }
3902 
3903 
3904 /* Exported interface, documented in netsurf/browser_window.h */
browser_window_get_history(struct browser_window * bw)3905 struct history * browser_window_get_history(struct browser_window *bw)
3906 {
3907 	assert(bw != NULL);
3908 
3909 	return bw->history;
3910 }
3911 
3912 
3913 /* Exported interface, documented in netsurf/browser_window.h */
browser_window_has_content(struct browser_window * bw)3914 bool browser_window_has_content(struct browser_window *bw)
3915 {
3916 	assert(bw != NULL);
3917 
3918 	if (bw->current_content == NULL) {
3919 		return false;
3920 	}
3921 
3922 	return true;
3923 }
3924 
3925 
3926 /* Exported interface, documented in netsurf/browser_window.h */
browser_window_get_content(struct browser_window * bw)3927 struct hlcache_handle *browser_window_get_content(struct browser_window *bw)
3928 {
3929 	return bw->current_content;
3930 }
3931 
3932 
3933 /* Exported interface, documented in netsurf/browser_window.h */
browser_window_get_extents(struct browser_window * bw,bool scaled,int * width,int * height)3934 nserror browser_window_get_extents(struct browser_window *bw, bool scaled,
3935 				   int *width, int *height)
3936 {
3937 	assert(bw != NULL);
3938 
3939 	if (bw->current_content == NULL) {
3940 		*width = 0;
3941 		*height = 0;
3942 		return NSERROR_BAD_CONTENT;
3943 	}
3944 
3945 	*width = content_get_width(bw->current_content);
3946 	*height = content_get_height(bw->current_content);
3947 
3948 	if (scaled) {
3949 		*width *= bw->scale;
3950 		*height *= bw->scale;
3951 	}
3952 
3953 	return NSERROR_OK;
3954 }
3955 
3956 
3957 /* exported internal interface, documented in desktop/browser_private.h */
3958 nserror
browser_window_get_dimensions(struct browser_window * bw,int * width,int * height)3959 browser_window_get_dimensions(struct browser_window *bw,
3960 			      int *width,
3961 			      int *height)
3962 {
3963 	nserror res;
3964 	assert(bw);
3965 
3966 	if (bw->window == NULL) {
3967 		/* Core managed browser window */
3968 		*width = bw->width;
3969 		*height = bw->height;
3970 		res = NSERROR_OK;
3971 	} else {
3972 		/* Front end window */
3973 		res = guit->window->get_dimensions(bw->window, width, height);
3974 	}
3975 	return res;
3976 }
3977 
3978 
3979 /* Exported interface, documented in netsurf/browser_window.h */
3980 void
browser_window_set_dimensions(struct browser_window * bw,int width,int height)3981 browser_window_set_dimensions(struct browser_window *bw, int width, int height)
3982 {
3983 	assert(bw);
3984 
3985 	if (bw->window == NULL) {
3986 		/* Core managed browser window */
3987 		bw->width = width;
3988 		bw->height = height;
3989 	} else {
3990 		NSLOG(netsurf, INFO,
3991 		      "Asked to set dimensions of front end window.");
3992 		assert(0);
3993 	}
3994 }
3995 
3996 
3997 /* Exported interface, documented in netsurf/browser_window.h */
browser_window_update(struct browser_window * bw,bool scroll_to_top)3998 void browser_window_update(struct browser_window *bw, bool scroll_to_top)
3999 {
4000 	static const struct rect zrect = {
4001 		.x0 = 0,
4002 		.y0 = 0,
4003 		.x1 = 0,
4004 		.y1 = 0
4005 	};
4006 
4007 	if (bw->current_content == NULL) {
4008 		return;
4009 	}
4010 
4011 	switch (bw->browser_window_type) {
4012 
4013 	case BROWSER_WINDOW_NORMAL:
4014 		/* Root browser window, constituting a front end window/tab */
4015 		guit->window->set_title(bw->window,
4016 					content_get_title(bw->current_content));
4017 
4018 		browser_window_update_extent(bw);
4019 
4020 		/* if frag_id exists, then try to scroll to it */
4021 		/** @todo don't do this if the user has scrolled */
4022 		if (!frag_scroll(bw)) {
4023 			if (scroll_to_top) {
4024 				browser_window_set_scroll(bw, &zrect);
4025 			}
4026 		}
4027 
4028 		guit->window->invalidate(bw->window, NULL);
4029 
4030 		break;
4031 
4032 	case BROWSER_WINDOW_IFRAME:
4033 		/* Internal iframe browser window */
4034 		assert(bw->parent != NULL);
4035 		assert(bw->parent->current_content != NULL);
4036 
4037 		browser_window_update_extent(bw);
4038 
4039 		if (scroll_to_top) {
4040 			browser_window_set_scroll(bw, &zrect);
4041 		}
4042 
4043 		/* if frag_id exists, then try to scroll to it */
4044 		/** @todo don't do this if the user has scrolled */
4045 		frag_scroll(bw);
4046 
4047 		html_redraw_a_box(bw->parent->current_content, bw->box);
4048 		break;
4049 
4050 	case BROWSER_WINDOW_FRAME:
4051 		{
4052 			struct rect rect;
4053 			browser_window_update_extent(bw);
4054 
4055 			if (scroll_to_top) {
4056 				browser_window_set_scroll(bw, &zrect);
4057 			}
4058 
4059 			/* if frag_id exists, then try to scroll to it */
4060 			/** @todo don't do this if the user has scrolled */
4061 			frag_scroll(bw);
4062 
4063 			rect.x0 = scrollbar_get_offset(bw->scroll_x);
4064 			rect.y0 = scrollbar_get_offset(bw->scroll_y);
4065 			rect.x1 = rect.x0 + bw->width;
4066 			rect.y1 = rect.y0 + bw->height;
4067 
4068 			browser_window_update_box(bw, &rect);
4069 		}
4070 		break;
4071 
4072 	default:
4073 	case BROWSER_WINDOW_FRAMESET:
4074 		/* Nothing to do */
4075 		break;
4076 	}
4077 }
4078 
4079 
4080 /* Exported interface, documented in netsurf/browser_window.h */
browser_window_update_box(struct browser_window * bw,struct rect * rect)4081 void browser_window_update_box(struct browser_window *bw, struct rect *rect)
4082 {
4083 	int pos_x;
4084 	int pos_y;
4085 	struct browser_window *top = bw;
4086 
4087 	assert(bw);
4088 
4089 	if (bw->window == NULL) {
4090 		/* Core managed browser window */
4091 		browser_window_get_position(bw, true, &pos_x, &pos_y);
4092 
4093 		top = browser_window_get_root(bw);
4094 
4095 		rect->x0 += pos_x / bw->scale;
4096 		rect->y0 += pos_y / bw->scale;
4097 		rect->x1 += pos_x / bw->scale;
4098 		rect->y1 += pos_y / bw->scale;
4099 	}
4100 
4101 	rect->x0 *= top->scale;
4102 	rect->y0 *= top->scale;
4103 	rect->x1 *= top->scale;
4104 	rect->y1 *= top->scale;
4105 
4106 	guit->window->invalidate(top->window, rect);
4107 }
4108 
4109 
4110 /* Exported interface, documented in netsurf/browser_window.h */
browser_window_stop(struct browser_window * bw)4111 void browser_window_stop(struct browser_window *bw)
4112 {
4113 	int children, index;
4114 
4115 	if (bw->loading_content != NULL) {
4116 		hlcache_handle_abort(bw->loading_content);
4117 		hlcache_handle_release(bw->loading_content);
4118 		bw->loading_content = NULL;
4119 	}
4120 
4121 	if (bw->current_content != NULL &&
4122 	    content_get_status(bw->current_content) != CONTENT_STATUS_DONE) {
4123 		nserror error;
4124 		assert(content_get_status(bw->current_content) ==
4125 		       CONTENT_STATUS_READY);
4126 		error = hlcache_handle_abort(bw->current_content);
4127 		assert(error == NSERROR_OK);
4128 	}
4129 
4130 	guit->misc->schedule(-1, browser_window_refresh, bw);
4131 
4132 	if (bw->children) {
4133 		children = bw->rows * bw->cols;
4134 		for (index = 0; index < children; index++)
4135 			browser_window_stop(&bw->children[index]);
4136 	}
4137 	if (bw->iframes) {
4138 		children = bw->iframe_count;
4139 		for (index = 0; index < children; index++)
4140 			browser_window_stop(&bw->iframes[index]);
4141 	}
4142 
4143 	if (bw->current_content != NULL) {
4144 		browser_window_refresh_url_bar(bw);
4145 	}
4146 
4147 	browser_window_stop_throbber(bw);
4148 }
4149 
4150 
4151 /* Exported interface, documented in netsurf/browser_window.h */
browser_window_reload(struct browser_window * bw,bool all)4152 nserror browser_window_reload(struct browser_window *bw, bool all)
4153 {
4154 	hlcache_handle *c;
4155 	unsigned int i;
4156 	struct nsurl *reload_url;
4157 
4158 	if ((bw->current_content) == NULL ||
4159 	    (bw->loading_content) != NULL) {
4160 		return NSERROR_INVALID;
4161 	}
4162 
4163 	if (all && content_get_type(bw->current_content) == CONTENT_HTML) {
4164 		struct html_stylesheet *sheets;
4165 		struct content_html_object *object;
4166 		unsigned int count;
4167 
4168 		c = bw->current_content;
4169 
4170 		/* invalidate objects */
4171 		object = html_get_objects(c, &count);
4172 
4173 		for (; object != NULL; object = object->next) {
4174 			if (object->content != NULL)
4175 				content_invalidate_reuse_data(object->content);
4176 		}
4177 
4178 		/* invalidate stylesheets */
4179 		sheets = html_get_stylesheets(c, &count);
4180 
4181 		for (i = STYLESHEET_START; i != count; i++) {
4182 			if (sheets[i].sheet != NULL) {
4183 				content_invalidate_reuse_data(sheets[i].sheet);
4184 			}
4185 		}
4186 	}
4187 
4188 	content_invalidate_reuse_data(bw->current_content);
4189 
4190 	reload_url = hlcache_handle_get_url(bw->current_content);
4191 
4192 	return browser_window_navigate(bw,
4193 				       reload_url,
4194 				       NULL,
4195 				       BW_NAVIGATE_NONE,
4196 				       NULL,
4197 				       NULL,
4198 				       NULL);
4199 }
4200 
4201 
4202 /* Exported interface, documented in netsurf/browser_window.h */
browser_window_set_status(struct browser_window * bw,const char * text)4203 void browser_window_set_status(struct browser_window *bw, const char *text)
4204 {
4205 	int text_len;
4206 	/* find topmost window */
4207 	while (bw->parent)
4208 		bw = bw->parent;
4209 
4210 	if ((bw->status.text != NULL) &&
4211 	    (strcmp(text, bw->status.text) == 0)) {
4212 		/* status text is unchanged */
4213 		bw->status.match++;
4214 		return;
4215 	}
4216 
4217 	/* status text is changed */
4218 
4219 	text_len = strlen(text);
4220 
4221 	if ((bw->status.text == NULL) || (bw->status.text_len < text_len)) {
4222 		/* no current string allocation or it is not long enough */
4223 		free(bw->status.text);
4224 		bw->status.text = strdup(text);
4225 		bw->status.text_len = text_len;
4226 	} else {
4227 		/* current allocation has enough space */
4228 		memcpy(bw->status.text, text, text_len + 1);
4229 	}
4230 
4231 	bw->status.miss++;
4232 	guit->window->set_status(bw->window, bw->status.text);
4233 }
4234 
4235 
4236 /* Exported interface, documented in netsurf/browser_window.h */
browser_window_set_pointer(struct browser_window * bw,browser_pointer_shape shape)4237 void browser_window_set_pointer(struct browser_window *bw,
4238 				browser_pointer_shape shape)
4239 {
4240 	struct browser_window *root = browser_window_get_root(bw);
4241 	gui_pointer_shape gui_shape;
4242 	bool loading;
4243 	uint64_t ms_now;
4244 
4245 	assert(root);
4246 	assert(root->window);
4247 
4248 	loading = ((bw->loading_content != NULL) ||
4249 		   ((bw->current_content != NULL) &&
4250 		    (content_get_status(bw->current_content) == CONTENT_STATUS_READY)));
4251 
4252 	nsu_getmonotonic_ms(&ms_now);
4253 
4254 	if (loading && ((ms_now - bw->last_action) < 1000)) {
4255 		/* If loading and less than 1 second since last link followed,
4256 		 * force progress indicator pointer */
4257 		gui_shape = GUI_POINTER_PROGRESS;
4258 
4259 	} else if (shape == BROWSER_POINTER_AUTO) {
4260 		/* Up to browser window to decide */
4261 		if (loading) {
4262 			gui_shape = GUI_POINTER_PROGRESS;
4263 		} else {
4264 			gui_shape = GUI_POINTER_DEFAULT;
4265 		}
4266 
4267 	} else {
4268 		/* Use what we were told */
4269 		gui_shape = (gui_pointer_shape)shape;
4270 	}
4271 
4272 	guit->window->set_pointer(root->window, gui_shape);
4273 }
4274 
4275 
4276 /* exported function documented in netsurf/browser_window.h */
browser_window_schedule_reformat(struct browser_window * bw)4277 nserror browser_window_schedule_reformat(struct browser_window *bw)
4278 {
4279 	if (bw->window == NULL) {
4280 		return NSERROR_BAD_PARAMETER;
4281 	}
4282 
4283 	return guit->misc->schedule(0, scheduled_reformat, bw);
4284 }
4285 
4286 
4287 /* exported function documented in netsurf/browser_window.h */
4288 void
browser_window_reformat(struct browser_window * bw,bool background,int width,int height)4289 browser_window_reformat(struct browser_window *bw,
4290 			bool background,
4291 			int width,
4292 			int height)
4293 {
4294 	hlcache_handle *c = bw->current_content;
4295 
4296 	if (c == NULL)
4297 		return;
4298 
4299 	if (bw->browser_window_type != BROWSER_WINDOW_IFRAME) {
4300 		/* Iframe dimensions are already scaled in parent's layout */
4301 		width  /= bw->scale;
4302 		height /= bw->scale;
4303 	}
4304 
4305 	if (bw->window == NULL) {
4306 		/* Core managed browser window; subtract scrollbar width */
4307 		width  -= bw->scroll_y ? SCROLLBAR_WIDTH : 0;
4308 		height -= bw->scroll_x ? SCROLLBAR_WIDTH : 0;
4309 
4310 		width  = width  > 0 ? width  : 0;
4311 		height = height > 0 ? height : 0;
4312 	}
4313 
4314 	content_reformat(c, background, width, height);
4315 }
4316 
4317 
4318 /* exported interface documented in netsurf/browser_window.h */
4319 nserror
browser_window_set_scale(struct browser_window * bw,float scale,bool absolute)4320 browser_window_set_scale(struct browser_window *bw, float scale, bool absolute)
4321 {
4322 	nserror res;
4323 
4324 	/* get top browser window */
4325 	while (bw->parent) {
4326 		bw = bw->parent;
4327 	}
4328 
4329 	if (!absolute) {
4330 		/* snap small values around 1.0 */
4331 		if ((scale + bw->scale) > (1.01 - scale) &&
4332 		    (scale + bw->scale) < (0.99 + scale)) {
4333 			scale = 1.0;
4334 		} else {
4335 			scale += bw->scale;
4336 		}
4337 	}
4338 
4339 	/* clamp range between 0.1 and 10 (10% and 1000%) */
4340 	if (scale < SCALE_MINIMUM) {
4341 		scale = SCALE_MINIMUM;
4342 	} else if (scale > SCALE_MAXIMUM) {
4343 		scale = SCALE_MAXIMUM;
4344 	}
4345 
4346 	res = browser_window_set_scale_internal(bw, scale);
4347 	if (res == NSERROR_OK) {
4348 		browser_window_recalculate_frameset(bw);
4349 	}
4350 
4351 	return res;
4352 }
4353 
4354 
4355 /* exported interface documented in netsurf/browser_window.h */
browser_window_get_scale(struct browser_window * bw)4356 float browser_window_get_scale(struct browser_window *bw)
4357 {
4358 	if (bw == NULL) {
4359 		return 1.0;
4360 	}
4361 
4362 	return bw->scale;
4363 }
4364 
4365 
4366 /* exported interface documented in netsurf/browser_window.h */
4367 struct browser_window *
browser_window_find_target(struct browser_window * bw,const char * target,browser_mouse_state mouse)4368 browser_window_find_target(struct browser_window *bw,
4369 			   const char *target,
4370 			   browser_mouse_state mouse)
4371 {
4372 	struct browser_window *bw_target;
4373 	struct browser_window *top;
4374 	hlcache_handle *c;
4375 	int rdepth;
4376 	nserror error;
4377 
4378 	/* use the base target if we don't have one */
4379 	c = bw->current_content;
4380 	if (target == NULL &&
4381 	    c != NULL &&
4382 	    content_get_type(c) == CONTENT_HTML) {
4383 		target = html_get_base_target(c);
4384 	}
4385 	if (target == NULL) {
4386 		target = TARGET_SELF;
4387 	}
4388 
4389 	/* allow the simple case of target="_blank" to be ignored if requested
4390 	 */
4391 	if ((!(mouse & BROWSER_MOUSE_CLICK_2)) &&
4392 	    (!((mouse & BROWSER_MOUSE_CLICK_2) &&
4393 	       (mouse & BROWSER_MOUSE_MOD_2))) &&
4394 	    (!nsoption_bool(target_blank))) {
4395 		/* not a mouse button 2 click
4396 		 * not a mouse button 1 click with ctrl pressed
4397 		 * configured to ignore target="_blank" */
4398 		if ((target == TARGET_BLANK) || (!strcasecmp(target, "_blank")))
4399 			return bw;
4400 	}
4401 
4402 	/* handle reserved keywords */
4403 	if (((nsoption_bool(button_2_tab)) &&
4404 	     (mouse & BROWSER_MOUSE_CLICK_2))||
4405 	    ((!nsoption_bool(button_2_tab)) &&
4406 	     ((mouse & BROWSER_MOUSE_CLICK_1) &&
4407 	      (mouse & BROWSER_MOUSE_MOD_2))) ||
4408 	    ((nsoption_bool(button_2_tab)) &&
4409 	     ((target == TARGET_BLANK) ||
4410 	      (!strcasecmp(target, "_blank"))))) {
4411 		/* open in new tab if:
4412 		 * - button_2 opens in new tab and button_2 was pressed
4413 		 * OR
4414 		 * - button_2 doesn't open in new tabs and button_1 was
4415 		 *   pressed with ctrl held
4416 		 * OR
4417 		 * - button_2 opens in new tab and the link target is "_blank"
4418 		 */
4419 		error = browser_window_create(BW_CREATE_TAB |
4420 					      BW_CREATE_HISTORY |
4421 					      BW_CREATE_CLONE,
4422 					      NULL,
4423 					      NULL,
4424 					      bw,
4425 					      &bw_target);
4426 		if (error != NSERROR_OK) {
4427 			return bw;
4428 		}
4429 		return bw_target;
4430 	} else if (((!nsoption_bool(button_2_tab)) &&
4431 		    (mouse & BROWSER_MOUSE_CLICK_2)) ||
4432 		   ((nsoption_bool(button_2_tab)) &&
4433 		    ((mouse & BROWSER_MOUSE_CLICK_1) &&
4434 		     (mouse & BROWSER_MOUSE_MOD_2))) ||
4435 		   ((!nsoption_bool(button_2_tab)) &&
4436 		    ((target == TARGET_BLANK) ||
4437 		     (!strcasecmp(target, "_blank"))))) {
4438 		/* open in new window if:
4439 		 * - button_2 doesn't open in new tabs and button_2 was pressed
4440 		 * OR
4441 		 * - button_2 opens in new tab and button_1 was pressed with
4442 		 *   ctrl held
4443 		 * OR
4444 		 * - button_2 doesn't open in new tabs and the link target is
4445 		 *   "_blank"
4446 		 */
4447 		error = browser_window_create(BW_CREATE_HISTORY |
4448 					      BW_CREATE_CLONE,
4449 					      NULL,
4450 					      NULL,
4451 					      bw,
4452 					      &bw_target);
4453 		if (error != NSERROR_OK) {
4454 			return bw;
4455 		}
4456 		return bw_target;
4457 	} else if ((target == TARGET_SELF) || (!strcasecmp(target, "_self"))) {
4458 		return bw;
4459 	} else if ((target == TARGET_PARENT) ||
4460 		   (!strcasecmp(target, "_parent"))) {
4461 		if (bw->parent)
4462 			return bw->parent;
4463 		return bw;
4464 	} else if ((target == TARGET_TOP) || (!strcasecmp(target, "_top"))) {
4465 		while (bw->parent)
4466 			bw = bw->parent;
4467 		return bw;
4468 	}
4469 
4470 	/* find frame according to B.8, ie using the following priorities:
4471 	 *
4472 	 *  1) current frame
4473 	 *  2) closest to front
4474 	 */
4475 	rdepth = -1;
4476 	bw_target = NULL;
4477 	for (top = bw; top->parent; top = top->parent);
4478 	browser_window_find_target_internal(top, target, 0, bw, &rdepth,
4479 					    &bw_target);
4480 	if (bw_target)
4481 		return bw_target;
4482 
4483 	/* we require a new window using the target name */
4484 	if (!nsoption_bool(target_blank))
4485 		return bw;
4486 
4487 	error = browser_window_create(BW_CREATE_CLONE | BW_CREATE_HISTORY,
4488 				      NULL,
4489 				      NULL,
4490 				      bw,
4491 				      &bw_target);
4492 	if (error != NSERROR_OK) {
4493 		return bw;
4494 	}
4495 
4496 	/* frame names should begin with an alphabetic character (a-z,A-Z),
4497 	 * however in practice you get things such as '_new' and '2left'. The
4498 	 * only real effect this has is when giving out names as it can be
4499 	 * assumed that an author intended '_new' to create a new nameless
4500 	 * window (ie '_blank') whereas in the case of '2left' the intention
4501 	 * was for a new named window. As such we merely special case windows
4502 	 * that begin with an underscore. */
4503 	if (target[0] != '_') {
4504 		bw_target->name = strdup(target);
4505 	}
4506 	return bw_target;
4507 }
4508 
4509 
4510 /* exported interface documented in netsurf/browser_window.h */
4511 void
browser_window_mouse_track(struct browser_window * bw,browser_mouse_state mouse,int x,int y)4512 browser_window_mouse_track(struct browser_window *bw,
4513 			   browser_mouse_state mouse,
4514 			   int x, int y)
4515 {
4516 	browser_window_mouse_track_internal(bw,
4517 					    mouse,
4518 					    x / bw->scale,
4519 					    y / bw->scale);
4520 }
4521 
4522 /* exported interface documented in netsurf/browser_window.h */
4523 void
browser_window_mouse_click(struct browser_window * bw,browser_mouse_state mouse,int x,int y)4524 browser_window_mouse_click(struct browser_window *bw,
4525 			   browser_mouse_state mouse,
4526 			   int x, int y)
4527 {
4528 	browser_window_mouse_click_internal(bw,
4529 					    mouse,
4530 					    x / bw->scale,
4531 					    y / bw->scale);
4532 }
4533 
4534 
4535 /* exported interface documented in netsurf/browser_window.h */
browser_window_page_drag_start(struct browser_window * bw,int x,int y)4536 void browser_window_page_drag_start(struct browser_window *bw, int x, int y)
4537 {
4538 	assert(bw != NULL);
4539 
4540 	browser_window_set_drag_type(bw, DRAGGING_PAGE_SCROLL, NULL);
4541 
4542 	bw->drag.start_x = x;
4543 	bw->drag.start_y = y;
4544 
4545 	if (bw->window != NULL) {
4546 		/* Front end window */
4547 		guit->window->get_scroll(bw->window,
4548 					 &bw->drag.start_scroll_x,
4549 					 &bw->drag.start_scroll_y);
4550 
4551 		guit->window->event(bw->window, GW_EVENT_SCROLL_START);
4552 	} else {
4553 		/* Core managed browser window */
4554 		bw->drag.start_scroll_x = scrollbar_get_offset(bw->scroll_x);
4555 		bw->drag.start_scroll_y = scrollbar_get_offset(bw->scroll_y);
4556 	}
4557 }
4558 
4559 
4560 /* exported interface documented in netsurf/browser_window.h */
browser_window_back_available(struct browser_window * bw)4561 bool browser_window_back_available(struct browser_window *bw)
4562 {
4563 	if (bw != NULL && bw->internal_nav) {
4564 		/* Internal nav, back is possible */
4565 		return true;
4566 	}
4567 	return (bw && bw->history && browser_window_history_back_available(bw));
4568 }
4569 
4570 
4571 /* exported interface documented in netsurf/browser_window.h */
browser_window_forward_available(struct browser_window * bw)4572 bool browser_window_forward_available(struct browser_window *bw)
4573 {
4574 	return (bw && bw->history && browser_window_history_forward_available(bw));
4575 }
4576 
4577 /* exported interface documented in netsurf/browser_window.h */
browser_window_reload_available(struct browser_window * bw)4578 bool browser_window_reload_available(struct browser_window *bw)
4579 {
4580 	return (bw && bw->current_content && !bw->loading_content);
4581 }
4582 
4583 
4584 /* exported interface documented in netsurf/browser_window.h */
browser_window_stop_available(struct browser_window * bw)4585 bool browser_window_stop_available(struct browser_window *bw)
4586 {
4587 	return (bw && (bw->loading_content ||
4588 		       (bw->current_content &&
4589 			(content_get_status(bw->current_content) !=
4590 			 CONTENT_STATUS_DONE))));
4591 }
4592 
4593 /* exported interface documented in netsurf/browser_window.h */
4594 bool
browser_window_exec(struct browser_window * bw,const char * src,size_t srclen)4595 browser_window_exec(struct browser_window *bw, const char *src, size_t srclen)
4596 {
4597 	assert(bw != NULL);
4598 
4599 	if (!bw->current_content) {
4600 		NSLOG(netsurf, DEEPDEBUG, "Unable to exec, no content");
4601 		return false;
4602 	}
4603 
4604 	if (content_get_status(bw->current_content) != CONTENT_STATUS_DONE) {
4605 		NSLOG(netsurf, DEEPDEBUG, "Unable to exec, content not done");
4606 		return false;
4607 	}
4608 
4609 	/* Okay it should be safe, forward the request through to the content
4610 	 * itself.  Only HTML contents currently support executing code
4611 	 */
4612 	return content_exec(bw->current_content, src, srclen);
4613 }
4614 
4615 
4616 /* exported interface documented in browser_window.h */
4617 nserror
browser_window_console_log(struct browser_window * bw,browser_window_console_source src,const char * msg,size_t msglen,browser_window_console_flags flags)4618 browser_window_console_log(struct browser_window *bw,
4619 			   browser_window_console_source src,
4620 			   const char *msg,
4621 			   size_t msglen,
4622 			   browser_window_console_flags flags)
4623 {
4624 	browser_window_console_flags log_level = flags & BW_CS_FLAG_LEVEL_MASK;
4625 	struct browser_window *root = browser_window_get_root(bw);
4626 
4627 	assert(msg != NULL);
4628 	/* We don't assert msglen > 0, if someone wants to log a real empty
4629 	 * string then we won't stop them.  It does sometimes happen from
4630 	 * JavaScript for example.
4631 	 */
4632 
4633 	/* bw is the target of the log, but root is where we log it */
4634 
4635 	NSLOG(netsurf, DEEPDEBUG, "Logging message in %p targetted at %p", root, bw);
4636 	NSLOG(netsurf, DEEPDEBUG, "Log came from %s",
4637 	      ((src == BW_CS_INPUT) ? "user input" :
4638 	       (src == BW_CS_SCRIPT_ERROR) ? "script error" :
4639 	       (src == BW_CS_SCRIPT_CONSOLE) ? "script console" :
4640 	       "unknown input location"));
4641 
4642 	switch (log_level) {
4643 	case BW_CS_FLAG_LEVEL_DEBUG:
4644 		NSLOG(netsurf, DEBUG, "%.*s", (int)msglen, msg);
4645 		break;
4646 	case BW_CS_FLAG_LEVEL_LOG:
4647 		NSLOG(netsurf, VERBOSE, "%.*s", (int)msglen, msg);
4648 		break;
4649 	case BW_CS_FLAG_LEVEL_INFO:
4650 		NSLOG(netsurf, INFO, "%.*s", (int)msglen, msg);
4651 		break;
4652 	case BW_CS_FLAG_LEVEL_WARN:
4653 		NSLOG(netsurf, WARNING, "%.*s", (int)msglen, msg);
4654 		break;
4655 	case BW_CS_FLAG_LEVEL_ERROR:
4656 		NSLOG(netsurf, ERROR, "%.*s", (int)msglen, msg);
4657 		break;
4658 	default:
4659 		/* Unreachable */
4660 		break;
4661 	}
4662 
4663 	guit->window->console_log(root->window, src, msg, msglen, flags);
4664 
4665 	return NSERROR_OK;
4666 }
4667 
4668 
4669 /* Exported interface, documented in browser_private.h */
4670 nserror
browser_window__reload_current_parameters(struct browser_window * bw)4671 browser_window__reload_current_parameters(struct browser_window *bw)
4672 {
4673 	assert(bw != NULL);
4674 
4675 	if (bw->current_parameters.post_urlenc != NULL) {
4676 		free(bw->current_parameters.post_urlenc);
4677 		bw->current_parameters.post_urlenc = NULL;
4678 	}
4679 
4680 	if (bw->current_parameters.post_multipart != NULL) {
4681 		fetch_multipart_data_destroy(bw->current_parameters.post_multipart);
4682 		bw->current_parameters.post_multipart = NULL;
4683 	}
4684 
4685 	if (bw->current_parameters.url == NULL) {
4686 		/* We have never navigated so go to about:blank */
4687 		bw->current_parameters.url = nsurl_ref(corestring_nsurl_about_blank);
4688 	}
4689 
4690 	bw->current_parameters.flags &= ~BW_NAVIGATE_HISTORY;
4691 	bw->internal_nav = false;
4692 
4693 	browser_window__free_fetch_parameters(&bw->loading_parameters);
4694 	memcpy(&bw->loading_parameters, &bw->current_parameters, sizeof(bw->loading_parameters));
4695 	memset(&bw->current_parameters, 0, sizeof(bw->current_parameters));
4696 	return browser_window__navigate_internal(bw, &bw->loading_parameters);
4697 }
4698 
4699 /* Exported interface, documented in browser_window.h */
browser_window_get_page_info_state(const struct browser_window * bw)4700 browser_window_page_info_state browser_window_get_page_info_state(
4701 		const struct browser_window *bw)
4702 {
4703 	lwc_string *scheme;
4704 	bool match;
4705 
4706 	assert(bw != NULL);
4707 
4708 	/* Do we have any content?  If not -- UNKNOWN */
4709 	if (bw->current_content == NULL) {
4710 		return PAGE_STATE_UNKNOWN;
4711 	}
4712 
4713 	scheme = nsurl_get_component(
4714 		hlcache_handle_get_url(bw->current_content), NSURL_SCHEME);
4715 
4716 	/* Is this an internal scheme? */
4717 	if ((lwc_string_isequal(scheme, corestring_lwc_about,
4718 				&match) == lwc_error_ok &&
4719 	     (match == true)) ||
4720 	    (lwc_string_isequal(scheme, corestring_lwc_data,
4721 				&match) == lwc_error_ok &&
4722 	     (match == true)) ||
4723 	    (lwc_string_isequal(scheme, corestring_lwc_resource,
4724 				&match) == lwc_error_ok &&
4725 	     (match == true))) {
4726 		lwc_string_unref(scheme);
4727 		return PAGE_STATE_INTERNAL;
4728 	}
4729 
4730 	/* Is this file:/// ? */
4731 	if (lwc_string_isequal(scheme, corestring_lwc_file,
4732 			       &match) == lwc_error_ok &&
4733 	    match == true) {
4734 		lwc_string_unref(scheme);
4735 		return PAGE_STATE_LOCAL;
4736 	}
4737 
4738 	/* If not https, from here on down that'd be insecure */
4739 	if ((lwc_string_isequal(scheme, corestring_lwc_https,
4740 			&match) == lwc_error_ok &&
4741 			(match == false))) {
4742 		/* Some remote content, not https, therefore insecure */
4743 		lwc_string_unref(scheme);
4744 		return PAGE_STATE_INSECURE;
4745 	}
4746 
4747 	lwc_string_unref(scheme);
4748 
4749 	/* Did we have to override this SSL setting? */
4750 	if (urldb_get_cert_permissions(hlcache_handle_get_url(bw->current_content))) {
4751 		return PAGE_STATE_SECURE_OVERRIDE;
4752 	}
4753 
4754 	/* If we've seen insecure content internally then we need to say so */
4755 	if (content_saw_insecure_objects(bw->current_content)) {
4756 		return PAGE_STATE_SECURE_ISSUES;
4757 	}
4758 
4759 	/* All is well, return secure state */
4760 	return PAGE_STATE_SECURE;
4761 }
4762 
4763 /* Exported interface, documented in browser_window.h */
4764 nserror
browser_window_get_ssl_chain(struct browser_window * bw,struct cert_chain ** chain)4765 browser_window_get_ssl_chain(struct browser_window *bw,
4766 			     struct cert_chain **chain)
4767 {
4768 	assert(bw != NULL);
4769 
4770 	if (bw->current_cert_chain == NULL) {
4771 		return NSERROR_NOT_FOUND;
4772 	}
4773 
4774 	*chain = bw->current_cert_chain;
4775 
4776 	return NSERROR_OK;
4777 }
4778 
4779 /* Exported interface, documented in browser_window.h */
browser_window_get_cookie_count(const struct browser_window * bw)4780 int browser_window_get_cookie_count(
4781 		const struct browser_window *bw)
4782 {
4783 	int count = 0;
4784 	char *cookies = urldb_get_cookie(browser_window_access_url(bw), true);
4785 	if (cookies == NULL) {
4786 		return 0;
4787 	}
4788 
4789 	for (char *c = cookies; *c != '\0'; c++) {
4790 		if (*c == ';')
4791 			count++;
4792 	}
4793 
4794 	free(cookies);
4795 
4796 	return count;
4797 }
4798 
4799 /* Exported interface, documented in browser_window.h */
browser_window_show_cookies(const struct browser_window * bw)4800 nserror browser_window_show_cookies(
4801 		const struct browser_window *bw)
4802 {
4803 	nserror err;
4804 	nsurl *url = browser_window_access_url(bw);
4805 	lwc_string *host = nsurl_get_component(url, NSURL_HOST);
4806 	const char *string = (host != NULL) ? lwc_string_data(host) : NULL;
4807 
4808 	err = guit->misc->present_cookies(string);
4809 
4810 	if (host != NULL) {
4811 		lwc_string_unref(host);
4812 	}
4813 	return err;
4814 }
4815 
4816 /* Exported interface, documented in browser_window.h */
browser_window_show_certificates(struct browser_window * bw)4817 nserror browser_window_show_certificates(struct browser_window *bw)
4818 {
4819 	nserror res;
4820 	nsurl *url;
4821 
4822 	if (bw->current_cert_chain == NULL) {
4823 		return NSERROR_NOT_FOUND;
4824 	}
4825 
4826 	res = cert_chain_to_query(bw->current_cert_chain, &url);
4827 	if (res == NSERROR_OK) {
4828 		res = browser_window_create(BW_CREATE_HISTORY |
4829 					    BW_CREATE_FOREGROUND |
4830 					    BW_CREATE_TAB,
4831 					    url,
4832 					    NULL,
4833 					    bw,
4834 					    NULL);
4835 
4836 		nsurl_unref(url);
4837 	}
4838 
4839 	return res;
4840 }
4841