/* * Copyright 2020 Michael Drake * * This file is part of NetSurf, http://www.netsurf-browser.org/ * * NetSurf is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 2 of the License. * * NetSurf is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ /** * \file * Pave info viewer window implementation */ #include #include #include "css/utils.h" #include "utils/nsurl.h" #include "netsurf/mouse.h" #include "netsurf/layout.h" #include "netsurf/keypress.h" #include "netsurf/plotters.h" #include "netsurf/core_window.h" #include "netsurf/browser_window.h" #include "desktop/knockout.h" #include "desktop/page-info.h" #include "desktop/gui_internal.h" #include "desktop/system_colour.h" /** * Plot style for heading font. */ static plot_font_style_t pi__heading[PAGE_STATE__COUNT] = { [PAGE_STATE_UNKNOWN] = { .family = PLOT_FONT_FAMILY_SANS_SERIF, .size = 14 * PLOT_STYLE_SCALE, .flags = FONTF_NONE, .weight = 400, }, [PAGE_STATE_INTERNAL] = { .family = PLOT_FONT_FAMILY_SANS_SERIF, .size = 14 * PLOT_STYLE_SCALE, .flags = FONTF_NONE, .weight = 400, }, [PAGE_STATE_LOCAL] = { .family = PLOT_FONT_FAMILY_SANS_SERIF, .size = 14 * PLOT_STYLE_SCALE, .flags = FONTF_NONE, .weight = 400, }, [PAGE_STATE_INSECURE] = { .family = PLOT_FONT_FAMILY_SANS_SERIF, .size = 14 * PLOT_STYLE_SCALE, .flags = FONTF_NONE, .weight = 400, }, [PAGE_STATE_SECURE_OVERRIDE] = { .family = PLOT_FONT_FAMILY_SANS_SERIF, .size = 14 * PLOT_STYLE_SCALE, .flags = FONTF_NONE, .weight = 400, }, [PAGE_STATE_SECURE_ISSUES] = { .family = PLOT_FONT_FAMILY_SANS_SERIF, .size = 14 * PLOT_STYLE_SCALE, .flags = FONTF_NONE, .weight = 400, }, [PAGE_STATE_SECURE] = { .family = PLOT_FONT_FAMILY_SANS_SERIF, .size = 14 * PLOT_STYLE_SCALE, .flags = FONTF_NONE, .weight = 400, }, }; /** * Plot style for domain font. */ static plot_font_style_t pi__domain = { .family = PLOT_FONT_FAMILY_SANS_SERIF, .size = 8 * PLOT_STYLE_SCALE, .flags = FONTF_NONE, .weight = 700, }; /** * Plot style for item font. */ static plot_font_style_t pi__item = { .family = PLOT_FONT_FAMILY_SANS_SERIF, .size = 11 * PLOT_STYLE_SCALE, .flags = FONTF_NONE, .weight = 400, }; /** * Plot style for item detail font. */ static plot_font_style_t pi__item_detail = { .family = PLOT_FONT_FAMILY_SANS_SERIF, .size = 11 * PLOT_STYLE_SCALE, .flags = FONTF_NONE, .weight = 400, }; /** * Plot style for window background. */ static plot_style_t pi__bg = { .fill_type = PLOT_OP_TYPE_SOLID, }; /** * Plot style for hover background. */ static plot_style_t pi__hover = { .fill_type = PLOT_OP_TYPE_SOLID, }; /** * An "text" type page info entry. */ struct page_info_text { const char *text; const plot_font_style_t *style; int width; int height; int padding_bottom; }; /** * An "item" type page info entry. */ struct page_info_item { struct page_info_text item; struct page_info_text detail; const plot_style_t *hover_bg; int padding_bottom; int padding_top; bool hover; }; /** * List of page info window entries. */ enum pi_entry { PI_ENTRY_HEADER, PI_ENTRY_DOMAIN, PI_ENTRY_CERT, PI_ENTRY_COOKIES, PI_ENTRY__COUNT, }; /** * An entry on a page info window. */ struct page_info_entry { /** * List of page info entry types. */ enum page_info_entry_type { PAGE_INFO_ENTRY_TYPE_TEXT, PAGE_INFO_ENTRY_TYPE_ITEM, } type; /** * Type-specific page info entry data. */ union { struct page_info_text text; struct page_info_item item; }; }; /** * The default page info window data. */ struct page_info_entry pi__entries[PI_ENTRY__COUNT] = { [PI_ENTRY_HEADER] = { .type = PAGE_INFO_ENTRY_TYPE_TEXT, }, [PI_ENTRY_DOMAIN] = { .type = PAGE_INFO_ENTRY_TYPE_TEXT, .text = { .style = &pi__domain, }, }, [PI_ENTRY_CERT] = { .type = PAGE_INFO_ENTRY_TYPE_ITEM, .item = { .item = { .style = &pi__item, }, .detail = { .style = &pi__item_detail, }, .hover_bg = &pi__hover, }, }, [PI_ENTRY_COOKIES] = { .type = PAGE_INFO_ENTRY_TYPE_ITEM, .item = { .item = { .style = &pi__item, }, .detail = { .style = &pi__item_detail, }, .hover_bg = &pi__hover, }, }, }; /** * The page info window structure. */ struct page_info { const struct core_window_callback_table *cw_t; struct core_window *cw_h; const struct browser_window *bw; lwc_string *domain; browser_window_page_info_state state; unsigned cookies; char cookie_text[64]; struct page_info_entry entries[PI_ENTRY__COUNT]; int width; int height; int window_padding; }; /* Exported interface documented in desktop/page_info.h */ nserror page_info_init(void) { bool dark_on_light; nserror err; colour good; colour bad; colour bg; colour fg; err = ns_system_colour_char("Window", &bg); if (err != NSERROR_OK) { return err; } err = ns_system_colour_char("WindowText", &fg); if (err != NSERROR_OK) { return err; } dark_on_light = colour_lightness(bg) > colour_lightness(fg); pi__bg.fill_colour = bg; pi__hover.fill_colour = dark_on_light? darken_colour(bg) : lighten_colour(bg); pi__domain.background = bg; pi__domain.foreground = fg; pi__item.background = bg; pi__item.foreground = fg; pi__item_detail.background = bg; pi__item_detail.foreground = blend_colour(bg, fg); good = colour_engorge_component(fg, dark_on_light, PLOT_COLOUR_COMPONENT_GREEN); bad = colour_engorge_component(fg, dark_on_light, PLOT_COLOUR_COMPONENT_RED); pi__heading[PAGE_STATE_UNKNOWN].background = bg; pi__heading[PAGE_STATE_UNKNOWN].foreground = bad; pi__heading[PAGE_STATE_INTERNAL].background = bg; pi__heading[PAGE_STATE_INTERNAL].foreground = fg; pi__heading[PAGE_STATE_LOCAL].background = bg; pi__heading[PAGE_STATE_LOCAL].foreground = fg; pi__heading[PAGE_STATE_INSECURE].background = bg; pi__heading[PAGE_STATE_INSECURE].foreground = bad; pi__heading[PAGE_STATE_SECURE_OVERRIDE].background = bg; pi__heading[PAGE_STATE_SECURE_OVERRIDE].foreground = bad; pi__heading[PAGE_STATE_SECURE_ISSUES].background = bg; pi__heading[PAGE_STATE_SECURE_ISSUES].foreground = bad; pi__heading[PAGE_STATE_SECURE].background = bg; pi__heading[PAGE_STATE_SECURE].foreground = good; return NSERROR_OK; } /* Exported interface documented in desktop/page_info.h */ nserror page_info_fini(void) { return NSERROR_OK; } /** * Measure the text in the page_info window. * * \param[in] pi The page info window handle. * \return NSERROR_OK on success, appropriate error code otherwise. */ static nserror page_info__measure_text_entry( struct page_info_text *pit) { nserror err; int height_px; err = guit->layout->width(pit->style, pit->text, strlen(pit->text), &pit->width); if (err != NSERROR_OK) { return err; } /* \todo: This needs to be a helper in plot style or in nscss. */ height_px = ((pit->style->size / PLOT_STYLE_SCALE) * FIXTOINT(nscss_screen_dpi) + 36) / 72; pit->height = (height_px * 8 + 3) / 6; return NSERROR_OK; } /** * Measure the text in the page_info window. * * \param[in] pi The page info window handle. * \return NSERROR_OK on success, appropriate error code otherwise. */ static nserror page_info__measure_text( struct page_info *pi) { nserror err; for (unsigned i = 0; i < PI_ENTRY__COUNT; i++) { struct page_info_entry *entry = pi->entries + i; int padding; switch (entry->type) { case PAGE_INFO_ENTRY_TYPE_TEXT: err = page_info__measure_text_entry( &entry->text); if (err != NSERROR_OK) { return err; } if (i == PI_ENTRY_DOMAIN) { entry->text.padding_bottom = entry->text.height * 3 / 2; } break; case PAGE_INFO_ENTRY_TYPE_ITEM: err = page_info__measure_text_entry( &entry->item.item); if (err != NSERROR_OK) { return err; } err = page_info__measure_text_entry( &entry->item.detail); if (err != NSERROR_OK) { return err; } padding = entry->item.item.height / 4; entry->item.padding_top = padding; entry->item.padding_bottom = padding; break; } } pi->window_padding = pi->entries[PI_ENTRY_DOMAIN].item.item.height / 2; return NSERROR_OK; } /** * Set the text for the page_info window. * * \todo Use messages for internationalisation. * * \param[in] pi The page info window handle. * \return NSERROR_OK on success, appropriate error code otherwise. */ static nserror page_info__set_text( struct page_info *pi) { int printed; static const char *header[PAGE_STATE__COUNT] = { [PAGE_STATE_UNKNOWN] = "Provenience unknown", [PAGE_STATE_INTERNAL] = "NetSurf data", [PAGE_STATE_LOCAL] = "Local data", [PAGE_STATE_INSECURE] = "Connection not secure", [PAGE_STATE_SECURE_OVERRIDE] = "Connection not secure", [PAGE_STATE_SECURE_ISSUES] = "Connection not secure", [PAGE_STATE_SECURE] = "Connection is secure", }; static const char *certificate[PAGE_STATE__COUNT] = { [PAGE_STATE_UNKNOWN] = "Missing", [PAGE_STATE_INTERNAL] = "None", [PAGE_STATE_LOCAL] = "None", [PAGE_STATE_INSECURE] = "Not valid", [PAGE_STATE_SECURE_OVERRIDE] = "Not valid", [PAGE_STATE_SECURE_ISSUES] = "Not valid", [PAGE_STATE_SECURE] = "Valid", }; assert(pi != NULL); assert(pi->state < PAGE_STATE__COUNT); pi->entries[PI_ENTRY_HEADER].text.style = &pi__heading[pi->state]; pi->entries[PI_ENTRY_HEADER].text.text = header[pi->state]; pi->entries[PI_ENTRY_DOMAIN].text.text = (pi->domain) ? lwc_string_data(pi->domain) : ""; pi->entries[PI_ENTRY_CERT].item.item.text = "Certificate: "; pi->entries[PI_ENTRY_CERT].item.detail.text = certificate[pi->state]; printed = snprintf(pi->cookie_text, sizeof(pi->cookie_text), "(%u in use)", pi->cookies); if (printed < 0) { return NSERROR_UNKNOWN; } else if ((unsigned) printed >= sizeof(pi->cookie_text)) { return NSERROR_NOSPACE; } pi->entries[PI_ENTRY_COOKIES].item.item.text = "Cookies: "; pi->entries[PI_ENTRY_COOKIES].item.detail.text = pi->cookie_text; return page_info__measure_text(pi); } /** * Create page info from a browser window. * * \param[in] pi The page info window handle. * \param[in] bw Browser window to show page info for. * \return NSERROR_OK on success, appropriate error code otherwise. */ static nserror page_info__create_from_bw( struct page_info *pi, const struct browser_window *bw) { nsurl *url = browser_window_access_url(bw); pi->bw = bw; pi->state = browser_window_get_page_info_state(bw); pi->cookies = browser_window_get_cookie_count(bw); pi->domain = nsurl_get_component(url, NSURL_HOST); return page_info__set_text(pi); } /** * Lay out the page info window. * * \param[in] pi The page info window handle. * \return NSERROR_OK on success, appropriate error code otherwise. */ static nserror page_info__layout( struct page_info *pi) { int cur_y = 0; int max_x = 0; cur_y += pi->window_padding; for (unsigned i = 0; i < PI_ENTRY__COUNT; i++) { struct page_info_entry *entry = pi->entries + i; switch (entry->type) { case PAGE_INFO_ENTRY_TYPE_TEXT: cur_y += entry->text.height; if (max_x < entry->text.width) { max_x = entry->text.width; } cur_y += entry->text.padding_bottom; break; case PAGE_INFO_ENTRY_TYPE_ITEM: { int full_width = entry->item.item.width + entry->item.detail.width; cur_y += entry->item.padding_top; cur_y += entry->item.item.height; if (max_x < full_width) { max_x = full_width; } cur_y += entry->item.padding_bottom; } break; } } cur_y += pi->window_padding; max_x += pi->window_padding * 2; pi->width = max_x; pi->height = cur_y; return pi->cw_t->update_size(pi->cw_h, max_x, cur_y); } /* Exported interface documented in desktop/page_info.h */ nserror page_info_create( const struct core_window_callback_table *cw_t, struct core_window *cw_h, const struct browser_window *bw, struct page_info **pi_out) { struct page_info *pi; nserror err; pi = calloc(1, sizeof(*pi)); if (pi == NULL) { return NSERROR_NOMEM; } pi->cw_t = cw_t; pi->cw_h = cw_h; memcpy(pi->entries, pi__entries, sizeof(pi__entries)); err = page_info__create_from_bw(pi, bw); if (err != NSERROR_OK) { page_info_destroy(pi); return err; } err = page_info__layout(pi); if (err != NSERROR_OK) { page_info_destroy(pi); return err; } *pi_out = pi; return NSERROR_OK; } /* Exported interface documented in desktop/page_info.h */ void page_info_destroy( struct page_info *pi) { if (pi->domain != NULL) { lwc_string_unref(pi->domain); } free(pi); }