AmendHub

Download

jcs

/

detritus

/

browser.c

 

(View History)

jcs   browser: Reorganize go functions, use http:// fallback from URL bar Latest amendment: 60 on 2024-12-14

1 /*
2 * Copyright (c) 2021-2024 joshua stein <jcs@jcs.org>
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17 #include <stdarg.h>
18 #include <stdio.h>
19 #include <string.h>
20
21 #include "detritus.h"
22 #include "focusable.h"
23
24 #define PADDING 6
25 #define BROWSER_FONT geneva
26 #define BROWSER_FONT_SIZE 10
27 #define BROWSER_STATUS_FONT geneva
28 #define BROWSER_STATUS_FONT_SIZE 9
29 #define BROWSER_PRE_FONT monaco
30 #define BROWSER_PRE_FONT_SIZE 9
31
32 static Rect zerorect = { 0, 0, 0, 0 };
33 static Pattern fill_pattern;
34
35 extern struct page_handler finger_handler;
36 extern struct page_handler gemini_handler;
37 extern struct page_handler gopher_handler;
38 extern struct page_handler http_handler;
39
40 struct page_handler * page_handlers[] = {
41 &finger_handler,
42 &gemini_handler,
43 &gopher_handler,
44 &http_handler,
45 };
46
47 bool browser_close(struct focusable *focusable);
48 void browser_idle(struct focusable *focusable, EventRecord *event);
49 void browser_update_menu(struct browser *browser);
50 void browser_update_buttons(struct browser *browser);
51 void browser_update(struct focusable *focusable, EventRecord *event);
52 void browser_key_down(struct focusable *focusable, EventRecord *event);
53 void browser_mouse_down(struct focusable *focusable, EventRecord *event);
54 void browser_mouse_move(struct focusable *focusable, EventRecord *event);
55 bool browser_handle_menu(struct focusable *focusable, short menu,
56 short item);
57 void browser_atexit(struct focusable *focusable);
58
59 void browser_go_uri_field(struct browser *browser);
60 void browser_go_dir(struct browser *browser, short dir);
61 page_handle browser_create_page(struct browser *browser, struct URI *uri);
62 void browser_draw_status(struct browser *browser);
63 struct TVStyle * browser_build_tvstyle(struct browser *browser);
64 void browser_finished_loading(struct browser *browser);
65 void browser_page_free(page_handle pageh);
66 void browser_follow_redir(struct browser *browser);
67 void browser_stop_loading_page(struct browser *browser);
68 void browser_free_links(struct browser *browser);
69 void browser_find_links(struct browser *browser);
70 Boolean browser_find_links_callback(struct TVRec *tv,
71 struct TVFindInRectMatch *match, void *cookie);
72
73 void
74 browser_idle(struct focusable *focusable, EventRecord *event)
75 {
76 struct browser *browser = (struct browser *)focusable->cookie;
77 struct page_handler *handler;
78 struct page *page;
79 bool ret;
80
81 TEIdle(browser->uri_te);
82
83 GlobalToLocal(&event->where);
84 browser_mouse_move(focusable, event);
85
86 if (browser->loading_page) {
87 HLock(browser->loading_page);
88 page = *(browser->loading_page);
89 handler = page->handler;
90
91 ret = true;
92 if (page->request != NULL) {
93 ret = request_data_shuffle(page->request,
94 handler->request_data_queuer, browser->loading_page,
95 handler->request_data_consumer, browser->loading_page);
96
97 /* handle may have grown/moved during fetch */
98 HLock(browser->loading_page);
99 page = *(browser->loading_page);
100 handler = page->handler;
101 }
102
103 if (!page->download_frefnum)
104 ret &= handler->process(browser->loading_page);
105
106 if (!ret)
107 browser_finished_loading(browser);
108
109 HUnlock(browser->loading_page);
110 }
111 }
112
113 struct browser *
114 browser_init(void)
115 {
116 char title[64];
117 struct browser *browser;
118 struct focusable *focusable;
119 Handle rgnsave;
120 Rect bounds, te_bounds, padding, r;
121 long width, height;
122
123 browser = xmalloczero(sizeof(struct browser));
124 if (browser == NULL)
125 panic("Out of memory allocating browser");
126
127 GetIndPattern(&fill_pattern, sysPatListID, 10);
128
129 /* main window */
130 width = screenBits.bounds.right - screenBits.bounds.left - PADDING;
131 width = MIN(width, 500);
132 height = screenBits.bounds.bottom - screenBits.bounds.top -
133 PADDING - (GetMBarHeight() * 2);
134 height = MIN(height, 290);
135 center_in_screen(width, height, true, &bounds);
136 //bounds.top = (screenBits.bounds.bottom / 2);
137
138 snprintf(title, sizeof(title), "%s", PROGRAM_NAME);
139 CtoPstr(title);
140 browser->win = NewWindow(0L, &bounds, title, false, noGrowDocProc,
141 (WindowPtr)-1L, true, 0);
142 if (!browser->win)
143 panic("Can't create window");
144 SetPort(browser->win);
145
146 browser->header = NewRgn();
147 OpenRgn();
148 r.top = 0;
149 r.left = 0;
150 r.right = bounds.right - bounds.left;
151 r.bottom = 26;
152 FrameRect(&r);
153
154 rgnsave = browser->win->rgnSave;
155 browser->win->rgnSave = NULL;
156
157 /* back and forward */
158 bounds.top = PADDING;
159 bounds.left = PADDING;
160 bounds.right = bounds.left + 20;
161 bounds.bottom = bounds.top + 16;
162 browser->back = NewControl(browser->win, &bounds, "\p<", true,
163 1, 1, 1, pushButProc, 0L);
164 HiliteControl(browser->back, 255);
165
166 browser->win->rgnSave = rgnsave;
167 FrameRoundRect(&bounds, 10, 10);
168 browser->win->rgnSave = NULL;
169
170 bounds.top = PADDING;
171 bounds.left = bounds.right - 1;
172 bounds.right = bounds.left + 20;
173 browser->fwd = NewControl(browser->win, &bounds, "\p>", true,
174 1, 1, 1, pushButProc, 0L);
175 HiliteControl(browser->fwd, 255);
176
177 browser->win->rgnSave = rgnsave;
178 FrameRoundRect(&bounds, 10, 10);
179 browser->win->rgnSave = NULL;
180
181 /* uri TE */
182 bounds.left = bounds.right + PADDING;
183 bounds.right = browser->win->portRect.right - PADDING - 60;
184 te_bounds = bounds;
185 InsetRect(&te_bounds, 2, 2);
186 TextFont(geneva);
187 TextSize(10);
188 browser->uri_te = TENew(&te_bounds, &bounds);
189 if (browser->uri_te == NULL)
190 panic("Out of memory allocating TE");
191 TEAutoView(false, browser->uri_te);
192 TEActivate(browser->uri_te);
193
194 browser->win->rgnSave = rgnsave;
195 FrameRect(&bounds);
196 browser->win->rgnSave = NULL;
197
198 bounds.left = bounds.right + PADDING;
199 bounds.right = browser->win->portRect.right - PADDING;
200 browser->go_stop = NewControl(browser->win, &bounds, "\pGo", true,
201 1, 1, 1, pushButProc, 0L);
202 HiliteControl(browser->go_stop, 255);
203
204 browser->win->rgnSave = rgnsave;
205 FrameRoundRect(&bounds, 10, 10);
206 CloseRgn(browser->header);
207
208 /* output TV */
209 bounds.left = 0;
210 bounds.right = browser->win->portRect.right - SCROLLBAR_WIDTH + 1;
211 bounds.top = bounds.bottom + PADDING;
212 bounds.bottom = browser->win->portRect.bottom - SCROLLBAR_WIDTH + 1;
213 SetRect(&padding, 4, 4, 4, 4);
214 browser->output_tv = TVNew(&bounds, &padding);
215 if (browser->output_tv == NULL)
216 panic("Out of memory allocating TV");
217
218 /* scrollbar for output text */
219 bounds.right = browser->win->portRect.right + 1;
220 bounds.left = bounds.right - SCROLLBAR_WIDTH;
221 bounds.bottom++;
222 bounds.top--;
223 browser->output_tv_scroller = NewControl(browser->win, &bounds, "\p",
224 true, 1, 1, 1, scrollBarProc, 0L);
225
226 browser_update_menu(browser);
227 TVUpdateScrollbar(browser->output_tv, browser->output_tv_scroller);
228
229 browser->status_rect.left = -1;
230 browser->status_rect.bottom = browser->win->portRect.bottom + 1;
231 browser->status_rect.right = browser->win->portRect.right -
232 SCROLLBAR_WIDTH + 2;
233 browser->status_rect.top = browser->status_rect.bottom -
234 SCROLLBAR_WIDTH;
235
236 focusable = xmalloczero(sizeof(struct focusable));
237 if (focusable == NULL)
238 panic("Out of memory!");
239 focusable->cookie = browser;
240 focusable->win = browser->win;
241 focusable->idle = browser_idle;
242 focusable->update = browser_update;
243 focusable->mouse_down = browser_mouse_down;
244 focusable->mouse_move = browser_mouse_move;
245 focusable->key_down = browser_key_down;
246 focusable->menu = browser_handle_menu;
247 focusable->close = browser_close;
248 focusable->atexit = browser_atexit;
249 focusable_add(focusable);
250
251 browser_update(focusable, NULL);
252 browser_statusf(browser, "Hello, cyberpal");
253
254 return browser;
255 }
256
257 bool
258 browser_close(struct focusable *focusable)
259 {
260 struct browser *browser = (struct browser *)focusable->cookie;
261 page_handle pageh, fpage, bpage, tpage;
262 struct page *page;
263
264 browser_stop_loading_page(browser);
265
266 if (browser->current_page) {
267 fpage = (*(browser->current_page))->fwd_page;
268 while (fpage) {
269 HLock(fpage);
270 tpage = (*fpage)->fwd_page;
271 browser_page_free(fpage);
272 fpage = tpage;
273 }
274
275 bpage = (*pageh)->back_page;
276 while (bpage) {
277 HLock(bpage);
278 tpage = (*bpage)->back_page;
279 browser_page_free(bpage);
280 bpage = tpage;
281 }
282
283 HLock(pageh);
284 browser_page_free(pageh);
285 }
286
287 browser_free_links(browser);
288
289 TEDispose(browser->uri_te);
290 TVDispose(browser->output_tv);
291 DisposeWindow(browser->win);
292 xfree(&browser);
293 focusable->cookie = NULL;
294
295 scsi_cleanup();
296
297 return true;
298 }
299
300 void
301 browser_finished_loading(struct browser *browser)
302 {
303 struct URI *redir;
304 size_t size;
305 bool download = false, committed;
306
307 if ((*(browser->loading_page))->redir_to) {
308 browser_follow_redir(browser);
309 return;
310 }
311
312 if ((*(browser->loading_page))->download_frefnum) {
313 download = true;
314 FSClose((*(browser->loading_page))->download_frefnum);
315 browser_statusf(browser, "Downloaded (%ld bytes)",
316 (*(browser->loading_page))->download_len);
317 }
318
319 committed = (browser->loading_page == browser->current_page);
320 browser_stop_loading_page(browser);
321 browser->redirs = 0;
322
323 if (download || !committed)
324 return;
325
326 /* release some wasted memory if we can */
327 if ((*(browser->current_page))->content_size >
328 (*(browser->current_page))->content_len) {
329 size = (*(browser->current_page))->content_len;
330 (*(browser->current_page))->content_size = size;
331 HUnlock(browser->current_page);
332 SetHandleSize(browser->current_page, sizeof(struct page) + size);
333 HLock(browser->current_page);
334 }
335
336 if (committed)
337 browser_statusf(browser, "Done (%ld bytes)",
338 (*(browser->current_page))->content_len);
339
340 browser_find_links(browser);
341 }
342
343 void
344 browser_stop_loading_page(struct browser *browser)
345 {
346 page_handle pageh;
347 struct page *page;
348
349 if (!browser->loading_page)
350 return;
351
352 page = *(browser->loading_page);
353
354 if ((*(browser->loading_page))->download_frefnum)
355 FSClose((*(browser->loading_page))->download_frefnum);
356
357 /* only dispose if uncommitted */
358 if (browser->current_page == browser->loading_page) {
359 if (page->request)
360 page->handler->request_cleanup(browser->loading_page);
361 HUnlock(browser->loading_page);
362 } else {
363 browser_page_free(browser->loading_page);
364 }
365
366 browser->loading_page = NULL;
367 browser_update_buttons(browser);
368 }
369
370 void
371 browser_follow_redir(struct browser *browser)
372 {
373 page_handle pageh;
374 struct page *page;
375 struct URI *redir;
376
377 if (!browser->loading_page)
378 return;
379
380 redir = (*(browser->loading_page))->redir_to;
381
382 browser_stop_loading_page(browser);
383
384 if (!redir)
385 return;
386
387 if (++browser->redirs == 5)
388 warn("Too many redirections, not following to %s",
389 redir->str);
390
391 browser_statusf(browser, "Following redirection to %s", redir->str);
392 browser_create_page(browser, redir);
393 }
394
395 void
396 browser_free_links(struct browser *browser)
397 {
398 struct browser_link *link;
399
400 while (browser->first_link) {
401 link = browser->first_link;
402 browser->first_link = link->next_link;
403 xfree(&link);
404 }
405
406 browser->first_link = NULL;
407 browser->last_link = NULL;
408
409 if (browser->hover_link) {
410 SetCursor(&arrow);
411 browser->hover_link = NULL;
412 browser_statusf(browser, "");
413 }
414 }
415
416 void
417 browser_atexit(struct focusable *focusable)
418 {
419 struct browser *browser = (struct browser *)focusable->cookie;
420
421 if (browser) {
422 browser_stop_loading_page(browser);
423 browser_free_links(browser);
424 }
425
426 scsi_cleanup();
427 }
428
429 void
430 browser_update_menu(struct browser *browser)
431 {
432 size_t vlines;
433 Cell cell = { 0, 0 };
434
435 TextFont(systemFont);
436 TextSize(12);
437
438 if ((*(browser->uri_te))->selStart == (*(browser->uri_te))->selEnd) {
439 DisableItem(edit_menu, EDIT_MENU_CUT_ID);
440 DisableItem(edit_menu, EDIT_MENU_COPY_ID);
441 } else {
442 EnableItem(edit_menu, EDIT_MENU_CUT_ID);
443 EnableItem(edit_menu, EDIT_MENU_COPY_ID);
444 }
445
446 EnableItem(edit_menu, EDIT_MENU_PASTE_ID);
447 EnableItem(edit_menu, EDIT_MENU_SELECT_ALL_ID);
448 }
449
450 void
451 browser_update_buttons(struct browser *browser)
452 {
453 if (browser->current_page) {
454 if ((*(browser->current_page))->back_page)
455 HiliteControl(browser->back, 0);
456 else
457 HiliteControl(browser->back, 255);
458
459 if ((*(browser->current_page))->fwd_page)
460 HiliteControl(browser->fwd, 0);
461 else
462 HiliteControl(browser->fwd, 255);
463 } else {
464 HiliteControl(browser->back, 255);
465 HiliteControl(browser->fwd, 255);
466 }
467
468 if (browser->loading_page) {
469 HiliteControl(browser->go_stop, 0);
470 if ((*(browser->go_stop))->contrlTitle[1] == 'G')
471 SetCTitle(browser->go_stop, "\pStop");
472 } else {
473 if ((*(browser->uri_te))->teLength)
474 HiliteControl(browser->go_stop, 0);
475 else
476 HiliteControl(browser->go_stop, 255);
477
478 if ((*(browser->go_stop))->contrlTitle[1] != 'G')
479 SetCTitle(browser->go_stop, "\pGo");
480 }
481 }
482
483 void
484 browser_update(struct focusable *focusable, EventRecord *event)
485 {
486 struct browser *browser = (struct browser *)focusable->cookie;
487 Rect r;
488 short what = -1;
489
490 if (event != NULL)
491 what = event->what;
492
493 switch (what) {
494 case -1:
495 case updateEvt:
496 FillRgn(browser->header, fill_pattern);
497
498 DrawControls(browser->win);
499 DrawGrowIconOnly(browser->win);
500 browser_draw_status(browser);
501
502 r = (*(browser->uri_te))->viewRect;
503 InsetRect(&r, -1, -1);
504 FrameRect(&r);
505 TEUpdate(&r, browser->uri_te);
506
507 r = (*(browser->output_tv))->view;
508 InsetRect(&r, -1, -1);
509 FrameRect(&r);
510 TVUpdate(browser->output_tv);
511
512 ValidRect(&browser->win->portRect);
513
514 browser_update_menu(browser);
515 break;
516 }
517 }
518
519 void
520 browser_mouse_down(struct focusable *focusable, EventRecord *event)
521 {
522 struct browser *browser = (struct browser *)focusable->cookie;
523 struct browser_link *link;
524 Cell selected = { 0 };
525 Point p;
526 ControlHandle control;
527 Rect r;
528 short val, adj, page, len, part;
529 long off;
530 size_t n;
531
532 p = event->where;
533 GlobalToLocal(&p);
534
535 r = (*(browser->uri_te))->viewRect;
536 if (PtInRect(p, &r)) {
537 TEClick(p, ((event->modifiers & shiftKey) != 0), browser->uri_te);
538 browser_update_menu(browser);
539 return;
540 }
541
542 r = (*(browser->output_tv))->view;
543 if (PtInRect(p, &r)) {
544 TVClick(browser->output_tv, p,
545 ((event->modifiers & shiftKey) != 0));
546
547 if (browser->first_link &&
548 (off = TVGetOffset(browser->output_tv, p)) >= 0) {
549 for (link = browser->first_link; link; link = link->next_link) {
550 if ((link->pos <= off) && (off < link->pos + link->len)) {
551 #if 0
552 if (event->modifiers & cmdKey) {
553 browser_init(link->link);
554 break;
555 }
556 #endif
557 browser_go_uri_str(browser, link->uri);
558 break;
559 }
560 }
561 }
562
563 browser_update_menu(browser);
564 return;
565 }
566
567 switch (part = FindControl(p, browser->win, &control)) {
568 case inButton:
569 if (TrackControl(control, p, 0L) && control == browser->go_stop) {
570 if (browser->loading_page) {
571 browser_stop_loading_page(browser);
572 browser_statusf(browser, "Stopped");
573 } else
574 browser_go_uri_field(browser);
575 } else if (TrackControl(control, p, 0L) && control == browser->back)
576 browser_go_dir(browser, -1);
577 else if (TrackControl(control, p, 0L) && control == browser->fwd)
578 browser_go_dir(browser, 1);
579 break;
580 case inUpButton:
581 case inDownButton:
582 case inPageUp:
583 case inPageDown:
584 if (control != browser->output_tv_scroller)
585 break;
586 TVSetTrackScrollControl(browser->output_tv);
587 TrackControl(control, p, TVTrackScrollControl);
588 browser_find_links(browser);
589 break;
590 case inThumb:
591 val = GetCtlValue(control);
592 if (TrackControl(control, p, 0L) == 0)
593 break;
594 adj = val - GetCtlValue(control);
595 if (adj != 0) {
596 val -= adj;
597 if (control == browser->output_tv_scroller)
598 TVScroll(browser->output_tv, 0, adj);
599 SetCtlValue(control, val);
600 browser_find_links(browser);
601 }
602 break;
603 }
604 }
605
606 void
607 browser_mouse_move(struct focusable *focusable, EventRecord *event)
608 {
609 struct browser *browser = (struct browser *)(focusable->cookie);
610 struct browser_link *link;
611 long h, v;
612
613 if (event->where.v < (*(browser->output_tv))->view.top)
614 goto no_link;
615
616 h = event->where.h - (*(browser->output_tv))->view.left -
617 (*(browser->output_tv))->scrolled.left;
618 v = event->where.v - (*(browser->output_tv))->view.top -
619 (*(browser->output_tv))->scrolled.top;
620
621 for (link = browser->first_link; link; link = link->next_link) {
622 if (h >= link->rect.left && h <= link->rect.right &&
623 v >= link->rect.top && v <= link->rect.bottom) {
624 if (link == browser->hover_link)
625 return;
626 browser->hover_link = link;
627 SetCursor(&finger_cursor);
628 if (browser->hover_link->uri[0])
629 browser_statusf(browser, browser->hover_link->uri);
630 else
631 browser_statusf(browser, "");
632 return;
633 }
634 }
635
636 no_link:
637 SetCursor(&arrow);
638 if (browser->hover_link) {
639 browser->hover_link = NULL;
640 browser_statusf(browser, "");
641 }
642 }
643
644 void
645 browser_key_down(struct focusable *focusable, EventRecord *event)
646 {
647 struct browser *browser = (struct browser *)(focusable->cookie);
648 TERec *te;
649 char k;
650
651 k = (event->message & charCodeMask);
652
653 if (k == '\r') {
654 browser_go_uri_field(browser);
655 return;
656 }
657
658 TEKey(k, browser->uri_te);
659 TESelView(browser->uri_te);
660
661 browser_update_buttons(browser);
662 }
663
664 bool
665 browser_handle_menu(struct focusable *focusable, short menu, short item)
666 {
667 struct browser *browser = (struct browser *)focusable->cookie;
668
669 switch (menu) {
670 case EDIT_MENU_ID:
671 switch (item) {
672 case EDIT_MENU_CUT_ID:
673 TECut(browser->uri_te);
674 return true;
675 case EDIT_MENU_COPY_ID:
676 TECopy(browser->uri_te);
677 return true;
678 case EDIT_MENU_PASTE_ID:
679 TEPaste(browser->uri_te);
680 return true;
681 case EDIT_MENU_SELECT_ALL_ID:
682 TESetSelect(0, 1024 * 32, browser->uri_te);
683 return true;
684 }
685 browser_update_buttons(browser);
686 break;
687 }
688
689 return false;
690 }
691
692 void
693 browser_go_uri_field(struct browser *browser)
694 {
695 char *uristr;
696 struct URI *uri;
697 size_t len;
698
699 len = (*(browser->uri_te))->teLength;
700 uristr = xmalloc(7 + len + 1);
701 if (uristr == NULL) {
702 warn("Out of memory");
703 return;
704 }
705 HLock(browser->uri_te);
706 HLock((*(browser->uri_te))->hText);
707 memcpy(uristr, *((*(browser->uri_te))->hText), len);
708 HUnlock((*(browser->uri_te))->hText);
709 HUnlock(browser->uri_te);
710 uristr[len] = '\0';
711
712 uri = parse_uri(uristr);
713 if (uri == NULL) {
714 /* kids these days */
715 memmove(uristr + 7, uristr, len + 1);
716 memcpy(uristr, "http://", 7);
717 uri = parse_uri(uristr);
718 if (uri == NULL)
719 goto fail;
720 }
721
722 xfree(&uristr);
723
724 if (!browser_create_page(browser, uri))
725 xfree(&uri);
726
727 return;
728
729 fail:
730 warn("Could not parse URI \"%s\"", uristr);
731 xfree(&uristr);
732 }
733
734 void
735 browser_go_uri_str(struct browser *browser, char *str)
736 {
737 struct URI *uri;
738
739 uri = parse_uri(str);
740 if (uri == NULL) {
741 warn("Failed to parse URI \"%s\"", str);
742 return;
743 }
744 if (!browser_create_page(browser, uri))
745 xfree(&uri);
746 }
747
748 void
749 browser_go_dir(struct browser *browser, short dir)
750 {
751 page_handle pageh, opage;
752 struct page *page;
753
754 if (dir != -1 && dir != 1)
755 return;
756
757 browser_statusf(browser, "");
758 browser_stop_loading_page(browser);
759
760 pageh = browser->current_page;
761 if (!pageh)
762 return;
763
764 opage = (dir == -1) ? (*pageh)->back_page : (*pageh)->fwd_page;
765 if (!opage)
766 return;
767
768 HLock(opage);
769 browser->loading_page = opage;
770 browser_update_buttons(browser);
771
772 if ((*opage)->handler->reset)
773 (*opage)->handler->reset(opage);
774 else
775 (*opage)->content_pos = 0;
776
777 browser_commit_to_loading_page(browser);
778
779 HUnlock(opage);
780 }
781
782 page_handle
783 browser_create_page(struct browser *browser, struct URI *uri)
784 {
785 page_handle pageh;
786 struct page *page;
787 struct page_handler *handler;
788 size_t len;
789 short n;
790
791 browser_stop_loading_page(browser);
792
793 for (n = 0; n < nitems(page_handlers); n++) {
794 handler = page_handlers[n];
795
796 if (!handler->accept(uri))
797 continue;
798
799 pageh = (page_handle)NewHandleClear(sizeof(struct page) +
800 PAGE_CONTENT_CHUNK_SIZE);
801 if (pageh == NULL) {
802 warn("Out of memory for new page");
803 return NULL;
804 }
805 HLock(pageh);
806 page = *pageh;
807 page->browser = browser;
808 page->uri = uri;
809 page->handler = handler;
810 page->content_size = PAGE_CONTENT_CHUNK_SIZE;
811 HUnlock(pageh);
812
813 browser->loading_page = pageh;
814 browser_update_buttons(browser);
815
816 HLock(pageh);
817 if (!handler->request_init(pageh)) {
818 /* handler failed, no point in keeping page */
819 browser->loading_page = NULL;
820 browser_update_buttons(browser);
821 browser_page_free(pageh);
822 return NULL;
823 }
824
825 HUnlock(pageh);
826 return pageh;
827 }
828
829 fail:
830 warn("Could not find handler for URI");
831 return NULL;
832 }
833
834 size_t
835 browser_statusf(struct browser *browser, const char *format, ...)
836 {
837 va_list argptr;
838
839 va_start(argptr, format);
840 browser->status_length = vsnprintf(browser->status_text,
841 sizeof(browser->status_text), format, argptr);
842 if (browser->status_length >= sizeof(browser->status_text))
843 browser->status_length = sizeof(browser->status_text) - 1;
844 va_end(argptr);
845
846 browser_draw_status(browser);
847 }
848
849 void
850 browser_draw_status(struct browser *browser)
851 {
852 EraseRect(&browser->status_rect);
853 FrameRect(&browser->status_rect);
854
855 if (!browser->status_length)
856 return;
857
858 MoveTo(browser->status_rect.left + 5, browser->status_rect.bottom - 5);
859 TextFont(BROWSER_STATUS_FONT);
860 TextFace(0);
861 TextSize(BROWSER_STATUS_FONT_SIZE);
862 DrawText(browser->status_text, 0, browser->status_length);
863 }
864
865 struct TVStyle *
866 browser_build_tvstyle(struct browser *browser)
867 {
868 static struct TVStyle style;
869
870 memset(&style, 0, sizeof(style));
871
872 style.font = BROWSER_FONT;
873 style.size = BROWSER_FONT_SIZE;
874 style.style = 0;
875
876 if (browser->style & STYLE_BOLD)
877 style.style |= bold | condense;
878 if (browser->style & (STYLE_H1 | STYLE_H2 | STYLE_H3 | STYLE_H4 |
879 STYLE_H5 | STYLE_H6))
880 style.style |= bold;
881 if (browser->style & STYLE_ITALIC)
882 style.style |= italic;
883 if (browser->style & (STYLE_LINK | STYLE_UNDERLINE))
884 style.style |= underline;
885 if (browser->style & STYLE_PRE) {
886 style.font = BROWSER_PRE_FONT;
887 style.size = BROWSER_PRE_FONT_SIZE;
888 }
889
890 if (browser->style & STYLE_H1)
891 style.size += 8;
892 else if (browser->style & STYLE_H2)
893 style.size += 4;
894 else if (browser->style & STYLE_H3)
895 style.size += 2;
896 else if (browser->style & STYLE_H5)
897 style.size -= 2;
898 else if (browser->style & STYLE_H6)
899 style.size -= 4;
900
901 return &style;
902 }
903
904 size_t
905 browser_print_bitmap(struct browser *browser, BitMap *icon,
906 FontInfo *sizing)
907 {
908 struct TVStyle *style;
909
910 style = browser_build_tvstyle(browser);
911 style->font_info = *sizing;
912
913 return !!TVAppendBitMap(browser->output_tv, icon, style);
914 }
915
916 size_t
917 browser_print(struct browser *browser, const char *str, size_t len,
918 bool newline)
919 {
920 struct TVStyle *style;
921 size_t n, nlines;
922
923 style = browser_build_tvstyle(browser);
924
925 nlines = (*(browser->output_tv))->nlines;
926
927 (newline ? TVAppendLine : TVAppend)(browser->output_tv, style,
928 (char *)str, len);
929
930 if ((*(browser->output_tv))->nlines != nlines)
931 TVUpdateScrollbar(browser->output_tv, browser->output_tv_scroller);
932
933 return len;
934 }
935
936 void
937 browser_supress_updates(struct browser *browser, bool supress)
938 {
939 TVAutoCalc(browser->output_tv, !supress);
940 }
941
942 void
943 browser_recalc_scrollbar(struct browser *browser)
944 {
945 TVCalcLines(browser->output_tv);
946 TVUpdate(browser->output_tv);
947 TVUpdateScrollbar(browser->output_tv, browser->output_tv_scroller);
948 }
949
950 size_t
951 browser_print_link(struct browser *browser, const char *uri, size_t uri_len,
952 const char *title, size_t title_len, bool newline)
953 {
954 struct TVStyle *style;
955 struct browser_link *link;
956 size_t len;
957 unsigned long nlines;
958
959 len = sizeof(struct browser_link) + uri_len + 1 + title_len + 1;
960 link = xmalloczero(len);
961 if (link == NULL) {
962 warn("Out of memory allocating link");
963 return 0;
964 }
965
966 link->pos = (*(browser->output_tv))->text_length;
967 link->len = title_len ? title_len : uri_len;
968
969 link->uri = (char *)link + sizeof(struct browser_link);
970 memcpy(link->uri, uri, uri_len);
971 link->uri[uri_len] = '\0';
972
973 /* if no title, just point title at uri */
974 if (title_len) {
975 link->title = link->uri + uri_len + 1;
976 memcpy(link->title, title, title_len);
977 link->title[title_len] = '\0';
978 } else
979 link->title = link->uri;
980
981 if (browser->last_link)
982 browser->last_link->next_link = link;
983 else
984 browser->first_link = link;
985
986 browser->last_link = link;
987
988 browser->style |= STYLE_LINK;
989 style = browser_build_tvstyle(browser);
990 style->tag = (unsigned long)(char *)link;
991
992 nlines = (*(browser->output_tv))->nlines;
993 (newline ? TVAppendLine : TVAppend)(browser->output_tv, style,
994 link->title, link->len);
995 if ((*(browser->output_tv))->nlines != nlines)
996 TVUpdateScrollbar(browser->output_tv, browser->output_tv_scroller);
997
998 browser->style &= ~(STYLE_LINK);
999
1000 return link->len;
1001 }
1002
1003 void
1004 browser_commit_to_loading_page(struct browser *browser)
1005 {
1006 page_handle tpage, fpage, bpage;
1007 Rect r;
1008
1009 browser_free_links(browser);
1010
1011 if (browser->current_page == browser->loading_page)
1012 Debugger();
1013
1014 TESetText((*(browser->loading_page))->uri->str,
1015 strlen((*(browser->loading_page))->uri->str), browser->uri_te);
1016 TESetSelect(SHRT_MAX, SHRT_MAX, browser->uri_te);
1017
1018 r = (*(browser->uri_te))->viewRect;
1019 TEUpdate(&r, browser->uri_te);
1020
1021 TVClear(browser->output_tv);
1022 TVUpdateScrollbar(browser->output_tv, browser->output_tv_scroller);
1023 TVAutoCalc(browser->output_tv, true);
1024 browser->style = STYLE_NONE;
1025
1026 if (browser->current_page) {
1027 fpage = (*(browser->current_page))->fwd_page;
1028 bpage = (*(browser->current_page))->back_page;
1029
1030 if (browser->loading_page == bpage) {
1031 /* we're going backwards in history */
1032 } else if (browser->loading_page == fpage) {
1033 /* we're going fowards in history */
1034 } else {
1035 /* purge any forward pages because we're forking history now */
1036 while (fpage) {
1037 tpage = (*fpage)->fwd_page;
1038 DisposeHandle(fpage);
1039 fpage = tpage;
1040 }
1041
1042 (*(browser->current_page))->fwd_page = browser->loading_page;
1043 (*(browser->loading_page))->back_page = browser->current_page;
1044 }
1045 }
1046
1047 browser->current_page = browser->loading_page;
1048 browser_update_buttons(browser);
1049 }
1050
1051 void
1052 browser_page_free(page_handle pageh)
1053 {
1054 struct page *page = *pageh;
1055
1056 if (page->handler->request_cleanup)
1057 page->handler->request_cleanup(pageh);
1058
1059 if (page->handler->free)
1060 page->handler->free(pageh);
1061
1062 if (page->redir_to)
1063 xfree(&page->redir_to);
1064
1065 xfree(&page->uri);
1066 DisposeHandle(pageh);
1067 }
1068
1069 bool
1070 browser_start_download(struct browser *browser, char *filename,
1071 char *dump_buf, size_t dump_len)
1072 {
1073 char buf[256], pfilename[32];
1074 SFReply reply;
1075 size_t wlen;
1076 short error, frefnum;
1077
1078 if (filename) {
1079 snprintf(buf, sizeof(buf), "Save %s:", filename);
1080 strlcpy(pfilename, filename, sizeof(pfilename));
1081 CtoPstr(pfilename);
1082 } else {
1083 snprintf(buf, sizeof(buf), "Save file:");
1084 pfilename[0] = '0';
1085 }
1086 CtoPstr(buf);
1087
1088 SFPutFile(centered_sfput_dialog(), buf, pfilename, NULL, &reply);
1089 if (!reply.good) {
1090 browser_statusf(browser, "Download canceled");
1091 return false;
1092 }
1093
1094 error = Create(reply.fName, reply.vRefNum, '????', '????');
1095 if (error && error != dupFNErr) {
1096 warn("Failed to create file %s: %d", PtoCstr(reply.fName), error);
1097 return false;
1098 }
1099
1100 error = FSOpen(reply.fName, reply.vRefNum, &frefnum);
1101 if (error) {
1102 warn("Failed to open new file %s: %d", PtoCstr(reply.fName), error);
1103 return false;
1104 }
1105
1106 error = SetEOF(frefnum, 0);
1107 if (error) {
1108 warn("Failed to truncate file %s: %d", PtoCstr(reply.fName), error);
1109 return false;
1110 }
1111
1112 if (dump_buf && dump_len) {
1113 wlen = dump_len;
1114 error = FSWrite(frefnum, &wlen, dump_buf);
1115 if (error || wlen != dump_len) {
1116 warn("Failed to write %ld bytes: %d", dump_len, error);
1117 return false;
1118 }
1119 (*(browser->loading_page))->download_len = dump_len;
1120 }
1121
1122 (*(browser->loading_page))->download_frefnum = frefnum;
1123
1124 /* subsequent fetches will use content as a temporary buffer */
1125 (*(browser->loading_page))->content_pos = 0;
1126 (*(browser->loading_page))->content_len = 0;
1127
1128 return true;
1129 }
1130
1131 struct page *
1132 browser_grow_page_content(struct page *page, size_t len)
1133 {
1134 unsigned long size, gsize;
1135 page_handle pageh;
1136
1137 pageh = (page_handle)RecoverHandle(page);
1138 if (pageh == NULL) {
1139 Debugger();
1140 return NULL;
1141 }
1142
1143 size = (*pageh)->content_size + MAX(len, PAGE_CONTENT_CHUNK_SIZE);
1144 gsize = sizeof(struct page) + size;
1145 HUnlock(pageh);
1146 SetHandleSize(pageh, gsize);
1147 if (MemError() != 0) {
1148 warn("Out of memory growing page to %ld bytes", gsize);
1149 return NULL;
1150 }
1151
1152 HLock(pageh);
1153 (*pageh)->content_size = size;
1154
1155 /* keep locked */
1156 return *pageh;
1157 }
1158
1159 void
1160 browser_find_links(struct browser *browser)
1161 {
1162 bool ret;
1163 BigRect visible;
1164
1165 visible.top = -((*(browser->output_tv))->scrolled.top);
1166 visible.left = -((*(browser->output_tv))->scrolled.left);
1167 visible.bottom = visible.top + (*(browser->output_tv))->frame.bottom;
1168 visible.right = visible.left + (*(browser->output_tv))->frame.right;
1169
1170 ret = TVFindInRect(browser->output_tv, &visible, 0,
1171 browser_find_links_callback, browser);
1172 }
1173
1174 Boolean
1175 browser_find_links_callback(struct TVRec *tv,
1176 struct TVFindInRectMatch *match, void *cookie)
1177 {
1178 struct browser *browser = (struct browser *)cookie;
1179 struct browser_link *link;
1180
1181 if (match->style->tag == 0)
1182 return true;
1183
1184 /* probably just a newline */
1185 if (match->rect.left == match->rect.right)
1186 return true;
1187
1188 link = (struct browser_link *)(match->style->tag);
1189 link->rect = match->rect;
1190
1191 return true;
1192 }
1193
1194 bool
1195 page_queue_output(struct request *request, void *cookie, char **buf,
1196 size_t *len, bool did_write)
1197 {
1198 struct page *page = *((page_handle)cookie);
1199
1200 if (did_write == false) {
1201 if (request->output_pos == 0)
1202 browser_statusf(page->browser,
1203 "Connected to %s, sending request...", page->uri->hostname);
1204
1205 *len = request->output_len - request->output_pos;
1206 *buf = request->output + request->output_pos;
1207 return true;
1208 }
1209
1210 request->output_pos += *len;
1211
1212 return true;
1213 }
1214
1215 bool
1216 page_consume_data(struct request *request, void *cookie, char **buf,
1217 size_t *len, bool did_read)
1218 {
1219 struct page *page;
1220 long wlen;
1221 short err;
1222
1223 page = *((page_handle)cookie);
1224
1225 if (did_read) {
1226 if (page->download_frefnum) {
1227 wlen = *len;
1228 err = FSWrite(page->download_frefnum, &wlen, page->content);
1229 if (err || wlen != *len) {
1230 warn("Failed to write %ld bytes: %d", *len, err);
1231 return false;
1232 }
1233 page->download_len += wlen;
1234 browser_statusf(page->browser, "%ld bytes", page->download_len);
1235 } else {
1236 page->content_len += *len;
1237 browser_statusf(page->browser, "%ld bytes", page->content_len);
1238 }
1239 } else {
1240 if (page->content_len + *len >= page->content_size) {
1241 page = browser_grow_page_content(page, *len);
1242 if (page == NULL)
1243 return false;
1244 }
1245
1246 *buf = page->content + page->content_len;
1247
1248 if (!page->handler->process((page_handle)cookie))
1249 return false;
1250 }
1251
1252 return true;
1253 }
1254
1255 void
1256 page_request_cleanup(page_handle pageh)
1257 {
1258 struct page *page = *pageh;
1259
1260 if (page->request)
1261 request_xfree(&page->request);
1262 }
1263
1264 bool
1265 page_print_plaintext(page_handle pageh)
1266 {
1267 struct page *page = *pageh;
1268 size_t n, trail, skip, len, tlen, j;
1269 bool newline;
1270
1271 for (n = page->content_pos; n < page->content_len; n++) {
1272 if (page->content[n] != '\n' &&
1273 !(n == page->content_len - 1 && !PAGE_CAN_READ_MORE(page)))
1274 continue;
1275
1276 len = n - page->content_pos + 1;
1277 trail = 0;
1278 skip = 0;
1279 newline = false;
1280
1281 if (page->content[n] == '\n') {
1282 len--;
1283 trail = 1;
1284 newline = true;
1285
1286 if (n > 0 && page->content[n - 1] == '\r') {
1287 len--;
1288 trail++;
1289 }
1290 } else if (page->request != NULL)
1291 /* no newline at the end and fetching, so wait for more data */
1292 return true;
1293
1294 print_line:
1295 if (len)
1296 browser_print(page->browser,
1297 page->content + page->content_pos + skip, len, newline);
1298
1299 page->content_pos += skip + len + trail;
1300 }
1301
1302 if (!PAGE_CAN_READ_MORE(page) &&
1303 page->content_pos < page->content_len)
1304 browser_print(page->browser,
1305 page->content + page->content_pos,
1306 page->content_len - page->content_pos, false);
1307
1308 return PAGE_CAN_READ_MORE(page);
1309 }