/* * Copyright (c) 2021-2024 joshua stein * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include "detritus.h" #include "focusable.h" #define PADDING 6 #define BROWSER_FONT geneva #define BROWSER_FONT_SIZE 10 #define BROWSER_STATUS_FONT geneva #define BROWSER_STATUS_FONT_SIZE 9 #define BROWSER_PRE_FONT monaco #define BROWSER_PRE_FONT_SIZE 9 static Rect zerorect = { 0, 0, 0, 0 }; static Pattern fill_pattern; extern struct page_handler finger_handler; extern struct page_handler gemini_handler; extern struct page_handler gopher_handler; extern struct page_handler http_handler; struct page_handler * page_handlers[] = { &finger_handler, &gemini_handler, &gopher_handler, &http_handler, }; bool browser_close(struct focusable *focusable); void browser_idle(struct focusable *focusable, EventRecord *event); void browser_update_menu(struct browser *browser); void browser_update_buttons(struct browser *browser); void browser_update(struct focusable *focusable, EventRecord *event); void browser_key_down(struct focusable *focusable, EventRecord *event); void browser_mouse_down(struct focusable *focusable, EventRecord *event); void browser_mouse_move(struct focusable *focusable, EventRecord *event); bool browser_handle_menu(struct focusable *focusable, short menu, short item); void browser_atexit(struct focusable *focusable); void browser_go_uri_field(struct browser *browser); void browser_go_dir(struct browser *browser, short dir); page_handle browser_create_page(struct browser *browser, struct URI *uri); void browser_draw_status(struct browser *browser); struct TVStyle * browser_build_tvstyle(struct browser *browser); void browser_finished_loading(struct browser *browser); void browser_page_free(page_handle pageh); void browser_follow_redir(struct browser *browser); void browser_stop_loading_page(struct browser *browser); void browser_free_links(struct browser *browser); void browser_find_links(struct browser *browser); Boolean browser_find_links_callback(struct TVRec *tv, struct TVFindInRectMatch *match, void *cookie); void browser_idle(struct focusable *focusable, EventRecord *event) { struct browser *browser = (struct browser *)focusable->cookie; struct page_handler *handler; struct page *page; bool ret; TEIdle(browser->uri_te); GlobalToLocal(&event->where); browser_mouse_move(focusable, event); if (browser->loading_page) { HLock(browser->loading_page); page = *(browser->loading_page); handler = page->handler; ret = true; if (page->request != NULL) { ret = request_data_shuffle(page->request, handler->request_data_queuer, browser->loading_page, handler->request_data_consumer, browser->loading_page); /* handle may have grown/moved during fetch */ HLock(browser->loading_page); page = *(browser->loading_page); handler = page->handler; } if (!page->download_frefnum) ret &= handler->process(browser->loading_page); if (!ret) browser_finished_loading(browser); HUnlock(browser->loading_page); } } struct browser * browser_init(void) { char title[64]; struct browser *browser; struct focusable *focusable; Handle rgnsave; Rect bounds, te_bounds, padding, r; long width, height; browser = xmalloczero(sizeof(struct browser)); if (browser == NULL) panic("Out of memory allocating browser"); GetIndPattern(&fill_pattern, sysPatListID, 10); /* main window */ width = screenBits.bounds.right - screenBits.bounds.left - PADDING; width = MIN(width, 500); height = screenBits.bounds.bottom - screenBits.bounds.top - PADDING - (GetMBarHeight() * 2); height = MIN(height, 290); center_in_screen(width, height, true, &bounds); //bounds.top = (screenBits.bounds.bottom / 2); snprintf(title, sizeof(title), "%s", PROGRAM_NAME); CtoPstr(title); browser->win = NewWindow(0L, &bounds, title, false, noGrowDocProc, (WindowPtr)-1L, true, 0); if (!browser->win) panic("Can't create window"); SetPort(browser->win); browser->header = NewRgn(); OpenRgn(); r.top = 0; r.left = 0; r.right = bounds.right - bounds.left; r.bottom = 26; FrameRect(&r); rgnsave = browser->win->rgnSave; browser->win->rgnSave = NULL; /* back and forward */ bounds.top = PADDING; bounds.left = PADDING; bounds.right = bounds.left + 20; bounds.bottom = bounds.top + 16; browser->back = NewControl(browser->win, &bounds, "\p<", true, 1, 1, 1, pushButProc, 0L); HiliteControl(browser->back, 255); browser->win->rgnSave = rgnsave; FrameRoundRect(&bounds, 10, 10); browser->win->rgnSave = NULL; bounds.top = PADDING; bounds.left = bounds.right - 1; bounds.right = bounds.left + 20; browser->fwd = NewControl(browser->win, &bounds, "\p>", true, 1, 1, 1, pushButProc, 0L); HiliteControl(browser->fwd, 255); browser->win->rgnSave = rgnsave; FrameRoundRect(&bounds, 10, 10); browser->win->rgnSave = NULL; /* uri TE */ bounds.left = bounds.right + PADDING; bounds.right = browser->win->portRect.right - PADDING - 60; te_bounds = bounds; InsetRect(&te_bounds, 2, 2); TextFont(geneva); TextSize(10); browser->uri_te = TENew(&te_bounds, &bounds); if (browser->uri_te == NULL) panic("Out of memory allocating TE"); TEAutoView(false, browser->uri_te); TEActivate(browser->uri_te); browser->win->rgnSave = rgnsave; FrameRect(&bounds); browser->win->rgnSave = NULL; bounds.left = bounds.right + PADDING; bounds.right = browser->win->portRect.right - PADDING; browser->go_stop = NewControl(browser->win, &bounds, "\pGo", true, 1, 1, 1, pushButProc, 0L); HiliteControl(browser->go_stop, 255); browser->win->rgnSave = rgnsave; FrameRoundRect(&bounds, 10, 10); CloseRgn(browser->header); /* output TV */ bounds.left = 0; bounds.right = browser->win->portRect.right - SCROLLBAR_WIDTH + 1; bounds.top = bounds.bottom + PADDING; bounds.bottom = browser->win->portRect.bottom - SCROLLBAR_WIDTH + 1; SetRect(&padding, 4, 4, 4, 4); browser->output_tv = TVNew(&bounds, &padding); if (browser->output_tv == NULL) panic("Out of memory allocating TV"); /* scrollbar for output text */ bounds.right = browser->win->portRect.right + 1; bounds.left = bounds.right - SCROLLBAR_WIDTH; bounds.bottom++; bounds.top--; browser->output_tv_scroller = NewControl(browser->win, &bounds, "\p", true, 1, 1, 1, scrollBarProc, 0L); browser_update_menu(browser); TVUpdateScrollbar(browser->output_tv, browser->output_tv_scroller); browser->status_rect.left = -1; browser->status_rect.bottom = browser->win->portRect.bottom + 1; browser->status_rect.right = browser->win->portRect.right - SCROLLBAR_WIDTH + 2; browser->status_rect.top = browser->status_rect.bottom - SCROLLBAR_WIDTH; focusable = xmalloczero(sizeof(struct focusable)); if (focusable == NULL) panic("Out of memory!"); focusable->cookie = browser; focusable->win = browser->win; focusable->idle = browser_idle; focusable->update = browser_update; focusable->mouse_down = browser_mouse_down; focusable->mouse_move = browser_mouse_move; focusable->key_down = browser_key_down; focusable->menu = browser_handle_menu; focusable->close = browser_close; focusable->atexit = browser_atexit; focusable_add(focusable); browser_update(focusable, NULL); browser_statusf(browser, "Hello, cyberpal"); return browser; } bool browser_close(struct focusable *focusable) { struct browser *browser = (struct browser *)focusable->cookie; page_handle pageh, fpage, bpage, tpage; struct page *page; browser_stop_loading_page(browser); if (browser->current_page) { fpage = (*(browser->current_page))->fwd_page; while (fpage) { HLock(fpage); tpage = (*fpage)->fwd_page; browser_page_free(fpage); fpage = tpage; } bpage = (*pageh)->back_page; while (bpage) { HLock(bpage); tpage = (*bpage)->back_page; browser_page_free(bpage); bpage = tpage; } HLock(pageh); browser_page_free(pageh); } browser_free_links(browser); TEDispose(browser->uri_te); TVDispose(browser->output_tv); DisposeWindow(browser->win); xfree(&browser); focusable->cookie = NULL; scsi_cleanup(); return true; } void browser_finished_loading(struct browser *browser) { struct URI *redir; size_t size; bool download = false, committed; if ((*(browser->loading_page))->redir_to) { browser_follow_redir(browser); return; } if ((*(browser->loading_page))->download_frefnum) { download = true; FSClose((*(browser->loading_page))->download_frefnum); browser_statusf(browser, "Downloaded (%ld bytes)", (*(browser->loading_page))->download_len); } committed = (browser->loading_page == browser->current_page); browser_stop_loading_page(browser); browser->redirs = 0; if (download || !committed) return; /* release some wasted memory if we can */ if ((*(browser->current_page))->content_size > (*(browser->current_page))->content_len) { size = (*(browser->current_page))->content_len; (*(browser->current_page))->content_size = size; HUnlock(browser->current_page); SetHandleSize(browser->current_page, sizeof(struct page) + size); HLock(browser->current_page); } if (committed) browser_statusf(browser, "Done (%ld bytes)", (*(browser->current_page))->content_len); browser_find_links(browser); } void browser_stop_loading_page(struct browser *browser) { page_handle pageh; struct page *page; if (!browser->loading_page) return; page = *(browser->loading_page); if ((*(browser->loading_page))->download_frefnum) FSClose((*(browser->loading_page))->download_frefnum); /* only dispose if uncommitted */ if (browser->current_page == browser->loading_page) { if (page->request) page->handler->request_cleanup(browser->loading_page); HUnlock(browser->loading_page); } else { browser_page_free(browser->loading_page); } browser->loading_page = NULL; browser_update_buttons(browser); } void browser_follow_redir(struct browser *browser) { page_handle pageh; struct page *page; struct URI *redir; if (!browser->loading_page) return; redir = (*(browser->loading_page))->redir_to; browser_stop_loading_page(browser); if (!redir) return; if (++browser->redirs == 5) warn("Too many redirections, not following to %s", redir->str); browser_statusf(browser, "Following redirection to %s", redir->str); browser_create_page(browser, redir); } void browser_free_links(struct browser *browser) { struct browser_link *link; while (browser->first_link) { link = browser->first_link; browser->first_link = link->next_link; xfree(&link); } browser->first_link = NULL; browser->last_link = NULL; if (browser->hover_link) { SetCursor(&arrow); browser->hover_link = NULL; browser_statusf(browser, ""); } } void browser_atexit(struct focusable *focusable) { struct browser *browser = (struct browser *)focusable->cookie; if (browser) { browser_stop_loading_page(browser); browser_free_links(browser); } scsi_cleanup(); } void browser_update_menu(struct browser *browser) { size_t vlines; Cell cell = { 0, 0 }; TextFont(systemFont); TextSize(12); if ((*(browser->uri_te))->selStart == (*(browser->uri_te))->selEnd) { DisableItem(edit_menu, EDIT_MENU_CUT_ID); DisableItem(edit_menu, EDIT_MENU_COPY_ID); } else { EnableItem(edit_menu, EDIT_MENU_CUT_ID); EnableItem(edit_menu, EDIT_MENU_COPY_ID); } EnableItem(edit_menu, EDIT_MENU_PASTE_ID); EnableItem(edit_menu, EDIT_MENU_SELECT_ALL_ID); } void browser_update_buttons(struct browser *browser) { if (browser->current_page) { if ((*(browser->current_page))->back_page) HiliteControl(browser->back, 0); else HiliteControl(browser->back, 255); if ((*(browser->current_page))->fwd_page) HiliteControl(browser->fwd, 0); else HiliteControl(browser->fwd, 255); } else { HiliteControl(browser->back, 255); HiliteControl(browser->fwd, 255); } if (browser->loading_page) { HiliteControl(browser->go_stop, 0); if ((*(browser->go_stop))->contrlTitle[1] == 'G') SetCTitle(browser->go_stop, "\pStop"); } else { if ((*(browser->uri_te))->teLength) HiliteControl(browser->go_stop, 0); else HiliteControl(browser->go_stop, 255); if ((*(browser->go_stop))->contrlTitle[1] != 'G') SetCTitle(browser->go_stop, "\pGo"); } } void browser_update(struct focusable *focusable, EventRecord *event) { struct browser *browser = (struct browser *)focusable->cookie; Rect r; short what = -1; if (event != NULL) what = event->what; switch (what) { case -1: case updateEvt: FillRgn(browser->header, fill_pattern); DrawControls(browser->win); DrawGrowIconOnly(browser->win); browser_draw_status(browser); r = (*(browser->uri_te))->viewRect; InsetRect(&r, -1, -1); FrameRect(&r); TEUpdate(&r, browser->uri_te); r = (*(browser->output_tv))->view; InsetRect(&r, -1, -1); FrameRect(&r); TVUpdate(browser->output_tv); ValidRect(&browser->win->portRect); browser_update_menu(browser); break; } } void browser_mouse_down(struct focusable *focusable, EventRecord *event) { struct browser *browser = (struct browser *)focusable->cookie; struct browser_link *link; Cell selected = { 0 }; Point p; ControlHandle control; Rect r; short val, adj, page, len, part; long off; size_t n; p = event->where; GlobalToLocal(&p); r = (*(browser->uri_te))->viewRect; if (PtInRect(p, &r)) { TEClick(p, ((event->modifiers & shiftKey) != 0), browser->uri_te); browser_update_menu(browser); return; } r = (*(browser->output_tv))->view; if (PtInRect(p, &r)) { TVClick(browser->output_tv, p, ((event->modifiers & shiftKey) != 0)); if (browser->first_link && (off = TVGetOffset(browser->output_tv, p)) >= 0) { for (link = browser->first_link; link; link = link->next_link) { if ((link->pos <= off) && (off < link->pos + link->len)) { #if 0 if (event->modifiers & cmdKey) { browser_init(link->link); break; } #endif browser_go_uri_str(browser, link->uri); break; } } } browser_update_menu(browser); return; } switch (part = FindControl(p, browser->win, &control)) { case inButton: if (TrackControl(control, p, 0L) && control == browser->go_stop) { if (browser->loading_page) { browser_stop_loading_page(browser); browser_statusf(browser, "Stopped"); } else browser_go_uri_field(browser); } else if (TrackControl(control, p, 0L) && control == browser->back) browser_go_dir(browser, -1); else if (TrackControl(control, p, 0L) && control == browser->fwd) browser_go_dir(browser, 1); break; case inUpButton: case inDownButton: case inPageUp: case inPageDown: if (control != browser->output_tv_scroller) break; TVSetTrackScrollControl(browser->output_tv); TrackControl(control, p, TVTrackScrollControl); browser_find_links(browser); break; case inThumb: val = GetCtlValue(control); if (TrackControl(control, p, 0L) == 0) break; adj = val - GetCtlValue(control); if (adj != 0) { val -= adj; if (control == browser->output_tv_scroller) TVScroll(browser->output_tv, 0, adj); SetCtlValue(control, val); browser_find_links(browser); } break; } } void browser_mouse_move(struct focusable *focusable, EventRecord *event) { struct browser *browser = (struct browser *)(focusable->cookie); struct browser_link *link; long h, v; if (event->where.v < (*(browser->output_tv))->view.top) goto no_link; h = event->where.h - (*(browser->output_tv))->view.left - (*(browser->output_tv))->scrolled.left; v = event->where.v - (*(browser->output_tv))->view.top - (*(browser->output_tv))->scrolled.top; for (link = browser->first_link; link; link = link->next_link) { if (h >= link->rect.left && h <= link->rect.right && v >= link->rect.top && v <= link->rect.bottom) { if (link == browser->hover_link) return; browser->hover_link = link; SetCursor(&finger_cursor); if (browser->hover_link->uri[0]) browser_statusf(browser, browser->hover_link->uri); else browser_statusf(browser, ""); return; } } no_link: SetCursor(&arrow); if (browser->hover_link) { browser->hover_link = NULL; browser_statusf(browser, ""); } } void browser_key_down(struct focusable *focusable, EventRecord *event) { struct browser *browser = (struct browser *)(focusable->cookie); TERec *te; char k; k = (event->message & charCodeMask); if (k == '\r') { browser_go_uri_field(browser); return; } TEKey(k, browser->uri_te); TESelView(browser->uri_te); browser_update_buttons(browser); } bool browser_handle_menu(struct focusable *focusable, short menu, short item) { struct browser *browser = (struct browser *)focusable->cookie; switch (menu) { case EDIT_MENU_ID: switch (item) { case EDIT_MENU_CUT_ID: TECut(browser->uri_te); return true; case EDIT_MENU_COPY_ID: TECopy(browser->uri_te); return true; case EDIT_MENU_PASTE_ID: TEPaste(browser->uri_te); return true; case EDIT_MENU_SELECT_ALL_ID: TESetSelect(0, 1024 * 32, browser->uri_te); return true; } browser_update_buttons(browser); break; } return false; } void browser_go_uri_field(struct browser *browser) { char *uristr; struct URI *uri; size_t len; len = (*(browser->uri_te))->teLength; uristr = xmalloc(7 + len + 1); if (uristr == NULL) { warn("Out of memory"); return; } HLock(browser->uri_te); HLock((*(browser->uri_te))->hText); memcpy(uristr, *((*(browser->uri_te))->hText), len); HUnlock((*(browser->uri_te))->hText); HUnlock(browser->uri_te); uristr[len] = '\0'; uri = parse_uri(uristr); if (uri == NULL) { /* kids these days */ memmove(uristr + 7, uristr, len + 1); memcpy(uristr, "http://", 7); uri = parse_uri(uristr); if (uri == NULL) goto fail; } xfree(&uristr); if (!browser_create_page(browser, uri)) xfree(&uri); return; fail: warn("Could not parse URI \"%s\"", uristr); xfree(&uristr); } void browser_go_uri_str(struct browser *browser, char *str) { struct URI *uri; uri = parse_uri(str); if (uri == NULL) { warn("Failed to parse URI \"%s\"", str); return; } if (!browser_create_page(browser, uri)) xfree(&uri); } void browser_go_dir(struct browser *browser, short dir) { page_handle pageh, opage; struct page *page; if (dir != -1 && dir != 1) return; browser_statusf(browser, ""); browser_stop_loading_page(browser); pageh = browser->current_page; if (!pageh) return; opage = (dir == -1) ? (*pageh)->back_page : (*pageh)->fwd_page; if (!opage) return; HLock(opage); browser->loading_page = opage; browser_update_buttons(browser); if ((*opage)->handler->reset) (*opage)->handler->reset(opage); else (*opage)->content_pos = 0; browser_commit_to_loading_page(browser); HUnlock(opage); } page_handle browser_create_page(struct browser *browser, struct URI *uri) { page_handle pageh; struct page *page; struct page_handler *handler; size_t len; short n; browser_stop_loading_page(browser); for (n = 0; n < nitems(page_handlers); n++) { handler = page_handlers[n]; if (!handler->accept(uri)) continue; pageh = (page_handle)NewHandleClear(sizeof(struct page) + PAGE_CONTENT_CHUNK_SIZE); if (pageh == NULL) { warn("Out of memory for new page"); return NULL; } HLock(pageh); page = *pageh; page->browser = browser; page->uri = uri; page->handler = handler; page->content_size = PAGE_CONTENT_CHUNK_SIZE; HUnlock(pageh); browser->loading_page = pageh; browser_update_buttons(browser); HLock(pageh); if (!handler->request_init(pageh)) { /* handler failed, no point in keeping page */ browser->loading_page = NULL; browser_update_buttons(browser); browser_page_free(pageh); return NULL; } HUnlock(pageh); return pageh; } fail: warn("Could not find handler for URI"); return NULL; } size_t browser_statusf(struct browser *browser, const char *format, ...) { va_list argptr; va_start(argptr, format); browser->status_length = vsnprintf(browser->status_text, sizeof(browser->status_text), format, argptr); if (browser->status_length >= sizeof(browser->status_text)) browser->status_length = sizeof(browser->status_text) - 1; va_end(argptr); browser_draw_status(browser); } void browser_draw_status(struct browser *browser) { EraseRect(&browser->status_rect); FrameRect(&browser->status_rect); if (!browser->status_length) return; MoveTo(browser->status_rect.left + 5, browser->status_rect.bottom - 5); TextFont(BROWSER_STATUS_FONT); TextFace(0); TextSize(BROWSER_STATUS_FONT_SIZE); DrawText(browser->status_text, 0, browser->status_length); } struct TVStyle * browser_build_tvstyle(struct browser *browser) { static struct TVStyle style; memset(&style, 0, sizeof(style)); style.font = BROWSER_FONT; style.size = BROWSER_FONT_SIZE; style.style = 0; if (browser->style & STYLE_BOLD) style.style |= bold | condense; if (browser->style & (STYLE_H1 | STYLE_H2 | STYLE_H3 | STYLE_H4 | STYLE_H5 | STYLE_H6)) style.style |= bold; if (browser->style & STYLE_ITALIC) style.style |= italic; if (browser->style & (STYLE_LINK | STYLE_UNDERLINE)) style.style |= underline; if (browser->style & STYLE_PRE) { style.font = BROWSER_PRE_FONT; style.size = BROWSER_PRE_FONT_SIZE; } if (browser->style & STYLE_H1) style.size += 8; else if (browser->style & STYLE_H2) style.size += 4; else if (browser->style & STYLE_H3) style.size += 2; else if (browser->style & STYLE_H5) style.size -= 2; else if (browser->style & STYLE_H6) style.size -= 4; return &style; } size_t browser_print_bitmap(struct browser *browser, BitMap *icon, FontInfo *sizing) { struct TVStyle *style; style = browser_build_tvstyle(browser); style->font_info = *sizing; return !!TVAppendBitMap(browser->output_tv, icon, style); } size_t browser_print(struct browser *browser, const char *str, size_t len, bool newline) { struct TVStyle *style; size_t n, nlines; style = browser_build_tvstyle(browser); nlines = (*(browser->output_tv))->nlines; (newline ? TVAppendLine : TVAppend)(browser->output_tv, style, (char *)str, len); if ((*(browser->output_tv))->nlines != nlines) TVUpdateScrollbar(browser->output_tv, browser->output_tv_scroller); return len; } void browser_supress_updates(struct browser *browser, bool supress) { TVAutoCalc(browser->output_tv, !supress); } void browser_recalc_scrollbar(struct browser *browser) { TVCalcLines(browser->output_tv); TVUpdate(browser->output_tv); TVUpdateScrollbar(browser->output_tv, browser->output_tv_scroller); } size_t browser_print_link(struct browser *browser, const char *uri, size_t uri_len, const char *title, size_t title_len, bool newline) { struct TVStyle *style; struct browser_link *link; size_t len; unsigned long nlines; len = sizeof(struct browser_link) + uri_len + 1 + title_len + 1; link = xmalloczero(len); if (link == NULL) { warn("Out of memory allocating link"); return 0; } link->pos = (*(browser->output_tv))->text_length; link->len = title_len ? title_len : uri_len; link->uri = (char *)link + sizeof(struct browser_link); memcpy(link->uri, uri, uri_len); link->uri[uri_len] = '\0'; /* if no title, just point title at uri */ if (title_len) { link->title = link->uri + uri_len + 1; memcpy(link->title, title, title_len); link->title[title_len] = '\0'; } else link->title = link->uri; if (browser->last_link) browser->last_link->next_link = link; else browser->first_link = link; browser->last_link = link; browser->style |= STYLE_LINK; style = browser_build_tvstyle(browser); style->tag = (unsigned long)(char *)link; nlines = (*(browser->output_tv))->nlines; (newline ? TVAppendLine : TVAppend)(browser->output_tv, style, link->title, link->len); if ((*(browser->output_tv))->nlines != nlines) TVUpdateScrollbar(browser->output_tv, browser->output_tv_scroller); browser->style &= ~(STYLE_LINK); return link->len; } void browser_commit_to_loading_page(struct browser *browser) { page_handle tpage, fpage, bpage; Rect r; browser_free_links(browser); if (browser->current_page == browser->loading_page) Debugger(); TESetText((*(browser->loading_page))->uri->str, strlen((*(browser->loading_page))->uri->str), browser->uri_te); TESetSelect(SHRT_MAX, SHRT_MAX, browser->uri_te); r = (*(browser->uri_te))->viewRect; TEUpdate(&r, browser->uri_te); TVClear(browser->output_tv); TVUpdateScrollbar(browser->output_tv, browser->output_tv_scroller); TVAutoCalc(browser->output_tv, true); browser->style = STYLE_NONE; if (browser->current_page) { fpage = (*(browser->current_page))->fwd_page; bpage = (*(browser->current_page))->back_page; if (browser->loading_page == bpage) { /* we're going backwards in history */ } else if (browser->loading_page == fpage) { /* we're going fowards in history */ } else { /* purge any forward pages because we're forking history now */ while (fpage) { tpage = (*fpage)->fwd_page; DisposeHandle(fpage); fpage = tpage; } (*(browser->current_page))->fwd_page = browser->loading_page; (*(browser->loading_page))->back_page = browser->current_page; } } browser->current_page = browser->loading_page; browser_update_buttons(browser); } void browser_page_free(page_handle pageh) { struct page *page = *pageh; if (page->handler->request_cleanup) page->handler->request_cleanup(pageh); if (page->handler->free) page->handler->free(pageh); if (page->redir_to) xfree(&page->redir_to); xfree(&page->uri); DisposeHandle(pageh); } bool browser_start_download(struct browser *browser, char *filename, char *dump_buf, size_t dump_len) { char buf[256], pfilename[32]; SFReply reply; size_t wlen; short error, frefnum; if (filename) { snprintf(buf, sizeof(buf), "Save %s:", filename); strlcpy(pfilename, filename, sizeof(pfilename)); CtoPstr(pfilename); } else { snprintf(buf, sizeof(buf), "Save file:"); pfilename[0] = '0'; } CtoPstr(buf); SFPutFile(centered_sfput_dialog(), buf, pfilename, NULL, &reply); if (!reply.good) { browser_statusf(browser, "Download canceled"); return false; } error = Create(reply.fName, reply.vRefNum, '????', '????'); if (error && error != dupFNErr) { warn("Failed to create file %s: %d", PtoCstr(reply.fName), error); return false; } error = FSOpen(reply.fName, reply.vRefNum, &frefnum); if (error) { warn("Failed to open new file %s: %d", PtoCstr(reply.fName), error); return false; } error = SetEOF(frefnum, 0); if (error) { warn("Failed to truncate file %s: %d", PtoCstr(reply.fName), error); return false; } if (dump_buf && dump_len) { wlen = dump_len; error = FSWrite(frefnum, &wlen, dump_buf); if (error || wlen != dump_len) { warn("Failed to write %ld bytes: %d", dump_len, error); return false; } (*(browser->loading_page))->download_len = dump_len; } (*(browser->loading_page))->download_frefnum = frefnum; /* subsequent fetches will use content as a temporary buffer */ (*(browser->loading_page))->content_pos = 0; (*(browser->loading_page))->content_len = 0; return true; } struct page * browser_grow_page_content(struct page *page, size_t len) { unsigned long size, gsize; page_handle pageh; pageh = (page_handle)RecoverHandle(page); if (pageh == NULL) { Debugger(); return NULL; } size = (*pageh)->content_size + MAX(len, PAGE_CONTENT_CHUNK_SIZE); gsize = sizeof(struct page) + size; HUnlock(pageh); SetHandleSize(pageh, gsize); if (MemError() != 0) { warn("Out of memory growing page to %ld bytes", gsize); return NULL; } HLock(pageh); (*pageh)->content_size = size; /* keep locked */ return *pageh; } void browser_find_links(struct browser *browser) { bool ret; BigRect visible; visible.top = -((*(browser->output_tv))->scrolled.top); visible.left = -((*(browser->output_tv))->scrolled.left); visible.bottom = visible.top + (*(browser->output_tv))->frame.bottom; visible.right = visible.left + (*(browser->output_tv))->frame.right; ret = TVFindInRect(browser->output_tv, &visible, 0, browser_find_links_callback, browser); } Boolean browser_find_links_callback(struct TVRec *tv, struct TVFindInRectMatch *match, void *cookie) { struct browser *browser = (struct browser *)cookie; struct browser_link *link; if (match->style->tag == 0) return true; /* probably just a newline */ if (match->rect.left == match->rect.right) return true; link = (struct browser_link *)(match->style->tag); link->rect = match->rect; return true; } bool page_queue_output(struct request *request, void *cookie, char **buf, size_t *len, bool did_write) { struct page *page = *((page_handle)cookie); if (did_write == false) { if (request->output_pos == 0) browser_statusf(page->browser, "Connected to %s, sending request...", page->uri->hostname); *len = request->output_len - request->output_pos; *buf = request->output + request->output_pos; return true; } request->output_pos += *len; return true; } bool page_consume_data(struct request *request, void *cookie, char **buf, size_t *len, bool did_read) { struct page *page; long wlen; short err; page = *((page_handle)cookie); if (did_read) { if (page->download_frefnum) { wlen = *len; err = FSWrite(page->download_frefnum, &wlen, page->content); if (err || wlen != *len) { warn("Failed to write %ld bytes: %d", *len, err); return false; } page->download_len += wlen; browser_statusf(page->browser, "%ld bytes", page->download_len); } else { page->content_len += *len; browser_statusf(page->browser, "%ld bytes", page->content_len); } } else { if (page->content_len + *len >= page->content_size) { page = browser_grow_page_content(page, *len); if (page == NULL) return false; } *buf = page->content + page->content_len; if (!page->handler->process((page_handle)cookie)) return false; } return true; } void page_request_cleanup(page_handle pageh) { struct page *page = *pageh; if (page->request) request_xfree(&page->request); } bool page_print_plaintext(page_handle pageh) { struct page *page = *pageh; size_t n, trail, skip, len, tlen, j; bool newline; for (n = page->content_pos; n < page->content_len; n++) { if (page->content[n] != '\n' && !(n == page->content_len - 1 && !PAGE_CAN_READ_MORE(page))) continue; len = n - page->content_pos + 1; trail = 0; skip = 0; newline = false; if (page->content[n] == '\n') { len--; trail = 1; newline = true; if (n > 0 && page->content[n - 1] == '\r') { len--; trail++; } } else if (page->request != NULL) /* no newline at the end and fetching, so wait for more data */ return true; print_line: if (len) browser_print(page->browser, page->content + page->content_pos + skip, len, newline); page->content_pos += skip + len + trail; } if (!PAGE_CAN_READ_MORE(page) && page->content_pos < page->content_len) browser_print(page->browser, page->content + page->content_pos, page->content_len - page->content_pos, false); return PAGE_CAN_READ_MORE(page); }