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(¶ms, 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(¶ms.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(¶ms.post_multipart,
1070 "realm",
1071 realm);
1072 if (err != NSERROR_OK) {
1073 goto out;
1074 }
1075
1076 err = fetch_multipart_data_new_kv(¶ms.post_multipart,
1077 "username",
1078 username);
1079 if (err != NSERROR_OK) {
1080 goto out;
1081 }
1082
1083 err = fetch_multipart_data_new_kv(¶ms.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, ¶ms);
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(¶ms);
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(¶ms, 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(¶ms.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(¶ms.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(¶ms.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, ¶ms);
1176 if (err != NSERROR_OK) {
1177 goto out;
1178 }
1179
1180 out:
1181 browser_window__free_fetch_parameters(¶ms);
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(¶ms, 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(¶ms.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, ¶ms);
1213 if (err != NSERROR_OK) {
1214 goto out;
1215 }
1216
1217 out:
1218 browser_window__free_fetch_parameters(¶ms);
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(¶ms, 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(¶ms.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(¶ms.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, ¶ms);
1257 if (err != NSERROR_OK) {
1258 goto out;
1259 }
1260
1261 out:
1262 browser_window__free_fetch_parameters(¶ms);
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(¶ms, 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 = ¶ms;
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, ¶ms, sizeof(params));
3435 memset(¶ms, 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(¶ms);
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