diff options
author | Vincent Sanders <vince@netsurf-browser.org> | 2018-05-10 11:34:26 +0100 |
---|---|---|
committer | Vincent Sanders <vince@kyllikki.org> | 2018-05-10 13:37:02 +0100 |
commit | 2a03ea30490892ac52b3da325ab78e1aa888f83e (patch) | |
tree | d041e4a2aab3b224ad41612d47ea2119895e27ac /render | |
parent | 1b892391d7859398c212b9fda5b532308fa6e8fd (diff) | |
download | netsurf-2a03ea30490892ac52b3da325ab78e1aa888f83e.tar.gz netsurf-2a03ea30490892ac52b3da325ab78e1aa888f83e.tar.bz2 |
move html and text content handlers where they belong
Diffstat (limited to 'render')
32 files changed, 0 insertions, 28575 deletions
diff --git a/render/Makefile b/render/Makefile deleted file mode 100644 index dc2e31c9e..000000000 --- a/render/Makefile +++ /dev/null @@ -1,10 +0,0 @@ -# Render sources - -S_RENDER := box.c box_construct.c box_normalise.c box_textarea.c \ - font.c form.c imagemap.c layout.c search.c table.c textplain.c \ - html.c html_css.c html_css_fetcher.c html_script.c \ - html_interaction.c html_redraw.c html_redraw_border.c \ - html_forms.c html_object.c - - -S_RENDER := $(addprefix render/,$(S_RENDER)) diff --git a/render/box.c b/render/box.c deleted file mode 100644 index c97e8982b..000000000 --- a/render/box.c +++ /dev/null @@ -1,1242 +0,0 @@ -/* - * Copyright 2005-2007 James Bursa <bursa@users.sourceforge.net> - * Copyright 2003 Phil Mellor <monkeyson@users.sourceforge.net> - * Copyright 2005 John M Bell <jmb202@ecs.soton.ac.uk> - * Copyright 2008 Michael Drake <tlsa@netsurf-browser.org> - * - * 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 <http://www.gnu.org/licenses/>. - */ - -/** - * \file - * implementation of box tree manipulation. - */ - -#include <assert.h> -#include <stdbool.h> -#include <stdio.h> -#include <string.h> -#include <dom/dom.h> - -#include "utils/nsoption.h" -#include "utils/log.h" -#include "utils/talloc.h" -#include "netsurf/misc.h" -#include "netsurf/content.h" -#include "netsurf/mouse.h" -#include "css/utils.h" -#include "css/dump.h" -#include "desktop/scrollbar.h" -#include "desktop/gui_internal.h" - -#include "render/box.h" -#include "render/form_internal.h" -#include "render/html_internal.h" - -#define box_is_float(box) (box->type == BOX_FLOAT_LEFT || \ - box->type == BOX_FLOAT_RIGHT) - -/** - * Destructor for box nodes which own styles - * - * \param b The box being destroyed. - * \return 0 to allow talloc to continue destroying the tree. - */ -static int box_talloc_destructor(struct box *b) -{ - struct html_scrollbar_data *data; - - if ((b->flags & STYLE_OWNED) && b->style != NULL) { - css_computed_style_destroy(b->style); - b->style = NULL; - } - - if (b->styles != NULL) { - css_select_results_destroy(b->styles); - b->styles = NULL; - } - - if (b->href != NULL) - nsurl_unref(b->href); - - if (b->id != NULL) { - lwc_string_unref(b->id); - } - - if (b->node != NULL) { - dom_node_unref(b->node); - } - - if (b->scroll_x != NULL) { - data = scrollbar_get_data(b->scroll_x); - scrollbar_destroy(b->scroll_x); - free(data); - } - - if (b->scroll_y != NULL) { - data = scrollbar_get_data(b->scroll_y); - scrollbar_destroy(b->scroll_y); - free(data); - } - - return 0; -} - -/** - * Create a box tree node. - * - * \param styles selection results for the box, or NULL - * \param style computed style for the box (not copied), or 0 - * \param style_owned whether style is owned by this box - * \param href href for the box (copied), or 0 - * \param target target for the box (not copied), or 0 - * \param title title for the box (not copied), or 0 - * \param id id for the box (not copied), or 0 - * \param context context for allocations - * \return allocated and initialised box, or 0 on memory exhaustion - * - * styles is always owned by the box, if it is set. - * style is only owned by the box in the case of implied boxes. - */ - -struct box * box_create(css_select_results *styles, css_computed_style *style, - bool style_owned, nsurl *href, const char *target, - const char *title, lwc_string *id, void *context) -{ - unsigned int i; - struct box *box; - - box = talloc(context, struct box); - if (!box) { - return 0; - } - - talloc_set_destructor(box, box_talloc_destructor); - - box->type = BOX_INLINE; - box->flags = 0; - box->flags = style_owned ? (box->flags | STYLE_OWNED) : box->flags; - box->styles = styles; - box->style = style; - box->x = box->y = 0; - box->width = UNKNOWN_WIDTH; - box->height = 0; - box->descendant_x0 = box->descendant_y0 = 0; - box->descendant_x1 = box->descendant_y1 = 0; - for (i = 0; i != 4; i++) - box->margin[i] = box->padding[i] = box->border[i].width = 0; - box->scroll_x = box->scroll_y = NULL; - box->min_width = 0; - box->max_width = UNKNOWN_MAX_WIDTH; - box->byte_offset = 0; - box->text = NULL; - box->length = 0; - box->space = 0; - box->href = (href == NULL) ? NULL : nsurl_ref(href); - box->target = target; - box->title = title; - box->columns = 1; - box->rows = 1; - box->start_column = 0; - box->next = NULL; - box->prev = NULL; - box->children = NULL; - box->last = NULL; - box->parent = NULL; - box->inline_end = NULL; - box->float_children = NULL; - box->float_container = NULL; - box->next_float = NULL; - box->cached_place_below_level = 0; - box->list_marker = NULL; - box->col = NULL; - box->gadget = NULL; - box->usemap = NULL; - box->id = id; - box->background = NULL; - box->object = NULL; - box->object_params = NULL; - box->iframe = NULL; - box->node = NULL; - - return box; -} - -/** - * Add a child to a box tree node. - * - * \param parent box giving birth - * \param child box to link as last child of parent - */ - -void box_add_child(struct box *parent, struct box *child) -{ - assert(parent); - assert(child); - - if (parent->children != 0) { /* has children already */ - parent->last->next = child; - child->prev = parent->last; - } else { /* this is the first child */ - parent->children = child; - child->prev = 0; - } - - parent->last = child; - child->parent = parent; -} - - -/** - * Insert a new box as a sibling to a box in a tree. - * - * \param box box already in tree - * \param new_box box to link into tree as next sibling - */ - -void box_insert_sibling(struct box *box, struct box *new_box) -{ - new_box->parent = box->parent; - new_box->prev = box; - new_box->next = box->next; - box->next = new_box; - if (new_box->next) - new_box->next->prev = new_box; - else if (new_box->parent) - new_box->parent->last = new_box; -} - - -/** - * Unlink a box from the box tree and then free it recursively. - * - * \param box box to unlink and free recursively. - */ - -void box_unlink_and_free(struct box *box) -{ - struct box *parent = box->parent; - struct box *next = box->next; - struct box *prev = box->prev; - - if (parent) { - if (parent->children == box) - parent->children = next; - if (parent->last == box) - parent->last = next ? next : prev; - } - - if (prev) - prev->next = next; - if (next) - next->prev = prev; - - box_free(box); -} - - -/** - * Free a box tree recursively. - * - * \param box box to free recursively - * - * The box and all its children is freed. - */ - -void box_free(struct box *box) -{ - struct box *child, *next; - - /* free children first */ - for (child = box->children; child; child = next) { - next = child->next; - box_free(child); - } - - /* last this box */ - box_free_box(box); -} - - -/** - * Free the data in a single box structure. - * - * \param box box to free - */ - -void box_free_box(struct box *box) -{ - if (!(box->flags & CLONE)) { - if (box->gadget) - form_free_control(box->gadget); - if (box->scroll_x != NULL) - scrollbar_destroy(box->scroll_x); - if (box->scroll_y != NULL) - scrollbar_destroy(box->scroll_y); - if (box->styles != NULL) - css_select_results_destroy(box->styles); - } - - talloc_free(box); -} - - -/** - * Find the absolute coordinates of a box. - * - * \param box the box to calculate coordinates of - * \param x updated to x coordinate - * \param y updated to y coordinate - */ - -void box_coords(struct box *box, int *x, int *y) -{ - *x = box->x; - *y = box->y; - while (box->parent) { - if (box_is_float(box)) { - do { - box = box->parent; - } while (!box->float_children); - } else - box = box->parent; - *x += box->x - scrollbar_get_offset(box->scroll_x); - *y += box->y - scrollbar_get_offset(box->scroll_y); - } -} - - -/** - * Find the bounds of a box. - * - * \param box the box to calculate bounds of - * \param r receives bounds - */ - -void box_bounds(struct box *box, struct rect *r) -{ - int width, height; - - box_coords(box, &r->x0, &r->y0); - - width = box->padding[LEFT] + box->width + box->padding[RIGHT]; - height = box->padding[TOP] + box->height + box->padding[BOTTOM]; - - r->x1 = r->x0 + width; - r->y1 = r->y0 + height; -} - - -/** - * Determine if a point lies within a box. - * - * \param[in] len_ctx CSS length conversion context to use. - * \param[in] box Box to consider - * \param[in] x Coordinate relative to box - * \param[in] y Coordinate relative to box - * \param[out] physically If function returning true, physically is set true - * iff point is within the box's physical dimensions and - * false if the point is not within the box's physical - * dimensions but is in the area defined by the box's - * descendants. If function returns false, physically - * is undefined. - * \return true if the point is within the box or a descendant box - * - * This is a helper function for box_at_point(). - */ - -static bool box_contains_point( - const nscss_len_ctx *len_ctx, - const struct box *box, - int x, - int y, - bool *physically) -{ - css_computed_clip_rect css_rect; - - if (box->style != NULL && - css_computed_position(box->style) == - CSS_POSITION_ABSOLUTE && - css_computed_clip(box->style, &css_rect) == - CSS_CLIP_RECT) { - /* We have an absolutly positioned box with a clip rect */ - struct rect r = { - .x0 = box->border[LEFT].width, - .y0 = box->border[TOP].width, - .x1 = box->padding[LEFT] + box->width + - box->border[RIGHT].width + - box->padding[RIGHT], - .y1 = box->padding[TOP] + box->height + - box->border[BOTTOM].width + - box->padding[BOTTOM] - }; - if (x >= r.x0 && x < r.x1 && y >= r.y0 && y < r.y1) - *physically = true; - else - *physically = false; - - /* Adjust rect to css clip region */ - if (css_rect.left_auto == false) { - r.x0 += FIXTOINT(nscss_len2px(len_ctx, - css_rect.left, css_rect.lunit, - box->style)); - } - if (css_rect.top_auto == false) { - r.y0 += FIXTOINT(nscss_len2px(len_ctx, - css_rect.top, css_rect.tunit, - box->style)); - } - if (css_rect.right_auto == false) { - r.x1 = box->border[LEFT].width + - FIXTOINT(nscss_len2px(len_ctx, - css_rect.right, - css_rect.runit, - box->style)); - } - if (css_rect.bottom_auto == false) { - r.y1 = box->border[TOP].width + - FIXTOINT(nscss_len2px(len_ctx, - css_rect.bottom, - css_rect.bunit, - box->style)); - } - - /* Test if point is in clipped box */ - if (x >= r.x0 && x < r.x1 && y >= r.y0 && y < r.y1) { - /* inside clip area */ - return true; - } - - /* Not inside clip area */ - return false; - } - if (x >= -box->border[LEFT].width && - x < box->padding[LEFT] + box->width + - box->padding[RIGHT] + box->border[RIGHT].width && - y >= -box->border[TOP].width && - y < box->padding[TOP] + box->height + - box->padding[BOTTOM] + box->border[BOTTOM].width) { - *physically = true; - return true; - } - if (box->list_marker && box->list_marker->x - box->x <= x + - box->list_marker->border[LEFT].width && - x < box->list_marker->x - box->x + - box->list_marker->padding[LEFT] + - box->list_marker->width + - box->list_marker->border[RIGHT].width + - box->list_marker->padding[RIGHT] && - box->list_marker->y - box->y <= y + - box->list_marker->border[TOP].width && - y < box->list_marker->y - box->y + - box->list_marker->padding[TOP] + - box->list_marker->height + - box->list_marker->border[BOTTOM].width + - box->list_marker->padding[BOTTOM]) { - *physically = true; - return true; - } - if ((box->style && css_computed_overflow_x(box->style) == - CSS_OVERFLOW_VISIBLE) || !box->style) { - if (box->descendant_x0 <= x && - x < box->descendant_x1) { - *physically = false; - return true; - } - } - if ((box->style && css_computed_overflow_y(box->style) == - CSS_OVERFLOW_VISIBLE) || !box->style) { - if (box->descendant_y0 <= y && - y < box->descendant_y1) { - *physically = false; - return true; - } - } - return false; -} - - -/** Direction to move in a box-tree walk */ -enum box_walk_dir { - BOX_WALK_CHILDREN, - BOX_WALK_PARENT, - BOX_WALK_NEXT_SIBLING, - BOX_WALK_FLOAT_CHILDREN, - BOX_WALK_NEXT_FLOAT_SIBLING, - BOX_WALK_FLOAT_CONTAINER -}; - - -/** - * Move from box to next box in given direction, adjusting for box coord change - * - * \param b box to move from from - * \param dir direction to move in - * \param x box's global x-coord, updated to position of next box - * \param y box's global y-coord, updated to position of next box - * - * If no box can be found in given direction, NULL is returned. - */ -static inline struct box *box_move_xy(struct box *b, enum box_walk_dir dir, - int *x, int *y) -{ - struct box *rb = NULL; - - switch (dir) { - case BOX_WALK_CHILDREN: - b = b->children; - if (b == NULL) - break; - *x += b->x; - *y += b->y; - if (!box_is_float(b)) { - rb = b; - break; - } - /* Fall through */ - - case BOX_WALK_NEXT_SIBLING: - do { - *x -= b->x; - *y -= b->y; - b = b->next; - if (b == NULL) - break; - *x += b->x; - *y += b->y; - } while (box_is_float(b)); - rb = b; - break; - - case BOX_WALK_PARENT: - *x -= b->x; - *y -= b->y; - rb = b->parent; - break; - - case BOX_WALK_FLOAT_CHILDREN: - b = b->float_children; - if (b == NULL) - break; - *x += b->x; - *y += b->y; - rb = b; - break; - - case BOX_WALK_NEXT_FLOAT_SIBLING: - *x -= b->x; - *y -= b->y; - b = b->next_float; - if (b == NULL) - break; - *x += b->x; - *y += b->y; - rb = b; - break; - - case BOX_WALK_FLOAT_CONTAINER: - *x -= b->x; - *y -= b->y; - rb = b->float_container; - break; - - default: - assert(0 && "Bad box walk type."); - } - - return rb; -} - - -/** - * Itterator for walking to next box in interaction order - * - * \param b box to find next box from - * \param x box's global x-coord, updated to position of next box - * \param y box's global y-coord, updated to position of next box - * \param skip_children whether to skip box's children - * - * This walks to a boxes float children before its children. When walking - * children, floating boxes are skipped. - */ -static inline struct box *box_next_xy(struct box *b, int *x, int *y, - bool skip_children) -{ - struct box *n; - int tx, ty; - - assert(b != NULL); - - if (skip_children) { - /* Caller is not interested in any kind of children */ - goto skip_children; - } - - tx = *x; ty = *y; - n = box_move_xy(b, BOX_WALK_FLOAT_CHILDREN, &tx, &ty); - if (n) { - /* Next node is float child */ - *x = tx; - *y = ty; - return n; - } -done_float_children: - - tx = *x; ty = *y; - n = box_move_xy(b, BOX_WALK_CHILDREN, &tx, &ty); - if (n) { - /* Next node is child */ - *x = tx; - *y = ty; - return n; - } - -skip_children: - tx = *x; ty = *y; - n = box_move_xy(b, BOX_WALK_NEXT_FLOAT_SIBLING, &tx, &ty); - if (n) { - /* Go to next float sibling */ - *x = tx; - *y = ty; - return n; - } - - if (box_is_float(b)) { - /* Done floats, but the float container may have children, - * or siblings, or ansestors with siblings. Change to - * float container and move past handling its float children. - */ - b = box_move_xy(b, BOX_WALK_FLOAT_CONTAINER, x, y); - goto done_float_children; - } - - /* Go to next sibling, or nearest ancestor with next sibling. */ - while (b) { - while (!b->next && b->parent) { - b = box_move_xy(b, BOX_WALK_PARENT, x, y); - if (box_is_float(b)) { - /* Go on to next float, if there is one */ - goto skip_children; - } - } - if (!b->next) { - /* No more boxes */ - return NULL; - } - - tx = *x; ty = *y; - n = box_move_xy(b, BOX_WALK_NEXT_SIBLING, &tx, &ty); - if (n) { - /* Go to non-float (ancestor) sibling */ - *x = tx; - *y = ty; - return n; - - } else if (b->parent) { - b = box_move_xy(b, BOX_WALK_PARENT, x, y); - if (box_is_float(b)) { - /* Go on to next float, if there is one */ - goto skip_children; - } - - } else { - /* No more boxes */ - return NULL; - } - } - - assert(b != NULL); - return NULL; -} - - - -/** - * Find the boxes at a point. - * - * \param len_ctx CSS length conversion context for document. - * \param box box to search children of - * \param x point to find, in global document coordinates - * \param y point to find, in global document coordinates - * \param box_x position of box, in global document coordinates, updated - * to position of returned box, if any - * \param box_y position of box, in global document coordinates, updated - * to position of returned box, if any - * \return box at given point, or 0 if none found - * - * To find all the boxes in the hierarchy at a certain point, use code like - * this: - * \code - * struct box *box = top_of_document_to_search; - * int box_x = 0, box_y = 0; - * - * while ((box = box_at_point(len_ctx, box, x, y, &box_x, &box_y))) { - * // process box - * } - * \endcode - */ - -struct box *box_at_point(const nscss_len_ctx *len_ctx, - struct box *box, const int x, const int y, - int *box_x, int *box_y) -{ - bool skip_children; - bool physically; - - assert(box); - - skip_children = false; - while ((box = box_next_xy(box, box_x, box_y, skip_children))) { - if (box_contains_point(len_ctx, box, x - *box_x, y - *box_y, - &physically)) { - *box_x -= scrollbar_get_offset(box->scroll_x); - *box_y -= scrollbar_get_offset(box->scroll_y); - - if (physically) - return box; - - skip_children = false; - } else { - skip_children = true; - } - } - - return NULL; -} - - -/** - * Check whether box is nearer mouse coordinates than current nearest box - * - * \param box box to test - * \param bx position of box, in global document coordinates - * \param by position of box, in global document coordinates - * \param x mouse point, in global document coordinates - * \param y mouse point, in global document coordinates - * \param dir direction in which to search (-1 = above-left, - * +1 = below-right) - * \param nearest nearest text box found, or NULL if none - * updated if box is nearer than existing nearest - * \param tx position of text_box, in global document coordinates - * updated if box is nearer than existing nearest - * \param ty position of text_box, in global document coordinates - * updated if box is nearer than existing nearest - * \param nr_xd distance to nearest text box found - * updated if box is nearer than existing nearest - * \param nr_yd distance to nearest text box found - * updated if box is nearer than existing nearest - * \return true if mouse point is inside box - */ - -static bool box_nearer_text_box(struct box *box, int bx, int by, - int x, int y, int dir, struct box **nearest, int *tx, int *ty, - int *nr_xd, int *nr_yd) -{ - int w = box->padding[LEFT] + box->width + box->padding[RIGHT]; - int h = box->padding[TOP] + box->height + box->padding[BOTTOM]; - int y1 = by + h; - int x1 = bx + w; - int yd = INT_MAX; - int xd = INT_MAX; - - if (x >= bx && x1 > x && y >= by && y1 > y) { - *nearest = box; - *tx = bx; - *ty = by; - return true; - } - - if (box->parent->list_marker != box) { - if (dir < 0) { - /* consider only those children (partly) above-left */ - if (by <= y && bx < x) { - yd = y <= y1 ? 0 : y - y1; - xd = x <= x1 ? 0 : x - x1; - } - } else { - /* consider only those children (partly) below-right */ - if (y1 > y && x1 > x) { - yd = y > by ? 0 : by - y; - xd = x > bx ? 0 : bx - x; - } - } - - /* give y displacement precedence over x */ - if (yd < *nr_yd || (yd == *nr_yd && xd <= *nr_xd)) { - *nr_yd = yd; - *nr_xd = xd; - *nearest = box; - *tx = bx; - *ty = by; - } - } - return false; -} - - -/** - * Pick the text box child of 'box' that is closest to and above-left - * (dir -ve) or below-right (dir +ve) of the point 'x,y' - * - * \param box parent box - * \param bx position of box, in global document coordinates - * \param by position of box, in global document coordinates - * \param fx position of float parent, in global document coordinates - * \param fy position of float parent, in global document coordinates - * \param x mouse point, in global document coordinates - * \param y mouse point, in global document coordinates - * \param dir direction in which to search (-1 = above-left, - * +1 = below-right) - * \param nearest nearest text box found, or NULL if none - * updated if a descendant of box is nearer than old nearest - * \param tx position of nearest, in global document coordinates - * updated if a descendant of box is nearer than old nearest - * \param ty position of nearest, in global document coordinates - * updated if a descendant of box is nearer than old nearest - * \param nr_xd distance to nearest text box found - * updated if a descendant of box is nearer than old nearest - * \param nr_yd distance to nearest text box found - * updated if a descendant of box is nearer than old nearest - * \return true if mouse point is inside text_box - */ - -static bool box_nearest_text_box(struct box *box, int bx, int by, - int fx, int fy, int x, int y, int dir, struct box **nearest, - int *tx, int *ty, int *nr_xd, int *nr_yd) -{ - struct box *child = box->children; - int c_bx, c_by; - int c_fx, c_fy; - bool in_box = false; - - if (*nearest == NULL) { - *nr_xd = INT_MAX / 2; /* displacement of 'nearest so far' */ - *nr_yd = INT_MAX / 2; - } - if (box->type == BOX_INLINE_CONTAINER) { - int bw = box->padding[LEFT] + box->width + box->padding[RIGHT]; - int bh = box->padding[TOP] + box->height + box->padding[BOTTOM]; - int b_y1 = by + bh; - int b_x1 = bx + bw; - if (x >= bx && b_x1 > x && y >= by && b_y1 > y) { - in_box = true; - } - } - - while (child) { - if (child->type == BOX_FLOAT_LEFT || - child->type == BOX_FLOAT_RIGHT) { - c_bx = fx + child->x - - scrollbar_get_offset(child->scroll_x); - c_by = fy + child->y - - scrollbar_get_offset(child->scroll_y); - } else { - c_bx = bx + child->x - - scrollbar_get_offset(child->scroll_x); - c_by = by + child->y - - scrollbar_get_offset(child->scroll_y); - } - if (child->float_children) { - c_fx = c_bx; - c_fy = c_by; - } else { - c_fx = fx; - c_fy = fy; - } - if (in_box && child->text && !child->object) { - if (box_nearer_text_box(child, - c_bx, c_by, x, y, dir, nearest, - tx, ty, nr_xd, nr_yd)) - return true; - } else { - if (child->list_marker) { - if (box_nearer_text_box( - child->list_marker, - c_bx + child->list_marker->x, - c_by + child->list_marker->y, - x, y, dir, nearest, - tx, ty, nr_xd, nr_yd)) - return true; - } - if (box_nearest_text_box(child, c_bx, c_by, - c_fx, c_fy, x, y, dir, nearest, tx, ty, - nr_xd, nr_yd)) - return true; - } - child = child->next; - } - - return false; -} - - -/** - * Peform pick text on browser window contents to locate the box under - * the mouse pointer, or nearest in the given direction if the pointer is - * not over a text box. - * - * \param html an HTML content - * \param x coordinate of mouse - * \param y coordinate of mouse - * \param dir direction to search (-1 = above-left, +1 = below-right) - * \param dx receives x ordinate of mouse relative to text box - * \param dy receives y ordinate of mouse relative to text box - */ - -struct box *box_pick_text_box(struct html_content *html, - int x, int y, int dir, int *dx, int *dy) -{ - struct box *text_box = NULL; - struct box *box; - int nr_xd, nr_yd; - int bx, by; - int fx, fy; - int tx, ty; - - if (html == NULL) - return NULL; - - box = html->layout; - bx = box->margin[LEFT]; - by = box->margin[TOP]; - fx = bx; - fy = by; - - if (!box_nearest_text_box(box, bx, by, fx, fy, x, y, - dir, &text_box, &tx, &ty, &nr_xd, &nr_yd)) { - if (text_box && text_box->text && !text_box->object) { - int w = (text_box->padding[LEFT] + - text_box->width + - text_box->padding[RIGHT]); - int h = (text_box->padding[TOP] + - text_box->height + - text_box->padding[BOTTOM]); - int x1, y1; - - y1 = ty + h; - x1 = tx + w; - - /* ensure point lies within the text box */ - if (x < tx) x = tx; - if (y < ty) y = ty; - if (y > y1) y = y1; - if (x > x1) x = x1; - } - } - - /* return coordinates relative to box */ - *dx = x - tx; - *dy = y - ty; - - return text_box; -} - - -/** - * Find a box based upon its id attribute. - * - * \param box box tree to search - * \param id id to look for - * \return the box or 0 if not found - */ - -struct box *box_find_by_id(struct box *box, lwc_string *id) -{ - struct box *a, *b; - bool m; - - if (box->id != NULL && - lwc_string_isequal(id, box->id, &m) == lwc_error_ok && - m == true) - return box; - - for (a = box->children; a; a = a->next) { - if ((b = box_find_by_id(a, id)) != NULL) - return b; - } - - return NULL; -} - - -/** - * Determine if a box is visible when the tree is rendered. - * - * \param box box to check - * \return true iff the box is rendered - */ - -bool box_visible(struct box *box) -{ - /* visibility: hidden */ - if (box->style && css_computed_visibility(box->style) == - CSS_VISIBILITY_HIDDEN) - return false; - - return true; -} - - -/** - * Print a box tree to a file. - */ - -void box_dump(FILE *stream, struct box *box, unsigned int depth, bool style) -{ - unsigned int i; - struct box *c, *prev; - - for (i = 0; i != depth; i++) - fprintf(stream, " "); - - fprintf(stream, "%p ", box); - fprintf(stream, "x%i y%i w%i h%i ", box->x, box->y, - box->width, box->height); - if (box->max_width != UNKNOWN_MAX_WIDTH) - fprintf(stream, "min%i max%i ", box->min_width, box->max_width); - fprintf(stream, "(%i %i %i %i) ", - box->descendant_x0, box->descendant_y0, - box->descendant_x1, box->descendant_y1); - - fprintf(stream, "m(%i %i %i %i) ", - box->margin[TOP], box->margin[LEFT], - box->margin[BOTTOM], box->margin[RIGHT]); - - switch (box->type) { - case BOX_BLOCK: fprintf(stream, "BLOCK "); break; - case BOX_INLINE_CONTAINER: fprintf(stream, "INLINE_CONTAINER "); break; - case BOX_INLINE: fprintf(stream, "INLINE "); break; - case BOX_INLINE_END: fprintf(stream, "INLINE_END "); break; - case BOX_INLINE_BLOCK: fprintf(stream, "INLINE_BLOCK "); break; - case BOX_TABLE: fprintf(stream, "TABLE [columns %i] ", - box->columns); break; - case BOX_TABLE_ROW: fprintf(stream, "TABLE_ROW "); break; - case BOX_TABLE_CELL: fprintf(stream, "TABLE_CELL [columns %i, " - "start %i, rows %i] ", box->columns, - box->start_column, box->rows); break; - case BOX_TABLE_ROW_GROUP: fprintf(stream, "TABLE_ROW_GROUP "); break; - case BOX_FLOAT_LEFT: fprintf(stream, "FLOAT_LEFT "); break; - case BOX_FLOAT_RIGHT: fprintf(stream, "FLOAT_RIGHT "); break; - case BOX_BR: fprintf(stream, "BR "); break; - case BOX_TEXT: fprintf(stream, "TEXT "); break; - default: fprintf(stream, "Unknown box type "); - } - - if (box->text) - fprintf(stream, "%li '%.*s' ", (unsigned long) box->byte_offset, - (int) box->length, box->text); - if (box->space) - fprintf(stream, "space "); - if (box->object) { - fprintf(stream, "(object '%s') ", - nsurl_access(hlcache_handle_get_url(box->object))); - } - if (box->iframe) { - fprintf(stream, "(iframe) "); - } - if (box->gadget) - fprintf(stream, "(gadget) "); - if (style && box->style) - nscss_dump_computed_style(stream, box->style); - if (box->href) - fprintf(stream, " -> '%s'", nsurl_access(box->href)); - if (box->target) - fprintf(stream, " |%s|", box->target); - if (box->title) - fprintf(stream, " [%s]", box->title); - if (box->id) - fprintf(stream, " ID:%s", lwc_string_data(box->id)); - if (box->type == BOX_INLINE || box->type == BOX_INLINE_END) - fprintf(stream, " inline_end %p", box->inline_end); - if (box->float_children) - fprintf(stream, " float_children %p", box->float_children); - if (box->next_float) - fprintf(stream, " next_float %p", box->next_float); - if (box->float_container) - fprintf(stream, " float_container %p", box->float_container); - if (box->col) { - fprintf(stream, " (columns"); - for (i = 0; i != box->columns; i++) - fprintf(stream, " (%s %s %i %i %i)", - ((const char *[]) {"UNKNOWN", "FIXED", - "AUTO", "PERCENT", "RELATIVE"}) - [box->col[i].type], - ((const char *[]) {"normal", - "positioned"}) - [box->col[i].positioned], - box->col[i].width, - box->col[i].min, box->col[i].max); - fprintf(stream, ")"); - } - if (box->node != NULL) { - dom_string *name; - if (dom_node_get_node_name(box->node, &name) == DOM_NO_ERR) { - fprintf(stream, " <%s>", dom_string_data(name)); - dom_string_unref(name); - } - } - fprintf(stream, "\n"); - - if (box->list_marker) { - for (i = 0; i != depth; i++) - fprintf(stream, " "); - fprintf(stream, "list_marker:\n"); - box_dump(stream, box->list_marker, depth + 1, style); - } - - for (c = box->children; c && c->next; c = c->next) - ; - if (box->last != c) - fprintf(stream, "warning: box->last %p (should be %p) " - "(box %p)\n", box->last, c, box); - for (prev = 0, c = box->children; c; prev = c, c = c->next) { - if (c->parent != box) - fprintf(stream, "warning: box->parent %p (should be " - "%p) (box on next line)\n", - c->parent, box); - if (c->prev != prev) - fprintf(stream, "warning: box->prev %p (should be " - "%p) (box on next line)\n", - c->prev, prev); - box_dump(stream, c, depth + 1, style); - } -} - -/** - * Applies the given scroll setup to a box. This includes scroll - * creation/deletion as well as scroll dimension updates. - * - * \param c content in which the box is located - * \param box the box to handle the scrolls for - * \param bottom whether the horizontal scrollbar should be present - * \param right whether the vertical scrollbar should be present - * \return true on success false otherwise - */ -bool box_handle_scrollbars(struct content *c, struct box *box, - bool bottom, bool right) -{ - struct html_scrollbar_data *data; - int visible_width, visible_height; - int full_width, full_height; - - if (!bottom && box->scroll_x != NULL) { - data = scrollbar_get_data(box->scroll_x); - scrollbar_destroy(box->scroll_x); - free(data); - box->scroll_x = NULL; - } - - if (!right && box->scroll_y != NULL) { - data = scrollbar_get_data(box->scroll_y); - scrollbar_destroy(box->scroll_y); - free(data); - box->scroll_y = NULL; - } - - if (!bottom && !right) - return true; - - visible_width = box->width + box->padding[RIGHT] + box->padding[LEFT]; - visible_height = box->height + box->padding[TOP] + box->padding[BOTTOM]; - - full_width = ((box->descendant_x1 - box->border[RIGHT].width) > - visible_width) ? - box->descendant_x1 + box->padding[RIGHT] : - visible_width; - full_height = ((box->descendant_y1 - box->border[BOTTOM].width) > - visible_height) ? - box->descendant_y1 + box->padding[BOTTOM] : - visible_height; - - if (right) { - if (box->scroll_y == NULL) { - data = malloc(sizeof(struct html_scrollbar_data)); - if (data == NULL) { - NSLOG(netsurf, INFO, "malloc failed"); - guit->misc->warning("NoMemory", 0); - return false; - } - data->c = c; - data->box = box; - if (scrollbar_create(false, visible_height, - full_height, visible_height, - data, html_overflow_scroll_callback, - &(box->scroll_y)) != NSERROR_OK) { - return false; - } - } else { - scrollbar_set_extents(box->scroll_y, visible_height, - visible_height, full_height); - } - } - if (bottom) { - if (box->scroll_x == NULL) { - data = malloc(sizeof(struct html_scrollbar_data)); - if (data == NULL) { - NSLOG(netsurf, INFO, "malloc failed"); - guit->misc->warning("NoMemory", 0); - return false; - } - data->c = c; - data->box = box; - if (scrollbar_create(true, - visible_width - - (right ? SCROLLBAR_WIDTH : 0), - full_width, visible_width, - data, html_overflow_scroll_callback, - &box->scroll_x) != NSERROR_OK) { - return false; - } - } else { - scrollbar_set_extents(box->scroll_x, - visible_width - - (right ? SCROLLBAR_WIDTH : 0), - visible_width, full_width); - } - } - - if (right && bottom) - scrollbar_make_pair(box->scroll_x, box->scroll_y); - - return true; -} - -/** - * Determine if a box has a vertical scrollbar. - * - * \param box scrolling box - * \return the box has a vertical scrollbar - */ - -bool box_vscrollbar_present(const struct box * const box) -{ - return box->padding[TOP] + box->height + box->padding[BOTTOM] + - box->border[BOTTOM].width < box->descendant_y1; -} - - -/** - * Determine if a box has a horizontal scrollbar. - * - * \param box scrolling box - * \return the box has a horizontal scrollbar - */ - -bool box_hscrollbar_present(const struct box * const box) -{ - return box->padding[LEFT] + box->width + box->padding[RIGHT] + - box->border[RIGHT].width < box->descendant_x1; -} - diff --git a/render/box.h b/render/box.h deleted file mode 100644 index 1af0a8b73..000000000 --- a/render/box.h +++ /dev/null @@ -1,368 +0,0 @@ -/* - * Copyright 2005 James Bursa <bursa@users.sourceforge.net> - * Copyright 2003 Phil Mellor <monkeyson@users.sourceforge.net> - * - * 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 <http://www.gnu.org/licenses/>. - */ - -/** \file - * Box tree construction and manipulation (interface). - * - * This stage of rendering converts a tree of dom_nodes (produced by libdom) - * to a tree of struct box. The box tree represents the structure of the - * document as given by the CSS display and float properties. - * - * For example, consider the following HTML: - * \code - * <h1>Example Heading</h1> - * <p>Example paragraph <em>with emphasised text</em> etc.</p> \endcode - * - * This would produce approximately the following box tree with default CSS - * rules: - * \code - * BOX_BLOCK (corresponds to h1) - * BOX_INLINE_CONTAINER - * BOX_INLINE "Example Heading" - * BOX_BLOCK (p) - * BOX_INLINE_CONTAINER - * BOX_INLINE "Example paragraph " - * BOX_INLINE "with emphasised text" (em) - * BOX_INLINE "etc." \endcode - * - * Note that the em has been collapsed into the INLINE_CONTAINER. - * - * If these CSS rules were applied: - * \code - * h1 { display: table-cell } - * p { display: table-cell } - * em { float: left; width: 5em } \endcode - * - * then the box tree would instead look like this: - * \code - * BOX_TABLE - * BOX_TABLE_ROW_GROUP - * BOX_TABLE_ROW - * BOX_TABLE_CELL (h1) - * BOX_INLINE_CONTAINER - * BOX_INLINE "Example Heading" - * BOX_TABLE_CELL (p) - * BOX_INLINE_CONTAINER - * BOX_INLINE "Example paragraph " - * BOX_FLOAT_LEFT (em) - * BOX_BLOCK - * BOX_INLINE_CONTAINER - * BOX_INLINE "with emphasised text" - * BOX_INLINE "etc." \endcode - * - * Here implied boxes have been added and a float is present. - * - * A box tree is "normalized" if the following is satisfied: - * \code - * parent permitted child nodes - * BLOCK, INLINE_BLOCK BLOCK, INLINE_CONTAINER, TABLE - * INLINE_CONTAINER INLINE, INLINE_BLOCK, FLOAT_LEFT, FLOAT_RIGHT, BR, TEXT, - * INLINE_END - * INLINE none - * TABLE at least 1 TABLE_ROW_GROUP - * TABLE_ROW_GROUP at least 1 TABLE_ROW - * TABLE_ROW at least 1 TABLE_CELL - * TABLE_CELL BLOCK, INLINE_CONTAINER, TABLE (same as BLOCK) - * FLOAT_(LEFT|RIGHT) exactly 1 BLOCK or TABLE - * \endcode - */ - -#ifndef _NETSURF_RENDER_BOX_H_ -#define _NETSURF_RENDER_BOX_H_ - -#include <limits.h> -#include <stdbool.h> -#include <stdio.h> -#include <libcss/libcss.h> - -#include "content/handlers/css/utils.h" - -struct content; -struct box; -struct browser_window; -struct column; -struct object_params; -struct object_param; -struct html_content; -struct nsurl; -struct dom_node; -struct dom_string; -struct rect; - -#define UNKNOWN_WIDTH INT_MAX -#define UNKNOWN_MAX_WIDTH INT_MAX - -typedef void (*box_construct_complete_cb)(struct html_content *c, bool success); - -/** Type of a struct box. */ -typedef enum { - BOX_BLOCK, BOX_INLINE_CONTAINER, BOX_INLINE, - BOX_TABLE, BOX_TABLE_ROW, BOX_TABLE_CELL, - BOX_TABLE_ROW_GROUP, - BOX_FLOAT_LEFT, BOX_FLOAT_RIGHT, - BOX_INLINE_BLOCK, BOX_BR, BOX_TEXT, - BOX_INLINE_END, BOX_NONE -} box_type; - - -/** Flags for a struct box. */ -typedef enum { - NEW_LINE = 1 << 0, /* first inline on a new line */ - STYLE_OWNED = 1 << 1, /* style is owned by this box */ - PRINTED = 1 << 2, /* box has already been printed */ - PRE_STRIP = 1 << 3, /* PRE tag needing leading newline stripped */ - CLONE = 1 << 4, /* continuation of previous box from wrapping */ - MEASURED = 1 << 5, /* text box width has been measured */ - HAS_HEIGHT = 1 << 6, /* box has height (perhaps due to children) */ - MAKE_HEIGHT = 1 << 7, /* box causes its own height */ - NEED_MIN = 1 << 8, /* minimum width is required for layout */ - REPLACE_DIM = 1 << 9, /* replaced element has given dimensions */ - IFRAME = 1 << 10, /* box contains an iframe */ - CONVERT_CHILDREN = 1 << 11, /* wanted children converting */ - IS_REPLACED = 1 << 12 /* box is a replaced element */ -} box_flags; - -/* Sides of a box */ -enum box_side { TOP, RIGHT, BOTTOM, LEFT }; - -/** - * Container for box border details - */ -struct box_border { - enum css_border_style_e style; /**< border-style */ - css_color c; /**< border-color value */ - int width; /**< border-width (pixels) */ -}; - -/** Node in box tree. All dimensions are in pixels. */ -struct box { - /** Type of box. */ - box_type type; - - /** Box flags */ - box_flags flags; - - /** Computed styles for elements and their pseudo elements. NULL on - * non-element boxes. */ - css_select_results *styles; - - /** Style for this box. 0 for INLINE_CONTAINER and FLOAT_*. Pointer into - * a box's 'styles' select results, except for implied boxes, where it - * is a pointer to an owned computed style. */ - css_computed_style *style; - - /** Coordinate of left padding edge relative to parent box, or relative - * to ancestor that contains this box in float_children for FLOAT_. */ - int x; - /** Coordinate of top padding edge, relative as for x. */ - int y; - - int width; /**< Width of content box (excluding padding etc.). */ - int height; /**< Height of content box (excluding padding etc.). */ - - /* These four variables determine the maximum extent of a box's - * descendants. They are relative to the x,y coordinates of the box. - * - * Their use depends on the overflow CSS property: - * - * Overflow: Usage: - * visible The content of the box is displayed within these - * dimensions. - * hidden These are ignored. Content is plotted within the box - * dimensions. - * scroll These are used to determine the extent of the - * scrollable area. - * auto As "scroll". - */ - int descendant_x0; /**< left edge of descendants */ - int descendant_y0; /**< top edge of descendants */ - int descendant_x1; /**< right edge of descendants */ - int descendant_y1; /**< bottom edge of descendants */ - - int margin[4]; /**< Margin: TOP, RIGHT, BOTTOM, LEFT. */ - int padding[4]; /**< Padding: TOP, RIGHT, BOTTOM, LEFT. */ - struct box_border border[4]; /**< Border: TOP, RIGHT, BOTTOM, LEFT. */ - - struct scrollbar *scroll_x; /**< Horizontal scroll. */ - struct scrollbar *scroll_y; /**< Vertical scroll. */ - - /** Width of box taking all line breaks (including margins etc). Must - * be non-negative. */ - int min_width; - /** Width that would be taken with no line breaks. Must be - * non-negative. */ - int max_width; - - /**< Byte offset within a textual representation of this content. */ - size_t byte_offset; - - char *text; /**< Text, or 0 if none. Unterminated. */ - size_t length; /**< Length of text. */ - - /** Width of space after current text (depends on font and size). */ - int space; - - struct nsurl *href; /**< Link, or 0. */ - const char *target; /**< Link target, or 0. */ - const char *title; /**< Title, or 0. */ - - unsigned int columns; /**< Number of columns for TABLE / TABLE_CELL. */ - unsigned int rows; /**< Number of rows for TABLE only. */ - unsigned int start_column; /**< Start column for TABLE_CELL only. */ - - struct box *next; /**< Next sibling box, or 0. */ - struct box *prev; /**< Previous sibling box, or 0. */ - struct box *children; /**< First child box, or 0. */ - struct box *last; /**< Last child box, or 0. */ - struct box *parent; /**< Parent box, or 0. */ - /** INLINE_END box corresponding to this INLINE box, or INLINE box - * corresponding to this INLINE_END box. */ - struct box *inline_end; - - /** First float child box, or 0. Float boxes are in the tree twice, in - * this list for the block box which defines the area for floats, and - * also in the standard tree given by children, next, prev, etc. */ - struct box *float_children; - /** Next sibling float box. */ - struct box *next_float; - /** If box is a float, points to box's containing block */ - struct box *float_container; - /** Level below which subsequent floats must be cleared. - * This is used only for boxes with float_children */ - int clear_level; - - /* Level below which floats have been placed. */ - int cached_place_below_level; - - /** List marker box if this is a list-item, or 0. */ - struct box *list_marker; - - struct column *col; /**< Array of table column data for TABLE only. */ - - /** Form control data, or 0 if not a form control. */ - struct form_control* gadget; - - char *usemap; /** (Image)map to use with this object, or 0 if none */ - lwc_string *id; /**< value of id attribute (or name for anchors) */ - - /** Background image for this box, or 0 if none */ - struct hlcache_handle *background; - - /** Object in this box (usually an image), or 0 if none. */ - struct hlcache_handle* object; - /** Parameters for the object, or 0. */ - struct object_params *object_params; - - /** Iframe's browser_window, or NULL if none */ - struct browser_window *iframe; - - struct dom_node *node; /**< DOM node that generated this box or NULL */ -}; - -/** Table column data. */ -struct column { - /** Type of column. */ - enum { COLUMN_WIDTH_UNKNOWN, COLUMN_WIDTH_FIXED, - COLUMN_WIDTH_AUTO, COLUMN_WIDTH_PERCENT, - COLUMN_WIDTH_RELATIVE } type; - /** Preferred width of column. Pixels for FIXED, percentage for PERCENT, - * relative units for RELATIVE, unused for AUTO. */ - int width; - /** Minimum width of content. */ - int min; - /** Maximum width of content. */ - int max; - /** Whether all of column's cells are css positioned. */ - bool positioned; -}; - -/** Parameters for object element and similar elements. */ -struct object_params { - struct nsurl *data; - char *type; - char *codetype; - struct nsurl *codebase; - struct nsurl *classid; - struct object_param *params; -}; - -/** Linked list of object element parameters. */ -struct object_param { - char *name; - char *value; - char *type; - char *valuetype; - struct object_param *next; -}; - -/** Frame target names (constant pointers to save duplicating the strings many - * times). We convert _blank to _top for user-friendliness. */ -extern const char *TARGET_SELF; -extern const char *TARGET_PARENT; -extern const char *TARGET_TOP; -extern const char *TARGET_BLANK; - - - -struct box * box_create(css_select_results *styles, css_computed_style *style, - bool style_owned, struct nsurl *href, const char *target, - const char *title, lwc_string *id, void *context); -void box_add_child(struct box *parent, struct box *child); -void box_insert_sibling(struct box *box, struct box *new_box); -void box_unlink_and_free(struct box *box); -void box_free(struct box *box); -void box_free_box(struct box *box); -void box_bounds(struct box *box, struct rect *r); -void box_coords(struct box *box, int *x, int *y); -struct box *box_at_point( - const nscss_len_ctx *len_ctx, - struct box *box, const int x, const int y, - int *box_x, int *box_y); -struct box *box_pick_text_box(struct html_content *html, - int x, int y, int dir, int *dx, int *dy); -struct box *box_find_by_id(struct box *box, lwc_string *id); -bool box_visible(struct box *box); -void box_dump(FILE *stream, struct box *box, unsigned int depth, bool style); - -/** - * Extract a URL from a relative link, handling junk like whitespace and - * attempting to read a real URL from "javascript:" links. - * - * \param content html content - * \param dsrel relative URL text taken from page - * \param base base for relative URLs - * \param result updated to target URL on heap, unchanged if extract failed - * \return true on success, false on memory exhaustion - */ -bool box_extract_link(const struct html_content *content, const struct dom_string *dsrel, struct nsurl *base, struct nsurl **result); - -bool box_handle_scrollbars(struct content *c, struct box *box, - bool bottom, bool right); -bool box_vscrollbar_present(const struct box *box); -bool box_hscrollbar_present(const struct box *box); - -nserror dom_to_box(struct dom_node *n, struct html_content *c, - box_construct_complete_cb cb); - -bool box_normalise_block( - struct box *block, - const struct box *root, - struct html_content *c); - -#endif diff --git a/render/box_construct.c b/render/box_construct.c deleted file mode 100644 index 1aa99e2d1..000000000 --- a/render/box_construct.c +++ /dev/null @@ -1,3137 +0,0 @@ -/* - * Copyright 2005 James Bursa <bursa@users.sourceforge.net> - * Copyright 2003 Phil Mellor <monkeyson@users.sourceforge.net> - * Copyright 2005 John M Bell <jmb202@ecs.soton.ac.uk> - * Copyright 2006 Richard Wilson <info@tinct.net> - * Copyright 2008 Michael Drake <tlsa@netsurf-browser.org> - * - * 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 <http://www.gnu.org/licenses/>. - */ - -/** - * \file - * Implementation of conversion from DOM tree to box tree. - */ - -#include <assert.h> -#include <stdio.h> -#include <stdbool.h> -#include <stdlib.h> -#include <string.h> -#include <strings.h> - -#include "utils/config.h" -#include "utils/nsoption.h" -#include "utils/corestrings.h" -#include "utils/log.h" -#include "utils/messages.h" -#include "utils/talloc.h" -#include "utils/string.h" -#include "utils/ascii.h" -#include "netsurf/css.h" -#include "netsurf/misc.h" -#include "netsurf/plot_style.h" -#include "content/content_protected.h" -#include "css/hints.h" -#include "css/select.h" -#include "css/utils.h" -#include "desktop/gui_internal.h" - -#include "render/box.h" -#include "render/box_textarea.h" -#include "render/form_internal.h" -#include "render/html_internal.h" - -/** - * Context for box tree construction - */ -struct box_construct_ctx { - html_content *content; /**< Content we're constructing for */ - - dom_node *n; /**< Current node to process */ - - struct box *root_box; /**< Root box in the tree */ - - box_construct_complete_cb cb; /**< Callback to invoke on completion */ - - int *bctx; /**< talloc context */ -}; - -/** - * Transient properties for construction of current node - */ -struct box_construct_props { - /** Style from which to inherit, or NULL if none */ - const css_computed_style *parent_style; - /** Current link target, or NULL if none */ - nsurl *href; - /** Current frame target, or NULL if none */ - const char *target; - /** Current title attribute, or NULL if none */ - const char *title; - /** Identity of the current block-level container */ - struct box *containing_block; - /** Current container for inlines, or NULL if none - * \note If non-NULL, will be the last child of containing_block */ - struct box *inline_container; - /** Whether the current node is the root of the DOM tree */ - bool node_is_root; -}; - -static const content_type image_types = CONTENT_IMAGE; - -/* the strings are not important, since we just compare the pointers */ -const char *TARGET_SELF = "_self"; -const char *TARGET_PARENT = "_parent"; -const char *TARGET_TOP = "_top"; -const char *TARGET_BLANK = "_blank"; - -static void convert_xml_to_box(struct box_construct_ctx *ctx); -static bool box_construct_element(struct box_construct_ctx *ctx, - bool *convert_children); -static void box_construct_element_after(dom_node *n, html_content *content); -static bool box_construct_text(struct box_construct_ctx *ctx); -static css_select_results * box_get_style(html_content *c, - const css_computed_style *parent_style, - const css_computed_style *root_style, dom_node *n); -static void box_text_transform(char *s, unsigned int len, - enum css_text_transform_e tt); -#define BOX_SPECIAL_PARAMS dom_node *n, html_content *content, \ - struct box *box, bool *convert_children -static bool box_a(BOX_SPECIAL_PARAMS); -static bool box_body(BOX_SPECIAL_PARAMS); -static bool box_br(BOX_SPECIAL_PARAMS); -static bool box_image(BOX_SPECIAL_PARAMS); -static bool box_textarea(BOX_SPECIAL_PARAMS); -static bool box_select(BOX_SPECIAL_PARAMS); -static bool box_input(BOX_SPECIAL_PARAMS); -static bool box_button(BOX_SPECIAL_PARAMS); -static bool box_frameset(BOX_SPECIAL_PARAMS); -static bool box_create_frameset(struct content_html_frames *f, dom_node *n, - html_content *content); -static bool box_select_add_option(struct form_control *control, dom_node *n); -static bool box_noscript(BOX_SPECIAL_PARAMS); -static bool box_object(BOX_SPECIAL_PARAMS); -static bool box_embed(BOX_SPECIAL_PARAMS); -static bool box_pre(BOX_SPECIAL_PARAMS); -static bool box_iframe(BOX_SPECIAL_PARAMS); -static bool box_get_attribute(dom_node *n, const char *attribute, - void *context, char **value); - -/* element_table must be sorted by name */ -struct element_entry { - char name[10]; /* element type */ - bool (*convert)(BOX_SPECIAL_PARAMS); -}; -static const struct element_entry element_table[] = { - {"a", box_a}, - {"body", box_body}, - {"br", box_br}, - {"button", box_button}, - {"embed", box_embed}, - {"frameset", box_frameset}, - {"iframe", box_iframe}, - {"image", box_image}, - {"img", box_image}, - {"input", box_input}, - {"noscript", box_noscript}, - {"object", box_object}, - {"pre", box_pre}, - {"select", box_select}, - {"textarea", box_textarea} -}; -#define ELEMENT_TABLE_COUNT (sizeof(element_table) / sizeof(element_table[0])) - -/** - * Construct a box tree from an xml tree and stylesheets. - * - * \param n xml tree - * \param c content of type CONTENT_HTML to construct box tree in - * \param cb callback to report conversion completion - * \return netsurf error code indicating status of call - */ - -nserror dom_to_box(dom_node *n, html_content *c, box_construct_complete_cb cb) -{ - struct box_construct_ctx *ctx; - - if (c->bctx == NULL) { - /* create a context allocation for this box tree */ - c->bctx = talloc_zero(0, int); - if (c->bctx == NULL) { - return NSERROR_NOMEM; - } - } - - ctx = malloc(sizeof(*ctx)); - if (ctx == NULL) { - return NSERROR_NOMEM; - } - - ctx->content = c; - ctx->n = dom_node_ref(n); - ctx->root_box = NULL; - ctx->cb = cb; - ctx->bctx = c->bctx; - - return guit->misc->schedule(0, (void *)convert_xml_to_box, ctx); -} - -/* mapping from CSS display to box type - * this table must be in sync with libcss' css_display enum */ -static const box_type box_map[] = { - 0, /*CSS_DISPLAY_INHERIT,*/ - BOX_INLINE, /*CSS_DISPLAY_INLINE,*/ - BOX_BLOCK, /*CSS_DISPLAY_BLOCK,*/ - BOX_BLOCK, /*CSS_DISPLAY_LIST_ITEM,*/ - BOX_INLINE, /*CSS_DISPLAY_RUN_IN,*/ - BOX_INLINE_BLOCK, /*CSS_DISPLAY_INLINE_BLOCK,*/ - BOX_TABLE, /*CSS_DISPLAY_TABLE,*/ - BOX_TABLE, /*CSS_DISPLAY_INLINE_TABLE,*/ - BOX_TABLE_ROW_GROUP, /*CSS_DISPLAY_TABLE_ROW_GROUP,*/ - BOX_TABLE_ROW_GROUP, /*CSS_DISPLAY_TABLE_HEADER_GROUP,*/ - BOX_TABLE_ROW_GROUP, /*CSS_DISPLAY_TABLE_FOOTER_GROUP,*/ - BOX_TABLE_ROW, /*CSS_DISPLAY_TABLE_ROW,*/ - BOX_NONE, /*CSS_DISPLAY_TABLE_COLUMN_GROUP,*/ - BOX_NONE, /*CSS_DISPLAY_TABLE_COLUMN,*/ - BOX_TABLE_CELL, /*CSS_DISPLAY_TABLE_CELL,*/ - BOX_INLINE, /*CSS_DISPLAY_TABLE_CAPTION,*/ - BOX_NONE /*CSS_DISPLAY_NONE*/ -}; - -static inline struct box *box_for_node(dom_node *n) -{ - struct box *box = NULL; - dom_exception err; - - err = dom_node_get_user_data(n, corestring_dom___ns_key_box_node_data, - (void *) &box); - if (err != DOM_NO_ERR) - return NULL; - - return box; -} - -static inline bool box_is_root(dom_node *n) -{ - dom_node *parent; - dom_node_type type; - dom_exception err; - - err = dom_node_get_parent_node(n, &parent); - if (err != DOM_NO_ERR) - return false; - - if (parent != NULL) { - err = dom_node_get_node_type(parent, &type); - - dom_node_unref(parent); - - if (err != DOM_NO_ERR) - return false; - - if (type != DOM_DOCUMENT_NODE) - return false; - } - - return true; -} - -/** - * Find the next node in the DOM tree, completing - * element construction where appropriate. - * - * \param n Current node - * \param content Containing content - * \param convert_children Whether to consider children of \a n - * \return Next node to process, or NULL if complete - * - * \note \a n will be unreferenced - */ -static dom_node *next_node(dom_node *n, html_content *content, - bool convert_children) -{ - dom_node *next = NULL; - bool has_children; - dom_exception err; - - err = dom_node_has_child_nodes(n, &has_children); - if (err != DOM_NO_ERR) { - dom_node_unref(n); - return NULL; - } - - if (convert_children && has_children) { - err = dom_node_get_first_child(n, &next); - if (err != DOM_NO_ERR) { - dom_node_unref(n); - return NULL; - } - dom_node_unref(n); - } else { - err = dom_node_get_next_sibling(n, &next); - if (err != DOM_NO_ERR) { - dom_node_unref(n); - return NULL; - } - - if (next != NULL) { - if (box_for_node(n) != NULL) - box_construct_element_after(n, content); - dom_node_unref(n); - } else { - if (box_for_node(n) != NULL) - box_construct_element_after(n, content); - - while (box_is_root(n) == false) { - dom_node *parent = NULL; - dom_node *parent_next = NULL; - - err = dom_node_get_parent_node(n, &parent); - if (err != DOM_NO_ERR) { - dom_node_unref(n); - return NULL; - } - - assert(parent != NULL); - - err = dom_node_get_next_sibling(parent, - &parent_next); - if (err != DOM_NO_ERR) { - dom_node_unref(parent); - dom_node_unref(n); - return NULL; - } - - if (parent_next != NULL) { - dom_node_unref(parent_next); - dom_node_unref(parent); - break; - } - - dom_node_unref(n); - n = parent; - parent = NULL; - - if (box_for_node(n) != NULL) { - box_construct_element_after( - n, content); - } - } - - if (box_is_root(n) == false) { - dom_node *parent = NULL; - - err = dom_node_get_parent_node(n, &parent); - if (err != DOM_NO_ERR) { - dom_node_unref(n); - return NULL; - } - - assert(parent != NULL); - - err = dom_node_get_next_sibling(parent, &next); - if (err != DOM_NO_ERR) { - dom_node_unref(parent); - dom_node_unref(n); - return NULL; - } - - if (box_for_node(parent) != NULL) { - box_construct_element_after(parent, - content); - } - - dom_node_unref(parent); - } - - dom_node_unref(n); - } - } - - return next; -} - -/** - * Convert an ELEMENT node to a box tree fragment, - * then schedule conversion of the next ELEMENT node - */ -void convert_xml_to_box(struct box_construct_ctx *ctx) -{ - dom_node *next; - bool convert_children; - uint32_t num_processed = 0; - const uint32_t max_processed_before_yield = 10; - - do { - convert_children = true; - - assert(ctx->n != NULL); - - if (box_construct_element(ctx, &convert_children) == false) { - ctx->cb(ctx->content, false); - dom_node_unref(ctx->n); - free(ctx); - return; - } - - /* Find next element to process, converting text nodes as we go */ - next = next_node(ctx->n, ctx->content, convert_children); - while (next != NULL) { - dom_node_type type; - dom_exception err; - - err = dom_node_get_node_type(next, &type); - if (err != DOM_NO_ERR) { - ctx->cb(ctx->content, false); - dom_node_unref(next); - free(ctx); - return; - } - - if (type == DOM_ELEMENT_NODE) - break; - - if (type == DOM_TEXT_NODE) { - ctx->n = next; - if (box_construct_text(ctx) == false) { - ctx->cb(ctx->content, false); - dom_node_unref(ctx->n); - free(ctx); - return; - } - } - - next = next_node(next, ctx->content, true); - } - - ctx->n = next; - - if (next == NULL) { - /* Conversion complete */ - struct box root; - - memset(&root, 0, sizeof(root)); - - root.type = BOX_BLOCK; - root.children = root.last = ctx->root_box; - root.children->parent = &root; - - /** \todo Remove box_normalise_block */ - if (box_normalise_block(&root, ctx->root_box, - ctx->content) == false) { - ctx->cb(ctx->content, false); - } else { - ctx->content->layout = root.children; - ctx->content->layout->parent = NULL; - - ctx->cb(ctx->content, true); - } - - assert(ctx->n == NULL); - - free(ctx); - return; - } - } while (++num_processed < max_processed_before_yield); - - /* More work to do: schedule a continuation */ - guit->misc->schedule(0, (void *)convert_xml_to_box, ctx); -} - -/** - * Construct a list marker box - * - * \param box Box to attach marker to - * \param title Current title attribute - * \param ctx Box construction context - * \param parent Current block-level container - * \return true on success, false on memory exhaustion - */ -static bool box_construct_marker(struct box *box, const char *title, - struct box_construct_ctx *ctx, struct box *parent) -{ - lwc_string *image_uri; - struct box *marker; - - marker = box_create(NULL, box->style, false, NULL, NULL, title, - NULL, ctx->bctx); - if (marker == false) - return false; - - marker->type = BOX_BLOCK; - - /** \todo marker content (list-style-type) */ - switch (css_computed_list_style_type(box->style)) { - case CSS_LIST_STYLE_TYPE_DISC: - /* 2022 BULLET */ - marker->text = (char *) "\342\200\242"; - marker->length = 3; - break; - case CSS_LIST_STYLE_TYPE_CIRCLE: - /* 25CB WHITE CIRCLE */ - marker->text = (char *) "\342\227\213"; - marker->length = 3; - break; - case CSS_LIST_STYLE_TYPE_SQUARE: - /* 25AA BLACK SMALL SQUARE */ - marker->text = (char *) "\342\226\252"; - marker->length = 3; - break; - case CSS_LIST_STYLE_TYPE_DECIMAL: - case CSS_LIST_STYLE_TYPE_LOWER_ALPHA: - case CSS_LIST_STYLE_TYPE_LOWER_ROMAN: - case CSS_LIST_STYLE_TYPE_UPPER_ALPHA: - case CSS_LIST_STYLE_TYPE_UPPER_ROMAN: - default: - if (parent->last) { - struct box *last = parent->last; - - /* Drill down into last child of parent - * to find the list marker (if any) - * - * Floated list boxes end up as: - * - * parent - * BOX_INLINE_CONTAINER - * BOX_FLOAT_{LEFT,RIGHT} - * BOX_BLOCK <-- list box - * ... - */ - while (last != NULL && last->list_marker == NULL) { - struct box *last_inner = last; - - while (last_inner != NULL) { - if (last_inner->list_marker != NULL) - break; - if (last_inner->type == - BOX_INLINE_CONTAINER || - last_inner->type == - BOX_FLOAT_LEFT || - last_inner->type == - BOX_FLOAT_RIGHT) { - last_inner = last_inner->last; - } else { - last_inner = NULL; - } - } - if (last_inner != NULL) { - last = last_inner; - } else { - last = last->prev; - } - } - - if (last && last->list_marker) { - marker->rows = last->list_marker->rows + 1; - } - } - - marker->text = talloc_array(ctx->bctx, char, 20); - if (marker->text == NULL) - return false; - - snprintf(marker->text, 20, "%u.", marker->rows); - marker->length = strlen(marker->text); - break; - case CSS_LIST_STYLE_TYPE_NONE: - marker->text = 0; - marker->length = 0; - break; - } - - if (css_computed_list_style_image(box->style, &image_uri) == CSS_LIST_STYLE_IMAGE_URI && - (image_uri != NULL) && - (nsoption_bool(foreground_images) == true)) { - nsurl *url; - nserror error; - - /* TODO: we get a url out of libcss as a lwc string, but - * earlier we already had it as a nsurl after we - * nsurl_joined it. Can this be improved? - * For now, just making another nsurl. */ - error = nsurl_create(lwc_string_data(image_uri), &url); - if (error != NSERROR_OK) - return false; - - if (html_fetch_object(ctx->content, url, marker, image_types, - ctx->content->base.available_width, 1000, false) == - false) { - nsurl_unref(url); - return false; - } - nsurl_unref(url); - } - - box->list_marker = marker; - marker->parent = box; - - return true; -} - -/** - * Construct the box required for a generated element. - * - * \param n XML node of type XML_ELEMENT_NODE - * \param content Content of type CONTENT_HTML that is being processed - * \param box Box which may have generated content - * \param style Complete computed style for pseudo element, or NULL - * - * TODO: - * This is currently incomplete. It just does enough to support the clearfix - * hack. ( http://www.positioniseverything.net/easyclearing.html ) - */ -static void box_construct_generate(dom_node *n, html_content *content, - struct box *box, const css_computed_style *style) -{ - struct box *gen = NULL; - enum css_display_e computed_display; - const css_computed_content_item *c_item; - - /* Nothing to generate if the parent box is not a block */ - if (box->type != BOX_BLOCK) - return; - - /* To determine if an element has a pseudo element, we select - * for it and test to see if the returned style's content - * property is set to normal. */ - if (style == NULL || - css_computed_content(style, &c_item) == - CSS_CONTENT_NORMAL) { - /* No pseudo element */ - return; - } - - /* create box for this element */ - computed_display = ns_computed_display(style, box_is_root(n)); - if (computed_display == CSS_DISPLAY_BLOCK || - computed_display == CSS_DISPLAY_TABLE) { - /* currently only support block level boxes */ - - /** \todo Not wise to drop const from the computed style */ - gen = box_create(NULL, (css_computed_style *) style, - false, NULL, NULL, NULL, NULL, content->bctx); - if (gen == NULL) { - return; - } - - /* set box type from computed display */ - gen->type = box_map[ns_computed_display( - style, box_is_root(n))]; - - box_add_child(box, gen); - } -} - -/** - * Extract transient construction properties - * - * \param n Current DOM node to convert - * \param props Property object to populate - */ -static void box_extract_properties(dom_node *n, - struct box_construct_props *props) -{ - memset(props, 0, sizeof(*props)); - - props->node_is_root = box_is_root(n); - - /* Extract properties from containing DOM node */ - if (props->node_is_root == false) { - dom_node *current_node = n; - dom_node *parent_node = NULL; - struct box *parent_box; - dom_exception err; - - /* Find ancestor node containing parent box */ - while (true) { - err = dom_node_get_parent_node(current_node, - &parent_node); - if (err != DOM_NO_ERR || parent_node == NULL) - break; - - parent_box = box_for_node(parent_node); - - if (parent_box != NULL) { - props->parent_style = parent_box->style; - props->href = parent_box->href; - props->target = parent_box->target; - props->title = parent_box->title; - - dom_node_unref(parent_node); - break; - } else { - if (current_node != n) - dom_node_unref(current_node); - current_node = parent_node; - parent_node = NULL; - } - } - - /* Find containing block (may be parent) */ - while (true) { - struct box *b; - - err = dom_node_get_parent_node(current_node, - &parent_node); - if (err != DOM_NO_ERR || parent_node == NULL) { - if (current_node != n) - dom_node_unref(current_node); - break; - } - - if (current_node != n) - dom_node_unref(current_node); - - b = box_for_node(parent_node); - - /* Children of nodes that created an inline box - * will generate boxes which are attached as - * _siblings_ of the box generated for their - * parent node. Note, however, that we'll still - * use the parent node's styling as the parent - * style, above. */ - if (b != NULL && b->type != BOX_INLINE && - b->type != BOX_BR) { - props->containing_block = b; - - dom_node_unref(parent_node); - break; - } else { - current_node = parent_node; - parent_node = NULL; - } - } - } - - /* Compute current inline container, if any */ - if (props->containing_block != NULL && - props->containing_block->last != NULL && - props->containing_block->last->type == - BOX_INLINE_CONTAINER) - props->inline_container = props->containing_block->last; -} - -/** - * Construct the box tree for an XML element. - * - * \param ctx Tree construction context - * \param convert_children Whether to convert children - * \return true on success, false on memory exhaustion - */ - -bool box_construct_element(struct box_construct_ctx *ctx, - bool *convert_children) -{ - dom_string *title0, *s; - lwc_string *id = NULL; - struct box *box = NULL, *old_box; - css_select_results *styles = NULL; - struct element_entry *element; - lwc_string *bgimage_uri; - dom_exception err; - struct box_construct_props props; - const css_computed_style *root_style = NULL; - - assert(ctx->n != NULL); - - box_extract_properties(ctx->n, &props); - - if (props.containing_block != NULL) { - /* In case the containing block is a pre block, we clear - * the PRE_STRIP flag since it is not used if we follow - * the pre with a tag */ - props.containing_block->flags &= ~PRE_STRIP; - } - - if (props.node_is_root == false) { - root_style = ctx->root_box->style; - } - - styles = box_get_style(ctx->content, props.parent_style, root_style, - ctx->n); - if (styles == NULL) - return false; - - /* Extract title attribute, if present */ - err = dom_element_get_attribute(ctx->n, corestring_dom_title, &title0); - if (err != DOM_NO_ERR) - return false; - - if (title0 != NULL) { - char *t = squash_whitespace(dom_string_data(title0)); - - dom_string_unref(title0); - - if (t == NULL) - return false; - - props.title = talloc_strdup(ctx->bctx, t); - - free(t); - - if (props.title == NULL) - return false; - } - - /* Extract id attribute, if present */ - err = dom_element_get_attribute(ctx->n, corestring_dom_id, &s); - if (err != DOM_NO_ERR) - return false; - - if (s != NULL) { - err = dom_string_intern(s, &id); - if (err != DOM_NO_ERR) - id = NULL; - - dom_string_unref(s); - } - - box = box_create(styles, styles->styles[CSS_PSEUDO_ELEMENT_NONE], false, - props.href, props.target, props.title, id, - ctx->bctx); - if (box == NULL) - return false; - - /* If this is the root box, add it to the context */ - if (props.node_is_root) - ctx->root_box = box; - - /* Deal with colspan/rowspan */ - err = dom_element_get_attribute(ctx->n, corestring_dom_colspan, &s); - if (err != DOM_NO_ERR) - return false; - - if (s != NULL) { - const char *val = dom_string_data(s); - - if ('0' <= val[0] && val[0] <= '9') - box->columns = strtol(val, NULL, 10); - - dom_string_unref(s); - } - - err = dom_element_get_attribute(ctx->n, corestring_dom_rowspan, &s); - if (err != DOM_NO_ERR) - return false; - - if (s != NULL) { - const char *val = dom_string_data(s); - - if ('0' <= val[0] && val[0] <= '9') - box->rows = strtol(val, NULL, 10); - - dom_string_unref(s); - } - - /* Set box type from computed display */ - if ((css_computed_position(box->style) == CSS_POSITION_ABSOLUTE || - css_computed_position(box->style) == - CSS_POSITION_FIXED) && - (ns_computed_display_static(box->style) == - CSS_DISPLAY_INLINE || - ns_computed_display_static(box->style) == - CSS_DISPLAY_INLINE_BLOCK || - ns_computed_display_static(box->style) == - CSS_DISPLAY_INLINE_TABLE)) { - /* Special case for absolute positioning: make absolute inlines - * into inline block so that the boxes are constructed in an - * inline container as if they were not absolutely positioned. - * Layout expects and handles this. */ - box->type = box_map[CSS_DISPLAY_INLINE_BLOCK]; - } else if (props.node_is_root) { - /* Special case for root element: force it to BLOCK, or the - * rest of the layout will break. */ - box->type = BOX_BLOCK; - } else { - /* Normal mapping */ - box->type = box_map[ns_computed_display(box->style, - props.node_is_root)]; - } - - err = dom_node_get_node_name(ctx->n, &s); - if (err != DOM_NO_ERR || s == NULL) - return false; - - /* Special elements */ - element = bsearch(dom_string_data(s), element_table, - ELEMENT_TABLE_COUNT, sizeof(element_table[0]), - (int (*)(const void *, const void *)) strcasecmp); - - dom_string_unref(s); - - if (element != NULL) { - /* A special convert function exists for this element */ - if (element->convert(ctx->n, ctx->content, box, - convert_children) == false) - return false; - } - - /* Handle the :before pseudo element */ - if (!(box->flags & IS_REPLACED)) { - box_construct_generate(ctx->n, ctx->content, box, - box->styles->styles[CSS_PSEUDO_ELEMENT_BEFORE]); - } - - if (box->type == BOX_NONE || (ns_computed_display(box->style, - props.node_is_root) == CSS_DISPLAY_NONE && - props.node_is_root == false)) { - css_select_results_destroy(styles); - box->styles = NULL; - box->style = NULL; - - /* Invalidate associated gadget, if any */ - if (box->gadget != NULL) { - box->gadget->box = NULL; - box->gadget = NULL; - } - - /* Can't do this, because the lifetimes of boxes and gadgets - * are inextricably linked. Fortunately, talloc will save us - * (for now) */ - /* box_free_box(box); */ - - *convert_children = false; - - return true; - } - - /* Attach DOM node to box */ - err = dom_node_set_user_data(ctx->n, - corestring_dom___ns_key_box_node_data, box, NULL, - (void *) &old_box); - if (err != DOM_NO_ERR) - return false; - - /* Attach box to DOM node */ - box->node = dom_node_ref(ctx->n); - - if (props.inline_container == NULL && - (box->type == BOX_INLINE || - box->type == BOX_BR || - box->type == BOX_INLINE_BLOCK || - css_computed_float(box->style) == CSS_FLOAT_LEFT || - css_computed_float(box->style) == CSS_FLOAT_RIGHT) && - props.node_is_root == false) { - /* Found an inline child of a block without a current container - * (i.e. this box is the first child of its parent, or was - * preceded by block-level siblings) */ - assert(props.containing_block != NULL && - "Box must have containing block."); - - props.inline_container = box_create(NULL, NULL, false, NULL, - NULL, NULL, NULL, ctx->bctx); - if (props.inline_container == NULL) - return false; - - props.inline_container->type = BOX_INLINE_CONTAINER; - - box_add_child(props.containing_block, props.inline_container); - } - - /* Kick off fetch for any background image */ - if (css_computed_background_image(box->style, &bgimage_uri) == - CSS_BACKGROUND_IMAGE_IMAGE && bgimage_uri != NULL && - nsoption_bool(background_images) == true) { - nsurl *url; - nserror error; - - /* TODO: we get a url out of libcss as a lwc string, but - * earlier we already had it as a nsurl after we - * nsurl_joined it. Can this be improved? - * For now, just making another nsurl. */ - error = nsurl_create(lwc_string_data(bgimage_uri), &url); - if (error == NSERROR_OK) { - /* Fetch image if we got a valid URL */ - if (html_fetch_object(ctx->content, url, box, - image_types, - ctx->content->base.available_width, - 1000, true) == false) { - nsurl_unref(url); - return false; - } - nsurl_unref(url); - } - } - - if (*convert_children) - box->flags |= CONVERT_CHILDREN; - - if (box->type == BOX_INLINE || box->type == BOX_BR || - box->type == BOX_INLINE_BLOCK) { - /* Inline container must exist, as we'll have - * created it above if it didn't */ - assert(props.inline_container != NULL); - - box_add_child(props.inline_container, box); - } else { - if (ns_computed_display(box->style, props.node_is_root) == - CSS_DISPLAY_LIST_ITEM) { - /* List item: compute marker */ - if (box_construct_marker(box, props.title, ctx, - props.containing_block) == false) - return false; - } - - if (props.node_is_root == false && - (css_computed_float(box->style) == - CSS_FLOAT_LEFT || - css_computed_float(box->style) == - CSS_FLOAT_RIGHT)) { - /* Float: insert a float between the parent and box. */ - struct box *flt = box_create(NULL, NULL, false, - props.href, props.target, props.title, - NULL, ctx->bctx); - if (flt == NULL) - return false; - - if (css_computed_float(box->style) == CSS_FLOAT_LEFT) - flt->type = BOX_FLOAT_LEFT; - else - flt->type = BOX_FLOAT_RIGHT; - - box_add_child(props.inline_container, flt); - box_add_child(flt, box); - } else { - /* Non-floated block-level box: add to containing block - * if there is one. If we're the root box, then there - * won't be. */ - if (props.containing_block != NULL) - box_add_child(props.containing_block, box); - } - } - - return true; -} - -/** - * Complete construction of the box tree for an element. - * - * \param n DOM node to construct for - * \param content Containing document - * - * This will be called after all children of an element have been processed - */ -void box_construct_element_after(dom_node *n, html_content *content) -{ - struct box_construct_props props; - struct box *box = box_for_node(n); - - assert(box != NULL); - - box_extract_properties(n, &props); - - if (box->type == BOX_INLINE || box->type == BOX_BR) { - /* Insert INLINE_END into containing block */ - struct box *inline_end; - bool has_children; - dom_exception err; - - err = dom_node_has_child_nodes(n, &has_children); - if (err != DOM_NO_ERR) - return; - - if (has_children == false || - (box->flags & CONVERT_CHILDREN) == 0) { - /* No children, or didn't want children converted */ - return; - } - - if (props.inline_container == NULL) { - /* Create inline container if we don't have one */ - props.inline_container = box_create(NULL, NULL, false, - NULL, NULL, NULL, NULL, content->bctx); - if (props.inline_container == NULL) - return; - - props.inline_container->type = BOX_INLINE_CONTAINER; - - box_add_child(props.containing_block, - props.inline_container); - } - - inline_end = box_create(NULL, box->style, false, - box->href, box->target, box->title, - box->id == NULL ? NULL : - lwc_string_ref(box->id), content->bctx); - if (inline_end != NULL) { - inline_end->type = BOX_INLINE_END; - - assert(props.inline_container != NULL); - - box_add_child(props.inline_container, inline_end); - - box->inline_end = inline_end; - inline_end->inline_end = box; - } - } else if (!(box->flags & IS_REPLACED)) { - /* Handle the :after pseudo element */ - box_construct_generate(n, content, box, - box->styles->styles[CSS_PSEUDO_ELEMENT_AFTER]); - } -} - -/** - * Construct the box tree for an XML text node. - * - * \param ctx Tree construction context - * \return true on success, false on memory exhaustion - */ - -bool box_construct_text(struct box_construct_ctx *ctx) -{ - struct box_construct_props props; - struct box *box = NULL; - dom_string *content; - dom_exception err; - - assert(ctx->n != NULL); - - box_extract_properties(ctx->n, &props); - - assert(props.containing_block != NULL); - - err = dom_characterdata_get_data(ctx->n, &content); - if (err != DOM_NO_ERR || content == NULL) - return false; - - if (css_computed_white_space(props.parent_style) == - CSS_WHITE_SPACE_NORMAL || - css_computed_white_space(props.parent_style) == - CSS_WHITE_SPACE_NOWRAP) { - char *text; - - text = squash_whitespace(dom_string_data(content)); - - dom_string_unref(content); - - if (text == NULL) - return false; - - /* if the text is just a space, combine it with the preceding - * text node, if any */ - if (text[0] == ' ' && text[1] == 0) { - if (props.inline_container != NULL) { - assert(props.inline_container->last != NULL); - - props.inline_container->last->space = - UNKNOWN_WIDTH; - } - - free(text); - - return true; - } - - if (props.inline_container == NULL) { - /* Child of a block without a current container - * (i.e. this box is the first child of its parent, or - * was preceded by block-level siblings) */ - props.inline_container = box_create(NULL, NULL, false, - NULL, NULL, NULL, NULL, ctx->bctx); - if (props.inline_container == NULL) { - free(text); - return false; - } - - props.inline_container->type = BOX_INLINE_CONTAINER; - - box_add_child(props.containing_block, - props.inline_container); - } - - /** \todo Dropping const here is not clever */ - box = box_create(NULL, - (css_computed_style *) props.parent_style, - false, props.href, props.target, props.title, - NULL, ctx->bctx); - if (box == NULL) { - free(text); - return false; - } - - box->type = BOX_TEXT; - - box->text = talloc_strdup(ctx->bctx, text); - free(text); - if (box->text == NULL) - return false; - - box->length = strlen(box->text); - - /* strip ending space char off */ - if (box->length > 1 && box->text[box->length - 1] == ' ') { - box->space = UNKNOWN_WIDTH; - box->length--; - } - - if (css_computed_text_transform(props.parent_style) != - CSS_TEXT_TRANSFORM_NONE) - box_text_transform(box->text, box->length, - css_computed_text_transform( - props.parent_style)); - - box_add_child(props.inline_container, box); - - if (box->text[0] == ' ') { - box->length--; - - memmove(box->text, &box->text[1], box->length); - - if (box->prev != NULL) - box->prev->space = UNKNOWN_WIDTH; - } - } else { - /* white-space: pre */ - char *text; - size_t text_len = dom_string_byte_length(content); - size_t i; - char *current; - enum css_white_space_e white_space = - css_computed_white_space(props.parent_style); - - /* note: pre-wrap/pre-line are unimplemented */ - assert(white_space == CSS_WHITE_SPACE_PRE || - white_space == CSS_WHITE_SPACE_PRE_LINE || - white_space == CSS_WHITE_SPACE_PRE_WRAP); - - text = malloc(text_len + 1); - dom_string_unref(content); - - if (text == NULL) - return false; - - memcpy(text, dom_string_data(content), text_len); - text[text_len] = '\0'; - - /* TODO: Handle tabs properly */ - for (i = 0; i < text_len; i++) - if (text[i] == '\t') - text[i] = ' '; - - if (css_computed_text_transform(props.parent_style) != - CSS_TEXT_TRANSFORM_NONE) - box_text_transform(text, strlen(text), - css_computed_text_transform( - props.parent_style)); - - current = text; - - /* swallow a single leading new line */ - if (props.containing_block->flags & PRE_STRIP) { - switch (*current) { - case '\n': - current++; - break; - case '\r': - current++; - if (*current == '\n') - current++; - break; - } - props.containing_block->flags &= ~PRE_STRIP; - } - - do { - size_t len = strcspn(current, "\r\n"); - - char old = current[len]; - - current[len] = 0; - - if (props.inline_container == NULL) { - /* Child of a block without a current container - * (i.e. this box is the first child of its - * parent, or was preceded by block-level - * siblings) */ - props.inline_container = box_create(NULL, NULL, - false, NULL, NULL, NULL, NULL, - ctx->bctx); - if (props.inline_container == NULL) { - free(text); - return false; - } - - props.inline_container->type = - BOX_INLINE_CONTAINER; - - box_add_child(props.containing_block, - props.inline_container); - } - - /** \todo Dropping const isn't clever */ - box = box_create(NULL, - (css_computed_style *) props.parent_style, - false, props.href, props.target, props.title, - NULL, ctx->bctx); - if (box == NULL) { - free(text); - return false; - } - - box->type = BOX_TEXT; - - box->text = talloc_strdup(ctx->bctx, current); - if (box->text == NULL) { - free(text); - return false; - } - - box->length = strlen(box->text); - - box_add_child(props.inline_container, box); - - current[len] = old; - - current += len; - - if (current[0] != '\0') { - /* Linebreak: create new inline container */ - props.inline_container = box_create(NULL, NULL, - false, NULL, NULL, NULL, NULL, - ctx->bctx); - if (props.inline_container == NULL) { - free(text); - return false; - } - - props.inline_container->type = - BOX_INLINE_CONTAINER; - - box_add_child(props.containing_block, - props.inline_container); - - if (current[0] == '\r' && current[1] == '\n') - current += 2; - else - current++; - } - } while (*current); - - free(text); - } - - return true; -} - -/** - * Get the style for an element. - * - * \param c content of type CONTENT_HTML that is being processed - * \param parent_style style at this point in xml tree, or NULL for root - * \param root_style root node's style, or NULL for root - * \param n node in xml tree - * \return the new style, or NULL on memory exhaustion - */ -css_select_results *box_get_style(html_content *c, - const css_computed_style *parent_style, - const css_computed_style *root_style, dom_node *n) -{ - dom_string *s; - dom_exception err; - css_stylesheet *inline_style = NULL; - css_select_results *styles; - nscss_select_ctx ctx; - - /* Firstly, construct inline stylesheet, if any */ - err = dom_element_get_attribute(n, corestring_dom_style, &s); - if (err != DOM_NO_ERR) - return NULL; - - if (s != NULL) { - inline_style = nscss_create_inline_style( - (const uint8_t *) dom_string_data(s), - dom_string_byte_length(s), - c->encoding, - nsurl_access(c->base_url), - c->quirks != DOM_DOCUMENT_QUIRKS_MODE_NONE); - - dom_string_unref(s); - - if (inline_style == NULL) - return NULL; - } - - /* Populate selection context */ - ctx.ctx = c->select_ctx; - ctx.quirks = (c->quirks == DOM_DOCUMENT_QUIRKS_MODE_FULL); - ctx.base_url = c->base_url; - ctx.universal = c->universal; - ctx.root_style = root_style; - ctx.parent_style = parent_style; - - /* Select style for element */ - styles = nscss_get_style(&ctx, n, CSS_MEDIA_SCREEN, inline_style); - - /* No longer need inline style */ - if (inline_style != NULL) - css_stylesheet_destroy(inline_style); - - return styles; -} - - -/** - * Apply the CSS text-transform property to given text for its ASCII chars. - * - * \param s string to transform - * \param len length of s - * \param tt transform type - */ - -void box_text_transform(char *s, unsigned int len, enum css_text_transform_e tt) -{ - unsigned int i; - if (len == 0) - return; - switch (tt) { - case CSS_TEXT_TRANSFORM_UPPERCASE: - for (i = 0; i < len; ++i) - if ((unsigned char) s[i] < 0x80) - s[i] = toupper(s[i]); - break; - case CSS_TEXT_TRANSFORM_LOWERCASE: - for (i = 0; i < len; ++i) - if ((unsigned char) s[i] < 0x80) - s[i] = tolower(s[i]); - break; - case CSS_TEXT_TRANSFORM_CAPITALIZE: - if ((unsigned char) s[0] < 0x80) - s[0] = toupper(s[0]); - for (i = 1; i < len; ++i) - if ((unsigned char) s[i] < 0x80 && - isspace(s[i - 1])) - s[i] = toupper(s[i]); - break; - default: - break; - } -} - - -/** - * \name Special case element handlers - * - * These functions are called by box_construct_element() when an element is - * being converted, according to the entries in element_table. - * - * The parameters are the xmlNode, the content for the document, and a partly - * filled in box structure for the element. - * - * Return true on success, false on memory exhaustion. Set *convert_children - * to false if children of this element in the XML tree should be skipped (for - * example, if they have been processed in some special way already). - * - * Elements ordered as in the HTML 4.01 specification. Section numbers in - * brackets [] refer to the spec. - * - * \{ - */ - -/** - * Document body [7.5.1]. - */ - -bool box_body(BOX_SPECIAL_PARAMS) -{ - css_color color; - - css_computed_background_color(box->style, &color); - if (nscss_color_is_transparent(color)) - content->background_colour = NS_TRANSPARENT; - else - content->background_colour = nscss_color_to_ns(color); - - return true; -} - - -/** - * Forced line break [9.3.2]. - */ - -bool box_br(BOX_SPECIAL_PARAMS) -{ - box->type = BOX_BR; - return true; -} - -/** - * Preformatted text [9.3.4]. - */ - -bool box_pre(BOX_SPECIAL_PARAMS) -{ - box->flags |= PRE_STRIP; - return true; -} - -/** - * Anchor [12.2]. - */ - -bool box_a(BOX_SPECIAL_PARAMS) -{ - bool ok; - nsurl *url; - dom_string *s; - dom_exception err; - - err = dom_element_get_attribute(n, corestring_dom_href, &s); - if (err == DOM_NO_ERR && s != NULL) { - ok = box_extract_link(content, s, content->base_url, &url); - dom_string_unref(s); - if (!ok) - return false; - if (url) { - if (box->href != NULL) - nsurl_unref(box->href); - box->href = url; - } - } - - /* name and id share the same namespace */ - err = dom_element_get_attribute(n, corestring_dom_name, &s); - if (err == DOM_NO_ERR && s != NULL) { - lwc_string *lwc_name; - - err = dom_string_intern(s, &lwc_name); - - dom_string_unref(s); - - if (err == DOM_NO_ERR) { - /* name replaces existing id - * TODO: really? */ - if (box->id != NULL) - lwc_string_unref(box->id); - - box->id = lwc_name; - } - } - - /* target frame [16.3] */ - err = dom_element_get_attribute(n, corestring_dom_target, &s); - if (err == DOM_NO_ERR && s != NULL) { - if (dom_string_caseless_lwc_isequal(s, - corestring_lwc__blank)) - box->target = TARGET_BLANK; - else if (dom_string_caseless_lwc_isequal(s, - corestring_lwc__top)) - box->target = TARGET_TOP; - else if (dom_string_caseless_lwc_isequal(s, - corestring_lwc__parent)) - box->target = TARGET_PARENT; - else if (dom_string_caseless_lwc_isequal(s, - corestring_lwc__self)) - /* the default may have been overridden by a - * <base target=...>, so this is different to 0 */ - box->target = TARGET_SELF; - else { - /* 6.16 says that frame names must begin with [a-zA-Z] - * This doesn't match reality, so just take anything */ - box->target = talloc_strdup(content->bctx, - dom_string_data(s)); - if (!box->target) { - dom_string_unref(s); - return false; - } - } - dom_string_unref(s); - } - - return true; -} - - -/** - * Embedded image [13.2]. - */ - -bool box_image(BOX_SPECIAL_PARAMS) -{ - bool ok; - dom_string *s; - dom_exception err; - nsurl *url; - enum css_width_e wtype; - enum css_height_e htype; - css_fixed value = 0; - css_unit wunit = CSS_UNIT_PX; - css_unit hunit = CSS_UNIT_PX; - - if (box->style && ns_computed_display(box->style, - box_is_root(n)) == CSS_DISPLAY_NONE) - return true; - - /* handle alt text */ - err = dom_element_get_attribute(n, corestring_dom_alt, &s); - if (err == DOM_NO_ERR && s != NULL) { - char *alt = squash_whitespace(dom_string_data(s)); - dom_string_unref(s); - if (alt == NULL) - return false; - box->text = talloc_strdup(content->bctx, alt); - free(alt); - if (box->text == NULL) - return false; - box->length = strlen(box->text); - } - - if (nsoption_bool(foreground_images) == false) { - return true; - } - - /* imagemap associated with this image */ - if (!box_get_attribute(n, "usemap", content->bctx, &box->usemap)) - return false; - if (box->usemap && box->usemap[0] == '#') - box->usemap++; - - /* get image URL */ - err = dom_element_get_attribute(n, corestring_dom_src, &s); - if (err != DOM_NO_ERR || s == NULL) - return true; - - if (box_extract_link(content, s, content->base_url, &url) == false) { - dom_string_unref(s); - return false; - } - - dom_string_unref(s); - - if (url == NULL) - return true; - - /* start fetch */ - box->flags |= IS_REPLACED; - ok = html_fetch_object(content, url, box, image_types, - content->base.available_width, 1000, false); - nsurl_unref(url); - - wtype = css_computed_width(box->style, &value, &wunit); - htype = css_computed_height(box->style, &value, &hunit); - - if (wtype == CSS_WIDTH_SET && wunit != CSS_UNIT_PCT && - htype == CSS_HEIGHT_SET && hunit != CSS_UNIT_PCT) { - /* We know the dimensions the image will be shown at before it's - * fetched. */ - box->flags |= REPLACE_DIM; - } - - return ok; -} - - -/** - * Noscript element - */ - -bool box_noscript(BOX_SPECIAL_PARAMS) -{ - /* If scripting is enabled, do not display the contents of noscript */ - if (content->enable_scripting) - *convert_children = false; - - return true; -} - - -/** - * Destructor for object_params, for <object> elements - * - * \param o The object params being destroyed. - * \return 0 to allow talloc to continue destroying the tree. - */ -static int box_object_talloc_destructor(struct object_params *o) -{ - if (o->codebase != NULL) - nsurl_unref(o->codebase); - if (o->classid != NULL) - nsurl_unref(o->classid); - if (o->data != NULL) - nsurl_unref(o->data); - - return 0; -} - -/** - * Generic embedded object [13.3]. - */ - -bool box_object(BOX_SPECIAL_PARAMS) -{ - struct object_params *params; - struct object_param *param; - dom_string *codebase, *classid, *data; - dom_node *c; - dom_exception err; - - if (box->style && ns_computed_display(box->style, - box_is_root(n)) == CSS_DISPLAY_NONE) - return true; - - if (box_get_attribute(n, "usemap", content->bctx, &box->usemap) == - false) - return false; - if (box->usemap && box->usemap[0] == '#') - box->usemap++; - - params = talloc(content->bctx, struct object_params); - if (params == NULL) - return false; - - talloc_set_destructor(params, box_object_talloc_destructor); - - params->data = NULL; - params->type = NULL; - params->codetype = NULL; - params->codebase = NULL; - params->classid = NULL; - params->params = NULL; - - /* codebase, classid, and data are URLs - * (codebase is the base for the other two) */ - err = dom_element_get_attribute(n, corestring_dom_codebase, &codebase); - if (err == DOM_NO_ERR && codebase != NULL) { - if (box_extract_link(content, codebase, content->base_url, - ¶ms->codebase) == false) { - dom_string_unref(codebase); - return false; - } - dom_string_unref(codebase); - } - if (params->codebase == NULL) - params->codebase = nsurl_ref(content->base_url); - - err = dom_element_get_attribute(n, corestring_dom_classid, &classid); - if (err == DOM_NO_ERR && classid != NULL) { - if (box_extract_link(content, classid, - params->codebase, ¶ms->classid) == false) { - dom_string_unref(classid); - return false; - } - dom_string_unref(classid); - } - - err = dom_element_get_attribute(n, corestring_dom_data, &data); - if (err == DOM_NO_ERR && data != NULL) { - if (box_extract_link(content, data, - params->codebase, ¶ms->data) == false) { - dom_string_unref(data); - return false; - } - dom_string_unref(data); - } - - if (params->classid == NULL && params->data == NULL) - /* nothing to embed; ignore */ - return true; - - /* Don't include ourself */ - if (params->classid != NULL && nsurl_compare(content->base_url, - params->classid, NSURL_COMPLETE)) - return true; - - if (params->data != NULL && nsurl_compare(content->base_url, - params->data, NSURL_COMPLETE)) - return true; - - /* codetype and type are MIME types */ - if (box_get_attribute(n, "codetype", params, - ¶ms->codetype) == false) - return false; - if (box_get_attribute(n, "type", params, ¶ms->type) == false) - return false; - - /* classid && !data => classid is used (consult codetype) - * (classid || !classid) && data => data is used (consult type) - * !classid && !data => invalid; ignored */ - - if (params->classid != NULL && params->data == NULL && - params->codetype != NULL) { - lwc_string *icodetype; - lwc_error lerror; - - lerror = lwc_intern_string(params->codetype, - strlen(params->codetype), &icodetype); - if (lerror != lwc_error_ok) - return false; - - if (content_factory_type_from_mime_type(icodetype) == - CONTENT_NONE) { - /* can't handle this MIME type */ - lwc_string_unref(icodetype); - return true; - } - - lwc_string_unref(icodetype); - } - - if (params->data != NULL && params->type != NULL) { - lwc_string *itype; - lwc_error lerror; - - lerror = lwc_intern_string(params->type, strlen(params->type), - &itype); - if (lerror != lwc_error_ok) - return false; - - if (content_factory_type_from_mime_type(itype) == - CONTENT_NONE) { - /* can't handle this MIME type */ - lwc_string_unref(itype); - return true; - } - - lwc_string_unref(itype); - } - - /* add parameters to linked list */ - err = dom_node_get_first_child(n, &c); - if (err != DOM_NO_ERR) - return false; - - while (c != NULL) { - dom_node *next; - dom_node_type type; - - err = dom_node_get_node_type(c, &type); - if (err != DOM_NO_ERR) { - dom_node_unref(c); - return false; - } - - if (type == DOM_ELEMENT_NODE) { - dom_string *name; - - err = dom_node_get_node_name(c, &name); - if (err != DOM_NO_ERR) { - dom_node_unref(c); - return false; - } - - if (!dom_string_caseless_lwc_isequal(name, - corestring_lwc_param)) { - /* The first non-param child is the start of - * the alt html. Therefore, we should break - * out of this loop. */ - dom_node_unref(c); - break; - } - - param = talloc(params, struct object_param); - if (param == NULL) { - dom_node_unref(c); - return false; - } - param->name = NULL; - param->value = NULL; - param->type = NULL; - param->valuetype = NULL; - param->next = NULL; - - if (box_get_attribute(c, "name", param, - ¶m->name) == false) { - dom_node_unref(c); - return false; - } - - if (box_get_attribute(c, "value", param, - ¶m->value) == false) { - dom_node_unref(c); - return false; - } - - if (box_get_attribute(c, "type", param, - ¶m->type) == false) { - dom_node_unref(c); - return false; - } - - if (box_get_attribute(c, "valuetype", param, - ¶m->valuetype) == false) { - dom_node_unref(c); - return false; - } - - if (param->valuetype == NULL) { - param->valuetype = talloc_strdup(param, "data"); - if (param->valuetype == NULL) { - dom_node_unref(c); - return false; - } - } - - param->next = params->params; - params->params = param; - } - - err = dom_node_get_next_sibling(c, &next); - if (err != DOM_NO_ERR) { - dom_node_unref(c); - return false; - } - - dom_node_unref(c); - c = next; - } - - box->object_params = params; - - /* start fetch (MIME type is ok or not specified) */ - box->flags |= IS_REPLACED; - if (!html_fetch_object(content, - params->data ? params->data : params->classid, - box, CONTENT_ANY, content->base.available_width, 1000, - false)) - return false; - - *convert_children = false; - return true; -} - - -/** - * Window subdivision [16.2.1]. - */ - -bool box_frameset(BOX_SPECIAL_PARAMS) -{ - bool ok; - - if (content->frameset) { - NSLOG(netsurf, INFO, "Error: multiple framesets in document."); - /* Don't convert children */ - if (convert_children) - *convert_children = false; - /* And ignore this spurious frameset */ - box->type = BOX_NONE; - return true; - } - - content->frameset = talloc_zero(content->bctx, struct content_html_frames); - if (!content->frameset) - return false; - - ok = box_create_frameset(content->frameset, n, content); - if (ok) - box->type = BOX_NONE; - - if (convert_children) - *convert_children = false; - return ok; -} - - -/** - * Destructor for content_html_frames, for frame elements - * - * \param f The frame params being destroyed. - * \return 0 to allow talloc to continue destroying the tree. - */ -static int box_frames_talloc_destructor(struct content_html_frames *f) -{ - if (f->url != NULL) { - nsurl_unref(f->url); - f->url = NULL; - } - - return 0; -} - - -/** - * Parse a multi-length-list, as defined by HTML 4.01. - * - * \param ds dom string to parse - * \param count updated to number of entries - * \return array of struct box_multi_length, or 0 on memory exhaustion - */ -static struct frame_dimension * -box_parse_multi_lengths(const dom_string *ds, unsigned int *count) -{ - char *end; - unsigned int i, n; - struct frame_dimension *length; - const char *s; - - s = dom_string_data(ds); - - for (i = 0, n = 1; s[i]; i++) - if (s[i] == ',') - n++; - - length = calloc(n, sizeof(struct frame_dimension)); - if (!length) - return NULL; - - for (i = 0; i != n; i++) { - while (ascii_is_space(*s)) { - s++; - } - length[i].value = strtof(s, &end); - if (length[i].value <= 0) { - length[i].value = 1; - } - s = end; - switch (*s) { - case '%': - length[i].unit = FRAME_DIMENSION_PERCENT; - break; - case '*': - length[i].unit = FRAME_DIMENSION_RELATIVE; - break; - default: - length[i].unit = FRAME_DIMENSION_PIXELS; - break; - } - while (*s && *s != ',') { - s++; - } - if (*s == ',') { - s++; - } - } - - *count = n; - return length; -} - - -bool box_create_frameset(struct content_html_frames *f, dom_node *n, - html_content *content) { - unsigned int row, col, index, i; - unsigned int rows = 1, cols = 1; - dom_string *s; - dom_exception err; - nsurl *url; - struct frame_dimension *row_height = 0, *col_width = 0; - dom_node *c, *next; - struct content_html_frames *frame; - bool default_border = true; - colour default_border_colour = 0x000000; - - /* parse rows and columns */ - err = dom_element_get_attribute(n, corestring_dom_rows, &s); - if (err == DOM_NO_ERR && s != NULL) { - row_height = box_parse_multi_lengths(s, &rows); - dom_string_unref(s); - if (row_height == NULL) - return false; - } else { - row_height = calloc(1, sizeof(struct frame_dimension)); - if (row_height == NULL) - return false; - row_height->value = 100; - row_height->unit = FRAME_DIMENSION_PERCENT; - } - - err = dom_element_get_attribute(n, corestring_dom_cols, &s); - if (err == DOM_NO_ERR && s != NULL) { - col_width = box_parse_multi_lengths(s, &cols); - dom_string_unref(s); - if (col_width == NULL) { - free(row_height); - return false; - } - } else { - col_width = calloc(1, sizeof(struct frame_dimension)); - if (col_width == NULL) { - free(row_height); - return false; - } - col_width->value = 100; - col_width->unit = FRAME_DIMENSION_PERCENT; - } - - /* common extension: border="0|1" to control all children */ - err = dom_element_get_attribute(n, corestring_dom_border, &s); - if (err == DOM_NO_ERR && s != NULL) { - if ((dom_string_data(s)[0] == '0') && - (dom_string_data(s)[1] == '\0')) - default_border = false; - dom_string_unref(s); - } - - /* common extension: frameborder="yes|no" to control all children */ - err = dom_element_get_attribute(n, corestring_dom_frameborder, &s); - if (err == DOM_NO_ERR && s != NULL) { - if (dom_string_caseless_lwc_isequal(s, - corestring_lwc_no) == 0) - default_border = false; - dom_string_unref(s); - } - - /* common extension: bordercolor="#RRGGBB|<named colour>" to control - *all children */ - err = dom_element_get_attribute(n, corestring_dom_bordercolor, &s); - if (err == DOM_NO_ERR && s != NULL) { - css_color color; - - if (nscss_parse_colour(dom_string_data(s), &color)) - default_border_colour = nscss_color_to_ns(color); - - dom_string_unref(s); - } - - /* update frameset and create default children */ - f->cols = cols; - f->rows = rows; - f->scrolling = BW_SCROLLING_NO; - f->children = talloc_array(content->bctx, struct content_html_frames, - (rows * cols)); - - talloc_set_destructor(f->children, box_frames_talloc_destructor); - - for (row = 0; row < rows; row++) { - for (col = 0; col < cols; col++) { - index = (row * cols) + col; - frame = &f->children[index]; - frame->cols = 0; - frame->rows = 0; - frame->width = col_width[col]; - frame->height = row_height[row]; - frame->margin_width = 0; - frame->margin_height = 0; - frame->name = NULL; - frame->url = NULL; - frame->no_resize = false; - frame->scrolling = BW_SCROLLING_AUTO; - frame->border = default_border; - frame->border_colour = default_border_colour; - frame->children = NULL; - } - } - free(col_width); - free(row_height); - - /* create the frameset windows */ - err = dom_node_get_first_child(n, &c); - if (err != DOM_NO_ERR) - return false; - - for (row = 0; c != NULL && row < rows; row++) { - for (col = 0; c != NULL && col < cols; col++) { - while (c != NULL) { - dom_node_type type; - dom_string *name; - - err = dom_node_get_node_type(c, &type); - if (err != DOM_NO_ERR) { - dom_node_unref(c); - return false; - } - - err = dom_node_get_node_name(c, &name); - if (err != DOM_NO_ERR) { - dom_node_unref(c); - return false; - } - - if (type != DOM_ELEMENT_NODE || - (!dom_string_caseless_lwc_isequal( - name, - corestring_lwc_frame) && - !dom_string_caseless_lwc_isequal( - name, - corestring_lwc_frameset - ))) { - err = dom_node_get_next_sibling(c, - &next); - if (err != DOM_NO_ERR) { - dom_string_unref(name); - dom_node_unref(c); - return false; - } - - dom_string_unref(name); - dom_node_unref(c); - c = next; - } else { - /* Got a FRAME or FRAMESET element */ - dom_string_unref(name); - break; - } - } - - if (c == NULL) - break; - - /* get current frame */ - index = (row * cols) + col; - frame = &f->children[index]; - - /* nest framesets */ - err = dom_node_get_node_name(c, &s); - if (err != DOM_NO_ERR) { - dom_node_unref(c); - return false; - } - - if (dom_string_caseless_lwc_isequal(s, - corestring_lwc_frameset)) { - dom_string_unref(s); - frame->border = 0; - if (box_create_frameset(frame, c, - content) == false) { - dom_node_unref(c); - return false; - } - - err = dom_node_get_next_sibling(c, &next); - if (err != DOM_NO_ERR) { - dom_node_unref(c); - return false; - } - - dom_node_unref(c); - c = next; - continue; - } - - dom_string_unref(s); - - /* get frame URL (not required) */ - url = NULL; - err = dom_element_get_attribute(c, corestring_dom_src, &s); - if (err == DOM_NO_ERR && s != NULL) { - box_extract_link(content, s, content->base_url, - &url); - dom_string_unref(s); - } - - /* copy url */ - if (url != NULL) { - /* no self-references */ - if (nsurl_compare(content->base_url, url, - NSURL_COMPLETE) == false) - frame->url = url; - url = NULL; - } - - /* fill in specified values */ - err = dom_element_get_attribute(c, corestring_dom_name, &s); - if (err == DOM_NO_ERR && s != NULL) { - frame->name = talloc_strdup(content->bctx, - dom_string_data(s)); - dom_string_unref(s); - } - - dom_element_has_attribute(c, corestring_dom_noresize, - &frame->no_resize); - - err = dom_element_get_attribute(c, corestring_dom_frameborder, - &s); - if (err == DOM_NO_ERR && s != NULL) { - i = atoi(dom_string_data(s)); - frame->border = (i != 0); - dom_string_unref(s); - } - - err = dom_element_get_attribute(c, corestring_dom_scrolling, &s); - if (err == DOM_NO_ERR && s != NULL) { - if (dom_string_caseless_lwc_isequal(s, - corestring_lwc_yes)) - frame->scrolling = BW_SCROLLING_YES; - else if (dom_string_caseless_lwc_isequal(s, - corestring_lwc_no)) - frame->scrolling = BW_SCROLLING_NO; - dom_string_unref(s); - } - - err = dom_element_get_attribute(c, corestring_dom_marginwidth, - &s); - if (err == DOM_NO_ERR && s != NULL) { - frame->margin_width = atoi(dom_string_data(s)); - dom_string_unref(s); - } - - err = dom_element_get_attribute(c, corestring_dom_marginheight, - &s); - if (err == DOM_NO_ERR && s != NULL) { - frame->margin_height = atoi(dom_string_data(s)); - dom_string_unref(s); - } - - err = dom_element_get_attribute(c, corestring_dom_bordercolor, - &s); - if (err == DOM_NO_ERR && s != NULL) { - css_color color; - - if (nscss_parse_colour(dom_string_data(s), - &color)) - frame->border_colour = - nscss_color_to_ns(color); - - dom_string_unref(s); - } - - /* advance */ - err = dom_node_get_next_sibling(c, &next); - if (err != DOM_NO_ERR) { - dom_node_unref(c); - return false; - } - - dom_node_unref(c); - c = next; - } - } - - /* If the last child wasn't a frame, we still need to unref it */ - if (c != NULL) { - dom_node_unref(c); - } - - return true; -} - - -/** - * Destructor for content_html_iframe, for <iframe> elements - * - * \param f The iframe params being destroyed. - * \return 0 to allow talloc to continue destroying the tree. - */ -static int box_iframes_talloc_destructor(struct content_html_iframe *f) -{ - if (f->url != NULL) { - nsurl_unref(f->url); - f->url = NULL; - } - - return 0; -} - - -/** - * Inline subwindow [16.5]. - */ - -bool box_iframe(BOX_SPECIAL_PARAMS) -{ - nsurl *url; - dom_string *s; - dom_exception err; - struct content_html_iframe *iframe; - int i; - - if (box->style && ns_computed_display(box->style, - box_is_root(n)) == CSS_DISPLAY_NONE) - return true; - - if (box->style && css_computed_visibility(box->style) == - CSS_VISIBILITY_HIDDEN) - /* Don't create iframe discriptors for invisible iframes - * TODO: handle hidden iframes at browser_window generation - * time instead? */ - return true; - - /* get frame URL */ - err = dom_element_get_attribute(n, corestring_dom_src, &s); - if (err != DOM_NO_ERR || s == NULL) - return true; - if (box_extract_link(content, s, content->base_url, &url) == false) { - dom_string_unref(s); - return false; - } - dom_string_unref(s); - if (url == NULL) - return true; - - /* don't include ourself */ - if (nsurl_compare(content->base_url, url, NSURL_COMPLETE)) { - nsurl_unref(url); - return true; - } - - /* create a new iframe */ - iframe = talloc(content->bctx, struct content_html_iframe); - if (iframe == NULL) { - nsurl_unref(url); - return false; - } - - talloc_set_destructor(iframe, box_iframes_talloc_destructor); - - iframe->box = box; - iframe->margin_width = 0; - iframe->margin_height = 0; - iframe->name = NULL; - iframe->url = url; - iframe->scrolling = BW_SCROLLING_AUTO; - iframe->border = true; - - /* Add this iframe to the linked list of iframes */ - iframe->next = content->iframe; - content->iframe = iframe; - - /* fill in specified values */ - err = dom_element_get_attribute(n, corestring_dom_name, &s); - if (err == DOM_NO_ERR && s != NULL) { - iframe->name = talloc_strdup(content->bctx, dom_string_data(s)); - dom_string_unref(s); - } - - err = dom_element_get_attribute(n, corestring_dom_frameborder, &s); - if (err == DOM_NO_ERR && s != NULL) { - i = atoi(dom_string_data(s)); - iframe->border = (i != 0); - dom_string_unref(s); - } - - err = dom_element_get_attribute(n, corestring_dom_bordercolor, &s); - if (err == DOM_NO_ERR && s != NULL) { - css_color color; - - if (nscss_parse_colour(dom_string_data(s), &color)) - iframe->border_colour = nscss_color_to_ns(color); - - dom_string_unref(s); - } - - err = dom_element_get_attribute(n, corestring_dom_scrolling, &s); - if (err == DOM_NO_ERR && s != NULL) { - if (dom_string_caseless_lwc_isequal(s, - corestring_lwc_yes)) - iframe->scrolling = BW_SCROLLING_YES; - else if (dom_string_caseless_lwc_isequal(s, - corestring_lwc_no)) - iframe->scrolling = BW_SCROLLING_NO; - dom_string_unref(s); - } - - err = dom_element_get_attribute(n, corestring_dom_marginwidth, &s); - if (err == DOM_NO_ERR && s != NULL) { - iframe->margin_width = atoi(dom_string_data(s)); - dom_string_unref(s); - } - - err = dom_element_get_attribute(n, corestring_dom_marginheight, &s); - if (err == DOM_NO_ERR && s != NULL) { - iframe->margin_height = atoi(dom_string_data(s)); - dom_string_unref(s); - } - - /* box */ - assert(box->style); - box->flags |= IFRAME; - box->flags |= IS_REPLACED; - - /* Showing iframe, so don't show alternate content */ - if (convert_children) - *convert_children = false; - return true; -} - - -/** - * Helper function for adding textarea widget to box. - * - * This is a load of hacks to ensure boxes replaced with textareas - * can be handled by the layout code. - */ - -static bool box_input_text(html_content *html, struct box *box, - struct dom_node *node) -{ - struct box *inline_container, *inline_box; - - box->type = BOX_INLINE_BLOCK; - - inline_container = box_create(NULL, 0, false, 0, 0, 0, 0, html->bctx); - if (!inline_container) - return false; - inline_container->type = BOX_INLINE_CONTAINER; - inline_box = box_create(NULL, box->style, false, 0, 0, box->title, 0, - html->bctx); - if (!inline_box) - return false; - inline_box->type = BOX_TEXT; - inline_box->text = talloc_strdup(html->bctx, ""); - - box_add_child(inline_container, inline_box); - box_add_child(box, inline_container); - - return box_textarea_create_textarea(html, box, node); -} - - -/** - * Form control [17.4]. - */ - -bool box_input(BOX_SPECIAL_PARAMS) -{ - struct form_control *gadget = NULL; - dom_string *type = NULL; - dom_exception err; - nsurl *url; - nserror error; - - dom_element_get_attribute(n, corestring_dom_type, &type); - - gadget = html_forms_get_control_for_node(content->forms, n); - if (gadget == NULL) - goto no_memory; - box->gadget = gadget; - box->flags |= IS_REPLACED; - gadget->box = box; - gadget->html = content; - - if (type && dom_string_caseless_lwc_isequal(type, - corestring_lwc_password)) { - if (box_input_text(content, box, n) == false) - goto no_memory; - - } else if (type && dom_string_caseless_lwc_isequal(type, - corestring_lwc_file)) { - box->type = BOX_INLINE_BLOCK; - - } else if (type && dom_string_caseless_lwc_isequal(type, - corestring_lwc_hidden)) { - /* no box for hidden inputs */ - box->type = BOX_NONE; - - } else if (type && - (dom_string_caseless_lwc_isequal(type, - corestring_lwc_checkbox) || - dom_string_caseless_lwc_isequal(type, - corestring_lwc_radio))) { - - } else if (type && - (dom_string_caseless_lwc_isequal(type, - corestring_lwc_submit) || - dom_string_caseless_lwc_isequal(type, - corestring_lwc_reset) || - dom_string_caseless_lwc_isequal(type, - corestring_lwc_button))) { - struct box *inline_container, *inline_box; - - if (box_button(n, content, box, 0) == false) - goto no_memory; - - inline_container = box_create(NULL, 0, false, 0, 0, 0, 0, - content->bctx); - if (inline_container == NULL) - goto no_memory; - - inline_container->type = BOX_INLINE_CONTAINER; - - inline_box = box_create(NULL, box->style, false, 0, 0, - box->title, 0, content->bctx); - if (inline_box == NULL) - goto no_memory; - - inline_box->type = BOX_TEXT; - - if (box->gadget->value != NULL) - inline_box->text = talloc_strdup(content->bctx, - box->gadget->value); - else if (box->gadget->type == GADGET_SUBMIT) - inline_box->text = talloc_strdup(content->bctx, - messages_get("Form_Submit")); - else if (box->gadget->type == GADGET_RESET) - inline_box->text = talloc_strdup(content->bctx, - messages_get("Form_Reset")); - else - inline_box->text = talloc_strdup(content->bctx, - "Button"); - - if (inline_box->text == NULL) - goto no_memory; - - inline_box->length = strlen(inline_box->text); - - box_add_child(inline_container, inline_box); - - box_add_child(box, inline_container); - - } else if (type && dom_string_caseless_lwc_isequal(type, - corestring_lwc_image)) { - gadget->type = GADGET_IMAGE; - - if (box->style && ns_computed_display(box->style, - box_is_root(n)) != CSS_DISPLAY_NONE && - nsoption_bool(foreground_images) == true) { - dom_string *s; - - err = dom_element_get_attribute(n, corestring_dom_src, &s); - if (err == DOM_NO_ERR && s != NULL) { - error = nsurl_join(content->base_url, - dom_string_data(s), &url); - dom_string_unref(s); - if (error != NSERROR_OK) - goto no_memory; - - /* if url is equivalent to the parent's url, - * we've got infinite inclusion. stop it here - */ - if (nsurl_compare(url, content->base_url, - NSURL_COMPLETE) == false) { - if (!html_fetch_object(content, url, - box, image_types, - content->base. - available_width, - 1000, false)) { - nsurl_unref(url); - goto no_memory; - } - } - nsurl_unref(url); - } - } - } else { - /* the default type is "text" */ - if (box_input_text(content, box, n) == false) - goto no_memory; - } - - if (type) - dom_string_unref(type); - - *convert_children = false; - return true; - -no_memory: - if (type) - dom_string_unref(type); - - return false; -} - - -/** - * Push button [17.5]. - */ - -bool box_button(BOX_SPECIAL_PARAMS) -{ - struct form_control *gadget; - - gadget = html_forms_get_control_for_node(content->forms, n); - if (!gadget) - return false; - - gadget->html = content; - box->gadget = gadget; - box->flags |= IS_REPLACED; - gadget->box = box; - - box->type = BOX_INLINE_BLOCK; - - /* Just render the contents */ - - return true; -} - - -/** - * Option selector [17.6]. - */ - -bool box_select(BOX_SPECIAL_PARAMS) -{ - struct box *inline_container; - struct box *inline_box; - struct form_control *gadget; - dom_node *c, *c2; - dom_node *next, *next2; - dom_exception err; - - gadget = html_forms_get_control_for_node(content->forms, n); - if (gadget == NULL) - return false; - - gadget->html = content; - err = dom_node_get_first_child(n, &c); - if (err != DOM_NO_ERR) { - form_free_control(gadget); - return false; - } - - while (c != NULL) { - dom_string *name; - - err = dom_node_get_node_name(c, &name); - if (err != DOM_NO_ERR) { - dom_node_unref(c); - form_free_control(gadget); - return false; - } - - if (dom_string_caseless_lwc_isequal(name, - corestring_lwc_option)) { - dom_string_unref(name); - - if (box_select_add_option(gadget, c) == false) { - dom_node_unref(c); - form_free_control(gadget); - return false; - } - } else if (dom_string_caseless_lwc_isequal(name, - corestring_lwc_optgroup)) { - dom_string_unref(name); - - err = dom_node_get_first_child(c, &c2); - if (err != DOM_NO_ERR) { - dom_node_unref(c); - form_free_control(gadget); - return false; - } - - while (c2 != NULL) { - dom_string *c2_name; - - err = dom_node_get_node_name(c2, &c2_name); - if (err != DOM_NO_ERR) { - dom_node_unref(c2); - dom_node_unref(c); - form_free_control(gadget); - return false; - } - - if (dom_string_caseless_lwc_isequal(c2_name, - corestring_lwc_option)) { - dom_string_unref(c2_name); - - if (box_select_add_option(gadget, - c2) == false) { - dom_node_unref(c2); - dom_node_unref(c); - return false; - } - } else { - dom_string_unref(c2_name); - } - - err = dom_node_get_next_sibling(c2, &next2); - if (err != DOM_NO_ERR) { - dom_node_unref(c2); - dom_node_unref(c); - return false; - } - - dom_node_unref(c2); - c2 = next2; - } - } else { - dom_string_unref(name); - } - - err = dom_node_get_next_sibling(c, &next); - if (err != DOM_NO_ERR) { - dom_node_unref(c); - form_free_control(gadget); - return false; - } - - dom_node_unref(c); - c = next; - } - - if (gadget->data.select.num_items == 0) { - /* no options: ignore entire select */ - form_free_control(gadget); - return true; - } - - box->type = BOX_INLINE_BLOCK; - box->gadget = gadget; - box->flags |= IS_REPLACED; - gadget->box = box; - - inline_container = box_create(NULL, 0, false, 0, 0, 0, 0, content->bctx); - if (inline_container == NULL) - goto no_memory; - inline_container->type = BOX_INLINE_CONTAINER; - inline_box = box_create(NULL, box->style, false, 0, 0, box->title, 0, - content->bctx); - if (inline_box == NULL) - goto no_memory; - inline_box->type = BOX_TEXT; - box_add_child(inline_container, inline_box); - box_add_child(box, inline_container); - - if (gadget->data.select.multiple == false && - gadget->data.select.num_selected == 0) { - gadget->data.select.current = gadget->data.select.items; - gadget->data.select.current->initial_selected = - gadget->data.select.current->selected = true; - gadget->data.select.num_selected = 1; - dom_html_option_element_set_selected( - gadget->data.select.current->node, true); - } - - if (gadget->data.select.num_selected == 0) - inline_box->text = talloc_strdup(content->bctx, - messages_get("Form_None")); - else if (gadget->data.select.num_selected == 1) - inline_box->text = talloc_strdup(content->bctx, - gadget->data.select.current->text); - else - inline_box->text = talloc_strdup(content->bctx, - messages_get("Form_Many")); - if (inline_box->text == NULL) - goto no_memory; - - inline_box->length = strlen(inline_box->text); - - *convert_children = false; - return true; - -no_memory: - return false; -} - - -/** - * Add an option to a form select control (helper function for box_select()). - * - * \param control select containing the <option> - * \param n xml element node for <option> - * \return true on success, false on memory exhaustion - */ - -bool box_select_add_option(struct form_control *control, dom_node *n) -{ - char *value = NULL; - char *text = NULL; - char *text_nowrap = NULL; - bool selected; - dom_string *content, *s; - dom_exception err; - - err = dom_node_get_text_content(n, &content); - if (err != DOM_NO_ERR) - return false; - - if (content != NULL) { - text = squash_whitespace(dom_string_data(content)); - dom_string_unref(content); - } else { - text = strdup(""); - } - - if (text == NULL) - goto no_memory; - - err = dom_element_get_attribute(n, corestring_dom_value, &s); - if (err == DOM_NO_ERR && s != NULL) { - value = strdup(dom_string_data(s)); - dom_string_unref(s); - } else { - value = strdup(text); - } - - if (value == NULL) - goto no_memory; - - dom_element_has_attribute(n, corestring_dom_selected, &selected); - - /* replace spaces/TABs with hard spaces to prevent line wrapping */ - text_nowrap = cnv_space2nbsp(text); - if (text_nowrap == NULL) - goto no_memory; - - if (form_add_option(control, value, text_nowrap, selected, n) == false) - goto no_memory; - - free(text); - - return true; - -no_memory: - free(value); - free(text); - free(text_nowrap); - return false; -} - - -/** - * Multi-line text field [17.7]. - */ - -bool box_textarea(BOX_SPECIAL_PARAMS) -{ - /* Get the form_control for the DOM node */ - box->gadget = html_forms_get_control_for_node(content->forms, n); - if (box->gadget == NULL) - return false; - - box->flags |= IS_REPLACED; - box->gadget->html = content; - box->gadget->box = box; - - if (!box_input_text(content, box, n)) - return false; - - *convert_children = false; - return true; -} - - -/** - * Embedded object (not in any HTML specification: - * see http://wp.netscape.com/assist/net_sites/new_html3_prop.html ) - */ - -bool box_embed(BOX_SPECIAL_PARAMS) -{ - struct object_params *params; - struct object_param *param; - dom_namednodemap *attrs; - unsigned long idx; - uint32_t num_attrs; - dom_string *src; - dom_exception err; - - if (box->style && ns_computed_display(box->style, - box_is_root(n)) == CSS_DISPLAY_NONE) - return true; - - params = talloc(content->bctx, struct object_params); - if (params == NULL) - return false; - - talloc_set_destructor(params, box_object_talloc_destructor); - - params->data = NULL; - params->type = NULL; - params->codetype = NULL; - params->codebase = NULL; - params->classid = NULL; - params->params = NULL; - - /* src is a URL */ - err = dom_element_get_attribute(n, corestring_dom_src, &src); - if (err != DOM_NO_ERR || src == NULL) - return true; - if (box_extract_link(content, src, content->base_url, - ¶ms->data) == false) { - dom_string_unref(src); - return false; - } - - dom_string_unref(src); - - if (params->data == NULL) - return true; - - /* Don't include ourself */ - if (nsurl_compare(content->base_url, params->data, NSURL_COMPLETE)) - return true; - - /* add attributes as parameters to linked list */ - err = dom_node_get_attributes(n, &attrs); - if (err != DOM_NO_ERR) - return false; - - err = dom_namednodemap_get_length(attrs, &num_attrs); - if (err != DOM_NO_ERR) { - dom_namednodemap_unref(attrs); - return false; - } - - for (idx = 0; idx < num_attrs; idx++) { - dom_attr *attr; - dom_string *name, *value; - - err = dom_namednodemap_item(attrs, idx, (void *) &attr); - if (err != DOM_NO_ERR) { - dom_namednodemap_unref(attrs); - return false; - } - - err = dom_attr_get_name(attr, &name); - if (err != DOM_NO_ERR) { - dom_namednodemap_unref(attrs); - return false; - } - - if (dom_string_caseless_lwc_isequal(name, corestring_lwc_src)) { - dom_string_unref(name); - continue; - } - - err = dom_attr_get_value(attr, &value); - if (err != DOM_NO_ERR) { - dom_string_unref(name); - dom_namednodemap_unref(attrs); - return false; - } - - param = talloc(content->bctx, struct object_param); - if (param == NULL) { - dom_string_unref(value); - dom_string_unref(name); - dom_namednodemap_unref(attrs); - return false; - } - - param->name = talloc_strdup(content->bctx, dom_string_data(name)); - param->value = talloc_strdup(content->bctx, dom_string_data(value)); - param->type = NULL; - param->valuetype = talloc_strdup(content->bctx, "data"); - param->next = NULL; - - dom_string_unref(value); - dom_string_unref(name); - - if (param->name == NULL || param->value == NULL || - param->valuetype == NULL) { - dom_namednodemap_unref(attrs); - return false; - } - - param->next = params->params; - params->params = param; - } - - dom_namednodemap_unref(attrs); - - box->object_params = params; - - /* start fetch */ - box->flags |= IS_REPLACED; - return html_fetch_object(content, params->data, box, CONTENT_ANY, - content->base.available_width, 1000, false); -} - -/** - * \} - */ - - -/** - * Get the value of an XML element's attribute. - * - * \param n xmlNode, of type XML_ELEMENT_NODE - * \param attribute name of attribute - * \param context talloc context for result buffer - * \param value updated to value, if the attribute is present - * \return true on success, false if attribute present but memory exhausted - * - * Note that returning true does not imply that the attribute was found. If the - * attribute was not found, *value will be unchanged. - */ - -bool box_get_attribute(dom_node *n, const char *attribute, - void *context, char **value) -{ - char *result; - dom_string *attr, *attr_name; - dom_exception err; - - err = dom_string_create_interned((const uint8_t *) attribute, - strlen(attribute), &attr_name); - if (err != DOM_NO_ERR) - return false; - - err = dom_element_get_attribute(n, attr_name, &attr); - if (err != DOM_NO_ERR) { - dom_string_unref(attr_name); - return false; - } - - dom_string_unref(attr_name); - - if (attr != NULL) { - result = talloc_strdup(context, dom_string_data(attr)); - - dom_string_unref(attr); - - if (result == NULL) - return false; - - *value = result; - } - - return true; -} - - -/* exported function documented in render/box.h */ -bool -box_extract_link(const html_content *content, - const dom_string *dsrel, - nsurl *base, - nsurl **result) -{ - char *s, *s1, *apos0 = 0, *apos1 = 0, *quot0 = 0, *quot1 = 0; - unsigned int i, j, end; - nserror error; - const char *rel; - - rel = dom_string_data(dsrel); - - s1 = s = malloc(3 * strlen(rel) + 1); - if (!s) - return false; - - /* copy to s, removing white space and control characters */ - for (i = 0; rel[i] && ascii_is_space(rel[i]); i++) - ; - for (end = strlen(rel); - (end != i) && ascii_is_space(rel[end - 1]); - end--) - ; - for (j = 0; i != end; i++) { - if ((unsigned char) rel[i] < 0x20) { - ; /* skip control characters */ - } else if (rel[i] == ' ') { - s[j++] = '%'; - s[j++] = '2'; - s[j++] = '0'; - } else { - s[j++] = rel[i]; - } - } - s[j] = 0; - - if (content->enable_scripting == false) { - /* extract first quoted string out of "javascript:" link */ - if (strncmp(s, "javascript:", 11) == 0) { - apos0 = strchr(s, '\''); - if (apos0) - apos1 = strchr(apos0 + 1, '\''); - quot0 = strchr(s, '"'); - if (quot0) - quot1 = strchr(quot0 + 1, '"'); - if (apos0 && apos1 && - (!quot0 || !quot1 || apos0 < quot0)) { - *apos1 = 0; - s1 = apos0 + 1; - } else if (quot0 && quot1) { - *quot1 = 0; - s1 = quot0 + 1; - } - } - } - - /* construct absolute URL */ - error = nsurl_join(base, s1, result); - free(s); - if (error != NSERROR_OK) { - *result = NULL; - return false; - } - - return true; -} - - - diff --git a/render/box_normalise.c b/render/box_normalise.c deleted file mode 100644 index 8da245754..000000000 --- a/render/box_normalise.c +++ /dev/null @@ -1,1046 +0,0 @@ -/* - * Copyright 2005 James Bursa <bursa@users.sourceforge.net> - * Copyright 2003 Phil Mellor <monkeyson@users.sourceforge.net> - * Copyright 2005 John M Bell <jmb202@ecs.soton.ac.uk> - * Copyright 2004 Kevin Bagust <kevin.bagust@ntlworld.com> - * - * 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 <http://www.gnu.org/licenses/>. - */ - -/** \file - * Box tree normalisation (implementation). - */ - -#include <assert.h> -#include <stdbool.h> -#include <string.h> - -#include "utils/log.h" -#include "utils/errors.h" -#include "css/select.h" - -#include "render/box.h" -#include "render/html_internal.h" -#include "render/table.h" - -/* Define to enable box normalise debug */ -#undef BOX_NORMALISE_DEBUG - -/** - * Row spanning information for a cell - */ -struct span_info { - /** Number of rows this cell spans */ - unsigned int row_span; - /** Row group of cell */ - struct box *rg; - /** The cell in this column spans all rows until the end of the table */ - bool auto_row; -}; - -/** - * Column record for a table - */ -struct columns { - /** Current column index */ - unsigned int current_column; - /** Number of columns in main part of table 1..max columns */ - unsigned int num_columns; - /** Information about columns in main table, array [0, num_columns) */ - struct span_info *spans; - /** Number of rows in table */ - unsigned int num_rows; -}; - - -static bool box_normalise_table( - struct box *table, - const struct box *root, - html_content *c); -static bool box_normalise_table_spans( - struct box *table, - const struct box *root, - struct span_info *spans, - html_content *c); -static bool box_normalise_table_row_group( - struct box *row_group, - const struct box *root, - struct columns *col_info, - html_content *c); -static bool box_normalise_table_row( - struct box *row, - const struct box *root, - struct columns *col_info, - html_content *c); -static bool calculate_table_row(struct columns *col_info, - unsigned int col_span, unsigned int row_span, - unsigned int *start_column, struct box *cell); -static bool box_normalise_inline_container( - struct box *cont, - const struct box *root, - html_content *c); - -/** - * Ensure the box tree is correctly nested by adding and removing nodes. - * - * \param block box of type BLOCK, INLINE_BLOCK, or TABLE_CELL - * \param root root box of document - * \param c content of boxes - * \return true on success, false on memory exhaustion - * - * The tree is modified to satisfy the following: - * \code - * parent permitted child nodes - * BLOCK, INLINE_BLOCK BLOCK, INLINE_CONTAINER, TABLE - * INLINE_CONTAINER INLINE, INLINE_BLOCK, FLOAT_LEFT, FLOAT_RIGHT, BR, TEXT - * INLINE, TEXT none - * TABLE at least 1 TABLE_ROW_GROUP - * TABLE_ROW_GROUP at least 1 TABLE_ROW - * TABLE_ROW at least 1 TABLE_CELL - * TABLE_CELL BLOCK, INLINE_CONTAINER, TABLE (same as BLOCK) - * FLOAT_(LEFT|RIGHT) exactly 1 BLOCK or TABLE - * \endcode - */ - -bool box_normalise_block( - struct box *block, - const struct box *root, - html_content *c) -{ - struct box *child; - struct box *next_child; - struct box *table; - css_computed_style *style; - nscss_select_ctx ctx; - - assert(block != NULL); - assert(root != NULL); - - ctx.root_style = root->style; - -#ifdef BOX_NORMALISE_DEBUG - NSLOG(netsurf, INFO, "block %p, block->type %u", block, block->type); -#endif - - assert(block->type == BOX_BLOCK || block->type == BOX_INLINE_BLOCK || - block->type == BOX_TABLE_CELL); - - for (child = block->children; child != NULL; child = next_child) { -#ifdef BOX_NORMALISE_DEBUG - NSLOG(netsurf, INFO, "child %p, child->type = %d", child, - child->type); -#endif - - next_child = child->next; /* child may be destroyed */ - - switch (child->type) { - case BOX_BLOCK: - /* ok */ - if (box_normalise_block(child, root, c) == false) - return false; - break; - case BOX_INLINE_CONTAINER: - if (box_normalise_inline_container(child, root, c) == false) - return false; - break; - case BOX_TABLE: - if (box_normalise_table(child, root, c) == false) - return false; - break; - case BOX_INLINE: - case BOX_INLINE_END: - case BOX_INLINE_BLOCK: - case BOX_FLOAT_LEFT: - case BOX_FLOAT_RIGHT: - case BOX_BR: - case BOX_TEXT: - /* should have been wrapped in inline - container by convert_xml_to_box() */ - assert(0); - break; - case BOX_TABLE_ROW_GROUP: - case BOX_TABLE_ROW: - case BOX_TABLE_CELL: - /* insert implied table */ - assert(block->style != NULL); - - ctx.ctx = c->select_ctx; - ctx.quirks = (c->quirks == DOM_DOCUMENT_QUIRKS_MODE_FULL); - ctx.base_url = c->base_url; - ctx.universal = c->universal; - - style = nscss_get_blank_style(&ctx, block->style); - if (style == NULL) - return false; - - table = box_create(NULL, style, true, block->href, - block->target, NULL, NULL, c->bctx); - if (table == NULL) { - css_computed_style_destroy(style); - return false; - } - table->type = BOX_TABLE; - - if (child->prev == NULL) - block->children = table; - else - child->prev->next = table; - - table->prev = child->prev; - - while (child != NULL && ( - child->type == BOX_TABLE_ROW_GROUP || - child->type == BOX_TABLE_ROW || - child->type == BOX_TABLE_CELL)) { - box_add_child(table, child); - - next_child = child->next; - child->next = NULL; - child = next_child; - } - - table->last->next = NULL; - table->next = next_child = child; - if (table->next != NULL) - table->next->prev = table; - else - block->last = table; - table->parent = block; - - if (box_normalise_table(table, root, c) == false) - return false; - break; - default: - assert(0); - } - } - - return true; -} - - -bool box_normalise_table( - struct box *table, - const struct box *root, - html_content * c) -{ - struct box *child; - struct box *next_child; - struct box *row_group; - css_computed_style *style; - struct columns col_info; - nscss_select_ctx ctx; - - assert(table != NULL); - assert(table->type == BOX_TABLE); - - ctx.root_style = root->style; - -#ifdef BOX_NORMALISE_DEBUG - NSLOG(netsurf, INFO, "table %p", table); -#endif - - col_info.num_columns = 1; - col_info.current_column = 0; - col_info.spans = malloc(2 * sizeof *col_info.spans); - if (col_info.spans == NULL) - return false; - - col_info.spans[0].row_span = col_info.spans[1].row_span = 0; - col_info.spans[0].auto_row = false; - col_info.spans[1].auto_row = false; - col_info.num_rows = 0; - - for (child = table->children; child != NULL; child = next_child) { - next_child = child->next; - switch (child->type) { - case BOX_TABLE_ROW_GROUP: - /* ok */ - if (box_normalise_table_row_group(child, root, - &col_info, c) == false) { - free(col_info.spans); - return false; - } - break; - case BOX_BLOCK: - case BOX_INLINE_CONTAINER: - case BOX_TABLE: - case BOX_TABLE_ROW: - case BOX_TABLE_CELL: - /* insert implied table row group */ - assert(table->style != NULL); - - ctx.ctx = c->select_ctx; - ctx.quirks = (c->quirks == DOM_DOCUMENT_QUIRKS_MODE_FULL); - ctx.base_url = c->base_url; - ctx.universal = c->universal; - - style = nscss_get_blank_style(&ctx, table->style); - if (style == NULL) { - free(col_info.spans); - return false; - } - - row_group = box_create(NULL, style, true, table->href, - table->target, NULL, NULL, c->bctx); - if (row_group == NULL) { - css_computed_style_destroy(style); - free(col_info.spans); - return false; - } - - row_group->type = BOX_TABLE_ROW_GROUP; - - if (child->prev == NULL) - table->children = row_group; - else - child->prev->next = row_group; - - row_group->prev = child->prev; - - while (child != NULL && ( - child->type == BOX_BLOCK || - child->type == BOX_INLINE_CONTAINER || - child->type == BOX_TABLE || - child->type == BOX_TABLE_ROW || - child->type == BOX_TABLE_CELL)) { - box_add_child(row_group, child); - - next_child = child->next; - child->next = NULL; - child = next_child; - } - - assert(row_group->last != NULL); - - row_group->last->next = NULL; - row_group->next = next_child = child; - if (row_group->next != NULL) - row_group->next->prev = row_group; - else - table->last = row_group; - row_group->parent = table; - - if (box_normalise_table_row_group(row_group, root, - &col_info, c) == false) { - free(col_info.spans); - return false; - } - break; - case BOX_INLINE: - case BOX_INLINE_END: - case BOX_INLINE_BLOCK: - case BOX_FLOAT_LEFT: - case BOX_FLOAT_RIGHT: - case BOX_BR: - case BOX_TEXT: - /* should have been wrapped in inline - container by convert_xml_to_box() */ - assert(0); - break; - default: - fprintf(stderr, "%i\n", child->type); - assert(0); - } - } - - table->columns = col_info.num_columns; - table->rows = col_info.num_rows; - - if (table->children == NULL) { - struct box *row; - -#ifdef BOX_NORMALISE_DEBUG - NSLOG(netsurf, INFO, - "table->children == 0, creating implied row"); -#endif - - assert(table->style != NULL); - - ctx.ctx = c->select_ctx; - ctx.quirks = (c->quirks == DOM_DOCUMENT_QUIRKS_MODE_FULL); - ctx.base_url = c->base_url; - ctx.universal = c->universal; - - style = nscss_get_blank_style(&ctx, table->style); - if (style == NULL) { - free(col_info.spans); - return false; - } - - row_group = box_create(NULL, style, true, table->href, - table->target, NULL, NULL, c->bctx); - if (row_group == NULL) { - css_computed_style_destroy(style); - free(col_info.spans); - return false; - } - row_group->type = BOX_TABLE_ROW_GROUP; - - style = nscss_get_blank_style(&ctx, row_group->style); - if (style == NULL) { - box_free(row_group); - free(col_info.spans); - return false; - } - - row = box_create(NULL, style, true, row_group->href, - row_group->target, NULL, NULL, c->bctx); - if (row == NULL) { - css_computed_style_destroy(style); - box_free(row_group); - free(col_info.spans); - return false; - } - row->type = BOX_TABLE_ROW; - - row->parent = row_group; - row_group->children = row_group->last = row; - - row_group->parent = table; - table->children = table->last = row_group; - - table->rows = 1; - } - - if (box_normalise_table_spans(table, root, col_info.spans, c) == false) { - free(col_info.spans); - return false; - } - - free(col_info.spans); - -#ifdef BOX_NORMALISE_DEBUG - NSLOG(netsurf, INFO, "table %p done", table); -#endif - - return true; -} - - -/** - * Normalise table cell column/row counts for colspan/rowspan = 0. - * Additionally, generate empty cells. - * - * \param table Table to process - * \param root root box of document - * \param spans Array of length table->columns for use in empty cell detection - * \param c Content containing table - * \return True on success, false on memory exhaustion. - */ - -bool box_normalise_table_spans( - struct box *table, - const struct box *root, - struct span_info *spans, - html_content *c) -{ - struct box *table_row_group; - struct box *table_row; - struct box *table_cell; - unsigned int rows_left = table->rows; - unsigned int group_rows_left; - unsigned int col; - nscss_select_ctx ctx; - - ctx.root_style = root->style; - - /* Clear span data */ - memset(spans, 0, table->columns * sizeof(struct span_info)); - - /* Scan table, filling in width and height of table cells with - * colspan = 0 and rowspan = 0. Also generate empty cells */ - for (table_row_group = table->children; - table_row_group != NULL; - table_row_group = table_row_group->next) { - - group_rows_left = table_row_group->rows; - - for (table_row = table_row_group->children; - table_row != NULL; - table_row = table_row->next) { - - for (table_cell = table_row->children; - table_cell != NULL; - table_cell = table_cell->next) { - - /* colspan = 0 -> colspan = 1 */ - if (table_cell->columns == 0) { - table_cell->columns = 1; - } - - /* if rowspan is 0 it is expanded to - * the number of rows left in the row - * group - */ - if (table_cell->rows == 0) { - table_cell->rows = group_rows_left; - } - - /* limit rowspans within group */ - if (table_cell->rows > group_rows_left) { - table_cell->rows = group_rows_left; - } - - /* Record span information */ - for (col = table_cell->start_column; - col < table_cell->start_column + - table_cell->columns; col++) { - spans[col].row_span = table_cell->rows; - } - } - - /* Reduce span count of each column */ - for (col = 0; col < table->columns; col++) { - if (spans[col].row_span == 0) { - unsigned int start = col; - css_computed_style *style; - struct box *cell, *prev; - - /* If it's already zero, then we need - * to generate an empty cell for the - * gap in the row that spans as many - * columns as remain blank. - */ - assert(table_row->style != NULL); - - /* Find width of gap */ - while (col < table->columns && - spans[col].row_span == - 0) { - col++; - } - - ctx.ctx = c->select_ctx; - ctx.quirks = (c->quirks == - DOM_DOCUMENT_QUIRKS_MODE_FULL); - ctx.base_url = c->base_url; - ctx.universal = c->universal; - - style = nscss_get_blank_style(&ctx, - table_row->style); - if (style == NULL) - return false; - - cell = box_create(NULL, style, true, - table_row->href, - table_row->target, - NULL, NULL, c->bctx); - if (cell == NULL) { - css_computed_style_destroy( - style); - return false; - } - cell->type = BOX_TABLE_CELL; - - cell->rows = 1; - cell->columns = col - start; - cell->start_column = start; - - /* Find place to insert cell */ - for (prev = table_row->children; - prev != NULL; - prev = prev->next) { - if (prev->start_column + - prev->columns == - start) - break; - if (prev->next == NULL) - break; - } - - /* Insert it */ - if (prev == NULL) { - if (table_row->children != NULL) - table_row->children-> - prev = cell; - else - table_row->last = cell; - - cell->next = - table_row->children; - table_row->children = cell; - } else { - if (prev->next != NULL) - prev->next->prev = cell; - else - table_row->last = cell; - - cell->next = prev->next; - prev->next = cell; - cell->prev = prev; - } - cell->parent = table_row; - } else { - spans[col].row_span--; - } - } - - assert(rows_left > 0); - - rows_left--; - } - - group_rows_left--; - } - - return true; -} - - -bool box_normalise_table_row_group( - struct box *row_group, - const struct box *root, - struct columns *col_info, - html_content * c) -{ - struct box *child; - struct box *next_child; - struct box *row; - css_computed_style *style; - nscss_select_ctx ctx; - unsigned int group_row_count = 0; - - assert(row_group != 0); - assert(row_group->type == BOX_TABLE_ROW_GROUP); - - ctx.root_style = root->style; - -#ifdef BOX_NORMALISE_DEBUG - NSLOG(netsurf, INFO, "row_group %p", row_group); -#endif - - for (child = row_group->children; child != NULL; child = next_child) { - next_child = child->next; - - switch (child->type) { - case BOX_TABLE_ROW: - /* ok */ - group_row_count++; - if (box_normalise_table_row(child, root, col_info, - c) == false) - return false; - break; - case BOX_BLOCK: - case BOX_INLINE_CONTAINER: - case BOX_TABLE: - case BOX_TABLE_ROW_GROUP: - case BOX_TABLE_CELL: - /* insert implied table row */ - assert(row_group->style != NULL); - - ctx.ctx = c->select_ctx; - ctx.quirks = (c->quirks == DOM_DOCUMENT_QUIRKS_MODE_FULL); - ctx.base_url = c->base_url; - ctx.universal = c->universal; - - style = nscss_get_blank_style(&ctx, row_group->style); - if (style == NULL) - return false; - - row = box_create(NULL, style, true, row_group->href, - row_group->target, NULL, NULL, c->bctx); - if (row == NULL) { - css_computed_style_destroy(style); - return false; - } - row->type = BOX_TABLE_ROW; - - if (child->prev == NULL) - row_group->children = row; - else - child->prev->next = row; - - row->prev = child->prev; - - while (child != NULL && ( - child->type == BOX_BLOCK || - child->type == BOX_INLINE_CONTAINER || - child->type == BOX_TABLE || - child->type == BOX_TABLE_ROW_GROUP || - child->type == BOX_TABLE_CELL)) { - box_add_child(row, child); - - next_child = child->next; - child->next = NULL; - child = next_child; - } - - assert(row->last != NULL); - - row->last->next = NULL; - row->next = next_child = child; - if (row->next != NULL) - row->next->prev = row; - else - row_group->last = row; - row->parent = row_group; - - group_row_count++; - if (box_normalise_table_row(row, root, col_info, - c) == false) - return false; - break; - case BOX_INLINE: - case BOX_INLINE_END: - case BOX_INLINE_BLOCK: - case BOX_FLOAT_LEFT: - case BOX_FLOAT_RIGHT: - case BOX_BR: - case BOX_TEXT: - /* should have been wrapped in inline - container by convert_xml_to_box() */ - assert(0); - break; - default: - assert(0); - } - } - - if (row_group->children == NULL) { -#ifdef BOX_NORMALISE_DEBUG - NSLOG(netsurf, INFO, - "row_group->children == 0, inserting implied row"); -#endif - - assert(row_group->style != NULL); - - ctx.ctx = c->select_ctx; - ctx.quirks = (c->quirks == DOM_DOCUMENT_QUIRKS_MODE_FULL); - ctx.base_url = c->base_url; - ctx.universal = c->universal; - - style = nscss_get_blank_style(&ctx, row_group->style); - if (style == NULL) { - return false; - } - - row = box_create(NULL, style, true, row_group->href, - row_group->target, NULL, NULL, c->bctx); - if (row == NULL) { - css_computed_style_destroy(style); - return false; - } - row->type = BOX_TABLE_ROW; - - row->parent = row_group; - row_group->children = row_group->last = row; - - group_row_count = 1; - - /* Keep table's row count in sync */ - col_info->num_rows++; - } - - row_group->rows = group_row_count; - -#ifdef BOX_NORMALISE_DEBUG - NSLOG(netsurf, INFO, "row_group %p done", row_group); -#endif - - return true; -} - - -bool box_normalise_table_row( - struct box *row, - const struct box *root, - struct columns *col_info, - html_content * c) -{ - struct box *child; - struct box *next_child; - struct box *cell = NULL; - css_computed_style *style; - unsigned int i; - nscss_select_ctx ctx; - - assert(row != NULL); - assert(row->type == BOX_TABLE_ROW); - - ctx.root_style = root->style; - -#ifdef BOX_NORMALISE_DEBUG - NSLOG(netsurf, INFO, "row %p", row); -#endif - - for (child = row->children; child != NULL; child = next_child) { - next_child = child->next; - - switch (child->type) { - case BOX_TABLE_CELL: - /* ok */ - if (box_normalise_block(child, root, c) == false) - return false; - cell = child; - break; - case BOX_BLOCK: - case BOX_INLINE_CONTAINER: - case BOX_TABLE: - case BOX_TABLE_ROW_GROUP: - case BOX_TABLE_ROW: - /* insert implied table cell */ - assert(row->style != NULL); - - ctx.ctx = c->select_ctx; - ctx.quirks = (c->quirks == DOM_DOCUMENT_QUIRKS_MODE_FULL); - ctx.base_url = c->base_url; - ctx.universal = c->universal; - - style = nscss_get_blank_style(&ctx, row->style); - if (style == NULL) - return false; - - cell = box_create(NULL, style, true, row->href, - row->target, NULL, NULL, c->bctx); - if (cell == NULL) { - css_computed_style_destroy(style); - return false; - } - cell->type = BOX_TABLE_CELL; - - if (child->prev == NULL) - row->children = cell; - else - child->prev->next = cell; - - cell->prev = child->prev; - - while (child != NULL && ( - child->type == BOX_BLOCK || - child->type == BOX_INLINE_CONTAINER || - child->type == BOX_TABLE || - child->type == BOX_TABLE_ROW_GROUP || - child->type == BOX_TABLE_ROW)) { - box_add_child(cell, child); - - next_child = child->next; - child->next = NULL; - child = next_child; - } - - assert(cell->last != NULL); - - cell->last->next = NULL; - cell->next = next_child = child; - if (cell->next != NULL) - cell->next->prev = cell; - else - row->last = cell; - cell->parent = row; - - if (box_normalise_block(cell, root, c) == false) - return false; - break; - case BOX_INLINE: - case BOX_INLINE_END: - case BOX_INLINE_BLOCK: - case BOX_FLOAT_LEFT: - case BOX_FLOAT_RIGHT: - case BOX_BR: - case BOX_TEXT: - /* should have been wrapped in inline - container by convert_xml_to_box() */ - assert(0); - break; - default: - assert(0); - } - - if (calculate_table_row(col_info, cell->columns, cell->rows, - &cell->start_column, cell) == false) - return false; - } - - - /* Update row spanning details for all columns */ - for (i = 0; i < col_info->num_columns; i++) { - if (col_info->spans[i].row_span != 0 && - col_info->spans[i].auto_row == false) { - /* This cell spans rows, and is not an auto row. - * Reduce number of rows left to span */ - col_info->spans[i].row_span--; - } - } - - /* Reset current column for next row */ - col_info->current_column = 0; - - /* Increment row counter */ - col_info->num_rows++; - -#ifdef BOX_NORMALISE_DEBUG - NSLOG(netsurf, INFO, "row %p done", row); -#endif - - return true; -} - - -/** - * Compute the column index at which the current cell begins. - * Additionally, update the column record to reflect row spanning. - * - * \param col_info Column record - * \param col_span Number of columns that current cell spans - * \param row_span Number of rows that current cell spans - * \param start_column Pointer to location to receive column index - * \param cell Box for current table cell - * \return true on success, false on memory exhaustion - */ - -bool calculate_table_row(struct columns *col_info, - unsigned int col_span, unsigned int row_span, - unsigned int *start_column, struct box *cell) -{ - unsigned int cell_start_col = col_info->current_column; - unsigned int cell_end_col; - unsigned int i; - struct span_info *spans; - struct box *rg = cell->parent->parent; /* Cell's row group */ - - /* Skip columns with cells spanning from above */ - /* TODO: Need to ignore cells spanning from above that belong to - * different row group. We don't have that info here. */ - while (col_info->spans[cell_start_col].row_span != 0 && - col_info->spans[cell_start_col].rg == rg) { - cell_start_col++; - } - - /* Update current column with calculated start */ - col_info->current_column = cell_start_col; - - /* If this cell has a colspan of 0, then assume 1. - * No other browser supports colspan=0, anyway. */ - if (col_span == 0) - col_span = 1; - - cell_end_col = cell_start_col + col_span; - - if (col_info->num_columns < cell_end_col) { - /* It appears that this row has more columns than - * the maximum recorded for the table so far. - * Allocate more span records. */ - spans = realloc(col_info->spans, - sizeof *spans * (cell_end_col + 1)); - if (spans == NULL) - return false; - - col_info->spans = spans; - col_info->num_columns = cell_end_col; - - /* Mark new final column as sentinel */ - col_info->spans[cell_end_col].row_span = 0; - col_info->spans[cell_end_col].auto_row = false; - } - - /* This cell may span multiple columns. If it also wants to span - * multiple rows, temporarily assume it spans 1 row only. This will - * be fixed up in box_normalise_table_spans() */ - for (i = cell_start_col; i < cell_end_col; i++) { - col_info->spans[i].row_span = (row_span == 0) ? 1 : row_span; - col_info->spans[i].auto_row = (row_span == 0); - col_info->spans[i].rg = rg; - } - - /* Update current column with calculated end. */ - col_info->current_column = cell_end_col; - - *start_column = cell_start_col; - - return true; -} - - -bool box_normalise_inline_container( - struct box *cont, - const struct box *root, - html_content * c) -{ - struct box *child; - struct box *next_child; - - assert(cont != NULL); - assert(cont->type == BOX_INLINE_CONTAINER); - -#ifdef BOX_NORMALISE_DEBUG - NSLOG(netsurf, INFO, "cont %p", cont); -#endif - - for (child = cont->children; child != NULL; child = next_child) { - next_child = child->next; - switch (child->type) { - case BOX_INLINE: - case BOX_INLINE_END: - case BOX_BR: - case BOX_TEXT: - /* ok */ - break; - case BOX_INLINE_BLOCK: - /* ok */ - if (box_normalise_block(child, root, c) == false) - return false; - break; - case BOX_FLOAT_LEFT: - case BOX_FLOAT_RIGHT: - /* ok */ - assert(child->children != NULL); - - switch (child->children->type) { - case BOX_BLOCK: - if (box_normalise_block(child->children, root, - c) == false) - return false; - break; - case BOX_TABLE: - if (box_normalise_table(child->children, root, - c) == false) - return false; - break; - default: - assert(0); - } - - if (child->children == NULL) { - /* the child has destroyed itself: remove float */ - if (child->prev == NULL) - child->parent->children = child->next; - else - child->prev->next = child->next; - if (child->next != NULL) - child->next->prev = child->prev; - else - child->parent->last = child->prev; - - box_free(child); - } - break; - case BOX_BLOCK: - case BOX_INLINE_CONTAINER: - case BOX_TABLE: - case BOX_TABLE_ROW_GROUP: - case BOX_TABLE_ROW: - case BOX_TABLE_CELL: - default: - assert(0); - } - } - -#ifdef BOX_NORMALISE_DEBUG - NSLOG(netsurf, INFO, "cont %p done", cont); -#endif - - return true; -} diff --git a/render/box_textarea.c b/render/box_textarea.c deleted file mode 100644 index a60984235..000000000 --- a/render/box_textarea.c +++ /dev/null @@ -1,350 +0,0 @@ -/* - * Copyright 2013 Michael Drake <tlsa@netsurf-browser.org> - * - * 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 <http://www.gnu.org/licenses/>. - */ - -/** \file - * Box tree treeview box replacement (implementation). - */ - -#include <dom/dom.h> - -#include "utils/config.h" -#include "utils/log.h" -#include "netsurf/keypress.h" -#include "desktop/textarea.h" - -#include "render/box_textarea.h" -#include "render/font.h" -#include "render/form_internal.h" - - -bool box_textarea_keypress(html_content *html, struct box *box, uint32_t key) -{ - struct form_control *gadget = box->gadget; - struct textarea *ta = gadget->data.text.ta; - struct form* form = box->gadget->form; - struct content *c = (struct content *) html; - - assert(ta != NULL); - - if (gadget->type != GADGET_TEXTAREA) { - switch (key) { - case NS_KEY_NL: - case NS_KEY_CR: - if (form) - form_submit(content_get_url(c), html->bw, - form, 0); - return true; - - case NS_KEY_TAB: - { - struct form_control *next_input; - /* Find next text entry field that is actually - * displayed (i.e. has an associated box) */ - for (next_input = gadget->next; - next_input && - ((next_input->type != GADGET_TEXTBOX && - next_input->type != GADGET_TEXTAREA && - next_input->type != GADGET_PASSWORD) || - !next_input->box); - next_input = next_input->next) - ; - if (!next_input) - return true; - - textarea_set_caret(ta, -1); - textarea_set_caret(next_input->data.text.ta, 0); - } - return true; - - case NS_KEY_SHIFT_TAB: - { - struct form_control *prev_input; - /* Find previous text entry field that is actually - * displayed (i.e. has an associated box) */ - for (prev_input = gadget->prev; - prev_input && - ((prev_input->type != GADGET_TEXTBOX && - prev_input->type != GADGET_TEXTAREA && - prev_input->type != GADGET_PASSWORD) || - !prev_input->box); - prev_input = prev_input->prev) - ; - if (!prev_input) - return true; - - textarea_set_caret(ta, -1); - textarea_set_caret(prev_input->data.text.ta, 0); - } - return true; - - default: - /* Pass to textarea widget */ - break; - } - } - - return textarea_keypress(ta, key); -} - - -/** - * Callback for html form textareas. - */ -static void box_textarea_callback(void *data, struct textarea_msg *msg) -{ - struct form_textarea_data *d = data; - struct form_control *gadget = d->gadget; - struct html_content *html = d->gadget->html; - struct box *box = gadget->box; - - switch (msg->type) { - case TEXTAREA_MSG_DRAG_REPORT: - if (msg->data.drag == TEXTAREA_DRAG_NONE) { - /* Textarea drag finished */ - html_drag_type drag_type = HTML_DRAG_NONE; - union html_drag_owner drag_owner; - drag_owner.no_owner = true; - - html_set_drag_type(html, drag_type, drag_owner, - NULL); - } else { - /* Textarea drag started */ - struct rect rect = { - .x0 = INT_MIN, - .y0 = INT_MIN, - .x1 = INT_MAX, - .y1 = INT_MAX - }; - union html_drag_owner drag_owner; - drag_owner.textarea = box; - - switch (msg->data.drag) { - case TEXTAREA_DRAG_SCROLLBAR: - html_set_drag_type(html, - HTML_DRAG_TEXTAREA_SCROLLBAR, - drag_owner, - &rect); - break; - - case TEXTAREA_DRAG_SELECTION: - html_set_drag_type(html, - HTML_DRAG_TEXTAREA_SELECTION, - drag_owner, - &rect); - break; - - default: - NSLOG(netsurf, INFO, - "Drag type %d not handled.", - msg->data.drag); - /* This is a logic faliure in the - * front end code so abort. - */ - assert(0); - break; - } - } - break; - - case TEXTAREA_MSG_REDRAW_REQUEST: - { - /* Request redraw of the required textarea rectangle */ - int x, y; - - if (html->reflowing == true) { - /* Can't redraw during layout, and it will - * be redrawn after layout anyway. */ - break; - } - - box_coords(box, &x, &y); - - content__request_redraw((struct content *)html, - x + msg->data.redraw.x0, - y + msg->data.redraw.y0, - msg->data.redraw.x1 - msg->data.redraw.x0, - msg->data.redraw.y1 - msg->data.redraw.y0); - } - break; - - case TEXTAREA_MSG_SELECTION_REPORT: - if (msg->data.selection.have_selection) { - /* Textarea now has a selection */ - union html_selection_owner sel_owner; - sel_owner.textarea = box; - - html_set_selection(html, HTML_SELECTION_TEXTAREA, - sel_owner, - msg->data.selection.read_only); - } else { - /* The textarea now has no selection */ - union html_selection_owner sel_owner; - sel_owner.none = true; - - html_set_selection(html, HTML_SELECTION_NONE, - sel_owner, true); - } - break; - - case TEXTAREA_MSG_CARET_UPDATE: - if (html->bw == NULL) - break; - - if (msg->data.caret.type == TEXTAREA_CARET_HIDE) { - union html_focus_owner focus_owner; - focus_owner.textarea = box; - html_set_focus(html, HTML_FOCUS_TEXTAREA, - focus_owner, true, 0, 0, 0, NULL); - } else { - union html_focus_owner focus_owner; - focus_owner.textarea = box; - html_set_focus(html, HTML_FOCUS_TEXTAREA, - focus_owner, false, - msg->data.caret.pos.x, - msg->data.caret.pos.y, - msg->data.caret.pos.height, - msg->data.caret.pos.clip); - } - break; - - case TEXTAREA_MSG_TEXT_MODIFIED: - form_gadget_update_value(gadget, - strndup(msg->data.modified.text, - msg->data.modified.len)); - break; - } -} - - -/* Exported interface, documented in box_textarea.h */ -bool box_textarea_create_textarea(html_content *html, - struct box *box, struct dom_node *node) -{ - dom_string *dom_text = NULL; - dom_exception err; - textarea_setup ta_setup; - textarea_flags ta_flags; - plot_font_style_t fstyle = { - .family = PLOT_FONT_FAMILY_SANS_SERIF, - .size = 10 * FONT_SIZE_SCALE, - .weight = 400, - .flags = FONTF_NONE, - .background = 0, - .foreground = 0, - }; - bool read_only = false; - bool disabled = false; - struct form_control *gadget = box->gadget; - const char *text; - - assert(gadget != NULL); - assert(gadget->type == GADGET_TEXTAREA || - gadget->type == GADGET_TEXTBOX || - gadget->type == GADGET_PASSWORD); - - if (gadget->type == GADGET_TEXTAREA) { - dom_html_text_area_element *textarea = - (dom_html_text_area_element *) node; - ta_flags = TEXTAREA_MULTILINE; - - err = dom_html_text_area_element_get_read_only( - textarea, &read_only); - if (err != DOM_NO_ERR) - return false; - - err = dom_html_text_area_element_get_disabled( - textarea, &disabled); - if (err != DOM_NO_ERR) - return false; - - /* Get the textarea's initial content */ - err = dom_html_text_area_element_get_value(textarea, &dom_text); - if (err != DOM_NO_ERR) - return false; - - } else { - dom_html_input_element *input = (dom_html_input_element *) node; - - err = dom_html_input_element_get_read_only( - input, &read_only); - if (err != DOM_NO_ERR) - return false; - - err = dom_html_input_element_get_disabled( - input, &disabled); - if (err != DOM_NO_ERR) - return false; - - if (gadget->type == GADGET_PASSWORD) - ta_flags = TEXTAREA_PASSWORD; - else - ta_flags = TEXTAREA_DEFAULT; - - /* Get initial text */ - err = dom_html_input_element_get_value(input, &dom_text); - if (err != DOM_NO_ERR) - return false; - } - - if (dom_text != NULL) { - text = dom_string_data(dom_text); - } else { - /* No initial text, or failed reading it; - * use a blank string */ - text = ""; - } - - if (read_only || disabled) - ta_flags |= TEXTAREA_READONLY; - - gadget->data.text.data.gadget = gadget; - - /* Reset to correct values by layout */ - ta_setup.width = 200; - ta_setup.height = 20; - ta_setup.pad_top = 4; - ta_setup.pad_right = 4; - ta_setup.pad_bottom = 4; - ta_setup.pad_left = 4; - - /* Set remaining data */ - ta_setup.border_width = 0; - ta_setup.border_col = 0x000000; - ta_setup.text = fstyle; - ta_setup.text.background = NS_TRANSPARENT; - /* Make selected text either black or white, as gives greatest contrast - * with background colour. */ - ta_setup.selected_bg = fstyle.foreground; - ta_setup.selected_text = colour_to_bw_furthest(ta_setup.selected_bg); - - /* Hand reference to dom text over to gadget */ - gadget->data.text.initial = dom_text; - - gadget->data.text.ta = textarea_create(ta_flags, &ta_setup, - box_textarea_callback, &gadget->data.text.data); - - if (gadget->data.text.ta == NULL) { - return false; - } - - if (!textarea_set_text(gadget->data.text.ta, text)) - return false; - - return true; -} - diff --git a/render/box_textarea.h b/render/box_textarea.h deleted file mode 100644 index a7a377076..000000000 --- a/render/box_textarea.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright 2013 Michael Drake <tlsa@netsurf-browser.org> - * - * 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 <http://www.gnu.org/licenses/>. - */ - -/** \file - * Box tree treeview box replacement (interface). - */ - - - -#ifndef _NETSURF_RENDER_BOX_TEXTAREA_H_ -#define _NETSURF_RENDER_BOX_TEXTAREA_H_ - - -#include "render/box.h" -#include "render/html_internal.h" - -struct dom_node; - -/** - * Create textarea widget for a form element - * - * \param html html content object - * \param box box with gadget to be given textarea widget - * \param node DOM node for form element - */ -bool box_textarea_create_textarea(html_content *html, - struct box *box, struct dom_node *node); - - -/** - * Handle form textarea keypress input - * - * \param html html content object - * \param box box with textarea widget - * \param key keypress - * \return true iff keypress handled - */ -bool box_textarea_keypress(html_content *html, struct box *box, uint32_t key); - -#endif diff --git a/render/font.c b/render/font.c deleted file mode 100644 index a769b476f..000000000 --- a/render/font.c +++ /dev/null @@ -1,163 +0,0 @@ -/* - * Copyright 2009 John-Mark Bell <jmb@netsurf-browser.org> - * - * 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 <http://www.gnu.org/licenses/>. - */ - -/** - * \file - * - * Renderer internal font handling implementation. - */ - -#include "utils/nsoption.h" -#include "netsurf/plot_style.h" -#include "css/utils.h" - -#include "render/font.h" - -/** - * Map a generic CSS font family to a generic plot font family - * - * \param css Generic CSS font family - * \return Plot font family - */ -static plot_font_generic_family_t plot_font_generic_family( - enum css_font_family_e css) -{ - plot_font_generic_family_t plot; - - switch (css) { - case CSS_FONT_FAMILY_SERIF: - plot = PLOT_FONT_FAMILY_SERIF; - break; - case CSS_FONT_FAMILY_MONOSPACE: - plot = PLOT_FONT_FAMILY_MONOSPACE; - break; - case CSS_FONT_FAMILY_CURSIVE: - plot = PLOT_FONT_FAMILY_CURSIVE; - break; - case CSS_FONT_FAMILY_FANTASY: - plot = PLOT_FONT_FAMILY_FANTASY; - break; - case CSS_FONT_FAMILY_SANS_SERIF: - default: - plot = PLOT_FONT_FAMILY_SANS_SERIF; - break; - } - - return plot; -} - -/** - * Map a CSS font weight to a plot weight value - * - * \param css CSS font weight - * \return Plot weight - */ -static int plot_font_weight(enum css_font_weight_e css) -{ - int weight; - - switch (css) { - case CSS_FONT_WEIGHT_100: - weight = 100; - break; - case CSS_FONT_WEIGHT_200: - weight = 200; - break; - case CSS_FONT_WEIGHT_300: - weight = 300; - break; - case CSS_FONT_WEIGHT_400: - case CSS_FONT_WEIGHT_NORMAL: - default: - weight = 400; - break; - case CSS_FONT_WEIGHT_500: - weight = 500; - break; - case CSS_FONT_WEIGHT_600: - weight = 600; - break; - case CSS_FONT_WEIGHT_700: - case CSS_FONT_WEIGHT_BOLD: - weight = 700; - break; - case CSS_FONT_WEIGHT_800: - weight = 800; - break; - case CSS_FONT_WEIGHT_900: - weight = 900; - break; - } - - return weight; -} - -/** - * Map a CSS font style and font variant to plot font flags - * - * \param style CSS font style - * \param variant CSS font variant - * \return Computed plot flags - */ -static plot_font_flags_t plot_font_flags(enum css_font_style_e style, - enum css_font_variant_e variant) -{ - plot_font_flags_t flags = FONTF_NONE; - - if (style == CSS_FONT_STYLE_ITALIC) - flags |= FONTF_ITALIC; - else if (style == CSS_FONT_STYLE_OBLIQUE) - flags |= FONTF_OBLIQUE; - - if (variant == CSS_FONT_VARIANT_SMALL_CAPS) - flags |= FONTF_SMALLCAPS; - - return flags; -} - - -/* exported function documented in render/font.h */ -void font_plot_style_from_css( - const nscss_len_ctx *len_ctx, - const css_computed_style *css, - plot_font_style_t *fstyle) -{ - lwc_string **families; - css_fixed length = 0; - css_unit unit = CSS_UNIT_PX; - css_color col; - - fstyle->family = plot_font_generic_family( - css_computed_font_family(css, &families)); - - css_computed_font_size(css, &length, &unit); - fstyle->size = FIXTOINT(FMUL(nscss_len2pt(len_ctx, length, unit), - INTTOFIX(FONT_SIZE_SCALE))); - - /* Clamp font size to configured minimum */ - if (fstyle->size < (nsoption_int(font_min_size) * FONT_SIZE_SCALE) / 10) - fstyle->size = (nsoption_int(font_min_size) * FONT_SIZE_SCALE) / 10; - - fstyle->weight = plot_font_weight(css_computed_font_weight(css)); - fstyle->flags = plot_font_flags(css_computed_font_style(css), - css_computed_font_variant(css)); - - css_computed_color(css, &col); - fstyle->foreground = nscss_color_to_ns(col); - fstyle->background = 0; -} diff --git a/render/font.h b/render/font.h deleted file mode 100644 index 52f5a62c2..000000000 --- a/render/font.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright 2014 Vincent Sanders <vince@netsurf-browser.org> - * - * 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 <http://www.gnu.org/licenses/>. - */ - -/** \file - * - * Internal font handling interfaces. - * - * These functions provide font related services. They all work on - * UTF-8 strings with lengths given. - */ - -#ifndef _NETSURF_RENDER_FONT_H_ -#define _NETSURF_RENDER_FONT_H_ - -struct plot_font_style; - -/** - * Populate a font style using data from a computed CSS style - * - * \param len_ctx Length conversion context - * \param css Computed style to consider - * \param fstyle Font style to populate - */ -void font_plot_style_from_css( - const nscss_len_ctx *len_ctx, - const css_computed_style *css, - struct plot_font_style *fstyle); - -#endif diff --git a/render/form.c b/render/form.c deleted file mode 100644 index 432002564..000000000 --- a/render/form.c +++ /dev/null @@ -1,1895 +0,0 @@ -/* - * Copyright 2004 James Bursa <bursa@users.sourceforge.net> - * Copyright 2003 Phil Mellor <monkeyson@users.sourceforge.net> - * Copyright 2004 John Tytgat <joty@netsurf-browser.org> - * Copyright 2005-9 John-Mark Bell <jmb@netsurf-browser.org> - * Copyright 2009 Paul Blokus <paul_pl@users.sourceforge.net> - * Copyright 2010 Michael Drake <tlsa@netsurf-browser.org> - * - * 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 <http://www.gnu.org/licenses/>. - */ - -/** - * \file - * Form handling functions (implementation). - */ - -#include <assert.h> -#include <limits.h> -#include <stdbool.h> -#include <stdio.h> -#include <string.h> -#include <dom/dom.h> - -#include "utils/corestrings.h" -#include "utils/log.h" -#include "utils/messages.h" -#include "utils/talloc.h" -#include "utils/url.h" -#include "utils/utf8.h" -#include "utils/ascii.h" -#include "netsurf/browser_window.h" -#include "netsurf/mouse.h" -#include "netsurf/plotters.h" -#include "netsurf/misc.h" -#include "content/fetch.h" -#include "content/hlcache.h" -#include "css/utils.h" -#include "desktop/knockout.h" -#include "desktop/scrollbar.h" -#include "desktop/textarea.h" -#include "desktop/gui_internal.h" - -#include "render/box.h" -#include "render/font.h" -#include "render/form_internal.h" -#include "render/html.h" -#include "render/html_internal.h" -#include "render/layout.h" - -#define MAX_SELECT_HEIGHT 210 -#define SELECT_LINE_SPACING 0.2 -#define SELECT_BORDER_WIDTH 1 -#define SELECT_SELECTED_COLOUR 0xDB9370 - -struct form_select_menu { - int line_height; - int width, height; - struct scrollbar *scrollbar; - int f_size; - bool scroll_capture; - select_menu_redraw_callback callback; - void *client_data; - struct content *c; -}; - -static plot_style_t plot_style_fill_selected = { - .fill_type = PLOT_OP_TYPE_SOLID, - .fill_colour = SELECT_SELECTED_COLOUR, -}; - -static plot_font_style_t plot_fstyle_entry = { - .family = PLOT_FONT_FAMILY_SANS_SERIF, - .weight = 400, - .flags = FONTF_NONE, - .background = 0xffffff, - .foreground = 0x000000, -}; - -static char *form_acceptable_charset(struct form *form); -static char *form_encode_item(const char *item, uint32_t len, const char *charset, - const char *fallback); -static void form_select_menu_clicked(struct form_control *control, - int x, int y); -static void form_select_menu_scroll_callback(void *client_data, - struct scrollbar_msg_data *scrollbar_data); - -/* exported interface documented in render/form_internal.h */ -struct form *form_new(void *node, const char *action, const char *target, - form_method method, const char *charset, - const char *doc_charset) -{ - struct form *form; - - form = calloc(1, sizeof *form); - if (!form) - return NULL; - - form->action = strdup(action != NULL ? action : ""); - if (form->action == NULL) { - free(form); - return NULL; - } - - form->target = target != NULL ? strdup(target) : NULL; - if (target != NULL && form->target == NULL) { - free(form->action); - free(form); - return NULL; - } - - form->method = method; - - form->accept_charsets = charset != NULL ? strdup(charset) : NULL; - if (charset != NULL && form->accept_charsets == NULL) { - free(form->target); - free(form->action); - free(form); - return NULL; - } - - form->document_charset = doc_charset != NULL ? strdup(doc_charset) - : NULL; - if (doc_charset && form->document_charset == NULL) { - free(form->accept_charsets); - free(form->target); - free(form->action); - free(form); - return NULL; - } - - form->node = node; - - return form; -} - - -/* exported interface documented in render/form_internal.h */ -void form_free(struct form *form) -{ - struct form_control *c, *d; - - for (c = form->controls; c != NULL; c = d) { - d = c->next; - - form_free_control(c); - } - - free(form->action); - free(form->target); - free(form->accept_charsets); - free(form->document_charset); - - free(form); -} - -/* exported interface documented in render/form_internal.h */ -struct form_control *form_new_control(void *node, form_control_type type) -{ - struct form_control *control; - - control = calloc(1, sizeof *control); - if (control == NULL) - return NULL; - - control->node = node; - control->type = type; - - return control; -} - - -/** - * Add a control to the list of controls in a form. - * - * \param form The form to add the control to - * \param control The control to add - */ -void form_add_control(struct form *form, struct form_control *control) -{ - if (form == NULL) { - return; - } - - control->form = form; - - if (form->controls != NULL) { - assert(form->last_control); - - form->last_control->next = control; - control->prev = form->last_control; - control->next = NULL; - form->last_control = control; - } else { - form->controls = form->last_control = control; - } -} - - -/** - * Free a struct form_control. - * - * \param control structure to free - */ -void form_free_control(struct form_control *control) -{ - struct form_control *c; - assert(control != NULL); - - NSLOG(netsurf, INFO, "Control:%p name:%p value:%p initial:%p", - control, control->name, control->value, control->initial_value); - free(control->name); - free(control->value); - free(control->initial_value); - - if (control->type == GADGET_SELECT) { - struct form_option *option, *next; - - for (option = control->data.select.items; option; - option = next) { - next = option->next; - NSLOG(netsurf, INFO, - "select option:%p text:%p value:%p", option, - option->text, option->value); - free(option->text); - free(option->value); - free(option); - } - if (control->data.select.menu != NULL) { - form_free_select_menu(control); - } - } - - if (control->type == GADGET_TEXTAREA || - control->type == GADGET_TEXTBOX || - control->type == GADGET_PASSWORD) { - - if (control->data.text.initial != NULL) { - dom_string_unref(control->data.text.initial); - } - - if (control->data.text.ta != NULL) { - textarea_destroy(control->data.text.ta); - } - } - - /* unlink the control from the form */ - if (control->form != NULL) { - for (c = control->form->controls; c != NULL; c = c->next) { - if (c->next == control) { - c->next = control->next; - if (control->form->last_control == control) - control->form->last_control = c; - break; - } - if (c == control) { - /* can only happen if control was first control */ - control->form->controls = control->next; - if (control->form->last_control == control) - control->form->controls = - control->form->last_control = NULL; - break; - } - } - } - - free(control); -} - - -/** - * Add an option to a form select control. - * - * \param control form control of type GADGET_SELECT - * \param value value of option, used directly (not copied) - * \param text text for option, used directly (not copied) - * \param selected this option is selected - * \param node the DOM node this option is associated with - * \return true on success, false on memory exhaustion - */ -bool form_add_option(struct form_control *control, char *value, char *text, - bool selected, void *node) -{ - struct form_option *option; - - assert(control); - assert(control->type == GADGET_SELECT); - - option = calloc(1, sizeof *option); - if (!option) - return false; - - option->value = value; - option->text = text; - - /* add to linked list */ - if (control->data.select.items == 0) - control->data.select.items = option; - else - control->data.select.last_item->next = option; - control->data.select.last_item = option; - - /* set selected */ - if (selected && (control->data.select.num_selected == 0 || - control->data.select.multiple)) { - option->selected = option->initial_selected = true; - control->data.select.num_selected++; - control->data.select.current = option; - } - - control->data.select.num_items++; - - option->node = node; - - return true; -} - - -/* exported interface documented in render/form_internal.h */ -bool form_successful_controls_dom(struct form *_form, - struct form_control *_submit_button, - struct fetch_multipart_data **successful_controls) -{ - dom_html_form_element *form = _form->node; - dom_html_element *submit_button = (_submit_button != NULL) ? _submit_button->node : NULL; - dom_html_collection *form_elements = NULL; - dom_html_options_collection *options = NULL; - dom_node *form_element = NULL, *option_element = NULL; - dom_exception err; - dom_string *nodename = NULL, *inputname = NULL, *inputvalue = NULL, *inputtype = NULL; - struct fetch_multipart_data sentinel, *last_success, *success_new; - bool had_submit = false, element_disabled, checked; - char *charset, *rawfile_temp = NULL, *basename; - uint32_t index, element_count; - struct image_input_coords *coords; - - last_success = &sentinel; - sentinel.next = NULL; - - /** \todo Replace this call with something DOMish */ - charset = form_acceptable_charset(_form); - if (charset == NULL) { - NSLOG(netsurf, INFO, "failed to find charset"); - return false; - } - -#define ENCODE_ITEM(i) (((i) == NULL) ? ( \ - form_encode_item("", 0, charset, _form->document_charset) \ - ):( \ - form_encode_item(dom_string_data(i), dom_string_byte_length(i), \ - charset, _form->document_charset) \ - )) - - err = dom_html_form_element_get_elements(form, &form_elements); - - if (err != DOM_NO_ERR) { - NSLOG(netsurf, INFO, "Could not get form elements"); - goto dom_no_memory; - } - - - err = dom_html_collection_get_length(form_elements, &element_count); - - if (err != DOM_NO_ERR) { - NSLOG(netsurf, INFO, "Could not get form element count"); - goto dom_no_memory; - } - - for (index = 0; index < element_count; index++) { - if (form_element != NULL) { - dom_node_unref(form_element); - form_element = NULL; - } - if (nodename != NULL) { - dom_string_unref(nodename); - nodename = NULL; - } - if (inputname != NULL) { - dom_string_unref(inputname); - inputname = NULL; - } - if (inputvalue != NULL) { - dom_string_unref(inputvalue); - inputvalue = NULL; - } - if (inputtype != NULL) { - dom_string_unref(inputtype); - inputtype = NULL; - } - if (options != NULL) { - dom_html_options_collection_unref(options); - options = NULL; - } - err = dom_html_collection_item(form_elements, - index, &form_element); - if (err != DOM_NO_ERR) { - NSLOG(netsurf, INFO, - "Could not retrieve form element %d", index); - goto dom_no_memory; - } - - /* Form elements are one of: - * HTMLButtonElement - * HTMLInputElement - * HTMLTextAreaElement - * HTMLSelectElement - */ - err = dom_node_get_node_name(form_element, &nodename); - if (err != DOM_NO_ERR) { - NSLOG(netsurf, INFO, "Could not get node name"); - goto dom_no_memory; - } - - if (dom_string_isequal(nodename, corestring_dom_TEXTAREA)) { - err = dom_html_text_area_element_get_disabled( - (dom_html_text_area_element *)form_element, - &element_disabled); - if (err != DOM_NO_ERR) { - NSLOG(netsurf, INFO, - "Could not get text area disabled property"); - goto dom_no_memory; - } - err = dom_html_text_area_element_get_name( - (dom_html_text_area_element *)form_element, - &inputname); - if (err != DOM_NO_ERR) { - NSLOG(netsurf, INFO, - "Could not get text area name property"); - goto dom_no_memory; - } - } else if (dom_string_isequal(nodename, corestring_dom_SELECT)) { - err = dom_html_select_element_get_disabled( - (dom_html_select_element *)form_element, - &element_disabled); - if (err != DOM_NO_ERR) { - NSLOG(netsurf, INFO, - "Could not get select disabled property"); - goto dom_no_memory; - } - err = dom_html_select_element_get_name( - (dom_html_select_element *)form_element, - &inputname); - if (err != DOM_NO_ERR) { - NSLOG(netsurf, INFO, - "Could not get select name property"); - goto dom_no_memory; - } - } else if (dom_string_isequal(nodename, corestring_dom_INPUT)) { - err = dom_html_input_element_get_disabled( - (dom_html_input_element *)form_element, - &element_disabled); - if (err != DOM_NO_ERR) { - NSLOG(netsurf, INFO, - "Could not get input disabled property"); - goto dom_no_memory; - } - err = dom_html_input_element_get_name( - (dom_html_input_element *)form_element, - &inputname); - if (err != DOM_NO_ERR) { - NSLOG(netsurf, INFO, - "Could not get input name property"); - goto dom_no_memory; - } - } else if (dom_string_isequal(nodename, corestring_dom_BUTTON)) { - err = dom_html_button_element_get_disabled( - (dom_html_button_element *)form_element, - &element_disabled); - if (err != DOM_NO_ERR) { - NSLOG(netsurf, INFO, - "Could not get button disabled property"); - goto dom_no_memory; - } - err = dom_html_button_element_get_name( - (dom_html_button_element *)form_element, - &inputname); - if (err != DOM_NO_ERR) { - NSLOG(netsurf, INFO, - "Could not get button name property"); - goto dom_no_memory; - } - } else { - /* Unknown element type came through! */ - NSLOG(netsurf, INFO, "Unknown element type: %*s", - (int)dom_string_byte_length(nodename), - dom_string_data(nodename)); - goto dom_no_memory; - } - if (element_disabled) - continue; - if (inputname == NULL) - continue; - - if (dom_string_isequal(nodename, corestring_dom_TEXTAREA)) { - err = dom_html_text_area_element_get_value( - (dom_html_text_area_element *)form_element, - &inputvalue); - if (err != DOM_NO_ERR) { - NSLOG(netsurf, INFO, - "Could not get text area content"); - goto dom_no_memory; - } - } else if (dom_string_isequal(nodename, corestring_dom_SELECT)) { - uint32_t options_count, option_index; - err = dom_html_select_element_get_options( - (dom_html_select_element *)form_element, - &options); - if (err != DOM_NO_ERR) { - NSLOG(netsurf, INFO, - "Could not get select options collection"); - goto dom_no_memory; - } - err = dom_html_options_collection_get_length( - options, &options_count); - if (err != DOM_NO_ERR) { - NSLOG(netsurf, INFO, - "Could not get select options collection length"); - goto dom_no_memory; - } - for(option_index = 0; option_index < options_count; - ++option_index) { - bool selected; - if (option_element != NULL) { - dom_node_unref(option_element); - option_element = NULL; - } - if (inputvalue != NULL) { - dom_string_unref(inputvalue); - inputvalue = NULL; - } - err = dom_html_options_collection_item( - options, option_index, &option_element); - if (err != DOM_NO_ERR) { - NSLOG(netsurf, INFO, - "Could not get options item %d", - option_index); - goto dom_no_memory; - } - err = dom_html_option_element_get_selected( - (dom_html_option_element *)option_element, - &selected); - if (err != DOM_NO_ERR) { - NSLOG(netsurf, INFO, - "Could not get option selected property"); - goto dom_no_memory; - } - if (!selected) - continue; - err = dom_html_option_element_get_value( - (dom_html_option_element *)option_element, - &inputvalue); - if (err != DOM_NO_ERR) { - NSLOG(netsurf, INFO, - "Could not get option value"); - goto dom_no_memory; - } - - success_new = calloc(1, sizeof(*success_new)); - if (success_new == NULL) { - NSLOG(netsurf, INFO, - "Could not allocate data for option"); - goto dom_no_memory; - } - - last_success->next = success_new; - last_success = success_new; - - success_new->name = ENCODE_ITEM(inputname); - if (success_new->name == NULL) { - NSLOG(netsurf, INFO, - "Could not encode name for option"); - goto dom_no_memory; - } - success_new->value = ENCODE_ITEM(inputvalue); - if (success_new->value == NULL) { - NSLOG(netsurf, INFO, - "Could not encode value for option"); - goto dom_no_memory; - } - } - continue; - } else if (dom_string_isequal(nodename, corestring_dom_BUTTON)) { - err = dom_html_button_element_get_type( - (dom_html_button_element *) form_element, - &inputtype); - if (err != DOM_NO_ERR) { - NSLOG(netsurf, INFO, - "Could not get button element type"); - goto dom_no_memory; - } - if (dom_string_caseless_isequal( - inputtype, corestring_dom_submit)) { - - if (submit_button == NULL && !had_submit) { - /* no button used, and first submit - * node found, so use it - */ - had_submit = true; - } else if ((dom_node *)submit_button != - (dom_node *)form_element) { - continue; - } - - err = dom_html_button_element_get_value( - (dom_html_button_element *)form_element, - &inputvalue); - if (err != DOM_NO_ERR) { - NSLOG(netsurf, INFO, - "Could not get submit button value"); - goto dom_no_memory; - } - /* Drop through to report successful button */ - } else { - continue; - } - } else if (dom_string_isequal(nodename, corestring_dom_INPUT)) { - /* Things to consider here */ - /* Buttons -- only if the successful control */ - /* radio and checkbox -- only if selected */ - /* file -- also get the rawfile */ - /* everything else -- just value */ - err = dom_html_input_element_get_type( - (dom_html_input_element *) form_element, - &inputtype); - if (err != DOM_NO_ERR) { - NSLOG(netsurf, INFO, - "Could not get input element type"); - goto dom_no_memory; - } - if (dom_string_caseless_isequal( - inputtype, corestring_dom_submit)) { - - if (submit_button == NULL && !had_submit) { - /* no button used, and first submit - * node found, so use it - */ - had_submit = true; - } else if ((dom_node *)submit_button != - (dom_node *)form_element) { - continue; - } - - err = dom_html_input_element_get_value( - (dom_html_input_element *)form_element, - &inputvalue); - if (err != DOM_NO_ERR) { - NSLOG(netsurf, INFO, - "Could not get submit button value"); - goto dom_no_memory; - } - /* Drop through to report the successful button */ - } else if (dom_string_caseless_isequal( - inputtype, corestring_dom_image)) { - /* We *ONLY* use an image input if it was the - * thing which activated us - */ - if ((dom_node *)submit_button != - (dom_node *)form_element) - continue; - - err = dom_node_get_user_data( - form_element, - corestring_dom___ns_key_image_coords_node_data, - &coords); - if (err != DOM_NO_ERR) { - NSLOG(netsurf, INFO, - "Could not get image XY data"); - goto dom_no_memory; - } - if (coords == NULL) { - NSLOG(netsurf, INFO, - "No XY data on the image input"); - goto dom_no_memory; - } - - basename = ENCODE_ITEM(inputname); - - success_new = calloc(1, sizeof(*success_new)); - if (success_new == NULL) { - free(basename); - NSLOG(netsurf, INFO, - "Could not allocate data for image.x"); - goto dom_no_memory; - } - - last_success->next = success_new; - last_success = success_new; - - success_new->name = malloc(strlen(basename) + 3); - if (success_new->name == NULL) { - free(basename); - NSLOG(netsurf, INFO, - "Could not allocate name for image.x"); - goto dom_no_memory; - } - success_new->value = malloc(20); - if (success_new->value == NULL) { - free(basename); - NSLOG(netsurf, INFO, - "Could not allocate value for image.x"); - goto dom_no_memory; - } - sprintf(success_new->name, "%s.x", basename); - sprintf(success_new->value, "%d", coords->x); - - success_new = calloc(1, sizeof(*success_new)); - if (success_new == NULL) { - free(basename); - NSLOG(netsurf, INFO, - "Could not allocate data for image.y"); - goto dom_no_memory; - } - - last_success->next = success_new; - last_success = success_new; - - success_new->name = malloc(strlen(basename) + 3); - if (success_new->name == NULL) { - free(basename); - NSLOG(netsurf, INFO, - "Could not allocate name for image.y"); - goto dom_no_memory; - } - success_new->value = malloc(20); - if (success_new->value == NULL) { - free(basename); - NSLOG(netsurf, INFO, - "Could not allocate value for image.y"); - goto dom_no_memory; - } - sprintf(success_new->name, "%s.y", basename); - sprintf(success_new->value, "%d", coords->y); - free(basename); - continue; - } else if (dom_string_caseless_isequal( - inputtype, corestring_dom_radio) || - dom_string_caseless_isequal( - inputtype, corestring_dom_checkbox)) { - err = dom_html_input_element_get_checked( - (dom_html_input_element *)form_element, - &checked); - if (err != DOM_NO_ERR) { - NSLOG(netsurf, INFO, - "Could not get input element checked"); - goto dom_no_memory; - } - if (!checked) - continue; - err = dom_html_input_element_get_value( - (dom_html_input_element *)form_element, - &inputvalue); - if (err != DOM_NO_ERR) { - NSLOG(netsurf, INFO, - "Could not get input element value"); - goto dom_no_memory; - } - if (inputvalue == NULL) { - inputvalue = dom_string_ref( - corestring_dom_on); - } - /* Fall through to simple allocation */ - } else if (dom_string_caseless_isequal( - inputtype, corestring_dom_file)) { - - err = dom_html_input_element_get_value( - (dom_html_input_element *)form_element, - &inputvalue); - if (err != DOM_NO_ERR) { - NSLOG(netsurf, INFO, - "Could not get file value"); - goto dom_no_memory; - } - err = dom_node_get_user_data( - form_element, - corestring_dom___ns_key_file_name_node_data, - &rawfile_temp); - if (err != DOM_NO_ERR) { - NSLOG(netsurf, INFO, - "Could not get file rawname"); - goto dom_no_memory; - } - rawfile_temp = strdup(rawfile_temp != NULL ? - rawfile_temp : - ""); - if (rawfile_temp == NULL) { - NSLOG(netsurf, INFO, - "Could not copy file rawname"); - goto dom_no_memory; - } - /* Fall out to the allocation */ - } else if (dom_string_caseless_isequal( - inputtype, corestring_dom_reset) || - dom_string_caseless_isequal( - inputtype, corestring_dom_button)) { - /* Skip these */ - NSLOG(netsurf, INFO, - "Skipping RESET and BUTTON"); - continue; - } else { - /* Everything else is treated as text values */ - err = dom_html_input_element_get_value( - (dom_html_input_element *)form_element, - &inputvalue); - if (err != DOM_NO_ERR) { - NSLOG(netsurf, INFO, - "Could not get input value"); - goto dom_no_memory; - } - /* Fall out to the allocation */ - } - } - - success_new = calloc(1, sizeof(*success_new)); - if (success_new == NULL) { - NSLOG(netsurf, INFO, - "Could not allocate data for generic"); - goto dom_no_memory; - } - - last_success->next = success_new; - last_success = success_new; - - success_new->name = ENCODE_ITEM(inputname); - if (success_new->name == NULL) { - NSLOG(netsurf, INFO, - "Could not encode name for generic"); - goto dom_no_memory; - } - success_new->value = ENCODE_ITEM(inputvalue); - if (success_new->value == NULL) { - NSLOG(netsurf, INFO, - "Could not encode value for generic"); - goto dom_no_memory; - } - if (rawfile_temp != NULL) { - success_new->file = true; - success_new->rawfile = rawfile_temp; - rawfile_temp = NULL; - } - } - - free(charset); - - if (form_element != NULL) { - dom_node_unref(form_element); - } - - if (form_elements != NULL) { - dom_html_collection_unref(form_elements); - } - - if (nodename != NULL) { - dom_string_unref(nodename); - } - - if (inputname != NULL) { - dom_string_unref(inputname); - } - - if (inputvalue != NULL) { - dom_string_unref(inputvalue); - } - - if (options != NULL) { - dom_html_options_collection_unref(options); - } - - if (option_element != NULL) { - dom_node_unref(option_element); - } - - if (inputtype != NULL) { - dom_string_unref(inputtype); - } - - if (rawfile_temp != NULL) { - free(rawfile_temp); - } - - *successful_controls = sentinel.next; - - return true; - -dom_no_memory: - free(charset); - fetch_multipart_data_destroy(sentinel.next); - - if (form_elements != NULL) - dom_html_collection_unref(form_elements); - if (form_element != NULL) - dom_node_unref(form_element); - if (nodename != NULL) - dom_string_unref(nodename); - if (inputname != NULL) - dom_string_unref(inputname); - if (inputvalue != NULL) - dom_string_unref(inputvalue); - if (options != NULL) - dom_html_options_collection_unref(options); - if (option_element != NULL) - dom_node_unref(option_element); - if (inputtype != NULL) - dom_string_unref(inputtype); - if (rawfile_temp != NULL) - free(rawfile_temp); - - return false; -} -#undef ENCODE_ITEM - -/** - * Encode controls using application/x-www-form-urlencoded. - * - * \param form form to which successful controls relate - * \param control linked list of fetch_multipart_data - * \param query_string iff true add '?' to the start of returned data - * \return URL-encoded form, or 0 on memory exhaustion - */ - -static char *form_url_encode(struct form *form, - struct fetch_multipart_data *control, - bool query_string) -{ - char *name, *value; - char *s, *s2; - unsigned int len, len1, len_init; - nserror url_err; - - if (query_string) - s = malloc(2); - else - s = malloc(1); - - if (s == NULL) - return NULL; - - if (query_string) { - s[0] = '?'; - s[1] = '\0'; - len_init = len = 1; - } else { - s[0] = '\0'; - len_init = len = 0; - } - - for (; control; control = control->next) { - url_err = url_escape(control->name, true, NULL, &name); - if (url_err == NSERROR_NOMEM) { - free(s); - return NULL; - } - - assert(url_err == NSERROR_OK); - - url_err = url_escape(control->value, true, NULL, &value); - if (url_err == NSERROR_NOMEM) { - free(name); - free(s); - return NULL; - } - - assert(url_err == NSERROR_OK); - - len1 = len + strlen(name) + strlen(value) + 2; - s2 = realloc(s, len1 + 1); - if (!s2) { - free(value); - free(name); - free(s); - return NULL; - } - s = s2; - sprintf(s + len, "%s=%s&", name, value); - len = len1; - free(name); - free(value); - } - - if (len > len_init) { - /* Replace trailing '&' */ - s[len - 1] = '\0'; - } - return s; -} - -/** - * Find an acceptable character set encoding with which to submit the form - * - * \param form The form - * \return Pointer to charset name (on heap, caller should free) or NULL - */ -char *form_acceptable_charset(struct form *form) -{ - char *temp, *c; - - if (!form) - return NULL; - - if (!form->accept_charsets) { - /* no accept-charsets attribute for this form */ - if (form->document_charset) - /* document charset present, so use it */ - return strdup(form->document_charset); - else - /* no document charset, so default to 8859-1 */ - return strdup("ISO-8859-1"); - } - - /* make temporary copy of accept-charsets attribute */ - temp = strdup(form->accept_charsets); - if (!temp) - return NULL; - - /* make it upper case */ - for (c = temp; *c; c++) { - *c = ascii_to_upper(*c); - } - - /* is UTF-8 specified? */ - c = strstr(temp, "UTF-8"); - if (c) { - free(temp); - return strdup("UTF-8"); - } - - /* dispense with temporary copy */ - free(temp); - - /* according to RFC2070, the accept-charsets attribute of the - * form element contains a space and/or comma separated list */ - c = form->accept_charsets; - - /** \todo an improvement would be to choose an encoding - * acceptable to the server which covers as much of the input - * values as possible. Additionally, we need to handle the - * case where none of the acceptable encodings cover all the - * textual input values. For now, we just extract the first - * element of the charset list - */ - while (*c && !ascii_is_space(*c)) { - if (*c == ',') - break; - c++; - } - - return strndup(form->accept_charsets, c - form->accept_charsets); -} - -/** - * Convert a string from UTF-8 to the specified charset - * As a final fallback, this will attempt to convert to ISO-8859-1. - * - * \todo Return charset used? - * - * \param item String to convert - * \param len Length of string to convert - * \param charset Destination charset - * \param fallback Fallback charset (may be NULL), - * used iff converting to charset fails - * \return Pointer to converted string (on heap, caller frees), or NULL - */ -char *form_encode_item(const char *item, uint32_t len, const char *charset, - const char *fallback) -{ - nserror err; - char *ret = NULL; - char cset[256]; - - if (!item || !charset) - return NULL; - - snprintf(cset, sizeof cset, "%s//TRANSLIT", charset); - - err = utf8_to_enc(item, cset, 0, &ret); - if (err == NSERROR_BAD_ENCODING) { - /* charset not understood, try without transliteration */ - snprintf(cset, sizeof cset, "%s", charset); - err = utf8_to_enc(item, cset, len, &ret); - - if (err == NSERROR_BAD_ENCODING) { - /* nope, try fallback charset (if any) */ - if (fallback) { - snprintf(cset, sizeof cset, - "%s//TRANSLIT", fallback); - err = utf8_to_enc(item, cset, 0, &ret); - - if (err == NSERROR_BAD_ENCODING) { - /* and without transliteration */ - snprintf(cset, sizeof cset, - "%s", fallback); - err = utf8_to_enc(item, cset, 0, &ret); - } - } - - if (err == NSERROR_BAD_ENCODING) { - /* that also failed, use 8859-1 */ - err = utf8_to_enc(item, "ISO-8859-1//TRANSLIT", - 0, &ret); - if (err == NSERROR_BAD_ENCODING) { - /* and without transliteration */ - err = utf8_to_enc(item, "ISO-8859-1", - 0, &ret); - } - } - } - } - if (err == NSERROR_NOMEM) { - return NULL; - } - - return ret; -} - -/* exported interface documented in render/form_internal.h */ -bool form_open_select_menu(void *client_data, - struct form_control *control, - select_menu_redraw_callback callback, - struct content *c) -{ - int line_height_with_spacing; - struct box *box; - plot_font_style_t fstyle; - int total_height; - struct form_select_menu *menu; - html_content *html = (html_content *)c; - - - /* if the menu is opened for the first time */ - if (control->data.select.menu == NULL) { - - menu = calloc(1, sizeof (struct form_select_menu)); - if (menu == NULL) { - guit->misc->warning("NoMemory", 0); - return false; - } - - control->data.select.menu = menu; - - box = control->box; - - menu->width = box->width + - box->border[RIGHT].width + - box->border[LEFT].width + - box->padding[RIGHT] + box->padding[LEFT]; - - font_plot_style_from_css(&html->len_ctx, control->box->style, - &fstyle); - menu->f_size = fstyle.size; - - menu->line_height = FIXTOINT(FDIV((FMUL(FLTTOFIX(1.2), - FMUL(nscss_screen_dpi, - INTTOFIX(fstyle.size / FONT_SIZE_SCALE)))), - F_72)); - - line_height_with_spacing = menu->line_height + - menu->line_height * - SELECT_LINE_SPACING; - - total_height = control->data.select.num_items * - line_height_with_spacing; - menu->height = total_height; - - if (menu->height > MAX_SELECT_HEIGHT) { - - menu->height = MAX_SELECT_HEIGHT; - } - menu->client_data = client_data; - menu->callback = callback; - if (scrollbar_create(false, - menu->height, - total_height, - menu->height, - control, - form_select_menu_scroll_callback, - &(menu->scrollbar)) != NSERROR_OK) { - free(menu); - return false; - } - menu->c = c; - } - else menu = control->data.select.menu; - - menu->callback(client_data, 0, 0, menu->width, menu->height); - - return true; -} - - -/* exported interface documented in render/form_internal.h */ -void form_free_select_menu(struct form_control *control) -{ - if (control->data.select.menu->scrollbar != NULL) - scrollbar_destroy(control->data.select.menu->scrollbar); - free(control->data.select.menu); - control->data.select.menu = NULL; -} - - -/* exported interface documented in render/form_internal.h */ -bool form_redraw_select_menu(struct form_control *control, int x, int y, - float scale, const struct rect *clip, - const struct redraw_context *ctx) -{ - struct box *box; - struct form_select_menu *menu = control->data.select.menu; - struct form_option *option; - int line_height, line_height_with_spacing; - int width, height; - int x0, y0, x1, scrollbar_x, y1, y2, y3; - int item_y; - int text_pos_offset, text_x; - int scrollbar_width = SCROLLBAR_WIDTH; - int i; - int scroll; - int x_cp, y_cp; - struct rect r; - struct rect rect; - nserror res; - - box = control->box; - - x_cp = x; - y_cp = y; - width = menu->width; - height = menu->height; - line_height = menu->line_height; - - line_height_with_spacing = line_height + - line_height * SELECT_LINE_SPACING; - scroll = scrollbar_get_offset(menu->scrollbar); - - if (scale != 1.0) { - x *= scale; - y *= scale; - width *= scale; - height *= scale; - scrollbar_width *= scale; - - i = scroll / line_height_with_spacing; - scroll -= i * line_height_with_spacing; - line_height *= scale; - line_height_with_spacing *= scale; - scroll *= scale; - scroll += i * line_height_with_spacing; - } - - - x0 = x; - y0 = y; - x1 = x + width - 1; - y1 = y + height - 1; - scrollbar_x = x1 - scrollbar_width; - - r.x0 = x0; - r.y0 = y0; - r.x1 = x1 + 1; - r.y1 = y1 + 1; - res = ctx->plot->clip(ctx, &r); - if (res != NSERROR_OK) { - return false; - } - - rect.x0 = x0; - rect.y0 = y0; - rect.x1 = x1; - rect.y1 = y1; - res = ctx->plot->rectangle(ctx, plot_style_stroke_darkwbasec, &rect); - if (res != NSERROR_OK) { - return false; - } - - x0 = x0 + SELECT_BORDER_WIDTH; - y0 = y0 + SELECT_BORDER_WIDTH; - x1 = x1 - SELECT_BORDER_WIDTH; - y1 = y1 - SELECT_BORDER_WIDTH; - height = height - 2 * SELECT_BORDER_WIDTH; - - r.x0 = x0; - r.y0 = y0; - r.x1 = x1 + 1; - r.y1 = y1 + 1; - res = ctx->plot->clip(ctx, &r); - if (res != NSERROR_OK) { - return false; - } - - res = ctx->plot->rectangle(ctx, plot_style_fill_lightwbasec, &r); - if (res != NSERROR_OK) { - return false; - } - - option = control->data.select.items; - item_y = line_height_with_spacing; - - while (item_y < scroll) { - option = option->next; - item_y += line_height_with_spacing; - } - item_y -= line_height_with_spacing; - text_pos_offset = y - scroll + - (int) (line_height * (0.75 + SELECT_LINE_SPACING)); - text_x = x + (box->border[LEFT].width + box->padding[LEFT]) * scale; - - plot_fstyle_entry.size = menu->f_size; - - while (option && item_y - scroll < height) { - - if (option->selected) { - y2 = y + item_y - scroll; - y3 = y + item_y + line_height_with_spacing - scroll; - - rect.x0 = x0; - rect.y0 = y0 > y2 ? y0 : y2; - rect.x1 = scrollbar_x + 1; - rect.y1 = y3 < y1 + 1 ? y3 : y1 + 1; - res = ctx->plot->rectangle(ctx, &plot_style_fill_selected, &rect); - if (res != NSERROR_OK) { - return false; - } - } - - y2 = text_pos_offset + item_y; - res = ctx->plot->text(ctx, - &plot_fstyle_entry, - text_x, y2, - option->text, strlen(option->text)); - if (res != NSERROR_OK) { - return false; - } - - item_y += line_height_with_spacing; - option = option->next; - } - - res = scrollbar_redraw(menu->scrollbar, - x_cp + menu->width - SCROLLBAR_WIDTH, - y_cp, - clip, scale, ctx); - if (res != NSERROR_OK) { - return false; - } - - return true; -} - -/** - * Check whether a clipping rectangle is completely contained in the - * select menu. - * - * \param control the select menu to check the clipping rectangle for - * \param scale the current browser window scale - * \param clip the clipping rectangle - * \return true if inside false otherwise - */ -bool form_clip_inside_select_menu(struct form_control *control, float scale, - const struct rect *clip) -{ - struct form_select_menu *menu = control->data.select.menu; - int width, height; - - - width = menu->width; - height = menu->height; - - if (scale != 1.0) { - width *= scale; - height *= scale; - } - - if (clip->x0 >= 0 && clip->x1 <= width && - clip->y0 >= 0 && clip->y1 <= height) - return true; - - return false; -} - - -/** - * Process a selection from a form select menu. - * - * \param html The html content handle for the form - * \param control form control with menu - * \param item index of item selected from the menu - * \return NSERROR_OK or appropriate error code. - */ -static nserror form__select_process_selection(html_content *html, - struct form_control *control, int item) -{ - struct box *inline_box; - struct form_option *o; - int count; - nserror ret = NSERROR_OK; - - assert(control != NULL); - assert(html != NULL); - - /** \todo Even though the form code is effectively part of the html - * content handler, poking around inside contents is not good - */ - - inline_box = control->box->children->children; - - for (count = 0, o = control->data.select.items; - o != NULL; - count++, o = o->next) { - if (!control->data.select.multiple && o->selected) { - o->selected = false; - dom_html_option_element_set_selected(o->node, false); - } - - if (count == item) { - if (control->data.select.multiple) { - if (o->selected) { - o->selected = false; - dom_html_option_element_set_selected( - o->node, false); - control->data.select.num_selected--; - } else { - o->selected = true; - dom_html_option_element_set_selected( - o->node, true); - control->data.select.num_selected++; - } - } else { - dom_html_option_element_set_selected( - o->node, true); - o->selected = true; - } - } - - if (o->selected) { - control->data.select.current = o; - } - } - - talloc_free(inline_box->text); - inline_box->text = 0; - - if (control->data.select.num_selected == 0) { - inline_box->text = talloc_strdup(html->bctx, - messages_get("Form_None")); - } else if (control->data.select.num_selected == 1) { - inline_box->text = talloc_strdup(html->bctx, - control->data.select.current->text); - } else { - inline_box->text = talloc_strdup(html->bctx, - messages_get("Form_Many")); - } - - if (!inline_box->text) { - ret = NSERROR_NOMEM; - inline_box->length = 0; - } else { - inline_box->length = strlen(inline_box->text); - } - inline_box->width = control->box->width; - - html__redraw_a_box(html, control->box); - - return ret; -} - -/* exported interface documented in netsurf/form.h */ -nserror form_select_process_selection(struct form_control *control, int item) -{ - assert(control != NULL); - - return form__select_process_selection(control->html, control, item); -} - -/* exported interface documented in netsurf/form.h */ -struct form_option * -form_select_get_option(struct form_control *control, int item) -{ - struct form_option *opt; - - opt = control->data.select.items; - while ((opt != NULL) && (item > 0)) { - opt = opt->next; - item--; - } - return opt; -} - -/* exported interface documented in netsurf/form.h */ -char *form_control_get_name(struct form_control *control) -{ - return control->name; -} - -/* exported interface documented in netsurf/form.h */ -nserror form_control_bounding_rect(struct form_control *control, struct rect *r) -{ - box_bounds( control->box, r ); - return NSERROR_OK; -} - - -/** - * Handle a click on the area of the currently opened select menu. - * - * \param control the select menu which received the click - * \param x X coordinate of click - * \param y Y coordinate of click - */ -void form_select_menu_clicked(struct form_control *control, int x, int y) -{ - struct form_select_menu *menu = control->data.select.menu; - struct form_option *option; - html_content *html = (html_content *)menu->c; - int line_height, line_height_with_spacing; - int item_bottom_y; - int scroll, i; - - scroll = scrollbar_get_offset(menu->scrollbar); - - line_height = menu->line_height; - line_height_with_spacing = line_height + - line_height * SELECT_LINE_SPACING; - - option = control->data.select.items; - item_bottom_y = line_height_with_spacing; - i = 0; - while (option && item_bottom_y < scroll + y) { - item_bottom_y += line_height_with_spacing; - option = option->next; - i++; - } - - if (option != NULL) { - form__select_process_selection(html, control, i); - } - - menu->callback(menu->client_data, 0, 0, menu->width, menu->height); -} - -/** - * Handle mouse action for the currently opened select menu. - * - * \param control the select menu which received the mouse action - * \param mouse current mouse state - * \param x X coordinate of click - * \param y Y coordinate of click - * \return text for the browser status bar or NULL if the menu has - * to be closed - */ -const char *form_select_mouse_action(struct form_control *control, - browser_mouse_state mouse, int x, int y) -{ - struct form_select_menu *menu = control->data.select.menu; - int x0, y0, x1, y1, scrollbar_x; - const char *status = NULL; - bool multiple = control->data.select.multiple; - - x0 = 0; - y0 = 0; - x1 = menu->width; - y1 = menu->height; - scrollbar_x = x1 - SCROLLBAR_WIDTH; - - if (menu->scroll_capture || - (x > scrollbar_x && x < x1 && y > y0 && y < y1)) { - /* The scroll is currently capturing all events or the mouse - * event is taking place on the scrollbar widget area - */ - x -= scrollbar_x; - return scrollbar_mouse_status_to_message( - scrollbar_mouse_action(menu->scrollbar, - mouse, x, y)); - } - - - if (x > x0 && x < scrollbar_x && y > y0 && y < y1) { - /* over option area */ - - if (mouse & (BROWSER_MOUSE_CLICK_1 | BROWSER_MOUSE_CLICK_2)) - /* button 1 or 2 click */ - form_select_menu_clicked(control, x, y); - - if (!(mouse & BROWSER_MOUSE_CLICK_1 && !multiple)) - /* anything but a button 1 click over a single select - menu */ - status = messages_get(control->data.select.multiple ? - "SelectMClick" : "SelectClick"); - - } else if (!(mouse & (BROWSER_MOUSE_CLICK_1 | BROWSER_MOUSE_CLICK_2))) - /* if not a button 1 or 2 click*/ - status = messages_get("SelectClose"); - - return status; -} - -/** - * Handle mouse drag end for the currently opened select menu. - * - * \param control the select menu which received the mouse drag end - * \param mouse current mouse state - * \param x X coordinate of drag end - * \param y Y coordinate of drag end - */ -void form_select_mouse_drag_end(struct form_control *control, - browser_mouse_state mouse, int x, int y) -{ - int x0, y0, x1, y1; - int box_x, box_y; - struct box *box; - struct form_select_menu *menu = control->data.select.menu; - - box = control->box; - - /* Get global coords of scrollbar */ - box_coords(box, &box_x, &box_y); - box_x -= box->border[LEFT].width; - box_y += box->height + box->border[BOTTOM].width + - box->padding[BOTTOM] + box->padding[TOP]; - - /* Get drag end coords relative to scrollbar */ - x = x - box_x; - y = y - box_y; - - if (menu->scroll_capture) { - x -= menu->width - SCROLLBAR_WIDTH; - scrollbar_mouse_drag_end(menu->scrollbar, mouse, x, y); - return; - } - - x0 = 0; - y0 = 0; - x1 = menu->width; - y1 = menu->height; - - - if (x > x0 && x < x1 - SCROLLBAR_WIDTH && y > y0 && y < y1) - /* handle drag end above the option area like a regular click */ - form_select_menu_clicked(control, x, y); -} - -/** - * Callback for the select menus scroll - */ -void form_select_menu_scroll_callback(void *client_data, - struct scrollbar_msg_data *scrollbar_data) -{ - struct form_control *control = client_data; - struct form_select_menu *menu = control->data.select.menu; - html_content *html = (html_content *)menu->c; - - switch (scrollbar_data->msg) { - case SCROLLBAR_MSG_MOVED: - menu->callback(menu->client_data, - 0, 0, - menu->width, - menu->height); - break; - case SCROLLBAR_MSG_SCROLL_START: - { - struct rect rect = { - .x0 = scrollbar_data->x0, - .y0 = scrollbar_data->y0, - .x1 = scrollbar_data->x1, - .y1 = scrollbar_data->y1 - }; - - browser_window_set_drag_type(html->bw, - DRAGGING_CONTENT_SCROLLBAR, &rect); - - menu->scroll_capture = true; - } - break; - case SCROLLBAR_MSG_SCROLL_FINISHED: - menu->scroll_capture = false; - - browser_window_set_drag_type(html->bw, - DRAGGING_NONE, NULL); - break; - default: - break; - } -} - -/** - * Get the dimensions of a select menu. - * - * \param control the select menu to get the dimensions of - * \param width gets updated to menu width - * \param height gets updated to menu height - */ -void form_select_get_dimensions(struct form_control *control, - int *width, int *height) -{ - *width = control->data.select.menu->width; - *height = control->data.select.menu->height; -} - -/** - * Callback for the core select menu. - */ -void form_select_menu_callback(void *client_data, - int x, int y, int width, int height) -{ - html_content *html = client_data; - int menu_x, menu_y; - struct box *box; - - box = html->visible_select_menu->box; - box_coords(box, &menu_x, &menu_y); - - menu_x -= box->border[LEFT].width; - menu_y += box->height + box->border[BOTTOM].width + - box->padding[BOTTOM] + - box->padding[TOP]; - content__request_redraw((struct content *)html, menu_x + x, menu_y + y, - width, height); -} - - -/** - * Set a radio form control and clear the others in the group. - * - * \param radio form control of type GADGET_RADIO - */ - -void form_radio_set(struct form_control *radio) -{ - struct form_control *control; - - assert(radio); - if (!radio->form) - return; - - if (radio->selected) - return; - - for (control = radio->form->controls; control; - control = control->next) { - if (control->type != GADGET_RADIO) - continue; - if (control == radio) - continue; - if (strcmp(control->name, radio->name) != 0) - continue; - - if (control->selected) { - control->selected = false; - dom_html_input_element_set_checked(control->node, false); - html__redraw_a_box(radio->html, control->box); - } - } - - radio->selected = true; - dom_html_input_element_set_checked(radio->node, true); - html__redraw_a_box(radio->html, radio->box); -} - - -/** - * Collect controls and submit a form. - */ - -void form_submit(nsurl *page_url, struct browser_window *target, - struct form *form, struct form_control *submit_button) -{ - char *data = NULL; - struct fetch_multipart_data *success; - nsurl *action_url; - nsurl *action_query; - nserror error; - - assert(form != NULL); - - if (form_successful_controls_dom(form, submit_button, &success) == false) { - guit->misc->warning("NoMemory", 0); - return; - } - - /* Decompose action */ - if (nsurl_create(form->action, &action_url) != NSERROR_OK) { - free(data); - fetch_multipart_data_destroy(success); - guit->misc->warning("NoMemory", 0); - return; - } - - switch (form->method) { - case method_GET: - data = form_url_encode(form, success, true); - if (data == NULL) { - fetch_multipart_data_destroy(success); - guit->misc->warning("NoMemory", 0); - return; - } - - /* Replace query segment */ - error = nsurl_replace_query(action_url, data, &action_query); - if (error != NSERROR_OK) { - nsurl_unref(action_query); - free(data); - fetch_multipart_data_destroy(success); - guit->misc->warning(messages_get_errorcode(error), 0); - return; - } - - /* Construct submit url */ - browser_window_navigate(target, - action_query, - page_url, - BW_NAVIGATE_HISTORY, - NULL, - NULL, - NULL); - - nsurl_unref(action_query); - break; - - case method_POST_URLENC: - data = form_url_encode(form, success, false); - if (data == NULL) { - fetch_multipart_data_destroy(success); - guit->misc->warning("NoMemory", 0); - nsurl_unref(action_url); - return; - } - - browser_window_navigate(target, - action_url, - page_url, - BW_NAVIGATE_HISTORY, - data, - NULL, - NULL); - break; - - case method_POST_MULTIPART: - browser_window_navigate(target, - action_url, - page_url, - BW_NAVIGATE_HISTORY, - NULL, - success, - NULL); - - break; - } - - nsurl_unref(action_url); - fetch_multipart_data_destroy(success); - free(data); -} - -void form_gadget_update_value(struct form_control *control, char *value) -{ - switch (control->type) { - case GADGET_HIDDEN: - case GADGET_TEXTBOX: - case GADGET_TEXTAREA: - case GADGET_PASSWORD: - case GADGET_FILE: - if (control->value != NULL) { - free(control->value); - } - control->value = value; - if (control->node != NULL) { - dom_exception err; - dom_string *str; - err = dom_string_create((uint8_t *)value, - strlen(value), &str); - if (err == DOM_NO_ERR) { - if (control->type == GADGET_TEXTAREA) - err = dom_html_text_area_element_set_value( - (dom_html_text_area_element *)(control->node), - str); - else - err = dom_html_input_element_set_value( - (dom_html_input_element *)(control->node), - str); - dom_string_unref(str); - } - } - break; - default: - /* Do nothing */ - break; - } -} diff --git a/render/form_internal.h b/render/form_internal.h deleted file mode 100644 index 0ffb6b46c..000000000 --- a/render/form_internal.h +++ /dev/null @@ -1,277 +0,0 @@ -/* - * Copyright 2014 Vincent Sanders <vince@netsurf-browser.org> - * - * 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 <http://www.gnu.org/licenses/>. - */ - -/** - * \file - * Interface to form handling functions internal to render. - */ - -#ifndef _NETSURF_RENDER_FORM_INTERNAL_H_ -#define _NETSURF_RENDER_FORM_INTERNAL_H_ - -#include <stdbool.h> - -#include "netsurf/form.h" - -struct box; -struct form_control; -struct form_option; -struct form_select_menu; -struct form; -struct html_content; -struct dom_string; -struct content; -struct nsurl; -struct fetch_multipart_data; -struct redraw_context; -struct browser_window; - -enum browser_mouse_state; - -/** Type of a struct form_control. */ -typedef enum { - GADGET_HIDDEN, - GADGET_TEXTBOX, - GADGET_RADIO, - GADGET_CHECKBOX, - GADGET_SELECT, - GADGET_TEXTAREA, - GADGET_IMAGE, - GADGET_PASSWORD, - GADGET_SUBMIT, - GADGET_RESET, - GADGET_FILE, - GADGET_BUTTON -} form_control_type; - -/** Data for textarea */ -struct form_textarea_data { - struct form_control *gadget; -}; - -struct image_input_coords { - int x; - int y; -}; - -/** Form control. */ -struct form_control { - void *node; /**< Corresponding DOM node */ - struct html_content *html; /**< HTML content containing control */ - - form_control_type type; /**< Type of control */ - - struct form *form; /**< Containing form */ - - char *name; /**< Control name */ - char *value; /**< Current value of control */ - char *initial_value; /**< Initial value of control */ - bool disabled; /**< Whether control is disabled */ - - struct box *box; /**< Box for control */ - - unsigned int length; /**< Number of characters in control */ - unsigned int maxlength; /**< Maximum characters permitted */ - - bool selected; /**< Whether control is selected */ - - union { - struct { - int mx, my; - } image; - struct { - int num_items; - struct form_option *items, *last_item; - bool multiple; - int num_selected; - /** Currently selected item, if num_selected == 1. */ - struct form_option *current; - struct form_select_menu *menu; - } select; - struct { - struct textarea *ta; - struct dom_string *initial; - struct form_textarea_data data; - } text; /**< input type=text or textarea */ - } data; - - struct form_control *prev; /**< Previous control in this form */ - struct form_control *next; /**< Next control in this form. */ -}; - -/** Form submit method. */ -typedef enum { - method_GET, /**< GET, always url encoded. */ - method_POST_URLENC, /**< POST, url encoded. */ - method_POST_MULTIPART /**< POST, multipart/form-data. */ -} form_method; - -/** HTML form. */ -struct form { - void *node; /**< Corresponding DOM node */ - - char *action; /**< Absolute URL to submit to. */ - char *target; /**< Target to submit to. */ - form_method method; /**< Method and enctype. */ - char *accept_charsets; /**< Charset to submit form in */ - char *document_charset; /**< Charset of document containing form */ - struct form_control *controls; /**< Linked list of controls. */ - struct form_control *last_control; /**< Last control in list. */ - - struct form *prev; /**< Previous form in doc. */ -}; - -/** - * Called by the select menu when it wants an area to be redrawn. The - * coordinates are menu origin relative. - * - * \param client_data data which was passed to form_open_select_menu - * \param x X coordinate of redraw rectangle - * \param y Y coordinate of redraw rectangle - * \param width width of redraw rectangle - * \param height height of redraw rectangle - */ -typedef void(*select_menu_redraw_callback)(void *client_data, - int x, int y, int width, int height); - -/** - * Create a struct form. - * - * \param node DOM node associated with form - * \param action URL to submit form to, or NULL for default - * \param target Target frame of form, or NULL for default - * \param method method and enctype - * \param charset acceptable encodings for form submission, or NULL - * \param doc_charset encoding of containing document, or NULL - * \return A new form or NULL on memory exhaustion - */ -struct form *form_new(void *node, const char *action, const char *target, - form_method method, const char *charset, - const char *doc_charset); - -/** - * Free a form and any controls it owns. - * - * \note There may exist controls attached to box tree nodes which are not - * associated with any form. These will leak at present. Ideally, they will - * be cleaned up when the box tree is destroyed. As that currently happens - * via talloc, this won't happen. These controls are distinguishable, as their - * form field will be NULL. - * - * \param form The form to free - */ -void form_free(struct form *form); - -/** - * Create a struct form_control. - * - * \param node Associated DOM node - * \param type control type - * \return a new structure, or NULL on memory exhaustion - */ -struct form_control *form_new_control(void *node, form_control_type type); - -void form_add_control(struct form *form, struct form_control *control); -void form_free_control(struct form_control *control); -bool form_add_option(struct form_control *control, char *value, char *text, - bool selected, void *node); -bool form_successful_controls(struct form *form, - struct form_control *submit_button, - struct fetch_multipart_data **successful_controls); - -/** - * Identify 'successful' controls via the DOM. - * - * All text strings in the successful controls list will be in the charset most - * appropriate for submission. Therefore, no utf8_to_* processing should be - * performed upon them. - * - * \todo The chosen charset needs to be made available such that it can be - * included in the submission request (e.g. in the fetch's Content-Type header) - * - * See HTML 4.01 section 17.13.2. - * - * \param[in] form form to search for successful controls - * \param[in] submit_button control used to submit the form, if any - * \param[out] successful_controls updated to point to linked list of - * fetch_multipart_data, 0 if no controls - * \return true on success, false on memory exhaustion - */ -bool form_successful_controls_dom(struct form *form, - struct form_control *submit_button, - struct fetch_multipart_data **successful_controls); - - -/** - * Open a select menu for a select form control, creating it if necessary. - * - * \param client_data data passed to the redraw callback - * \param control The select form control for which the menu is being opened - * \param redraw_callback The callback to redraw the select menu. - * \param c The content the select menu is opening for. - * \return false on memory exhaustion, true otherwise - */ -bool form_open_select_menu(void *client_data, - struct form_control *control, - select_menu_redraw_callback redraw_callback, - struct content *c); - - -void form_select_menu_callback(void *client_data, - int x, int y, int width, int height); - - -/** - * Destroy a select menu and free allocated memory. - * - * \param control the select form control owning the select menu being - * destroyed. - */ -void form_free_select_menu(struct form_control *control); - - -/** - * Redraw an opened select menu. - * - * \param control the select menu being redrawn - * \param x the X coordinate to draw the menu at - * \param y the Y coordinate to draw the menu at - * \param scale current redraw scale - * \param clip clipping rectangle - * \param ctx current redraw context - * \return true on success, false otherwise - */ -bool form_redraw_select_menu(struct form_control *control, int x, int y, - float scale, const struct rect *clip, - const struct redraw_context *ctx); - -bool form_clip_inside_select_menu(struct form_control *control, float scale, - const struct rect *clip); -const char *form_select_mouse_action(struct form_control *control, - enum browser_mouse_state mouse, int x, int y); -void form_select_mouse_drag_end(struct form_control *control, - enum browser_mouse_state mouse, int x, int y); -void form_select_get_dimensions(struct form_control *control, - int *width, int *height); -void form_submit(struct nsurl *page_url, struct browser_window *target, - struct form *form, struct form_control *submit_button); -void form_radio_set(struct form_control *radio); - -void form_gadget_update_value(struct form_control *control, char *value); - -#endif diff --git a/render/html.c b/render/html.c deleted file mode 100644 index b7d7aa313..000000000 --- a/render/html.c +++ /dev/null @@ -1,2467 +0,0 @@ -/* - * Copyright 2007 James Bursa <bursa@users.sourceforge.net> - * Copyright 2010 Michael Drake <tlsa@netsurf-browser.org> - * - * 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 <http://www.gnu.org/licenses/>. - */ - -/** - * \file - * Content for text/html (implementation). - */ - -#include <assert.h> -#include <stdint.h> -#include <string.h> -#include <strings.h> -#include <stdlib.h> -#include <nsutils/time.h> - -#include "utils/utils.h" -#include "utils/config.h" -#include "utils/corestrings.h" -#include "utils/http.h" -#include "utils/libdom.h" -#include "utils/log.h" -#include "utils/messages.h" -#include "utils/talloc.h" -#include "utils/utf8.h" -#include "utils/nsoption.h" -#include "utils/string.h" -#include "utils/ascii.h" -#include "netsurf/content.h" -#include "netsurf/browser_window.h" -#include "netsurf/utf8.h" -#include "netsurf/layout.h" -#include "netsurf/misc.h" -#include "content/hlcache.h" -#include "desktop/selection.h" -#include "desktop/scrollbar.h" -#include "desktop/textarea.h" -#include "netsurf/bitmap.h" -#include "javascript/js.h" -#include "desktop/gui_internal.h" - -#include "render/box.h" -#include "render/form_internal.h" -#include "render/html_internal.h" -#include "render/imagemap.h" -#include "render/layout.h" -#include "render/search.h" - -#define CHUNK 4096 - -/* Change these to 1 to cause a dump to stderr of the frameset or box - * when the trees have been built. - */ -#define ALWAYS_DUMP_FRAMESET 0 -#define ALWAYS_DUMP_BOX 0 - -static const char *html_types[] = { - "application/xhtml+xml", - "text/html" -}; - -/* Exported interface, see html_internal.h */ -bool fire_dom_event(dom_string *type, dom_node *target, - bool bubbles, bool cancelable) -{ - dom_exception exc; - dom_event *evt; - bool result; - - exc = dom_event_create(&evt); - if (exc != DOM_NO_ERR) return false; - exc = dom_event_init(evt, type, bubbles, cancelable); - if (exc != DOM_NO_ERR) { - dom_event_unref(evt); - return false; - } - NSLOG(netsurf, INFO, "Dispatching '%*s' against %p", - dom_string_length(type), dom_string_data(type), target); - exc = dom_event_target_dispatch_event(target, evt, &result); - if (exc != DOM_NO_ERR) { - result = false; - } - dom_event_unref(evt); - return result; -} - -/** - * Perform post-box-creation conversion of a document - * - * \param c HTML content to complete conversion of - * \param success Whether box tree construction was successful - */ -static void html_box_convert_done(html_content *c, bool success) -{ - nserror err; - dom_exception exc; /* returned by libdom functions */ - dom_node *html; - - NSLOG(netsurf, INFO, "Done XML to box (%p)", c); - - /* Clean up and report error if unsuccessful or aborted */ - if ((success == false) || (c->aborted)) { - html_object_free_objects(c); - - if (success == false) { - content_broadcast_errorcode(&c->base, NSERROR_BOX_CONVERT); - } else { - content_broadcast_errorcode(&c->base, NSERROR_STOPPED); - } - - content_set_error(&c->base); - return; - } - - -#if ALWAYS_DUMP_BOX - box_dump(stderr, c->layout->children, 0, true); -#endif -#if ALWAYS_DUMP_FRAMESET - if (c->frameset) - html_dump_frameset(c->frameset, 0); -#endif - - exc = dom_document_get_document_element(c->document, (void *) &html); - if ((exc != DOM_NO_ERR) || (html == NULL)) { - /** @todo should this call html_object_free_objects(c); - * like the other error paths - */ - NSLOG(netsurf, INFO, "error retrieving html element from dom"); - content_broadcast_errorcode(&c->base, NSERROR_DOM); - content_set_error(&c->base); - return; - } - - /* extract image maps - can't do this sensibly in dom_to_box */ - err = imagemap_extract(c); - if (err != NSERROR_OK) { - NSLOG(netsurf, INFO, "imagemap extraction failed"); - html_object_free_objects(c); - content_broadcast_errorcode(&c->base, err); - content_set_error(&c->base); - dom_node_unref(html); - return; - } - /*imagemap_dump(c);*/ - - /* Destroy the parser binding */ - dom_hubbub_parser_destroy(c->parser); - c->parser = NULL; - - content_set_ready(&c->base); - - if (c->base.active == 0) { - content_set_done(&c->base); - } - - dom_node_unref(html); -} - - -/** process link node */ -static bool html_process_link(html_content *c, dom_node *node) -{ - struct content_rfc5988_link link; /* the link added to the content */ - dom_exception exc; /* returned by libdom functions */ - dom_string *atr_string; - nserror error; - - memset(&link, 0, sizeof(struct content_rfc5988_link)); - - /* check that the relation exists - w3c spec says must be present */ - exc = dom_element_get_attribute(node, corestring_dom_rel, &atr_string); - if ((exc != DOM_NO_ERR) || (atr_string == NULL)) { - return false; - } - /* get a lwc string containing the link relation */ - exc = dom_string_intern(atr_string, &link.rel); - dom_string_unref(atr_string); - if (exc != DOM_NO_ERR) { - return false; - } - - /* check that the href exists - w3c spec says must be present */ - exc = dom_element_get_attribute(node, corestring_dom_href, &atr_string); - if ((exc != DOM_NO_ERR) || (atr_string == NULL)) { - lwc_string_unref(link.rel); - return false; - } - - /* get nsurl */ - error = nsurl_join(c->base_url, dom_string_data(atr_string), - &link.href); - dom_string_unref(atr_string); - if (error != NSERROR_OK) { - lwc_string_unref(link.rel); - return false; - } - - /* look for optional properties -- we don't care if internment fails */ - - exc = dom_element_get_attribute(node, - corestring_dom_hreflang, &atr_string); - if ((exc == DOM_NO_ERR) && (atr_string != NULL)) { - /* get a lwc string containing the href lang */ - exc = dom_string_intern(atr_string, &link.hreflang); - dom_string_unref(atr_string); - } - - exc = dom_element_get_attribute(node, - corestring_dom_type, &atr_string); - if ((exc == DOM_NO_ERR) && (atr_string != NULL)) { - /* get a lwc string containing the type */ - exc = dom_string_intern(atr_string, &link.type); - dom_string_unref(atr_string); - } - - exc = dom_element_get_attribute(node, - corestring_dom_media, &atr_string); - if ((exc == DOM_NO_ERR) && (atr_string != NULL)) { - /* get a lwc string containing the media */ - exc = dom_string_intern(atr_string, &link.media); - dom_string_unref(atr_string); - } - - exc = dom_element_get_attribute(node, - corestring_dom_sizes, &atr_string); - if ((exc == DOM_NO_ERR) && (atr_string != NULL)) { - /* get a lwc string containing the sizes */ - exc = dom_string_intern(atr_string, &link.sizes); - dom_string_unref(atr_string); - } - - /* add to content */ - content__add_rfc5988_link(&c->base, &link); - - if (link.sizes != NULL) - lwc_string_unref(link.sizes); - if (link.media != NULL) - lwc_string_unref(link.media); - if (link.type != NULL) - lwc_string_unref(link.type); - if (link.hreflang != NULL) - lwc_string_unref(link.hreflang); - - nsurl_unref(link.href); - lwc_string_unref(link.rel); - - return true; -} - -/** process title node */ -static bool html_process_title(html_content *c, dom_node *node) -{ - dom_exception exc; /* returned by libdom functions */ - dom_string *title; - char *title_str; - bool success; - - exc = dom_node_get_text_content(node, &title); - if ((exc != DOM_NO_ERR) || (title == NULL)) { - return false; - } - - title_str = squash_whitespace(dom_string_data(title)); - dom_string_unref(title); - - if (title_str == NULL) { - return false; - } - - success = content__set_title(&c->base, title_str); - - free(title_str); - - return success; -} - -static bool html_process_base(html_content *c, dom_node *node) -{ - dom_exception exc; /* returned by libdom functions */ - dom_string *atr_string; - - /* get href attribute if present */ - exc = dom_element_get_attribute(node, - corestring_dom_href, &atr_string); - if ((exc == DOM_NO_ERR) && (atr_string != NULL)) { - nsurl *url; - nserror error; - - /* get url from string */ - error = nsurl_create(dom_string_data(atr_string), &url); - dom_string_unref(atr_string); - if (error == NSERROR_OK) { - if (c->base_url != NULL) - nsurl_unref(c->base_url); - c->base_url = url; - } - } - - - /* get target attribute if present and not already set */ - if (c->base_target != NULL) { - return true; - } - - exc = dom_element_get_attribute(node, - corestring_dom_target, &atr_string); - if ((exc == DOM_NO_ERR) && (atr_string != NULL)) { - /* Validation rules from the HTML5 spec for the base element: - * The target must be one of _blank, _self, _parent, or - * _top or any identifier which does not begin with an - * underscore - */ - if (*dom_string_data(atr_string) != '_' || - dom_string_caseless_lwc_isequal(atr_string, - corestring_lwc__blank) || - dom_string_caseless_lwc_isequal(atr_string, - corestring_lwc__self) || - dom_string_caseless_lwc_isequal(atr_string, - corestring_lwc__parent) || - dom_string_caseless_lwc_isequal(atr_string, - corestring_lwc__top)) { - c->base_target = strdup(dom_string_data(atr_string)); - } - dom_string_unref(atr_string); - } - - return true; -} - -static nserror html_meta_refresh_process_element(html_content *c, dom_node *n) -{ - union content_msg_data msg_data; - const char *url, *end, *refresh = NULL; - char *new_url; - char quote = '\0'; - dom_string *equiv, *content; - dom_exception exc; - nsurl *nsurl; - nserror error = NSERROR_OK; - - exc = dom_element_get_attribute(n, corestring_dom_http_equiv, &equiv); - if (exc != DOM_NO_ERR) { - return NSERROR_DOM; - } - - if (equiv == NULL) { - return NSERROR_OK; - } - - if (!dom_string_caseless_lwc_isequal(equiv, corestring_lwc_refresh)) { - dom_string_unref(equiv); - return NSERROR_OK; - } - - dom_string_unref(equiv); - - exc = dom_element_get_attribute(n, corestring_dom_content, &content); - if (exc != DOM_NO_ERR) { - return NSERROR_DOM; - } - - if (content == NULL) { - return NSERROR_OK; - } - - end = dom_string_data(content) + dom_string_byte_length(content); - - /* content := *LWS intpart fracpart? *LWS [';' *LWS *1url *LWS] - * intpart := 1*DIGIT - * fracpart := 1*('.' | DIGIT) - * url := "url" *LWS '=' *LWS (url-nq | url-sq | url-dq) - * url-nq := *urlchar - * url-sq := "'" *(urlchar | '"') "'" - * url-dq := '"' *(urlchar | "'") '"' - * urlchar := [#x9#x21#x23-#x26#x28-#x7E] | nonascii - * nonascii := [#x80-#xD7FF#xE000-#xFFFD#x10000-#x10FFFF] - */ - - url = dom_string_data(content); - - /* *LWS */ - while (url < end && ascii_is_space(*url)) { - url++; - } - - /* intpart */ - if (url == end || (*url < '0' || '9' < *url)) { - /* Empty content, or invalid timeval */ - dom_string_unref(content); - return NSERROR_OK; - } - - msg_data.delay = (int) strtol(url, &new_url, 10); - /* a very small delay and self-referencing URL can cause a loop - * that grinds machines to a halt. To prevent this we set a - * minimum refresh delay of 1s. */ - if (msg_data.delay < 1) { - msg_data.delay = 1; - } - - url = new_url; - - /* fracpart? (ignored, as delay is integer only) */ - while (url < end && (('0' <= *url && *url <= '9') || - *url == '.')) { - url++; - } - - /* *LWS */ - while (url < end && ascii_is_space(*url)) { - url++; - } - - /* ';' */ - if (url < end && *url == ';') - url++; - - /* *LWS */ - while (url < end && ascii_is_space(*url)) { - url++; - } - - if (url == end) { - /* Just delay specified, so refresh current page */ - dom_string_unref(content); - - c->base.refresh = nsurl_ref( - content_get_url(&c->base)); - - content_broadcast(&c->base, CONTENT_MSG_REFRESH, &msg_data); - - return NSERROR_OK; - } - - /* "url" */ - if (url <= end - 3) { - if (strncasecmp(url, "url", 3) == 0) { - url += 3; - } else { - /* Unexpected input, ignore this header */ - dom_string_unref(content); - return NSERROR_OK; - } - } else { - /* Insufficient input, ignore this header */ - dom_string_unref(content); - return NSERROR_OK; - } - - /* *LWS */ - while (url < end && ascii_is_space(*url)) { - url++; - } - - /* '=' */ - if (url < end) { - if (*url == '=') { - url++; - } else { - /* Unexpected input, ignore this header */ - dom_string_unref(content); - return NSERROR_OK; - } - } else { - /* Insufficient input, ignore this header */ - dom_string_unref(content); - return NSERROR_OK; - } - - /* *LWS */ - while (url < end && ascii_is_space(*url)) { - url++; - } - - /* '"' or "'" */ - if (url < end && (*url == '"' || *url == '\'')) { - quote = *url; - url++; - } - - /* Start of URL */ - refresh = url; - - if (quote != 0) { - /* url-sq | url-dq */ - while (url < end && *url != quote) - url++; - } else { - /* url-nq */ - while (url < end && !ascii_is_space(*url)) - url++; - } - - /* '"' or "'" or *LWS (we don't care) */ - if (url > refresh) { - /* There's a URL */ - new_url = strndup(refresh, url - refresh); - if (new_url == NULL) { - dom_string_unref(content); - return NSERROR_NOMEM; - } - - error = nsurl_join(c->base_url, new_url, &nsurl); - if (error == NSERROR_OK) { - /* broadcast valid refresh url */ - - c->base.refresh = nsurl; - - content_broadcast(&c->base, CONTENT_MSG_REFRESH, - &msg_data); - c->refresh = true; - } - - free(new_url); - - } - - dom_string_unref(content); - - return error; -} - -static bool html_process_img(html_content *c, dom_node *node) -{ - dom_string *src; - nsurl *url; - nserror err; - dom_exception exc; - bool success; - - /* Do nothing if foreground images are disabled */ - if (nsoption_bool(foreground_images) == false) { - return true; - } - - exc = dom_element_get_attribute(node, corestring_dom_src, &src); - if (exc != DOM_NO_ERR || src == NULL) { - return true; - } - - err = nsurl_join(c->base_url, dom_string_data(src), &url); - if (err != NSERROR_OK) { - dom_string_unref(src); - return false; - } - dom_string_unref(src); - - /* Speculatively fetch the image */ - success = html_fetch_object(c, url, NULL, CONTENT_IMAGE, 0, 0, false); - nsurl_unref(url); - - return success; -} - -/* exported function documented in render/html_internal.h */ -void html_finish_conversion(html_content *htmlc) -{ - union content_msg_data msg_data; - dom_exception exc; /* returned by libdom functions */ - dom_node *html; - nserror error; - - /* Bail out if we've been aborted */ - if (htmlc->aborted) { - content_broadcast_errorcode(&htmlc->base, NSERROR_STOPPED); - content_set_error(&htmlc->base); - return; - } - - /* create new css selection context */ - error = html_css_new_selection_context(htmlc, &htmlc->select_ctx); - if (error != NSERROR_OK) { - content_broadcast_errorcode(&htmlc->base, error); - content_set_error(&htmlc->base); - return; - } - - - /* fire a simple event named load at the Document's Window - * object, but with its target set to the Document object (and - * the currentTarget set to the Window object) - */ - if (htmlc->jscontext != NULL) { - js_fire_event(htmlc->jscontext, "load", htmlc->document, NULL); - } - - /* convert dom tree to box tree */ - NSLOG(netsurf, INFO, "DOM to box (%p)", htmlc); - content_set_status(&htmlc->base, messages_get("Processing")); - msg_data.explicit_status_text = NULL; - content_broadcast(&htmlc->base, CONTENT_MSG_STATUS, &msg_data); - - exc = dom_document_get_document_element(htmlc->document, (void *) &html); - if ((exc != DOM_NO_ERR) || (html == NULL)) { - NSLOG(netsurf, INFO, "error retrieving html element from dom"); - content_broadcast_errorcode(&htmlc->base, NSERROR_DOM); - content_set_error(&htmlc->base); - return; - } - - error = dom_to_box(html, htmlc, html_box_convert_done); - if (error != NSERROR_OK) { - NSLOG(netsurf, INFO, "box conversion failed"); - dom_node_unref(html); - html_object_free_objects(htmlc); - content_broadcast_errorcode(&htmlc->base, error); - content_set_error(&htmlc->base); - return; - } - - dom_node_unref(html); -} - -/* callback for DOMNodeInserted end type */ -static void -dom_default_action_DOMNodeInserted_cb(struct dom_event *evt, void *pw) -{ - dom_event_target *node; - dom_node_type type; - dom_exception exc; - html_content *htmlc = pw; - - exc = dom_event_get_target(evt, &node); - if ((exc == DOM_NO_ERR) && (node != NULL)) { - exc = dom_node_get_node_type(node, &type); - if ((exc == DOM_NO_ERR) && (type == DOM_ELEMENT_NODE)) { - /* an element node has been inserted */ - dom_html_element_type tag_type; - - exc = dom_html_element_get_tag_type(node, &tag_type); - if (exc != DOM_NO_ERR) { - tag_type = DOM_HTML_ELEMENT_TYPE__UNKNOWN; - } - - switch (tag_type) { - case DOM_HTML_ELEMENT_TYPE_LINK: - /* Handle stylesheet loading */ - html_css_process_link(htmlc, (dom_node *)node); - /* Generic link handling */ - html_process_link(htmlc, (dom_node *)node); - break; - case DOM_HTML_ELEMENT_TYPE_META: - if (htmlc->refresh) - break; - html_meta_refresh_process_element(htmlc, - (dom_node *)node); - break; - case DOM_HTML_ELEMENT_TYPE_TITLE: - if (htmlc->title != NULL) - break; - htmlc->title = dom_node_ref(node); - break; - case DOM_HTML_ELEMENT_TYPE_BASE: - html_process_base(htmlc, (dom_node *)node); - break; - case DOM_HTML_ELEMENT_TYPE_IMG: - html_process_img(htmlc, (dom_node *) node); - break; - case DOM_HTML_ELEMENT_TYPE_STYLE: - html_css_process_style(htmlc, (dom_node *) node); - break; - default: - break; - } - if (htmlc->enable_scripting) { - /* ensure javascript context is available */ - if (htmlc->jscontext == NULL) { - union content_msg_data msg_data; - - msg_data.jscontext = &htmlc->jscontext; - content_broadcast(&htmlc->base, - CONTENT_MSG_GETCTX, - &msg_data); - NSLOG(netsurf, INFO, - "javascript context: %p (htmlc: %p)", - htmlc->jscontext, - htmlc); - } - if (htmlc->jscontext != NULL) { - js_handle_new_element(htmlc->jscontext, - (dom_element *) node); - } - } - } - dom_node_unref(node); - } -} - -/* callback for DOMNodeInserted end type */ -static void -dom_default_action_DOMSubtreeModified_cb(struct dom_event *evt, void *pw) -{ - dom_event_target *node; - dom_node_type type; - dom_exception exc; - html_content *htmlc = pw; - - exc = dom_event_get_target(evt, &node); - if ((exc == DOM_NO_ERR) && (node != NULL)) { - if (htmlc->title == (dom_node *)node) { - /* Node is our title node */ - html_process_title(htmlc, (dom_node *)node); - dom_node_unref(node); - return; - } - - exc = dom_node_get_node_type(node, &type); - if ((exc == DOM_NO_ERR) && (type == DOM_ELEMENT_NODE)) { - /* an element node has been modified */ - dom_html_element_type tag_type; - - exc = dom_html_element_get_tag_type(node, &tag_type); - if (exc != DOM_NO_ERR) { - tag_type = DOM_HTML_ELEMENT_TYPE__UNKNOWN; - } - - switch (tag_type) { - case DOM_HTML_ELEMENT_TYPE_STYLE: - html_css_update_style(htmlc, (dom_node *)node); - break; - default: - break; - } - } - dom_node_unref(node); - } -} - -static void -dom_default_action_finished_cb(struct dom_event *evt, void *pw) -{ - html_content *htmlc = pw; - - if (htmlc->jscontext != NULL) - js_event_cleanup(htmlc->jscontext, evt); -} - -/* callback function selector - * - * selects a callback function for libdom to call based on the type and phase. - * dom_default_action_phase from events/document_event.h - * - * The principle events are: - * DOMSubtreeModified - * DOMAttrModified - * DOMNodeInserted - * DOMNodeInsertedIntoDocument - * - * @return callback function pointer or NULL for none - */ -static dom_default_action_callback -dom_event_fetcher(dom_string *type, - dom_default_action_phase phase, - void **pw) -{ - NSLOG(netsurf, DEEPDEBUG, "type:%s", dom_string_data(type)); - - if (phase == DOM_DEFAULT_ACTION_END) { - if (dom_string_isequal(type, corestring_dom_DOMNodeInserted)) { - return dom_default_action_DOMNodeInserted_cb; - } else if (dom_string_isequal(type, corestring_dom_DOMSubtreeModified)) { - return dom_default_action_DOMSubtreeModified_cb; - } - } else if (phase == DOM_DEFAULT_ACTION_FINISHED) { - return dom_default_action_finished_cb; - } - return NULL; -} - -static void -html_document_user_data_handler(dom_node_operation operation, - dom_string *key, void *data, - struct dom_node *src, - struct dom_node *dst) -{ - if (dom_string_isequal(corestring_dom___ns_key_html_content_data, - key) == false || data == NULL) { - return; - } - - switch (operation) { - case DOM_NODE_CLONED: - NSLOG(netsurf, INFO, "Cloned"); - break; - case DOM_NODE_RENAMED: - NSLOG(netsurf, INFO, "Renamed"); - break; - case DOM_NODE_IMPORTED: - NSLOG(netsurf, INFO, "imported"); - break; - case DOM_NODE_ADOPTED: - NSLOG(netsurf, INFO, "Adopted"); - break; - case DOM_NODE_DELETED: - /* This is the only path I expect */ - break; - default: - NSLOG(netsurf, INFO, "User data operation not handled."); - assert(0); - } -} - - -static nserror -html_create_html_data(html_content *c, const http_parameter *params) -{ - lwc_string *charset; - nserror nerror; - dom_hubbub_parser_params parse_params; - dom_hubbub_error error; - dom_exception err; - void *old_node_data; - - c->parser = NULL; - c->parse_completed = false; - c->document = NULL; - c->quirks = DOM_DOCUMENT_QUIRKS_MODE_NONE; - c->encoding = NULL; - c->base_url = nsurl_ref(content_get_url(&c->base)); - c->base_target = NULL; - c->aborted = false; - c->refresh = false; - c->reflowing = false; - c->title = NULL; - c->bctx = NULL; - c->layout = NULL; - c->background_colour = NS_TRANSPARENT; - c->stylesheet_count = 0; - c->stylesheets = NULL; - c->select_ctx = NULL; - c->universal = NULL; - c->num_objects = 0; - c->object_list = NULL; - c->forms = NULL; - c->imagemaps = NULL; - c->bw = NULL; - c->frameset = NULL; - c->iframe = NULL; - c->page = NULL; - c->font_func = guit->layout; - c->drag_type = HTML_DRAG_NONE; - c->drag_owner.no_owner = true; - c->selection_type = HTML_SELECTION_NONE; - c->selection_owner.none = true; - c->focus_type = HTML_FOCUS_SELF; - c->focus_owner.self = true; - c->search = NULL; - c->search_string = NULL; - c->scripts_count = 0; - c->scripts = NULL; - c->jscontext = NULL; - - c->enable_scripting = nsoption_bool(enable_javascript); - c->base.active = 1; /* The html content itself is active */ - - if (lwc_intern_string("*", SLEN("*"), &c->universal) != lwc_error_ok) { - return NSERROR_NOMEM; - } - - selection_prepare(&c->sel, (struct content *)c, true); - - nerror = http_parameter_list_find_item(params, corestring_lwc_charset, &charset); - if (nerror == NSERROR_OK) { - c->encoding = strdup(lwc_string_data(charset)); - - lwc_string_unref(charset); - - if (c->encoding == NULL) { - lwc_string_unref(c->universal); - c->universal = NULL; - return NSERROR_NOMEM; - - } - c->encoding_source = DOM_HUBBUB_ENCODING_SOURCE_HEADER; - } - - /* Create the parser binding */ - parse_params.enc = c->encoding; - parse_params.fix_enc = true; - parse_params.enable_script = c->enable_scripting; - parse_params.msg = NULL; - parse_params.script = html_process_script; - parse_params.ctx = c; - parse_params.daf = dom_event_fetcher; - - error = dom_hubbub_parser_create(&parse_params, - &c->parser, - &c->document); - if ((error != DOM_HUBBUB_OK) && (c->encoding != NULL)) { - /* Ok, we don't support the declared encoding. Bailing out - * isn't exactly user-friendly, so fall back to autodetect */ - free(c->encoding); - c->encoding = NULL; - - parse_params.enc = c->encoding; - - error = dom_hubbub_parser_create(&parse_params, - &c->parser, - &c->document); - } - if (error != DOM_HUBBUB_OK) { - nsurl_unref(c->base_url); - c->base_url = NULL; - - lwc_string_unref(c->universal); - c->universal = NULL; - - return libdom_hubbub_error_to_nserror(error); - } - - err = dom_node_set_user_data(c->document, - corestring_dom___ns_key_html_content_data, - c, html_document_user_data_handler, - (void *) &old_node_data); - if (err != DOM_NO_ERR) { - dom_hubbub_parser_destroy(c->parser); - nsurl_unref(c->base_url); - c->base_url = NULL; - - lwc_string_unref(c->universal); - c->universal = NULL; - - NSLOG(netsurf, INFO, "Unable to set user data."); - return NSERROR_DOM; - } - - assert(old_node_data == NULL); - - return NSERROR_OK; - -} - -/** - * Create a CONTENT_HTML. - * - * The content_html_data structure is initialized and the HTML parser is - * created. - */ - -static nserror -html_create(const content_handler *handler, - lwc_string *imime_type, - const http_parameter *params, - llcache_handle *llcache, - const char *fallback_charset, - bool quirks, - struct content **c) -{ - html_content *html; - nserror error; - - html = calloc(1, sizeof(html_content)); - if (html == NULL) - return NSERROR_NOMEM; - - error = content__init(&html->base, handler, imime_type, params, - llcache, fallback_charset, quirks); - if (error != NSERROR_OK) { - free(html); - return error; - } - - error = html_create_html_data(html, params); - if (error != NSERROR_OK) { - content_broadcast_errorcode(&html->base, error); - free(html); - return error; - } - - error = html_css_new_stylesheets(html); - if (error != NSERROR_OK) { - content_broadcast_errorcode(&html->base, error); - free(html); - return error; - } - - *c = (struct content *) html; - - return NSERROR_OK; -} - - - -static nserror -html_process_encoding_change(struct content *c, - const char *data, - unsigned int size) -{ - html_content *html = (html_content *) c; - dom_hubbub_parser_params parse_params; - dom_hubbub_error error; - const char *encoding; - const char *source_data; - unsigned long source_size; - - /* Retrieve new encoding */ - encoding = dom_hubbub_parser_get_encoding(html->parser, - &html->encoding_source); - if (encoding == NULL) { - return NSERROR_NOMEM; - } - - if (html->encoding != NULL) { - free(html->encoding); - html->encoding = NULL; - } - - html->encoding = strdup(encoding); - if (html->encoding == NULL) { - return NSERROR_NOMEM; - } - - /* Destroy binding */ - dom_hubbub_parser_destroy(html->parser); - html->parser = NULL; - - if (html->document != NULL) { - dom_node_unref(html->document); - } - - parse_params.enc = html->encoding; - parse_params.fix_enc = true; - parse_params.enable_script = html->enable_scripting; - parse_params.msg = NULL; - parse_params.script = html_process_script; - parse_params.ctx = html; - parse_params.daf = dom_event_fetcher; - - /* Create new binding, using the new encoding */ - error = dom_hubbub_parser_create(&parse_params, - &html->parser, - &html->document); - if (error != DOM_HUBBUB_OK) { - /* Ok, we don't support the declared encoding. Bailing out - * isn't exactly user-friendly, so fall back to Windows-1252 */ - free(html->encoding); - html->encoding = strdup("Windows-1252"); - if (html->encoding == NULL) { - return NSERROR_NOMEM; - } - parse_params.enc = html->encoding; - - error = dom_hubbub_parser_create(&parse_params, - &html->parser, - &html->document); - - if (error != DOM_HUBBUB_OK) { - return libdom_hubbub_error_to_nserror(error); - } - - } - - source_data = content__get_source_data(c, &source_size); - - /* Reprocess all the data. This is safe because - * the encoding is now specified at parser start which means - * it cannot be changed again. - */ - error = dom_hubbub_parser_parse_chunk(html->parser, - (const uint8_t *)source_data, - source_size); - - return libdom_hubbub_error_to_nserror(error); -} - - -/** - * Process data for CONTENT_HTML. - */ - -static bool -html_process_data(struct content *c, const char *data, unsigned int size) -{ - html_content *html = (html_content *) c; - dom_hubbub_error dom_ret; - nserror err = NSERROR_OK; /* assume its all going to be ok */ - - dom_ret = dom_hubbub_parser_parse_chunk(html->parser, - (const uint8_t *) data, - size); - - err = libdom_hubbub_error_to_nserror(dom_ret); - - /* deal with encoding change */ - if (err == NSERROR_ENCODING_CHANGE) { - err = html_process_encoding_change(c, data, size); - } - - /* broadcast the error if necessary */ - if (err != NSERROR_OK) { - content_broadcast_errorcode(c, err); - return false; - } - - return true; -} - - -/** - * Convert a CONTENT_HTML for display. - * - * The following steps are carried out in order: - * - * - parsing to an XML tree is completed - * - stylesheets are fetched - * - the XML tree is converted to a box tree and object fetches are started - * - * On exit, the content status will be either CONTENT_STATUS_DONE if the - * document is completely loaded or CONTENT_STATUS_READY if objects are still - * being fetched. - */ - -static bool html_convert(struct content *c) -{ - html_content *htmlc = (html_content *) c; - dom_exception exc; /* returned by libdom functions */ - - /* The quirk check and associated stylesheet fetch is "safe" - * once the root node has been inserted into the document - * which must have happened by this point in the parse. - * - * faliure to retrive the quirk mode or to start the - * stylesheet fetch is non fatal as this "only" affects the - * render and it would annoy the user to fail the entire - * render for want of a quirks stylesheet. - */ - exc = dom_document_get_quirks_mode(htmlc->document, &htmlc->quirks); - if (exc == DOM_NO_ERR) { - html_css_quirks_stylesheets(htmlc); - NSLOG(netsurf, INFO, "quirks set to %d", htmlc->quirks); - } - - htmlc->base.active--; /* the html fetch is no longer active */ - NSLOG(netsurf, INFO, "%d fetches active (%p)", htmlc->base.active, c); - - /* The parse cannot be completed here because it may be paused - * untill all the resources being fetched have completed. - */ - - /* if there are no active fetches in progress no scripts are - * being fetched or they completed already. - */ - if (html_can_begin_conversion(htmlc)) { - return html_begin_conversion(htmlc); - } - return true; -} - -/* Exported interface documented in html_internal.h */ -bool html_can_begin_conversion(html_content *htmlc) -{ - unsigned int i; - - if (htmlc->base.active != 0) - return false; - - for (i = 0; i != htmlc->stylesheet_count; i++) { - if (htmlc->stylesheets[i].modified) - return false; - } - - return true; -} - -bool -html_begin_conversion(html_content *htmlc) -{ - dom_node *html; - nserror ns_error; - struct form *f; - dom_exception exc; /* returned by libdom functions */ - dom_string *node_name = NULL; - dom_hubbub_error error; - - /* The act of completing the parse can result in additional data - * being flushed through the parser. This may result in new style or - * script nodes, upon which the conversion depends. Thus, once we - * have completed the parse, we must check again to see if we can - * begin the conversion. If we can't, we must stop and wait for the - * new styles/scripts to be processed. Once they have been processed, - * we will be called again to begin the conversion for real. Thus, - * we must also ensure that we don't attempt to complete the parse - * multiple times, so store a flag to indicate that parsing is - * complete to avoid repeating the completion pointlessly. - */ - if (htmlc->parse_completed == false) { - NSLOG(netsurf, INFO, "Completing parse (%p)", htmlc); - /* complete parsing */ - error = dom_hubbub_parser_completed(htmlc->parser); - if (error != DOM_HUBBUB_OK) { - NSLOG(netsurf, INFO, "Parsing failed"); - - content_broadcast_errorcode(&htmlc->base, - libdom_hubbub_error_to_nserror(error)); - - return false; - } - htmlc->parse_completed = true; - } - - if (html_can_begin_conversion(htmlc) == false) { - NSLOG(netsurf, INFO, "Can't begin conversion (%p)", htmlc); - /* We can't proceed (see commentary above) */ - return true; - } - - /* Give up processing if we've been aborted */ - if (htmlc->aborted) { - NSLOG(netsurf, INFO, "Conversion aborted (%p) (active: %u)", - htmlc, htmlc->base.active); - content_set_error(&htmlc->base); - content_broadcast_errorcode(&htmlc->base, NSERROR_STOPPED); - return false; - } - - /* complete script execution */ - html_script_exec(htmlc); - - /* fire a simple event that bubbles named DOMContentLoaded at - * the Document. - */ - - /* get encoding */ - if (htmlc->encoding == NULL) { - const char *encoding; - - encoding = dom_hubbub_parser_get_encoding(htmlc->parser, - &htmlc->encoding_source); - if (encoding == NULL) { - content_broadcast_errorcode(&htmlc->base, - NSERROR_NOMEM); - return false; - } - - htmlc->encoding = strdup(encoding); - if (htmlc->encoding == NULL) { - content_broadcast_errorcode(&htmlc->base, - NSERROR_NOMEM); - return false; - } - } - - /* locate root element and ensure it is html */ - exc = dom_document_get_document_element(htmlc->document, (void *) &html); - if ((exc != DOM_NO_ERR) || (html == NULL)) { - NSLOG(netsurf, INFO, "error retrieving html element from dom"); - content_broadcast_errorcode(&htmlc->base, NSERROR_DOM); - return false; - } - - exc = dom_node_get_node_name(html, &node_name); - if ((exc != DOM_NO_ERR) || - (node_name == NULL) || - (!dom_string_caseless_lwc_isequal(node_name, - corestring_lwc_html))) { - NSLOG(netsurf, INFO, "root element not html"); - content_broadcast_errorcode(&htmlc->base, NSERROR_DOM); - dom_node_unref(html); - return false; - } - dom_string_unref(node_name); - - /* Retrieve forms from parser */ - htmlc->forms = html_forms_get_forms(htmlc->encoding, - (dom_html_document *) htmlc->document); - for (f = htmlc->forms; f != NULL; f = f->prev) { - nsurl *action; - - /* Make all actions absolute */ - if (f->action == NULL || f->action[0] == '\0') { - /* HTML5 4.10.22.3 step 9 */ - nsurl *doc_addr = content_get_url(&htmlc->base); - ns_error = nsurl_join(htmlc->base_url, - nsurl_access(doc_addr), - &action); - } else { - ns_error = nsurl_join(htmlc->base_url, - f->action, - &action); - } - - if (ns_error != NSERROR_OK) { - content_broadcast_errorcode(&htmlc->base, ns_error); - - dom_node_unref(html); - return false; - } - - free(f->action); - f->action = strdup(nsurl_access(action)); - nsurl_unref(action); - if (f->action == NULL) { - content_broadcast_errorcode(&htmlc->base, - NSERROR_NOMEM); - - dom_node_unref(html); - return false; - } - - /* Ensure each form has a document encoding */ - if (f->document_charset == NULL) { - f->document_charset = strdup(htmlc->encoding); - if (f->document_charset == NULL) { - content_broadcast_errorcode(&htmlc->base, - NSERROR_NOMEM); - dom_node_unref(html); - return false; - } - } - } - - dom_node_unref(html); - - if (htmlc->base.active == 0) { - html_finish_conversion(htmlc); - } - - return true; -} - - -/** - * Stop loading a CONTENT_HTML. - * - * called when the content is aborted. This must clean up any state - * created during the fetch. - */ - -static void html_stop(struct content *c) -{ - html_content *htmlc = (html_content *) c; - - /* invalidate the html content reference to the javascript context - * as it is about to become invalid and must not be used any - * more. - */ - html_script_invalidate_ctx(htmlc); - - switch (c->status) { - case CONTENT_STATUS_LOADING: - /* Still loading; simply flag that we've been aborted - * html_convert/html_finish_conversion will do the rest */ - htmlc->aborted = true; - break; - - case CONTENT_STATUS_READY: - html_object_abort_objects(htmlc); - - /* If there are no further active fetches and we're still - * in the READY state, transition to the DONE state. */ - if (c->status == CONTENT_STATUS_READY && c->active == 0) { - content_set_done(c); - } - - break; - - case CONTENT_STATUS_DONE: - /* Nothing to do */ - break; - - default: - NSLOG(netsurf, INFO, "Unexpected status %d (%p)", c->status, - c); - assert(0); - } -} - - -/** - * Reformat a CONTENT_HTML to a new width. - */ - -static void html_reformat(struct content *c, int width, int height) -{ - html_content *htmlc = (html_content *) c; - struct box *layout; - uint64_t ms_before; - uint64_t ms_after; - uint64_t ms_interval; - - nsu_getmonotonic_ms(&ms_before); - - htmlc->reflowing = true; - - htmlc->len_ctx.vw = width; - htmlc->len_ctx.vh = height; - htmlc->len_ctx.root_style = htmlc->layout->style; - - layout_document(htmlc, width, height); - layout = htmlc->layout; - - /* width and height are at least margin box of document */ - c->width = layout->x + layout->padding[LEFT] + layout->width + - layout->padding[RIGHT] + layout->border[RIGHT].width + - layout->margin[RIGHT]; - c->height = layout->y + layout->padding[TOP] + layout->height + - layout->padding[BOTTOM] + layout->border[BOTTOM].width + - layout->margin[BOTTOM]; - - /* if boxes overflow right or bottom edge, expand to contain it */ - if (c->width < layout->x + layout->descendant_x1) - c->width = layout->x + layout->descendant_x1; - if (c->height < layout->y + layout->descendant_y1) - c->height = layout->y + layout->descendant_y1; - - selection_reinit(&htmlc->sel, htmlc->layout); - - htmlc->reflowing = false; - - /* calculate next reflow time at three times what it took to reflow */ - nsu_getmonotonic_ms(&ms_after); - - ms_interval = (ms_before - ms_after) * 3; - if (ms_interval < (nsoption_uint(min_reflow_period) * 10)) { - ms_interval = nsoption_uint(min_reflow_period) * 10; - } - c->reformat_time = ms_after + ms_interval; -} - - -/** - * Redraw a box. - * - * \param h content containing the box, of type CONTENT_HTML - * \param box box to redraw - */ - -void html_redraw_a_box(hlcache_handle *h, struct box *box) -{ - int x, y; - - box_coords(box, &x, &y); - - content_request_redraw(h, x, y, - box->padding[LEFT] + box->width + box->padding[RIGHT], - box->padding[TOP] + box->height + box->padding[BOTTOM]); -} - - -/** - * Redraw a box. - * - * \param html content containing the box, of type CONTENT_HTML - * \param box box to redraw. - */ - -void html__redraw_a_box(struct html_content *html, struct box *box) -{ - int x, y; - - box_coords(box, &x, &y); - - content__request_redraw((struct content *)html, x, y, - box->padding[LEFT] + box->width + box->padding[RIGHT], - box->padding[TOP] + box->height + box->padding[BOTTOM]); -} - -static void html_destroy_frameset(struct content_html_frames *frameset) -{ - int i; - - if (frameset->name) { - talloc_free(frameset->name); - frameset->name = NULL; - } - if (frameset->url) { - talloc_free(frameset->url); - frameset->url = NULL; - } - if (frameset->children) { - for (i = 0; i < (frameset->rows * frameset->cols); i++) { - if (frameset->children[i].name) { - talloc_free(frameset->children[i].name); - frameset->children[i].name = NULL; - } - if (frameset->children[i].url) { - nsurl_unref(frameset->children[i].url); - frameset->children[i].url = NULL; - } - if (frameset->children[i].children) - html_destroy_frameset(&frameset->children[i]); - } - talloc_free(frameset->children); - frameset->children = NULL; - } -} - -static void html_destroy_iframe(struct content_html_iframe *iframe) -{ - struct content_html_iframe *next; - next = iframe; - while ((iframe = next) != NULL) { - next = iframe->next; - if (iframe->name) - talloc_free(iframe->name); - if (iframe->url) { - nsurl_unref(iframe->url); - iframe->url = NULL; - } - talloc_free(iframe); - } -} - - -static void html_free_layout(html_content *htmlc) -{ - if (htmlc->bctx != NULL) { - /* freeing talloc context should let the entire box - * set be destroyed - */ - talloc_free(htmlc->bctx); - } -} - -/** - * Destroy a CONTENT_HTML and free all resources it owns. - */ - -static void html_destroy(struct content *c) -{ - html_content *html = (html_content *) c; - struct form *f, *g; - - NSLOG(netsurf, INFO, "content %p", c); - - /* Destroy forms */ - for (f = html->forms; f != NULL; f = g) { - g = f->prev; - - form_free(f); - } - - imagemap_destroy(html); - - if (c->refresh) - nsurl_unref(c->refresh); - - if (html->base_url) - nsurl_unref(html->base_url); - - if (html->parser != NULL) { - dom_hubbub_parser_destroy(html->parser); - html->parser = NULL; - } - - if (html->document != NULL) { - dom_node_unref(html->document); - html->document = NULL; - } - - if (html->title != NULL) { - dom_node_unref(html->title); - html->title = NULL; - } - - /* Free encoding */ - if (html->encoding != NULL) { - free(html->encoding); - html->encoding = NULL; - } - - /* Free base target */ - if (html->base_target != NULL) { - free(html->base_target); - html->base_target = NULL; - } - - /* Free frameset */ - if (html->frameset != NULL) { - html_destroy_frameset(html->frameset); - talloc_free(html->frameset); - html->frameset = NULL; - } - - /* Free iframes */ - if (html->iframe != NULL) { - html_destroy_iframe(html->iframe); - html->iframe = NULL; - } - - /* Destroy selection context */ - if (html->select_ctx != NULL) { - css_select_ctx_destroy(html->select_ctx); - html->select_ctx = NULL; - } - - if (html->universal != NULL) { - lwc_string_unref(html->universal); - html->universal = NULL; - } - - /* Free stylesheets */ - html_css_free_stylesheets(html); - - /* Free scripts */ - html_script_free(html); - - /* Free objects */ - html_object_free_objects(html); - - /* free layout */ - html_free_layout(html); -} - - -static nserror html_clone(const struct content *old, struct content **newc) -{ - /** \todo Clone HTML specifics */ - - /* In the meantime, we should never be called, as HTML contents - * cannot be shared and we're not intending to fix printing's - * cloning of documents. */ - assert(0 && "html_clone should never be called"); - - return true; -} - - -/** - * Handle a window containing a CONTENT_HTML being opened. - */ - -static void -html_open(struct content *c, - struct browser_window *bw, - struct content *page, - struct object_params *params) -{ - html_content *html = (html_content *) c; - - html->bw = bw; - html->page = (html_content *) page; - - html->drag_type = HTML_DRAG_NONE; - html->drag_owner.no_owner = true; - - /* text selection */ - selection_init(&html->sel, html->layout, &html->len_ctx); - html->selection_type = HTML_SELECTION_NONE; - html->selection_owner.none = true; - - html_object_open_objects(html, bw); -} - - -/** - * Handle a window containing a CONTENT_HTML being closed. - */ - -static void html_close(struct content *c) -{ - html_content *htmlc = (html_content *) c; - - selection_clear(&htmlc->sel, false); - - if (htmlc->search != NULL) { - search_destroy_context(htmlc->search); - } - - /* clear the html content reference to the browser window */ - htmlc->bw = NULL; - - /* invalidate the html content reference to the javascript context - * as it is about to become invalid and must not be used any - * more. - */ - html_script_invalidate_ctx(htmlc); - - /* remove all object references from the html content */ - html_object_close_objects(htmlc); -} - - -/** - * Return an HTML content's selection context - */ - -static void html_clear_selection(struct content *c) -{ - html_content *html = (html_content *) c; - - switch (html->selection_type) { - case HTML_SELECTION_NONE: - /* Nothing to do */ - assert(html->selection_owner.none == true); - break; - case HTML_SELECTION_TEXTAREA: - textarea_clear_selection(html->selection_owner.textarea-> - gadget->data.text.ta); - break; - case HTML_SELECTION_SELF: - assert(html->selection_owner.none == false); - selection_clear(&html->sel, true); - break; - case HTML_SELECTION_CONTENT: - content_clear_selection(html->selection_owner.content->object); - break; - default: - break; - } - - /* There is no selection now. */ - html->selection_type = HTML_SELECTION_NONE; - html->selection_owner.none = true; -} - - -/** - * Return an HTML content's selection context - */ - -static char *html_get_selection(struct content *c) -{ - html_content *html = (html_content *) c; - - switch (html->selection_type) { - case HTML_SELECTION_TEXTAREA: - return textarea_get_selection(html->selection_owner.textarea-> - gadget->data.text.ta); - case HTML_SELECTION_SELF: - assert(html->selection_owner.none == false); - return selection_get_copy(&html->sel); - case HTML_SELECTION_CONTENT: - return content_get_selection( - html->selection_owner.content->object); - case HTML_SELECTION_NONE: - /* Nothing to do */ - assert(html->selection_owner.none == true); - break; - default: - break; - } - - return NULL; -} - - -/** - * Get access to any content, link URLs and objects (images) currently - * at the given (x, y) coordinates. - * - * \param[in] c html content to look inside - * \param[in] x x-coordinate of point of interest - * \param[in] y y-coordinate of point of interest - * \param[out] data Positional features struct to be updated with any - * relevent content, or set to NULL if none. - * \return NSERROR_OK on success else appropriate error code. - */ -static nserror -html_get_contextual_content(struct content *c, int x, int y, - struct browser_window_features *data) -{ - html_content *html = (html_content *) c; - - struct box *box = html->layout; - struct box *next; - int box_x = 0, box_y = 0; - - while ((next = box_at_point(&html->len_ctx, box, x, y, - &box_x, &box_y)) != NULL) { - box = next; - - /* hidden boxes are ignored */ - if ((box->style != NULL) && - css_computed_visibility(box->style) == CSS_VISIBILITY_HIDDEN) { - continue; - } - - if (box->iframe) { - browser_window_get_features(box->iframe, - x - box_x, y - box_y, data); - } - - if (box->object) - content_get_contextual_content(box->object, - x - box_x, y - box_y, data); - - if (box->object) - data->object = box->object; - - if (box->href) - data->link = box->href; - - if (box->usemap) { - const char *target = NULL; - nsurl *url = imagemap_get(html, box->usemap, box_x, - box_y, x, y, &target); - /* Box might have imagemap, but no actual link area - * at point */ - if (url != NULL) - data->link = url; - } - if (box->gadget) { - switch (box->gadget->type) { - case GADGET_TEXTBOX: - case GADGET_TEXTAREA: - case GADGET_PASSWORD: - data->form_features = CTX_FORM_TEXT; - break; - - case GADGET_FILE: - data->form_features = CTX_FORM_FILE; - break; - - default: - data->form_features = CTX_FORM_NONE; - break; - } - } - } - return NSERROR_OK; -} - - -/** - * Scroll deepest thing within the content which can be scrolled at given point - * - * \param c html content to look inside - * \param x x-coordinate of point of interest - * \param y y-coordinate of point of interest - * \param scrx number of px try to scroll something in x direction - * \param scry number of px try to scroll something in y direction - * \return true iff scroll was consumed by something in the content - */ -static bool -html_scroll_at_point(struct content *c, int x, int y, int scrx, int scry) -{ - html_content *html = (html_content *) c; - - struct box *box = html->layout; - struct box *next; - int box_x = 0, box_y = 0; - bool handled_scroll = false; - - /* TODO: invert order; visit deepest box first */ - - while ((next = box_at_point(&html->len_ctx, box, x, y, - &box_x, &box_y)) != NULL) { - box = next; - - if (box->style && css_computed_visibility(box->style) == - CSS_VISIBILITY_HIDDEN) - continue; - - /* Pass into iframe */ - if (box->iframe && browser_window_scroll_at_point(box->iframe, - x - box_x, y - box_y, scrx, scry) == true) - return true; - - /* Pass into textarea widget */ - if (box->gadget && (box->gadget->type == GADGET_TEXTAREA || - box->gadget->type == GADGET_PASSWORD || - box->gadget->type == GADGET_TEXTBOX) && - textarea_scroll(box->gadget->data.text.ta, - scrx, scry) == true) - return true; - - /* Pass into object */ - if (box->object != NULL && content_scroll_at_point( - box->object, x - box_x, y - box_y, - scrx, scry) == true) - return true; - - /* Handle box scrollbars */ - if (box->scroll_y && scrollbar_scroll(box->scroll_y, scry)) - handled_scroll = true; - - if (box->scroll_x && scrollbar_scroll(box->scroll_x, scrx)) - handled_scroll = true; - - if (handled_scroll == true) - return true; - } - - return false; -} - -/** Helper for file gadgets to store their filename unencoded on the - * dom node associated with the gadget. - * - * \todo Get rid of this crap eventually - */ -static void html__dom_user_data_handler(dom_node_operation operation, - dom_string *key, void *_data, struct dom_node *src, - struct dom_node *dst) -{ - char *oldfile; - char *data = (char *)_data; - - if (!dom_string_isequal(corestring_dom___ns_key_file_name_node_data, - key) || data == NULL) { - return; - } - - switch (operation) { - case DOM_NODE_CLONED: - if (dom_node_set_user_data(dst, - corestring_dom___ns_key_file_name_node_data, - strdup(data), html__dom_user_data_handler, - &oldfile) == DOM_NO_ERR) { - if (oldfile != NULL) - free(oldfile); - } - break; - - case DOM_NODE_RENAMED: - case DOM_NODE_IMPORTED: - case DOM_NODE_ADOPTED: - break; - - case DOM_NODE_DELETED: - free(data); - break; - default: - NSLOG(netsurf, INFO, "User data operation not handled."); - assert(0); - } -} - -static void html__set_file_gadget_filename(struct content *c, - struct form_control *gadget, const char *fn) -{ - nserror ret; - char *utf8_fn, *oldfile = NULL; - html_content *html = (html_content *)c; - struct box *file_box = gadget->box; - - ret = guit->utf8->local_to_utf8(fn, 0, &utf8_fn); - if (ret != NSERROR_OK) { - assert(ret != NSERROR_BAD_ENCODING); - NSLOG(netsurf, INFO, - "utf8 to local encoding conversion failed"); - /* Load was for us - just no memory */ - return; - } - - form_gadget_update_value(gadget, utf8_fn); - - /* corestring_dom___ns_key_file_name_node_data */ - if (dom_node_set_user_data((dom_node *)file_box->gadget->node, - corestring_dom___ns_key_file_name_node_data, - strdup(fn), html__dom_user_data_handler, - &oldfile) == DOM_NO_ERR) { - if (oldfile != NULL) - free(oldfile); - } - - /* Redraw box. */ - html__redraw_a_box(html, file_box); -} - -void html_set_file_gadget_filename(struct hlcache_handle *hl, - struct form_control *gadget, const char *fn) -{ - return html__set_file_gadget_filename(hlcache_handle_get_content(hl), - gadget, fn); -} - -/** - * Drop a file onto a content at a particular point, or determine if a file - * may be dropped onto the content at given point. - * - * \param c html content to look inside - * \param x x-coordinate of point of interest - * \param y y-coordinate of point of interest - * \param file path to file to be dropped, or NULL to know if drop allowed - * \return true iff file drop has been handled, or if drop possible (NULL file) - */ -static bool html_drop_file_at_point(struct content *c, int x, int y, char *file) -{ - html_content *html = (html_content *) c; - - struct box *box = html->layout; - struct box *next; - struct box *file_box = NULL; - struct box *text_box = NULL; - int box_x = 0, box_y = 0; - - /* Scan box tree for boxes that can handle drop */ - while ((next = box_at_point(&html->len_ctx, box, x, y, - &box_x, &box_y)) != NULL) { - box = next; - - if (box->style && css_computed_visibility(box->style) == - CSS_VISIBILITY_HIDDEN) - continue; - - if (box->iframe) - return browser_window_drop_file_at_point(box->iframe, - x - box_x, y - box_y, file); - - if (box->object && content_drop_file_at_point(box->object, - x - box_x, y - box_y, file) == true) - return true; - - if (box->gadget) { - switch (box->gadget->type) { - case GADGET_FILE: - file_box = box; - break; - - case GADGET_TEXTBOX: - case GADGET_TEXTAREA: - case GADGET_PASSWORD: - text_box = box; - break; - - default: /* appease compiler */ - break; - } - } - } - - if (!file_box && !text_box) - /* No box capable of handling drop */ - return false; - - if (file == NULL) - /* There is a box capable of handling drop here */ - return true; - - /* Handle the drop */ - if (file_box) { - /* File dropped on file input */ - html__set_file_gadget_filename(c, file_box->gadget, file); - - } else { - /* File dropped on text input */ - - size_t file_len; - FILE *fp = NULL; - char *buffer; - char *utf8_buff; - nserror ret; - unsigned int size; - int bx, by; - - /* Open file */ - fp = fopen(file, "rb"); - if (fp == NULL) { - /* Couldn't open file, but drop was for us */ - return true; - } - - /* Get filesize */ - fseek(fp, 0, SEEK_END); - file_len = ftell(fp); - fseek(fp, 0, SEEK_SET); - - if ((long)file_len == -1) { - /* unable to get file length, but drop was for us */ - fclose(fp); - return true; - } - - /* Allocate buffer for file data */ - buffer = malloc(file_len + 1); - if (buffer == NULL) { - /* No memory, but drop was for us */ - fclose(fp); - return true; - } - - /* Stick file into buffer */ - if (file_len != fread(buffer, 1, file_len, fp)) { - /* Failed, but drop was for us */ - free(buffer); - fclose(fp); - return true; - } - - /* Done with file */ - fclose(fp); - - /* Ensure buffer's string termination */ - buffer[file_len] = '\0'; - - /* TODO: Sniff for text? */ - - /* Convert to UTF-8 */ - ret = guit->utf8->local_to_utf8(buffer, file_len, &utf8_buff); - if (ret != NSERROR_OK) { - /* bad encoding shouldn't happen */ - assert(ret != NSERROR_BAD_ENCODING); - NSLOG(netsurf, INFO, "local to utf8 encoding failed"); - free(buffer); - guit->misc->warning("NoMemory", NULL); - return true; - } - - /* Done with buffer */ - free(buffer); - - /* Get new length */ - size = strlen(utf8_buff); - - /* Simulate a click over the input box, to place caret */ - box_coords(text_box, &bx, &by); - textarea_mouse_action(text_box->gadget->data.text.ta, - BROWSER_MOUSE_PRESS_1, x - bx, y - by); - - /* Paste the file as text */ - textarea_drop_text(text_box->gadget->data.text.ta, - utf8_buff, size); - - free(utf8_buff); - } - - return true; -} - - -/** - * set debug status. - * - * \param c The content to debug - * \param op The debug operation type - */ -static nserror -html_debug(struct content *c, enum content_debug op) -{ - html_redraw_debug = !html_redraw_debug; - - return NSERROR_OK; -} - - -/** - * Dump debug info concerning the html_content - * - * \param c The content to debug - * \param f The file to dump to - * \param op The debug dump type - */ -static nserror -html_debug_dump(struct content *c, FILE *f, enum content_debug op) -{ - html_content *htmlc = (html_content *)c; - dom_node *html; - dom_exception exc; /* returned by libdom functions */ - nserror ret; - - assert(htmlc != NULL); - - if (op == CONTENT_DEBUG_RENDER) { - assert(htmlc->layout != NULL); - box_dump(f, htmlc->layout, 0, true); - ret = NSERROR_OK; - } else { - if (htmlc->document == NULL) { - NSLOG(netsurf, INFO, "No document to dump"); - return NSERROR_DOM; - } - - exc = dom_document_get_document_element(htmlc->document, (void *) &html); - if ((exc != DOM_NO_ERR) || (html == NULL)) { - NSLOG(netsurf, INFO, "Unable to obtain root node"); - return NSERROR_DOM; - } - - ret = libdom_dump_structure(html, f, 0); - - NSLOG(netsurf, INFO, "DOM structure dump returning %d", ret); - - dom_node_unref(html); - } - - return ret; -} - - -#if ALWAYS_DUMP_FRAMESET -/** - * Print a frameset tree to stderr. - */ - -static void -html_dump_frameset(struct content_html_frames *frame, unsigned int depth) -{ - unsigned int i; - int row, col, index; - const char *unit[] = {"px", "%", "*"}; - const char *scrolling[] = {"auto", "yes", "no"}; - - assert(frame); - - fprintf(stderr, "%p ", frame); - - fprintf(stderr, "(%i %i) ", frame->rows, frame->cols); - - fprintf(stderr, "w%g%s ", frame->width.value, unit[frame->width.unit]); - fprintf(stderr, "h%g%s ", frame->height.value,unit[frame->height.unit]); - fprintf(stderr, "(margin w%i h%i) ", - frame->margin_width, frame->margin_height); - - if (frame->name) - fprintf(stderr, "'%s' ", frame->name); - if (frame->url) - fprintf(stderr, "<%s> ", frame->url); - - if (frame->no_resize) - fprintf(stderr, "noresize "); - fprintf(stderr, "(scrolling %s) ", scrolling[frame->scrolling]); - if (frame->border) - fprintf(stderr, "border %x ", - (unsigned int) frame->border_colour); - - fprintf(stderr, "\n"); - - if (frame->children) { - for (row = 0; row != frame->rows; row++) { - for (col = 0; col != frame->cols; col++) { - for (i = 0; i != depth; i++) - fprintf(stderr, " "); - fprintf(stderr, "(%i %i): ", row, col); - index = (row * frame->cols) + col; - html_dump_frameset(&frame->children[index], - depth + 1); - } - } - } -} - -#endif - -/** - * Retrieve HTML document tree - * - * \param h HTML content to retrieve document tree from - * \return Pointer to document tree - */ -dom_document *html_get_document(hlcache_handle *h) -{ - html_content *c = (html_content *) hlcache_handle_get_content(h); - - assert(c != NULL); - - return c->document; -} - -/** - * Retrieve box tree - * - * \param h HTML content to retrieve tree from - * \return Pointer to box tree - * - * \todo This API must die, as must all use of the box tree outside render/ - */ -struct box *html_get_box_tree(hlcache_handle *h) -{ - html_content *c = (html_content *) hlcache_handle_get_content(h); - - assert(c != NULL); - - return c->layout; -} - -/** - * Retrieve the charset of an HTML document - * - * \param c Content to retrieve charset from - * \param op The content encoding operation to perform. - * \return Pointer to charset, or NULL - */ -static const char *html_encoding(const struct content *c, enum content_encoding_type op) -{ - html_content *html = (html_content *) c; - static char enc_token[10] = "Encoding0"; - - assert(html != NULL); - - if (op == CONTENT_ENCODING_SOURCE) { - enc_token[8] = '0' + html->encoding_source; - return messages_get(enc_token); - } - - return html->encoding; -} - - -/** - * Retrieve framesets used in an HTML document - * - * \param h Content to inspect - * \return Pointer to framesets, or NULL if none - */ -struct content_html_frames *html_get_frameset(hlcache_handle *h) -{ - html_content *c = (html_content *) hlcache_handle_get_content(h); - - assert(c != NULL); - - return c->frameset; -} - -/** - * Retrieve iframes used in an HTML document - * - * \param h Content to inspect - * \return Pointer to iframes, or NULL if none - */ -struct content_html_iframe *html_get_iframe(hlcache_handle *h) -{ - html_content *c = (html_content *) hlcache_handle_get_content(h); - - assert(c != NULL); - - return c->iframe; -} - -/** - * Retrieve an HTML content's base URL - * - * \param h Content to retrieve base target from - * \return Pointer to URL - */ -nsurl *html_get_base_url(hlcache_handle *h) -{ - html_content *c = (html_content *) hlcache_handle_get_content(h); - - assert(c != NULL); - - return c->base_url; -} - -/** - * Retrieve an HTML content's base target - * - * \param h Content to retrieve base target from - * \return Pointer to target, or NULL if none - */ -const char *html_get_base_target(hlcache_handle *h) -{ - html_content *c = (html_content *) hlcache_handle_get_content(h); - - assert(c != NULL); - - return c->base_target; -} - - -/** - * Retrieve layout coordinates of box with given id - * - * \param h HTML document to search - * \param frag_id String containing an element id - * \param x Updated to global x coord iff id found - * \param y Updated to global y coord iff id found - * \return true iff id found - */ -bool html_get_id_offset(hlcache_handle *h, lwc_string *frag_id, int *x, int *y) -{ - struct box *pos; - struct box *layout; - - if (content_get_type(h) != CONTENT_HTML) - return false; - - layout = html_get_box_tree(h); - - if ((pos = box_find_by_id(layout, frag_id)) != 0) { - box_coords(pos, x, y); - return true; - } - return false; -} - -/** - * Compute the type of a content - * - * \return CONTENT_HTML - */ -static content_type html_content_type(void) -{ - return CONTENT_HTML; -} - - -static void html_fini(void) -{ - html_css_fini(); -} - -static const content_handler html_content_handler = { - .fini = html_fini, - .create = html_create, - .process_data = html_process_data, - .data_complete = html_convert, - .reformat = html_reformat, - .destroy = html_destroy, - .stop = html_stop, - .mouse_track = html_mouse_track, - .mouse_action = html_mouse_action, - .keypress = html_keypress, - .redraw = html_redraw, - .open = html_open, - .close = html_close, - .get_selection = html_get_selection, - .clear_selection = html_clear_selection, - .get_contextual_content = html_get_contextual_content, - .scroll_at_point = html_scroll_at_point, - .drop_file_at_point = html_drop_file_at_point, - .search = html_search, - .search_clear = html_search_clear, - .debug_dump = html_debug_dump, - .debug = html_debug, - .clone = html_clone, - .get_encoding = html_encoding, - .type = html_content_type, - .no_share = true, -}; - -nserror html_init(void) -{ - uint32_t i; - nserror error; - - error = html_css_init(); - if (error != NSERROR_OK) - goto error; - - for (i = 0; i < NOF_ELEMENTS(html_types); i++) { - error = content_factory_register_handler(html_types[i], - &html_content_handler); - if (error != NSERROR_OK) - goto error; - } - - return NSERROR_OK; - -error: - html_fini(); - - return error; -} - -/** - * Get the browser window containing an HTML content - * - * \param c HTML content - * \return the browser window - */ -struct browser_window *html_get_browser_window(struct content *c) -{ - html_content *html = (html_content *) c; - - assert(c != NULL); - assert(c->handler == &html_content_handler); - - return html->bw; -} diff --git a/render/html.h b/render/html.h deleted file mode 100644 index 30219a3bd..000000000 --- a/render/html.h +++ /dev/null @@ -1,197 +0,0 @@ -/* - * Copyright 2004 James Bursa <bursa@users.sourceforge.net> - * - * 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 <http://www.gnu.org/licenses/>. - */ - -/** \file - * Content for text/html (interface). - * - * These functions should in general be called via the content interface. - */ - -#ifndef _NETSURF_RENDER_HTML_H_ -#define _NETSURF_RENDER_HTML_H_ - -#include <stdbool.h> - -#include <dom/dom.h> -#include <dom/bindings/hubbub/parser.h> - -#include "netsurf/types.h" -#include "netsurf/content_type.h" -#include "netsurf/browser_window.h" -#include "netsurf/mouse.h" -#include "desktop/frame_types.h" - -struct fetch_multipart_data; -struct box; -struct rect; -struct browser_window; -struct content; -struct hlcache_handle; -struct http_parameter; -struct imagemap; -struct object_params; -struct plotters; -struct textarea; -struct scrollbar; -struct scrollbar_msg_data; -struct search_context; -struct selection; -struct nsurl; -struct plot_font_style; - -/** - * Container for stylesheets used by an HTML document - */ -struct html_stylesheet { - struct dom_node *node; /**< dom node associated with sheet */ - struct hlcache_handle *sheet; - bool modified; - bool unused; -}; - -/** - * Container for scripts used by an HTML document - */ -struct html_script { - /** Type of script */ - enum html_script_type { HTML_SCRIPT_INLINE, - HTML_SCRIPT_SYNC, - HTML_SCRIPT_DEFER, - HTML_SCRIPT_ASYNC } type; - union { - struct hlcache_handle *handle; - struct dom_string *string; - } data; /**< Script data */ - struct dom_string *mimetype; - struct dom_string *encoding; - bool already_started; - bool parser_inserted; - bool force_async; - bool ready_exec; - bool async; - bool defer; -}; - - -/** An object (img, object, etc. tag) in a CONTENT_HTML document. */ -struct content_html_object { - struct content *parent; /**< Parent document */ - struct content_html_object *next; /**< Next in chain */ - - struct hlcache_handle *content; /**< Content, or 0. */ - struct box *box; /**< Node in box tree containing it. */ - /** Bitmap of acceptable content types */ - content_type permitted_types; - bool background; /**< This object is a background image. */ -}; - -struct html_scrollbar_data { - struct content *c; - struct box *box; -}; - -/** Frame tree (frameset or frame tag) */ -struct content_html_frames { - int cols; /** number of columns in frameset */ - int rows; /** number of rows in frameset */ - - struct frame_dimension width; /** frame width */ - struct frame_dimension height; /** frame width */ - int margin_width; /** frame margin width */ - int margin_height; /** frame margin height */ - - char *name; /** frame name (for targetting) */ - struct nsurl *url; /** frame url */ - - bool no_resize; /** frame is not resizable */ - browser_scrolling scrolling; /** scrolling characteristics */ - bool border; /** frame has a border */ - colour border_colour; /** frame border colour */ - - struct content_html_frames *children; /** [cols * rows] children */ -}; - -/** Inline frame list (iframe tag) */ -struct content_html_iframe { - struct box *box; - - int margin_width; /** frame margin width */ - int margin_height; /** frame margin height */ - - char *name; /** frame name (for targetting) */ - struct nsurl *url; /** frame url */ - - browser_scrolling scrolling; /** scrolling characteristics */ - bool border; /** frame has a border */ - colour border_colour; /** frame border colour */ - - struct content_html_iframe *next; -}; - -/* entries in stylesheet_content */ -#define STYLESHEET_BASE 0 /* base style sheet */ -#define STYLESHEET_QUIRKS 1 /* quirks mode stylesheet */ -#define STYLESHEET_ADBLOCK 2 /* adblocking stylesheet */ -#define STYLESHEET_USER 3 /* user stylesheet */ -#define STYLESHEET_START 4 /* start of document stylesheets */ - -nserror html_init(void); - -void html_redraw_a_box(struct hlcache_handle *h, struct box *box); - -void html_overflow_scroll_drag_end(struct scrollbar *scrollbar, - browser_mouse_state mouse, int x, int y); - -bool text_redraw(const char *utf8_text, size_t utf8_len, - size_t offset, int space, - const struct plot_font_style *fstyle, - int x, int y, - const struct rect *clip, - int height, - float scale, - bool excluded, - struct content *c, - const struct selection *sel, - struct search_context *search, - const struct redraw_context *ctx); - -dom_document *html_get_document(struct hlcache_handle *h); -struct box *html_get_box_tree(struct hlcache_handle *h); -struct content_html_frames *html_get_frameset(struct hlcache_handle *h); -struct content_html_iframe *html_get_iframe(struct hlcache_handle *h); -struct nsurl *html_get_base_url(struct hlcache_handle *h); -const char *html_get_base_target(struct hlcache_handle *h); -void html_set_file_gadget_filename(struct hlcache_handle *hl, - struct form_control *gadget, const char *fn); - -/** - * Retrieve stylesheets used by HTML document - * - * \param h Content to retrieve stylesheets from - * \param n Pointer to location to receive number of sheets - * \return Pointer to array of stylesheets - */ -struct html_stylesheet *html_get_stylesheets(struct hlcache_handle *h, - unsigned int *n); - -struct content_html_object *html_get_objects(struct hlcache_handle *h, - unsigned int *n); -bool html_get_id_offset(struct hlcache_handle *h, lwc_string *frag_id, - int *x, int *y); - -#endif diff --git a/render/html_css.c b/render/html_css.c deleted file mode 100644 index 45bc16f56..000000000 --- a/render/html_css.c +++ /dev/null @@ -1,713 +0,0 @@ -/* - * Copyright 2013 Vincent Sanders <vince@netsurf-browser.org> - * - * 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 <http://www.gnu.org/licenses/>. - */ - -/** \file - * Processing for html content css operations. - */ - -#include <assert.h> -#include <ctype.h> -#include <stdint.h> -#include <string.h> -#include <strings.h> -#include <stdlib.h> - -#include "utils/nsoption.h" -#include "utils/corestrings.h" -#include "utils/config.h" -#include "utils/log.h" -#include "netsurf/misc.h" -#include "netsurf/content.h" -#include "content/hlcache.h" -#include "css/css.h" -#include "desktop/gui_internal.h" - -#include "render/html_internal.h" - -static nsurl *html_default_stylesheet_url; -static nsurl *html_adblock_stylesheet_url; -static nsurl *html_quirks_stylesheet_url; -static nsurl *html_user_stylesheet_url; - -static nserror css_error_to_nserror(css_error error) -{ - switch (error) { - case CSS_OK: - return NSERROR_OK; - - case CSS_NOMEM: - return NSERROR_NOMEM; - - case CSS_BADPARM: - return NSERROR_BAD_PARAMETER; - - case CSS_INVALID: - return NSERROR_INVALID; - - case CSS_FILENOTFOUND: - return NSERROR_NOT_FOUND; - - case CSS_NEEDDATA: - return NSERROR_NEED_DATA; - - case CSS_BADCHARSET: - return NSERROR_BAD_ENCODING; - - case CSS_EOF: - case CSS_IMPORTS_PENDING: - case CSS_PROPERTY_NOT_SET: - default: - break; - } - return NSERROR_CSS; -} - -/** - * Callback for fetchcache() for stylesheets. - */ - -static nserror -html_convert_css_callback(hlcache_handle *css, - const hlcache_event *event, - void *pw) -{ - html_content *parent = pw; - unsigned int i; - struct html_stylesheet *s; - - /* Find sheet */ - for (i = 0, s = parent->stylesheets; - i != parent->stylesheet_count; - i++, s++) { - if (s->sheet == css) - break; - } - - assert(i != parent->stylesheet_count); - - switch (event->type) { - - case CONTENT_MSG_DONE: - NSLOG(netsurf, INFO, "done stylesheet slot %d '%s'", i, - nsurl_access(hlcache_handle_get_url(css))); - parent->base.active--; - NSLOG(netsurf, INFO, "%d fetches active", parent->base.active); - break; - - case CONTENT_MSG_ERROR: - NSLOG(netsurf, INFO, "stylesheet %s failed: %s", - nsurl_access(hlcache_handle_get_url(css)), - event->data.error); - /* fall through */ - - case CONTENT_MSG_ERRORCODE: - hlcache_handle_release(css); - s->sheet = NULL; - parent->base.active--; - NSLOG(netsurf, INFO, "%d fetches active", parent->base.active); - content_add_error(&parent->base, "?", 0); - break; - - case CONTENT_MSG_POINTER: - /* Really don't want this to continue after the switch */ - return NSERROR_OK; - - default: - break; - } - - if (html_can_begin_conversion(parent)) { - html_begin_conversion(parent); - } - - return NSERROR_OK; -} - -static nserror -html_stylesheet_from_domnode(html_content *c, - dom_node *node, - hlcache_handle **sheet) -{ - hlcache_child_context child; - dom_string *style; - nsurl *url; - dom_exception exc; - nserror error; - uint32_t key; - char urlbuf[64]; - - child.charset = c->encoding; - child.quirks = c->base.quirks; - - exc = dom_node_get_text_content(node, &style); - if ((exc != DOM_NO_ERR) || (style == NULL)) { - NSLOG(netsurf, INFO, "No text content"); - return NSERROR_OK; - } - - error = html_css_fetcher_add_item(style, c->base_url, &key); - if (error != NSERROR_OK) { - dom_string_unref(style); - return error; - } - - dom_string_unref(style); - - snprintf(urlbuf, sizeof(urlbuf), "x-ns-css:%u", key); - - error = nsurl_create(urlbuf, &url); - if (error != NSERROR_OK) { - return error; - } - - error = hlcache_handle_retrieve(url, 0, - content_get_url(&c->base), NULL, - html_convert_css_callback, c, &child, CONTENT_CSS, - sheet); - if (error != NSERROR_OK) { - nsurl_unref(url); - return error; - } - - nsurl_unref(url); - - c->base.active++; - NSLOG(netsurf, INFO, "%d fetches active", c->base.active); - - return NSERROR_OK; -} - -/** - * Process an inline stylesheet in the document. - * - * \param c content structure - * \param style xml node of style element - * \return true on success, false if an error occurred - */ - -static struct html_stylesheet * -html_create_style_element(html_content *c, dom_node *style) -{ - dom_string *val; - dom_exception exc; - struct html_stylesheet *stylesheets; - - /* type='text/css', or not present (invalid but common) */ - exc = dom_element_get_attribute(style, corestring_dom_type, &val); - if (exc == DOM_NO_ERR && val != NULL) { - if (!dom_string_caseless_lwc_isequal(val, - corestring_lwc_text_css)) { - dom_string_unref(val); - return NULL; - } - dom_string_unref(val); - } - - /* media contains 'screen' or 'all' or not present */ - exc = dom_element_get_attribute(style, corestring_dom_media, &val); - if (exc == DOM_NO_ERR && val != NULL) { - if (strcasestr(dom_string_data(val), "screen") == NULL && - strcasestr(dom_string_data(val), - "all") == NULL) { - dom_string_unref(val); - return NULL; - } - dom_string_unref(val); - } - - /* Extend array */ - stylesheets = realloc(c->stylesheets, - sizeof(struct html_stylesheet) * - (c->stylesheet_count + 1)); - if (stylesheets == NULL) { - - content_broadcast_errorcode(&c->base, NSERROR_NOMEM); - return false; - - } - c->stylesheets = stylesheets; - - c->stylesheets[c->stylesheet_count].node = dom_node_ref(style); - c->stylesheets[c->stylesheet_count].sheet = NULL; - c->stylesheets[c->stylesheet_count].modified = false; - c->stylesheets[c->stylesheet_count].unused = false; - c->stylesheet_count++; - - return c->stylesheets + (c->stylesheet_count - 1); -} - -static bool html_css_process_modified_style(html_content *c, - struct html_stylesheet *s) -{ - hlcache_handle *sheet = NULL; - nserror error; - - error = html_stylesheet_from_domnode(c, s->node, &sheet); - if (error != NSERROR_OK) { - NSLOG(netsurf, INFO, "Failed to update sheet"); - content_broadcast_errorcode(&c->base, error); - return false; - } - - if (sheet != NULL) { - NSLOG(netsurf, INFO, "Updating sheet %p with %p", s->sheet, - sheet); - - if (s->sheet != NULL) { - switch (content_get_status(s->sheet)) { - case CONTENT_STATUS_DONE: - break; - default: - hlcache_handle_abort(s->sheet); - c->base.active--; - NSLOG(netsurf, INFO, "%d fetches active", - c->base.active); - } - hlcache_handle_release(s->sheet); - } - s->sheet = sheet; - } - - s->modified = false; - - return true; -} - -static void html_css_process_modified_styles(void *pw) -{ - html_content *c = pw; - struct html_stylesheet *s; - unsigned int i; - bool all_done = true; - - for (i = 0, s = c->stylesheets; i != c->stylesheet_count; i++, s++) { - if (c->stylesheets[i].modified) { - all_done &= html_css_process_modified_style(c, s); - } - } - - /* If we failed to process any sheet, schedule a retry */ - if (all_done == false) { - guit->misc->schedule(1000, html_css_process_modified_styles, c); - } -} - -bool html_css_update_style(html_content *c, dom_node *style) -{ - unsigned int i; - struct html_stylesheet *s; - - /* Find sheet */ - for (i = 0, s = c->stylesheets; i != c->stylesheet_count; i++, s++) { - if (s->node == style) - break; - } - if (i == c->stylesheet_count) { - s = html_create_style_element(c, style); - } - if (s == NULL) { - NSLOG(netsurf, INFO, - "Could not find or create inline stylesheet for %p", - style); - return false; - } - - s->modified = true; - - guit->misc->schedule(0, html_css_process_modified_styles, c); - - return true; -} - -bool html_css_process_style(html_content *c, dom_node *node) -{ - unsigned int i; - dom_string *val; - dom_exception exc; - struct html_stylesheet *s; - - /* Find sheet */ - for (i = 0, s = c->stylesheets; i != c->stylesheet_count; i++, s++) { - if (s->node == node) - break; - } - - /* Should already exist */ - if (i == c->stylesheet_count) { - return false; - } - - exc = dom_element_get_attribute(node, corestring_dom_media, &val); - if (exc == DOM_NO_ERR && val != NULL) { - if (strcasestr(dom_string_data(val), "screen") == NULL && - strcasestr(dom_string_data(val), - "all") == NULL) { - s->unused = true; - } - dom_string_unref(val); - } - - return true; -} - -bool html_css_process_link(html_content *htmlc, dom_node *node) -{ - dom_string *rel, *type_attr, *media, *href; - struct html_stylesheet *stylesheets; - nsurl *joined; - dom_exception exc; - nserror ns_error; - hlcache_child_context child; - - /* rel=<space separated list, including 'stylesheet'> */ - exc = dom_element_get_attribute(node, corestring_dom_rel, &rel); - if (exc != DOM_NO_ERR || rel == NULL) - return true; - - if (strcasestr(dom_string_data(rel), "stylesheet") == 0) { - dom_string_unref(rel); - return true; - } else if (strcasestr(dom_string_data(rel), "alternate") != 0) { - /* Ignore alternate stylesheets */ - dom_string_unref(rel); - return true; - } - dom_string_unref(rel); - - /* type='text/css' or not present */ - exc = dom_element_get_attribute(node, corestring_dom_type, &type_attr); - if (exc == DOM_NO_ERR && type_attr != NULL) { - if (!dom_string_caseless_lwc_isequal(type_attr, - corestring_lwc_text_css)) { - dom_string_unref(type_attr); - return true; - } - dom_string_unref(type_attr); - } - - /* media contains 'screen' or 'all' or not present */ - exc = dom_element_get_attribute(node, corestring_dom_media, &media); - if (exc == DOM_NO_ERR && media != NULL) { - if (strcasestr(dom_string_data(media), "screen") == NULL && - strcasestr(dom_string_data(media), "all") == NULL) { - dom_string_unref(media); - return true; - } - dom_string_unref(media); - } - - /* href='...' */ - exc = dom_element_get_attribute(node, corestring_dom_href, &href); - if (exc != DOM_NO_ERR || href == NULL) - return true; - - /* TODO: only the first preferred stylesheets (ie. - * those with a title attribute) should be loaded - * (see HTML4 14.3) */ - - ns_error = nsurl_join(htmlc->base_url, dom_string_data(href), &joined); - if (ns_error != NSERROR_OK) { - dom_string_unref(href); - goto no_memory; - } - dom_string_unref(href); - - NSLOG(netsurf, INFO, "linked stylesheet %i '%s'", - htmlc->stylesheet_count, nsurl_access(joined)); - - /* extend stylesheets array to allow for new sheet */ - stylesheets = realloc(htmlc->stylesheets, - sizeof(struct html_stylesheet) * - (htmlc->stylesheet_count + 1)); - if (stylesheets == NULL) { - nsurl_unref(joined); - ns_error = NSERROR_NOMEM; - goto no_memory; - } - - htmlc->stylesheets = stylesheets; - htmlc->stylesheets[htmlc->stylesheet_count].node = NULL; - htmlc->stylesheets[htmlc->stylesheet_count].modified = false; - htmlc->stylesheets[htmlc->stylesheet_count].unused = false; - - /* start fetch */ - child.charset = htmlc->encoding; - child.quirks = htmlc->base.quirks; - - ns_error = hlcache_handle_retrieve(joined, 0, - content_get_url(&htmlc->base), - NULL, html_convert_css_callback, - htmlc, &child, CONTENT_CSS, - &htmlc->stylesheets[htmlc->stylesheet_count].sheet); - - nsurl_unref(joined); - - if (ns_error != NSERROR_OK) - goto no_memory; - - htmlc->stylesheet_count++; - - htmlc->base.active++; - NSLOG(netsurf, INFO, "%d fetches active", htmlc->base.active); - - return true; - -no_memory: - content_broadcast_errorcode(&htmlc->base, ns_error); - return false; -} - -/* exported interface documented in render/html.h */ -struct html_stylesheet *html_get_stylesheets(hlcache_handle *h, unsigned int *n) -{ - html_content *c = (html_content *) hlcache_handle_get_content(h); - - assert(c != NULL); - assert(n != NULL); - - *n = c->stylesheet_count; - - return c->stylesheets; -} - - -/* exported interface documented in render/html_internal.h */ -nserror html_css_free_stylesheets(html_content *html) -{ - unsigned int i; - - guit->misc->schedule(-1, html_css_process_modified_styles, html); - - for (i = 0; i != html->stylesheet_count; i++) { - if (html->stylesheets[i].sheet != NULL) { - hlcache_handle_release(html->stylesheets[i].sheet); - } - if (html->stylesheets[i].node != NULL) { - dom_node_unref(html->stylesheets[i].node); - } - } - free(html->stylesheets); - - return NSERROR_OK; -} - -/* exported interface documented in render/html_internal.h */ -nserror html_css_quirks_stylesheets(html_content *c) -{ - nserror ns_error = NSERROR_OK; - hlcache_child_context child; - - assert(c->stylesheets != NULL); - - if (c->quirks == DOM_DOCUMENT_QUIRKS_MODE_FULL) { - child.charset = c->encoding; - child.quirks = c->base.quirks; - - ns_error = hlcache_handle_retrieve(html_quirks_stylesheet_url, - 0, content_get_url(&c->base), NULL, - html_convert_css_callback, c, &child, - CONTENT_CSS, - &c->stylesheets[STYLESHEET_QUIRKS].sheet); - if (ns_error != NSERROR_OK) { - return ns_error; - } - - c->base.active++; - NSLOG(netsurf, INFO, "%d fetches active", c->base.active); - } - - return ns_error; -} - -/* exported interface documented in render/html_internal.h */ -nserror html_css_new_stylesheets(html_content *c) -{ - nserror ns_error; - hlcache_child_context child; - - if (c->stylesheets != NULL) { - return NSERROR_OK; /* already initialised */ - } - - /* stylesheet 0 is the base style sheet, - * stylesheet 1 is the quirks mode style sheet, - * stylesheet 2 is the adblocking stylesheet, - * stylesheet 3 is the user stylesheet */ - c->stylesheets = calloc(STYLESHEET_START, - sizeof(struct html_stylesheet)); - if (c->stylesheets == NULL) { - return NSERROR_NOMEM; - } - - c->stylesheets[STYLESHEET_BASE].sheet = NULL; - c->stylesheets[STYLESHEET_QUIRKS].sheet = NULL; - c->stylesheets[STYLESHEET_ADBLOCK].sheet = NULL; - c->stylesheets[STYLESHEET_USER].sheet = NULL; - c->stylesheet_count = STYLESHEET_START; - - child.charset = c->encoding; - child.quirks = c->base.quirks; - - ns_error = hlcache_handle_retrieve(html_default_stylesheet_url, 0, - content_get_url(&c->base), NULL, - html_convert_css_callback, c, &child, CONTENT_CSS, - &c->stylesheets[STYLESHEET_BASE].sheet); - if (ns_error != NSERROR_OK) { - return ns_error; - } - - c->base.active++; - NSLOG(netsurf, INFO, "%d fetches active", c->base.active); - - - if (nsoption_bool(block_advertisements)) { - ns_error = hlcache_handle_retrieve(html_adblock_stylesheet_url, - 0, content_get_url(&c->base), NULL, - html_convert_css_callback, - c, &child, CONTENT_CSS, - &c->stylesheets[STYLESHEET_ADBLOCK].sheet); - if (ns_error != NSERROR_OK) { - return ns_error; - } - - c->base.active++; - NSLOG(netsurf, INFO, "%d fetches active", c->base.active); - - } - - ns_error = hlcache_handle_retrieve(html_user_stylesheet_url, 0, - content_get_url(&c->base), NULL, - html_convert_css_callback, c, &child, CONTENT_CSS, - &c->stylesheets[STYLESHEET_USER].sheet); - if (ns_error != NSERROR_OK) { - return ns_error; - } - - c->base.active++; - NSLOG(netsurf, INFO, "%d fetches active", c->base.active); - - return ns_error; -} - -nserror -html_css_new_selection_context(html_content *c, css_select_ctx **ret_select_ctx) -{ - uint32_t i; - css_error css_ret; - css_select_ctx *select_ctx; - - /* check that the base stylesheet loaded; layout fails without it */ - if (c->stylesheets[STYLESHEET_BASE].sheet == NULL) { - return NSERROR_CSS_BASE; - } - - /* Create selection context */ - css_ret = css_select_ctx_create(&select_ctx); - if (css_ret != CSS_OK) { - return css_error_to_nserror(css_ret); - } - - /* Add sheets to it */ - for (i = STYLESHEET_BASE; i != c->stylesheet_count; i++) { - const struct html_stylesheet *hsheet = &c->stylesheets[i]; - css_stylesheet *sheet = NULL; - css_origin origin = CSS_ORIGIN_AUTHOR; - - /* Filter out stylesheets for non-screen media. */ - if (hsheet->unused) { - continue; - } - - if (i < STYLESHEET_USER) { - origin = CSS_ORIGIN_UA; - } else if (i < STYLESHEET_START) { - origin = CSS_ORIGIN_USER; - } - - if (hsheet->sheet != NULL) { - sheet = nscss_get_stylesheet(hsheet->sheet); - } - - if (sheet != NULL) { - css_ret = css_select_ctx_append_sheet(select_ctx, - sheet, - origin, - CSS_MEDIA_SCREEN); - if (css_ret != CSS_OK) { - css_select_ctx_destroy(select_ctx); - return css_error_to_nserror(css_ret); - } - } - } - - /* return new selection context to caller */ - *ret_select_ctx = select_ctx; - return NSERROR_OK; -} - -nserror html_css_init(void) -{ - nserror error; - - error = html_css_fetcher_register(); - if (error != NSERROR_OK) - return error; - - error = nsurl_create("resource:default.css", - &html_default_stylesheet_url); - if (error != NSERROR_OK) - return error; - - error = nsurl_create("resource:adblock.css", - &html_adblock_stylesheet_url); - if (error != NSERROR_OK) - return error; - - error = nsurl_create("resource:quirks.css", - &html_quirks_stylesheet_url); - if (error != NSERROR_OK) - return error; - - error = nsurl_create("resource:user.css", - &html_user_stylesheet_url); - - return error; -} - -void html_css_fini(void) -{ - if (html_user_stylesheet_url != NULL) { - nsurl_unref(html_user_stylesheet_url); - html_user_stylesheet_url = NULL; - } - - if (html_quirks_stylesheet_url != NULL) { - nsurl_unref(html_quirks_stylesheet_url); - html_quirks_stylesheet_url = NULL; - } - - if (html_adblock_stylesheet_url != NULL) { - nsurl_unref(html_adblock_stylesheet_url); - html_adblock_stylesheet_url = NULL; - } - - if (html_default_stylesheet_url != NULL) { - nsurl_unref(html_default_stylesheet_url); - html_default_stylesheet_url = NULL; - } -} diff --git a/render/html_css_fetcher.c b/render/html_css_fetcher.c deleted file mode 100644 index 0f8809a42..000000000 --- a/render/html_css_fetcher.c +++ /dev/null @@ -1,321 +0,0 @@ -/* - * Copyright 2008 Rob Kendrick <rjek@netsurf-browser.org> - * Copyright 2013 John-Mark Bell <jmb@netsurf-browser.org> - * - * This file is part of NetSurf. - * - * 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 <http://www.gnu.org/licenses/>. - */ - -#include <assert.h> -#include <stdbool.h> -#include <stdlib.h> -#include <string.h> -#include <dom/dom.h> -#include <libwapcaplet/libwapcaplet.h> - -#include "netsurf/inttypes.h" -#include "utils/config.h" -#include "utils/log.h" -#include "utils/ring.h" -#include "utils/nsurl.h" -#include "utils/utils.h" -#include "content/fetch.h" -#include "content/fetchers.h" - -#include "render/html_internal.h" - -typedef struct html_css_fetcher_item { - uint32_t key; - dom_string *data; - nsurl *base_url; - - struct html_css_fetcher_item *r_next, *r_prev; -} html_css_fetcher_item; - -typedef struct html_css_fetcher_context { - struct fetch *parent_fetch; - - nsurl *url; - html_css_fetcher_item *item; - - bool aborted; - bool locked; - - struct html_css_fetcher_context *r_next, *r_prev; -} html_css_fetcher_context; - -static uint32_t current_key = 0; -static html_css_fetcher_item *items = NULL; -static html_css_fetcher_context *ring = NULL; - -static bool html_css_fetcher_initialise(lwc_string *scheme) -{ - NSLOG(netsurf, INFO, "html_css_fetcher_initialise called for %s", - lwc_string_data(scheme)); - return true; -} - -static void html_css_fetcher_finalise(lwc_string *scheme) -{ - NSLOG(netsurf, INFO, "html_css_fetcher_finalise called for %s", - lwc_string_data(scheme)); -} - -static bool html_css_fetcher_can_fetch(const nsurl *url) -{ - return true; -} - -static void *html_css_fetcher_setup(struct fetch *parent_fetch, nsurl *url, - bool only_2xx, bool downgrade_tls, const char *post_urlenc, - const struct fetch_multipart_data *post_multipart, - const char **headers) -{ - html_css_fetcher_context *ctx; - lwc_string *path; - uint32_t key; - html_css_fetcher_item *item, *found = NULL; - - /* format of a x-ns-css URL is: - * x-ns-url:<key> - * Where key is an unsigned 32bit integer - */ - - path = nsurl_get_component(url, NSURL_PATH); - /* The path must exist */ - if (path == NULL) { - return NULL; - } - - key = strtoul(lwc_string_data(path), NULL, 10); - - lwc_string_unref(path); - - /* There must be at least one item */ - if (items == NULL) { - return NULL; - } - - item = items; - do { - if (item->key == key) { - found = item; - break; - } - - item = item->r_next; - } while (item != items); - - /* We must have found the item */ - if (found == NULL) { - return NULL; - } - - ctx = calloc(1, sizeof(*ctx)); - if (ctx == NULL) - return NULL; - - ctx->parent_fetch = parent_fetch; - ctx->url = nsurl_ref(url); - ctx->item = found; - - RING_INSERT(ring, ctx); - - return ctx; -} - -static bool html_css_fetcher_start(void *ctx) -{ - return true; -} - -static void html_css_fetcher_free(void *ctx) -{ - html_css_fetcher_context *c = ctx; - - nsurl_unref(c->url); - if (c->item != NULL) { - nsurl_unref(c->item->base_url); - dom_string_unref(c->item->data); - RING_REMOVE(items, c->item); - free(c->item); - } - RING_REMOVE(ring, c); - free(ctx); -} - -static void html_css_fetcher_abort(void *ctx) -{ - html_css_fetcher_context *c = ctx; - - /* To avoid the poll loop having to deal with the fetch context - * disappearing from under it, we simply flag the abort here. - * The poll loop itself will perform the appropriate cleanup. - */ - c->aborted = true; -} - -static void html_css_fetcher_send_callback(const fetch_msg *msg, - html_css_fetcher_context *c) -{ - c->locked = true; - fetch_send_callback(msg, c->parent_fetch); - c->locked = false; -} - -static void html_css_fetcher_poll(lwc_string *scheme) -{ - fetch_msg msg; - html_css_fetcher_context *c, *next; - - if (ring == NULL) return; - - /* Iterate over ring, processing each pending fetch */ - c = ring; - do { - /* Ignore fetches that have been flagged as locked. - * This allows safe re-entrant calls to this function. - * Re-entrancy can occur if, as a result of a callback, - * the interested party causes fetch_poll() to be called - * again. - */ - if (c->locked == true) { - next = c->r_next; - continue; - } - - /* Only process non-aborted fetches */ - if (c->aborted) { - /* Nothing to do */ - assert(c->locked == false); - } else if (c->item != NULL) { - char header[4096]; - - fetch_set_http_code(c->parent_fetch, 200); - - /* Any callback can result in the fetch being aborted. - * Therefore, we _must_ check for this after _every_ - * call to html_css_fetcher_send_callback(). - */ - snprintf(header, sizeof header, - "Content-Type: text/css; charset=utf-8"); - msg.type = FETCH_HEADER; - msg.data.header_or_data.buf = (const uint8_t *) header; - msg.data.header_or_data.len = strlen(header); - html_css_fetcher_send_callback(&msg, c); - - if (c->aborted == false) { - snprintf(header, sizeof header, - "Content-Length: %"PRIsizet, - dom_string_byte_length(c->item->data)); - msg.type = FETCH_HEADER; - msg.data.header_or_data.buf = - (const uint8_t *) header; - msg.data.header_or_data.len = strlen(header); - html_css_fetcher_send_callback(&msg, c); - } - - if (c->aborted == false) { - snprintf(header, sizeof header, - "X-NS-Base: %.*s", - (int) nsurl_length(c->item->base_url), - nsurl_access(c->item->base_url)); - msg.type = FETCH_HEADER; - msg.data.header_or_data.buf = - (const uint8_t *) header; - msg.data.header_or_data.len = strlen(header); - html_css_fetcher_send_callback(&msg, c); - } - - if (c->aborted == false) { - msg.type = FETCH_DATA; - msg.data.header_or_data.buf = - (const uint8_t *) - dom_string_data(c->item->data); - msg.data.header_or_data.len = - dom_string_byte_length(c->item->data); - html_css_fetcher_send_callback(&msg, c); - } - - if (c->aborted == false) { - msg.type = FETCH_FINISHED; - html_css_fetcher_send_callback(&msg, c); - } - } else { - NSLOG(netsurf, INFO, "Processing of %s failed!", - nsurl_access(c->url)); - - /* Ensure that we're unlocked here. If we aren't, - * then html_css_fetcher_process() is broken. - */ - assert(c->locked == false); - } - - /* Compute next fetch item at the last possible moment as - * processing this item may have added to the ring. - */ - next = c->r_next; - - fetch_remove_from_queues(c->parent_fetch); - fetch_free(c->parent_fetch); - - /* Advance to next ring entry, exiting if we've reached - * the start of the ring or the ring has become empty - */ - } while ( (c = next) != ring && ring != NULL); -} - -/* exported interface documented in html_internal.h */ -nserror html_css_fetcher_register(void) -{ - lwc_string *scheme; - const struct fetcher_operation_table html_css_fetcher_ops = { - .initialise = html_css_fetcher_initialise, - .acceptable = html_css_fetcher_can_fetch, - .setup = html_css_fetcher_setup, - .start = html_css_fetcher_start, - .abort = html_css_fetcher_abort, - .free = html_css_fetcher_free, - .poll = html_css_fetcher_poll, - .finalise = html_css_fetcher_finalise - }; - - if (lwc_intern_string("x-ns-css", SLEN("x-ns-css"), - &scheme) != lwc_error_ok) { - NSLOG(netsurf, INFO, "could not intern \"x-ns-css\"."); - return NSERROR_INIT_FAILED; - } - - return fetcher_add(scheme, &html_css_fetcher_ops); -} - -/* exported interface documented in html_internal.h */ -nserror -html_css_fetcher_add_item(dom_string *data, nsurl *base_url, uint32_t *key) -{ - html_css_fetcher_item *item = malloc(sizeof(*item)); - - if (item == NULL) { - return NSERROR_NOMEM; - } - - *key = item->key = current_key++; - item->data = dom_string_ref(data); - item->base_url = nsurl_ref(base_url); - - RING_INSERT(items, item); - - return NSERROR_OK; -} - diff --git a/render/html_forms.c b/render/html_forms.c deleted file mode 100644 index 39bc690d9..000000000 --- a/render/html_forms.c +++ /dev/null @@ -1,575 +0,0 @@ -/* - * Copyright 2011 Vincent Sanders <vince@netsurf-browser.org> - * - * 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 <http://www.gnu.org/licenses/>. - */ - -#include "utils/config.h" -#include "utils/corestrings.h" -#include "utils/log.h" - -#include "render/form_internal.h" -#include "render/html_internal.h" - -/** - * process form element from dom - */ -static struct form * -parse_form_element(const char *docenc, dom_node *node) -{ - dom_string *ds_action = NULL; - dom_string *ds_charset = NULL; - dom_string *ds_target = NULL; - dom_string *ds_method = NULL; - dom_string *ds_enctype = NULL; - char *action = NULL, *charset = NULL, *target = NULL; - form_method method; - dom_html_form_element *formele = (dom_html_form_element *)(node); - struct form * ret = NULL; - - /* Retrieve the attributes from the node */ - if (dom_html_form_element_get_action(formele, - &ds_action) != DOM_NO_ERR) - goto out; - - if (dom_html_form_element_get_accept_charset(formele, - &ds_charset) != DOM_NO_ERR) - goto out; - - if (dom_html_form_element_get_target(formele, - &ds_target) != DOM_NO_ERR) - goto out; - - if (dom_html_form_element_get_method(formele, - &ds_method) != DOM_NO_ERR) - goto out; - - if (dom_html_form_element_get_enctype(formele, - &ds_enctype) != DOM_NO_ERR) - goto out; - - /* Extract the plain attributes ready for use. We have to do this - * because we cannot guarantee that the dom_strings are NULL terminated - * and thus we copy them. - */ - if (ds_action != NULL) - action = strndup(dom_string_data(ds_action), - dom_string_byte_length(ds_action)); - - if (ds_charset != NULL) - charset = strndup(dom_string_data(ds_charset), - dom_string_byte_length(ds_charset)); - - if (ds_target != NULL) - target = strndup(dom_string_data(ds_target), - dom_string_byte_length(ds_target)); - - /* Determine the method */ - method = method_GET; - if (ds_method != NULL) { - if (dom_string_caseless_lwc_isequal(ds_method, - corestring_lwc_post)) { - method = method_POST_URLENC; - if (ds_enctype != NULL) { - if (dom_string_caseless_lwc_isequal(ds_enctype, - corestring_lwc_multipart_form_data)) { - - method = method_POST_MULTIPART; - } - } - } - } - - /* Construct the form object */ - ret = form_new(node, action, target, method, charset, docenc); - -out: - if (ds_action != NULL) - dom_string_unref(ds_action); - if (ds_charset != NULL) - dom_string_unref(ds_charset); - if (ds_target != NULL) - dom_string_unref(ds_target); - if (ds_method != NULL) - dom_string_unref(ds_method); - if (ds_enctype != NULL) - dom_string_unref(ds_enctype); - if (action != NULL) - free(action); - if (charset != NULL) - free(charset); - if (target != NULL) - free(target); - return ret; -} - -/* documented in html_internal.h */ -struct form *html_forms_get_forms(const char *docenc, dom_html_document *doc) -{ - dom_html_collection *forms; - struct form *ret = NULL, *newf; - dom_node *node; - unsigned long n; - uint32_t nforms; - - if (doc == NULL) - return NULL; - - /* Attempt to build a set of all the forms */ - if (dom_html_document_get_forms(doc, &forms) != DOM_NO_ERR) - return NULL; - - /* Count the number of forms so we can iterate */ - if (dom_html_collection_get_length(forms, &nforms) != DOM_NO_ERR) - goto out; - - /* Iterate the forms collection, making form structs for returning */ - for (n = 0; n < nforms; ++n) { - if (dom_html_collection_item(forms, n, &node) != DOM_NO_ERR) { - goto out; - } - newf = parse_form_element(docenc, node); - dom_node_unref(node); - if (newf == NULL) { - goto err; - } - newf->prev = ret; - ret = newf; - } - - /* All went well */ - goto out; -err: - while (ret != NULL) { - struct form *prev = ret->prev; - /* Destroy ret */ - free(ret); - ret = prev; - } -out: - /* Finished with the collection, return it */ - dom_html_collection_unref(forms); - - return ret; -} - -static struct form * -find_form(struct form *forms, dom_html_form_element *form) -{ - while (forms != NULL) { - if (forms->node == form) - break; - forms = forms->prev; - } - - return forms; -} - -static struct form_control * -parse_button_element(struct form *forms, dom_html_button_element *button) -{ - struct form_control *control = NULL; - dom_exception err; - dom_html_form_element *form = NULL; - dom_string *ds_type = NULL; - dom_string *ds_value = NULL; - dom_string *ds_name = NULL; - - err = dom_html_button_element_get_form(button, &form); - if (err != DOM_NO_ERR) - goto out; - - err = dom_html_button_element_get_type(button, &ds_type); - if (err != DOM_NO_ERR) - goto out; - - if (ds_type == NULL) { - control = form_new_control(button, GADGET_SUBMIT); - } else { - if (dom_string_caseless_lwc_isequal(ds_type, - corestring_lwc_submit)) { - control = form_new_control(button, GADGET_SUBMIT); - } else if (dom_string_caseless_lwc_isequal(ds_type, - corestring_lwc_reset)) { - control = form_new_control(button, GADGET_RESET); - } else { - control = form_new_control(button, GADGET_BUTTON); - } - } - - if (control == NULL) - goto out; - - err = dom_html_button_element_get_value(button, &ds_value); - if (err != DOM_NO_ERR) - goto out; - err = dom_html_button_element_get_name(button, &ds_name); - if (err != DOM_NO_ERR) - goto out; - - if (ds_value != NULL) { - control->value = strndup( - dom_string_data(ds_value), - dom_string_byte_length(ds_value)); - - if (control->value == NULL) { - form_free_control(control); - control = NULL; - goto out; - } - } - - if (ds_name != NULL) { - control->name = strndup( - dom_string_data(ds_name), - dom_string_byte_length(ds_name)); - - if (control->name == NULL) { - form_free_control(control); - control = NULL; - goto out; - } - } - - if (form != NULL && control != NULL) - form_add_control(find_form(forms, form), control); - -out: - if (form != NULL) - dom_node_unref(form); - if (ds_type != NULL) - dom_string_unref(ds_type); - if (ds_value != NULL) - dom_string_unref(ds_value); - if (ds_name != NULL) - dom_string_unref(ds_name); - - return control; -} - -static struct form_control * -parse_input_element(struct form *forms, dom_html_input_element *input) -{ - struct form_control *control = NULL; - dom_html_form_element *form = NULL; - dom_string *ds_type = NULL; - dom_string *ds_name = NULL; - dom_string *ds_value = NULL; - - char *name = NULL; - - if (dom_html_input_element_get_form(input, &form) != DOM_NO_ERR) - goto out; - - if (dom_html_input_element_get_type(input, &ds_type) != DOM_NO_ERR) - goto out; - - if (dom_html_input_element_get_name(input, &ds_name) != DOM_NO_ERR) - goto out; - - if (ds_name != NULL) - name = strndup(dom_string_data(ds_name), - dom_string_byte_length(ds_name)); - - if (ds_type != NULL && dom_string_caseless_lwc_isequal(ds_type, - corestring_lwc_password)) { - control = form_new_control(input, GADGET_PASSWORD); - } else if (ds_type != NULL && dom_string_caseless_lwc_isequal(ds_type, - corestring_lwc_file)) { - control = form_new_control(input, GADGET_FILE); - } else if (ds_type != NULL && dom_string_caseless_lwc_isequal(ds_type, - corestring_lwc_hidden)) { - control = form_new_control(input, GADGET_HIDDEN); - } else if (ds_type != NULL && dom_string_caseless_lwc_isequal(ds_type, - corestring_lwc_checkbox)) { - control = form_new_control(input, GADGET_CHECKBOX); - } else if (ds_type != NULL && dom_string_caseless_lwc_isequal(ds_type, - corestring_lwc_radio)) { - control = form_new_control(input, GADGET_RADIO); - } else if (ds_type != NULL && dom_string_caseless_lwc_isequal(ds_type, - corestring_lwc_submit)) { - control = form_new_control(input, GADGET_SUBMIT); - } else if (ds_type != NULL && dom_string_caseless_lwc_isequal(ds_type, - corestring_lwc_reset)) { - control = form_new_control(input, GADGET_RESET); - } else if (ds_type != NULL && dom_string_caseless_lwc_isequal(ds_type, - corestring_lwc_button)) { - control = form_new_control(input, GADGET_BUTTON); - } else if (ds_type != NULL && dom_string_caseless_lwc_isequal(ds_type, - corestring_lwc_image)) { - control = form_new_control(input, GADGET_IMAGE); - } else { - control = form_new_control(input, GADGET_TEXTBOX); - } - - if (control == NULL) - goto out; - - if (name != NULL) { - /* Hand the name string over */ - control->name = name; - name = NULL; - } - - if (control->type == GADGET_CHECKBOX || control->type == GADGET_RADIO) { - bool selected; - if (dom_html_input_element_get_checked( - input, &selected) == DOM_NO_ERR) { - control->selected = selected; - } - } - - if (control->type == GADGET_PASSWORD || - control->type == GADGET_TEXTBOX) { - int32_t maxlength; - if (dom_html_input_element_get_max_length( - input, &maxlength) != DOM_NO_ERR) { - maxlength = -1; - } - - if (maxlength >= 0) { - /* Got valid maxlength */ - control->maxlength = maxlength; - } else { - /* Input has no maxlength attr, or - * dom_html_input_element_get_max_length failed. - * - * Set it to something insane. */ - control->maxlength = UINT_MAX; - } - } - - if (control->type != GADGET_FILE && control->type != GADGET_IMAGE) { - if (dom_html_input_element_get_value( - input, &ds_value) == DOM_NO_ERR) { - if (ds_value != NULL) { - control->value = strndup( - dom_string_data(ds_value), - dom_string_byte_length(ds_value)); - if (control->value == NULL) { - form_free_control(control); - control = NULL; - goto out; - } - control->length = strlen(control->value); - } - } - - if (control->type == GADGET_TEXTBOX || - control->type == GADGET_PASSWORD) { - if (control->value == NULL) { - control->value = strdup(""); - if (control->value == NULL) { - form_free_control(control); - control = NULL; - goto out; - } - - control->length = 0; - } - - control->initial_value = strdup(control->value); - if (control->initial_value == NULL) { - form_free_control(control); - control = NULL; - goto out; - } - } - } - - if (form != NULL && control != NULL) - form_add_control(find_form(forms, form), control); - -out: - if (form != NULL) - dom_node_unref(form); - if (ds_type != NULL) - dom_string_unref(ds_type); - if (ds_name != NULL) - dom_string_unref(ds_name); - if (ds_value != NULL) - dom_string_unref(ds_value); - - if (name != NULL) - free(name); - - return control; -} - -static struct form_control * -parse_textarea_element(struct form *forms, dom_html_text_area_element *ta) -{ - struct form_control *control = NULL; - dom_html_form_element *form = NULL; - dom_string *ds_name = NULL; - - char *name = NULL; - - if (dom_html_text_area_element_get_form(ta, &form) != DOM_NO_ERR) - goto out; - - if (dom_html_text_area_element_get_name(ta, &ds_name) != DOM_NO_ERR) - goto out; - - if (ds_name != NULL) - name = strndup(dom_string_data(ds_name), - dom_string_byte_length(ds_name)); - - control = form_new_control(ta, GADGET_TEXTAREA); - - if (control == NULL) - goto out; - - if (name != NULL) { - /* Hand the name string over */ - control->name = name; - name = NULL; - } - - if (form != NULL && control != NULL) - form_add_control(find_form(forms, form), control); - -out: - if (form != NULL) - dom_node_unref(form); - if (ds_name != NULL) - dom_string_unref(ds_name); - - if (name != NULL) - free(name); - - - return control; -} - -static struct form_control * -parse_select_element(struct form *forms, dom_html_select_element *select) -{ - struct form_control *control = NULL; - dom_html_form_element *form = NULL; - dom_string *ds_name = NULL; - - char *name = NULL; - - if (dom_html_select_element_get_form(select, &form) != DOM_NO_ERR) - goto out; - - if (dom_html_select_element_get_name(select, &ds_name) != DOM_NO_ERR) - goto out; - - if (ds_name != NULL) - name = strndup(dom_string_data(ds_name), - dom_string_byte_length(ds_name)); - - control = form_new_control(select, GADGET_SELECT); - - if (control == NULL) - goto out; - - if (name != NULL) { - /* Hand the name string over */ - control->name = name; - name = NULL; - } - - dom_html_select_element_get_multiple(select, - &(control->data.select.multiple)); - - if (form != NULL && control != NULL) - form_add_control(find_form(forms, form), control); - -out: - if (form != NULL) - dom_node_unref(form); - if (ds_name != NULL) - dom_string_unref(ds_name); - - if (name != NULL) - free(name); - - - return control; -} - - -static struct form_control * -invent_fake_gadget(dom_node *node) -{ - struct form_control *ctl = form_new_control(node, GADGET_HIDDEN); - if (ctl != NULL) { - ctl->value = strdup(""); - ctl->initial_value = strdup(""); - ctl->name = strdup("foo"); - - if (ctl->value == NULL || ctl->initial_value == NULL || - ctl->name == NULL) { - form_free_control(ctl); - ctl = NULL; - } - } - return ctl; -} - -/* documented in html_internal.h */ -struct form_control *html_forms_get_control_for_node(struct form *forms, - dom_node *node) -{ - struct form *f; - struct form_control *ctl = NULL; - dom_exception err; - dom_string *ds_name = NULL; - - /* Step one, see if we already have a control */ - for (f = forms; f != NULL; f = f->prev) { - for (ctl = f->controls; ctl != NULL; ctl = ctl->next) { - if (ctl->node == node) - return ctl; - } - } - - /* Step two, extract the node's name so we can construct a gadget. */ - err = dom_element_get_tag_name(node, &ds_name); - if (err == DOM_NO_ERR && ds_name != NULL) { - - /* Step three, attempt to work out what gadget to make */ - if (dom_string_caseless_lwc_isequal(ds_name, - corestring_lwc_button)) { - ctl = parse_button_element(forms, - (dom_html_button_element *) node); - } else if (dom_string_caseless_lwc_isequal(ds_name, - corestring_lwc_input)) { - ctl = parse_input_element(forms, - (dom_html_input_element *) node); - } else if (dom_string_caseless_lwc_isequal(ds_name, - corestring_lwc_textarea)) { - ctl = parse_textarea_element(forms, - (dom_html_text_area_element *) node); - } else if (dom_string_caseless_lwc_isequal(ds_name, - corestring_lwc_select)) { - ctl = parse_select_element(forms, - (dom_html_select_element *) node); - } - } - - /* If all else fails, fake gadget time */ - if (ctl == NULL) - ctl = invent_fake_gadget(node); - - if (ds_name != NULL) - dom_string_unref(ds_name); - - return ctl; -} - diff --git a/render/html_interaction.c b/render/html_interaction.c deleted file mode 100644 index 2d14ed2ae..000000000 --- a/render/html_interaction.c +++ /dev/null @@ -1,1434 +0,0 @@ -/* - * Copyright 2006 James Bursa <bursa@users.sourceforge.net> - * Copyright 2006 Richard Wilson <info@tinct.net> - * Copyright 2008 Michael Drake <tlsa@netsurf-browser.org> - * Copyright 2009 Paul Blokus <paul_pl@users.sourceforge.net> - * - * 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 <http://www.gnu.org/licenses/>. - */ - -/** - * \file - * User interaction with a CONTENT_HTML (implementation). - */ - -#include <assert.h> -#include <stdbool.h> - -#include <dom/dom.h> - -#include "utils/corestrings.h" -#include "utils/messages.h" -#include "utils/utils.h" -#include "utils/log.h" -#include "utils/nsoption.h" -#include "netsurf/content.h" -#include "netsurf/browser_window.h" -#include "netsurf/mouse.h" -#include "netsurf/misc.h" -#include "netsurf/layout.h" -#include "netsurf/keypress.h" -#include "content/hlcache.h" -#include "desktop/frames.h" -#include "desktop/scrollbar.h" -#include "desktop/selection.h" -#include "desktop/textarea.h" -#include "javascript/js.h" -#include "desktop/gui_internal.h" - -#include "render/box.h" -#include "render/box_textarea.h" -#include "render/font.h" -#include "render/form_internal.h" -#include "render/html_internal.h" -#include "render/imagemap.h" -#include "render/search.h" - -/** - * Get pointer shape for given box - * - * \param box box in question - * \param imagemap whether an imagemap applies to the box - */ - -static browser_pointer_shape get_pointer_shape(struct box *box, bool imagemap) -{ - browser_pointer_shape pointer; - css_computed_style *style; - enum css_cursor_e cursor; - lwc_string **cursor_uris; - - if (box->type == BOX_FLOAT_LEFT || box->type == BOX_FLOAT_RIGHT) - style = box->children->style; - else - style = box->style; - - if (style == NULL) - return BROWSER_POINTER_DEFAULT; - - cursor = css_computed_cursor(style, &cursor_uris); - - switch (cursor) { - case CSS_CURSOR_AUTO: - if (box->href || (box->gadget && - (box->gadget->type == GADGET_IMAGE || - box->gadget->type == GADGET_SUBMIT)) || - imagemap) { - /* link */ - pointer = BROWSER_POINTER_POINT; - } else if (box->gadget && - (box->gadget->type == GADGET_TEXTBOX || - box->gadget->type == GADGET_PASSWORD || - box->gadget->type == GADGET_TEXTAREA)) { - /* text input */ - pointer = BROWSER_POINTER_CARET; - } else { - /* html content doesn't mind */ - pointer = BROWSER_POINTER_AUTO; - } - break; - case CSS_CURSOR_CROSSHAIR: - pointer = BROWSER_POINTER_CROSS; - break; - case CSS_CURSOR_POINTER: - pointer = BROWSER_POINTER_POINT; - break; - case CSS_CURSOR_MOVE: - pointer = BROWSER_POINTER_MOVE; - break; - case CSS_CURSOR_E_RESIZE: - pointer = BROWSER_POINTER_RIGHT; - break; - case CSS_CURSOR_W_RESIZE: - pointer = BROWSER_POINTER_LEFT; - break; - case CSS_CURSOR_N_RESIZE: - pointer = BROWSER_POINTER_UP; - break; - case CSS_CURSOR_S_RESIZE: - pointer = BROWSER_POINTER_DOWN; - break; - case CSS_CURSOR_NE_RESIZE: - pointer = BROWSER_POINTER_RU; - break; - case CSS_CURSOR_SW_RESIZE: - pointer = BROWSER_POINTER_LD; - break; - case CSS_CURSOR_SE_RESIZE: - pointer = BROWSER_POINTER_RD; - break; - case CSS_CURSOR_NW_RESIZE: - pointer = BROWSER_POINTER_LU; - break; - case CSS_CURSOR_TEXT: - pointer = BROWSER_POINTER_CARET; - break; - case CSS_CURSOR_WAIT: - pointer = BROWSER_POINTER_WAIT; - break; - case CSS_CURSOR_PROGRESS: - pointer = BROWSER_POINTER_PROGRESS; - break; - case CSS_CURSOR_HELP: - pointer = BROWSER_POINTER_HELP; - break; - default: - pointer = BROWSER_POINTER_DEFAULT; - break; - } - - return pointer; -} - - -/** - * Start drag scrolling the contents of a box - * - * \param box the box to be scrolled - * \param x x ordinate of initial mouse position - * \param y y ordinate - */ - -static void html_box_drag_start(struct box *box, int x, int y) -{ - int box_x, box_y; - int scroll_mouse_x, scroll_mouse_y; - - box_coords(box, &box_x, &box_y); - - if (box->scroll_x != NULL) { - scroll_mouse_x = x - box_x ; - scroll_mouse_y = y - (box_y + box->padding[TOP] + - box->height + box->padding[BOTTOM] - - SCROLLBAR_WIDTH); - scrollbar_start_content_drag(box->scroll_x, - scroll_mouse_x, scroll_mouse_y); - } else if (box->scroll_y != NULL) { - scroll_mouse_x = x - (box_x + box->padding[LEFT] + - box->width + box->padding[RIGHT] - - SCROLLBAR_WIDTH); - scroll_mouse_y = y - box_y; - - scrollbar_start_content_drag(box->scroll_y, - scroll_mouse_x, scroll_mouse_y); - } -} - - -/** - * End overflow scroll scrollbar drags - * - * \param html html content - * \param mouse state of mouse buttons and modifier keys - * \param x coordinate of mouse - * \param y coordinate of mouse - * \param dir Direction of drag - */ -static size_t html_selection_drag_end(struct html_content *html, - browser_mouse_state mouse, int x, int y, int dir) -{ - int pixel_offset; - struct box *box; - int dx, dy; - size_t idx = 0; - - box = box_pick_text_box(html, x, y, dir, &dx, &dy); - if (box) { - plot_font_style_t fstyle; - - font_plot_style_from_css(&html->len_ctx, box->style, &fstyle); - - guit->layout->position(&fstyle, box->text, box->length, - dx, &idx, &pixel_offset); - - idx += box->byte_offset; - } - - return idx; -} - - -/** - * Handle mouse tracking (including drags) in an HTML content window. - * - * \param c content of type html - * \param bw browser window - * \param mouse state of mouse buttons and modifier keys - * \param x coordinate of mouse - * \param y coordinate of mouse - */ - -void html_mouse_track(struct content *c, struct browser_window *bw, - browser_mouse_state mouse, int x, int y) -{ - html_mouse_action(c, bw, mouse, x, y); -} - -/** - * Helper for file gadgets to store their filename. - * - * Stores the filename unencoded on the dom node associated with the - * gadget. - * - * \todo Get rid of this crap eventually - * - * \param operation DOM operation - * \param key DOM node key being considerd - * \param _data The data assocated with the key - * \param src The source DOM node. - * \param dst The destination DOM node. - */ -static void -html__image_coords_dom_user_data_handler(dom_node_operation operation, - dom_string *key, - void *_data, - struct dom_node *src, - struct dom_node *dst) -{ - struct image_input_coords *oldcoords, *coords = _data, *newcoords; - - if (!dom_string_isequal(corestring_dom___ns_key_image_coords_node_data, - key) || coords == NULL) { - return; - } - - switch (operation) { - case DOM_NODE_CLONED: - newcoords = calloc(1, sizeof(*newcoords)); - if (newcoords != NULL) { - *newcoords = *coords; - if (dom_node_set_user_data(dst, - corestring_dom___ns_key_image_coords_node_data, - newcoords, - html__image_coords_dom_user_data_handler, - &oldcoords) == DOM_NO_ERR) { - free(oldcoords); - } - } - break; - - case DOM_NODE_DELETED: - free(coords); - break; - - case DOM_NODE_RENAMED: - case DOM_NODE_IMPORTED: - case DOM_NODE_ADOPTED: - break; - - default: - NSLOG(netsurf, INFO, "User data operation not handled."); - assert(0); - } -} - -/** - * Handle mouse clicks and movements in an HTML content window. - * - * \param c content of type html - * \param bw browser window - * \param mouse state of mouse buttons and modifier keys - * \param x coordinate of mouse - * \param y coordinate of mouse - * - * This function handles both hovering and clicking. It is important that the - * code path is identical (except that hovering doesn't carry out the action), - * so that the status bar reflects exactly what will happen. Having separate - * code paths opens the possibility that an attacker will make the status bar - * show some harmless action where clicking will be harmful. - */ - -void html_mouse_action(struct content *c, struct browser_window *bw, - browser_mouse_state mouse, int x, int y) -{ - html_content *html = (html_content *) c; - enum { ACTION_NONE, ACTION_SUBMIT, ACTION_GO } action = ACTION_NONE; - const char *title = 0; - nsurl *url = 0; - char *url_s = NULL; - size_t url_l = 0; - const char *target = 0; - char status_buffer[200]; - const char *status = 0; - browser_pointer_shape pointer = BROWSER_POINTER_DEFAULT; - bool imagemap = false; - int box_x = 0, box_y = 0; - int gadget_box_x = 0, gadget_box_y = 0; - int html_object_pos_x = 0, html_object_pos_y = 0; - int text_box_x = 0; - struct box *url_box = 0; - struct box *gadget_box = 0; - struct box *text_box = 0; - struct box *box; - struct form_control *gadget = 0; - hlcache_handle *object = NULL; - struct box *html_object_box = NULL; - struct browser_window *iframe = NULL; - struct box *drag_candidate = NULL; - struct scrollbar *scrollbar = NULL; - plot_font_style_t fstyle; - int scroll_mouse_x = 0, scroll_mouse_y = 0; - int padding_left, padding_right, padding_top, padding_bottom; - browser_drag_type drag_type = browser_window_get_drag_type(bw); - union content_msg_data msg_data; - struct dom_node *node = NULL; - union html_drag_owner drag_owner; - union html_selection_owner sel_owner; - bool click = mouse & (BROWSER_MOUSE_PRESS_1 | BROWSER_MOUSE_PRESS_2 | - BROWSER_MOUSE_CLICK_1 | BROWSER_MOUSE_CLICK_2 | - BROWSER_MOUSE_DRAG_1 | BROWSER_MOUSE_DRAG_2); - - if (drag_type != DRAGGING_NONE && !mouse && - html->visible_select_menu != NULL) { - /* drag end: select menu */ - form_select_mouse_drag_end(html->visible_select_menu, - mouse, x, y); - } - - if (html->visible_select_menu != NULL) { - box = html->visible_select_menu->box; - box_coords(box, &box_x, &box_y); - - box_x -= box->border[LEFT].width; - box_y += box->height + box->border[BOTTOM].width + - box->padding[BOTTOM] + box->padding[TOP]; - status = form_select_mouse_action(html->visible_select_menu, - mouse, x - box_x, y - box_y); - if (status != NULL) { - msg_data.explicit_status_text = status; - content_broadcast(c, CONTENT_MSG_STATUS, &msg_data); - } else { - int width, height; - form_select_get_dimensions(html->visible_select_menu, - &width, &height); - html->visible_select_menu = NULL; - browser_window_redraw_rect(bw, box_x, box_y, - width, height); - } - return; - } - - if (html->drag_type == HTML_DRAG_SELECTION) { - /* Selection drag */ - struct box *box; - int dir = -1; - int dx, dy; - - if (!mouse) { - /* End of selection drag */ - int dir = -1; - size_t idx; - - if (selection_dragging_start(&html->sel)) - dir = 1; - - idx = html_selection_drag_end(html, mouse, x, y, dir); - - if (idx != 0) - selection_track(&html->sel, mouse, idx); - - drag_owner.no_owner = true; - html_set_drag_type(html, HTML_DRAG_NONE, - drag_owner, NULL); - return; - } - - if (selection_dragging_start(&html->sel)) - dir = 1; - - box = box_pick_text_box(html, x, y, dir, &dx, &dy); - - if (box != NULL) { - int pixel_offset; - size_t idx; - plot_font_style_t fstyle; - - font_plot_style_from_css(&html->len_ctx, - box->style, &fstyle); - - guit->layout->position(&fstyle, - box->text, box->length, - dx, &idx, &pixel_offset); - - selection_track(&html->sel, mouse, - box->byte_offset + idx); - } - return; - } - - if (html->drag_type == HTML_DRAG_SCROLLBAR) { - struct scrollbar *scr = html->drag_owner.scrollbar; - struct html_scrollbar_data *data = scrollbar_get_data(scr); - - if (!mouse) { - /* drag end: scrollbar */ - html_overflow_scroll_drag_end(scr, mouse, x, y); - } - - box = data->box; - box_coords(box, &box_x, &box_y); - if (scrollbar_is_horizontal(scr)) { - scroll_mouse_x = x - box_x ; - scroll_mouse_y = y - (box_y + box->padding[TOP] + - box->height + box->padding[BOTTOM] - - SCROLLBAR_WIDTH); - status = scrollbar_mouse_status_to_message( - scrollbar_mouse_action(scr, mouse, - scroll_mouse_x, - scroll_mouse_y)); - } else { - scroll_mouse_x = x - (box_x + box->padding[LEFT] + - box->width + box->padding[RIGHT] - - SCROLLBAR_WIDTH); - scroll_mouse_y = y - box_y; - status = scrollbar_mouse_status_to_message( - scrollbar_mouse_action(scr, mouse, - scroll_mouse_x, - scroll_mouse_y)); - } - - msg_data.explicit_status_text = status; - content_broadcast(c, CONTENT_MSG_STATUS, &msg_data); - return; - } - - if (html->drag_type == HTML_DRAG_TEXTAREA_SELECTION || - html->drag_type == HTML_DRAG_TEXTAREA_SCROLLBAR) { - box = html->drag_owner.textarea; - assert(box->gadget != NULL); - assert(box->gadget->type == GADGET_TEXTAREA || - box->gadget->type == GADGET_PASSWORD || - box->gadget->type == GADGET_TEXTBOX); - - box_coords(box, &box_x, &box_y); - textarea_mouse_action(box->gadget->data.text.ta, mouse, - x - box_x, y - box_y); - - /* TODO: Set appropriate statusbar message */ - return; - } - - if (html->drag_type == HTML_DRAG_CONTENT_SELECTION || - html->drag_type == HTML_DRAG_CONTENT_SCROLL) { - box = html->drag_owner.content; - assert(box->object != NULL); - - box_coords(box, &box_x, &box_y); - content_mouse_track(box->object, bw, mouse, - x - box_x, y - box_y); - return; - } - - if (html->drag_type == HTML_DRAG_CONTENT_SELECTION) { - box = html->drag_owner.content; - assert(box->object != NULL); - - box_coords(box, &box_x, &box_y); - content_mouse_track(box->object, bw, mouse, - x - box_x, y - box_y); - return; - } - - /* Content related drags handled by now */ - assert(html->drag_type == HTML_DRAG_NONE); - - /* search the box tree for a link, imagemap, form control, or - * box with scrollbars - */ - - box = html->layout; - - /* Consider the margins of the html page now */ - box_x = box->margin[LEFT]; - box_y = box->margin[TOP]; - - /* descend through visible boxes setting more specific values for: - * box - deepest box at point - * html_object_box - html object - * html_object_pos_x - html object - * html_object_pos_y - html object - * object - non html object - * iframe - iframe - * url - href or imagemap - * target - href or imagemap or gadget - * url_box - href or imagemap - * imagemap - imagemap - * gadget - gadget - * gadget_box - gadget - * gadget_box_x - gadget - * gadget_box_y - gadget - * title - title - * pointer - * - * drag_candidate - first box with scroll - * padding_left - box with scroll - * padding_right - * padding_top - * padding_bottom - * scrollbar - inside padding box stops decent - * scroll_mouse_x - inside padding box stops decent - * scroll_mouse_y - inside padding box stops decent - * - * text_box - text box - * text_box_x - text_box - */ - do { - if ((box->style != NULL) && - (css_computed_visibility(box->style) == - CSS_VISIBILITY_HIDDEN)) { - continue; - } - - if (box->node != NULL) { - node = box->node; - } - - if (box->object) { - if (content_get_type(box->object) == CONTENT_HTML) { - html_object_box = box; - html_object_pos_x = box_x; - html_object_pos_y = box_y; - } else { - object = box->object; - } - } - - if (box->iframe) { - iframe = box->iframe; - } - - if (box->href) { - url = box->href; - target = box->target; - url_box = box; - } - - if (box->usemap) { - url = imagemap_get(html, box->usemap, - box_x, box_y, x, y, &target); - if (url) { - imagemap = true; - url_box = box; - } - } - - if (box->gadget) { - gadget = box->gadget; - gadget_box = box; - gadget_box_x = box_x; - gadget_box_y = box_y; - if (gadget->form) - target = gadget->form->target; - } - - if (box->title) { - title = box->title; - } - - pointer = get_pointer_shape(box, false); - - if ((box->scroll_x != NULL) || - (box->scroll_y != NULL)) { - - if (drag_candidate == NULL) { - drag_candidate = box; - } - - padding_left = box_x + - scrollbar_get_offset(box->scroll_x); - padding_right = padding_left + box->padding[LEFT] + - box->width + box->padding[RIGHT]; - padding_top = box_y + - scrollbar_get_offset(box->scroll_y); - padding_bottom = padding_top + box->padding[TOP] + - box->height + box->padding[BOTTOM]; - - if ((x > padding_left) && - (x < padding_right) && - (y > padding_top) && - (y < padding_bottom)) { - /* mouse inside padding box */ - - if ((box->scroll_y != NULL) && - (x > (padding_right - - SCROLLBAR_WIDTH))) { - /* mouse above vertical box scroll */ - - scrollbar = box->scroll_y; - scroll_mouse_x = x - (padding_right - - SCROLLBAR_WIDTH); - scroll_mouse_y = y - padding_top; - break; - - } else if ((box->scroll_x != NULL) && - (y > (padding_bottom - - SCROLLBAR_WIDTH))) { - /* mouse above horizontal box scroll */ - - scrollbar = box->scroll_x; - scroll_mouse_x = x - padding_left; - scroll_mouse_y = y - (padding_bottom - - SCROLLBAR_WIDTH); - break; - } - } - } - - if (box->text && !box->object) { - text_box = box; - text_box_x = box_x; - } - } while ((box = box_at_point(&html->len_ctx, box, x, y, - &box_x, &box_y)) != NULL); - - /* use of box_x, box_y, or content below this point is probably a - * mistake; they will refer to the last box returned by box_at_point */ - assert(node != NULL); - - if (scrollbar) { - status = scrollbar_mouse_status_to_message( - scrollbar_mouse_action(scrollbar, mouse, - scroll_mouse_x, - scroll_mouse_y)); - pointer = BROWSER_POINTER_DEFAULT; - } else if (gadget) { - textarea_mouse_status ta_status; - - switch (gadget->type) { - case GADGET_SELECT: - status = messages_get("FormSelect"); - pointer = BROWSER_POINTER_MENU; - if (mouse & BROWSER_MOUSE_CLICK_1 && - nsoption_bool(core_select_menu)) { - html->visible_select_menu = gadget; - form_open_select_menu(c, gadget, - form_select_menu_callback, - c); - pointer = BROWSER_POINTER_DEFAULT; - } else if (mouse & BROWSER_MOUSE_CLICK_1) { - msg_data.select_menu.gadget = gadget; - content_broadcast(c, CONTENT_MSG_SELECTMENU, - &msg_data); - } - break; - case GADGET_CHECKBOX: - status = messages_get("FormCheckbox"); - if (mouse & BROWSER_MOUSE_CLICK_1) { - gadget->selected = !gadget->selected; - dom_html_input_element_set_checked( - (dom_html_input_element *)(gadget->node), - gadget->selected); - html__redraw_a_box(html, gadget_box); - } - break; - case GADGET_RADIO: - status = messages_get("FormRadio"); - if (mouse & BROWSER_MOUSE_CLICK_1) - form_radio_set(gadget); - break; - case GADGET_IMAGE: - /* This falls through to SUBMIT */ - if (mouse & BROWSER_MOUSE_CLICK_1) { - struct image_input_coords *coords, *oldcoords; - /** \todo Find a way to not ignore errors */ - coords = calloc(1, sizeof(*coords)); - if (coords == NULL) { - return; - } - coords->x = x - gadget_box_x; - coords->y = y - gadget_box_y; - if (dom_node_set_user_data( - gadget->node, - corestring_dom___ns_key_image_coords_node_data, - coords, html__image_coords_dom_user_data_handler, - &oldcoords) != DOM_NO_ERR) - return; - free(oldcoords); - } - /* Fall through */ - case GADGET_SUBMIT: - if (gadget->form) { - snprintf(status_buffer, sizeof status_buffer, - messages_get("FormSubmit"), - gadget->form->action); - status = status_buffer; - pointer = get_pointer_shape(gadget_box, false); - if (mouse & (BROWSER_MOUSE_CLICK_1 | - BROWSER_MOUSE_CLICK_2)) - action = ACTION_SUBMIT; - } else { - status = messages_get("FormBadSubmit"); - } - break; - case GADGET_TEXTBOX: - case GADGET_PASSWORD: - case GADGET_TEXTAREA: - if (gadget->type == GADGET_TEXTAREA) - status = messages_get("FormTextarea"); - else - status = messages_get("FormTextbox"); - - if (click && (html->selection_type != - HTML_SELECTION_TEXTAREA || - html->selection_owner.textarea != - gadget_box)) { - sel_owner.none = true; - html_set_selection(html, HTML_SELECTION_NONE, - sel_owner, true); - } - - ta_status = textarea_mouse_action(gadget->data.text.ta, - mouse, x - gadget_box_x, - y - gadget_box_y); - - if (ta_status & TEXTAREA_MOUSE_EDITOR) { - pointer = get_pointer_shape(gadget_box, false); - } else { - pointer = BROWSER_POINTER_DEFAULT; - status = scrollbar_mouse_status_to_message( - ta_status >> 3); - } - break; - case GADGET_HIDDEN: - /* not possible: no box generated */ - break; - case GADGET_RESET: - status = messages_get("FormReset"); - break; - case GADGET_FILE: - status = messages_get("FormFile"); - if (mouse & BROWSER_MOUSE_CLICK_1) { - msg_data.gadget_click.gadget = gadget; - content_broadcast(c, CONTENT_MSG_GADGETCLICK, - &msg_data); - } - break; - case GADGET_BUTTON: - /* This gadget cannot be activated */ - status = messages_get("FormButton"); - break; - } - - } else if (object && (mouse & BROWSER_MOUSE_MOD_2)) { - - if (mouse & BROWSER_MOUSE_DRAG_2) { - msg_data.dragsave.type = CONTENT_SAVE_NATIVE; - msg_data.dragsave.content = object; - content_broadcast(c, CONTENT_MSG_DRAGSAVE, &msg_data); - - } else if (mouse & BROWSER_MOUSE_DRAG_1) { - msg_data.dragsave.type = CONTENT_SAVE_ORIG; - msg_data.dragsave.content = object; - content_broadcast(c, CONTENT_MSG_DRAGSAVE, &msg_data); - } - - /* \todo should have a drag-saving object msg */ - - } else if (iframe) { - int pos_x, pos_y; - float scale = browser_window_get_scale(bw); - - browser_window_get_position(iframe, false, &pos_x, &pos_y); - - pos_x /= scale; - pos_y /= scale; - - if (mouse & BROWSER_MOUSE_CLICK_1 || - mouse & BROWSER_MOUSE_CLICK_2) { - browser_window_mouse_click(iframe, mouse, - x - pos_x, y - pos_y); - } else { - browser_window_mouse_track(iframe, mouse, - x - pos_x, y - pos_y); - } - } else if (html_object_box) { - - if (click && (html->selection_type != HTML_SELECTION_CONTENT || - html->selection_owner.content != - html_object_box)) { - sel_owner.none = true; - html_set_selection(html, HTML_SELECTION_NONE, - sel_owner, true); - } - if (mouse & BROWSER_MOUSE_CLICK_1 || - mouse & BROWSER_MOUSE_CLICK_2) { - content_mouse_action(html_object_box->object, - bw, mouse, - x - html_object_pos_x, - y - html_object_pos_y); - } else { - content_mouse_track(html_object_box->object, - bw, mouse, - x - html_object_pos_x, - y - html_object_pos_y); - } - } else if (url) { - if (nsoption_bool(display_decoded_idn) == true) { - if (nsurl_get_utf8(url, &url_s, &url_l) != NSERROR_OK) { - /* Unable to obtain a decoded IDN. This is not a fatal error. - * Ensure the string pointer is NULL so we use the encoded version. */ - url_s = NULL; - } - } - - if (title) { - snprintf(status_buffer, sizeof status_buffer, "%s: %s", - url_s ? url_s : nsurl_access(url), title); - } else { - snprintf(status_buffer, sizeof status_buffer, "%s", - url_s ? url_s : nsurl_access(url)); - } - - status = status_buffer; - - if (url_s != NULL) - free(url_s); - - pointer = get_pointer_shape(url_box, imagemap); - - if (mouse & BROWSER_MOUSE_CLICK_1 && - mouse & BROWSER_MOUSE_MOD_1) { - /* force download of link */ - browser_window_navigate(bw, - url, - content_get_url(c), - BW_NAVIGATE_DOWNLOAD, - NULL, - NULL, - NULL); - - } else if (mouse & BROWSER_MOUSE_CLICK_2 && - mouse & BROWSER_MOUSE_MOD_1) { - msg_data.savelink.url = url; - msg_data.savelink.title = title; - content_broadcast(c, CONTENT_MSG_SAVELINK, &msg_data); - - } else if (mouse & (BROWSER_MOUSE_CLICK_1 | - BROWSER_MOUSE_CLICK_2)) - action = ACTION_GO; - } else { - bool done = false; - - /* frame resizing */ - if (browser_window_frame_resize_start(bw, mouse, x, y, - &pointer)) { - if (mouse & (BROWSER_MOUSE_DRAG_1 | - BROWSER_MOUSE_DRAG_2)) { - status = messages_get("FrameDrag"); - } - done = true; - } - - /* if clicking in the main page, remove the selection from any - * text areas */ - if (!done) { - - if (click && html->focus_type != HTML_FOCUS_SELF) { - union html_focus_owner fo; - fo.self = true; - html_set_focus(html, HTML_FOCUS_SELF, fo, - true, 0, 0, 0, NULL); - } - if (click && html->selection_type != - HTML_SELECTION_SELF) { - sel_owner.none = true; - html_set_selection(html, HTML_SELECTION_NONE, - sel_owner, true); - } - - if (text_box) { - int pixel_offset; - size_t idx; - - font_plot_style_from_css(&html->len_ctx, - text_box->style, &fstyle); - - guit->layout->position(&fstyle, - text_box->text, - text_box->length, - x - text_box_x, - &idx, - &pixel_offset); - - if (selection_click(&html->sel, mouse, - text_box->byte_offset + idx)) { - /* key presses must be directed at the - * main browser window, paste text - * operations ignored */ - html_drag_type drag_type; - union html_drag_owner drag_owner; - - if (selection_dragging(&html->sel)) { - drag_type = HTML_DRAG_SELECTION; - drag_owner.no_owner = true; - html_set_drag_type(html, - drag_type, - drag_owner, - NULL); - status = messages_get( - "Selecting"); - } - - done = true; - } - - } else if (mouse & BROWSER_MOUSE_PRESS_1) { - sel_owner.none = true; - selection_clear(&html->sel, true); - } - - if (selection_defined(&html->sel)) { - sel_owner.none = false; - html_set_selection(html, HTML_SELECTION_SELF, - sel_owner, true); - } else if (click && html->selection_type != - HTML_SELECTION_NONE) { - sel_owner.none = true; - html_set_selection(html, HTML_SELECTION_NONE, - sel_owner, true); - } - } - - if (!done) { - if (title) - status = title; - - if (mouse & BROWSER_MOUSE_DRAG_1) { - if (mouse & BROWSER_MOUSE_MOD_2) { - msg_data.dragsave.type = - CONTENT_SAVE_COMPLETE; - msg_data.dragsave.content = NULL; - content_broadcast(c, - CONTENT_MSG_DRAGSAVE, - &msg_data); - } else { - if (drag_candidate == NULL) { - browser_window_page_drag_start( - bw, x, y); - } else { - html_box_drag_start( - drag_candidate, - x, y); - } - pointer = BROWSER_POINTER_MOVE; - } - } - else if (mouse & BROWSER_MOUSE_DRAG_2) { - if (mouse & BROWSER_MOUSE_MOD_2) { - msg_data.dragsave.type = - CONTENT_SAVE_SOURCE; - msg_data.dragsave.content = NULL; - content_broadcast(c, - CONTENT_MSG_DRAGSAVE, - &msg_data); - } else { - if (drag_candidate == NULL) { - browser_window_page_drag_start( - bw, x, y); - } else { - html_box_drag_start( - drag_candidate, - x, y); - } - pointer = BROWSER_POINTER_MOVE; - } - } - } - if (mouse && mouse < BROWSER_MOUSE_MOD_1) { - /* ensure key presses still act on the browser window */ - union html_focus_owner fo; - fo.self = true; - html_set_focus(html, HTML_FOCUS_SELF, fo, - true, 0, 0, 0, NULL); - } - } - - if (!iframe && !html_object_box) { - msg_data.explicit_status_text = status; - content_broadcast(c, CONTENT_MSG_STATUS, &msg_data); - - msg_data.pointer = pointer; - content_broadcast(c, CONTENT_MSG_POINTER, &msg_data); - } - - /* fire dom click event */ - if (mouse & BROWSER_MOUSE_CLICK_1) { - fire_dom_event(corestring_dom_click, node, true, true); - } - - /* deferred actions that can cause this browser_window to be destroyed - * and must therefore be done after set_status/pointer - */ - switch (action) { - case ACTION_SUBMIT: - form_submit(content_get_url(c), - browser_window_find_target(bw, target, mouse), - gadget->form, gadget); - break; - case ACTION_GO: - browser_window_navigate(browser_window_find_target(bw, target, mouse), - url, - content_get_url(c), - BW_NAVIGATE_HISTORY, - NULL, - NULL, - NULL); - break; - case ACTION_NONE: - break; - } -} - - -/** - * Handle keypresses. - * - * \param c content of type HTML - * \param key The UCS4 character codepoint - * \return true if key handled, false otherwise - */ - -bool html_keypress(struct content *c, uint32_t key) -{ - html_content *html = (html_content *) c; - struct selection *sel = &html->sel; - struct box *box; - - switch (html->focus_type) { - case HTML_FOCUS_CONTENT: - box = html->focus_owner.content; - return content_keypress(box->object, key); - - case HTML_FOCUS_TEXTAREA: - box = html->focus_owner.textarea; - return box_textarea_keypress(html, box, key); - - default: - /* Deal with it below */ - break; - } - - switch (key) { - case NS_KEY_COPY_SELECTION: - selection_copy_to_clipboard(sel); - return true; - - case NS_KEY_CLEAR_SELECTION: - selection_clear(sel, true); - return true; - - case NS_KEY_SELECT_ALL: - selection_select_all(sel); - return true; - - case NS_KEY_ESCAPE: - if (selection_defined(sel)) { - selection_clear(sel, true); - return true; - } - - /* if there's no selection, leave Escape for the caller */ - return false; - } - - return false; -} - - -/** - * Handle search. - * - * \param c content of type HTML - * \param context front end private data - * \param flags search flags - * \param string search string - */ -void html_search(struct content *c, void *context, - search_flags_t flags, const char *string) -{ - html_content *html = (html_content *)c; - - assert(c != NULL); - - if (string != NULL && html->search_string != NULL && - strcmp(string, html->search_string) == 0 && - html->search != NULL) { - /* Continue prev. search */ - search_step(html->search, flags, string); - - } else if (string != NULL) { - /* New search */ - free(html->search_string); - html->search_string = strdup(string); - if (html->search_string == NULL) - return; - - if (html->search != NULL) { - search_destroy_context(html->search); - html->search = NULL; - } - - html->search = search_create_context(c, CONTENT_HTML, context); - - if (html->search == NULL) - return; - - search_step(html->search, flags, string); - - } else { - /* Clear search */ - html_search_clear(c); - - free(html->search_string); - html->search_string = NULL; - } -} - - -/** - * Terminate a search. - * - * \param c content of type HTML - */ -void html_search_clear(struct content *c) -{ - html_content *html = (html_content *)c; - - assert(c != NULL); - - free(html->search_string); - html->search_string = NULL; - - if (html->search != NULL) { - search_destroy_context(html->search); - } - html->search = NULL; -} - - -/** - * Callback for in-page scrollbars. - */ -void html_overflow_scroll_callback(void *client_data, - struct scrollbar_msg_data *scrollbar_data) -{ - struct html_scrollbar_data *data = client_data; - html_content *html = (html_content *)data->c; - struct box *box = data->box; - union content_msg_data msg_data; - html_drag_type drag_type; - union html_drag_owner drag_owner; - - switch(scrollbar_data->msg) { - case SCROLLBAR_MSG_MOVED: - - if (html->reflowing == true) { - /* Can't redraw during layout, and it will - * be redrawn after layout anyway. */ - break; - } - - html__redraw_a_box(html, box); - break; - case SCROLLBAR_MSG_SCROLL_START: - { - struct rect rect = { - .x0 = scrollbar_data->x0, - .y0 = scrollbar_data->y0, - .x1 = scrollbar_data->x1, - .y1 = scrollbar_data->y1 - }; - drag_type = HTML_DRAG_SCROLLBAR; - drag_owner.scrollbar = scrollbar_data->scrollbar; - html_set_drag_type(html, drag_type, drag_owner, &rect); - } - break; - case SCROLLBAR_MSG_SCROLL_FINISHED: - drag_type = HTML_DRAG_NONE; - drag_owner.no_owner = true; - html_set_drag_type(html, drag_type, drag_owner, NULL); - - msg_data.pointer = BROWSER_POINTER_AUTO; - content_broadcast(data->c, CONTENT_MSG_POINTER, &msg_data); - break; - } -} - - -/** - * End overflow scroll scrollbar drags - * - * \param scrollbar scrollbar widget - * \param mouse state of mouse buttons and modifier keys - * \param x coordinate of mouse - * \param y coordinate of mouse - */ -void html_overflow_scroll_drag_end(struct scrollbar *scrollbar, - browser_mouse_state mouse, int x, int y) -{ - int scroll_mouse_x, scroll_mouse_y, box_x, box_y; - struct html_scrollbar_data *data = scrollbar_get_data(scrollbar); - struct box *box; - - box = data->box; - box_coords(box, &box_x, &box_y); - - if (scrollbar_is_horizontal(scrollbar)) { - scroll_mouse_x = x - box_x; - scroll_mouse_y = y - (box_y + box->padding[TOP] + - box->height + box->padding[BOTTOM] - - SCROLLBAR_WIDTH); - scrollbar_mouse_drag_end(scrollbar, mouse, - scroll_mouse_x, scroll_mouse_y); - } else { - scroll_mouse_x = x - (box_x + box->padding[LEFT] + - box->width + box->padding[RIGHT] - - SCROLLBAR_WIDTH); - scroll_mouse_y = y - box_y; - scrollbar_mouse_drag_end(scrollbar, mouse, - scroll_mouse_x, scroll_mouse_y); - } -} - -/* Documented in html_internal.h */ -void html_set_drag_type(html_content *html, html_drag_type drag_type, - union html_drag_owner drag_owner, const struct rect *rect) -{ - union content_msg_data msg_data; - - assert(html != NULL); - - html->drag_type = drag_type; - html->drag_owner = drag_owner; - - switch (drag_type) { - case HTML_DRAG_NONE: - assert(drag_owner.no_owner == true); - msg_data.drag.type = CONTENT_DRAG_NONE; - break; - - case HTML_DRAG_SCROLLBAR: - case HTML_DRAG_TEXTAREA_SCROLLBAR: - case HTML_DRAG_CONTENT_SCROLL: - msg_data.drag.type = CONTENT_DRAG_SCROLL; - break; - - case HTML_DRAG_SELECTION: - assert(drag_owner.no_owner == true); - /* Fall through */ - case HTML_DRAG_TEXTAREA_SELECTION: - case HTML_DRAG_CONTENT_SELECTION: - msg_data.drag.type = CONTENT_DRAG_SELECTION; - break; - } - msg_data.drag.rect = rect; - - /* Inform of the content's drag status change */ - content_broadcast((struct content *)html, CONTENT_MSG_DRAG, &msg_data); -} - -/* Documented in html_internal.h */ -void html_set_focus(html_content *html, html_focus_type focus_type, - union html_focus_owner focus_owner, bool hide_caret, - int x, int y, int height, const struct rect *clip) -{ - union content_msg_data msg_data; - int x_off = 0; - int y_off = 0; - struct rect cr; - bool textarea_lost_focus = html->focus_type == HTML_FOCUS_TEXTAREA && - focus_type != HTML_FOCUS_TEXTAREA; - - assert(html != NULL); - - switch (focus_type) { - case HTML_FOCUS_SELF: - assert(focus_owner.self == true); - if (html->focus_type == HTML_FOCUS_SELF) - /* Don't need to tell anyone anything */ - return; - break; - - case HTML_FOCUS_CONTENT: - box_coords(focus_owner.content, &x_off, &y_off); - break; - - case HTML_FOCUS_TEXTAREA: - box_coords(focus_owner.textarea, &x_off, &y_off); - break; - } - - html->focus_type = focus_type; - html->focus_owner = focus_owner; - - if (textarea_lost_focus) { - msg_data.caret.type = CONTENT_CARET_REMOVE; - } else if (focus_type != HTML_FOCUS_SELF && hide_caret) { - msg_data.caret.type = CONTENT_CARET_HIDE; - } else { - if (clip != NULL) { - cr = *clip; - cr.x0 += x_off; - cr.y0 += y_off; - cr.x1 += x_off; - cr.y1 += y_off; - } - - msg_data.caret.type = CONTENT_CARET_SET_POS; - msg_data.caret.pos.x = x + x_off; - msg_data.caret.pos.y = y + y_off; - msg_data.caret.pos.height = height; - msg_data.caret.pos.clip = (clip == NULL) ? NULL : &cr; - } - - /* Inform of the content's drag status change */ - content_broadcast((struct content *)html, CONTENT_MSG_CARET, &msg_data); -} - -/* Documented in html_internal.h */ -void html_set_selection(html_content *html, html_selection_type selection_type, - union html_selection_owner selection_owner, bool read_only) -{ - union content_msg_data msg_data; - struct box *box; - bool changed = false; - bool same_type = html->selection_type == selection_type; - - assert(html != NULL); - - if ((selection_type == HTML_SELECTION_NONE && - html->selection_type != HTML_SELECTION_NONE) || - (selection_type != HTML_SELECTION_NONE && - html->selection_type == HTML_SELECTION_NONE)) - /* Existance of selection has changed, and we'll need to - * inform our owner */ - changed = true; - - /* Clear any existing selection */ - if (html->selection_type != HTML_SELECTION_NONE) { - switch (html->selection_type) { - case HTML_SELECTION_SELF: - if (same_type) - break; - selection_clear(&html->sel, true); - break; - case HTML_SELECTION_TEXTAREA: - if (same_type && html->selection_owner.textarea == - selection_owner.textarea) - break; - box = html->selection_owner.textarea; - textarea_clear_selection(box->gadget->data.text.ta); - break; - case HTML_SELECTION_CONTENT: - if (same_type && html->selection_owner.content == - selection_owner.content) - break; - box = html->selection_owner.content; - content_clear_selection(box->object); - break; - default: - break; - } - } - - html->selection_type = selection_type; - html->selection_owner = selection_owner; - - if (!changed) - /* Don't need to report lack of change to owner */ - return; - - /* Prepare msg */ - switch (selection_type) { - case HTML_SELECTION_NONE: - assert(selection_owner.none == true); - msg_data.selection.selection = false; - break; - case HTML_SELECTION_SELF: - assert(selection_owner.none == false); - /* fall through */ - case HTML_SELECTION_TEXTAREA: - case HTML_SELECTION_CONTENT: - msg_data.selection.selection = true; - break; - default: - break; - } - msg_data.selection.read_only = read_only; - - /* Inform of the content's selection status change */ - content_broadcast((struct content *)html, CONTENT_MSG_SELECTION, - &msg_data); -} diff --git a/render/html_internal.h b/render/html_internal.h deleted file mode 100644 index 66ecb2b36..000000000 --- a/render/html_internal.h +++ /dev/null @@ -1,410 +0,0 @@ -/* - * Copyright 2004 James Bursa <bursa@users.sourceforge.net> - * - * 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 <http://www.gnu.org/licenses/>. - */ - -/** - * \file - * Content for text/html (private data). - */ - -#ifndef NETSURF_RENDER_HTML_INTERNAL_H_ -#define NETSURF_RENDER_HTML_INTERNAL_H_ - -#include <libcss/libcss.h> - -#include "content/handlers/css/utils.h" -#include "content/content_protected.h" -#include "desktop/selection.h" -#include "render/html.h" - -struct gui_layout_table; - -typedef enum { - HTML_DRAG_NONE, /** No drag */ - HTML_DRAG_SELECTION, /** Own; Text selection */ - HTML_DRAG_SCROLLBAR, /** Not own; drag in scrollbar widget */ - HTML_DRAG_TEXTAREA_SELECTION, /** Not own; drag in textarea widget */ - HTML_DRAG_TEXTAREA_SCROLLBAR, /** Not own; drag in textarea widget */ - HTML_DRAG_CONTENT_SELECTION, /** Not own; drag in child content */ - HTML_DRAG_CONTENT_SCROLL /** Not own; drag in child content */ -} html_drag_type; - -union html_drag_owner { - bool no_owner; - struct box *content; - struct scrollbar *scrollbar; - struct box *textarea; -}; /**< For drags we don't own */ - -typedef enum { - HTML_SELECTION_NONE, /** No selection */ - HTML_SELECTION_TEXTAREA, /** Selection in one of our textareas */ - HTML_SELECTION_SELF, /** Selection in this html content */ - HTML_SELECTION_CONTENT /** Selection in child content */ -} html_selection_type; -union html_selection_owner { - bool none; - struct box *textarea; - struct box *content; -}; /**< For getting at selections in this content or things in this content */ - -typedef enum { - HTML_FOCUS_SELF, /** Focus is our own */ - HTML_FOCUS_CONTENT, /** Focus belongs to child content */ - HTML_FOCUS_TEXTAREA /** Focus belongs to textarea */ -} html_focus_type; -union html_focus_owner { - bool self; - struct box *textarea; - struct box *content; -}; /**< For directing input */ - -/** Data specific to CONTENT_HTML. */ -typedef struct html_content { - struct content base; - - dom_hubbub_parser *parser; /**< Parser object handle */ - bool parse_completed; /**< Whether the parse has been completed */ - - /** Document tree */ - dom_document *document; - /** Quirkyness of document */ - dom_document_quirks_mode quirks; - - /** Encoding of source, NULL if unknown. */ - char *encoding; - /** Source of encoding information. */ - dom_hubbub_encoding_source encoding_source; - - /** Base URL (may be a copy of content->url). */ - nsurl *base_url; - /** Base target */ - char *base_target; - - /** CSS length conversion context for document. */ - nscss_len_ctx len_ctx; - - /** Content has been aborted in the LOADING state */ - bool aborted; - - /** Whether a meta refresh has been handled */ - bool refresh; - - /** Whether a layout (reflow) is in progress */ - bool reflowing; - - /** Whether scripts are enabled for this content */ - bool enable_scripting; - - /* Title element node */ - dom_node *title; - - /** A talloc context purely for the render box tree */ - int *bctx; - /** Box tree, or NULL. */ - struct box *layout; - /** Document background colour. */ - colour background_colour; - - /** Font callback table */ - const struct gui_layout_table *font_func; - - /** Number of entries in scripts */ - unsigned int scripts_count; - /** Scripts */ - struct html_script *scripts; - /** javascript context */ - struct jscontext *jscontext; - - /** Number of entries in stylesheet_content. */ - unsigned int stylesheet_count; - /** Stylesheets. Each may be NULL. */ - struct html_stylesheet *stylesheets; - /**< Style selection context */ - css_select_ctx *select_ctx; - /**< Universal selector */ - lwc_string *universal; - - /** Number of entries in object_list. */ - unsigned int num_objects; - /** List of objects. */ - struct content_html_object *object_list; - /** Forms, in reverse order to document. */ - struct form *forms; - /** Hash table of imagemaps. */ - struct imagemap **imagemaps; - - /** Browser window containing this document, or NULL if not open. */ - struct browser_window *bw; - - /** Frameset information */ - struct content_html_frames *frameset; - - /** Inline frame information */ - struct content_html_iframe *iframe; - - /** Content of type CONTENT_HTML containing this, or NULL if not an - * object within a page. */ - struct html_content *page; - - /** Current drag type */ - html_drag_type drag_type; - /** Widget capturing all mouse events */ - union html_drag_owner drag_owner; - - /** Current selection state */ - html_selection_type selection_type; - /** Current selection owner */ - union html_selection_owner selection_owner; - - /** Current input focus target type */ - html_focus_type focus_type; - /** Current input focus target */ - union html_focus_owner focus_owner; - - /** HTML content's own text selection object */ - struct selection sel; - - /** Open core-handled form SELECT menu, - * or NULL if none currently open. */ - struct form_control *visible_select_menu; - - /** Context for free text search, or NULL if none */ - struct search_context *search; - /** Search string or NULL */ - char *search_string; - -} html_content; - -/** Render padding and margin box outlines in html_redraw(). */ -extern bool html_redraw_debug; - -void html__redraw_a_box(html_content *html, struct box *box); - -/** - * Set our drag status, and inform whatever owns the content - * - * \param html HTML content - * \param drag_type Type of drag - * \param drag_owner What owns the drag - * \param rect Pointer movement bounds - */ -void html_set_drag_type(html_content *html, html_drag_type drag_type, - union html_drag_owner drag_owner, const struct rect *rect); - -/** - * Set our selection status, and inform whatever owns the content - * - * \param html HTML content - * \param selection_type Type of selection - * \param selection_owner What owns the selection - * \param read_only True iff selection is read only - */ -void html_set_selection(html_content *html, html_selection_type selection_type, - union html_selection_owner selection_owner, bool read_only); - -/** - * Set our input focus, and inform whatever owns the content - * - * \param html HTML content - * \param focus_type Type of input focus - * \param focus_owner What owns the focus - * \param hide_caret True iff caret to be hidden - * \param x Carret x-coord rel to owner - * \param y Carret y-coord rel to owner - * \param height Carret height - * \param clip Carret clip rect - */ -void html_set_focus(html_content *html, html_focus_type focus_type, - union html_focus_owner focus_owner, bool hide_caret, - int x, int y, int height, const struct rect *clip); - - -struct browser_window *html_get_browser_window(struct content *c); - -/** - * Complete conversion of an HTML document - * - * \param htmlc Content to convert - */ -void html_finish_conversion(html_content *htmlc); - -/** - * Test if an HTML content conversion can begin - * - * \param htmlc html content to test - * \return true iff the html content conversion can begin - */ -bool html_can_begin_conversion(html_content *htmlc); - -/** - * Begin conversion of an HTML document - * - * \param htmlc Content to convert - */ -bool html_begin_conversion(html_content *htmlc); - -/* in render/html_redraw.c */ -bool html_redraw(struct content *c, struct content_redraw_data *data, - const struct rect *clip, const struct redraw_context *ctx); - -/* in render/html_redraw_border.c */ -bool html_redraw_borders(struct box *box, int x_parent, int y_parent, - int p_width, int p_height, const struct rect *clip, float scale, - const struct redraw_context *ctx); - -bool html_redraw_inline_borders(struct box *box, struct rect b, - const struct rect *clip, float scale, bool first, bool last, - const struct redraw_context *ctx); - -/* in render/html_interaction.c */ -void html_mouse_track(struct content *c, struct browser_window *bw, - browser_mouse_state mouse, int x, int y); -void html_mouse_action(struct content *c, struct browser_window *bw, - browser_mouse_state mouse, int x, int y); -bool html_keypress(struct content *c, uint32_t key); -void html_overflow_scroll_callback(void *client_data, - struct scrollbar_msg_data *scrollbar_data); -void html_search(struct content *c, void *context, - search_flags_t flags, const char *string); -void html_search_clear(struct content *c); - - -/* in render/html_script.c */ -dom_hubbub_error html_process_script(void *ctx, dom_node *node); - -/** - * Attempt script execution for defer and async scripts - * - * execute scripts using algorithm found in: - * http://www.whatwg.org/specs/web-apps/current-work/multipage/scripting-1.html#the-script-element - * - * \param htmlc html content. - * \return NSERROR_OK error code. - */ -nserror html_script_exec(html_content *htmlc); - -/** - * Free all script resources and references for a html content. - * - * \param htmlc html content. - * \return NSERROR_OK or error code. - */ -nserror html_script_free(html_content *htmlc); - -/** - * Ensure the html content javascript context is invalidated. - * - * \param htmlc html content. - * \return NSERROR_OK or error code. - */ -nserror html_script_invalidate_ctx(html_content *htmlc); - -/* in render/html_forms.c */ -struct form *html_forms_get_forms(const char *docenc, dom_html_document *doc); -struct form_control *html_forms_get_control_for_node(struct form *forms, - dom_node *node); - -/* in render/html_css.c */ -nserror html_css_init(void); -void html_css_fini(void); - -/** - * Initialise core stylesheets for a content - * - * \param c content structure to update - * \return nserror - */ -nserror html_css_new_stylesheets(html_content *c); -nserror html_css_quirks_stylesheets(html_content *c); -nserror html_css_free_stylesheets(html_content *html); - -bool html_css_process_link(html_content *htmlc, dom_node *node); -bool html_css_process_style(html_content *htmlc, dom_node *node); -bool html_css_update_style(html_content *c, dom_node *style); - -nserror html_css_new_selection_context(html_content *c, - css_select_ctx **ret_select_ctx); - -/* in render/html_css_fetcher.c */ -/** - * Register the fetcher for the pseudo x-ns-css scheme. - * - * \return NSERROR_OK on successful registration or error code on failure. - */ -nserror html_css_fetcher_register(void); -nserror html_css_fetcher_add_item(dom_string *data, nsurl *base_url, - uint32_t *key); - -/* in render/html_object.c */ - -/** - * Start a fetch for an object required by a page. - * - * \param c content of type CONTENT_HTML - * \param url URL of object to fetch (copied) - * \param box box that will contain the object - * \param permitted_types bitmap of acceptable types - * \param available_width estimate of width of object - * \param available_height estimate of height of object - * \param background this is a background image - * \return true on success, false on memory exhaustion - */ -bool html_fetch_object(html_content *c, nsurl *url, struct box *box, - content_type permitted_types, - int available_width, int available_height, - bool background); - -nserror html_object_free_objects(html_content *html); -nserror html_object_close_objects(html_content *html); -nserror html_object_open_objects(html_content *html, struct browser_window *bw); -nserror html_object_abort_objects(html_content *html); - -/* Events */ -/** - * Construct an event and fire it at the DOM - * - */ -bool fire_dom_event(dom_string *type, dom_node *target, - bool bubbles, bool cancelable); - -/* Useful dom_string pointers */ -struct dom_string; - -extern struct dom_string *html_dom_string_map; -extern struct dom_string *html_dom_string_id; -extern struct dom_string *html_dom_string_name; -extern struct dom_string *html_dom_string_area; -extern struct dom_string *html_dom_string_a; -extern struct dom_string *html_dom_string_nohref; -extern struct dom_string *html_dom_string_href; -extern struct dom_string *html_dom_string_target; -extern struct dom_string *html_dom_string_shape; -extern struct dom_string *html_dom_string_default; -extern struct dom_string *html_dom_string_rect; -extern struct dom_string *html_dom_string_rectangle; -extern struct dom_string *html_dom_string_coords; -extern struct dom_string *html_dom_string_circle; -extern struct dom_string *html_dom_string_poly; -extern struct dom_string *html_dom_string_polygon; -extern struct dom_string *html_dom_string_text_javascript; -extern struct dom_string *html_dom_string_type; -extern struct dom_string *html_dom_string_src; - -#endif - - diff --git a/render/html_object.c b/render/html_object.c deleted file mode 100644 index 74e4bf0f3..000000000 --- a/render/html_object.c +++ /dev/null @@ -1,729 +0,0 @@ -/* - * Copyright 2013 Vincent Sanders <vince@netsurf-browser.org> - * - * 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 <http://www.gnu.org/licenses/>. - */ - -/** \file - * Processing for html content object operations. - */ - -#include <assert.h> -#include <ctype.h> -#include <stdint.h> -#include <string.h> -#include <strings.h> -#include <stdlib.h> -#include <nsutils/time.h> - -#include "utils/corestrings.h" -#include "utils/config.h" -#include "utils/log.h" -#include "utils/nsoption.h" -#include "netsurf/content.h" -#include "netsurf/misc.h" -#include "content/hlcache.h" -#include "css/utils.h" -#include "desktop/scrollbar.h" -#include "desktop/gui_internal.h" - -#include "render/box.h" -#include "render/html_internal.h" - -/* break reference loop */ -static void html_object_refresh(void *p); - -/** - * Retrieve objects used by HTML document - * - * \param h Content to retrieve objects from - * \param n Pointer to location to receive number of objects - * \return Pointer to list of objects - */ -struct content_html_object *html_get_objects(hlcache_handle *h, unsigned int *n) -{ - html_content *c = (html_content *) hlcache_handle_get_content(h); - - assert(c != NULL); - assert(n != NULL); - - *n = c->num_objects; - - return c->object_list; -} - -/** - * Handle object fetching or loading failure. - * - * \param box box containing object which failed to load - * \param content document of type CONTENT_HTML - * \param background the object was the background image for the box - */ - -static void -html_object_failed(struct box *box, html_content *content, bool background) -{ - /* Nothing to do */ - return; -} - -/** - * Update a box whose content has completed rendering. - */ - -static void -html_object_done(struct box *box, - hlcache_handle *object, - bool background) -{ - struct box *b; - - if (background) { - box->background = object; - return; - } - - box->object = object; - - /* Normalise the box type, now it has been replaced. */ - switch (box->type) { - case BOX_TABLE: - box->type = BOX_BLOCK; - break; - default: - /* TODO: Any other box types need mapping? */ - break; - } - - if (!(box->flags & REPLACE_DIM)) { - /* invalidate parent min, max widths */ - for (b = box; b; b = b->parent) - b->max_width = UNKNOWN_MAX_WIDTH; - - /* delete any clones of this box */ - while (box->next && (box->next->flags & CLONE)) { - /* box_free_box(box->next); */ - box->next = box->next->next; - } - } -} - -/** - * Callback for hlcache_handle_retrieve() for objects. - */ - -static nserror -html_object_callback(hlcache_handle *object, - const hlcache_event *event, - void *pw) -{ - struct content_html_object *o = pw; - html_content *c = (html_content *) o->parent; - int x, y; - struct box *box; - - box = o->box; - if (box == NULL && - event->type != CONTENT_MSG_ERROR && - event->type != CONTENT_MSG_ERRORCODE) { - return NSERROR_OK; - } - - switch (event->type) { - case CONTENT_MSG_LOADING: - if (c->base.status != CONTENT_STATUS_LOADING && c->bw != NULL) - content_open(object, - c->bw, &c->base, - box->object_params); - break; - - case CONTENT_MSG_READY: - if (content_can_reformat(object)) { - /* TODO: avoid knowledge of box internals here */ - content_reformat(object, false, - box->max_width != UNKNOWN_MAX_WIDTH ? - box->width : 0, - box->max_width != UNKNOWN_MAX_WIDTH ? - box->height : 0); - - /* Adjust parent content for new object size */ - html_object_done(box, object, o->background); - if (c->base.status == CONTENT_STATUS_READY || - c->base.status == CONTENT_STATUS_DONE) - content__reformat(&c->base, false, - c->base.available_width, - c->base.height); - } - break; - - case CONTENT_MSG_DONE: - c->base.active--; - NSLOG(netsurf, INFO, "%d fetches active", c->base.active); - - html_object_done(box, object, o->background); - - if (c->base.status != CONTENT_STATUS_LOADING && - box->flags & REPLACE_DIM) { - union content_msg_data data; - - if (!box_visible(box)) - break; - - box_coords(box, &x, &y); - - data.redraw.x = x + box->padding[LEFT]; - data.redraw.y = y + box->padding[TOP]; - data.redraw.width = box->width; - data.redraw.height = box->height; - data.redraw.full_redraw = true; - - content_broadcast(&c->base, CONTENT_MSG_REDRAW, &data); - } - break; - - case CONTENT_MSG_ERRORCODE: - case CONTENT_MSG_ERROR: - hlcache_handle_release(object); - - o->content = NULL; - - if (box != NULL) { - c->base.active--; - NSLOG(netsurf, INFO, "%d fetches active", - c->base.active); - - content_add_error(&c->base, "?", 0); - html_object_failed(box, c, o->background); - } - break; - - case CONTENT_MSG_REDRAW: - if (c->base.status != CONTENT_STATUS_LOADING) { - union content_msg_data data = event->data; - - if (!box_visible(box)) - break; - - box_coords(box, &x, &y); - - if (object == box->background) { - /* Redraw request is for background */ - css_fixed hpos = 0, vpos = 0; - css_unit hunit = CSS_UNIT_PX; - css_unit vunit = CSS_UNIT_PX; - int width = box->padding[LEFT] + box->width + - box->padding[RIGHT]; - int height = box->padding[TOP] + box->height + - box->padding[BOTTOM]; - int t, h, l, w; - - /* Need to know background-position */ - css_computed_background_position(box->style, - &hpos, &hunit, &vpos, &vunit); - - w = content_get_width(box->background); - if (hunit == CSS_UNIT_PCT) { - l = (width - w) * hpos / INTTOFIX(100); - } else { - l = FIXTOINT(nscss_len2px(&c->len_ctx, - hpos, hunit, - box->style)); - } - - h = content_get_height(box->background); - if (vunit == CSS_UNIT_PCT) { - t = (height - h) * vpos / INTTOFIX(100); - } else { - t = FIXTOINT(nscss_len2px(&c->len_ctx, - vpos, vunit, - box->style)); - } - - /* Redraw area depends on background-repeat */ - switch (css_computed_background_repeat( - box->style)) { - case CSS_BACKGROUND_REPEAT_REPEAT: - data.redraw.x = 0; - data.redraw.y = 0; - data.redraw.width = box->width; - data.redraw.height = box->height; - break; - - case CSS_BACKGROUND_REPEAT_REPEAT_X: - data.redraw.x = 0; - data.redraw.y += t; - data.redraw.width = box->width; - break; - - case CSS_BACKGROUND_REPEAT_REPEAT_Y: - data.redraw.x += l; - data.redraw.y = 0; - data.redraw.height = box->height; - break; - - case CSS_BACKGROUND_REPEAT_NO_REPEAT: - data.redraw.x += l; - data.redraw.y += t; - break; - - default: - break; - } - - data.redraw.object_width = box->width; - data.redraw.object_height = box->height; - - /* Add offset to box */ - data.redraw.x += x; - data.redraw.y += y; - data.redraw.object_x += x; - data.redraw.object_y += y; - - content_broadcast(&c->base, - CONTENT_MSG_REDRAW, &data); - break; - - } else { - /* Non-background case */ - if (hlcache_handle_get_content(object) == - event->data.redraw.object) { - - int w = content_get_width(object); - int h = content_get_height(object); - - if (w != 0) { - data.redraw.x = - data.redraw.x * - box->width / w; - data.redraw.width = - data.redraw.width * - box->width / w; - } - - if (h != 0) { - data.redraw.y = - data.redraw.y * - box->height / h; - data.redraw.height = - data.redraw.height * - box->height / h; - } - - data.redraw.object_width = box->width; - data.redraw.object_height = box->height; - } - - data.redraw.x += x + box->padding[LEFT]; - data.redraw.y += y + box->padding[TOP]; - data.redraw.object_x += x + box->padding[LEFT]; - data.redraw.object_y += y + box->padding[TOP]; - } - - content_broadcast(&c->base, CONTENT_MSG_REDRAW, &data); - } - break; - - case CONTENT_MSG_REFRESH: - if (content_get_type(object) == CONTENT_HTML) { - /* only for HTML objects */ - guit->misc->schedule(event->data.delay * 1000, - html_object_refresh, o); - } - - break; - - case CONTENT_MSG_LINK: - /* Don't care about favicons that aren't on top level content */ - break; - - case CONTENT_MSG_GETCTX: - *(event->data.jscontext) = NULL; - break; - - case CONTENT_MSG_SCROLL: - if (box->scroll_x != NULL) - scrollbar_set(box->scroll_x, event->data.scroll.x0, - false); - if (box->scroll_y != NULL) - scrollbar_set(box->scroll_y, event->data.scroll.y0, - false); - break; - - case CONTENT_MSG_DRAGSAVE: - { - union content_msg_data msg_data; - if (event->data.dragsave.content == NULL) - msg_data.dragsave.content = object; - else - msg_data.dragsave.content = - event->data.dragsave.content; - - content_broadcast(&c->base, CONTENT_MSG_DRAGSAVE, &msg_data); - } - break; - - case CONTENT_MSG_SAVELINK: - case CONTENT_MSG_POINTER: - case CONTENT_MSG_SELECTMENU: - case CONTENT_MSG_GADGETCLICK: - /* These messages are for browser window layer. - * we're not interested, so pass them on. */ - content_broadcast(&c->base, event->type, &event->data); - break; - - case CONTENT_MSG_CARET: - { - union html_focus_owner focus_owner; - focus_owner.content = box; - - switch (event->data.caret.type) { - case CONTENT_CARET_REMOVE: - case CONTENT_CARET_HIDE: - html_set_focus(c, HTML_FOCUS_CONTENT, focus_owner, - true, 0, 0, 0, NULL); - break; - case CONTENT_CARET_SET_POS: - html_set_focus(c, HTML_FOCUS_CONTENT, focus_owner, - false, event->data.caret.pos.x, - event->data.caret.pos.y, - event->data.caret.pos.height, - event->data.caret.pos.clip); - break; - } - } - break; - - case CONTENT_MSG_DRAG: - { - html_drag_type drag_type = HTML_DRAG_NONE; - union html_drag_owner drag_owner; - drag_owner.content = box; - - switch (event->data.drag.type) { - case CONTENT_DRAG_NONE: - drag_type = HTML_DRAG_NONE; - drag_owner.no_owner = true; - break; - case CONTENT_DRAG_SCROLL: - drag_type = HTML_DRAG_CONTENT_SCROLL; - break; - case CONTENT_DRAG_SELECTION: - drag_type = HTML_DRAG_CONTENT_SELECTION; - break; - } - html_set_drag_type(c, drag_type, drag_owner, - event->data.drag.rect); - } - break; - - case CONTENT_MSG_SELECTION: - { - html_selection_type sel_type; - union html_selection_owner sel_owner; - - if (event->data.selection.selection) { - sel_type = HTML_SELECTION_CONTENT; - sel_owner.content = box; - } else { - sel_type = HTML_SELECTION_NONE; - sel_owner.none = true; - } - html_set_selection(c, sel_type, sel_owner, - event->data.selection.read_only); - } - break; - - default: - break; - } - - if (c->base.status == CONTENT_STATUS_READY && - c->base.active == 0 && - (event->type == CONTENT_MSG_LOADING || - event->type == CONTENT_MSG_DONE || - event->type == CONTENT_MSG_ERROR || - event->type == CONTENT_MSG_ERRORCODE)) { - /* all objects have arrived */ - content__reformat(&c->base, false, c->base.available_width, - c->base.height); - content_set_done(&c->base); - } else if (nsoption_bool(incremental_reflow) && - event->type == CONTENT_MSG_DONE && - box != NULL && - !(box->flags & REPLACE_DIM) && - (c->base.status == CONTENT_STATUS_READY || - c->base.status == CONTENT_STATUS_DONE)) { - /* 1) the configuration option to reflow pages while - * objects are fetched is set - * 2) an object is newly fetched & converted, - * 3) the box's dimensions need to change due to being replaced - * 4) the object's parent HTML is ready for reformat, - */ - uint64_t ms_now; - nsu_getmonotonic_ms(&ms_now); - if (ms_now > c->base.reformat_time) { - /* The time since the previous reformat is - * more than the configured minimum time - * between reformats so reformat the page to - * display newly fetched objects - */ - content__reformat(&c->base, - false, - c->base.available_width, - c->base.height); - } - } - - return NSERROR_OK; -} - -/** - * Start a fetch for an object required by a page, replacing an existing object. - * - * \param object Object to replace - * \param url URL of object to fetch (copied) - * \return true on success, false on memory exhaustion - */ - -static bool html_replace_object(struct content_html_object *object, nsurl *url) -{ - html_content *c; - hlcache_child_context child; - html_content *page; - nserror error; - - assert(object != NULL); - assert(object->box != NULL); - - c = (html_content *) object->parent; - - child.charset = c->encoding; - child.quirks = c->base.quirks; - - if (object->content != NULL) { - /* remove existing object */ - if (content_get_status(object->content) != CONTENT_STATUS_DONE) { - c->base.active--; - NSLOG(netsurf, INFO, "%d fetches active", - c->base.active); - } - - hlcache_handle_release(object->content); - object->content = NULL; - - object->box->object = NULL; - } - - /* initialise fetch */ - error = hlcache_handle_retrieve(url, HLCACHE_RETRIEVE_SNIFF_TYPE, - content_get_url(&c->base), NULL, - html_object_callback, object, &child, - object->permitted_types, - &object->content); - - if (error != NSERROR_OK) - return false; - - for (page = c; page != NULL; page = page->page) { - page->base.active++; - NSLOG(netsurf, INFO, "%d fetches active", c->base.active); - - page->base.status = CONTENT_STATUS_READY; - } - - return true; -} - -/** - * schedule callback for object refresh - */ - -static void html_object_refresh(void *p) -{ - struct content_html_object *object = p; - nsurl *refresh_url; - - assert(content_get_type(object->content) == CONTENT_HTML); - - refresh_url = content_get_refresh_url(object->content); - - /* Ignore if refresh URL has gone - * (may happen if fetch errored) */ - if (refresh_url == NULL) - return; - - content_invalidate_reuse_data(object->content); - - if (!html_replace_object(object, refresh_url)) { - /** \todo handle memory exhaustion */ - } -} - -nserror html_object_open_objects(html_content *html, struct browser_window *bw) -{ - struct content_html_object *object, *next; - - for (object = html->object_list; object != NULL; object = next) { - next = object->next; - - if (object->content == NULL || object->box == NULL) - continue; - - if (content_get_type(object->content) == CONTENT_NONE) - continue; - - content_open(object->content, - bw, - &html->base, - object->box->object_params); - } - return NSERROR_OK; -} - -nserror html_object_abort_objects(html_content *htmlc) -{ - struct content_html_object *object; - - for (object = htmlc->object_list; - object != NULL; - object = object->next) { - if (object->content == NULL) - continue; - - switch (content_get_status(object->content)) { - case CONTENT_STATUS_DONE: - /* already loaded: do nothing */ - break; - - case CONTENT_STATUS_READY: - hlcache_handle_abort(object->content); - /* Active count will be updated when - * html_object_callback receives - * CONTENT_MSG_DONE from this object - */ - break; - - default: - hlcache_handle_abort(object->content); - hlcache_handle_release(object->content); - object->content = NULL; - if (object->box != NULL) { - htmlc->base.active--; - NSLOG(netsurf, INFO, "%d fetches active", - htmlc->base.active); - } - break; - - } - } - - return NSERROR_OK; -} - -nserror html_object_close_objects(html_content *html) -{ - struct content_html_object *object, *next; - - for (object = html->object_list; object != NULL; object = next) { - next = object->next; - - if (object->content == NULL || object->box == NULL) - continue; - - if (content_get_type(object->content) == CONTENT_NONE) - continue; - - if (content_get_type(object->content) == CONTENT_HTML) { - guit->misc->schedule(-1, html_object_refresh, object); - } - - content_close(object->content); - } - return NSERROR_OK; -} - -nserror html_object_free_objects(html_content *html) -{ - while (html->object_list != NULL) { - struct content_html_object *victim = html->object_list; - - if (victim->content != NULL) { - NSLOG(netsurf, INFO, "object %p", victim->content); - - if (content_get_type(victim->content) == CONTENT_HTML) { - guit->misc->schedule(-1, html_object_refresh, victim); - } - hlcache_handle_release(victim->content); - } - - html->object_list = victim->next; - free(victim); - } - return NSERROR_OK; -} - - - -/* exported interface documented in render/html_internal.h */ -bool html_fetch_object(html_content *c, nsurl *url, struct box *box, - content_type permitted_types, - int available_width, int available_height, - bool background) -{ - struct content_html_object *object; - hlcache_child_context child; - nserror error; - - /* If we've already been aborted, don't bother attempting the fetch */ - if (c->aborted) - return true; - - child.charset = c->encoding; - child.quirks = c->base.quirks; - - object = calloc(1, sizeof(struct content_html_object)); - if (object == NULL) { - return false; - } - - object->parent = (struct content *) c; - object->next = NULL; - object->content = NULL; - object->box = box; - object->permitted_types = permitted_types; - object->background = background; - - error = hlcache_handle_retrieve(url, - HLCACHE_RETRIEVE_SNIFF_TYPE, - content_get_url(&c->base), NULL, - html_object_callback, object, &child, - object->permitted_types, &object->content); - if (error != NSERROR_OK) { - free(object); - return error != NSERROR_NOMEM; - } - - /* add to content object list */ - object->next = c->object_list; - c->object_list = object; - - c->num_objects++; - if (box != NULL) { - c->base.active++; - NSLOG(netsurf, INFO, "%d fetches active", c->base.active); - } - - return true; -} diff --git a/render/html_redraw.c b/render/html_redraw.c deleted file mode 100644 index 9a97e5ec5..000000000 --- a/render/html_redraw.c +++ /dev/null @@ -1,1951 +0,0 @@ -/* - * Copyright 2004-2008 James Bursa <bursa@users.sourceforge.net> - * Copyright 2004-2007 John M Bell <jmb202@ecs.soton.ac.uk> - * Copyright 2004-2007 Richard Wilson <info@tinct.net> - * Copyright 2005-2006 Adrian Lees <adrianl@users.sourceforge.net> - * Copyright 2006 Rob Kendrick <rjek@netsurf-browser.org> - * Copyright 2008 Michael Drake <tlsa@netsurf-browser.org> - * Copyright 2009 Paul Blokus <paul_pl@users.sourceforge.net> - * - * 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 <http://www.gnu.org/licenses/>. - */ - -/** - * \file - * - * Redrawing CONTENT_HTML implementation. - */ - -#include "utils/config.h" -#include <assert.h> -#include <stdbool.h> -#include <stdlib.h> -#include <string.h> -#include <math.h> -#include <dom/dom.h> - -#include "utils/log.h" -#include "utils/messages.h" -#include "utils/utils.h" -#include "utils/nsoption.h" -#include "netsurf/content.h" -#include "netsurf/browser_window.h" -#include "netsurf/plotters.h" -#include "netsurf/bitmap.h" -#include "netsurf/layout.h" -#include "content/content_protected.h" -#include "css/utils.h" -#include "desktop/selection.h" -#include "desktop/print.h" -#include "desktop/scrollbar.h" -#include "desktop/textarea.h" -#include "desktop/gui_internal.h" - -#include "render/box.h" -#include "render/font.h" -#include "render/form_internal.h" -#include "render/html_internal.h" -#include "render/layout.h" -#include "render/search.h" - - -bool html_redraw_debug = false; - -/** - * Determine if a box has a background that needs drawing - * - * \param box Box to consider - * \return True if box has a background, false otherwise. - */ -static bool html_redraw_box_has_background(struct box *box) -{ - if (box->background != NULL) - return true; - - if (box->style != NULL) { - css_color colour; - - css_computed_background_color(box->style, &colour); - - if (nscss_color_is_transparent(colour) == false) - return true; - } - - return false; -} - -/** - * Find the background box for a box - * - * \param box Box to find background box for - * \return Pointer to background box, or NULL if there is none - */ -static struct box *html_redraw_find_bg_box(struct box *box) -{ - /* Thanks to backwards compatibility, CSS defines the following: - * - * + If the box is for the root element and it has a background, - * use that (and then process the body box with no special case) - * + If the box is for the root element and it has no background, - * then use the background (if any) from the body element as if - * it were specified on the root. Then, when the box for the body - * element is processed, ignore the background. - * + For any other box, just use its own styling. - */ - if (box->parent == NULL) { - /* Root box */ - if (html_redraw_box_has_background(box)) - return box; - - /* No background on root box: consider body box, if any */ - if (box->children != NULL) { - if (html_redraw_box_has_background(box->children)) - return box->children; - } - } else if (box->parent != NULL && box->parent->parent == NULL) { - /* Body box: only render background if root has its own */ - if (html_redraw_box_has_background(box) && - html_redraw_box_has_background(box->parent)) - return box; - } else { - /* Any other box */ - if (html_redraw_box_has_background(box)) - return box; - } - - return NULL; -} - -/** - * Redraw a short text string, complete with highlighting - * (for selection/search) - * - * \param utf8_text pointer to UTF-8 text string - * \param utf8_len length of string, in bytes - * \param offset byte offset within textual representation - * \param space width of space that follows string (0 = no space) - * \param fstyle text style to use (pass text size unscaled) - * \param x x ordinate at which to plot text - * \param y y ordinate at which to plot text - * \param clip pointer to current clip rectangle - * \param height height of text string - * \param scale current display scale (1.0 = 100%) - * \param excluded exclude this text string from the selection - * \param c Content being redrawn. - * \param sel Selection context - * \param search Search context - * \param ctx current redraw context - * \return true iff successful and redraw should proceed - */ - -bool -text_redraw(const char *utf8_text, - size_t utf8_len, - size_t offset, - int space, - const plot_font_style_t *fstyle, - int x, - int y, - const struct rect *clip, - int height, - float scale, - bool excluded, - struct content *c, - const struct selection *sel, - struct search_context *search, - const struct redraw_context *ctx) -{ - bool highlighted = false; - plot_font_style_t plot_fstyle = *fstyle; - nserror res; - - /* Need scaled text size to pass to plotters */ - plot_fstyle.size *= scale; - - /* is this box part of a selection? */ - if (!excluded && ctx->interactive == true) { - unsigned len = utf8_len + (space ? 1 : 0); - unsigned start_idx; - unsigned end_idx; - - /* first try the browser window's current selection */ - if (selection_defined(sel) && selection_highlighted(sel, - offset, offset + len, - &start_idx, &end_idx)) { - highlighted = true; - } - - /* what about the current search operation, if any? */ - if (!highlighted && (search != NULL) && - search_term_highlighted(c, - offset, offset + len, - &start_idx, &end_idx, - search)) { - highlighted = true; - } - - /* \todo make search terms visible within selected text */ - if (highlighted) { - struct rect r; - unsigned endtxt_idx = end_idx; - bool clip_changed = false; - bool text_visible = true; - int startx, endx; - plot_style_t pstyle_fill_hback = *plot_style_fill_white; - plot_font_style_t fstyle_hback = plot_fstyle; - - if (end_idx > utf8_len) { - /* adjust for trailing space, not present in - * utf8_text */ - assert(end_idx == utf8_len + 1); - endtxt_idx = utf8_len; - } - - res = guit->layout->width(fstyle, - utf8_text, start_idx, - &startx); - if (res != NSERROR_OK) { - startx = 0; - } - - res = guit->layout->width(fstyle, - utf8_text, endtxt_idx, - &endx); - if (res != NSERROR_OK) { - endx = 0; - } - - /* is there a trailing space that should be highlighted - * as well? */ - if (end_idx > utf8_len) { - endx += space; - } - - if (scale != 1.0) { - startx *= scale; - endx *= scale; - } - - /* draw any text preceding highlighted portion */ - if ((start_idx > 0) && - (ctx->plot->text(ctx, - &plot_fstyle, - x, - y + (int)(height * 0.75 * scale), - utf8_text, - start_idx) != NSERROR_OK)) - return false; - - pstyle_fill_hback.fill_colour = fstyle->foreground; - - /* highlighted portion */ - r.x0 = x + startx; - r.y0 = y; - r.x1 = x + endx; - r.y1 = y + height * scale; - res = ctx->plot->rectangle(ctx, &pstyle_fill_hback, &r); - if (res != NSERROR_OK) { - return false; - } - - if (start_idx > 0) { - int px0 = max(x + startx, clip->x0); - int px1 = min(x + endx, clip->x1); - - if (px0 < px1) { - r.x0 = px0; - r.y0 = clip->y0; - r.x1 = px1; - r.y1 = clip->y1; - res = ctx->plot->clip(ctx, &r); - if (res != NSERROR_OK) { - return false; - } - - clip_changed = true; - } else { - text_visible = false; - } - } - - fstyle_hback.background = - pstyle_fill_hback.fill_colour; - fstyle_hback.foreground = colour_to_bw_furthest( - pstyle_fill_hback.fill_colour); - - if (text_visible && - (ctx->plot->text(ctx, - &fstyle_hback, - x, - y + (int)(height * 0.75 * scale), - utf8_text, - endtxt_idx) != NSERROR_OK)) { - return false; - } - - /* draw any text succeeding highlighted portion */ - if (endtxt_idx < utf8_len) { - int px0 = max(x + endx, clip->x0); - if (px0 < clip->x1) { - - r.x0 = px0; - r.y0 = clip->y0; - r.x1 = clip->x1; - r.y1 = clip->y1; - res = ctx->plot->clip(ctx, &r); - if (res != NSERROR_OK) { - return false; - } - - clip_changed = true; - - res = ctx->plot->text(ctx, - &plot_fstyle, - x, - y + (int)(height * 0.75 * scale), - utf8_text, - utf8_len); - if (res != NSERROR_OK) { - return false; - } - } - } - - if (clip_changed && - (ctx->plot->clip(ctx, clip) != NSERROR_OK)) { - return false; - } - } - } - - if (!highlighted) { - res = ctx->plot->text(ctx, - &plot_fstyle, - x, - y + (int) (height * 0.75 * scale), - utf8_text, - utf8_len); - if (res != NSERROR_OK) { - return false; - } - } - return true; -} - - -/** - * Plot a checkbox. - * - * \param x left coordinate - * \param y top coordinate - * \param width dimensions of checkbox - * \param height dimensions of checkbox - * \param selected the checkbox is selected - * \param ctx current redraw context - * \return true if successful, false otherwise - */ - -static bool html_redraw_checkbox(int x, int y, int width, int height, - bool selected, const struct redraw_context *ctx) -{ - double z; - nserror res; - struct rect rect; - - z = width * 0.15; - if (z == 0) { - z = 1; - } - - rect.x0 = x; - rect.y0 = y ; - rect.x1 = x + width; - rect.y1 = y + height; - res = ctx->plot->rectangle(ctx, plot_style_fill_wbasec, &rect); - if (res != NSERROR_OK) { - return false; - } - - /* dark line across top */ - rect.y1 = y; - res = ctx->plot->line(ctx, plot_style_stroke_darkwbasec, &rect); - if (res != NSERROR_OK) { - return false; - } - - /* dark line across left */ - rect.x1 = x; - rect.y1 = y + height; - res = ctx->plot->line(ctx, plot_style_stroke_darkwbasec, &rect); - if (res != NSERROR_OK) { - return false; - } - - /* light line across right */ - rect.x0 = x + width; - rect.x1 = x + width; - res = ctx->plot->line(ctx, plot_style_stroke_lightwbasec, &rect); - if (res != NSERROR_OK) { - return false; - } - - /* light line across bottom */ - rect.x0 = x; - rect.y0 = y + height; - res = ctx->plot->line(ctx, plot_style_stroke_lightwbasec, &rect); - if (res != NSERROR_OK) { - return false; - } - - if (selected) { - if (width < 12 || height < 12) { - /* render a solid box instead of a tick */ - rect.x0 = x + z + z; - rect.y0 = y + z + z; - rect.x1 = x + width - z; - rect.y1 = y + height - z; - res = ctx->plot->rectangle(ctx, plot_style_fill_wblobc, &rect); - if (res != NSERROR_OK) { - return false; - } - } else { - /* render a tick, as it'll fit comfortably */ - rect.x0 = x + width - z; - rect.y0 = y + z; - rect.x1 = x + (z * 3); - rect.y1 = y + height - z; - res = ctx->plot->line(ctx, plot_style_stroke_wblobc, &rect); - if (res != NSERROR_OK) { - return false; - } - - rect.x0 = x + (z * 3); - rect.y0 = y + height - z; - rect.x1 = x + z + z; - rect.y1 = y + (height / 2); - res = ctx->plot->line(ctx, plot_style_stroke_wblobc, &rect); - if (res != NSERROR_OK) { - return false; - } - } - } - return true; -} - - -/** - * Plot a radio icon. - * - * \param x left coordinate - * \param y top coordinate - * \param width dimensions of radio icon - * \param height dimensions of radio icon - * \param selected the radio icon is selected - * \param ctx current redraw context - * \return true if successful, false otherwise - */ -static bool html_redraw_radio(int x, int y, int width, int height, - bool selected, const struct redraw_context *ctx) -{ - nserror res; - - /* plot background of radio button */ - res = ctx->plot->disc(ctx, - plot_style_fill_wbasec, - x + width * 0.5, - y + height * 0.5, - width * 0.5 - 1); - if (res != NSERROR_OK) { - return false; - } - - /* plot dark arc */ - res = ctx->plot->arc(ctx, - plot_style_fill_darkwbasec, - x + width * 0.5, - y + height * 0.5, - width * 0.5 - 1, - 45, - 225); - if (res != NSERROR_OK) { - return false; - } - - /* plot light arc */ - res = ctx->plot->arc(ctx, - plot_style_fill_lightwbasec, - x + width * 0.5, - y + height * 0.5, - width * 0.5 - 1, - 225, - 45); - if (res != NSERROR_OK) { - return false; - } - - if (selected) { - /* plot selection blob */ - res = ctx->plot->disc(ctx, - plot_style_fill_wblobc, - x + width * 0.5, - y + height * 0.5, - width * 0.3 - 1); - if (res != NSERROR_OK) { - return false; - } - } - - return true; -} - - -/** - * Plot a file upload input. - * - * \param x left coordinate - * \param y top coordinate - * \param width dimensions of input - * \param height dimensions of input - * \param box box of input - * \param scale scale for redraw - * \param background_colour current background colour - * \param len_ctx Length conversion context - * \param ctx current redraw context - * \return true if successful, false otherwise - */ - -static bool html_redraw_file(int x, int y, int width, int height, - struct box *box, float scale, colour background_colour, - const nscss_len_ctx *len_ctx, - const struct redraw_context *ctx) -{ - int text_width; - const char *text; - size_t length; - plot_font_style_t fstyle; - nserror res; - - font_plot_style_from_css(len_ctx, box->style, &fstyle); - fstyle.background = background_colour; - - if (box->gadget->value) { - text = box->gadget->value; - } else { - text = messages_get("Form_Drop"); - } - length = strlen(text); - - res = guit->layout->width(&fstyle, text, length, &text_width); - if (res != NSERROR_OK) { - return false; - } - text_width *= scale; - if (width < text_width + 8) { - x = x + width - text_width - 4; - } else { - x = x + 4; - } - - res = ctx->plot->text(ctx, &fstyle, x, y + height * 0.75, text, length); - if (res != NSERROR_OK) { - return false; - } - return true; -} - - -/** - * Plot background images. - * - * The reason for the presence of \a background is the backwards compatibility - * mess that is backgrounds on <body>. The background will be drawn relative - * to \a box, using the background information contained within \a background. - * - * \param x coordinate of box - * \param y coordinate of box - * \param box box to draw background image of - * \param scale scale for redraw - * \param clip current clip rectangle - * \param background_colour current background colour - * \param background box containing background details (usually \a box) - * \param len_ctx Length conversion context - * \param ctx current redraw context - * \return true if successful, false otherwise - */ - -static bool html_redraw_background(int x, int y, struct box *box, float scale, - const struct rect *clip, colour *background_colour, - struct box *background, - const nscss_len_ctx *len_ctx, - const struct redraw_context *ctx) -{ - bool repeat_x = false; - bool repeat_y = false; - bool plot_colour = true; - bool plot_content; - bool clip_to_children = false; - struct box *clip_box = box; - int ox = x, oy = y; - int width, height; - css_fixed hpos = 0, vpos = 0; - css_unit hunit = CSS_UNIT_PX, vunit = CSS_UNIT_PX; - struct box *parent; - struct rect r = *clip; - css_color bgcol; - plot_style_t pstyle_fill_bg = { - .fill_type = PLOT_OP_TYPE_SOLID, - .fill_colour = *background_colour, - }; - nserror res; - - if (ctx->background_images == false) - return true; - - plot_content = (background->background != NULL); - - if (plot_content) { - if (!box->parent) { - /* Root element, special case: - * background origin calc. is based on margin box */ - x -= box->margin[LEFT] * scale; - y -= box->margin[TOP] * scale; - width = box->margin[LEFT] + box->padding[LEFT] + - box->width + box->padding[RIGHT] + - box->margin[RIGHT]; - height = box->margin[TOP] + box->padding[TOP] + - box->height + box->padding[BOTTOM] + - box->margin[BOTTOM]; - } else { - width = box->padding[LEFT] + box->width + - box->padding[RIGHT]; - height = box->padding[TOP] + box->height + - box->padding[BOTTOM]; - } - /* handle background-repeat */ - switch (css_computed_background_repeat(background->style)) { - case CSS_BACKGROUND_REPEAT_REPEAT: - repeat_x = repeat_y = true; - /* optimisation: only plot the colour if - * bitmap is not opaque */ - plot_colour = !content_get_opaque(background->background); - break; - - case CSS_BACKGROUND_REPEAT_REPEAT_X: - repeat_x = true; - break; - - case CSS_BACKGROUND_REPEAT_REPEAT_Y: - repeat_y = true; - break; - - case CSS_BACKGROUND_REPEAT_NO_REPEAT: - break; - - default: - break; - } - - /* handle background-position */ - css_computed_background_position(background->style, - &hpos, &hunit, &vpos, &vunit); - if (hunit == CSS_UNIT_PCT) { - x += (width - - content_get_width(background->background)) * - scale * FIXTOFLT(hpos) / 100.; - } else { - x += (int) (FIXTOFLT(nscss_len2px(len_ctx, hpos, hunit, - background->style)) * scale); - } - - if (vunit == CSS_UNIT_PCT) { - y += (height - - content_get_height(background->background)) * - scale * FIXTOFLT(vpos) / 100.; - } else { - y += (int) (FIXTOFLT(nscss_len2px(len_ctx, vpos, vunit, - background->style)) * scale); - } - } - - /* special case for table rows as their background needs - * to be clipped to all the cells */ - if (box->type == BOX_TABLE_ROW) { - css_fixed h = 0, v = 0; - css_unit hu = CSS_UNIT_PX, vu = CSS_UNIT_PX; - - for (parent = box->parent; - ((parent) && (parent->type != BOX_TABLE)); - parent = parent->parent); - assert(parent && (parent->style)); - - css_computed_border_spacing(parent->style, &h, &hu, &v, &vu); - - clip_to_children = (h > 0) || (v > 0); - - if (clip_to_children) - clip_box = box->children; - } - - for (; clip_box; clip_box = clip_box->next) { - /* clip to child boxes if needed */ - if (clip_to_children) { - assert(clip_box->type == BOX_TABLE_CELL); - - /* update clip.* to the child cell */ - r.x0 = ox + (clip_box->x * scale); - r.y0 = oy + (clip_box->y * scale); - r.x1 = r.x0 + (clip_box->padding[LEFT] + - clip_box->width + - clip_box->padding[RIGHT]) * scale; - r.y1 = r.y0 + (clip_box->padding[TOP] + - clip_box->height + - clip_box->padding[BOTTOM]) * scale; - - if (r.x0 < clip->x0) r.x0 = clip->x0; - if (r.y0 < clip->y0) r.y0 = clip->y0; - if (r.x1 > clip->x1) r.x1 = clip->x1; - if (r.y1 > clip->y1) r.y1 = clip->y1; - - css_computed_background_color(clip_box->style, &bgcol); - - /* <td> attributes override <tr> */ - /* if the background content is opaque there - * is no need to plot underneath it. - */ - if ((r.x0 >= r.x1) || - (r.y0 >= r.y1) || - (nscss_color_is_transparent(bgcol) == false) || - ((clip_box->background != NULL) && - content_get_opaque(clip_box->background))) - continue; - } - - /* plot the background colour */ - css_computed_background_color(background->style, &bgcol); - - if (nscss_color_is_transparent(bgcol) == false) { - *background_colour = nscss_color_to_ns(bgcol); - pstyle_fill_bg.fill_colour = *background_colour; - if (plot_colour) { - res = ctx->plot->rectangle(ctx, &pstyle_fill_bg, &r); - if (res != NSERROR_OK) { - return false; - } - } - } - /* and plot the image */ - if (plot_content) { - width = content_get_width(background->background); - height = content_get_height(background->background); - - /* ensure clip area only as large as required */ - if (!repeat_x) { - if (r.x0 < x) - r.x0 = x; - if (r.x1 > x + width * scale) - r.x1 = x + width * scale; - } - if (!repeat_y) { - if (r.y0 < y) - r.y0 = y; - if (r.y1 > y + height * scale) - r.y1 = y + height * scale; - } - /* valid clipping rectangles only */ - if ((r.x0 < r.x1) && (r.y0 < r.y1)) { - struct content_redraw_data bg_data; - - res = ctx->plot->clip(ctx, &r); - if (res != NSERROR_OK) { - return false; - } - - bg_data.x = x; - bg_data.y = y; - bg_data.width = ceilf(width * scale); - bg_data.height = ceilf(height * scale); - bg_data.background_colour = *background_colour; - bg_data.scale = scale; - bg_data.repeat_x = repeat_x; - bg_data.repeat_y = repeat_y; - - /* We just continue if redraw fails */ - content_redraw(background->background, - &bg_data, &r, ctx); - } - } - - /* only <tr> rows being clipped to child boxes loop */ - if (!clip_to_children) - return true; - } - return true; -} - - -/** - * Plot an inline's background and/or background image. - * - * \param x coordinate of box - * \param y coordinate of box - * \param box BOX_INLINE which created the background - * \param scale scale for redraw - * \param clip coordinates of clip rectangle - * \param b coordinates of border edge rectangle - * \param first true if this is the first rectangle associated with the inline - * \param last true if this is the last rectangle associated with the inline - * \param background_colour updated to current background colour if plotted - * \param len_ctx Length conversion context - * \param ctx current redraw context - * \return true if successful, false otherwise - */ - -static bool html_redraw_inline_background(int x, int y, struct box *box, - float scale, const struct rect *clip, struct rect b, - bool first, bool last, colour *background_colour, - const nscss_len_ctx *len_ctx, - const struct redraw_context *ctx) -{ - struct rect r = *clip; - bool repeat_x = false; - bool repeat_y = false; - bool plot_colour = true; - bool plot_content; - css_fixed hpos = 0, vpos = 0; - css_unit hunit = CSS_UNIT_PX, vunit = CSS_UNIT_PX; - css_color bgcol; - plot_style_t pstyle_fill_bg = { - .fill_type = PLOT_OP_TYPE_SOLID, - .fill_colour = *background_colour, - }; - nserror res; - - plot_content = (box->background != NULL); - - if (html_redraw_printing && nsoption_bool(remove_backgrounds)) - return true; - - if (plot_content) { - /* handle background-repeat */ - switch (css_computed_background_repeat(box->style)) { - case CSS_BACKGROUND_REPEAT_REPEAT: - repeat_x = repeat_y = true; - /* optimisation: only plot the colour if - * bitmap is not opaque - */ - plot_colour = !content_get_opaque(box->background); - break; - - case CSS_BACKGROUND_REPEAT_REPEAT_X: - repeat_x = true; - break; - - case CSS_BACKGROUND_REPEAT_REPEAT_Y: - repeat_y = true; - break; - - case CSS_BACKGROUND_REPEAT_NO_REPEAT: - break; - - default: - break; - } - - /* handle background-position */ - css_computed_background_position(box->style, - &hpos, &hunit, &vpos, &vunit); - if (hunit == CSS_UNIT_PCT) { - x += (b.x1 - b.x0 - - content_get_width(box->background) * - scale) * FIXTOFLT(hpos) / 100.; - - if (!repeat_x && ((hpos < 2 && !first) || - (hpos > 98 && !last))){ - plot_content = false; - } - } else { - x += (int) (FIXTOFLT(nscss_len2px(len_ctx, hpos, hunit, - box->style)) * scale); - } - - if (vunit == CSS_UNIT_PCT) { - y += (b.y1 - b.y0 - - content_get_height(box->background) * - scale) * FIXTOFLT(vpos) / 100.; - } else { - y += (int) (FIXTOFLT(nscss_len2px(len_ctx, vpos, vunit, - box->style)) * scale); - } - } - - /* plot the background colour */ - css_computed_background_color(box->style, &bgcol); - - if (nscss_color_is_transparent(bgcol) == false) { - *background_colour = nscss_color_to_ns(bgcol); - pstyle_fill_bg.fill_colour = *background_colour; - - if (plot_colour) { - res = ctx->plot->rectangle(ctx, &pstyle_fill_bg, &r); - if (res != NSERROR_OK) { - return false; - } - } - } - /* and plot the image */ - if (plot_content) { - int width = content_get_width(box->background); - int height = content_get_height(box->background); - - if (!repeat_x) { - if (r.x0 < x) - r.x0 = x; - if (r.x1 > x + width * scale) - r.x1 = x + width * scale; - } - if (!repeat_y) { - if (r.y0 < y) - r.y0 = y; - if (r.y1 > y + height * scale) - r.y1 = y + height * scale; - } - /* valid clipping rectangles only */ - if ((r.x0 < r.x1) && (r.y0 < r.y1)) { - struct content_redraw_data bg_data; - - res = ctx->plot->clip(ctx, &r); - if (res != NSERROR_OK) { - return false; - } - - bg_data.x = x; - bg_data.y = y; - bg_data.width = ceilf(width * scale); - bg_data.height = ceilf(height * scale); - bg_data.background_colour = *background_colour; - bg_data.scale = scale; - bg_data.repeat_x = repeat_x; - bg_data.repeat_y = repeat_y; - - /* We just continue if redraw fails */ - content_redraw(box->background, &bg_data, &r, ctx); - } - } - - return true; -} - - -/** - * Plot text decoration for an inline box. - * - * \param box box to plot decorations for, of type BOX_INLINE - * \param x x coordinate of parent of box - * \param y y coordinate of parent of box - * \param scale scale for redraw - * \param colour colour for decorations - * \param ratio position of line as a ratio of line height - * \param ctx current redraw context - * \return true if successful, false otherwise - */ - -static bool -html_redraw_text_decoration_inline(struct box *box, - int x, int y, - float scale, - colour colour, - float ratio, - const struct redraw_context *ctx) -{ - struct box *c; - plot_style_t plot_style_box = { - .stroke_type = PLOT_OP_TYPE_SOLID, - .stroke_colour = colour, - }; - nserror res; - struct rect rect; - - for (c = box->next; - c && c != box->inline_end; - c = c->next) { - if (c->type != BOX_TEXT) { - continue; - } - rect.x0 = (x + c->x) * scale; - rect.y0 = (y + c->y + c->height * ratio) * scale; - rect.x1 = (x + c->x + c->width) * scale; - rect.y1 = (y + c->y + c->height * ratio) * scale; - res = ctx->plot->line(ctx, &plot_style_box, &rect); - if (res != NSERROR_OK) { - return false; - } - } - return true; -} - - -/** - * Plot text decoration for an non-inline box. - * - * \param box box to plot decorations for, of type other than BOX_INLINE - * \param x x coordinate of box - * \param y y coordinate of box - * \param scale scale for redraw - * \param colour colour for decorations - * \param ratio position of line as a ratio of line height - * \param ctx current redraw context - * \return true if successful, false otherwise - */ - -static bool -html_redraw_text_decoration_block(struct box *box, - int x, int y, - float scale, - colour colour, - float ratio, - const struct redraw_context *ctx) -{ - struct box *c; - plot_style_t plot_style_box = { - .stroke_type = PLOT_OP_TYPE_SOLID, - .stroke_colour = colour, - }; - nserror res; - struct rect rect; - - /* draw through text descendants */ - for (c = box->children; c; c = c->next) { - if (c->type == BOX_TEXT) { - rect.x0 = (x + c->x) * scale; - rect.y0 = (y + c->y + c->height * ratio) * scale; - rect.x1 = (x + c->x + c->width) * scale; - rect.y1 = (y + c->y + c->height * ratio) * scale; - res = ctx->plot->line(ctx, &plot_style_box, &rect); - if (res != NSERROR_OK) { - return false; - } - } else if ((c->type == BOX_INLINE_CONTAINER) || (c->type == BOX_BLOCK)) { - if (!html_redraw_text_decoration_block(c, - x + c->x, y + c->y, - scale, colour, ratio, ctx)) - return false; - } - } - return true; -} - - -/** - * Plot text decoration for a box. - * - * \param box box to plot decorations for - * \param x_parent x coordinate of parent of box - * \param y_parent y coordinate of parent of box - * \param scale scale for redraw - * \param background_colour current background colour - * \param ctx current redraw context - * \return true if successful, false otherwise - */ - -static bool html_redraw_text_decoration(struct box *box, - int x_parent, int y_parent, float scale, - colour background_colour, const struct redraw_context *ctx) -{ - static const enum css_text_decoration_e decoration[] = { - CSS_TEXT_DECORATION_UNDERLINE, CSS_TEXT_DECORATION_OVERLINE, - CSS_TEXT_DECORATION_LINE_THROUGH }; - static const float line_ratio[] = { 0.9, 0.1, 0.5 }; - colour fgcol; - unsigned int i; - css_color col; - - css_computed_color(box->style, &col); - fgcol = nscss_color_to_ns(col); - - /* antialias colour for under/overline */ - if (html_redraw_printing == false) - fgcol = blend_colour(background_colour, fgcol); - - if (box->type == BOX_INLINE) { - if (!box->inline_end) - return true; - for (i = 0; i != NOF_ELEMENTS(decoration); i++) - if (css_computed_text_decoration(box->style) & - decoration[i]) - if (!html_redraw_text_decoration_inline(box, - x_parent, y_parent, scale, - fgcol, line_ratio[i], ctx)) - return false; - } else { - for (i = 0; i != NOF_ELEMENTS(decoration); i++) - if (css_computed_text_decoration(box->style) & - decoration[i]) - if (!html_redraw_text_decoration_block(box, - x_parent + box->x, - y_parent + box->y, - scale, - fgcol, line_ratio[i], ctx)) - return false; - } - - return true; -} - - -/** - * Redraw the text content of a box, possibly partially highlighted - * because the text has been selected, or matches a search operation. - * - * \param html The html content to redraw text within. - * \param box box with text content - * \param x x co-ord of box - * \param y y co-ord of box - * \param clip current clip rectangle - * \param scale current scale setting (1.0 = 100%) - * \param current_background_color - * \param ctx current redraw context - * \return true iff successful and redraw should proceed - */ - -static bool html_redraw_text_box(const html_content *html, struct box *box, - int x, int y, const struct rect *clip, float scale, - colour current_background_color, - const struct redraw_context *ctx) -{ - bool excluded = (box->object != NULL); - plot_font_style_t fstyle; - - font_plot_style_from_css(&html->len_ctx, box->style, &fstyle); - fstyle.background = current_background_color; - - if (!text_redraw(box->text, box->length, box->byte_offset, - box->space, &fstyle, x, y, - clip, box->height, scale, excluded, - (struct content *)html, &html->sel, - html->search, ctx)) - return false; - - return true; -} - -bool html_redraw_box(const html_content *html, struct box *box, - int x_parent, int y_parent, - const struct rect *clip, float scale, - colour current_background_color, - const struct redraw_context *ctx); - -/** - * Draw the various children of a box. - * - * \param html html content - * \param box box to draw children of - * \param x_parent coordinate of parent box - * \param y_parent coordinate of parent box - * \param clip clip rectangle - * \param scale scale for redraw - * \param current_background_color background colour under this box - * \param ctx current redraw context - * \return true if successful, false otherwise - */ - -static bool html_redraw_box_children(const html_content *html, struct box *box, - int x_parent, int y_parent, - const struct rect *clip, float scale, - colour current_background_color, - const struct redraw_context *ctx) -{ - struct box *c; - - for (c = box->children; c; c = c->next) { - - if (c->type != BOX_FLOAT_LEFT && c->type != BOX_FLOAT_RIGHT) - if (!html_redraw_box(html, c, - x_parent + box->x - - scrollbar_get_offset(box->scroll_x), - y_parent + box->y - - scrollbar_get_offset(box->scroll_y), - clip, scale, current_background_color, - ctx)) - return false; - } - for (c = box->float_children; c; c = c->next_float) - if (!html_redraw_box(html, c, - x_parent + box->x - - scrollbar_get_offset(box->scroll_x), - y_parent + box->y - - scrollbar_get_offset(box->scroll_y), - clip, scale, current_background_color, - ctx)) - return false; - - return true; -} - -/** - * Recursively draw a box. - * - * \param html html content - * \param box box to draw - * \param x_parent coordinate of parent box - * \param y_parent coordinate of parent box - * \param clip clip rectangle - * \param scale scale for redraw - * \param current_background_color background colour under this box - * \param ctx current redraw context - * \return true if successful, false otherwise - * - * x, y, clip_[xy][01] are in target coordinates. - */ - -bool html_redraw_box(const html_content *html, struct box *box, - int x_parent, int y_parent, - const struct rect *clip, const float scale, - colour current_background_color, - const struct redraw_context *ctx) -{ - const struct plotter_table *plot = ctx->plot; - int x, y; - int width, height; - int padding_left, padding_top, padding_width, padding_height; - int border_left, border_top, border_right, border_bottom; - struct rect r; - struct rect rect; - int x_scrolled, y_scrolled; - struct box *bg_box = NULL; - bool has_x_scroll, has_y_scroll; - css_computed_clip_rect css_rect; - enum css_overflow_e overflow_x = CSS_OVERFLOW_VISIBLE; - enum css_overflow_e overflow_y = CSS_OVERFLOW_VISIBLE; - - if (html_redraw_printing && (box->flags & PRINTED)) - return true; - - if (box->style != NULL) { - overflow_x = css_computed_overflow_x(box->style); - overflow_y = css_computed_overflow_y(box->style); - } - - /* avoid trivial FP maths */ - if (scale == 1.0) { - x = x_parent + box->x; - y = y_parent + box->y; - width = box->width; - height = box->height; - padding_left = box->padding[LEFT]; - padding_top = box->padding[TOP]; - padding_width = padding_left + box->width + box->padding[RIGHT]; - padding_height = padding_top + box->height + - box->padding[BOTTOM]; - border_left = box->border[LEFT].width; - border_top = box->border[TOP].width; - border_right = box->border[RIGHT].width; - border_bottom = box->border[BOTTOM].width; - } else { - x = (x_parent + box->x) * scale; - y = (y_parent + box->y) * scale; - width = box->width * scale; - height = box->height * scale; - /* left and top padding values are normally zero, - * so avoid trivial FP maths */ - padding_left = box->padding[LEFT] ? box->padding[LEFT] * scale - : 0; - padding_top = box->padding[TOP] ? box->padding[TOP] * scale - : 0; - padding_width = (box->padding[LEFT] + box->width + - box->padding[RIGHT]) * scale; - padding_height = (box->padding[TOP] + box->height + - box->padding[BOTTOM]) * scale; - border_left = box->border[LEFT].width * scale; - border_top = box->border[TOP].width * scale; - border_right = box->border[RIGHT].width * scale; - border_bottom = box->border[BOTTOM].width * scale; - } - - /* calculate rectangle covering this box and descendants */ - if (box->style && overflow_x != CSS_OVERFLOW_VISIBLE && - box->parent != NULL) { - /* box contents clipped to box size */ - r.x0 = x - border_left; - r.x1 = x + padding_width + border_right; - } else { - /* box contents can hang out of the box; use descendant box */ - if (scale == 1.0) { - r.x0 = x + box->descendant_x0; - r.x1 = x + box->descendant_x1 + 1; - } else { - r.x0 = x + box->descendant_x0 * scale; - r.x1 = x + box->descendant_x1 * scale + 1; - } - if (!box->parent) { - /* root element */ - int margin_left, margin_right; - if (scale == 1.0) { - margin_left = box->margin[LEFT]; - margin_right = box->margin[RIGHT]; - } else { - margin_left = box->margin[LEFT] * scale; - margin_right = box->margin[RIGHT] * scale; - } - r.x0 = x - border_left - margin_left < r.x0 ? - x - border_left - margin_left : r.x0; - r.x1 = x + padding_width + border_right + - margin_right > r.x1 ? - x + padding_width + border_right + - margin_right : r.x1; - } - } - - /* calculate rectangle covering this box and descendants */ - if (box->style && overflow_y != CSS_OVERFLOW_VISIBLE && - box->parent != NULL) { - /* box contents clipped to box size */ - r.y0 = y - border_top; - r.y1 = y + padding_height + border_bottom; - } else { - /* box contents can hang out of the box; use descendant box */ - if (scale == 1.0) { - r.y0 = y + box->descendant_y0; - r.y1 = y + box->descendant_y1 + 1; - } else { - r.y0 = y + box->descendant_y0 * scale; - r.y1 = y + box->descendant_y1 * scale + 1; - } - if (!box->parent) { - /* root element */ - int margin_top, margin_bottom; - if (scale == 1.0) { - margin_top = box->margin[TOP]; - margin_bottom = box->margin[BOTTOM]; - } else { - margin_top = box->margin[TOP] * scale; - margin_bottom = box->margin[BOTTOM] * scale; - } - r.y0 = y - border_top - margin_top < r.y0 ? - y - border_top - margin_top : r.y0; - r.y1 = y + padding_height + border_bottom + - margin_bottom > r.y1 ? - y + padding_height + border_bottom + - margin_bottom : r.y1; - } - } - - /* return if the rectangle is completely outside the clip rectangle */ - if (clip->y1 < r.y0 || r.y1 < clip->y0 || - clip->x1 < r.x0 || r.x1 < clip->x0) - return true; - - /*if the rectangle is under the page bottom but it can fit in a page, - don't print it now*/ - if (html_redraw_printing) { - if (r.y1 > html_redraw_printing_border) { - if (r.y1 - r.y0 <= html_redraw_printing_border && - (box->type == BOX_TEXT || - box->type == BOX_TABLE_CELL - || box->object || box->gadget)) { - /*remember the highest of all points from the - not printed elements*/ - if (r.y0 < html_redraw_printing_top_cropped) - html_redraw_printing_top_cropped = r.y0; - return true; - } - } - else box->flags |= PRINTED; /*it won't be printed anymore*/ - } - - /* if visibility is hidden render children only */ - if (box->style && css_computed_visibility(box->style) == - CSS_VISIBILITY_HIDDEN) { - if ((ctx->plot->group_start) && - (ctx->plot->group_start(ctx, "hidden box") != NSERROR_OK)) - return false; - if (!html_redraw_box_children(html, box, x_parent, y_parent, - &r, scale, current_background_color, ctx)) - return false; - return ((!ctx->plot->group_end) || (ctx->plot->group_end(ctx) == NSERROR_OK)); - } - - if ((ctx->plot->group_start) && - (ctx->plot->group_start(ctx,"vis box") != NSERROR_OK)) { - return false; - } - - if (box->style != NULL && - css_computed_position(box->style) == - CSS_POSITION_ABSOLUTE && - css_computed_clip(box->style, &css_rect) == - CSS_CLIP_RECT) { - /* We have an absolutly positioned box with a clip rect */ - if (css_rect.left_auto == false) - r.x0 = x - border_left + FIXTOINT(nscss_len2px( - &html->len_ctx, - css_rect.left, css_rect.lunit, - box->style)); - - if (css_rect.top_auto == false) - r.y0 = y - border_top + FIXTOINT(nscss_len2px( - &html->len_ctx, - css_rect.top, css_rect.tunit, - box->style)); - - if (css_rect.right_auto == false) - r.x1 = x - border_left + FIXTOINT(nscss_len2px( - &html->len_ctx, - css_rect.right, css_rect.runit, - box->style)); - - if (css_rect.bottom_auto == false) - r.y1 = y - border_top + FIXTOINT(nscss_len2px( - &html->len_ctx, - css_rect.bottom, css_rect.bunit, - box->style)); - - /* find intersection of clip rectangle and box */ - if (r.x0 < clip->x0) r.x0 = clip->x0; - if (r.y0 < clip->y0) r.y0 = clip->y0; - if (clip->x1 < r.x1) r.x1 = clip->x1; - if (clip->y1 < r.y1) r.y1 = clip->y1; - /* Nothing to do for invalid rectangles */ - if (r.x0 >= r.x1 || r.y0 >= r.y1) - /* not an error */ - return ((!ctx->plot->group_end) || - (ctx->plot->group_end(ctx) == NSERROR_OK)); - /* clip to it */ - if (ctx->plot->clip(ctx, &r) != NSERROR_OK) - return false; - - } else if (box->type == BOX_BLOCK || box->type == BOX_INLINE_BLOCK || - box->type == BOX_TABLE_CELL || box->object) { - /* find intersection of clip rectangle and box */ - if (r.x0 < clip->x0) r.x0 = clip->x0; - if (r.y0 < clip->y0) r.y0 = clip->y0; - if (clip->x1 < r.x1) r.x1 = clip->x1; - if (clip->y1 < r.y1) r.y1 = clip->y1; - /* no point trying to draw 0-width/height boxes */ - if (r.x0 == r.x1 || r.y0 == r.y1) - /* not an error */ - return ((!ctx->plot->group_end) || - (ctx->plot->group_end(ctx) == NSERROR_OK)); - /* clip to it */ - if (ctx->plot->clip(ctx, &r) != NSERROR_OK) - return false; - } else { - /* clip box is fine, clip to it */ - r = *clip; - if (ctx->plot->clip(ctx, &r) != NSERROR_OK) - return false; - } - - /* background colour and image for block level content and replaced - * inlines */ - - bg_box = html_redraw_find_bg_box(box); - - /* bg_box == NULL implies that this box should not have - * its background rendered. Otherwise filter out linebreaks, - * optimize away non-differing inlines, only plot background - * for BOX_TEXT it's in an inline */ - if (bg_box && bg_box->type != BOX_BR && - bg_box->type != BOX_TEXT && - bg_box->type != BOX_INLINE_END && - (bg_box->type != BOX_INLINE || bg_box->object || - bg_box->flags & IFRAME || box->flags & REPLACE_DIM || - (bg_box->gadget != NULL && - (bg_box->gadget->type == GADGET_TEXTAREA || - bg_box->gadget->type == GADGET_TEXTBOX || - bg_box->gadget->type == GADGET_PASSWORD)))) { - /* find intersection of clip box and border edge */ - struct rect p; - p.x0 = x - border_left < r.x0 ? r.x0 : x - border_left; - p.y0 = y - border_top < r.y0 ? r.y0 : y - border_top; - p.x1 = x + padding_width + border_right < r.x1 ? - x + padding_width + border_right : r.x1; - p.y1 = y + padding_height + border_bottom < r.y1 ? - y + padding_height + border_bottom : r.y1; - if (!box->parent) { - /* Root element, special case: - * background covers margins too */ - int m_left, m_top, m_right, m_bottom; - if (scale == 1.0) { - m_left = box->margin[LEFT]; - m_top = box->margin[TOP]; - m_right = box->margin[RIGHT]; - m_bottom = box->margin[BOTTOM]; - } else { - m_left = box->margin[LEFT] * scale; - m_top = box->margin[TOP] * scale; - m_right = box->margin[RIGHT] * scale; - m_bottom = box->margin[BOTTOM] * scale; - } - p.x0 = p.x0 - m_left < r.x0 ? r.x0 : p.x0 - m_left; - p.y0 = p.y0 - m_top < r.y0 ? r.y0 : p.y0 - m_top; - p.x1 = p.x1 + m_right < r.x1 ? p.x1 + m_right : r.x1; - p.y1 = p.y1 + m_bottom < r.y1 ? p.y1 + m_bottom : r.y1; - } - /* valid clipping rectangles only */ - if ((p.x0 < p.x1) && (p.y0 < p.y1)) { - /* plot background */ - if (!html_redraw_background(x, y, box, scale, &p, - ¤t_background_color, bg_box, - &html->len_ctx, ctx)) - return false; - /* restore previous graphics window */ - if (ctx->plot->clip(ctx, &r) != NSERROR_OK) - return false; - } - } - - /* borders for block level content and replaced inlines */ - if (box->style && - box->type != BOX_TEXT && - box->type != BOX_INLINE_END && - (box->type != BOX_INLINE || box->object || - box->flags & IFRAME || box->flags & REPLACE_DIM || - (box->gadget != NULL && - (box->gadget->type == GADGET_TEXTAREA || - box->gadget->type == GADGET_TEXTBOX || - box->gadget->type == GADGET_PASSWORD))) && - (border_top || border_right || border_bottom || border_left)) { - if (!html_redraw_borders(box, x_parent, y_parent, - padding_width, padding_height, &r, - scale, ctx)) - return false; - } - - /* backgrounds and borders for non-replaced inlines */ - if (box->style && box->type == BOX_INLINE && box->inline_end && - (html_redraw_box_has_background(box) || - border_top || border_right || - border_bottom || border_left)) { - /* inline backgrounds and borders span other boxes and may - * wrap onto separate lines */ - struct box *ib; - struct rect b; /* border edge rectangle */ - struct rect p; /* clipped rect */ - bool first = true; - int ib_x; - int ib_y = y; - int ib_p_width; - int ib_b_left, ib_b_right; - - b.x0 = x - border_left; - b.x1 = x + padding_width + border_right; - b.y0 = y - border_top; - b.y1 = y + padding_height + border_bottom; - - p.x0 = b.x0 < r.x0 ? r.x0 : b.x0; - p.x1 = b.x1 < r.x1 ? b.x1 : r.x1; - p.y0 = b.y0 < r.y0 ? r.y0 : b.y0; - p.y1 = b.y1 < r.y1 ? b.y1 : r.y1; - for (ib = box; ib; ib = ib->next) { - /* to get extents of rectangle(s) associated with - * inline, cycle though all boxes in inline, skipping - * over floats */ - if (ib->type == BOX_FLOAT_LEFT || - ib->type == BOX_FLOAT_RIGHT) - continue; - if (scale == 1.0) { - ib_x = x_parent + ib->x; - ib_y = y_parent + ib->y; - ib_p_width = ib->padding[LEFT] + ib->width + - ib->padding[RIGHT]; - ib_b_left = ib->border[LEFT].width; - ib_b_right = ib->border[RIGHT].width; - } else { - ib_x = (x_parent + ib->x) * scale; - ib_y = (y_parent + ib->y) * scale; - ib_p_width = (ib->padding[LEFT] + ib->width + - ib->padding[RIGHT]) * scale; - ib_b_left = ib->border[LEFT].width * scale; - ib_b_right = ib->border[RIGHT].width * scale; - } - - if ((ib->flags & NEW_LINE) && ib != box) { - /* inline element has wrapped, plot background - * and borders */ - if (!html_redraw_inline_background( - x, y, box, scale, &p, b, - first, false, - ¤t_background_color, - &html->len_ctx, ctx)) - return false; - /* restore previous graphics window */ - if (ctx->plot->clip(ctx, &r) != NSERROR_OK) - return false; - if (!html_redraw_inline_borders(box, b, &r, - scale, first, false, ctx)) - return false; - /* reset coords */ - b.x0 = ib_x - ib_b_left; - b.y0 = ib_y - border_top - padding_top; - b.y1 = ib_y + padding_height - padding_top + - border_bottom; - - p.x0 = b.x0 < r.x0 ? r.x0 : b.x0; - p.y0 = b.y0 < r.y0 ? r.y0 : b.y0; - p.y1 = b.y1 < r.y1 ? b.y1 : r.y1; - - first = false; - } - - /* increase width for current box */ - b.x1 = ib_x + ib_p_width + ib_b_right; - p.x1 = b.x1 < r.x1 ? b.x1 : r.x1; - - if (ib == box->inline_end) - /* reached end of BOX_INLINE span */ - break; - } - /* plot background and borders for last rectangle of - * the inline */ - if (!html_redraw_inline_background(x, ib_y, box, scale, &p, b, - first, true, ¤t_background_color, - &html->len_ctx, ctx)) - return false; - /* restore previous graphics window */ - if (ctx->plot->clip(ctx, &r) != NSERROR_OK) - return false; - if (!html_redraw_inline_borders(box, b, &r, scale, first, true, - ctx)) - return false; - - } - - /* Debug outlines */ - if (html_redraw_debug) { - int margin_left, margin_right; - int margin_top, margin_bottom; - if (scale == 1.0) { - /* avoid trivial fp maths */ - margin_left = box->margin[LEFT]; - margin_top = box->margin[TOP]; - margin_right = box->margin[RIGHT]; - margin_bottom = box->margin[BOTTOM]; - } else { - margin_left = box->margin[LEFT] * scale; - margin_top = box->margin[TOP] * scale; - margin_right = box->margin[RIGHT] * scale; - margin_bottom = box->margin[BOTTOM] * scale; - } - /* Content edge -- blue */ - rect.x0 = x + padding_left; - rect.y0 = y + padding_top; - rect.x1 = x + padding_left + width; - rect.y1 = y + padding_top + height; - if (ctx->plot->rectangle(ctx, plot_style_content_edge, &rect) != NSERROR_OK) - return false; - - /* Padding edge -- red */ - rect.x0 = x; - rect.y0 = y; - rect.x1 = x + padding_width; - rect.y1 = y + padding_height; - if (ctx->plot->rectangle(ctx, plot_style_padding_edge, &rect) != NSERROR_OK) - return false; - - /* Margin edge -- yellow */ - rect.x0 = x - border_left - margin_left; - rect.y0 = y - border_top - margin_top; - rect.x1 = x + padding_width + border_right + margin_right; - rect.y1 = y + padding_height + border_bottom + margin_bottom; - if (ctx->plot->rectangle(ctx, plot_style_margin_edge, &rect) != NSERROR_OK) - return false; - } - - /* clip to the padding edge for objects, or boxes with overflow hidden - * or scroll, unless it's the root element */ - if (box->parent != NULL) { - bool need_clip = false; - if (box->object || box->flags & IFRAME || - (overflow_x != CSS_OVERFLOW_VISIBLE && - overflow_y != CSS_OVERFLOW_VISIBLE)) { - r.x0 = x; - r.y0 = y; - r.x1 = x + padding_width; - r.y1 = y + padding_height; - if (r.x0 < clip->x0) r.x0 = clip->x0; - if (r.y0 < clip->y0) r.y0 = clip->y0; - if (clip->x1 < r.x1) r.x1 = clip->x1; - if (clip->y1 < r.y1) r.y1 = clip->y1; - if (r.x1 <= r.x0 || r.y1 <= r.y0) { - return (!ctx->plot->group_end || - (ctx->plot->group_end(ctx) == NSERROR_OK)); - } - need_clip = true; - - } else if (overflow_x != CSS_OVERFLOW_VISIBLE) { - r.x0 = x; - r.y0 = clip->y0; - r.x1 = x + padding_width; - r.y1 = clip->y1; - if (r.x0 < clip->x0) r.x0 = clip->x0; - if (clip->x1 < r.x1) r.x1 = clip->x1; - if (r.x1 <= r.x0) { - return (!ctx->plot->group_end || - (ctx->plot->group_end(ctx) == NSERROR_OK)); - } - need_clip = true; - - } else if (overflow_y != CSS_OVERFLOW_VISIBLE) { - r.x0 = clip->x0; - r.y0 = y; - r.x1 = clip->x1; - r.y1 = y + padding_height; - if (r.y0 < clip->y0) r.y0 = clip->y0; - if (clip->y1 < r.y1) r.y1 = clip->y1; - if (r.y1 <= r.y0) { - return (!ctx->plot->group_end || - (ctx->plot->group_end(ctx) == NSERROR_OK)); - } - need_clip = true; - } - - if (need_clip && - (box->type == BOX_BLOCK || - box->type == BOX_INLINE_BLOCK || - box->type == BOX_TABLE_CELL || box->object)) { - if (ctx->plot->clip(ctx, &r) != NSERROR_OK) - return false; - } - } - - /* text decoration */ - if ((box->type != BOX_TEXT) && - box->style && - css_computed_text_decoration(box->style) != CSS_TEXT_DECORATION_NONE) { - if (!html_redraw_text_decoration(box, x_parent, y_parent, - scale, current_background_color, ctx)) - return false; - } - - if (box->object && width != 0 && height != 0) { - struct content_redraw_data obj_data; - - x_scrolled = x - scrollbar_get_offset(box->scroll_x) * scale; - y_scrolled = y - scrollbar_get_offset(box->scroll_y) * scale; - - obj_data.x = x_scrolled + padding_left; - obj_data.y = y_scrolled + padding_top; - obj_data.width = width; - obj_data.height = height; - obj_data.background_colour = current_background_color; - obj_data.scale = scale; - obj_data.repeat_x = false; - obj_data.repeat_y = false; - - if (content_get_type(box->object) == CONTENT_HTML) { - obj_data.x /= scale; - obj_data.y /= scale; - } - - if (!content_redraw(box->object, &obj_data, &r, ctx)) { - /* Show image fail */ - /* Unicode (U+FFFC) 'OBJECT REPLACEMENT CHARACTER' */ - const char *obj = "\xef\xbf\xbc"; - int obj_width; - int obj_x = x + padding_left; - nserror res; - - rect.x0 = x + padding_left; - rect.y0 = y + padding_top; - rect.x1 = x + padding_left + width - 1; - rect.y1 = y + padding_top + height - 1; - res = ctx->plot->rectangle(ctx, plot_style_broken_object, &rect); - if (res != NSERROR_OK) { - return false; - } - - res = guit->layout->width(plot_fstyle_broken_object, - obj, - sizeof(obj) - 1, - &obj_width); - if (res != NSERROR_OK) { - obj_x += 1; - } else { - obj_x += width / 2 - obj_width / 2; - } - - if (ctx->plot->text(ctx, - plot_fstyle_broken_object, - obj_x, y + padding_top + (int)(height * 0.75), - obj, sizeof(obj) - 1) != NSERROR_OK) - return false; - } - - } else if (box->iframe) { - /* Offset is passed to browser window redraw unscaled */ - browser_window_redraw(box->iframe, - (x + padding_left) / scale, - (y + padding_top) / scale, &r, ctx); - - } else if (box->gadget && box->gadget->type == GADGET_CHECKBOX) { - if (!html_redraw_checkbox(x + padding_left, y + padding_top, - width, height, box->gadget->selected, ctx)) - return false; - - } else if (box->gadget && box->gadget->type == GADGET_RADIO) { - if (!html_redraw_radio(x + padding_left, y + padding_top, - width, height, box->gadget->selected, ctx)) - return false; - - } else if (box->gadget && box->gadget->type == GADGET_FILE) { - if (!html_redraw_file(x + padding_left, y + padding_top, - width, height, box, scale, - current_background_color, &html->len_ctx, ctx)) - return false; - - } else if (box->gadget && - (box->gadget->type == GADGET_TEXTAREA || - box->gadget->type == GADGET_PASSWORD || - box->gadget->type == GADGET_TEXTBOX)) { - textarea_redraw(box->gadget->data.text.ta, x, y, - current_background_color, scale, &r, ctx); - - } else if (box->text) { - if (!html_redraw_text_box(html, box, x, y, &r, scale, - current_background_color, ctx)) - return false; - - } else { - if (!html_redraw_box_children(html, box, x_parent, y_parent, &r, - scale, current_background_color, ctx)) - return false; - } - - if (box->type == BOX_BLOCK || box->type == BOX_INLINE_BLOCK || - box->type == BOX_TABLE_CELL || box->type == BOX_INLINE) - if (ctx->plot->clip(ctx, clip) != NSERROR_OK) - return false; - - /* list marker */ - if (box->list_marker) { - if (!html_redraw_box(html, box->list_marker, - x_parent + box->x - - scrollbar_get_offset(box->scroll_x), - y_parent + box->y - - scrollbar_get_offset(box->scroll_y), - clip, scale, current_background_color, ctx)) - return false; - } - - /* scrollbars */ - if (((box->style && box->type != BOX_BR && - box->type != BOX_TABLE && box->type != BOX_INLINE && - (overflow_x == CSS_OVERFLOW_SCROLL || - overflow_x == CSS_OVERFLOW_AUTO || - overflow_y == CSS_OVERFLOW_SCROLL || - overflow_y == CSS_OVERFLOW_AUTO)) || - (box->object && content_get_type(box->object) == - CONTENT_HTML)) && box->parent != NULL) { - - has_x_scroll = box_hscrollbar_present(box); - has_y_scroll = box_vscrollbar_present(box); - - if (!box_handle_scrollbars((struct content *)html, - box, has_x_scroll, has_y_scroll)) - return false; - - if (box->scroll_x != NULL) - scrollbar_redraw(box->scroll_x, - x_parent + box->x, - y_parent + box->y + box->padding[TOP] + - box->height + box->padding[BOTTOM] - - SCROLLBAR_WIDTH, clip, scale, ctx); - if (box->scroll_y != NULL) - scrollbar_redraw(box->scroll_y, - x_parent + box->x + box->padding[LEFT] + - box->width + box->padding[RIGHT] - - SCROLLBAR_WIDTH, - y_parent + box->y, clip, scale, ctx); - } - - if (box->type == BOX_BLOCK || box->type == BOX_INLINE_BLOCK || - box->type == BOX_TABLE_CELL || box->type == BOX_INLINE) { - if (ctx->plot->clip(ctx, clip) != NSERROR_OK) - return false; - } - - return ((!plot->group_end) || (ctx->plot->group_end(ctx) == NSERROR_OK)); -} - -/** - * Draw a CONTENT_HTML using the current set of plotters (plot). - * - * \param c content of type CONTENT_HTML - * \param data redraw data for this content redraw - * \param clip current clip region - * \param ctx current redraw context - * \return true if successful, false otherwise - * - * x, y, clip_[xy][01] are in target coordinates. - */ - -bool html_redraw(struct content *c, struct content_redraw_data *data, - const struct rect *clip, const struct redraw_context *ctx) -{ - html_content *html = (html_content *) c; - struct box *box; - bool result = true; - bool select, select_only; - plot_style_t pstyle_fill_bg = { - .fill_type = PLOT_OP_TYPE_SOLID, - .fill_colour = data->background_colour, - }; - - box = html->layout; - assert(box); - - /* The select menu needs special treating because, when opened, it - * reaches beyond its layout box. - */ - select = false; - select_only = false; - if (ctx->interactive && html->visible_select_menu != NULL) { - struct form_control *control = html->visible_select_menu; - select = true; - /* check if the redraw rectangle is completely inside of the - select menu */ - select_only = form_clip_inside_select_menu(control, - data->scale, clip); - } - - if (!select_only) { - /* clear to background colour */ - result = (ctx->plot->clip(ctx, clip) == NSERROR_OK); - - if (html->background_colour != NS_TRANSPARENT) - pstyle_fill_bg.fill_colour = html->background_colour; - - result &= (ctx->plot->rectangle(ctx, &pstyle_fill_bg, clip) == NSERROR_OK); - - result &= html_redraw_box(html, box, data->x, data->y, clip, - data->scale, pstyle_fill_bg.fill_colour, ctx); - } - - if (select) { - int menu_x, menu_y; - box = html->visible_select_menu->box; - box_coords(box, &menu_x, &menu_y); - - menu_x -= box->border[LEFT].width; - menu_y += box->height + box->border[BOTTOM].width + - box->padding[BOTTOM] + box->padding[TOP]; - result &= form_redraw_select_menu(html->visible_select_menu, - data->x + menu_x, data->y + menu_y, - data->scale, clip, ctx); - } - - return result; - -} diff --git a/render/html_redraw_border.c b/render/html_redraw_border.c deleted file mode 100644 index 07c503c41..000000000 --- a/render/html_redraw_border.c +++ /dev/null @@ -1,928 +0,0 @@ -/* - * Copyright 2017 Vincent Sanders <vince@netsurf-browser.org> - * - * 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 <http://www.gnu.org/licenses/>. - */ - -/** - * \file - * - * Redrawing CONTENT_HTML borders implementation. - */ - -#include <stdbool.h> -#include <stdlib.h> - -#include "utils/log.h" -#include "netsurf/plotters.h" -#include "netsurf/css.h" - -#include "render/box.h" -#include "render/html_internal.h" - - -static plot_style_t plot_style_bdr = { - .stroke_type = PLOT_OP_TYPE_DASH, -}; -static plot_style_t plot_style_fillbdr = { - .fill_type = PLOT_OP_TYPE_SOLID, -}; -static plot_style_t plot_style_fillbdr_dark = { - .fill_type = PLOT_OP_TYPE_SOLID, -}; -static plot_style_t plot_style_fillbdr_light = { - .fill_type = PLOT_OP_TYPE_SOLID, -}; -static plot_style_t plot_style_fillbdr_ddark = { - .fill_type = PLOT_OP_TYPE_SOLID, -}; -static plot_style_t plot_style_fillbdr_dlight = { - .fill_type = PLOT_OP_TYPE_SOLID, -}; - - -static inline nserror -plot_clipped_rectangle(const struct redraw_context *ctx, - const plot_style_t *style, - const struct rect *clip, - struct rect *rect) -{ - nserror res; - - rect->x0 = (clip->x0 > rect->x0) ? clip->x0 : rect->x0; - rect->y0 = (clip->y0 > rect->y0) ? clip->y0 : rect->y0; - rect->x1 = (clip->x1 < rect->x1) ? clip->x1 : rect->x1; - rect->y1 = (clip->y1 < rect->y1) ? clip->y1 : rect->y1; - if ((rect->x0 < rect->x1) && (rect->y0 < rect->y1)) { - /* valid clip rectangles only */ - res = ctx->plot->rectangle(ctx, style, rect); - } else { - res = NSERROR_OK; - } - return res; -} - - -/** - * Draw one border. - * - * \param side index of border side (TOP, RIGHT, BOTTOM, LEFT) - * \param p array of precomputed border vertices - * \param c colour for border - * \param style border line style - * \param thickness border thickness - * \param rectangular whether border is rectangular - * \param clip cliping area for redrawing border. - * \param ctx current redraw context - * \return NSERROR_OK if successful otherwise appropriate error code - */ -static nserror -html_redraw_border_plot(const int side, - const int *p, - colour c, - enum css_border_style_e style, - int thickness, - bool rectangular, - const struct rect *clip, - const struct redraw_context *ctx) -{ - int z[8]; /* Vertices of border part */ - unsigned int light = side; - plot_style_t *plot_style_bdr_in; - plot_style_t *plot_style_bdr_out; - nserror res = NSERROR_OK; - struct rect rect; - - if (c == NS_TRANSPARENT) { - return res; - } - - plot_style_bdr.stroke_type = PLOT_OP_TYPE_DASH; - plot_style_bdr.stroke_colour = c; - plot_style_bdr.stroke_width = thickness; - plot_style_fillbdr.fill_colour = c; - plot_style_fillbdr_dark.fill_colour = darken_colour(c); - plot_style_fillbdr_light.fill_colour = lighten_colour(c); - plot_style_fillbdr_ddark.fill_colour = double_darken_colour(c); - plot_style_fillbdr_dlight.fill_colour = double_lighten_colour(c); - - switch (style) { - case CSS_BORDER_STYLE_DOTTED: - plot_style_bdr.stroke_type = PLOT_OP_TYPE_DOT; - /* fall through */ - case CSS_BORDER_STYLE_DASHED: - rect.x0 = (p[0] + p[2]) / 2; - rect.y0 = (p[1] + p[3]) / 2; - rect.x1 = (p[4] + p[6]) / 2; - rect.y1 = (p[5] + p[7]) / 2; - res = ctx->plot->line(ctx, &plot_style_bdr, &rect); - break; - - case CSS_BORDER_STYLE_SOLID: - /* fall through to default */ - default: - if (rectangular || thickness == 1) { - - if (side == TOP || side == RIGHT) { - rect.x0 = p[2]; - rect.y0 = p[3]; - if ((side == TOP) && - (p[4] - p[6] != 0)) { - rect.x1 = p[4]; - } else { - rect.x1 = p[6]; - } - rect.y1 = p[7]; - } else { - rect.x0 = p[6]; - rect.y0 = p[7]; - rect.x1 = p[2]; - if ((side == LEFT) && - (p[1] - p[3] != 0)) { - rect.y1 = p[1]; - } else { - rect.y1 = p[3]; - } - } - res = plot_clipped_rectangle(ctx, - &plot_style_fillbdr, - clip, - &rect); - } else { - res = ctx->plot->polygon(ctx, &plot_style_fillbdr, p, 4); - } - break; - - case CSS_BORDER_STYLE_DOUBLE: - z[0] = p[0]; - z[1] = p[1]; - z[2] = (p[0] * 2 + p[2]) / 3; - z[3] = (p[1] * 2 + p[3]) / 3; - z[4] = (p[6] * 2 + p[4]) / 3; - z[5] = (p[7] * 2 + p[5]) / 3; - z[6] = p[6]; - z[7] = p[7]; - res = ctx->plot->polygon(ctx, &plot_style_fillbdr, z, 4); - if (res == NSERROR_OK) { - z[0] = p[2]; - z[1] = p[3]; - z[2] = (p[2] * 2 + p[0]) / 3; - z[3] = (p[3] * 2 + p[1]) / 3; - z[4] = (p[4] * 2 + p[6]) / 3; - z[5] = (p[5] * 2 + p[7]) / 3; - z[6] = p[4]; - z[7] = p[5]; - res = ctx->plot->polygon(ctx, &plot_style_fillbdr, z, 4); - } - break; - - case CSS_BORDER_STYLE_GROOVE: - light = 3 - light; - /* fall through */ - case CSS_BORDER_STYLE_RIDGE: - /* choose correct colours for each part of the border line */ - if (light <= 1) { - plot_style_bdr_in = &plot_style_fillbdr_dark; - plot_style_bdr_out = &plot_style_fillbdr_light; - } else { - plot_style_bdr_in = &plot_style_fillbdr_light; - plot_style_bdr_out = &plot_style_fillbdr_dark; - } - - /* Render border */ - if ((rectangular || thickness == 2) && thickness != 1) { - /* Border made up from two parts and can be - * plotted with rectangles - */ - - /* First part */ - if (side == TOP || side == RIGHT) { - rect.x0 = (p[0] + p[2]) / 2; - rect.y0 = (p[1] + p[3]) / 2; - rect.x1 = p[6]; - rect.y1 = p[7]; - } else { - rect.x0 = p[6]; - rect.y0 = p[7]; - rect.x1 = (p[0] + p[2]) / 2; - rect.y1 = (p[1] + p[3]) / 2; - } - res = plot_clipped_rectangle(ctx, - plot_style_bdr_in, - clip, - &rect); - if (res != NSERROR_OK) { - return res; - } - - /* Second part */ - if (side == TOP || side == RIGHT) { - rect.x0 = p[2]; - rect.y0 = p[3]; - rect.x1 = (p[6] + p[4]) / 2; - rect.y1 = (p[7] + p[5]) / 2; - } else { - rect.x0 = (p[6] + p[4]) / 2; - rect.y0 = (p[7] + p[5]) / 2; - rect.x1 = p[2]; - rect.y1 = p[3]; - } - res = plot_clipped_rectangle(ctx, - plot_style_bdr_out, - clip, - &rect); - } else if (thickness == 1) { - /* Border made up from one part which can be - * plotted as a rectangle - */ - - if (side == TOP || side == RIGHT) { - rect.x0 = p[2]; - rect.y0 = p[3]; - rect.x1 = p[6]; - rect.y1 = p[7]; - rect.x1 = ((side == TOP) && (p[4] - p[6] != 0)) ? - rect.x1 + p[4] - p[6] : rect.x1; - - res = plot_clipped_rectangle(ctx, - plot_style_bdr_in, - clip, - &rect); - } else { - rect.x0 = p[6]; - rect.y0 = p[7]; - rect.x1 = p[2]; - rect.y1 = p[3]; - rect.y1 = ((side == LEFT) && (p[1] - p[3] != 0)) ? - rect.y1 + p[1] - p[3] : rect.y1; - res = plot_clipped_rectangle(ctx, - plot_style_bdr_out, - clip, - &rect); - } - } else { - /* Border made up from two parts and can't be - * plotted with rectangles - */ - z[0] = p[0]; - z[1] = p[1]; - z[2] = (p[0] + p[2]) / 2; - z[3] = (p[1] + p[3]) / 2; - z[4] = (p[6] + p[4]) / 2; - z[5] = (p[7] + p[5]) / 2; - z[6] = p[6]; - z[7] = p[7]; - res = ctx->plot->polygon(ctx, plot_style_bdr_in, z, 4); - if (res == NSERROR_OK) { - z[0] = p[2]; - z[1] = p[3]; - z[6] = p[4]; - z[7] = p[5]; - res = ctx->plot->polygon(ctx, - plot_style_bdr_out, - z, - 4); - } - } - break; - - case CSS_BORDER_STYLE_INSET: - light = (light + 2) % 4; - /* fall through */ - case CSS_BORDER_STYLE_OUTSET: - /* choose correct colours for each part of the border line */ - switch (light) { - case 0: - plot_style_bdr_in = &plot_style_fillbdr_light; - plot_style_bdr_out = &plot_style_fillbdr_dlight; - break; - case 1: - plot_style_bdr_in = &plot_style_fillbdr_ddark; - plot_style_bdr_out = &plot_style_fillbdr_dark; - break; - case 2: - plot_style_bdr_in = &plot_style_fillbdr_dark; - plot_style_bdr_out = &plot_style_fillbdr_ddark; - break; - case 3: - plot_style_bdr_in = &plot_style_fillbdr_dlight; - plot_style_bdr_out = &plot_style_fillbdr_light; - break; - default: - plot_style_bdr_in = &plot_style_fillbdr; - plot_style_bdr_out = &plot_style_fillbdr; - break; - } - - /* Render border */ - if ((rectangular || thickness == 2) && thickness != 1) { - /* Border made up from two parts and can be - * plotted with rectangles - */ - - /* First part */ - if (side == TOP || side == RIGHT) { - rect.x0 = (p[0] + p[2]) / 2; - rect.y0 = (p[1] + p[3]) / 2; - rect.x1 = p[6]; - rect.y1 = p[7]; - } else { - rect.x0 = p[6]; - rect.y0 = p[7]; - rect.x1 = (p[0] + p[2]) / 2; - rect.y1 = (p[1] + p[3]) / 2; - } - res = plot_clipped_rectangle(ctx, - plot_style_bdr_in, - clip, - &rect); - if (res != NSERROR_OK) { - return res; - } - - /* Second part */ - if (side == TOP || side == RIGHT) { - rect.x0 = p[2]; - rect.y0 = p[3]; - rect.x1 = (p[6] + p[4]) / 2; - rect.y1 = (p[7] + p[5]) / 2; - } else { - rect.x0 = (p[6] + p[4]) / 2; - rect.y0 = (p[7] + p[5]) / 2; - rect.x1 = p[2]; - rect.y1 = p[3]; - } - res = plot_clipped_rectangle(ctx, - plot_style_bdr_out, - clip, - &rect); - } else if (thickness == 1) { - /* Border made up from one part which can be - * plotted as a rectangle - */ - - if (side == TOP || side == RIGHT) { - rect.x0 = p[2]; - rect.y0 = p[3]; - rect.x1 = p[6]; - rect.y1 = p[7]; - rect.x1 = ((side == TOP) && (p[4] - p[6] != 0)) ? - rect.x1 + p[4] - p[6] : rect.x1; - res = plot_clipped_rectangle(ctx, - plot_style_bdr_in, - clip, - &rect); - } else { - rect.x0 = p[6]; - rect.y0 = p[7]; - rect.x1 = p[2]; - rect.y1 = p[3]; - rect.y1 = ((side == LEFT) && (p[1] - p[3] != 0)) ? - rect.y1 + p[1] - p[3] : rect.y1; - res = plot_clipped_rectangle(ctx, - plot_style_bdr_out, - clip, - &rect); - } - } else { - /* Border made up from two parts and can't be - * plotted with rectangles - */ - - z[0] = p[0]; - z[1] = p[1]; - z[2] = (p[0] + p[2]) / 2; - z[3] = (p[1] + p[3]) / 2; - z[4] = (p[6] + p[4]) / 2; - z[5] = (p[7] + p[5]) / 2; - z[6] = p[6]; - z[7] = p[7]; - res = ctx->plot->polygon(ctx, plot_style_bdr_in, z, 4); - if (res != NSERROR_OK) { - return res; - } - z[0] = p[2]; - z[1] = p[3]; - z[6] = p[4]; - z[7] = p[5]; - res = ctx->plot->polygon(ctx, plot_style_bdr_out, z, 4); - } - break; - } - - return res; -} - - -/** - * Draw borders for a box. - * - * \param box box to draw - * \param x_parent coordinate of left padding edge of parent of box - * \param y_parent coordinate of top padding edge of parent of box - * \param p_width width of padding box - * \param p_height height of padding box - * \param clip cliping area for redrawing border. - * \param scale scale for redraw - * \param ctx current redraw context - * \return true if successful, false otherwise - */ -bool -html_redraw_borders(struct box *box, - int x_parent, - int y_parent, - int p_width, - int p_height, - const struct rect *clip, - float scale, - const struct redraw_context *ctx) -{ - unsigned int sides[] = { LEFT, RIGHT, TOP, BOTTOM }; - int top = box->border[TOP].width; - int right = box->border[RIGHT].width; - int bottom = box->border[BOTTOM].width; - int left = box->border[LEFT].width; - int x, y; - unsigned int i, side; - int p[8]; /* Box border vertices */ - int z[8]; /* Border vertices */ - bool square_end_1 = false; - bool square_end_2 = false; - nserror res; - - x = x_parent + box->x; - y = y_parent + box->y; - - if (scale != 1.0) { - top *= scale; - right *= scale; - bottom *= scale; - left *= scale; - x *= scale; - y *= scale; - } - - assert(box->style); - - /* Calculate border vertices - * - * A----------------------+ - * | \ / | - * | B--------------+ | - * | | | | - * | +--------------C | - * | / \ | - * +----------------------D - */ - p[0] = x - left; p[1] = y - top; /* A */ - p[2] = x; p[3] = y; /* B */ - p[4] = x + p_width; p[5] = y + p_height; /* C */ - p[6] = x + p_width + right; p[7] = y + p_height + bottom; /* D */ - - for (i = 0; i != 4; i++) { - colour col = 0; - side = sides[i]; /* plot order */ - - if (box->border[side].width == 0 || - nscss_color_is_transparent(box->border[side].c)) { - continue; - } - - switch (side) { - case LEFT: - square_end_1 = (top == 0); - square_end_2 = (bottom == 0); - - z[0] = p[0]; z[1] = p[7]; - z[2] = p[2]; z[3] = p[5]; - z[4] = p[2]; z[5] = p[3]; - z[6] = p[0]; z[7] = p[1]; - - if (nscss_color_is_transparent(box->border[TOP].c) == false && - box->border[TOP].style != CSS_BORDER_STYLE_DOUBLE) { - /* make border overhang top corner fully, - * if top border is opaque - */ - z[5] -= top; - square_end_1 = true; - } - if (nscss_color_is_transparent(box->border[BOTTOM].c) == false && - box->border[BOTTOM].style != CSS_BORDER_STYLE_DOUBLE) { - /* make border overhang bottom corner fully, - * if bottom border is opaque - */ - z[3] += bottom; - square_end_2 = true; - } - - col = nscss_color_to_ns(box->border[side].c); - - res = html_redraw_border_plot(side, - z, - col, - box->border[side].style, - box->border[side].width * scale, - square_end_1 && square_end_2, - clip, - ctx); - if (res != NSERROR_OK) { - return false; - } - break; - - case RIGHT: - square_end_1 = (top == 0); - square_end_2 = (bottom == 0); - - z[0] = p[6]; z[1] = p[1]; - z[2] = p[4]; z[3] = p[3]; - z[4] = p[4]; z[5] = p[5]; - z[6] = p[6]; z[7] = p[7]; - - if (nscss_color_is_transparent(box->border[TOP].c) == false && - box->border[TOP].style != CSS_BORDER_STYLE_DOUBLE) { - /* make border overhang top corner fully, - * if top border is opaque - */ - z[3] -= top; - square_end_1 = true; - } - if (nscss_color_is_transparent(box->border[BOTTOM].c) == false && - box->border[BOTTOM].style != CSS_BORDER_STYLE_DOUBLE) { - /* make border overhang bottom corner fully, - * if bottom border is opaque - */ - z[5] += bottom; - square_end_2 = true; - } - - col = nscss_color_to_ns(box->border[side].c); - - res = html_redraw_border_plot(side, - z, - col, - box->border[side].style, - box->border[side].width * scale, - square_end_1 && square_end_2, - clip, - ctx); - if (res != NSERROR_OK) { - return false; - } - break; - - case TOP: - if (clip->y0 > p[3]) { - /* clip rectangle is below border; nothing to - * plot - */ - continue; - } - - square_end_1 = (left == 0); - square_end_2 = (right == 0); - - z[0] = p[2]; z[1] = p[3]; - z[2] = p[0]; z[3] = p[1]; - z[4] = p[6]; z[5] = p[1]; - z[6] = p[4]; z[7] = p[3]; - - if (box->border[TOP].style == CSS_BORDER_STYLE_SOLID && - box->border[TOP].c == box->border[LEFT].c) { - /* don't bother overlapping left corner if - * it's the same colour anyway - */ - z[2] += left; - square_end_1 = true; - } - if (box->border[TOP].style == CSS_BORDER_STYLE_SOLID && - box->border[TOP].c == box->border[RIGHT].c) { - /* don't bother overlapping right corner if - * it's the same colour anyway - */ - z[4] -= right; - square_end_2 = true; - } - - col = nscss_color_to_ns(box->border[side].c); - - res = html_redraw_border_plot(side, - z, - col, - box->border[side].style, - box->border[side].width * scale, - square_end_1 && square_end_2, - clip, - ctx); - if (res != NSERROR_OK) { - return false; - } - break; - - case BOTTOM: - if (clip->y1 < p[5]) { - /* clip rectangle is above border; nothing to - * plot - */ - continue; - } - - square_end_1 = (left == 0); - square_end_2 = (right == 0); - - z[0] = p[4]; z[1] = p[5]; - z[2] = p[6]; z[3] = p[7]; - z[4] = p[0]; z[5] = p[7]; - z[6] = p[2]; z[7] = p[5]; - - if (box->border[BOTTOM].style == CSS_BORDER_STYLE_SOLID && - box->border[BOTTOM].c == box->border[LEFT].c) { - /* don't bother overlapping left corner if - * it's the same colour anyway - */ - z[4] += left; - square_end_1 = true; - } - if (box->border[BOTTOM].style == CSS_BORDER_STYLE_SOLID && - box->border[BOTTOM].c == box->border[RIGHT].c) { - /* don't bother overlapping right corner if - * it's the same colour anyway - */ - z[2] -= right; - square_end_2 = true; - } - - col = nscss_color_to_ns(box->border[side].c); - - res = html_redraw_border_plot(side, - z, - col, - box->border[side].style, - box->border[side].width * scale, - square_end_1 && square_end_2, - clip, - ctx); - if (res != NSERROR_OK) { - return false; - } - break; - - default: - assert(side == TOP || side == BOTTOM || - side == LEFT || side == RIGHT); - break; - } - } - - return true; -} - - -/** - * Draw an inline's borders. - * - * \param box BOX_INLINE which created the border - * \param b coordinates of border edge rectangle - * \param clip cliping area for redrawing border. - * \param scale scale for redraw - * \param first true if this is the first rectangle associated with the inline - * \param last true if this is the last rectangle associated with the inline - * \param ctx current redraw context - * \return true if successful, false otherwise - */ -bool -html_redraw_inline_borders(struct box *box, - struct rect b, - const struct rect *clip, - float scale, - bool first, - bool last, - const struct redraw_context *ctx) -{ - int top = box->border[TOP].width; - int right = box->border[RIGHT].width; - int bottom = box->border[BOTTOM].width; - int left = box->border[LEFT].width; - colour col; - int p[8]; /* Box border vertices */ - int z[8]; /* Border vertices */ - bool square_end_1; - bool square_end_2; - nserror res; - - if (scale != 1.0) { - top *= scale; - right *= scale; - bottom *= scale; - left *= scale; - } - - /* Calculate border vertices - * - * A----------------------+ - * | \ / | - * | B--------------+ | - * | | | | - * | +--------------C | - * | / \ | - * +----------------------D - */ - p[0] = b.x0; p[1] = b.y0; /* A */ - p[2] = first ? b.x0 + left : b.x0; p[3] = b.y0 + top; /* B */ - p[4] = last ? b.x1 - right : b.x1; p[5] = b.y1 - bottom; /* C */ - p[6] = b.x1; p[7] = b.y1; /* D */ - - assert(box->style); - - /* Left */ - square_end_1 = (top == 0); - square_end_2 = (bottom == 0); - if (left != 0 && - first && - nscss_color_is_transparent(box->border[LEFT].c) == false) { - col = nscss_color_to_ns(box->border[LEFT].c); - - z[0] = p[0]; z[1] = p[7]; - z[2] = p[2]; z[3] = p[5]; - z[4] = p[2]; z[5] = p[3]; - z[6] = p[0]; z[7] = p[1]; - - if (nscss_color_is_transparent(box->border[TOP].c) == false && - box->border[TOP].style != CSS_BORDER_STYLE_DOUBLE) { - /* make border overhang top corner fully, - * if top border is opaque - */ - z[5] -= top; - square_end_1 = true; - } - - if (nscss_color_is_transparent(box->border[BOTTOM].c) == false && - box->border[BOTTOM].style != CSS_BORDER_STYLE_DOUBLE) { - /* make border overhang bottom corner fully, - * if bottom border is opaque - */ - z[3] += bottom; - square_end_2 = true; - } - - res = html_redraw_border_plot(LEFT, - z, - col, - box->border[LEFT].style, - left, - square_end_1 && square_end_2, - clip, - ctx); - if (res != NSERROR_OK) { - return false; - } - } - - /* Right */ - square_end_1 = (top == 0); - square_end_2 = (bottom == 0); - if (right != 0 && - last && - nscss_color_is_transparent(box->border[RIGHT].c) == false) { - col = nscss_color_to_ns(box->border[RIGHT].c); - - z[0] = p[6]; z[1] = p[1]; - z[2] = p[4]; z[3] = p[3]; - z[4] = p[4]; z[5] = p[5]; - z[6] = p[6]; z[7] = p[7]; - - if (nscss_color_is_transparent(box->border[TOP].c) == false && - box->border[TOP].style != CSS_BORDER_STYLE_DOUBLE) { - /* make border overhang top corner fully, - * if top border is opaque - */ - z[3] -= top; - square_end_1 = true; - } - - if (nscss_color_is_transparent(box->border[BOTTOM].c) == false && - box->border[BOTTOM].style != CSS_BORDER_STYLE_DOUBLE) { - /* make border overhang bottom corner fully, - * if bottom border is opaque - */ - z[5] += bottom; - square_end_2 = true; - } - - res = html_redraw_border_plot(RIGHT, - z, - col, - box->border[RIGHT].style, - right, - square_end_1 && square_end_2, - clip, - ctx); - if (res != NSERROR_OK) { - return false; - } - } - - /* Top */ - square_end_1 = (left == 0); - square_end_2 = (right == 0); - if (top != 0 && - nscss_color_is_transparent(box->border[TOP].c) == false) { - col = nscss_color_to_ns(box->border[TOP].c); - - z[0] = p[2]; z[1] = p[3]; - z[2] = p[0]; z[3] = p[1]; - z[4] = p[6]; z[5] = p[1]; - z[6] = p[4]; z[7] = p[3]; - - if (first && - box->border[TOP].style == CSS_BORDER_STYLE_SOLID && - box->border[TOP].c == box->border[LEFT].c) { - /* don't bother overlapping left corner if - * it's the same colour anyway - */ - z[2] += left; - square_end_1 = true; - } - - if (last && - box->border[TOP].style == CSS_BORDER_STYLE_SOLID && - box->border[TOP].c == box->border[RIGHT].c) { - /* don't bother overlapping right corner if - * it's the same colour anyway - */ - z[4] -= right; - square_end_2 = true; - } - - res = html_redraw_border_plot(TOP, - z, - col, - box->border[TOP].style, - top, - square_end_1 && square_end_2, - clip, - ctx); - if (res != NSERROR_OK) { - return false; - } - } - - /* Bottom */ - square_end_1 = (left == 0); - square_end_2 = (right == 0); - if (bottom != 0 && - nscss_color_is_transparent(box->border[BOTTOM].c) == false) { - col = nscss_color_to_ns(box->border[BOTTOM].c); - - z[0] = p[4]; z[1] = p[5]; - z[2] = p[6]; z[3] = p[7]; - z[4] = p[0]; z[5] = p[7]; - z[6] = p[2]; z[7] = p[5]; - - if (first && - box->border[BOTTOM].style == CSS_BORDER_STYLE_SOLID && - box->border[BOTTOM].c == box->border[LEFT].c) { - /* don't bother overlapping left corner if - * it's the same colour anyway - */ - z[4] += left; - square_end_1 = true; - } - - if (last && - box->border[BOTTOM].style == CSS_BORDER_STYLE_SOLID && - box->border[BOTTOM].c == box->border[RIGHT].c) { - /* don't bother overlapping right corner if - * it's the same colour anyway - */ - z[2] -= right; - square_end_2 = true; - } - - res = html_redraw_border_plot(BOTTOM, - z, - col, - box->border[BOTTOM].style, - bottom, - square_end_1 && square_end_2, - clip, - ctx); - if (res != NSERROR_OK) { - return false; - } - } - - return true; -} diff --git a/render/html_script.c b/render/html_script.c deleted file mode 100644 index c73a4806d..000000000 --- a/render/html_script.c +++ /dev/null @@ -1,603 +0,0 @@ -/* - * Copyright 2012 Vincent Sanders <vince@netsurf-browser.org> - * - * 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 <http://www.gnu.org/licenses/>. - */ - -/** \file - * Content for text/html scripts (implementation). - */ - -#include <assert.h> -#include <ctype.h> -#include <stdint.h> -#include <stdbool.h> -#include <string.h> -#include <strings.h> -#include <stdlib.h> - -#include "utils/config.h" -#include "utils/corestrings.h" -#include "utils/log.h" -#include "utils/messages.h" -#include "netsurf/content.h" -#include "javascript/js.h" -#include "content/content_protected.h" -#include "content/fetch.h" -#include "content/hlcache.h" - -#include "render/html_internal.h" - -typedef bool (script_handler_t)(struct jscontext *jscontext, const char *data, size_t size) ; - - -static script_handler_t *select_script_handler(content_type ctype) -{ - if (ctype == CONTENT_JS) { - return js_exec; - } - return NULL; -} - - -/* exported internal interface documented in render/html_internal.h */ -nserror html_script_exec(html_content *c) -{ - unsigned int i; - struct html_script *s; - script_handler_t *script_handler; - - if (c->jscontext == NULL) { - return NSERROR_BAD_PARAMETER; - } - - for (i = 0, s = c->scripts; i != c->scripts_count; i++, s++) { - if (s->already_started) { - continue; - } - - if ((s->type == HTML_SCRIPT_ASYNC) || - (s->type == HTML_SCRIPT_DEFER)) { - /* ensure script content is present */ - if (s->data.handle == NULL) - continue; - - /* ensure script content fetch status is not an error */ - if (content_get_status(s->data.handle) == - CONTENT_STATUS_ERROR) - continue; - - /* ensure script handler for content type */ - script_handler = select_script_handler( - content_get_type(s->data.handle)); - if (script_handler == NULL) - continue; /* unsupported type */ - - if (content_get_status(s->data.handle) == - CONTENT_STATUS_DONE) { - /* external script is now available */ - const char *data; - unsigned long size; - data = content_get_source_data( - s->data.handle, &size ); - script_handler(c->jscontext, data, size); - - s->already_started = true; - - } - } - } - - return NSERROR_OK; -} - -/* create new html script entry */ -static struct html_script * -html_process_new_script(html_content *c, - dom_string *mimetype, - enum html_script_type type) -{ - struct html_script *nscript; - /* add space for new script entry */ - nscript = realloc(c->scripts, - sizeof(struct html_script) * (c->scripts_count + 1)); - if (nscript == NULL) { - return NULL; - } - - c->scripts = nscript; - - /* increment script entry count */ - nscript = &c->scripts[c->scripts_count]; - c->scripts_count++; - - nscript->already_started = false; - nscript->parser_inserted = false; - nscript->force_async = true; - nscript->ready_exec = false; - nscript->async = false; - nscript->defer = false; - - nscript->type = type; - - nscript->mimetype = dom_string_ref(mimetype); /* reference mimetype */ - - return nscript; -} - -/** - * Callback for asyncronous scripts - */ -static nserror -convert_script_async_cb(hlcache_handle *script, - const hlcache_event *event, - void *pw) -{ - html_content *parent = pw; - unsigned int i; - struct html_script *s; - - /* Find script */ - for (i = 0, s = parent->scripts; i != parent->scripts_count; i++, s++) { - if (s->type == HTML_SCRIPT_ASYNC && s->data.handle == script) - break; - } - - assert(i != parent->scripts_count); - - switch (event->type) { - case CONTENT_MSG_LOADING: - break; - - case CONTENT_MSG_READY: - break; - - case CONTENT_MSG_DONE: - NSLOG(netsurf, INFO, "script %d done '%s'", i, - nsurl_access(hlcache_handle_get_url(script))); - parent->base.active--; - NSLOG(netsurf, INFO, "%d fetches active", parent->base.active); - - break; - - case CONTENT_MSG_ERROR: - NSLOG(netsurf, INFO, "script %s failed: %s", - nsurl_access(hlcache_handle_get_url(script)), - event->data.error); - /* fall through */ - - case CONTENT_MSG_ERRORCODE: - hlcache_handle_release(script); - s->data.handle = NULL; - parent->base.active--; - NSLOG(netsurf, INFO, "%d fetches active", parent->base.active); - content_add_error(&parent->base, "?", 0); - - break; - - default: - break; - } - - /* if there are no active fetches remaining begin post parse - * conversion - */ - if (html_can_begin_conversion(parent)) { - html_begin_conversion(parent); - } - - return NSERROR_OK; -} - -/** - * Callback for defer scripts - */ -static nserror -convert_script_defer_cb(hlcache_handle *script, - const hlcache_event *event, - void *pw) -{ - html_content *parent = pw; - unsigned int i; - struct html_script *s; - - /* Find script */ - for (i = 0, s = parent->scripts; i != parent->scripts_count; i++, s++) { - if (s->type == HTML_SCRIPT_DEFER && s->data.handle == script) - break; - } - - assert(i != parent->scripts_count); - - switch (event->type) { - - case CONTENT_MSG_DONE: - NSLOG(netsurf, INFO, "script %d done '%s'", i, - nsurl_access(hlcache_handle_get_url(script))); - parent->base.active--; - NSLOG(netsurf, INFO, "%d fetches active", parent->base.active); - - break; - - case CONTENT_MSG_ERROR: - NSLOG(netsurf, INFO, "script %s failed: %s", - nsurl_access(hlcache_handle_get_url(script)), - event->data.error); - /* fall through */ - - case CONTENT_MSG_ERRORCODE: - hlcache_handle_release(script); - s->data.handle = NULL; - parent->base.active--; - NSLOG(netsurf, INFO, "%d fetches active", parent->base.active); - content_add_error(&parent->base, "?", 0); - - break; - - default: - break; - } - - /* if there are no active fetches remaining begin post parse - * conversion - */ - if (html_can_begin_conversion(parent)) { - html_begin_conversion(parent); - } - - return NSERROR_OK; -} - -/** - * Callback for syncronous scripts - */ -static nserror -convert_script_sync_cb(hlcache_handle *script, - const hlcache_event *event, - void *pw) -{ - html_content *parent = pw; - unsigned int i; - struct html_script *s; - script_handler_t *script_handler; - dom_hubbub_error err; - - /* Find script */ - for (i = 0, s = parent->scripts; i != parent->scripts_count; i++, s++) { - if (s->type == HTML_SCRIPT_SYNC && s->data.handle == script) - break; - } - - assert(i != parent->scripts_count); - - switch (event->type) { - case CONTENT_MSG_DONE: - NSLOG(netsurf, INFO, "script %d done '%s'", i, - nsurl_access(hlcache_handle_get_url(script))); - parent->base.active--; - NSLOG(netsurf, INFO, "%d fetches active", parent->base.active); - - s->already_started = true; - - /* attempt to execute script */ - script_handler = select_script_handler(content_get_type(s->data.handle)); - if (script_handler != NULL && parent->jscontext != NULL) { - /* script has a handler */ - const char *data; - unsigned long size; - data = content_get_source_data(s->data.handle, &size ); - script_handler(parent->jscontext, data, size); - } - - /* continue parse */ - err = dom_hubbub_parser_pause(parent->parser, false); - if (err != DOM_HUBBUB_OK) { - NSLOG(netsurf, INFO, "unpause returned 0x%x", err); - } - - break; - - case CONTENT_MSG_ERROR: - NSLOG(netsurf, INFO, "script %s failed: %s", - nsurl_access(hlcache_handle_get_url(script)), - event->data.error); - /* fall through */ - - case CONTENT_MSG_ERRORCODE: - hlcache_handle_release(script); - s->data.handle = NULL; - parent->base.active--; - - NSLOG(netsurf, INFO, "%d fetches active", parent->base.active); - content_add_error(&parent->base, "?", 0); - - s->already_started = true; - - /* continue parse */ - err = dom_hubbub_parser_pause(parent->parser, false); - if (err != DOM_HUBBUB_OK) { - NSLOG(netsurf, INFO, "unpause returned 0x%x", err); - } - - break; - - default: - break; - } - - /* if there are no active fetches remaining begin post parse - * conversion - */ - if (html_can_begin_conversion(parent)) { - html_begin_conversion(parent); - } - - return NSERROR_OK; -} - -/** - * process a script with a src tag - */ -static dom_hubbub_error -exec_src_script(html_content *c, - dom_node *node, - dom_string *mimetype, - dom_string *src) -{ - nserror ns_error; - nsurl *joined; - hlcache_child_context child; - struct html_script *nscript; - bool async; - bool defer; - enum html_script_type script_type; - hlcache_handle_callback script_cb; - dom_hubbub_error ret = DOM_HUBBUB_OK; - dom_exception exc; /* returned by libdom functions */ - - /* src url */ - ns_error = nsurl_join(c->base_url, dom_string_data(src), &joined); - if (ns_error != NSERROR_OK) { - content_broadcast_errorcode(&c->base, NSERROR_NOMEM); - return DOM_HUBBUB_NOMEM; - } - - NSLOG(netsurf, INFO, "script %i '%s'", c->scripts_count, - nsurl_access(joined)); - - /* there are three ways to process the script tag at this point: - * - * Syncronously pause the parent parse and continue after - * the script has downloaded and executed. (default) - * Async Start the script downloading and execute it when it - * becomes available. - * Defered Start the script downloading and execute it when - * the page has completed parsing, may be set along - * with async where it is ignored. - */ - - /* we interpret the presence of the async and defer attribute - * as true and ignore its value, technically only the empty - * value or the attribute name itself are valid. However - * various browsers interpret this in various ways the most - * compatible approach is to be liberal and accept any - * value. Note setting the values to "false" still makes them true! - */ - exc = dom_element_has_attribute(node, corestring_dom_async, &async); - if (exc != DOM_NO_ERR) { - return DOM_HUBBUB_OK; /* dom error */ - } - - if (async) { - /* asyncronous script */ - script_type = HTML_SCRIPT_ASYNC; - script_cb = convert_script_async_cb; - - } else { - exc = dom_element_has_attribute(node, - corestring_dom_defer, &defer); - if (exc != DOM_NO_ERR) { - return DOM_HUBBUB_OK; /* dom error */ - } - - if (defer) { - /* defered script */ - script_type = HTML_SCRIPT_DEFER; - script_cb = convert_script_defer_cb; - } else { - /* syncronous script */ - script_type = HTML_SCRIPT_SYNC; - script_cb = convert_script_sync_cb; - } - } - - nscript = html_process_new_script(c, mimetype, script_type); - if (nscript == NULL) { - nsurl_unref(joined); - content_broadcast_errorcode(&c->base, NSERROR_NOMEM); - return DOM_HUBBUB_NOMEM; - } - - /* set up child fetch encoding and quirks */ - child.charset = c->encoding; - child.quirks = c->base.quirks; - - ns_error = hlcache_handle_retrieve(joined, - 0, - content_get_url(&c->base), - NULL, - script_cb, - c, - &child, - CONTENT_SCRIPT, - &nscript->data.handle); - - - nsurl_unref(joined); - - if (ns_error != NSERROR_OK) { - /* @todo Deal with fetch error better. currently assume - * fetch never became active - */ - /* mark duff script fetch as already started */ - nscript->already_started = true; - NSLOG(netsurf, INFO, "Fetch failed with error %d", ns_error); - } else { - /* update base content active fetch count */ - c->base.active++; - NSLOG(netsurf, INFO, "%d fetches active", c->base.active); - - switch (script_type) { - case HTML_SCRIPT_SYNC: - ret = DOM_HUBBUB_HUBBUB_ERR | HUBBUB_PAUSED; - - case HTML_SCRIPT_ASYNC: - break; - - case HTML_SCRIPT_DEFER: - break; - - default: - assert(0); - } - } - - return ret; -} - -static dom_hubbub_error -exec_inline_script(html_content *c, dom_node *node, dom_string *mimetype) -{ - dom_string *script; - dom_exception exc; /* returned by libdom functions */ - struct lwc_string_s *lwcmimetype; - script_handler_t *script_handler; - struct html_script *nscript; - - /* does not appear to be a src so script is inline content */ - exc = dom_node_get_text_content(node, &script); - if ((exc != DOM_NO_ERR) || (script == NULL)) { - return DOM_HUBBUB_OK; /* no contents, skip */ - } - - nscript = html_process_new_script(c, mimetype, HTML_SCRIPT_INLINE); - if (nscript == NULL) { - dom_string_unref(script); - - content_broadcast_errorcode(&c->base, NSERROR_NOMEM); - return DOM_HUBBUB_NOMEM; - - } - - nscript->data.string = script; - nscript->already_started = true; - - /* ensure script handler for content type */ - dom_string_intern(mimetype, &lwcmimetype); - script_handler = select_script_handler(content_factory_type_from_mime_type(lwcmimetype)); - lwc_string_unref(lwcmimetype); - - if (script_handler != NULL) { - script_handler(c->jscontext, - dom_string_data(script), - dom_string_byte_length(script)); - } - return DOM_HUBBUB_OK; -} - - -/** - * process script node parser callback - * - * - */ -dom_hubbub_error -html_process_script(void *ctx, dom_node *node) -{ - html_content *c = (html_content *)ctx; - dom_exception exc; /* returned by libdom functions */ - dom_string *src, *mimetype; - dom_hubbub_error err = DOM_HUBBUB_OK; - - /* ensure javascript context is available */ - /* We should only ever be here if scripting was enabled for this - * content so it's correct to make a javascript context if there - * isn't one already. */ - if (c->jscontext == NULL) { - union content_msg_data msg_data; - - msg_data.jscontext = &c->jscontext; - content_broadcast(&c->base, CONTENT_MSG_GETCTX, &msg_data); - NSLOG(netsurf, INFO, "javascript context %p ", c->jscontext); - if (c->jscontext == NULL) { - /* no context and it could not be created, abort */ - return DOM_HUBBUB_OK; - } - } - - NSLOG(netsurf, INFO, "content %p parser %p node %p", c, c->parser, - node); - - exc = dom_element_get_attribute(node, corestring_dom_type, &mimetype); - if (exc != DOM_NO_ERR || mimetype == NULL) { - mimetype = dom_string_ref(corestring_dom_text_javascript); - } - - exc = dom_element_get_attribute(node, corestring_dom_src, &src); - if (exc != DOM_NO_ERR || src == NULL) { - err = exec_inline_script(c, node, mimetype); - } else { - err = exec_src_script(c, node, mimetype, src); - dom_string_unref(src); - } - - dom_string_unref(mimetype); - - return err; -} - -/* exported internal interface documented in render/html_internal.h */ -nserror html_script_free(html_content *html) -{ - unsigned int i; - - for (i = 0; i != html->scripts_count; i++) { - if (html->scripts[i].mimetype != NULL) { - dom_string_unref(html->scripts[i].mimetype); - } - - if ((html->scripts[i].type == HTML_SCRIPT_INLINE) && - (html->scripts[i].data.string != NULL)) { - - dom_string_unref(html->scripts[i].data.string); - - } else if ((html->scripts[i].type == HTML_SCRIPT_SYNC) && - (html->scripts[i].data.handle != NULL)) { - - hlcache_handle_release(html->scripts[i].data.handle); - - } - } - free(html->scripts); - - return NSERROR_OK; -} - -/* exported internal interface documented in render/html_internal.h */ -nserror html_script_invalidate_ctx(html_content *htmlc) -{ - htmlc->jscontext = NULL; - return NSERROR_OK; -} diff --git a/render/imagemap.c b/render/imagemap.c deleted file mode 100644 index 0d3b42a1b..000000000 --- a/render/imagemap.c +++ /dev/null @@ -1,801 +0,0 @@ -/* - * Copyright 2004 John M Bell <jmb202@ecs.soton.ac.uk> - * - * 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 <http://www.gnu.org/licenses/>. - */ - -/* - * Much of this shamelessly copied from utils/messages.c - */ - -#include <assert.h> -#include <stdbool.h> -#include <string.h> -#include <strings.h> - -#include <dom/dom.h> - -#include "utils/log.h" -#include "utils/corestrings.h" -#include "content/content_protected.h" -#include "content/hlcache.h" - -#include "render/box.h" -#include "render/html_internal.h" -#include "render/imagemap.h" - -#define HASH_SIZE 31 /* fixed size hash table */ - -typedef enum { - IMAGEMAP_DEFAULT, - IMAGEMAP_RECT, - IMAGEMAP_CIRCLE, - IMAGEMAP_POLY -} imagemap_entry_type; - -struct mapentry { - imagemap_entry_type type; /**< type of shape */ - nsurl *url; /**< absolute url to go to */ - char *target; /**< target frame (if any) */ - union { - struct { - int x; /**< x coordinate of centre */ - int y; /**< y coordinate of center */ - int r; /**< radius of circle */ - } circle; - struct { - int x0; /**< left hand edge */ - int y0; /**< top edge */ - int x1; /**< right hand edge */ - int y1; /**< bottom edge */ - } rect; - struct { - int num; /**< number of points */ - float *xcoords; /**< x coordinates */ - float *ycoords; /**< y coordinates */ - } poly; - } bounds; - struct mapentry *next; /**< next entry in list */ -}; - -struct imagemap { - char *key; /**< key for this entry */ - struct mapentry *list; /**< pointer to linked list of entries */ - struct imagemap *next; /**< next entry in this hash chain */ -}; - -/** - * Create hashtable of imagemaps - * - * \param c The containing content - * \return true on success, false otherwise - */ -static bool imagemap_create(html_content *c) -{ - assert(c != NULL); - - if (c->imagemaps == NULL) { - c->imagemaps = calloc(HASH_SIZE, sizeof(struct imagemap *)); - if (c->imagemaps == NULL) { - return false; - } - } - - return true; -} - -/** - * Hash function. - * - * \param key The key to hash. - * \return The hashed value. - */ -static unsigned int imagemap_hash(const char *key) -{ - unsigned int z = 0; - - if (key == 0) return 0; - - for (; *key != 0; key++) { - z += *key & 0x1f; - } - - return (z % (HASH_SIZE - 1)) + 1; -} - -/** - * Add an imagemap to the hashtable, creating it if it doesn't exist - * - * \param c The containing content - * \param key The name of the imagemap - * \param list List of map regions - * \return true on succes, false otherwise - */ -static bool -imagemap_add(html_content *c, dom_string *key, struct mapentry *list) -{ - struct imagemap *map; - unsigned int slot; - - assert(c != NULL); - assert(key != NULL); - assert(list != NULL); - - if (imagemap_create(c) == false) - return false; - - map = calloc(1, sizeof(*map)); - if (map == NULL) - return false; - - /* \todo Stop relying on NULL termination of dom_string */ - map->key = strdup(dom_string_data(key)); - if (map->key == NULL) { - free(map); - return false; - } - - map->list = list; - - slot = imagemap_hash(map->key); - - map->next = c->imagemaps[slot]; - c->imagemaps[slot] = map; - - return true; -} - -/** - * Free list of imagemap entries - * - * \param list Pointer to head of list - */ -static void imagemap_freelist(struct mapentry *list) -{ - struct mapentry *entry, *prev; - - assert(list != NULL); - - entry = list; - - while (entry != NULL) { - prev = entry; - - nsurl_unref(entry->url); - - if (entry->target) - free(entry->target); - - if (entry->type == IMAGEMAP_POLY) { - free(entry->bounds.poly.xcoords); - free(entry->bounds.poly.ycoords); - } - - entry = entry->next; - free(prev); - } -} - -/** - * Destroy hashtable of imagemaps - * - * \param c The containing content - */ -void imagemap_destroy(html_content *c) -{ - unsigned int i; - - assert(c != NULL); - - /* no imagemaps -> return */ - if (c->imagemaps == NULL) - return; - - for (i = 0; i != HASH_SIZE; i++) { - struct imagemap *map, *next; - - map = c->imagemaps[i]; - while (map != NULL) { - next = map->next; - imagemap_freelist(map->list); - free(map->key); - free(map); - map = next; - } - } - - free(c->imagemaps); -} - -/** - * Dump imagemap data to the log - * - * \param c The containing content - */ -void imagemap_dump(html_content *c) -{ - unsigned int i; - - int j; - - assert(c != NULL); - - if (c->imagemaps == NULL) - return; - - for (i = 0; i != HASH_SIZE; i++) { - struct imagemap *map; - struct mapentry *entry; - - map = c->imagemaps[i]; - while (map != NULL) { - NSLOG(netsurf, INFO, "Imagemap: %s", map->key); - - for (entry = map->list; entry; entry = entry->next) { - switch (entry->type) { - case IMAGEMAP_DEFAULT: - NSLOG(netsurf, INFO, "\tDefault: %s", - nsurl_access(entry->url)); - break; - case IMAGEMAP_RECT: - NSLOG(netsurf, INFO, - "\tRectangle: %s: [(%d,%d),(%d,%d)]", - nsurl_access(entry->url), - entry->bounds.rect.x0, - entry->bounds.rect.y0, - entry->bounds.rect.x1, - entry->bounds.rect.y1); - break; - case IMAGEMAP_CIRCLE: - NSLOG(netsurf, INFO, - "\tCircle: %s: [(%d,%d),%d]", - nsurl_access(entry->url), - entry->bounds.circle.x, - entry->bounds.circle.y, - entry->bounds.circle.r); - break; - case IMAGEMAP_POLY: - NSLOG(netsurf, INFO, - "\tPolygon: %s:", - nsurl_access(entry->url)); - for (j = 0; j != entry->bounds.poly.num; - j++) { - fprintf(stderr, "(%d,%d) ", - (int)entry->bounds.poly.xcoords[j], - (int)entry->bounds.poly.ycoords[j]); - } - fprintf(stderr,"\n"); - break; - } - } - map = map->next; - } - } -} - -/** - * Adds an imagemap entry to the list - * - * \param c The html content that the imagemap belongs to - * \param n The xmlNode representing the entry to add - * \param base_url Base URL for resolving relative URLs - * \param entry Pointer to list of entries - * \param tagtype The type of tag - * \return false on memory exhaustion, true otherwise - */ -static bool -imagemap_addtolist(const struct html_content *c, dom_node *n, nsurl *base_url, - struct mapentry **entry, dom_string *tagtype) -{ - dom_exception exc; - dom_string *href = NULL, *target = NULL, *shape = NULL; - dom_string *coords = NULL; - struct mapentry *new_map, *temp; - bool ret = true; - - if (dom_string_caseless_isequal(tagtype, corestring_dom_area)) { - bool nohref = false; - exc = dom_element_has_attribute(n, - corestring_dom_nohref, &nohref); - if ((exc != DOM_NO_ERR) || nohref) - /* Skip <area nohref="anything" /> */ - goto ok_out; - } - - exc = dom_element_get_attribute(n, corestring_dom_href, &href); - if (exc != DOM_NO_ERR || href == NULL) { - /* No href="" attribute, skip this element */ - goto ok_out; - } - - exc = dom_element_get_attribute(n, corestring_dom_target, &target); - if (exc != DOM_NO_ERR) { - goto ok_out; - } - - exc = dom_element_get_attribute(n, corestring_dom_shape, &shape); - if (exc != DOM_NO_ERR) { - goto ok_out; - } - - /* If there's no shape, we default to rectangles */ - if (shape == NULL) - shape = dom_string_ref(corestring_dom_rect); - - if (!dom_string_caseless_lwc_isequal(shape, corestring_lwc_default)) { - /* If not 'default' and there's no 'coords' give up */ - exc = dom_element_get_attribute(n, corestring_dom_coords, - &coords); - if (exc != DOM_NO_ERR || coords == NULL) { - goto ok_out; - } - } - - new_map = calloc(1, sizeof(*new_map)); - if (new_map == NULL) { - goto bad_out; - } - - if (dom_string_caseless_lwc_isequal(shape, corestring_lwc_rect) || - dom_string_caseless_lwc_isequal(shape, corestring_lwc_rectangle)) - new_map->type = IMAGEMAP_RECT; - else if (dom_string_caseless_lwc_isequal(shape, corestring_lwc_circle)) - new_map->type = IMAGEMAP_CIRCLE; - else if (dom_string_caseless_lwc_isequal(shape, corestring_lwc_poly) || - dom_string_caseless_lwc_isequal(shape, corestring_lwc_polygon)) - new_map->type = IMAGEMAP_POLY; - else if (dom_string_caseless_lwc_isequal(shape, corestring_lwc_default)) - new_map->type = IMAGEMAP_DEFAULT; - else - goto bad_out; - - if (box_extract_link(c, href, base_url, &new_map->url) == false) - goto bad_out; - - if (new_map->url == NULL) { - /* non-fatal error -> ignore this */ - goto ok_free_map_out; - } - - if (target != NULL) { - /* Copy target into the map */ - new_map->target = malloc(dom_string_byte_length(target) + 1); - if (new_map->target == NULL) - goto bad_out; - /* Safe, but relies on dom_strings being NULL terminated */ - /* \todo Do this better */ - strcpy(new_map->target, dom_string_data(target)); - } - - if (new_map->type != IMAGEMAP_DEFAULT) { - int x, y; - float *xcoords, *ycoords; - /* coordinates are a comma-separated list of values */ - char *val = strtok((char *)dom_string_data(coords), ","); - int num = 1; - - switch (new_map->type) { - case IMAGEMAP_RECT: - /* (left, top, right, bottom) */ - while (val != NULL && num <= 4) { - switch (num) { - case 1: - new_map->bounds.rect.x0 = atoi(val); - break; - case 2: - new_map->bounds.rect.y0 = atoi(val); - break; - case 3: - new_map->bounds.rect.x1 = atoi(val); - break; - case 4: - new_map->bounds.rect.y1 = atoi(val); - break; - } - - num++; - val = strtok(NULL, ","); - } - break; - case IMAGEMAP_CIRCLE: - /* (x, y, radius ) */ - while (val != NULL && num <= 3) { - switch (num) { - case 1: - new_map->bounds.circle.x = atoi(val); - break; - case 2: - new_map->bounds.circle.y = atoi(val); - break; - case 3: - new_map->bounds.circle.r = atoi(val); - break; - } - - num++; - val = strtok(NULL, ","); - } - break; - case IMAGEMAP_POLY: - new_map->bounds.poly.xcoords = NULL; - new_map->bounds.poly.ycoords = NULL; - - while (val != NULL) { - x = atoi(val); - - val = strtok(NULL, ","); - if (val == NULL) - break; - - y = atoi(val); - - xcoords = realloc(new_map->bounds.poly.xcoords, - num * sizeof(float)); - if (xcoords == NULL) { - goto bad_out; - } - new_map->bounds.poly.xcoords = xcoords; - - ycoords = realloc(new_map->bounds.poly.ycoords, - num * sizeof(float)); - if (ycoords == NULL) { - goto bad_out; - } - new_map->bounds.poly.ycoords = ycoords; - - new_map->bounds.poly.xcoords[num - 1] = x; - new_map->bounds.poly.ycoords[num - 1] = y; - - num++; - val = strtok(NULL, ","); - } - - new_map->bounds.poly.num = num - 1; - - break; - default: - break; - } - } - - new_map->next = NULL; - - if (*entry) { - /* add to END of list */ - for (temp = (*entry); temp->next != NULL; temp = temp->next) - ; - temp->next = new_map; - } else { - (*entry) = new_map; - } - - /* All good, linked in, let's clean up */ - goto ok_out; - -bad_out: - ret = false; -ok_free_map_out: - if (new_map != NULL) { - if (new_map->url != NULL) - nsurl_unref(new_map->url); - if (new_map->type == IMAGEMAP_POLY && - new_map->bounds.poly.ycoords != NULL) - free(new_map->bounds.poly.ycoords); - if (new_map->type == IMAGEMAP_POLY && - new_map->bounds.poly.xcoords != NULL) - free(new_map->bounds.poly.xcoords); - if (new_map->target != NULL) - free(new_map->target); - - free(new_map); - } -ok_out: - if (href != NULL) - dom_string_unref(href); - if (target != NULL) - dom_string_unref(target); - if (shape != NULL) - dom_string_unref(shape); - if (coords != NULL) - dom_string_unref(coords); - - return ret; -} - -/** - * Extract an imagemap from html source - * - * \param node XML node containing map - * \param c Content containing document - * \param entry List of map entries - * \param tname The sub-tags to consider on this pass - * \return false on memory exhaustion, true otherwise - */ -static bool -imagemap_extract_map_entries(dom_node *node, html_content *c, - struct mapentry **entry, dom_string *tname) -{ - dom_nodelist *nlist; - dom_exception exc; - unsigned long ent; - uint32_t tag_count; - - exc = dom_element_get_elements_by_tag_name(node, tname, &nlist); - if (exc != DOM_NO_ERR) { - return false; - } - - exc = dom_nodelist_get_length(nlist, &tag_count); - if (exc != DOM_NO_ERR) { - dom_nodelist_unref(nlist); - return false; - } - - for (ent = 0; ent < tag_count; ++ent) { - dom_node *subnode; - - exc = dom_nodelist_item(nlist, ent, &subnode); - if (exc != DOM_NO_ERR) { - dom_nodelist_unref(nlist); - return false; - } - if (imagemap_addtolist(c, subnode, c->base_url, - entry, tname) == false) { - dom_node_unref(subnode); - dom_nodelist_unref(nlist); - return false; - } - dom_node_unref(subnode); - } - - dom_nodelist_unref(nlist); - - return true; -} - -/** - * Extract an imagemap from html source - * - * \param node XML node containing map - * \param c Content containing document - * \param entry List of map entries - * \return false on memory exhaustion, true otherwise - */ -static bool imagemap_extract_map(dom_node *node, html_content *c, - struct mapentry **entry) -{ - if (imagemap_extract_map_entries(node, c, entry, - corestring_dom_area) == false) - return false; - return imagemap_extract_map_entries(node, c, entry, - corestring_dom_a); -} - -/** - * Extract all imagemaps from a document tree - * - * \param c The content to extract imagemaps from. - * \return false on memory exhaustion, true otherwise - */ -nserror -imagemap_extract(html_content *c) -{ - dom_nodelist *nlist; - dom_exception exc; - unsigned long mapnr; - uint32_t maybe_maps; - nserror ret = NSERROR_OK; - - exc = dom_document_get_elements_by_tag_name(c->document, - corestring_dom_map, - &nlist); - if (exc != DOM_NO_ERR) { - return NSERROR_DOM; - } - - exc = dom_nodelist_get_length(nlist, &maybe_maps); - if (exc != DOM_NO_ERR) { - ret = NSERROR_DOM; - goto out_nlist; - } - - for (mapnr = 0; mapnr < maybe_maps; ++mapnr) { - dom_node *node; - dom_string *name; - exc = dom_nodelist_item(nlist, mapnr, &node); - if (exc != DOM_NO_ERR) { - ret = NSERROR_DOM; - goto out_nlist; - } - - exc = dom_element_get_attribute(node, corestring_dom_id, - &name); - if (exc != DOM_NO_ERR) { - dom_node_unref(node); - ret = NSERROR_DOM; - goto out_nlist; - } - - if (name == NULL) { - exc = dom_element_get_attribute(node, - corestring_dom_name, - &name); - if (exc != DOM_NO_ERR) { - dom_node_unref(node); - ret = NSERROR_DOM; - goto out_nlist; - } - } - - if (name != NULL) { - struct mapentry *entry = NULL; - if (imagemap_extract_map(node, c, &entry) == false) { - if (entry != NULL) { - imagemap_freelist(entry); - } - - dom_string_unref(name); - dom_node_unref(node); - ret = NSERROR_NOMEM; /** @todo check this */ - goto out_nlist; - } - - /* imagemap_extract_map may not extract anything, - * so entry can still be NULL here. This isn't an - * error as it just means that we've encountered - * an incorrectly defined <map>...</map> block - */ - if ((entry != NULL) && - (imagemap_add(c, name, entry) == false)) { - imagemap_freelist(entry); - - dom_string_unref(name); - dom_node_unref(node); - ret = NSERROR_NOMEM; /** @todo check this */ - goto out_nlist; - } - } - - dom_string_unref(name); - dom_node_unref(node); - } - -out_nlist: - - dom_nodelist_unref(nlist); - - return ret; -} - -/** - * Test if a point lies within an arbitrary polygon - * Modified from comp.graphics.algorithms FAQ 2.03 - * - * \param num Number of vertices - * \param xpt Array of x coordinates - * \param ypt Array of y coordinates - * \param x Left hand edge of containing box - * \param y Top edge of containing box - * \param click_x X coordinate of click - * \param click_y Y coordinate of click - * \return 1 if point is in polygon, 0 if outside. 0 or 1 if on boundary - */ -static int -imagemap_point_in_poly(int num, float *xpt, float *ypt, unsigned long x, - unsigned long y, unsigned long click_x, unsigned long click_y) -{ - int i, j, c = 0; - - assert(xpt != NULL); - assert(ypt != NULL); - - for (i = 0, j = num - 1; i < num; j = i++) { - if ((((ypt[i] + y <= click_y) && (click_y < ypt[j] + y)) || - ((ypt[j] + y <= click_y) && (click_y < ypt[i] + y))) && - (click_x < (xpt[j] - xpt[i]) * - (click_y - (ypt[i] + y)) / (ypt[j] - ypt[i]) + xpt[i] + x)) - c = !c; - } - - return c; -} - -/** - * Retrieve url associated with imagemap entry - * - * \param c The containing content - * \param key The map name to search for - * \param x The left edge of the containing box - * \param y The top edge of the containing box - * \param click_x The horizontal location of the click - * \param click_y The vertical location of the click - * \param target Pointer to location to receive target pointer (if any) - * \return The url associated with this area, or NULL if not found - */ -nsurl *imagemap_get(struct html_content *c, const char *key, - unsigned long x, unsigned long y, - unsigned long click_x, unsigned long click_y, - const char **target) -{ - unsigned int slot = 0; - struct imagemap *map; - struct mapentry *entry; - unsigned long cx, cy; - - assert(c != NULL); - - if (key == NULL) - return NULL; - - if (c->imagemaps == NULL) - return NULL; - - slot = imagemap_hash(key); - - for (map = c->imagemaps[slot]; map != NULL; map = map->next) { - if (map->key != NULL && strcasecmp(map->key, key) == 0) - break; - } - - if (map == NULL || map->list == NULL) - return NULL; - - for (entry = map->list; entry; entry = entry->next) { - switch (entry->type) { - case IMAGEMAP_DEFAULT: - /* just return the URL. no checks required */ - if (target) - *target = entry->target; - return entry->url; - break; - case IMAGEMAP_RECT: - if (click_x >= x + entry->bounds.rect.x0 && - click_x <= x + entry->bounds.rect.x1 && - click_y >= y + entry->bounds.rect.y0 && - click_y <= y + entry->bounds.rect.y1) { - if (target) - *target = entry->target; - return entry->url; - } - break; - case IMAGEMAP_CIRCLE: - cx = x + entry->bounds.circle.x - click_x; - cy = y + entry->bounds.circle.y - click_y; - if ((cx * cx + cy * cy) <= - (unsigned long) (entry->bounds.circle.r * - entry->bounds.circle.r)) { - if (target) - *target = entry->target; - return entry->url; - } - break; - case IMAGEMAP_POLY: - if (imagemap_point_in_poly(entry->bounds.poly.num, - entry->bounds.poly.xcoords, - entry->bounds.poly.ycoords, x, y, - click_x, click_y)) { - if (target) - *target = entry->target; - return entry->url; - } - break; - } - } - - if (target) - *target = NULL; - - return NULL; -} diff --git a/render/imagemap.h b/render/imagemap.h deleted file mode 100644 index 3ae6819da..000000000 --- a/render/imagemap.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2004 John M Bell <jmb202@ecs.soton.ac.uk> - * - * 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 <http://www.gnu.org/licenses/>. - */ - -#ifndef _NETSURF_RENDER_IMAGEMAP_H_ -#define _NETSURF_RENDER_IMAGEMAP_H_ - -#include <dom/dom.h> - -struct html_content; -struct hlcache_handle; -struct nsurl; - -void imagemap_destroy(struct html_content *c); -void imagemap_dump(struct html_content *c); -nserror imagemap_extract(struct html_content *c); - -struct nsurl *imagemap_get(struct html_content *c, const char *key, - unsigned long x, unsigned long y, - unsigned long click_x, unsigned long click_y, - const char **target); - -#endif diff --git a/render/layout.c b/render/layout.c deleted file mode 100644 index 121137adc..000000000 --- a/render/layout.c +++ /dev/null @@ -1,5432 +0,0 @@ -/* - * Copyright 2005 Richard Wilson <info@tinct.net> - * Copyright 2006 James Bursa <bursa@users.sourceforge.net> - * Copyright 2008 Michael Drake <tlsa@netsurf-browser.org> - * Copyright 2003 Phil Mellor <monkeyson@users.sourceforge.net> - * - * 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 <http://www.gnu.org/licenses/>. - */ - -/** - * \file - * HTML layout implementation. - * - * Layout is carried out in two stages: - * - * 1. + calculation of minimum / maximum box widths, and - * + determination of whether block level boxes will have >zero height - * - * 2. + layout (position and dimensions) - * - * In most cases the functions for the two stages are a corresponding pair - * layout_minmax_X() and layout_X(). - */ - -#include <assert.h> -#include <limits.h> -#include <stdbool.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <math.h> -#include <dom/dom.h> - -#include "utils/log.h" -#include "utils/talloc.h" -#include "utils/utils.h" -#include "utils/nsoption.h" -#include "netsurf/inttypes.h" -#include "netsurf/content.h" -#include "netsurf/browser_window.h" -#include "netsurf/layout.h" -#include "content/content_protected.h" -#include "css/utils.h" -#include "desktop/scrollbar.h" -#include "desktop/textarea.h" - -#include "render/box.h" -#include "render/font.h" -#include "render/form_internal.h" -#include "render/html_internal.h" -#include "render/layout.h" -#include "render/table.h" - -#define AUTO INT_MIN - -/* Fixed point percentage (a) of an integer (b), to an integer */ -#define FPCT_OF_INT_TOINT(a, b) (FIXTOINT(FDIV((a * b), F_100))) - -typedef uint8_t (*css_len_func)( - const css_computed_style *style, - css_fixed *length, css_unit *unit); -typedef uint8_t (*css_border_style_func)( - const css_computed_style *style); -typedef uint8_t (*css_border_color_func)( - const css_computed_style *style, - css_color *color); - -/** Array of per-side access functions for computed style margins. */ -static const css_len_func margin_funcs[4] = { - [TOP] = css_computed_margin_top, - [RIGHT] = css_computed_margin_right, - [BOTTOM] = css_computed_margin_bottom, - [LEFT] = css_computed_margin_left, -}; - -/** Array of per-side access functions for computed style paddings. */ -static const css_len_func padding_funcs[4] = { - [TOP] = css_computed_padding_top, - [RIGHT] = css_computed_padding_right, - [BOTTOM] = css_computed_padding_bottom, - [LEFT] = css_computed_padding_left, -}; - -/** Array of per-side access functions for computed style border_widths. */ -static const css_len_func border_width_funcs[4] = { - [TOP] = css_computed_border_top_width, - [RIGHT] = css_computed_border_right_width, - [BOTTOM] = css_computed_border_bottom_width, - [LEFT] = css_computed_border_left_width, -}; - -/** Array of per-side access functions for computed style border styles. */ -static const css_border_style_func border_style_funcs[4] = { - [TOP] = css_computed_border_top_style, - [RIGHT] = css_computed_border_right_style, - [BOTTOM] = css_computed_border_bottom_style, - [LEFT] = css_computed_border_left_style, -}; - -/** Array of per-side access functions for computed style border colors. */ -static const css_border_color_func border_color_funcs[4] = { - [TOP] = css_computed_border_top_color, - [RIGHT] = css_computed_border_right_color, - [BOTTOM] = css_computed_border_bottom_color, - [LEFT] = css_computed_border_left_color, -}; - -/* forward declaration to break cycles */ -static bool layout_block_context( - struct box *block, - int viewport_height, - html_content *content); -static void layout_minmax_block( - struct box *block, - const struct gui_layout_table *font_func, - const html_content *content); - - -/** - * Compute the size of replaced boxes with auto dimensions, according to - * content. - * - * \param box Box with object - * \param width Width value in px or AUTO. If AUTO, updated to value in px. - * \param height Height value in px or AUTO. If AUTO, updated to value in px. - * \param min_width Box's min width, as given by layout_find_dimensions. - * \param max_width Box's max width, as given by layout_find_dimensions. - * \param min_height Box's min height, as given by layout_find_dimensions. - * \param max_height Box's max height, as given by layout_find_dimensions. - * - * See CSS 2.1 sections 10.3 and 10.6. - */ -static void -layout_get_object_dimensions(struct box *box, - int *width, int *height, - int min_width, int max_width, - int min_height, int max_height) -{ - assert(box->object != NULL); - assert(width != NULL && height != NULL); - - if (*width == AUTO && *height == AUTO) { - /* No given dimensions */ - - bool scaled = false; - int intrinsic_width = content_get_width(box->object); - int intrinsic_height = content_get_height(box->object); - - /* use intrinsic dimensions */ - *width = intrinsic_width; - *height = intrinsic_height; - - /* Deal with min/max-width first */ - if (min_width > 0 && min_width > *width) { - *width = min_width; - scaled = true; - } - if (max_width >= 0 && max_width < *width) { - *width = max_width; - scaled = true; - } - - if (scaled && (intrinsic_width != 0)) { - /* Update height */ - *height = (*width * intrinsic_height) / - intrinsic_width; - } - - scaled = false; - /* Deal with min/max-height */ - if (min_height > 0 && min_height > *height) { - *height = min_height; - scaled = true; - } - if (max_height >= 0 && max_height < *height) { - *height = max_height; - scaled = true; - } - - if (scaled && (intrinsic_height != 0)) { - /* Update width */ - *width = (*height * intrinsic_width) / - intrinsic_height; - } - - } else if (*width == AUTO) { - /* Have given height; width is calculated from the given height - * and ratio of intrinsic dimensions */ - int intrinsic_width = content_get_width(box->object); - int intrinsic_height = content_get_height(box->object); - - if (intrinsic_height != 0) - *width = (*height * intrinsic_width) / - intrinsic_height; - else - *width = intrinsic_width; - - if (min_width > 0 && min_width > *width) - *width = min_width; - if (max_width >= 0 && max_width < *width) - *width = max_width; - - } else if (*height == AUTO) { - /* Have given width; height is calculated from the given width - * and ratio of intrinsic dimensions */ - int intrinsic_width = content_get_width(box->object); - int intrinsic_height = content_get_height(box->object); - - if (intrinsic_width != 0) - *height = (*width * intrinsic_height) / - intrinsic_width; - else - *height = intrinsic_height; - } -} - - -/** - * Calculate the text-indent length. - * - * \param style style of block - * \param width width of containing block - * \return length of indent - */ -static int layout_text_indent( - const nscss_len_ctx *len_ctx, - const css_computed_style *style, int width) -{ - css_fixed value = 0; - css_unit unit = CSS_UNIT_PX; - - css_computed_text_indent(style, &value, &unit); - - if (unit == CSS_UNIT_PCT) { - return FPCT_OF_INT_TOINT(value, width); - } else { - return FIXTOINT(nscss_len2px(len_ctx, value, unit, style)); - } -} - - -/** - * Determine width of margin, borders, and padding on one side of a box. - * - * \param len_ctx CSS length conversion context for document - * \param style style to measure - * \param side side of box to measure - * \param margin whether margin width is required - * \param border whether border width is required - * \param padding whether padding width is required - * \param fixed increased by sum of fixed margin, border, and padding - * \param frac increased by sum of fractional margin and padding - */ -static void -calculate_mbp_width(const nscss_len_ctx *len_ctx, - const css_computed_style *style, - unsigned int side, - bool margin, - bool border, - bool padding, - int *fixed, - float *frac) -{ - css_fixed value = 0; - css_unit unit = CSS_UNIT_PX; - - assert(style); - - /* margin */ - if (margin) { - enum css_margin_e type; - - type = margin_funcs[side](style, &value, &unit); - if (type == CSS_MARGIN_SET) { - if (unit == CSS_UNIT_PCT) { - *frac += FIXTOINT(FDIV(value, F_100)); - } else { - *fixed += FIXTOINT(nscss_len2px(len_ctx, - value, unit, style)); - } - } - } - - /* border */ - if (border) { - if (border_style_funcs[side](style) != - CSS_BORDER_STYLE_NONE) { - border_width_funcs[side](style, &value, &unit); - - *fixed += FIXTOINT(nscss_len2px(len_ctx, - value, unit, style)); - } - } - - /* padding */ - if (padding) { - padding_funcs[side](style, &value, &unit); - if (unit == CSS_UNIT_PCT) { - *frac += FIXTOINT(FDIV(value, F_100)); - } else { - *fixed += FIXTOINT(nscss_len2px(len_ctx, - value, unit, style)); - } - } -} - - -/** - * Calculate minimum and maximum width of a table. - * - * \param table box of type TABLE - * \param font_func Font functions - * \param content The HTML content we are laying out. - * \post table->min_width and table->max_width filled in, - * 0 <= table->min_width <= table->max_width - */ -static void layout_minmax_table(struct box *table, - const struct gui_layout_table *font_func, - const html_content *content) -{ - unsigned int i, j; - int border_spacing_h = 0; - int table_min = 0, table_max = 0; - int extra_fixed = 0; - float extra_frac = 0; - struct column *col; - struct box *row_group, *row, *cell; - enum css_width_e wtype; - css_fixed value = 0; - css_unit unit = CSS_UNIT_PX; - - /* check if the widths have already been calculated */ - if (table->max_width != UNKNOWN_MAX_WIDTH) - return; - - if (table_calculate_column_types(&content->len_ctx, table) == false) { - NSLOG(netsurf, WARNING, - "Could not establish table column types."); - return; - } - col = table->col; - - /* start with 0 except for fixed-width columns */ - for (i = 0; i != table->columns; i++) { - if (col[i].type == COLUMN_WIDTH_FIXED) - col[i].min = col[i].max = col[i].width; - else - col[i].min = col[i].max = 0; - } - - /* border-spacing is used in the separated borders model */ - if (css_computed_border_collapse(table->style) == - CSS_BORDER_COLLAPSE_SEPARATE) { - css_fixed h = 0, v = 0; - css_unit hu = CSS_UNIT_PX, vu = CSS_UNIT_PX; - - css_computed_border_spacing(table->style, &h, &hu, &v, &vu); - - border_spacing_h = FIXTOINT(nscss_len2px(&content->len_ctx, - h, hu, table->style)); - } - - /* 1st pass: consider cells with colspan 1 only */ - for (row_group = table->children; row_group; row_group =row_group->next) - for (row = row_group->children; row; row = row->next) - for (cell = row->children; cell; cell = cell->next) { - assert(cell->type == BOX_TABLE_CELL); - assert(cell->style); - /** TODO: Handle colspan="0" correctly. - * It's currently converted to 1 in box normaisation */ - assert(cell->columns != 0); - - if (cell->columns != 1) - continue; - - layout_minmax_block(cell, font_func, content); - i = cell->start_column; - - if (col[i].positioned) - continue; - - /* update column min, max widths using cell widths */ - if (col[i].min < cell->min_width) - col[i].min = cell->min_width; - if (col[i].max < cell->max_width) - col[i].max = cell->max_width; - } - - /* 2nd pass: cells which span multiple columns */ - for (row_group = table->children; row_group; row_group =row_group->next) - for (row = row_group->children; row; row = row->next) - for (cell = row->children; cell; cell = cell->next) { - unsigned int flexible_columns = 0; - int min = 0, max = 0, fixed_width = 0, extra; - - if (cell->columns == 1) - continue; - - layout_minmax_block(cell, font_func, content); - i = cell->start_column; - - /* find min width so far of spanned columns, and count - * number of non-fixed spanned columns and total fixed width */ - for (j = 0; j != cell->columns; j++) { - min += col[i + j].min; - if (col[i + j].type == COLUMN_WIDTH_FIXED) - fixed_width += col[i + j].width; - else - flexible_columns++; - } - min += (cell->columns - 1) * border_spacing_h; - - /* distribute extra min to spanned columns */ - if (min < cell->min_width) { - if (flexible_columns == 0) { - extra = 1 + (cell->min_width - min) / - cell->columns; - for (j = 0; j != cell->columns; j++) { - col[i + j].min += extra; - if (col[i + j].max < col[i + j].min) - col[i + j].max = col[i + j].min; - } - } else { - extra = 1 + (cell->min_width - min) / - flexible_columns; - for (j = 0; j != cell->columns; j++) { - if (col[i + j].type != - COLUMN_WIDTH_FIXED) { - col[i + j].min += extra; - if (col[i + j].max < - col[i + j].min) - col[i + j].max = - col[i + j].min; - } - } - } - } - - /* find max width so far of spanned columns */ - for (j = 0; j != cell->columns; j++) - max += col[i + j].max; - max += (cell->columns - 1) * border_spacing_h; - - /* distribute extra max to spanned columns */ - if (max < cell->max_width && flexible_columns) { - extra = 1 + (cell->max_width - max) / flexible_columns; - for (j = 0; j != cell->columns; j++) - if (col[i + j].type != COLUMN_WIDTH_FIXED) - col[i + j].max += extra; - } - } - - for (i = 0; i != table->columns; i++) { - if (col[i].max < col[i].min) { - box_dump(stderr, table, 0, true); - assert(0); - } - table_min += col[i].min; - table_max += col[i].max; - } - - /* fixed width takes priority, unless it is too narrow */ - wtype = css_computed_width(table->style, &value, &unit); - if (wtype == CSS_WIDTH_SET && unit != CSS_UNIT_PCT) { - int width = FIXTOINT(nscss_len2px(&content->len_ctx, - value, unit, table->style)); - if (table_min < width) - table_min = width; - if (table_max < width) - table_max = width; - } - - /* add margins, border, padding to min, max widths */ - calculate_mbp_width(&content->len_ctx, - table->style, LEFT, true, true, true, - &extra_fixed, &extra_frac); - calculate_mbp_width(&content->len_ctx, - table->style, RIGHT, true, true, true, - &extra_fixed, &extra_frac); - if (extra_fixed < 0) - extra_fixed = 0; - if (extra_frac < 0) - extra_frac = 0; - if (1.0 <= extra_frac) - extra_frac = 0.9; - table->min_width = (table_min + extra_fixed) / (1.0 - extra_frac); - table->max_width = (table_max + extra_fixed) / (1.0 - extra_frac); - table->min_width += (table->columns + 1) * border_spacing_h; - table->max_width += (table->columns + 1) * border_spacing_h; - - assert(0 <= table->min_width && table->min_width <= table->max_width); -} - -/** - * Helper to check if a box has percentage max width. - * - * \param[in] b Box to check. - * \return true iff box has percnetage max width. - */ -static inline bool box_has_percentage_max_width(struct box *b) -{ - css_unit unit = CSS_UNIT_PX; - enum css_max_width_e type; - css_fixed value = 0; - - assert(b != NULL); - - type = css_computed_max_width(b->style, &value, &unit); - return ((type == CSS_MAX_WIDTH_SET) && (unit == CSS_UNIT_PCT)); -} - -/** - * Calculate minimum and maximum width of a line. - * - * \param first a box in an inline container - * \param line_min updated to minimum width of line starting at first - * \param line_max updated to maximum width of line starting at first - * \param first_line true iff this is the first line in the inline container - * \param line_has_height updated to true or false, depending on line - * \param font_func Font functions. - * \return first box in next line, or 0 if no more lines - * \post 0 <= *line_min <= *line_max - */ -static struct box * -layout_minmax_line(struct box *first, - int *line_min, - int *line_max, - bool first_line, - bool *line_has_height, - const struct gui_layout_table *font_func, - const html_content *content) -{ - int min = 0, max = 0, width, height, fixed; - float frac; - size_t i, j; - struct box *b; - struct box *block; - plot_font_style_t fstyle; - bool no_wrap; - - assert(first->parent); - assert(first->parent->parent); - assert(first->parent->parent->style); - - block = first->parent->parent; - no_wrap = (css_computed_white_space(block->style) == - CSS_WHITE_SPACE_NOWRAP || - css_computed_white_space(block->style) == - CSS_WHITE_SPACE_PRE); - - *line_has_height = false; - - /* corresponds to the pass 1 loop in layout_line() */ - for (b = first; b; b = b->next) { - enum css_width_e wtype; - enum css_height_e htype; - enum css_box_sizing_e bs; - css_fixed value = 0; - css_unit unit = CSS_UNIT_PX; - - assert(b->type == BOX_INLINE || b->type == BOX_INLINE_BLOCK || - b->type == BOX_FLOAT_LEFT || - b->type == BOX_FLOAT_RIGHT || - b->type == BOX_BR || b->type == BOX_TEXT || - b->type == BOX_INLINE_END); - - NSLOG(layout, DEBUG, "%p: min %i, max %i", b, min, max); - - if (b->type == BOX_BR) { - b = b->next; - break; - } - - if (b->type == BOX_FLOAT_LEFT || b->type == BOX_FLOAT_RIGHT) { - assert(b->children); - if (b->children->type == BOX_BLOCK) - layout_minmax_block(b->children, font_func, - content); - else - layout_minmax_table(b->children, font_func, - content); - b->min_width = b->children->min_width; - b->max_width = b->children->max_width; - if (min < b->min_width) - min = b->min_width; - max += b->max_width; - continue; - } - - if (b->type == BOX_INLINE_BLOCK) { - layout_minmax_block(b, font_func, content); - if (min < b->min_width) - min = b->min_width; - max += b->max_width; - - if (b->flags & HAS_HEIGHT) - *line_has_height = true; - continue; - } - - assert(b->style); - font_plot_style_from_css(&content->len_ctx, b->style, &fstyle); - - if (b->type == BOX_INLINE && !b->object && - !(b->flags & REPLACE_DIM) && - !(b->flags & IFRAME)) { - fixed = frac = 0; - calculate_mbp_width(&content->len_ctx, - b->style, LEFT, true, true, true, - &fixed, &frac); - if (!b->inline_end) - calculate_mbp_width(&content->len_ctx, - b->style, RIGHT, - true, true, true, - &fixed, &frac); - if (0 < fixed) - max += fixed; - *line_has_height = true; - /* \todo update min width, consider fractional extra */ - } else if (b->type == BOX_INLINE_END) { - fixed = frac = 0; - calculate_mbp_width(&content->len_ctx, - b->inline_end->style, RIGHT, - true, true, true, - &fixed, &frac); - if (0 < fixed) - max += fixed; - - if (b->next) { - if (b->space == UNKNOWN_WIDTH) { - font_func->width(&fstyle, " ", 1, - &b->space); - } - max += b->space; - } - - *line_has_height = true; - continue; - } - - if (!b->object && !(b->flags & IFRAME) && !b->gadget && - !(b->flags & REPLACE_DIM)) { - /* inline non-replaced, 10.3.1 and 10.6.1 */ - bool no_wrap_box; - if (!b->text) - continue; - - no_wrap_box = (css_computed_white_space(b->style) == - CSS_WHITE_SPACE_NOWRAP || - css_computed_white_space(b->style) == - CSS_WHITE_SPACE_PRE); - - if (b->width == UNKNOWN_WIDTH) { - /** \todo handle errors */ - - /* If it's a select element, we must use the - * width of the widest option text */ - if (b->parent->parent->gadget && - b->parent->parent->gadget->type - == GADGET_SELECT) { - int opt_maxwidth = 0; - struct form_option *o; - - for (o = b->parent->parent->gadget-> - data.select.items; o; - o = o->next) { - int opt_width; - font_func->width(&fstyle, - o->text, - strlen(o->text), - &opt_width); - - if (opt_maxwidth < opt_width) - opt_maxwidth =opt_width; - } - - b->width = opt_maxwidth; - if (nsoption_bool(core_select_menu)) - b->width += SCROLLBAR_WIDTH; - - } else { - font_func->width(&fstyle, b->text, - b->length, &b->width); - b->flags |= MEASURED; - } - } - max += b->width; - if (b->next) { - if (b->space == UNKNOWN_WIDTH) { - font_func->width(&fstyle, " ", 1, - &b->space); - } - max += b->space; - } - - if (no_wrap) { - /* Don't wrap due to block style, - * so min is the same as max */ - min = max; - - } else if (no_wrap_box) { - /* This inline box can't be wrapped, - * for min, consider box's width */ - if (min < b->width) - min = b->width; - - } else if (b->parent->flags & NEED_MIN) { - /* If we care what the minimum width is, - * calculate it. (It's only needed if we're - * shrinking-to-fit.) */ - /* min = widest single word */ - i = 0; - do { - for (j = i; j != b->length && - b->text[j] != ' '; j++) - ; - font_func->width(&fstyle, b->text + i, - j - i, &width); - if (min < width) - min = width; - i = j + 1; - } while (j != b->length); - } - - *line_has_height = true; - - continue; - } - - /* inline replaced, 10.3.2 and 10.6.2 */ - assert(b->style); - - /* calculate box width */ - wtype = css_computed_width(b->style, &value, &unit); - bs = css_computed_box_sizing(block->style); - if (wtype == CSS_WIDTH_SET) { - if (unit == CSS_UNIT_PCT) { - width = AUTO; - } else { - width = FIXTOINT(nscss_len2px(&content->len_ctx, - value, unit, b->style)); - - if (bs == CSS_BOX_SIZING_BORDER_BOX) { - fixed = frac = 0; - calculate_mbp_width(&content->len_ctx, - block->style, LEFT, - false, true, true, - &fixed, &frac); - calculate_mbp_width(&content->len_ctx, - block->style, RIGHT, - false, true, true, - &fixed, &frac); - if (width < fixed) { - width = fixed; - } - } - if (width < 0) - width = 0; - } - } else { - width = AUTO; - } - - /* height */ - htype = css_computed_height(b->style, &value, &unit); - if (htype == CSS_HEIGHT_SET) { - height = FIXTOINT(nscss_len2px(&content->len_ctx, - value, unit, b->style)); - } else { - height = AUTO; - } - - if (b->object || (b->flags & REPLACE_DIM)) { - if (b->object) { - int temp_height = height; - layout_get_object_dimensions(b, - &width, &temp_height, - INT_MIN, INT_MAX, - INT_MIN, INT_MAX); - } - - fixed = frac = 0; - if (bs == CSS_BOX_SIZING_BORDER_BOX) { - calculate_mbp_width(&content->len_ctx, - b->style, LEFT, - true, false, false, - &fixed, &frac); - calculate_mbp_width(&content->len_ctx, - b->style, RIGHT, - true, false, false, - &fixed, &frac); - } else { - calculate_mbp_width(&content->len_ctx, - b->style, LEFT, - true, true, true, - &fixed, &frac); - calculate_mbp_width(&content->len_ctx, - b->style, RIGHT, - true, true, true, - &fixed, &frac); - } - if (0 < width + fixed) - width += fixed; - } else if (b->flags & IFRAME) { - /* TODO: handle percentage widths properly */ - if (width == AUTO) - width = 400; - - fixed = frac = 0; - if (bs == CSS_BOX_SIZING_BORDER_BOX) { - calculate_mbp_width(&content->len_ctx, - b->style, LEFT, - true, false, false, - &fixed, &frac); - calculate_mbp_width(&content->len_ctx, - b->style, RIGHT, - true, false, false, - &fixed, &frac); - } else { - calculate_mbp_width(&content->len_ctx, - b->style, LEFT, - true, true, true, - &fixed, &frac); - calculate_mbp_width(&content->len_ctx, - b->style, RIGHT, - true, true, true, - &fixed, &frac); - } - - if (0 < width + fixed) - width += fixed; - - } else { - /* form control with no object */ - if (width == AUTO) - width = FIXTOINT(nscss_len2px( - &content->len_ctx, - INTTOFIX(1), CSS_UNIT_EM, - b->style)); - } - - if (min < width && !box_has_percentage_max_width(b)) - min = width; - if (width > 0) - max += width; - - *line_has_height = true; - } - - if (first_line) { - /* todo: handle percentage values properly */ - /* todo: handle text-indent interaction with floats */ - int text_indent = layout_text_indent(&content->len_ctx, - first->parent->parent->style, 100); - min = (min + text_indent < 0) ? 0 : min + text_indent; - max = (max + text_indent < 0) ? 0 : max + text_indent; - } - - *line_min = min; - *line_max = max; - - NSLOG(layout, DEBUG, "line_min %i, line_max %i", min, max); - - assert(b != first); - assert(0 <= *line_min); - assert(*line_min <= *line_max); - return b; -} - - -/** - * Calculate minimum and maximum width of an inline container. - * - * \param inline_container box of type INLINE_CONTAINER - * \param[out] has_height set to true if container has height - * \param font_func Font functions. - * \post inline_container->min_width and inline_container->max_width filled in, - * 0 <= inline_container->min_width <= inline_container->max_width - */ -static void -layout_minmax_inline_container(struct box *inline_container, - bool *has_height, - const struct gui_layout_table *font_func, - const html_content *content) -{ - struct box *child; - int line_min = 0, line_max = 0; - int min = 0, max = 0; - bool first_line = true; - bool line_has_height; - - assert(inline_container->type == BOX_INLINE_CONTAINER); - - /* check if the widths have already been calculated */ - if (inline_container->max_width != UNKNOWN_MAX_WIDTH) - return; - - *has_height = false; - - for (child = inline_container->children; child; ) { - child = layout_minmax_line(child, &line_min, &line_max, - first_line, &line_has_height, font_func, - content); - if (min < line_min) - min = line_min; - if (max < line_max) - max = line_max; - first_line = false; - *has_height |= line_has_height; - } - - inline_container->min_width = min; - inline_container->max_width = max; - - assert(0 <= inline_container->min_width && - inline_container->min_width <= - inline_container->max_width); -} - - -/** - * Calculate minimum and maximum width of a block. - * - * \param block box of type BLOCK, INLINE_BLOCK, or TABLE_CELL - * \param font_func font functions - * \param content The HTML content being layed out. - * \post block->min_width and block->max_width filled in, - * 0 <= block->min_width <= block->max_width - */ -static void layout_minmax_block( - struct box *block, - const struct gui_layout_table *font_func, - const html_content *content) -{ - struct box *child; - int min = 0, max = 0; - int extra_fixed = 0; - float extra_frac = 0; - enum css_width_e wtype = CSS_WIDTH_AUTO; - css_fixed width = 0; - css_unit wunit = CSS_UNIT_PX; - enum css_height_e htype = CSS_HEIGHT_AUTO; - css_fixed height = 0; - css_unit hunit = CSS_UNIT_PX; - enum css_box_sizing_e bs = CSS_BOX_SIZING_CONTENT_BOX; - bool child_has_height = false; - - assert(block->type == BOX_BLOCK || - block->type == BOX_INLINE_BLOCK || - block->type == BOX_TABLE_CELL); - - /* check if the widths have already been calculated */ - if (block->max_width != UNKNOWN_MAX_WIDTH) - return; - - if (block->style != NULL) { - wtype = css_computed_width(block->style, &width, &wunit); - htype = css_computed_height(block->style, &height, &hunit); - bs = css_computed_box_sizing(block->style); - } - - /* set whether the minimum width is of any interest for this box */ - if (((block->parent && (block->parent->type == BOX_FLOAT_LEFT || - block->parent->type == BOX_FLOAT_RIGHT)) || - block->type == BOX_INLINE_BLOCK) && - wtype != CSS_WIDTH_SET) { - /* box shrinks to fit; need minimum width */ - block->flags |= NEED_MIN; - } else if (block->type == BOX_TABLE_CELL) { - /* box shrinks to fit; need minimum width */ - block->flags |= NEED_MIN; - } else if (block->parent && (block->parent->flags & NEED_MIN) && - wtype != CSS_WIDTH_SET) { - /* box inside shrink-to-fit context; need minimum width */ - block->flags |= NEED_MIN; - } - - if (block->gadget && (block->gadget->type == GADGET_TEXTBOX || - block->gadget->type == GADGET_PASSWORD || - block->gadget->type == GADGET_FILE || - block->gadget->type == GADGET_TEXTAREA) && - block->style && wtype == CSS_WIDTH_AUTO) { - css_fixed size = INTTOFIX(10); - css_unit unit = CSS_UNIT_EM; - - min = max = FIXTOINT(nscss_len2px(&content->len_ctx, - size, unit, block->style)); - - block->flags |= HAS_HEIGHT; - } - - if (block->gadget && (block->gadget->type == GADGET_RADIO || - block->gadget->type == GADGET_CHECKBOX) && - block->style && wtype == CSS_WIDTH_AUTO) { - css_fixed size = INTTOFIX(1); - css_unit unit = CSS_UNIT_EM; - - /* form checkbox or radio button - * if width is AUTO, set it to 1em */ - min = max = FIXTOINT(nscss_len2px(&content->len_ctx, - size, unit, block->style)); - - block->flags |= HAS_HEIGHT; - } - - if (block->object) { - if (content_get_type(block->object) == CONTENT_HTML) { - layout_minmax_block(html_get_box_tree(block->object), - font_func, content); - min = html_get_box_tree(block->object)->min_width; - max = html_get_box_tree(block->object)->max_width; - } else { - min = max = content_get_width(block->object); - } - - block->flags |= HAS_HEIGHT; - } else if (block->flags & IFRAME) { - /** \todo do we need to know the min/max width of the iframe's - * content? */ - block->flags |= HAS_HEIGHT; - } else { - /* recurse through children */ - for (child = block->children; child; child = child->next) { - switch (child->type) { - case BOX_BLOCK: - layout_minmax_block(child, font_func, - content); - if (child->flags & HAS_HEIGHT) - child_has_height = true; - break; - case BOX_INLINE_CONTAINER: - if (block->flags & NEED_MIN) - child->flags |= NEED_MIN; - - layout_minmax_inline_container(child, - &child_has_height, font_func, - content); - if (child_has_height && - child == - child->parent->children) { - block->flags |= MAKE_HEIGHT; - } - break; - case BOX_TABLE: - layout_minmax_table(child, font_func, - content); - /* todo: fix for zero height tables */ - child_has_height = true; - child->flags |= MAKE_HEIGHT; - break; - default: - assert(0); - } - assert(child->max_width != UNKNOWN_MAX_WIDTH); - - if (child->style && - (css_computed_position(child->style) == - CSS_POSITION_ABSOLUTE || - css_computed_position(child->style) == - CSS_POSITION_FIXED)) { - /* This child is positioned out of normal flow, - * so it will have no affect on width */ - continue; - } - - if (min < child->min_width) - min = child->min_width; - if (max < child->max_width) - max = child->max_width; - - if (child_has_height) - block->flags |= HAS_HEIGHT; - } - } - - if (max < min) { - box_dump(stderr, block, 0, true); - assert(0); - } - - /* fixed width takes priority */ - if (block->type != BOX_TABLE_CELL && wtype == CSS_WIDTH_SET && - wunit != CSS_UNIT_PCT) { - min = max = FIXTOINT(nscss_len2px(&content->len_ctx, - width, wunit, block->style)); - if (bs == CSS_BOX_SIZING_BORDER_BOX) { - int border_box_fixed = 0; - float border_box_frac = 0; - calculate_mbp_width(&content->len_ctx, - block->style, LEFT, - false, true, true, - &border_box_fixed, &border_box_frac); - calculate_mbp_width(&content->len_ctx, - block->style, RIGHT, - false, true, true, - &border_box_fixed, &border_box_frac); - if (min < border_box_fixed) { - min = max = border_box_fixed; - } - } - } - - if (htype == CSS_HEIGHT_SET && hunit != CSS_UNIT_PCT && - height > INTTOFIX(0)) { - block->flags |= MAKE_HEIGHT; - block->flags |= HAS_HEIGHT; - } - - /* add margins, border, padding to min, max widths */ - /* Note: we don't know available width here so percentage margin - * and paddings are wrong. */ - if (bs == CSS_BOX_SIZING_BORDER_BOX && wtype == CSS_WIDTH_SET) { - /* Border and padding included in width, so just get margin */ - calculate_mbp_width(&content->len_ctx, - block->style, LEFT, true, false, false, - &extra_fixed, &extra_frac); - calculate_mbp_width(&content->len_ctx, - block->style, RIGHT, true, false, false, - &extra_fixed, &extra_frac); - } else { - calculate_mbp_width(&content->len_ctx, - block->style, LEFT, true, true, true, - &extra_fixed, &extra_frac); - calculate_mbp_width(&content->len_ctx, - block->style, RIGHT, true, true, true, - &extra_fixed, &extra_frac); - } - if (extra_fixed < 0) - extra_fixed = 0; - if (extra_frac < 0) - extra_frac = 0; - if (1.0 <= extra_frac) - extra_frac = 0.9; - if (block->style != NULL && - (css_computed_float(block->style) == CSS_FLOAT_LEFT || - css_computed_float(block->style) == CSS_FLOAT_RIGHT)) { - /* floated boxs */ - block->min_width = min + extra_fixed; - block->max_width = max + extra_fixed; - } else { - /* not floated */ - block->min_width = (min + extra_fixed) / (1.0 - extra_frac); - block->max_width = (max + extra_fixed) / (1.0 - extra_frac); - } - - assert(0 <= block->min_width && block->min_width <= block->max_width); -} - - -/** - * Adjust a specified width or height for the box-sizing property. - * - * This turns the specified dimension into a content-box dimension. - * - * \param len_ctx Length conversion context - * \param box gadget to adjust dimensions of - * \param available_width width of containing block - * \param setwidth set true if the dimension to be tweaked is a width, - * else set false for a height - * \param dimension current value for given width/height dimension. - * updated to new value after consideration of - * gadget properties. - */ -static void layout_handle_box_sizing( - const nscss_len_ctx *len_ctx, - struct box *box, - int available_width, - bool setwidth, - int *dimension) -{ - enum css_box_sizing_e bs; - - assert(box && box->style); - - bs = css_computed_box_sizing(box->style); - - if (bs == CSS_BOX_SIZING_BORDER_BOX) { - int orig = *dimension; - int fixed = 0; - float frac = 0; - - calculate_mbp_width(len_ctx, box->style, - setwidth ? LEFT : TOP, - false, true, true, &fixed, &frac); - calculate_mbp_width(len_ctx, box->style, - setwidth ? RIGHT : BOTTOM, - false, true, true, &fixed, &frac); - orig -= frac * available_width + fixed; - *dimension = orig > 0 ? orig : 0; - } -} - - -/** - * Calculate width, height, and thickness of margins, paddings, and borders. - * - * \param len_ctx Length conversion context - * \param available_width width of containing block - * \param viewport_height height of viewport in pixels or -ve if unknown - * \param box current box - * \param style style giving width, height, margins, paddings, - * and borders - * \param width updated to width, may be NULL - * \param height updated to height, may be NULL - * \param max_width updated to max-width, may be NULL - * \param min_width updated to min-width, may be NULL - * \param max_height updated to max-height, may be NULL - * \param min_height updated to min-height, may be NULL - * \param margin filled with margins, may be NULL - * \param padding filled with paddings, may be NULL - * \param border filled with border widths, may be NULL - */ -static void -layout_find_dimensions(const nscss_len_ctx *len_ctx, - int available_width, - int viewport_height, - struct box *box, - const css_computed_style *style, - int *width, - int *height, - int *max_width, - int *min_width, - int *max_height, - int *min_height, - int margin[4], - int padding[4], - struct box_border border[4]) -{ - struct box *containing_block = NULL; - unsigned int i; - - if (width) { - enum css_width_e wtype; - css_fixed value = 0; - css_unit unit = CSS_UNIT_PX; - - wtype = css_computed_width(style, &value, &unit); - - if (wtype == CSS_WIDTH_SET) { - if (unit == CSS_UNIT_PCT) { - *width = FPCT_OF_INT_TOINT( - value, available_width); - } else { - *width = FIXTOINT(nscss_len2px(len_ctx, - value, unit, style)); - } - } else { - *width = AUTO; - } - - if (*width != AUTO) { - layout_handle_box_sizing(len_ctx, box, available_width, - true, width); - } - } - - if (height) { - enum css_height_e htype; - css_fixed value = 0; - css_unit unit = CSS_UNIT_PX; - - htype = css_computed_height(style, &value, &unit); - - if (htype == CSS_HEIGHT_SET) { - if (unit == CSS_UNIT_PCT) { - enum css_height_e cbhtype; - - if (css_computed_position(box->style) == - CSS_POSITION_ABSOLUTE && - box->parent) { - /* Box is absolutely positioned */ - assert(box->float_container); - containing_block = box->float_container; - } else if (box->float_container && - css_computed_position(box->style) != - CSS_POSITION_ABSOLUTE && - (css_computed_float(box->style) == - CSS_FLOAT_LEFT || - css_computed_float(box->style) == - CSS_FLOAT_RIGHT)) { - /* Box is a float */ - assert(box->parent && - box->parent->parent && - box->parent->parent->parent); - - containing_block = - box->parent->parent->parent; - } else if (box->parent && box->parent->type != - BOX_INLINE_CONTAINER) { - /* Box is a block level element */ - containing_block = box->parent; - } else if (box->parent && box->parent->type == - BOX_INLINE_CONTAINER) { - /* Box is an inline block */ - assert(box->parent->parent); - containing_block = box->parent->parent; - } - - if (containing_block) { - css_fixed f = 0; - css_unit u = CSS_UNIT_PX; - - cbhtype = css_computed_height( - containing_block->style, - &f, &u); - } - - if (containing_block && - containing_block->height != AUTO && - (css_computed_position(box->style) == - CSS_POSITION_ABSOLUTE || - cbhtype == CSS_HEIGHT_SET)) { - /* Box is absolutely positioned or its - * containing block has a valid - * specified height. - * (CSS 2.1 Section 10.5) */ - *height = FPCT_OF_INT_TOINT(value, - containing_block->height); - } else if ((!box->parent || - !box->parent->parent) && - viewport_height >= 0) { - /* If root element or it's child - * (HTML or BODY) */ - *height = FPCT_OF_INT_TOINT(value, - viewport_height); - } else { - /* precentage height not permissible - * treat height as auto */ - *height = AUTO; - } - } else { - *height = FIXTOINT(nscss_len2px(len_ctx, - value, unit, style)); - } - } else { - *height = AUTO; - } - - if (*height != AUTO) { - layout_handle_box_sizing(len_ctx, box, available_width, - false, height); - } - } - - if (max_width) { - enum css_max_width_e type; - css_fixed value = 0; - css_unit unit = CSS_UNIT_PX; - - type = css_computed_max_width(style, &value, &unit); - - if (type == CSS_MAX_WIDTH_SET) { - if (unit == CSS_UNIT_PCT) { - *max_width = FPCT_OF_INT_TOINT(value, - available_width); - } else { - *max_width = FIXTOINT(nscss_len2px(len_ctx, - value, unit, style)); - } - } else { - /* Inadmissible */ - *max_width = -1; - } - - if (*max_width != -1) { - layout_handle_box_sizing(len_ctx, box, available_width, - true, max_width); - } - } - - if (min_width) { - enum css_min_width_e type; - css_fixed value = 0; - css_unit unit = CSS_UNIT_PX; - - type = ns_computed_min_width(style, &value, &unit); - - if (type == CSS_MIN_WIDTH_SET) { - if (unit == CSS_UNIT_PCT) { - *min_width = FPCT_OF_INT_TOINT(value, - available_width); - } else { - *min_width = FIXTOINT(nscss_len2px(len_ctx, - value, unit, style)); - } - } else { - /* Inadmissible */ - *min_width = 0; - } - - if (*min_width != 0) { - layout_handle_box_sizing(len_ctx, box, available_width, - true, min_width); - } - } - - if (max_height) { - enum css_max_height_e type; - css_fixed value = 0; - css_unit unit = CSS_UNIT_PX; - - type = css_computed_max_height(style, &value, &unit); - - if (type == CSS_MAX_HEIGHT_SET) { - if (unit == CSS_UNIT_PCT) { - /* TODO: handle percentage */ - *max_height = -1; - } else { - *max_height = FIXTOINT(nscss_len2px(len_ctx, - value, unit, style)); - } - } else { - /* Inadmissible */ - *max_height = -1; - } - } - - if (min_height) { - enum css_min_height_e type; - css_fixed value = 0; - css_unit unit = CSS_UNIT_PX; - - type = ns_computed_min_height(style, &value, &unit); - - if (type == CSS_MIN_HEIGHT_SET) { - if (unit == CSS_UNIT_PCT) { - /* TODO: handle percentage */ - *min_height = 0; - } else { - *min_height = FIXTOINT(nscss_len2px(len_ctx, - value, unit, style)); - } - } else { - /* Inadmissible */ - *min_height = 0; - } - } - - for (i = 0; i != 4; i++) { - if (margin) { - enum css_margin_e type = CSS_MARGIN_AUTO; - css_fixed value = 0; - css_unit unit = CSS_UNIT_PX; - - type = margin_funcs[i](style, &value, &unit); - - if (type == CSS_MARGIN_SET) { - if (unit == CSS_UNIT_PCT) { - margin[i] = FPCT_OF_INT_TOINT(value, - available_width); - } else { - margin[i] = FIXTOINT(nscss_len2px( - len_ctx, - value, unit, style)); - } - } else { - margin[i] = AUTO; - } - } - - if (padding) { - css_fixed value = 0; - css_unit unit = CSS_UNIT_PX; - - padding_funcs[i](style, &value, &unit); - - if (unit == CSS_UNIT_PCT) { - padding[i] = FPCT_OF_INT_TOINT(value, - available_width); - } else { - padding[i] = FIXTOINT(nscss_len2px(len_ctx, - value, unit, style)); - } - } - - /* Table cell borders are populated in table.c */ - if (border && box->type != BOX_TABLE_CELL) { - enum css_border_style_e bstyle = CSS_BORDER_STYLE_NONE; - css_color color = 0; - css_fixed value = 0; - css_unit unit = CSS_UNIT_PX; - - border_width_funcs[i](style, &value, &unit); - bstyle = border_style_funcs[i](style); - border_color_funcs[i](style, &color); - - border[i].style = bstyle; - border[i].c = color; - - if (bstyle == CSS_BORDER_STYLE_HIDDEN || - bstyle == CSS_BORDER_STYLE_NONE) - /* spec unclear: following Mozilla */ - border[i].width = 0; - else - border[i].width = FIXTOINT(nscss_len2px(len_ctx, - value, unit, style)); - - /* Special case for border-collapse: make all borders - * on table/table-row-group/table-row zero width. */ - if (css_computed_border_collapse(style) == - CSS_BORDER_COLLAPSE_COLLAPSE && - (box->type == BOX_TABLE || - box->type == BOX_TABLE_ROW_GROUP || - box->type == BOX_TABLE_ROW)) - border[i].width = 0; - } - } -} - - -/** - * Find next block that current margin collapses to. - * - * \param len_ctx Length conversion context - * \param box box to start tree-order search from (top margin is included) - * \param block box responsible for current block fromatting context - * \param viewport_height height of viewport in px - * \param max_pos_margin updated to to maximum positive margin encountered - * \param max_neg_margin updated to to maximum negative margin encountered - * \return next box that current margin collapses to, or NULL if none. - */ -static struct box* -layout_next_margin_block(const nscss_len_ctx *len_ctx, - struct box *box, - struct box *block, - int viewport_height, - int *max_pos_margin, - int *max_neg_margin) -{ - assert(block != NULL); - - while (box != NULL) { - - if (box->type == BOX_INLINE_CONTAINER || (box->style && - (css_computed_position(box->style) != - CSS_POSITION_ABSOLUTE && - css_computed_position(box->style) != - CSS_POSITION_FIXED))) { - /* Not positioned */ - - /* Get margins */ - if (box->style) { - layout_find_dimensions(len_ctx, - box->parent->width, - viewport_height, box, - box->style, - NULL, NULL, NULL, NULL, - NULL, NULL, box->margin, - box->padding, box->border); - - /* Apply top margin */ - if (*max_pos_margin < box->margin[TOP]) - *max_pos_margin = box->margin[TOP]; - else if (*max_neg_margin < -box->margin[TOP]) - *max_neg_margin = -box->margin[TOP]; - } - - /* Check whether box is the box current margin collapses - * to */ - if (box->flags & MAKE_HEIGHT || - box->border[TOP].width || - box->padding[TOP] || - (box->style && - css_computed_overflow_y(box->style) != - CSS_OVERFLOW_VISIBLE) || - (box->type == BOX_INLINE_CONTAINER && - box != box->parent->children)) { - /* Collapse to this box; return it */ - return box; - } - } - - - /* Find next box */ - if (box->type == BOX_BLOCK && !box->object && box->children && - box->style && - css_computed_overflow_y(box->style) == - CSS_OVERFLOW_VISIBLE) { - /* Down into children. */ - box = box->children; - } else { - if (!box->next) { - /* No more siblings: - * Go up to first ancestor with a sibling. */ - do { - /* Apply bottom margin */ - if (*max_pos_margin < - box->margin[BOTTOM]) - *max_pos_margin = - box->margin[BOTTOM]; - else if (*max_neg_margin < - -box->margin[BOTTOM]) - *max_neg_margin = - -box->margin[BOTTOM]; - - box = box->parent; - } while (box != block && !box->next); - - if (box == block) { - /* Margins don't collapse with stuff - * outside the block formatting context - */ - return block; - } - } - - /* Apply bottom margin */ - if (*max_pos_margin < box->margin[BOTTOM]) - *max_pos_margin = box->margin[BOTTOM]; - else if (*max_neg_margin < -box->margin[BOTTOM]) - *max_neg_margin = -box->margin[BOTTOM]; - - /* To next sibling. */ - box = box->next; - - /* Get margins */ - if (box->style) { - layout_find_dimensions(len_ctx, - box->parent->width, - viewport_height, box, - box->style, - NULL, NULL, NULL, NULL, - NULL, NULL, box->margin, - box->padding, box->border); - } - } - } - - return NULL; -} - - -/** - * Find y coordinate which clears all floats on left and/or right. - * - * \param fl first float in float list - * \param clear type of clear - * \return y coordinate relative to ancestor box for floats - */ -static int layout_clear(struct box *fl, enum css_clear_e clear) -{ - int y = 0; - for (; fl; fl = fl->next_float) { - if ((clear == CSS_CLEAR_LEFT || clear == CSS_CLEAR_BOTH) && - fl->type == BOX_FLOAT_LEFT) - if (y < fl->y + fl->height) - y = fl->y + fl->height; - if ((clear == CSS_CLEAR_RIGHT || clear == CSS_CLEAR_BOTH) && - fl->type == BOX_FLOAT_RIGHT) - if (y < fl->y + fl->height) - y = fl->y + fl->height; - } - return y; -} - - -/** - * Find left and right edges in a vertical range. - * - * \param fl first float in float list - * \param y0 start of y range to search - * \param y1 end of y range to search - * \param x0 start left edge, updated to available left edge - * \param x1 start right edge, updated to available right edge - * \param left returns float on left if present - * \param right returns float on right if present - */ -static void -find_sides(struct box *fl, - int y0, int y1, - int *x0, int *x1, - struct box **left, - struct box **right) -{ - int fy0, fy1, fx0, fx1; - - NSLOG(layout, DEBUG, "y0 %i, y1 %i, x0 %i, x1 %i", y0, y1, *x0, *x1); - - *left = *right = 0; - for (; fl; fl = fl->next_float) { - fy1 = fl->y + fl->height; - if (fy1 < y0) { - /* Floats are sorted in order of decreasing bottom pos. - * Past here, all floats will be too high to concern us. - */ - return; - } - fy0 = fl->y; - if (y0 < fy1 && fy0 <= y1) { - if (fl->type == BOX_FLOAT_LEFT) { - fx1 = fl->x + fl->width; - if (*x0 < fx1) { - *x0 = fx1; - *left = fl; - } - } else { - fx0 = fl->x; - if (fx0 < *x1) { - *x1 = fx0; - *right = fl; - } - } - } - } - - NSLOG(layout, DEBUG, "x0 %i, x1 %i, left %p, right %p", *x0, *x1, - *left, *right); -} - - - - -/** - * Solve the width constraint as given in CSS 2.1 section 10.3.3. - * - * \param box Box to solve constraint for - * \param available_width Max width available in pixels - * \param width Current box width - * \param lm Min left margin required to avoid floats in px. - * zero if not applicable - * \param rm Min right margin required to avoid floats in px. - * zero if not applicable - * \param max_width Box max-width ( -ve means no max-width to apply) - * \param min_width Box min-width ( <=0 means no min-width to apply) - * \return New box width - * - * \post \a box's left/right margins will be updated. - */ -static int -layout_solve_width(struct box *box, - int available_width, - int width, - int lm, - int rm, - int max_width, - int min_width) -{ - bool auto_width = false; - - /* Increase specified left/right margins */ - if (box->margin[LEFT] != AUTO && box->margin[LEFT] < lm && - box->margin[LEFT] >= 0) - box->margin[LEFT] = lm; - if (box->margin[RIGHT] != AUTO && box->margin[RIGHT] < rm && - box->margin[RIGHT] >= 0) - box->margin[RIGHT] = rm; - - /* Find width */ - if (width == AUTO) { - int margin_left = box->margin[LEFT]; - int margin_right = box->margin[RIGHT]; - - if (margin_left == AUTO) { - margin_left = lm; - } - if (margin_right == AUTO) { - margin_right = rm; - } - - width = available_width - - (margin_left + box->border[LEFT].width + - box->padding[LEFT] + box->padding[RIGHT] + - box->border[RIGHT].width + margin_right); - width = width < 0 ? 0 : width; - auto_width = true; - } - - if (max_width >= 0 && width > max_width) { - /* max-width is admissable and width exceeds max-width */ - width = max_width; - auto_width = false; - } - - if (min_width > 0 && width < min_width) { - /* min-width is admissable and width is less than max-width */ - width = min_width; - auto_width = false; - } - - /* Width was auto, and unconstrained by min/max width, so we're done */ - if (auto_width) { - /* any other 'auto' become 0 or the minimum required values */ - if (box->margin[LEFT] == AUTO) { - box->margin[LEFT] = lm; - } - if (box->margin[RIGHT] == AUTO) { - box->margin[RIGHT] = rm; - } - return width; - } - - /* Width was not auto, or was constrained by min/max width - * Need to compute left/right margins */ - - /* HTML alignment (only applies to over-constrained boxes) */ - if (box->margin[LEFT] != AUTO && box->margin[RIGHT] != AUTO && - box->parent != NULL && box->parent->style != NULL) { - switch (css_computed_text_align(box->parent->style)) { - case CSS_TEXT_ALIGN_LIBCSS_RIGHT: - box->margin[LEFT] = AUTO; - box->margin[RIGHT] = 0; - break; - case CSS_TEXT_ALIGN_LIBCSS_CENTER: - box->margin[LEFT] = box->margin[RIGHT] = AUTO; - break; - case CSS_TEXT_ALIGN_LIBCSS_LEFT: - box->margin[LEFT] = 0; - box->margin[RIGHT] = AUTO; - break; - default: - /* Leave it alone; no HTML alignment */ - break; - } - } - - if (box->margin[LEFT] == AUTO && box->margin[RIGHT] == AUTO) { - /* make the margins equal, centering the element */ - box->margin[LEFT] = box->margin[RIGHT] = - (available_width - lm - rm - - (box->border[LEFT].width + box->padding[LEFT] + - width + box->padding[RIGHT] + - box->border[RIGHT].width)) / 2; - - if (box->margin[LEFT] < 0) { - box->margin[RIGHT] += box->margin[LEFT]; - box->margin[LEFT] = 0; - } - - box->margin[LEFT] += lm; - - } else if (box->margin[LEFT] == AUTO) { - box->margin[LEFT] = available_width - lm - - (box->border[LEFT].width + box->padding[LEFT] + - width + box->padding[RIGHT] + - box->border[RIGHT].width + box->margin[RIGHT]); - box->margin[LEFT] = box->margin[LEFT] < lm - ? lm : box->margin[LEFT]; - } else { - /* margin-right auto or "over-constrained" */ - box->margin[RIGHT] = available_width - rm - - (box->margin[LEFT] + box->border[LEFT].width + - box->padding[LEFT] + width + - box->padding[RIGHT] + - box->border[RIGHT].width); - } - - return width; -} - - -/** - * Compute dimensions of box, margins, paddings, and borders for a block-level - * element. - * - * \param len_ctx Length conversion context - * \param available_width Max width available in pixels - * \param viewport_height Height of viewport in pixels or -ve if unknown - * \param lm min left margin required to avoid floats in px. - * zero if not applicable - * \param rm min right margin required to avoid floats in px. - * zero if not applicable - * \param box box to find dimensions of. updated with new width, - * height, margins, borders and paddings - * - * See CSS 2.1 10.3.3, 10.3.4, 10.6.2, and 10.6.3. - */ -static void -layout_block_find_dimensions(const nscss_len_ctx *len_ctx, - int available_width, - int viewport_height, - int lm, - int rm, - struct box *box) -{ - int width, max_width, min_width; - int height, max_height, min_height; - int *margin = box->margin; - int *padding = box->padding; - struct box_border *border = box->border; - const css_computed_style *style = box->style; - - layout_find_dimensions(len_ctx, available_width, viewport_height, box, - style, &width, &height, &max_width, &min_width, - &max_height, &min_height, margin, padding, border); - - if (box->object && !(box->flags & REPLACE_DIM) && - content_get_type(box->object) != CONTENT_HTML) { - /* block-level replaced element, see 10.3.4 and 10.6.2 */ - layout_get_object_dimensions(box, &width, &height, - min_width, max_width, min_height, max_height); - } - - box->width = layout_solve_width(box, available_width, width, lm, rm, - max_width, min_width); - box->height = height; - - if (margin[TOP] == AUTO) - margin[TOP] = 0; - if (margin[BOTTOM] == AUTO) - margin[BOTTOM] = 0; -} - - -/** - * Manipulate a block's [RB]padding/height/width to accommodate scrollbars - * - * \param box Box to apply scrollbar space too. Must be BOX_BLOCK. - * \param which Which scrollbar to make space for. Must be RIGHT or BOTTOM. - */ -static void layout_block_add_scrollbar(struct box *box, int which) -{ - enum css_overflow_e overflow_x, overflow_y; - - assert(box->type == BOX_BLOCK && (which == RIGHT || which == BOTTOM)); - - if (box->style == NULL) - return; - - overflow_x = css_computed_overflow_x(box->style); - overflow_y = css_computed_overflow_y(box->style); - - if (which == BOTTOM && - (overflow_x == CSS_OVERFLOW_SCROLL || - overflow_x == CSS_OVERFLOW_AUTO || - (box->object && - content_get_type(box->object) == CONTENT_HTML))) { - /* make space for scrollbar, unless height is AUTO */ - if (box->height != AUTO && - (overflow_x == CSS_OVERFLOW_SCROLL || - box_hscrollbar_present(box))) { - box->padding[BOTTOM] += SCROLLBAR_WIDTH; - } - - } else if (which == RIGHT && - (overflow_y == CSS_OVERFLOW_SCROLL || - overflow_y == CSS_OVERFLOW_AUTO || - (box->object && - content_get_type(box->object) == CONTENT_HTML))) { - /* make space for scrollbars, unless width is AUTO */ - enum css_height_e htype; - css_fixed height = 0; - css_unit hunit = CSS_UNIT_PX; - htype = css_computed_height(box->style, &height, &hunit); - - if (which == RIGHT && box->width != AUTO && - htype == CSS_HEIGHT_SET && - (overflow_y == CSS_OVERFLOW_SCROLL || - box_vscrollbar_present(box))) { - box->width -= SCROLLBAR_WIDTH; - box->padding[RIGHT] += SCROLLBAR_WIDTH; - } - } -} - - -/** - * Moves the children of a box by a specified amount - * - * \param box top of tree of boxes - * \param x the amount to move children by horizontally - * \param y the amount to move children by vertically - */ -static void layout_move_children(struct box *box, int x, int y) -{ - assert(box); - - for (box = box->children; box; box = box->next) { - box->x += x; - box->y += y; - } -} - - -/** - * Layout a table. - * - * \param table table to layout - * \param available_width width of containing block - * \param content memory pool for any new boxes - * \return true on success, false on memory exhaustion - */ -static bool layout_table(struct box *table, int available_width, - html_content *content) -{ - unsigned int columns = table->columns; /* total columns */ - unsigned int i; - unsigned int *row_span; - int *excess_y; - int table_width, min_width = 0, max_width = 0; - int required_width = 0; - int x, remainder = 0, count = 0; - int table_height = 0; - int min_height = 0; - int *xs; /* array of column x positions */ - int auto_width; - int spare_width; - int relative_sum = 0; - int border_spacing_h = 0, border_spacing_v = 0; - int spare_height; - int positioned_columns = 0; - struct box *containing_block = NULL; - struct box *c; - struct box *row; - struct box *row_group; - struct box **row_span_cell; - struct column *col; - const css_computed_style *style = table->style; - enum css_width_e wtype; - enum css_height_e htype; - css_fixed value = 0; - css_unit unit = CSS_UNIT_PX; - - assert(table->type == BOX_TABLE); - assert(style); - assert(table->children && table->children->children); - assert(columns); - - /* allocate working buffers */ - col = malloc(columns * sizeof col[0]); - excess_y = malloc(columns * sizeof excess_y[0]); - row_span = malloc(columns * sizeof row_span[0]); - row_span_cell = malloc(columns * sizeof row_span_cell[0]); - xs = malloc((columns + 1) * sizeof xs[0]); - if (!col || !xs || !row_span || !excess_y || !row_span_cell) { - free(col); - free(excess_y); - free(row_span); - free(row_span_cell); - free(xs); - return false; - } - - memcpy(col, table->col, sizeof(col[0]) * columns); - - /* find margins, paddings, and borders for table and cells */ - layout_find_dimensions(&content->len_ctx, available_width, -1, table, - style, 0, 0, 0, 0, 0, 0, table->margin, table->padding, - table->border); - for (row_group = table->children; row_group; - row_group = row_group->next) { - for (row = row_group->children; row; row = row->next) { - for (c = row->children; c; c = c->next) { - enum css_overflow_e overflow_x; - enum css_overflow_e overflow_y; - - assert(c->style); - table_used_border_for_cell( - &content->len_ctx, c); - layout_find_dimensions(&content->len_ctx, - available_width, -1, c, - c->style, 0, 0, 0, 0, 0, 0, - 0, c->padding, c->border); - - overflow_x = css_computed_overflow_x(c->style); - overflow_y = css_computed_overflow_y(c->style); - - if (overflow_x == CSS_OVERFLOW_SCROLL || - overflow_x == - CSS_OVERFLOW_AUTO) { - c->padding[BOTTOM] += SCROLLBAR_WIDTH; - } - if (overflow_y == CSS_OVERFLOW_SCROLL || - overflow_y == - CSS_OVERFLOW_AUTO) { - c->padding[RIGHT] += SCROLLBAR_WIDTH; - } - } - } - } - - /* border-spacing is used in the separated borders model */ - if (css_computed_border_collapse(style) == - CSS_BORDER_COLLAPSE_SEPARATE) { - css_fixed h = 0, v = 0; - css_unit hu = CSS_UNIT_PX, vu = CSS_UNIT_PX; - - css_computed_border_spacing(style, &h, &hu, &v, &vu); - - border_spacing_h = FIXTOINT(nscss_len2px(&content->len_ctx, - h, hu, style)); - border_spacing_v = FIXTOINT(nscss_len2px(&content->len_ctx, - v, vu, style)); - } - - /* find specified table width, or available width if auto-width */ - wtype = css_computed_width(style, &value, &unit); - if (wtype == CSS_WIDTH_SET) { - if (unit == CSS_UNIT_PCT) { - table_width = FPCT_OF_INT_TOINT(value, available_width); - } else { - table_width = - FIXTOINT(nscss_len2px(&content->len_ctx, - value, unit, style)); - } - - /* specified width includes border */ - table_width -= table->border[LEFT].width + - table->border[RIGHT].width; - table_width = table_width < 0 ? 0 : table_width; - - auto_width = table_width; - } else { - table_width = AUTO; - auto_width = available_width - - ((table->margin[LEFT] == AUTO ? 0 : - table->margin[LEFT]) + - table->border[LEFT].width + - table->padding[LEFT] + - table->padding[RIGHT] + - table->border[RIGHT].width + - (table->margin[RIGHT] == AUTO ? 0 : - table->margin[RIGHT])); - } - - /* Find any table height specified within CSS/HTML */ - htype = css_computed_height(style, &value, &unit); - if (htype == CSS_HEIGHT_SET) { - if (unit == CSS_UNIT_PCT) { - /* This is the minimum height for the table - * (see 17.5.3) */ - if (css_computed_position(table->style) == - CSS_POSITION_ABSOLUTE) { - /* Table is absolutely positioned */ - assert(table->float_container); - containing_block = table->float_container; - } else if (table->float_container && - css_computed_position(table->style) != - CSS_POSITION_ABSOLUTE && - (css_computed_float(table->style) == - CSS_FLOAT_LEFT || - css_computed_float(table->style) == - CSS_FLOAT_RIGHT)) { - /* Table is a float */ - assert(table->parent && table->parent->parent && - table->parent->parent->parent); - containing_block = - table->parent->parent->parent; - } else if (table->parent && table->parent->type != - BOX_INLINE_CONTAINER) { - /* Table is a block level element */ - containing_block = table->parent; - } else if (table->parent && table->parent->type == - BOX_INLINE_CONTAINER) { - /* Table is an inline block */ - assert(table->parent->parent); - containing_block = table->parent->parent; - } - - if (containing_block) { - css_fixed ignored = 0; - - htype = css_computed_height( - containing_block->style, - &ignored, &unit); - } - - if (containing_block && - containing_block->height != AUTO && - (css_computed_position(table->style) == - CSS_POSITION_ABSOLUTE || - htype == CSS_HEIGHT_SET)) { - /* Table is absolutely positioned or its - * containing block has a valid specified - * height. (CSS 2.1 Section 10.5) */ - min_height = FPCT_OF_INT_TOINT(value, - containing_block->height); - } - } else { - /* This is the minimum height for the table - * (see 17.5.3) */ - min_height = FIXTOINT(nscss_len2px(&content->len_ctx, - value, unit, style)); - } - } - - /* calculate width required by cells */ - for (i = 0; i != columns; i++) { - - NSLOG(layout, DEBUG, - "table %p, column %u: type %s, width %i, min %i, max %i", - table, - i, - ((const char *[]){ - "UNKNOWN", - "FIXED", - "AUTO", - "PERCENT", - "RELATIVE", - })[col[i].type], - col[i].width, - col[i].min, - col[i].max); - - - if (col[i].positioned) { - positioned_columns++; - continue; - } else if (col[i].type == COLUMN_WIDTH_FIXED) { - if (col[i].width < col[i].min) - col[i].width = col[i].max = col[i].min; - else - col[i].min = col[i].max = col[i].width; - required_width += col[i].width; - } else if (col[i].type == COLUMN_WIDTH_PERCENT) { - int width = col[i].width * auto_width / 100; - required_width += col[i].min < width ? width : - col[i].min; - } else - required_width += col[i].min; - - NSLOG(layout, DEBUG, "required_width %i", required_width); - } - required_width += (columns + 1 - positioned_columns) * - border_spacing_h; - - NSLOG(layout, DEBUG, - "width %i, min %i, max %i, auto %i, required %i", table_width, - table->min_width, table->max_width, auto_width, required_width); - - if (auto_width < required_width) { - /* table narrower than required width for columns: - * treat percentage widths as maximums */ - for (i = 0; i != columns; i++) { - if (col[i].type == COLUMN_WIDTH_RELATIVE) - continue; - if (col[i].type == COLUMN_WIDTH_PERCENT) { - col[i].max = auto_width * col[i].width / 100; - if (col[i].max < col[i].min) - col[i].max = col[i].min; - } - min_width += col[i].min; - max_width += col[i].max; - } - } else { - /* take percentages exactly */ - for (i = 0; i != columns; i++) { - if (col[i].type == COLUMN_WIDTH_RELATIVE) - continue; - if (col[i].type == COLUMN_WIDTH_PERCENT) { - int width = auto_width * col[i].width / 100; - if (width < col[i].min) - width = col[i].min; - col[i].min = col[i].width = col[i].max = width; - col[i].type = COLUMN_WIDTH_FIXED; - } - min_width += col[i].min; - max_width += col[i].max; - } - } - - /* allocate relative widths */ - spare_width = auto_width; - for (i = 0; i != columns; i++) { - if (col[i].type == COLUMN_WIDTH_RELATIVE) - relative_sum += col[i].width; - else if (col[i].type == COLUMN_WIDTH_FIXED) - spare_width -= col[i].width; - else - spare_width -= col[i].min; - } - spare_width -= (columns + 1) * border_spacing_h; - if (relative_sum != 0) { - if (spare_width < 0) - spare_width = 0; - for (i = 0; i != columns; i++) { - if (col[i].type == COLUMN_WIDTH_RELATIVE) { - col[i].min = ceil(col[i].max = - (float) spare_width - * (float) col[i].width - / relative_sum); - min_width += col[i].min; - max_width += col[i].max; - } - } - } - min_width += (columns + 1) * border_spacing_h; - max_width += (columns + 1) * border_spacing_h; - - if (auto_width <= min_width) { - /* not enough space: minimise column widths */ - for (i = 0; i < columns; i++) { - col[i].width = col[i].min; - } - table_width = min_width; - } else if (max_width <= auto_width) { - /* more space than maximum width */ - if (table_width == AUTO) { - /* for auto-width tables, make columns max width */ - for (i = 0; i < columns; i++) { - col[i].width = col[i].max; - } - table_width = max_width; - } else { - /* for fixed-width tables, distribute the extra space - * too */ - unsigned int flexible_columns = 0; - for (i = 0; i != columns; i++) - if (col[i].type != COLUMN_WIDTH_FIXED) - flexible_columns++; - if (flexible_columns == 0) { - int extra = (table_width - max_width) / columns; - remainder = (table_width - max_width) - - (extra * columns); - for (i = 0; i != columns; i++) { - col[i].width = col[i].max + extra; - count -= remainder; - if (count < 0) { - col[i].width++; - count += columns; - } - } - - } else { - int extra = (table_width - max_width) / - flexible_columns; - remainder = (table_width - max_width) - - (extra * flexible_columns); - for (i = 0; i != columns; i++) - if (col[i].type != COLUMN_WIDTH_FIXED) { - col[i].width = col[i].max + - extra; - count -= remainder; - if (count < 0) { - col[i].width++; - count += flexible_columns; - } - } - } - } - } else { - /* space between min and max: fill it exactly */ - float scale = (float) (auto_width - min_width) / - (float) (max_width - min_width); - /* fprintf(stderr, "filling, scale %f\n", scale); */ - for (i = 0; i < columns; i++) { - col[i].width = col[i].min + (int) (0.5 + - (col[i].max - col[i].min) * scale); - } - table_width = auto_width; - } - - xs[0] = x = border_spacing_h; - for (i = 0; i != columns; i++) { - if (!col[i].positioned) - x += col[i].width + border_spacing_h; - xs[i + 1] = x; - row_span[i] = 0; - excess_y[i] = 0; - row_span_cell[i] = 0; - } - - /* position cells */ - table_height = border_spacing_v; - for (row_group = table->children; row_group; - row_group = row_group->next) { - int row_group_height = 0; - for (row = row_group->children; row; row = row->next) { - int row_height = 0; - - htype = css_computed_height(row->style, &value, &unit); - if (htype == CSS_HEIGHT_SET && unit != CSS_UNIT_PCT) { - row_height = FIXTOINT(nscss_len2px( - &content->len_ctx, - value, unit, row->style)); - } - for (c = row->children; c; c = c->next) { - assert(c->style); - c->width = xs[c->start_column + c->columns] - - xs[c->start_column] - - border_spacing_h - - c->border[LEFT].width - - c->padding[LEFT] - - c->padding[RIGHT] - - c->border[RIGHT].width; - c->float_children = 0; - c->cached_place_below_level = 0; - - c->height = AUTO; - if (!layout_block_context(c, -1, content)) { - free(col); - free(excess_y); - free(row_span); - free(row_span_cell); - free(xs); - return false; - } - /* warning: c->descendant_y0 and - * c->descendant_y1 used as temporary storage - * until after vertical alignment is complete */ - c->descendant_y0 = c->height; - c->descendant_y1 = c->padding[BOTTOM]; - - htype = css_computed_height(c->style, - &value, &unit); - - if (htype == CSS_HEIGHT_SET && - unit != CSS_UNIT_PCT) { - /* some sites use height="1" or similar - * to attempt to make cells as small as - * possible, so treat it as a minimum */ - int h = FIXTOINT(nscss_len2px( - &content->len_ctx, - value, unit, c->style)); - if (c->height < h) - c->height = h; - } - /* specified row height is treated as a minimum - */ - if (c->height < row_height) - c->height = row_height; - c->x = xs[c->start_column] + - c->border[LEFT].width; - c->y = c->border[TOP].width; - for (i = 0; i != c->columns; i++) { - row_span[c->start_column + i] = c->rows; - excess_y[c->start_column + i] = - c->border[TOP].width + - c->padding[TOP] + - c->height + - c->padding[BOTTOM] + - c->border[BOTTOM].width; - row_span_cell[c->start_column + i] = 0; - } - row_span_cell[c->start_column] = c; - c->padding[BOTTOM] = -border_spacing_v - - c->border[TOP].width - - c->padding[TOP] - - c->height - - c->border[BOTTOM].width; - } - for (i = 0; i != columns; i++) - if (row_span[i] != 0) - row_span[i]--; - else - row_span_cell[i] = 0; - if (row->next || row_group->next) { - /* row height is greatest excess of a cell - * which ends in this row */ - for (i = 0; i != columns; i++) - if (row_span[i] == 0 && row_height < - excess_y[i]) - row_height = excess_y[i]; - } else { - /* except in the last row */ - for (i = 0; i != columns; i++) - if (row_height < excess_y[i]) - row_height = excess_y[i]; - } - for (i = 0; i != columns; i++) { - if (row_height < excess_y[i]) - excess_y[i] -= row_height; - else - excess_y[i] = 0; - if (row_span_cell[i] != 0) - row_span_cell[i]->padding[BOTTOM] += - row_height + - border_spacing_v; - } - - row->x = 0; - row->y = row_group_height; - row->width = table_width; - row->height = row_height; - row_group_height += row_height + border_spacing_v; - } - row_group->x = 0; - row_group->y = table_height; - row_group->width = table_width; - row_group->height = row_group_height; - table_height += row_group_height; - } - /* Table height is either the height of the contents, or specified - * height if greater */ - table_height = max(table_height, min_height); - /** \todo distribute spare height over the row groups / rows / cells */ - - /* perform vertical alignment */ - for (row_group = table->children; row_group; - row_group = row_group->next) { - for (row = row_group->children; row; row = row->next) { - for (c = row->children; c; c = c->next) { - enum css_vertical_align_e vertical_align; - - /* unextended bottom padding is in - * c->descendant_y1, and unextended - * cell height is in c->descendant_y0 */ - spare_height = (c->padding[BOTTOM] - - c->descendant_y1) + - (c->height - c->descendant_y0); - - vertical_align = css_computed_vertical_align( - c->style, &value, &unit); - - switch (vertical_align) { - case CSS_VERTICAL_ALIGN_SUB: - case CSS_VERTICAL_ALIGN_SUPER: - case CSS_VERTICAL_ALIGN_TEXT_TOP: - case CSS_VERTICAL_ALIGN_TEXT_BOTTOM: - case CSS_VERTICAL_ALIGN_SET: - case CSS_VERTICAL_ALIGN_BASELINE: - /* todo: baseline alignment, for now - * just use ALIGN_TOP */ - case CSS_VERTICAL_ALIGN_TOP: - break; - case CSS_VERTICAL_ALIGN_MIDDLE: - c->padding[TOP] += spare_height / 2; - c->padding[BOTTOM] -= spare_height / 2; - layout_move_children(c, 0, - spare_height / 2); - break; - case CSS_VERTICAL_ALIGN_BOTTOM: - c->padding[TOP] += spare_height; - c->padding[BOTTOM] -= spare_height; - layout_move_children(c, 0, - spare_height); - break; - case CSS_VERTICAL_ALIGN_INHERIT: - assert(0); - break; - } - } - } - } - - /* Top and bottom margins of 'auto' are set to 0. CSS2.1 10.6.3 */ - if (table->margin[TOP] == AUTO) - table->margin[TOP] = 0; - if (table->margin[BOTTOM] == AUTO) - table->margin[BOTTOM] = 0; - - free(col); - free(excess_y); - free(row_span); - free(row_span_cell); - free(xs); - - table->width = table_width; - table->height = table_height; - - return true; -} - - -/** - * Manimpulate box height according to CSS min-height and max-height properties - * - * \param len_ctx CSS length conversion context for document. - * \param box block to modify with any min-height or max-height - * \param container containing block for absolutely positioned elements, or - * NULL for non absolutely positioned elements. - * \return whether the height has been changed - */ -static bool layout_apply_minmax_height( - const nscss_len_ctx *len_ctx, - struct box *box, - struct box *container) -{ - int h; - struct box *containing_block = NULL; - bool updated = false; - - /* Find containing block for percentage heights */ - if (box->style != NULL && css_computed_position(box->style) == - CSS_POSITION_ABSOLUTE) { - /* Box is absolutely positioned */ - assert(container); - containing_block = container; - } else if (box->float_container && box->style != NULL && - (css_computed_float(box->style) == CSS_FLOAT_LEFT || - css_computed_float(box->style) == CSS_FLOAT_RIGHT)) { - /* Box is a float */ - assert(box->parent && box->parent->parent && - box->parent->parent->parent); - containing_block = box->parent->parent->parent; - } else if (box->parent && box->parent->type != BOX_INLINE_CONTAINER) { - /* Box is a block level element */ - containing_block = box->parent; - } else if (box->parent && box->parent->type == BOX_INLINE_CONTAINER) { - /* Box is an inline block */ - assert(box->parent->parent); - containing_block = box->parent->parent; - } - - if (box->style) { - enum css_height_e htype = CSS_HEIGHT_AUTO; - css_fixed value = 0; - css_unit unit = CSS_UNIT_PX; - - if (containing_block) { - htype = css_computed_height(containing_block->style, - &value, &unit); - } - - /* max-height */ - if (css_computed_max_height(box->style, &value, &unit) == - CSS_MAX_HEIGHT_SET) { - if (unit == CSS_UNIT_PCT) { - if (containing_block && - containing_block->height != AUTO && - (css_computed_position(box->style) == - CSS_POSITION_ABSOLUTE || - htype == CSS_HEIGHT_SET)) { - /* Box is absolutely positioned or its - * containing block has a valid - * specified height. (CSS 2.1 - * Section 10.5) */ - h = FPCT_OF_INT_TOINT(value, - containing_block->height); - if (h < box->height) { - box->height = h; - updated = true; - } - } - } else { - h = FIXTOINT(nscss_len2px(len_ctx, - value, unit, box->style)); - if (h < box->height) { - box->height = h; - updated = true; - } - } - } - - /* min-height */ - if (ns_computed_min_height(box->style, &value, &unit) == - CSS_MIN_HEIGHT_SET) { - if (unit == CSS_UNIT_PCT) { - if (containing_block && - containing_block->height != AUTO && - (css_computed_position(box->style) == - CSS_POSITION_ABSOLUTE || - htype == CSS_HEIGHT_SET)) { - /* Box is absolutely positioned or its - * containing block has a valid - * specified height. (CSS 2.1 - * Section 10.5) */ - h = FPCT_OF_INT_TOINT(value, - containing_block->height); - if (h > box->height) { - box->height = h; - updated = true; - } - } - } else { - h = FIXTOINT(nscss_len2px(len_ctx, - value, unit, box->style)); - if (h > box->height) { - box->height = h; - updated = true; - } - } - } - } - return updated; -} - - -/** - * Layout a block which contains an object. - * - * \param block box of type BLOCK, INLINE_BLOCK, TABLE, or TABLE_CELL - * \return true on success, false on memory exhaustion - */ -static bool layout_block_object(struct box *block) -{ - assert(block); - assert(block->type == BOX_BLOCK || - block->type == BOX_INLINE_BLOCK || - block->type == BOX_TABLE || - block->type == BOX_TABLE_CELL); - assert(block->object); - - NSLOG(layout, DEBUG, "block %p, object %p, width %i", block, - hlcache_handle_get_url(block->object), block->width); - - if (content_get_type(block->object) == CONTENT_HTML) { - content_reformat(block->object, false, block->width, 1); - } else { - /* Non-HTML objects */ - /* this case handled already in - * layout_block_find_dimensions() */ - } - - return true; -} - - -/** - * Insert a float into a container. - * - * \param cont block formatting context block, used to contain float - * \param b box to add to float - * - * This sorts floats in order of descending bottom edges. - */ -static void add_float_to_container(struct box *cont, struct box *b) -{ - struct box *box = cont->float_children; - int b_bottom = b->y + b->height; - - assert(b->type == BOX_FLOAT_LEFT || b->type == BOX_FLOAT_RIGHT); - - if (box == NULL) { - /* No other float children */ - b->next_float = NULL; - cont->float_children = b; - return; - } else if (b_bottom >= box->y + box->height) { - /* Goes at start of list */ - b->next_float = cont->float_children; - cont->float_children = b; - } else { - struct box *prev = NULL; - while (box != NULL && b_bottom < box->y + box->height) { - prev = box; - box = box->next_float; - } - if (prev != NULL) { - b->next_float = prev->next_float; - prev->next_float = b; - } - } -} - - -/** - * Split a text box. - * - * \param content memory pool for any new boxes - * \param fstyle style for text in text box - * \param split_box box with text to split - * \param new_length new length for text in split_box, after splitting - * \param new_width new width for text in split_box, after splitting - * \return true on success, false on memory exhaustion - * - * A new box is created and inserted into the box tree after split_box, - * containing the text after new_length excluding the initial space character. - */ -static bool -layout_text_box_split(html_content *content, - plot_font_style_t *fstyle, - struct box *split_box, - size_t new_length, - int new_width) -{ - int space_width = split_box->space; - struct box *c2; - const struct gui_layout_table *font_func = content->font_func; - bool space = (split_box->text[new_length] == ' '); - int used_length = new_length + (space ? 1 : 0); - - if ((space && space_width == 0) || space_width == UNKNOWN_WIDTH) { - /* We're need to add a space, and we don't know how big - * it's to be, OR we have a space of unknown width anyway; - * Calculate space width */ - font_func->width(fstyle, " ", 1, &space_width); - } - - if (split_box->space == UNKNOWN_WIDTH) - split_box->space = space_width; - if (!space) - space_width = 0; - - /* Create clone of split_box, c2 */ - c2 = talloc_memdup(content->bctx, split_box, sizeof *c2); - if (!c2) - return false; - c2->flags |= CLONE; - - /* Set remaining text in c2 */ - c2->text += used_length; - - /* Set c2 according to the remaining text */ - c2->width -= new_width + space_width; - c2->flags &= ~MEASURED; /* width has been estimated */ - c2->length = split_box->length - used_length; - - /* Update split_box for its reduced text */ - split_box->width = new_width; - split_box->flags |= MEASURED; - split_box->length = new_length; - split_box->space = space_width; - - /* Insert c2 into box list */ - c2->next = split_box->next; - split_box->next = c2; - c2->prev = split_box; - if (c2->next) - c2->next->prev = c2; - else - c2->parent->last = c2; - - NSLOG(layout, DEBUG, - "split_box %p len: %" PRIsizet " \"%.*s\"", - split_box, - split_box->length, - (int)split_box->length, - split_box->text); - NSLOG(layout, DEBUG, - " new_box %p len: %" PRIsizet " \"%.*s\"", - c2, - c2->length, - (int)c2->length, - c2->text); - - return true; -} - - -/** - * Compute dimensions of box, margins, paddings, and borders for a floating - * element using shrink-to-fit. Also used for inline-blocks. - * - * \param len_ctx CSS length conversion context for document. - * \param available_width Max width available in pixels - * \param style Box's style - * \param box Box for which to find dimensions - * Box margins, borders, paddings, width and - * height are updated. - */ -static void -layout_float_find_dimensions( - const nscss_len_ctx *len_ctx, - int available_width, - const css_computed_style *style, - struct box *box) -{ - int width, height, max_width, min_width, max_height, min_height; - int *margin = box->margin; - int *padding = box->padding; - struct box_border *border = box->border; - enum css_overflow_e overflow_x = css_computed_overflow_x(style); - enum css_overflow_e overflow_y = css_computed_overflow_y(style); - int scrollbar_width_x = - (overflow_x == CSS_OVERFLOW_SCROLL || - overflow_x == CSS_OVERFLOW_AUTO) ? - SCROLLBAR_WIDTH : 0; - int scrollbar_width_y = - (overflow_y == CSS_OVERFLOW_SCROLL || - overflow_y == CSS_OVERFLOW_AUTO) ? - SCROLLBAR_WIDTH : 0; - - layout_find_dimensions(len_ctx, available_width, -1, box, style, - &width, &height, &max_width, &min_width, - &max_height, &min_height, margin, padding, border); - - if (margin[LEFT] == AUTO) - margin[LEFT] = 0; - if (margin[RIGHT] == AUTO) - margin[RIGHT] = 0; - - if (box->gadget == NULL) { - padding[RIGHT] += scrollbar_width_y; - padding[BOTTOM] += scrollbar_width_x; - } - - if (box->object && !(box->flags & REPLACE_DIM) && - content_get_type(box->object) != CONTENT_HTML) { - /* Floating replaced element, with intrinsic width or height. - * See 10.3.6 and 10.6.2 */ - layout_get_object_dimensions(box, &width, &height, - min_width, max_width, min_height, max_height); - } else if (box->gadget && (box->gadget->type == GADGET_TEXTBOX || - box->gadget->type == GADGET_PASSWORD || - box->gadget->type == GADGET_FILE || - box->gadget->type == GADGET_TEXTAREA)) { - css_fixed size = 0; - css_unit unit = CSS_UNIT_EM; - - /* Give sensible dimensions to gadgets, with auto width/height, - * that don't shrink to fit contained text. */ - assert(box->style); - - if (box->gadget->type == GADGET_TEXTBOX || - box->gadget->type == GADGET_PASSWORD || - box->gadget->type == GADGET_FILE) { - if (width == AUTO) { - size = INTTOFIX(10); - width = FIXTOINT(nscss_len2px(len_ctx, - size, unit, box->style)); - } - if (box->gadget->type == GADGET_FILE && - height == AUTO) { - size = FLTTOFIX(1.5); - height = FIXTOINT(nscss_len2px(len_ctx, - size, unit, box->style)); - } - } - if (box->gadget->type == GADGET_TEXTAREA) { - if (width == AUTO) { - size = INTTOFIX(10); - width = FIXTOINT(nscss_len2px(len_ctx, - size, unit, box->style)); - } - if (height == AUTO) { - size = INTTOFIX(4); - height = FIXTOINT(nscss_len2px(len_ctx, - size, unit, box->style)); - } - } - } else if (width == AUTO) { - /* CSS 2.1 section 10.3.5 */ - width = min(max(box->min_width, available_width), - box->max_width); - - /* width includes margin, borders and padding */ - if (width == available_width) { - width -= box->margin[LEFT] + box->border[LEFT].width + - box->padding[LEFT] + - box->padding[RIGHT] + - box->border[RIGHT].width + - box->margin[RIGHT]; - } else { - /* width was obtained from a min_width or max_width - * value, so need to use the same method for calculating - * mbp as was used in layout_minmax_block() */ - int fixed = 0; - float frac = 0; - calculate_mbp_width(len_ctx, box->style, LEFT, - true, true, true, &fixed, &frac); - calculate_mbp_width(len_ctx, box->style, RIGHT, - true, true, true, &fixed, &frac); - if (fixed < 0) - fixed = 0; - - width -= fixed; - } - - if (max_width >= 0 && width > max_width) width = max_width; - if (min_width > 0 && width < min_width) width = min_width; - - } else { - if (max_width >= 0 && width > max_width) width = max_width; - if (min_width > 0 && width < min_width) width = min_width; - width -= scrollbar_width_y; - } - - box->width = width; - box->height = height; - - if (margin[TOP] == AUTO) - margin[TOP] = 0; - if (margin[BOTTOM] == AUTO) - margin[BOTTOM] = 0; -} - - -/** - * Layout the contents of a float or inline block. - * - * \param b float or inline block box - * \param width available width - * \param content memory pool for any new boxes - * \return true on success, false on memory exhaustion - */ -static bool layout_float(struct box *b, int width, html_content *content) -{ - assert(b->type == BOX_TABLE || b->type == BOX_BLOCK || - b->type == BOX_INLINE_BLOCK); - layout_float_find_dimensions(&content->len_ctx, width, b->style, b); - if (b->type == BOX_TABLE) { - if (!layout_table(b, width, content)) - return false; - if (b->margin[LEFT] == AUTO) - b->margin[LEFT] = 0; - if (b->margin[RIGHT] == AUTO) - b->margin[RIGHT] = 0; - if (b->margin[TOP] == AUTO) - b->margin[TOP] = 0; - if (b->margin[BOTTOM] == AUTO) - b->margin[BOTTOM] = 0; - } else - return layout_block_context(b, -1, content); - return true; -} - - -/** - * Position a float in the first available space. - * - * \param c float box to position - * \param width available width - * \param cx x coordinate relative to cont to place float right of - * \param y y coordinate relative to cont to place float below - * \param cont ancestor box which defines horizontal space, for floats - */ -static void -place_float_below(struct box *c, int width, int cx, int y, struct box *cont) -{ - int x0, x1, yy; - struct box *left; - struct box *right; - - yy = y > cont->cached_place_below_level ? - y : cont->cached_place_below_level; - - NSLOG(layout, DEBUG, - "c %p, width %i, cx %i, y %i, cont %p", c, - width, cx, y, cont); - - do { - y = yy; - x0 = cx; - x1 = cx + width; - find_sides(cont->float_children, y, y + c->height, &x0, &x1, - &left, &right); - if (left != 0 && right != 0) { - yy = (left->y + left->height < - right->y + right->height ? - left->y + left->height : - right->y + right->height); - } else if (left == 0 && right != 0) { - yy = right->y + right->height; - } else if (left != 0 && right == 0) { - yy = left->y + left->height; - } - } while ((left != 0 || right != 0) && (c->width > x1 - x0)); - - if (c->type == BOX_FLOAT_LEFT) { - c->x = x0; - } else { - c->x = x1 - c->width; - } - c->y = y; - cont->cached_place_below_level = y; -} - - -/** - * Calculate line height from a style. - */ -static int line_height( - const nscss_len_ctx *len_ctx, - const css_computed_style *style) -{ - enum css_line_height_e lhtype; - css_fixed lhvalue = 0; - css_unit lhunit = CSS_UNIT_PX; - css_fixed line_height; - - assert(style); - - lhtype = css_computed_line_height(style, &lhvalue, &lhunit); - if (lhtype == CSS_LINE_HEIGHT_NORMAL) { - /* Normal => use a constant of 1.3 * font-size */ - lhvalue = FLTTOFIX(1.3); - lhtype = CSS_LINE_HEIGHT_NUMBER; - } - - if (lhtype == CSS_LINE_HEIGHT_NUMBER || - lhunit == CSS_UNIT_PCT) { - line_height = nscss_len2px(len_ctx, - lhvalue, CSS_UNIT_EM, style); - - if (lhtype != CSS_LINE_HEIGHT_NUMBER) - line_height = FDIV(line_height, F_100); - } else { - assert(lhunit != CSS_UNIT_PCT); - - line_height = nscss_len2px(len_ctx, - lhvalue, lhunit, style); - } - - return FIXTOINT(line_height); -} - - -/** - * Position a line of boxes in inline formatting context. - * - * \param first box at start of line - * \param width available width on input, updated with actual width on output - * (may be incorrect if the line gets split?) - * \param y coordinate of top of line, updated on exit to bottom - * \param cx coordinate of left of line relative to cont - * \param cy coordinate of top of line relative to cont - * \param cont ancestor box which defines horizontal space, for floats - * \param indent apply any first-line indent - * \param has_text_children at least one TEXT in the inline_container - * \param next_box updated to first box for next line, or 0 at end - * \param content memory pool for any new boxes - * \return true on success, false on memory exhaustion - */ -static bool -layout_line(struct box *first, - int *width, - int *y, - int cx, - int cy, - struct box *cont, - bool indent, - bool has_text_children, - html_content *content, - struct box **next_box) -{ - int height, used_height; - int x0 = 0; - int x1 = *width; - int x, h, x_previous; - int fy = cy; - struct box *left; - struct box *right; - struct box *b; - struct box *split_box = 0; - struct box *d; - struct box *br_box = 0; - bool move_y = false; - bool place_below = false; - int space_before = 0, space_after = 0; - unsigned int inline_count = 0; - unsigned int i; - const struct gui_layout_table *font_func = content->font_func; - plot_font_style_t fstyle; - - NSLOG(layout, DEBUG, - "first %p, first->text '%.*s', width %i, y %i, cx %i, cy %i", - first, - (int)first->length, - first->text, - *width, - *y, - cx, - cy); - - /* find sides at top of line */ - x0 += cx; - x1 += cx; - find_sides(cont->float_children, cy, cy, &x0, &x1, &left, &right); - x0 -= cx; - x1 -= cx; - - if (indent) - x0 += layout_text_indent(&content->len_ctx, - first->parent->parent->style, *width); - - if (x1 < x0) - x1 = x0; - - /* get minimum line height from containing block. - * this is the line-height if there are text children and also in the - * case of an initially empty text input */ - if (has_text_children || first->parent->parent->gadget) - used_height = height = line_height(&content->len_ctx, - first->parent->parent->style); - else - /* inline containers with no text are usually for layout and - * look better with no minimum line-height */ - used_height = height = 0; - - /* pass 1: find height of line assuming sides at top of line: loop - * body executed at least once - * keep in sync with the loop in layout_minmax_line() */ - - NSLOG(layout, DEBUG, "x0 %i, x1 %i, x1 - x0 %i", x0, x1, x1 - x0); - - - for (x = 0, b = first; x <= x1 - x0 && b != 0; b = b->next) { - int min_width, max_width, min_height, max_height; - - assert(b->type == BOX_INLINE || b->type == BOX_INLINE_BLOCK || - b->type == BOX_FLOAT_LEFT || - b->type == BOX_FLOAT_RIGHT || - b->type == BOX_BR || b->type == BOX_TEXT || - b->type == BOX_INLINE_END); - - - NSLOG(layout, DEBUG, "pass 1: b %p, x %i", b, x); - - - if (b->type == BOX_BR) - break; - - if (b->type == BOX_FLOAT_LEFT || b->type == BOX_FLOAT_RIGHT) - continue; - if (b->type == BOX_INLINE_BLOCK && - (css_computed_position(b->style) == - CSS_POSITION_ABSOLUTE || - css_computed_position(b->style) == - CSS_POSITION_FIXED)) - continue; - - assert(b->style != NULL); - font_plot_style_from_css(&content->len_ctx, b->style, &fstyle); - - x += space_after; - - if (b->type == BOX_INLINE_BLOCK) { - if (b->max_width != UNKNOWN_WIDTH) - if (!layout_float(b, *width, content)) - return false; - h = b->border[TOP].width + b->padding[TOP] + b->height + - b->padding[BOTTOM] + - b->border[BOTTOM].width; - if (height < h) - height = h; - x += b->margin[LEFT] + b->border[LEFT].width + - b->padding[LEFT] + b->width + - b->padding[RIGHT] + - b->border[RIGHT].width + - b->margin[RIGHT]; - space_after = 0; - continue; - } - - if (b->type == BOX_INLINE) { - /* calculate borders, margins, and padding */ - layout_find_dimensions(&content->len_ctx, - *width, -1, b, b->style, 0, 0, 0, 0, - 0, 0, b->margin, b->padding, b->border); - for (i = 0; i != 4; i++) - if (b->margin[i] == AUTO) - b->margin[i] = 0; - x += b->margin[LEFT] + b->border[LEFT].width + - b->padding[LEFT]; - if (b->inline_end) { - b->inline_end->margin[RIGHT] = b->margin[RIGHT]; - b->inline_end->padding[RIGHT] = - b->padding[RIGHT]; - b->inline_end->border[RIGHT] = - b->border[RIGHT]; - } else { - x += b->padding[RIGHT] + - b->border[RIGHT].width + - b->margin[RIGHT]; - } - } else if (b->type == BOX_INLINE_END) { - b->width = 0; - if (b->space == UNKNOWN_WIDTH) { - font_func->width(&fstyle, " ", 1, &b->space); - /** \todo handle errors */ - } - space_after = b->space; - - x += b->padding[RIGHT] + b->border[RIGHT].width + - b->margin[RIGHT]; - continue; - } - - if (!b->object && !(b->flags & IFRAME) && !b->gadget && - !(b->flags & REPLACE_DIM)) { - /* inline non-replaced, 10.3.1 and 10.6.1 */ - b->height = line_height(&content->len_ctx, - b->style ? b->style : - b->parent->parent->style); - if (height < b->height) - height = b->height; - - if (!b->text) { - b->width = 0; - space_after = 0; - continue; - } - - if (b->width == UNKNOWN_WIDTH) { - /** \todo handle errors */ - - /* If it's a select element, we must use the - * width of the widest option text */ - if (b->parent->parent->gadget && - b->parent->parent->gadget->type - == GADGET_SELECT) { - int opt_maxwidth = 0; - struct form_option *o; - - for (o = b->parent->parent->gadget-> - data.select.items; o; - o = o->next) { - int opt_width; - font_func->width(&fstyle, - o->text, - strlen(o->text), - &opt_width); - - if (opt_maxwidth < opt_width) - opt_maxwidth =opt_width; - } - b->width = opt_maxwidth; - if (nsoption_bool(core_select_menu)) - b->width += SCROLLBAR_WIDTH; - } else { - font_func->width(&fstyle, b->text, - b->length, &b->width); - b->flags |= MEASURED; - } - } - - /* If the current text has not been measured (i.e. its - * width was estimated after splitting), and it fits on - * the line, measure it properly, so next box is placed - * correctly. */ - if (b->text && (x + b->width < x1 - x0) && - !(b->flags & MEASURED) && - b->next) { - font_func->width(&fstyle, b->text, - b->length, &b->width); - b->flags |= MEASURED; - } - - x += b->width; - if (b->space == UNKNOWN_WIDTH) { - font_func->width(&fstyle, " ", 1, &b->space); - /** \todo handle errors */ - } - space_after = b->space; - continue; - } - - space_after = 0; - - /* inline replaced, 10.3.2 and 10.6.2 */ - assert(b->style); - - layout_find_dimensions(&content->len_ctx, - *width, -1, b, b->style, - &b->width, &b->height, - &max_width, &min_width, - &max_height, &min_height, - NULL, NULL, NULL); - - if (b->object && !(b->flags & REPLACE_DIM)) { - layout_get_object_dimensions(b, &b->width, &b->height, - min_width, max_width, - min_height, max_height); - } else if (b->flags & IFRAME) { - /* TODO: should we look at the content dimensions? */ - if (b->width == AUTO) - b->width = 400; - if (b->height == AUTO) - b->height = 300; - - /* We reformat the iframe browser window to new - * dimensions in pass 2 */ - } else { - /* form control with no object */ - if (b->width == AUTO) - b->width = FIXTOINT(nscss_len2px( - &content->len_ctx, INTTOFIX(1), - CSS_UNIT_EM, b->style)); - if (b->height == AUTO) - b->height = FIXTOINT(nscss_len2px( - &content->len_ctx, INTTOFIX(1), - CSS_UNIT_EM, b->style)); - } - - /* Reformat object to new box size */ - if (b->object && content_get_type(b->object) == CONTENT_HTML && - b->width != - content_get_available_width(b->object)) { - css_fixed value = 0; - css_unit unit = CSS_UNIT_PX; - enum css_height_e htype = css_computed_height(b->style, - &value, &unit); - - content_reformat(b->object, false, b->width, b->height); - - if (htype == CSS_HEIGHT_AUTO) - b->height = content_get_height(b->object); - } - - if (height < b->height) - height = b->height; - - x += b->width; - } - - /* find new sides using this height */ - x0 = cx; - x1 = cx + *width; - find_sides(cont->float_children, cy, cy + height, &x0, &x1, - &left, &right); - x0 -= cx; - x1 -= cx; - - if (indent) - x0 += layout_text_indent(&content->len_ctx, - first->parent->parent->style, *width); - - if (x1 < x0) - x1 = x0; - - space_after = space_before = 0; - - /* pass 2: place boxes in line: loop body executed at least once */ - - NSLOG(layout, DEBUG, "x0 %i, x1 %i, x1 - x0 %i", x0, x1, x1 - x0); - - for (x = x_previous = 0, b = first; x <= x1 - x0 && b; b = b->next) { - - NSLOG(layout, DEBUG, "pass 2: b %p, x %i", b, x); - - if (b->type == BOX_INLINE_BLOCK && - (css_computed_position(b->style) == - CSS_POSITION_ABSOLUTE || - css_computed_position(b->style) == - CSS_POSITION_FIXED)) { - b->x = x + space_after; - - } else if (b->type == BOX_INLINE || - b->type == BOX_INLINE_BLOCK || - b->type == BOX_TEXT || - b->type == BOX_INLINE_END) { - assert(b->width != UNKNOWN_WIDTH); - - x_previous = x; - x += space_after; - b->x = x; - - if ((b->type == BOX_INLINE && !b->inline_end) || - b->type == BOX_INLINE_BLOCK) { - b->x += b->margin[LEFT] + b->border[LEFT].width; - x = b->x + b->padding[LEFT] + b->width + - b->padding[RIGHT] + - b->border[RIGHT].width + - b->margin[RIGHT]; - } else if (b->type == BOX_INLINE) { - b->x += b->margin[LEFT] + b->border[LEFT].width; - x = b->x + b->padding[LEFT] + b->width; - } else if (b->type == BOX_INLINE_END) { - b->height = b->inline_end->height; - x += b->padding[RIGHT] + - b->border[RIGHT].width + - b->margin[RIGHT]; - } else { - x += b->width; - } - - space_before = space_after; - if (b->object || b->flags & REPLACE_DIM || - b->flags & IFRAME) - space_after = 0; - else if (b->text || b->type == BOX_INLINE_END) { - if (b->space == UNKNOWN_WIDTH) { - font_plot_style_from_css( - &content->len_ctx, - b->style, &fstyle); - /** \todo handle errors */ - font_func->width(&fstyle, " ", 1, - &b->space); - } - space_after = b->space; - } else { - space_after = 0; - } - split_box = b; - move_y = true; - inline_count++; - } else if (b->type == BOX_BR) { - b->x = x; - b->width = 0; - br_box = b; - b = b->next; - split_box = 0; - move_y = true; - break; - - } else { - /* float */ - NSLOG(layout, DEBUG, "float %p", b); - - d = b->children; - d->float_children = 0; - d->cached_place_below_level = 0; - b->float_container = d->float_container = cont; - - if (!layout_float(d, *width, content)) - return false; - - NSLOG(layout, DEBUG, - "%p : %d %d", - d, - d->margin[TOP], - d->border[TOP].width); - - d->x = d->margin[LEFT] + d->border[LEFT].width; - d->y = d->margin[TOP] + d->border[TOP].width; - b->width = d->margin[LEFT] + d->border[LEFT].width + - d->padding[LEFT] + d->width + - d->padding[RIGHT] + - d->border[RIGHT].width + - d->margin[RIGHT]; - b->height = d->margin[TOP] + d->border[TOP].width + - d->padding[TOP] + d->height + - d->padding[BOTTOM] + - d->border[BOTTOM].width + - d->margin[BOTTOM]; - - if (b->width > (x1 - x0) - x) - place_below = true; - if (d->style && (css_computed_clear(d->style) == - CSS_CLEAR_NONE || - (css_computed_clear(d->style) == - CSS_CLEAR_LEFT && left == 0) || - (css_computed_clear(d->style) == - CSS_CLEAR_RIGHT && - right == 0) || - (css_computed_clear(d->style) == - CSS_CLEAR_BOTH && - left == 0 && right == 0)) && - (!place_below || - (left == 0 && right == 0 && x == 0)) && - cy >= cont->clear_level && - cy >= cont->cached_place_below_level) { - /* + not cleared or, - * cleared and there are no floats to clear - * + fits without needing to be placed below or, - * this line is empty with no floats - * + current y, cy, is below the clear level - * - * Float affects current line */ - if (b->type == BOX_FLOAT_LEFT) { - b->x = cx + x0; - if (b->width > 0) - x0 += b->width; - left = b; - } else { - b->x = cx + x1 - b->width; - if (b->width > 0) - x1 -= b->width; - right = b; - } - b->y = cy; - } else { - /* cleared or doesn't fit on line */ - /* place below into next available space */ - int fcy = (cy > cont->clear_level) ? cy : - cont->clear_level; - fcy = (fcy > cont->cached_place_below_level) ? - fcy : - cont->cached_place_below_level; - fy = (fy > fcy) ? fy : fcy; - fy = (fy == cy) ? fy + height : fy; - - place_float_below(b, *width, cx, fy, cont); - fy = b->y; - if (d->style && ( - (css_computed_clear(d->style) == - CSS_CLEAR_LEFT && left != 0) || - (css_computed_clear(d->style) == - CSS_CLEAR_RIGHT && - right != 0) || - (css_computed_clear(d->style) == - CSS_CLEAR_BOTH && - (left != 0 || right != 0)))) { - /* to be cleared below existing - * floats */ - if (b->type == BOX_FLOAT_LEFT) - b->x = cx; - else - b->x = cx + *width - b->width; - - fcy = layout_clear(cont->float_children, - css_computed_clear(d->style)); - if (fcy > cont->clear_level) - cont->clear_level = fcy; - if (b->y < fcy) - b->y = fcy; - } - if (b->type == BOX_FLOAT_LEFT) - left = b; - else - right = b; - } - add_float_to_container(cont, b); - - split_box = 0; - } - } - - if (x1 - x0 < x && split_box) { - /* the last box went over the end */ - size_t split = 0; - int w; - bool no_wrap = css_computed_white_space( - split_box->style) == CSS_WHITE_SPACE_NOWRAP || - css_computed_white_space( - split_box->style) == CSS_WHITE_SPACE_PRE; - - x = x_previous; - - if (!no_wrap && - (split_box->type == BOX_INLINE || - split_box->type == BOX_TEXT) && - !split_box->object && - !(split_box->flags & REPLACE_DIM) && - !(split_box->flags & IFRAME) && - !split_box->gadget && split_box->text) { - - font_plot_style_from_css(&content->len_ctx, - split_box->style, &fstyle); - /** \todo handle errors */ - font_func->split(&fstyle, - split_box->text, - split_box->length, - x1 - x0 - x - space_before, - &split, - &w); - } - - /* split == 0 implies that text can't be split */ - - if (split == 0) - w = split_box->width; - - - NSLOG(layout, DEBUG, - "splitting: split_box %p \"%.*s\", spilt %zu, w %i, " - "left %p, right %p, inline_count %u", - split_box, - (int)split_box->length, - split_box->text, - split, - w, - left, - right, - inline_count); - - if ((split == 0 || x1 - x0 <= x + space_before + w) && - !left && !right && inline_count == 1) { - /* first word of box doesn't fit, but no floats and - * first box on line so force in */ - if (split == 0 || split == split_box->length) { - /* only one word in this box, or not text - * or white-space:nowrap */ - b = split_box->next; - } else { - /* cut off first word for this line */ - if (!layout_text_box_split(content, &fstyle, - split_box, split, w)) - return false; - b = split_box->next; - } - x += space_before + w; - - NSLOG(layout, DEBUG, "forcing"); - - } else if ((split == 0 || x1 - x0 <= x + space_before + w) && - inline_count == 1) { - /* first word of first box doesn't fit, but a float is - * taking some of the width so move below it */ - assert(left || right); - used_height = 0; - if (left) { - - NSLOG(layout, DEBUG, - "cy %i, left->y %i, left->height %i", - cy, - left->y, - left->height); - - used_height = left->y + left->height - cy + 1; - - NSLOG(layout, DEBUG, "used_height %i", - used_height); - - } - if (right && used_height < - right->y + right->height - cy + 1) - used_height = right->y + right->height - cy + 1; - - if (used_height < 0) - used_height = 0; - - b = split_box; - - NSLOG(layout, DEBUG, "moving below float"); - - } else if (split == 0 || x1 - x0 <= x + space_before + w) { - /* first word of box doesn't fit so leave box for next - * line */ - b = split_box; - - NSLOG(layout, DEBUG, "leaving for next line"); - - } else { - /* fit as many words as possible */ - assert(split != 0); - - NSLOG(layout, DEBUG, "'%.*s' %i %zu %i", - (int)split_box->length, split_box->text, - x1 - x0, split, w); - - if (split != split_box->length) { - if (!layout_text_box_split(content, &fstyle, - split_box, split, w)) - return false; - b = split_box->next; - } - x += space_before + w; - - NSLOG(layout, DEBUG, "fitting words"); - - } - move_y = true; - } - - /* set positions */ - switch (css_computed_text_align(first->parent->parent->style)) { - case CSS_TEXT_ALIGN_RIGHT: - case CSS_TEXT_ALIGN_LIBCSS_RIGHT: - x0 = x1 - x; - break; - case CSS_TEXT_ALIGN_CENTER: - case CSS_TEXT_ALIGN_LIBCSS_CENTER: - x0 = (x0 + (x1 - x)) / 2; - break; - case CSS_TEXT_ALIGN_LEFT: - case CSS_TEXT_ALIGN_LIBCSS_LEFT: - case CSS_TEXT_ALIGN_JUSTIFY: - /* leave on left */ - break; - case CSS_TEXT_ALIGN_DEFAULT: - /* None; consider text direction */ - switch (css_computed_direction(first->parent->parent->style)) { - case CSS_DIRECTION_LTR: - /* leave on left */ - break; - case CSS_DIRECTION_RTL: - x0 = x1 - x; - break; - } - break; - } - - for (d = first; d != b; d = d->next) { - d->flags &= ~NEW_LINE; - - if (d->type == BOX_INLINE_BLOCK && - (css_computed_position(d->style) == - CSS_POSITION_ABSOLUTE || - css_computed_position(d->style) == - CSS_POSITION_FIXED)) { - /* positioned inline-blocks: - * set static position (x,y) only, rest of positioning - * is handled later */ - d->x += x0; - d->y = *y; - continue; - } else if ((d->type == BOX_INLINE && - ((d->object || d->gadget) == false) && - !(d->flags & IFRAME) && - !(d->flags & REPLACE_DIM)) || - d->type == BOX_BR || - d->type == BOX_TEXT || - d->type == BOX_INLINE_END) { - /* regular (non-replaced) inlines */ - d->x += x0; - d->y = *y - d->padding[TOP]; - - if (d->type == BOX_TEXT && d->height > used_height) { - /* text */ - used_height = d->height; - } - } else if ((d->type == BOX_INLINE) || - d->type == BOX_INLINE_BLOCK) { - /* replaced inlines and inline-blocks */ - d->x += x0; - d->y = *y + d->border[TOP].width + d->margin[TOP]; - h = d->margin[TOP] + d->border[TOP].width + - d->padding[TOP] + d->height + - d->padding[BOTTOM] + - d->border[BOTTOM].width + - d->margin[BOTTOM]; - if (used_height < h) - used_height = h; - } - } - - first->flags |= NEW_LINE; - - assert(b != first || (move_y && 0 < used_height && (left || right))); - - /* handle vertical-align by adjusting box y values */ - /** \todo proper vertical alignment handling */ - for (d = first; d != b; d = d->next) { - if ((d->type == BOX_INLINE && d->inline_end) || - d->type == BOX_BR || - d->type == BOX_TEXT || - d->type == BOX_INLINE_END) { - css_fixed value = 0; - css_unit unit = CSS_UNIT_PX; - switch (css_computed_vertical_align(d->style, &value, - &unit)) { - case CSS_VERTICAL_ALIGN_SUPER: - case CSS_VERTICAL_ALIGN_TOP: - case CSS_VERTICAL_ALIGN_TEXT_TOP: - /* already at top */ - break; - case CSS_VERTICAL_ALIGN_SUB: - case CSS_VERTICAL_ALIGN_BOTTOM: - case CSS_VERTICAL_ALIGN_TEXT_BOTTOM: - d->y += used_height - d->height; - break; - default: - case CSS_VERTICAL_ALIGN_BASELINE: - d->y += 0.75 * (used_height - d->height); - break; - } - } - } - - /* handle clearance for br */ - if (br_box && css_computed_clear(br_box->style) != CSS_CLEAR_NONE) { - int clear_y = layout_clear(cont->float_children, - css_computed_clear(br_box->style)); - if (used_height < clear_y - cy) - used_height = clear_y - cy; - } - - if (move_y) - *y += used_height; - *next_box = b; - *width = x; /* return actual width */ - return true; -} - - -/** - * Layout lines of text or inline boxes with floats. - * - * \param box inline container box - * \param width horizontal space available - * \param cont ancestor box which defines horizontal space, for floats - * \param cx box position relative to cont - * \param cy box position relative to cont - * \param content memory pool for any new boxes - * \return true on success, false on memory exhaustion - */ -static bool layout_inline_container(struct box *inline_container, int width, - struct box *cont, int cx, int cy, html_content *content) -{ - bool first_line = true; - bool has_text_children; - struct box *c, *next; - int y = 0; - int curwidth,maxwidth = width; - - assert(inline_container->type == BOX_INLINE_CONTAINER); - - NSLOG(layout, DEBUG, - "inline_container %p, width %i, cont %p, cx %i, cy %i", - inline_container, - width, - cont, - cx, - cy); - - - has_text_children = false; - for (c = inline_container->children; c; c = c->next) { - bool is_pre = false; - - if (c->style) { - enum css_white_space_e whitespace; - - whitespace = css_computed_white_space(c->style); - - is_pre = (whitespace == CSS_WHITE_SPACE_PRE || - whitespace == CSS_WHITE_SPACE_PRE_LINE || - whitespace == CSS_WHITE_SPACE_PRE_WRAP); - } - - if ((!c->object && !(c->flags & REPLACE_DIM) && - !(c->flags & IFRAME) && - c->text && (c->length || is_pre)) || - c->type == BOX_BR) - has_text_children = true; - } - - /** \todo fix wrapping so that a box with horizontal scrollbar will - * shrink back to 'width' if no word is wider than 'width' (Or just set - * curwidth = width and have the multiword lines wrap to the min width) - */ - for (c = inline_container->children; c; ) { - - NSLOG(layout, DEBUG, "c %p", c); - - curwidth = inline_container->width; - if (!layout_line(c, &curwidth, &y, cx, cy + y, cont, first_line, - has_text_children, content, &next)) - return false; - maxwidth = max(maxwidth,curwidth); - c = next; - first_line = false; - } - - inline_container->width = maxwidth; - inline_container->height = y; - - return true; -} - - -/** - * Layout a block formatting context. - * - * \param block BLOCK, INLINE_BLOCK, or TABLE_CELL to layout - * \param viewport_height Height of viewport in pixels or -ve if unknown - * \param content Memory pool for any new boxes - * \return true on success, false on memory exhaustion - * - * This function carries out layout of a block and its children, as described - * in CSS 2.1 9.4.1. - */ -static bool -layout_block_context(struct box *block, - int viewport_height, - html_content *content) -{ - struct box *box; - int cx, cy; /**< current coordinates */ - int max_pos_margin = 0; - int max_neg_margin = 0; - int y = 0; - int lm, rm; - struct box *margin_collapse = NULL; - bool in_margin = false; - css_fixed gadget_size; - css_unit gadget_unit; /* Checkbox / radio buttons */ - - assert(block->type == BOX_BLOCK || - block->type == BOX_INLINE_BLOCK || - block->type == BOX_TABLE_CELL); - assert(block->width != UNKNOWN_WIDTH); - assert(block->width != AUTO); - - block->float_children = NULL; - block->cached_place_below_level = 0; - block->clear_level = 0; - - /* special case if the block contains an object */ - if (block->object) { - int temp_width = block->width; - if (!layout_block_object(block)) - return false; - layout_get_object_dimensions(block, &temp_width, - &block->height, INT_MIN, INT_MAX, - INT_MIN, INT_MAX); - return true; - } else if (block->flags & REPLACE_DIM) { - return true; - } - - /* special case if the block contains an radio button or checkbox */ - if (block->gadget && (block->gadget->type == GADGET_RADIO || - block->gadget->type == GADGET_CHECKBOX)) { - /* form checkbox or radio button - * if width or height is AUTO, set it to 1em */ - gadget_unit = CSS_UNIT_EM; - gadget_size = INTTOFIX(1); - if (block->height == AUTO) - block->height = FIXTOINT(nscss_len2px( - &content->len_ctx, gadget_size, - gadget_unit, block->style)); - } - - box = block->children; - /* set current coordinates to top-left of the block */ - cx = 0; - y = cy = block->padding[TOP]; - if (box) - box->y = block->padding[TOP]; - - /* Step through the descendants of the block in depth-first order, but - * not into the children of boxes which aren't blocks. For example, if - * the tree passed to this function looks like this (box->type shown): - * - * block -> BOX_BLOCK - * BOX_BLOCK * (1) - * BOX_INLINE_CONTAINER * (2) - * BOX_INLINE - * BOX_TEXT - * ... - * BOX_BLOCK * (3) - * BOX_TABLE * (4) - * BOX_TABLE_ROW - * BOX_TABLE_CELL - * ... - * BOX_TABLE_CELL - * ... - * BOX_BLOCK * (5) - * BOX_INLINE_CONTAINER * (6) - * BOX_TEXT - * ... - * then the while loop will visit each box marked with *, setting box - * to each in the order shown. */ - while (box) { - enum css_overflow_e overflow_x = CSS_OVERFLOW_VISIBLE; - enum css_overflow_e overflow_y = CSS_OVERFLOW_VISIBLE; - - assert(box->type == BOX_BLOCK || box->type == BOX_TABLE || - box->type == BOX_INLINE_CONTAINER); - - /* Tables are laid out before being positioned, because the - * position depends on the width which is calculated in - * table layout. Blocks and inline containers are positioned - * before being laid out, because width is not dependent on - * content, and the position is required during layout for - * correct handling of floats. - */ - - if (box->style && - (css_computed_position(box->style) == - CSS_POSITION_ABSOLUTE || - css_computed_position(box->style) == - CSS_POSITION_FIXED)) { - box->x = box->parent->padding[LEFT]; - /* absolute positioned; this element will establish - * its own block context when it gets laid out later, - * so no need to look at its children now. */ - goto advance_to_next_box; - } - - /* If we don't know which box the current margin collapses - * through to, find out. Update the pos/neg margin values. */ - if (margin_collapse == NULL) { - margin_collapse = layout_next_margin_block( - &content->len_ctx, box, block, - viewport_height, - &max_pos_margin, &max_neg_margin); - /* We have a margin that has not yet been applied. */ - in_margin = true; - } - - /* Clearance. */ - y = 0; - if (box->style && css_computed_clear(box->style) != - CSS_CLEAR_NONE) - y = layout_clear(block->float_children, - css_computed_clear(box->style)); - - /* Find box's overflow properties */ - if (box->style) { - overflow_x = css_computed_overflow_x(box->style); - overflow_y = css_computed_overflow_y(box->style); - } - - /* Blocks establishing a block formatting context get minimum - * left and right margins to avoid any floats. */ - lm = rm = 0; - - if (box->type == BOX_BLOCK || box->flags & IFRAME) { - if (!box->object && !(box->flags & IFRAME) && - !(box->flags & REPLACE_DIM) && - box->style && - (overflow_x != CSS_OVERFLOW_VISIBLE || - overflow_y != CSS_OVERFLOW_VISIBLE)) { - /* box establishes new block formatting context - * so available width may be diminished due to - * floats. */ - int x0, x1, top; - struct box *left, *right; - top = cy + max_pos_margin - max_neg_margin; - top = (top > y) ? top : y; - x0 = cx; - x1 = cx + box->parent->width - - box->parent->padding[LEFT] - - box->parent->padding[RIGHT]; - find_sides(block->float_children, top, top, - &x0, &x1, &left, &right); - /* calculate min required left & right margins - * needed to avoid floats */ - lm = x0 - cx; - rm = cx + box->parent->width - - box->parent->padding[LEFT] - - box->parent->padding[RIGHT] - - x1; - } - layout_block_find_dimensions(&content->len_ctx, - box->parent->width, - viewport_height, lm, rm, box); - if (box->type == BOX_BLOCK && !(box->flags & IFRAME)) { - layout_block_add_scrollbar(box, RIGHT); - layout_block_add_scrollbar(box, BOTTOM); - } - } else if (box->type == BOX_TABLE) { - if (box->style != NULL) { - enum css_width_e wtype; - css_fixed width = 0; - css_unit unit = CSS_UNIT_PX; - - wtype = css_computed_width(box->style, &width, - &unit); - - if (wtype == CSS_WIDTH_AUTO) { - /* max available width may be - * diminished due to floats. */ - int x0, x1, top; - struct box *left, *right; - top = cy + max_pos_margin - - max_neg_margin; - top = (top > y) ? top : y; - x0 = cx; - x1 = cx + box->parent->width - - box->parent->padding[LEFT] - - box->parent->padding[RIGHT]; - find_sides(block->float_children, - top, top, &x0, &x1, - &left, &right); - /* calculate min required left & right - * margins needed to avoid floats */ - lm = x0 - cx; - rm = cx + box->parent->width - - box->parent->padding[LEFT] - - box->parent->padding[RIGHT] - - x1; - } - } - if (!layout_table(box, box->parent->width - lm - rm, - content)) - return false; - layout_solve_width(box, box->parent->width, box->width, - lm, rm, -1, -1); - } - - /* Position box: horizontal. */ - box->x = box->parent->padding[LEFT] + box->margin[LEFT] + - box->border[LEFT].width; - cx += box->x; - - /* Position box: vertical. */ - if (box->border[TOP].width) { - box->y += box->border[TOP].width; - cy += box->border[TOP].width; - } - - /* Vertical margin */ - if (((box->type == BOX_BLOCK && - (box->flags & HAS_HEIGHT)) || - box->type == BOX_TABLE || - (box->type == BOX_INLINE_CONTAINER && - box != box->parent->children) || - margin_collapse == box) && - in_margin == true) { - /* Margin goes above this box. */ - cy += max_pos_margin - max_neg_margin; - box->y += max_pos_margin - max_neg_margin; - - /* Current margin has been applied. */ - in_margin = false; - max_pos_margin = max_neg_margin = 0; - } - - /* Handle clearance */ - if (box->type != BOX_INLINE_CONTAINER && - (y > 0) && (cy < y)) { - /* box clears something*/ - box->y += y - cy; - cy = y; - } - - /* Unless the box has an overflow style of visible, the box - * establishes a new block context. */ - if (box->type == BOX_BLOCK && box->style && - (overflow_x != CSS_OVERFLOW_VISIBLE || - overflow_y != CSS_OVERFLOW_VISIBLE)) { - - layout_block_context(box, viewport_height, content); - - cy += box->padding[TOP]; - - if (box->height == AUTO) { - box->height = 0; - layout_block_add_scrollbar(box, BOTTOM); - } - - cx -= box->x; - cy += box->height + box->padding[BOTTOM] + - box->border[BOTTOM].width; - y = box->y + box->padding[TOP] + box->height + - box->padding[BOTTOM] + - box->border[BOTTOM].width; - - /* Skip children, because they are done in the new - * block context */ - goto advance_to_next_box; - } - - NSLOG(layout, DEBUG, "box %p, cx %i, cy %i", box, cx, cy); - - /* Layout (except tables). */ - if (box->object) { - if (!layout_block_object(box)) - return false; - - } else if (box->type == BOX_INLINE_CONTAINER) { - box->width = box->parent->width; - if (!layout_inline_container(box, box->width, block, - cx, cy, content)) - return false; - - } else if (box->type == BOX_TABLE) { - /* Move down to avoid floats if necessary. */ - int x0, x1; - struct box *left, *right; - y = cy; - while (1) { - enum css_width_e wtype; - css_fixed width = 0; - css_unit unit = CSS_UNIT_PX; - - wtype = css_computed_width(box->style, - &width, &unit); - - x0 = cx; - x1 = cx + box->parent->width; - find_sides(block->float_children, y, - y + box->height, - &x0, &x1, &left, &right); - if (wtype == CSS_WIDTH_AUTO) - break; - if (box->width <= x1 - x0) - break; - if (!left && !right) - break; - else if (!left) - y = right->y + right->height + 1; - else if (!right) - y = left->y + left->height + 1; - else if (left->y + left->height < - right->y + right->height) - y = left->y + left->height + 1; - else - y = right->y + right->height + 1; - } - box->x += x0 - cx; - cx = x0; - box->y += y - cy; - cy = y; - } - - /* Advance to next box. */ - if (box->type == BOX_BLOCK && !box->object && !(box->iframe) && - box->children) { - /* Down into children. */ - - if (box == margin_collapse) { - /* Current margin collapsed though to this box. - * Unset margin_collapse. */ - margin_collapse = NULL; - } - - y = box->padding[TOP]; - box = box->children; - box->y = y; - cy += y; - continue; - } else if (box->type == BOX_BLOCK || box->object || - box->flags & IFRAME) - cy += box->padding[TOP]; - - if (box->type == BOX_BLOCK && box->height == AUTO) { - box->height = 0; - layout_block_add_scrollbar(box, BOTTOM); - } - - cy += box->height + box->padding[BOTTOM] + - box->border[BOTTOM].width; - cx -= box->x; - y = box->y + box->padding[TOP] + box->height + - box->padding[BOTTOM] + - box->border[BOTTOM].width; - - advance_to_next_box: - if (!box->next) { - /* No more siblings: - * up to first ancestor with a sibling. */ - - do { - if (box == margin_collapse) { - /* Current margin collapsed though to - * this box. Unset margin_collapse. */ - margin_collapse = NULL; - } - - /* Apply bottom margin */ - if (max_pos_margin < box->margin[BOTTOM]) - max_pos_margin = box->margin[BOTTOM]; - else if (max_neg_margin < -box->margin[BOTTOM]) - max_neg_margin = -box->margin[BOTTOM]; - - box = box->parent; - if (box == block) - break; - - /* Margin is invalidated if this is a box - * margins can't collapse through. */ - if (box->type == BOX_BLOCK && - box->flags & MAKE_HEIGHT) { - margin_collapse = NULL; - in_margin = false; - max_pos_margin = max_neg_margin = 0; - } - - if (box->height == AUTO) { - box->height = y - box->padding[TOP]; - - if (box->type == BOX_BLOCK) - layout_block_add_scrollbar(box, - BOTTOM); - } else - cy += box->height - - (y - box->padding[TOP]); - - /* Apply any min-height and max-height to - * boxes in normal flow */ - if (box->style && - css_computed_position(box->style) != - CSS_POSITION_ABSOLUTE && - layout_apply_minmax_height( - &content->len_ctx, - box, NULL)) { - /* Height altered */ - /* Set current cy */ - cy += box->height - - (y - box->padding[TOP]); - } - - cy += box->padding[BOTTOM] + - box->border[BOTTOM].width; - cx -= box->x; - y = box->y + box->padding[TOP] + box->height + - box->padding[BOTTOM] + - box->border[BOTTOM].width; - - } while (box->next == NULL); - if (box == block) - break; - } - - /* To next sibling. */ - - if (box == margin_collapse) { - /* Current margin collapsed though to this box. - * Unset margin_collapse. */ - margin_collapse = NULL; - } - - if (max_pos_margin < box->margin[BOTTOM]) - max_pos_margin = box->margin[BOTTOM]; - else if (max_neg_margin < -box->margin[BOTTOM]) - max_neg_margin = -box->margin[BOTTOM]; - - box = box->next; - box->y = y; - } - - /* Account for bottom margin of last contained block */ - cy += max_pos_margin - max_neg_margin; - - /* Increase height to contain any floats inside (CSS 2.1 10.6.7). */ - for (box = block->float_children; box; box = box->next_float) { - y = box->y + box->height + box->padding[BOTTOM] + - box->border[BOTTOM].width + box->margin[BOTTOM]; - if (cy < y) - cy = y; - } - - if (block->height == AUTO) { - block->height = cy - block->padding[TOP]; - if (block->type == BOX_BLOCK) - layout_block_add_scrollbar(block, BOTTOM); - } - - if (block->style && css_computed_position(block->style) != - CSS_POSITION_ABSOLUTE) { - /* Block is in normal flow */ - layout_apply_minmax_height(&content->len_ctx, block, NULL); - } - - if (block->gadget && - (block->gadget->type == GADGET_TEXTAREA || - block->gadget->type == GADGET_PASSWORD || - block->gadget->type == GADGET_TEXTBOX)) { - plot_font_style_t fstyle; - int ta_width = block->padding[LEFT] + block->width + - block->padding[RIGHT]; - int ta_height = block->padding[TOP] + block->height + - block->padding[BOTTOM]; - font_plot_style_from_css(&content->len_ctx, - block->style, &fstyle); - fstyle.background = NS_TRANSPARENT; - textarea_set_layout(block->gadget->data.text.ta, - &fstyle, ta_width, ta_height, - block->padding[TOP], block->padding[RIGHT], - block->padding[BOTTOM], block->padding[LEFT]); - } - - return true; -} - - -/** - * Layout list markers. - */ -static void -layout_lists(struct box *box, - const struct gui_layout_table *font_func, - const nscss_len_ctx *len_ctx) -{ - struct box *child; - struct box *marker; - plot_font_style_t fstyle; - - for (child = box->children; child; child = child->next) { - if (child->list_marker) { - marker = child->list_marker; - if (marker->object) { - marker->width = - content_get_width(marker->object); - marker->x = -marker->width; - marker->height = - content_get_height(marker->object); - marker->y = (line_height(len_ctx, - marker->style) - - marker->height) / 2; - } else if (marker->text) { - if (marker->width == UNKNOWN_WIDTH) { - font_plot_style_from_css(len_ctx, - marker->style, &fstyle); - font_func->width(&fstyle, - marker->text, - marker->length, - &marker->width); - marker->flags |= MEASURED; - } - marker->x = -marker->width; - marker->y = 0; - marker->height = line_height(len_ctx, - marker->style); - } else { - marker->x = 0; - marker->y = 0; - marker->width = 0; - marker->height = 0; - } - /* Gap between marker and content */ - marker->x -= 4; - } - layout_lists(child, font_func, len_ctx); - } -} - - -/** - * Compute box offsets for a relatively or absolutely positioned box with - * respect to a box. - * - * \param len_ctx Length conversion context - * \param box box to compute offsets for - * \param containing_block box to compute percentages with respect to - * \param top updated to top offset, or AUTO - * \param right updated to right offset, or AUTO - * \param bottom updated to bottom offset, or AUTO - * \param left updated to left offset, or AUTO - * - * See CSS 2.1 9.3.2. containing_block must have width and height. - */ -static void -layout_compute_offsets(const nscss_len_ctx *len_ctx, - struct box *box, - struct box *containing_block, - int *top, - int *right, - int *bottom, - int *left) -{ - uint32_t type; - css_fixed value = 0; - css_unit unit = CSS_UNIT_PX; - - assert(containing_block->width != UNKNOWN_WIDTH && - containing_block->width != AUTO && - containing_block->height != AUTO); - - /* left */ - type = css_computed_left(box->style, &value, &unit); - if (type == CSS_LEFT_SET) { - if (unit == CSS_UNIT_PCT) { - *left = FPCT_OF_INT_TOINT(value, - containing_block->width); - } else { - *left = FIXTOINT(nscss_len2px(len_ctx, - value, unit, box->style)); - } - } else { - *left = AUTO; - } - - /* right */ - type = css_computed_right(box->style, &value, &unit); - if (type == CSS_RIGHT_SET) { - if (unit == CSS_UNIT_PCT) { - *right = FPCT_OF_INT_TOINT(value, - containing_block->width); - } else { - *right = FIXTOINT(nscss_len2px(len_ctx, - value, unit, box->style)); - } - } else { - *right = AUTO; - } - - /* top */ - type = css_computed_top(box->style, &value, &unit); - if (type == CSS_TOP_SET) { - if (unit == CSS_UNIT_PCT) { - *top = FPCT_OF_INT_TOINT(value, - containing_block->height); - } else { - *top = FIXTOINT(nscss_len2px(len_ctx, - value, unit, box->style)); - } - } else { - *top = AUTO; - } - - /* bottom */ - type = css_computed_bottom(box->style, &value, &unit); - if (type == CSS_BOTTOM_SET) { - if (unit == CSS_UNIT_PCT) { - *bottom = FPCT_OF_INT_TOINT(value, - containing_block->height); - } else { - *bottom = FIXTOINT(nscss_len2px(len_ctx, - value, unit, box->style)); - } - } else { - *bottom = AUTO; - } -} - - -/** - * Layout and position an absolutely positioned box. - * - * \param box absolute box to layout and position - * \param containing_block containing block - * \param cx position of box relative to containing_block - * \param cy position of box relative to containing_block - * \param content memory pool for any new boxes - * \return true on success, false on memory exhaustion - */ -static bool -layout_absolute(struct box *box, - struct box *containing_block, - int cx, int cy, - html_content *content) -{ - int static_left, static_top; /* static position */ - int top, right, bottom, left; - int width, height, max_width, min_width; - int *margin = box->margin; - int *padding = box->padding; - struct box_border *border = box->border; - int available_width = containing_block->width; - int space; - - assert(box->type == BOX_BLOCK || box->type == BOX_TABLE || - box->type == BOX_INLINE_BLOCK); - - /* The static position is where the box would be if it was not - * absolutely positioned. The x and y are filled in by - * layout_block_context(). */ - static_left = cx + box->x; - static_top = cy + box->y; - - if (containing_block->type == BOX_BLOCK || - containing_block->type == BOX_INLINE_BLOCK || - containing_block->type == BOX_TABLE_CELL) { - /* Block level container => temporarily increase containing - * block dimensions to include padding (we restore this - * again at the end) */ - containing_block->width += containing_block->padding[LEFT] + - containing_block->padding[RIGHT]; - containing_block->height += containing_block->padding[TOP] + - containing_block->padding[BOTTOM]; - } else { - /** \todo inline containers */ - } - - layout_compute_offsets(&content->len_ctx, box, containing_block, - &top, &right, &bottom, &left); - - /* Pass containing block into layout_find_dimensions via the float - * containing block box member. This is unused for absolutely positioned - * boxes because a box can't be floated and absolutely positioned. */ - box->float_container = containing_block; - layout_find_dimensions(&content->len_ctx, available_width, -1, - box, box->style, &width, &height, - &max_width, &min_width, 0, 0, - margin, padding, border); - box->float_container = NULL; - - /* 10.3.7 */ - NSLOG(layout, DEBUG, - "%i + %i + %i + %i + %i + %i + %i + %i + %i = %i", - left, margin[LEFT], border[LEFT].width, padding[LEFT], width, - padding[RIGHT], border[RIGHT].width, margin[RIGHT], right, - containing_block->width); - - - if (left == AUTO && width == AUTO && right == AUTO) { - if (margin[LEFT] == AUTO) - margin[LEFT] = 0; - if (margin[RIGHT] == AUTO) - margin[RIGHT] = 0; - left = static_left; - - width = min(max(box->min_width, available_width), - box->max_width); - width -= box->margin[LEFT] + box->border[LEFT].width + - box->padding[LEFT] + box->padding[RIGHT] + - box->border[RIGHT].width + box->margin[RIGHT]; - - /* Adjust for {min|max}-width */ - if (max_width >= 0 && width > max_width) width = max_width; - if (width < min_width) width = min_width; - - right = containing_block->width - - left - - margin[LEFT] - border[LEFT].width - padding[LEFT] - - width - - padding[RIGHT] - border[RIGHT].width - margin[RIGHT]; - } else if (left != AUTO && width != AUTO && right != AUTO) { - - /* Adjust for {min|max}-width */ - if (max_width >= 0 && width > max_width) width = max_width; - if (min_width > 0 && width < min_width) width = min_width; - - if (margin[LEFT] == AUTO && margin[RIGHT] == AUTO) { - space = containing_block->width - - left - border[LEFT].width - - padding[LEFT] - width - padding[RIGHT] - - border[RIGHT].width - right; - if (space < 0) { - margin[LEFT] = 0; - margin[RIGHT] = space; - } else { - margin[LEFT] = margin[RIGHT] = space / 2; - } - } else if (margin[LEFT] == AUTO) { - margin[LEFT] = containing_block->width - - left - border[LEFT].width - - padding[LEFT] - width - padding[RIGHT] - - border[RIGHT].width - margin[RIGHT] - - right; - } else if (margin[RIGHT] == AUTO) { - margin[RIGHT] = containing_block->width - - left - margin[LEFT] - - border[LEFT].width - - padding[LEFT] - width - padding[RIGHT] - - border[RIGHT].width - right; - } else { - right = containing_block->width - - left - margin[LEFT] - - border[LEFT].width - - padding[LEFT] - width - padding[RIGHT] - - border[RIGHT].width - margin[RIGHT]; - } - } else { - if (margin[LEFT] == AUTO) - margin[LEFT] = 0; - if (margin[RIGHT] == AUTO) - margin[RIGHT] = 0; - - if (left == AUTO && width == AUTO && right != AUTO) { - available_width -= right; - - width = min(max(box->min_width, available_width), - box->max_width); - width -= box->margin[LEFT] + box->border[LEFT].width + - box->padding[LEFT] + box->padding[RIGHT] + - box->border[RIGHT].width + box->margin[RIGHT]; - - /* Adjust for {min|max}-width */ - if (max_width >= 0 && width > max_width) - width = max_width; - if (width < min_width) - width = min_width; - - left = containing_block->width - - margin[LEFT] - border[LEFT].width - - padding[LEFT] - width - padding[RIGHT] - - border[RIGHT].width - margin[RIGHT] - - right; - } else if (left == AUTO && width != AUTO && right == AUTO) { - - /* Adjust for {min|max}-width */ - if (max_width >= 0 && width > max_width) - width = max_width; - if (min_width > 0 && width < min_width) - width = min_width; - - left = static_left; - right = containing_block->width - - left - margin[LEFT] - - border[LEFT].width - - padding[LEFT] - width - padding[RIGHT] - - border[RIGHT].width - margin[RIGHT]; - } else if (left != AUTO && width == AUTO && right == AUTO) { - available_width -= left; - - width = min(max(box->min_width, available_width), - box->max_width); - width -= box->margin[LEFT] + box->border[LEFT].width + - box->padding[LEFT] + box->padding[RIGHT] + - box->border[RIGHT].width + box->margin[RIGHT]; - - /* Adjust for {min|max}-width */ - if (max_width >= 0 && width > max_width) - width = max_width; - if (width < min_width) - width = min_width; - - right = containing_block->width - - left - margin[LEFT] - - border[LEFT].width - - padding[LEFT] - width - padding[RIGHT] - - border[RIGHT].width - margin[RIGHT]; - } else if (left == AUTO && width != AUTO && right != AUTO) { - - /* Adjust for {min|max}-width */ - if (max_width >= 0 && width > max_width) - width = max_width; - if (width < min_width) - width = min_width; - - left = containing_block->width - - margin[LEFT] - border[LEFT].width - - padding[LEFT] - width - padding[RIGHT] - - border[RIGHT].width - margin[RIGHT] - - right; - } else if (left != AUTO && width == AUTO && right != AUTO) { - width = containing_block->width - - left - margin[LEFT] - - border[LEFT].width - - padding[LEFT] - padding[RIGHT] - - border[RIGHT].width - margin[RIGHT] - - right; - - /* Adjust for {min|max}-width */ - if (max_width >= 0 && width > max_width) - width = max_width; - if (width < min_width) - width = min_width; - - } else if (left != AUTO && width != AUTO && right == AUTO) { - - /* Adjust for {min|max}-width */ - if (max_width >= 0 && width > max_width) - width = max_width; - if (width < min_width) - width = min_width; - - right = containing_block->width - - left - margin[LEFT] - - border[LEFT].width - - padding[LEFT] - width - padding[RIGHT] - - border[RIGHT].width - margin[RIGHT]; - } - } - - NSLOG(layout, DEBUG, - "%i + %i + %i + %i + %i + %i + %i + %i + %i = %i", - left, margin[LEFT], border[LEFT].width, padding[LEFT], width, - padding[RIGHT], border[RIGHT].width, margin[RIGHT], right, - containing_block->width); - - box->x = left + margin[LEFT] + border[LEFT].width - cx; - if (containing_block->type == BOX_BLOCK || - containing_block->type == BOX_INLINE_BLOCK || - containing_block->type == BOX_TABLE_CELL) { - /* Block-level ancestor => reset container's width */ - containing_block->width -= containing_block->padding[LEFT] + - containing_block->padding[RIGHT]; - } else { - /** \todo inline ancestors */ - } - box->width = width; - box->height = height; - - if (box->type == BOX_BLOCK || box->type == BOX_INLINE_BLOCK || - box->object || box->flags & IFRAME) { - if (!layout_block_context(box, -1, content)) - return false; - } else if (box->type == BOX_TABLE) { - /* layout_table also expects the containing block to be - * stored in the float_container field */ - box->float_container = containing_block; - /* \todo layout_table considers margins etc. again */ - if (!layout_table(box, width, content)) - return false; - box->float_container = NULL; - layout_solve_width(box, box->parent->width, box->width, 0, 0, - -1, -1); - } - - /* 10.6.4 */ - NSLOG(layout, DEBUG, - "%i + %i + %i + %i + %i + %i + %i + %i + %i = %i", - top, margin[TOP], border[TOP].width, padding[TOP], height, - padding[BOTTOM], border[BOTTOM].width, margin[BOTTOM], bottom, - containing_block->height); - - if (top == AUTO && height == AUTO && bottom == AUTO) { - top = static_top; - height = box->height; - if (margin[TOP] == AUTO) - margin[TOP] = 0; - if (margin[BOTTOM] == AUTO) - margin[BOTTOM] = 0; - bottom = containing_block->height - - top - margin[TOP] - border[TOP].width - - padding[TOP] - height - padding[BOTTOM] - - border[BOTTOM].width - margin[BOTTOM]; - } else if (top != AUTO && height != AUTO && bottom != AUTO) { - if (margin[TOP] == AUTO && margin[BOTTOM] == AUTO) { - space = containing_block->height - - top - border[TOP].width - padding[TOP] - - height - padding[BOTTOM] - - border[BOTTOM].width - bottom; - margin[TOP] = margin[BOTTOM] = space / 2; - } else if (margin[TOP] == AUTO) { - margin[TOP] = containing_block->height - - top - border[TOP].width - padding[TOP] - - height - padding[BOTTOM] - - border[BOTTOM].width - margin[BOTTOM] - - bottom; - } else if (margin[BOTTOM] == AUTO) { - margin[BOTTOM] = containing_block->height - - top - margin[TOP] - border[TOP].width - - padding[TOP] - height - - padding[BOTTOM] - border[BOTTOM].width - - bottom; - } else { - bottom = containing_block->height - - top - margin[TOP] - border[TOP].width - - padding[TOP] - height - - padding[BOTTOM] - border[BOTTOM].width - - margin[BOTTOM]; - } - } else { - if (margin[TOP] == AUTO) - margin[TOP] = 0; - if (margin[BOTTOM] == AUTO) - margin[BOTTOM] = 0; - if (top == AUTO && height == AUTO && bottom != AUTO) { - height = box->height; - top = containing_block->height - - margin[TOP] - border[TOP].width - - padding[TOP] - height - - padding[BOTTOM] - border[BOTTOM].width - - margin[BOTTOM] - bottom; - } else if (top == AUTO && height != AUTO && bottom == AUTO) { - top = static_top; - bottom = containing_block->height - - top - margin[TOP] - border[TOP].width - - padding[TOP] - height - - padding[BOTTOM] - border[BOTTOM].width - - margin[BOTTOM]; - } else if (top != AUTO && height == AUTO && bottom == AUTO) { - height = box->height; - bottom = containing_block->height - - top - margin[TOP] - border[TOP].width - - padding[TOP] - height - - padding[BOTTOM] - border[BOTTOM].width - - margin[BOTTOM]; - } else if (top == AUTO && height != AUTO && bottom != AUTO) { - top = containing_block->height - - margin[TOP] - border[TOP].width - - padding[TOP] - height - - padding[BOTTOM] - border[BOTTOM].width - - margin[BOTTOM] - bottom; - } else if (top != AUTO && height == AUTO && bottom != AUTO) { - height = containing_block->height - - top - margin[TOP] - border[TOP].width - - padding[TOP] - padding[BOTTOM] - - border[BOTTOM].width - margin[BOTTOM] - - bottom; - } else if (top != AUTO && height != AUTO && bottom == AUTO) { - bottom = containing_block->height - - top - margin[TOP] - border[TOP].width - - padding[TOP] - height - - padding[BOTTOM] - border[BOTTOM].width - - margin[BOTTOM]; - } - } - - NSLOG(layout, DEBUG, - "%i + %i + %i + %i + %i + %i + %i + %i + %i = %i", - top, margin[TOP], border[TOP].width, padding[TOP], height, - padding[BOTTOM], border[BOTTOM].width, margin[BOTTOM], bottom, - containing_block->height); - - box->y = top + margin[TOP] + border[TOP].width - cy; - if (containing_block->type == BOX_BLOCK || - containing_block->type == BOX_INLINE_BLOCK || - containing_block->type == BOX_TABLE_CELL) { - /* Block-level ancestor => reset container's height */ - containing_block->height -= containing_block->padding[TOP] + - containing_block->padding[BOTTOM]; - } else { - /** \todo Inline ancestors */ - } - box->height = height; - layout_apply_minmax_height(&content->len_ctx, box, containing_block); - - return true; -} - - -/** - * Recursively layout and position absolutely positioned boxes. - * - * \param box tree of boxes to layout - * \param containing_block current containing block - * \param cx position of box relative to containing_block - * \param cy position of box relative to containing_block - * \param content memory pool for any new boxes - * \return true on success, false on memory exhaustion - */ -static bool -layout_position_absolute(struct box *box, - struct box *containing_block, - int cx, int cy, - html_content *content) -{ - struct box *c; - - for (c = box->children; c; c = c->next) { - if ((c->type == BOX_BLOCK || c->type == BOX_TABLE || - c->type == BOX_INLINE_BLOCK) && - (css_computed_position(c->style) == - CSS_POSITION_ABSOLUTE || - css_computed_position(c->style) == - CSS_POSITION_FIXED)) { - if (!layout_absolute(c, containing_block, - cx, cy, content)) - return false; - if (!layout_position_absolute(c, c, 0, 0, content)) - return false; - } else if (c->style && css_computed_position(c->style) == - CSS_POSITION_RELATIVE) { - if (!layout_position_absolute(c, c, 0, 0, content)) - return false; - } else { - int px, py; - if (c->style && (css_computed_float(c->style) == - CSS_FLOAT_LEFT || - css_computed_float(c->style) == - CSS_FLOAT_RIGHT)) { - /* Float x/y coords are relative to nearest - * ansestor with float_children, rather than - * relative to parent. Need to get x/y relative - * to parent */ - struct box *p; - px = c->x; - py = c->y; - for (p = box->parent; p && !p->float_children; - p = p->parent) { - px -= p->x; - py -= p->y; - } - } else { - /* Not a float, so box x/y coords are relative - * to parent */ - px = c->x; - py = c->y; - } - if (!layout_position_absolute(c, containing_block, - cx + px, cy + py, content)) - return false; - } - } - - return true; -} - - -/** - * Compute a box's relative offset as per CSS 2.1 9.4.3 - * - * \param len_ctx Length conversion context - * \param box Box to compute relative offsets for. - * \param x Receives relative offset in x. - * \param y Receives relative offset in y. - */ -static void layout_compute_relative_offset( - const nscss_len_ctx *len_ctx, - struct box *box, - int *x, - int *y) -{ - int left, right, top, bottom; - struct box *containing_block; - - assert(box && box->parent && box->style && - css_computed_position(box->style) == - CSS_POSITION_RELATIVE); - - if (box->float_container && - (css_computed_float(box->style) == CSS_FLOAT_LEFT || - css_computed_float(box->style) == CSS_FLOAT_RIGHT)) { - containing_block = box->float_container; - } else { - containing_block = box->parent; - } - - layout_compute_offsets(len_ctx, box, containing_block, - &top, &right, &bottom, &left); - - if (left == AUTO && right == AUTO) - left = right = 0; - else if (left == AUTO) - /* left is auto => computed = -right */ - left = -right; - else if (right == AUTO) - /* right is auto => computed = -left */ - right = -left; - else { - /* over constrained => examine direction property - * of containing block */ - if (containing_block->style && - css_computed_direction( - containing_block->style) == - CSS_DIRECTION_RTL) { - /* right wins */ - left = -right; - } else { - /* assume LTR in all other cases */ - right = -left; - } - } - - assert(left == -right); - - if (top == AUTO && bottom == AUTO) { - top = bottom = 0; - } else if (top == AUTO) { - top = -bottom; - } else { - /* bottom is AUTO, or neither are AUTO */ - bottom = -top; - } - - NSLOG(layout, DEBUG, "left %i, right %i, top %i, bottom %i", left, - right, top, bottom); - - *x = left; - *y = top; -} - - -/** - * Adjust positions of relatively positioned boxes. - * - * \param len_ctx Length conversion context - * \param root box to adjust the position of - * \param fp box which forms the block formatting context for children of - * "root" which are floats - * \param fx x offset due to intervening relatively positioned boxes - * between current box, "root", and the block formatting context - * box, "fp", for float children of "root" - * \param fy y offset due to intervening relatively positioned boxes - * between current box, "root", and the block formatting context - * box, "fp", for float children of "root" - */ -static void -layout_position_relative( - const nscss_len_ctx *len_ctx, - struct box *root, - struct box *fp, - int fx, - int fy) -{ - struct box *box; /* for children of "root" */ - struct box *fn; /* for block formatting context box for children of - * "box" */ - struct box *fc; /* for float children of the block formatting context, - * "fp" */ - int x, y; /* for the offsets resulting from any relative - * positioning on the current block */ - int fnx, fny; /* for affsets which apply to flat children of "box" */ - - /**\todo ensure containing box is large enough after moving boxes */ - - assert(root); - - /* Normal children */ - for (box = root->children; box; box = box->next) { - - if (box->type == BOX_TEXT) - continue; - - /* If relatively positioned, get offsets */ - if (box->style && css_computed_position(box->style) == - CSS_POSITION_RELATIVE) - layout_compute_relative_offset( - len_ctx, box, &x, &y); - else - x = y = 0; - - /* Adjust float coordinates. - * (note float x and y are relative to their block formatting - * context box and not their parent) */ - if (box->style && (css_computed_float(box->style) == - CSS_FLOAT_LEFT || - css_computed_float(box->style) == - CSS_FLOAT_RIGHT) && - (fx != 0 || fy != 0)) { - /* box is a float and there is a float offset to - * apply */ - for (fc = fp->float_children; fc; fc = fc->next_float) { - if (box == fc->children) { - /* Box is floated in the block - * formatting context block, fp. - * Apply float offsets. */ - box->x += fx; - box->y += fy; - fx = fy = 0; - } - } - } - - if (box->float_children) { - fn = box; - fnx = fny = 0; - } else { - fn = fp; - fnx = fx + x; - fny = fy + y; - } - - /* recurse first */ - layout_position_relative(len_ctx, box, fn, fnx, fny); - - /* Ignore things we're not interested in. */ - if (!box->style || (box->style && - css_computed_position(box->style) != - CSS_POSITION_RELATIVE)) - continue; - - box->x += x; - box->y += y; - - /* Handle INLINEs - their "children" are in fact - * the sibling boxes between the INLINE and - * INLINE_END boxes */ - if (box->type == BOX_INLINE && box->inline_end) { - struct box *b; - for (b = box->next; b && b != box->inline_end; - b = b->next) { - b->x += x; - b->y += y; - } - } - } -} - - -/** - * Find a box's bounding box relative to itself, i.e. the box's border edge box - * - * \param len_ctx Length conversion context - * \param box box find bounding box of - * \param desc_x0 updated to left of box's bbox - * \param desc_y0 updated to top of box's bbox - * \param desc_x1 updated to right of box's bbox - * \param desc_y1 updated to bottom of box's bbox - */ -static void -layout_get_box_bbox( - const nscss_len_ctx *len_ctx, - struct box *box, - int *desc_x0, int *desc_y0, - int *desc_x1, int *desc_y1) -{ - *desc_x0 = -box->border[LEFT].width; - *desc_y0 = -box->border[TOP].width; - *desc_x1 = box->padding[LEFT] + box->width + box->padding[RIGHT] + - box->border[RIGHT].width; - *desc_y1 = box->padding[TOP] + box->height + box->padding[BOTTOM] + - box->border[BOTTOM].width; - - /* To stop the top of text getting clipped when css line-height is - * reduced, we increase the top of the descendant bbox. */ - if (box->type == BOX_BLOCK && box->style != NULL && - css_computed_overflow_y(box->style) == - CSS_OVERFLOW_VISIBLE && - box->object == NULL) { - css_fixed font_size = 0; - css_unit font_unit = CSS_UNIT_PT; - int text_height; - - css_computed_font_size(box->style, &font_size, &font_unit); - text_height = nscss_len2px(len_ctx, font_size, font_unit, - box->style); - text_height = FIXTOINT(text_height * 3 / 4); - *desc_y0 = (*desc_y0 < -text_height) ? *desc_y0 : -text_height; - } -} - - -/** - * Apply changes to box descendant_[xy][01] values due to given child. - * - * \param len_ctx Length conversion context - * \param box box to update - * \param child a box, which may affect box's descendant bbox - * \param off_x offset to apply to child->x coord to treat as child of box - * \param off_y offset to apply to child->y coord to treat as child of box - */ -static void -layout_update_descendant_bbox( - const nscss_len_ctx *len_ctx, - struct box *box, - struct box *child, - int off_x, - int off_y) -{ - int child_desc_x0, child_desc_y0, child_desc_x1, child_desc_y1; - - /* get coordinates of child relative to box */ - int child_x = child->x - off_x; - int child_y = child->y - off_y; - - bool html_object = (child->object && - content_get_type(child->object) == CONTENT_HTML); - - enum css_overflow_e overflow_x = CSS_OVERFLOW_VISIBLE; - enum css_overflow_e overflow_y = CSS_OVERFLOW_VISIBLE; - - if (child->style != NULL) { - overflow_x = css_computed_overflow_x(child->style); - overflow_y = css_computed_overflow_y(child->style); - } - - /* Get child's border edge */ - layout_get_box_bbox(len_ctx, child, - &child_desc_x0, &child_desc_y0, - &child_desc_x1, &child_desc_y1); - - if (overflow_x == CSS_OVERFLOW_VISIBLE && - html_object == false) { - /* get child's descendant bbox relative to box */ - child_desc_x0 = child->descendant_x0; - child_desc_x1 = child->descendant_x1; - } - if (overflow_y == CSS_OVERFLOW_VISIBLE && - html_object == false) { - /* get child's descendant bbox relative to box */ - child_desc_y0 = child->descendant_y0; - child_desc_y1 = child->descendant_y1; - } - - child_desc_x0 += child_x; - child_desc_y0 += child_y; - child_desc_x1 += child_x; - child_desc_y1 += child_y; - - /* increase box's descendant bbox to contain descendants */ - if (child_desc_x0 < box->descendant_x0) - box->descendant_x0 = child_desc_x0; - if (child_desc_y0 < box->descendant_y0) - box->descendant_y0 = child_desc_y0; - if (box->descendant_x1 < child_desc_x1) - box->descendant_x1 = child_desc_x1; - if (box->descendant_y1 < child_desc_y1) - box->descendant_y1 = child_desc_y1; -} - - -/** - * Recursively calculate the descendant_[xy][01] values for a laid-out box tree - * and inform iframe browser windows of their size and position. - * - * \param len_ctx Length conversion context - * \param box tree of boxes to update - */ -static void layout_calculate_descendant_bboxes( - const nscss_len_ctx *len_ctx, - struct box *box) -{ - struct box *child; - - assert(box->width != UNKNOWN_WIDTH); - assert(box->height != AUTO); - /* assert((box->width >= 0) && (box->height >= 0)); */ - - /* Initialise box's descendant box to border edge box */ - layout_get_box_bbox(len_ctx, box, - &box->descendant_x0, &box->descendant_y0, - &box->descendant_x1, &box->descendant_y1); - - /* Extend it to contain HTML contents if box is replaced */ - if (box->object && content_get_type(box->object) == CONTENT_HTML) { - if (box->descendant_x1 < content_get_width(box->object)) - box->descendant_x1 = content_get_width(box->object); - if (box->descendant_y1 < content_get_height(box->object)) - box->descendant_y1 = content_get_height(box->object); - } - - if (box->iframe != NULL) { - int x, y; - box_coords(box, &x, &y); - - browser_window_set_position(box->iframe, x, y); - browser_window_set_dimensions(box->iframe, - box->width, box->height); - browser_window_reformat(box->iframe, true, - box->width, box->height); - } - - if (box->type == BOX_INLINE || box->type == BOX_TEXT) - return; - - if (box->type == BOX_INLINE_END) { - box = box->inline_end; - for (child = box->next; child; - child = child->next) { - if (child->type == BOX_FLOAT_LEFT || - child->type == BOX_FLOAT_RIGHT) - continue; - - layout_update_descendant_bbox(len_ctx, box, child, - box->x, box->y); - - if (child == box->inline_end) - break; - } - return; - } - - if (box->flags & REPLACE_DIM) - /* Box's children aren't displayed if the box is replaced */ - return; - - for (child = box->children; child; child = child->next) { - if (child->type == BOX_FLOAT_LEFT || - child->type == BOX_FLOAT_RIGHT) - continue; - - layout_calculate_descendant_bboxes(len_ctx, child); - - if (box->style && css_computed_overflow_x(box->style) == - CSS_OVERFLOW_HIDDEN && - css_computed_overflow_y(box->style) == - CSS_OVERFLOW_HIDDEN) - continue; - - layout_update_descendant_bbox(len_ctx, box, child, 0, 0); - } - - for (child = box->float_children; child; child = child->next_float) { - assert(child->type == BOX_FLOAT_LEFT || - child->type == BOX_FLOAT_RIGHT); - - layout_calculate_descendant_bboxes(len_ctx, child); - - layout_update_descendant_bbox(len_ctx, box, child, 0, 0); - } - - if (box->list_marker) { - child = box->list_marker; - layout_calculate_descendant_bboxes(len_ctx, child); - - layout_update_descendant_bbox(len_ctx, box, child, 0, 0); - } -} - - -/* exported function documented in render/layout.h */ -bool layout_document(html_content *content, int width, int height) -{ - bool ret; - struct box *doc = content->layout; - const struct gui_layout_table *font_func = content->font_func; - - layout_minmax_block(doc, font_func, content); - - layout_block_find_dimensions(&content->len_ctx, - width, height, 0, 0, doc); - doc->x = doc->margin[LEFT] + doc->border[LEFT].width; - doc->y = doc->margin[TOP] + doc->border[TOP].width; - width -= doc->margin[LEFT] + doc->border[LEFT].width + - doc->padding[LEFT] + doc->padding[RIGHT] + - doc->border[RIGHT].width + doc->margin[RIGHT]; - if (width < 0) { - width = 0; - } - doc->width = width; - - ret = layout_block_context(doc, height, content); - - /* make <html> and <body> fill available height */ - if (doc->y + doc->padding[TOP] + doc->height + doc->padding[BOTTOM] + - doc->border[BOTTOM].width + doc->margin[BOTTOM] < - height) { - doc->height = height - (doc->y + doc->padding[TOP] + - doc->padding[BOTTOM] + - doc->border[BOTTOM].width + - doc->margin[BOTTOM]); - if (doc->children) - doc->children->height = doc->height - - (doc->children->margin[TOP] + - doc->children->border[TOP].width + - doc->children->padding[TOP] + - doc->children->padding[BOTTOM] + - doc->children->border[BOTTOM].width + - doc->children->margin[BOTTOM]); - } - - layout_lists(doc, font_func, &content->len_ctx); - layout_position_absolute(doc, doc, 0, 0, content); - layout_position_relative(&content->len_ctx, doc, doc, 0, 0); - - layout_calculate_descendant_bboxes(&content->len_ctx, doc); - - return ret; -} diff --git a/render/layout.h b/render/layout.h deleted file mode 100644 index cd5ddd77f..000000000 --- a/render/layout.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2003 James Bursa <bursa@users.sourceforge.net> - * - * 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 <http://www.gnu.org/licenses/>. - */ - -/** - * \file - * HTML layout (interface). - * - * The main interface to the layout code is layout_document(), which takes a - * normalized box tree and assigns coordinates and dimensions to the boxes, and - * also adds boxes to the tree (eg. when formatting lines of text). - */ - -#ifndef _NETSURF_RENDER_LAYOUT_H_ -#define _NETSURF_RENDER_LAYOUT_H_ - -struct box; -struct html_content; -struct gui_layout_table; - -/** - * Calculate positions of boxes in a document. - * - * \param content content of type CONTENT_HTML - * \param width available width - * \param height available height - * \return true on success, false on memory exhaustion - */ -bool layout_document(struct html_content *content, int width, int height); - -#endif diff --git a/render/search.c b/render/search.c deleted file mode 100644 index ca9520165..000000000 --- a/render/search.c +++ /dev/null @@ -1,656 +0,0 @@ -/* - * Copyright 2004 John M Bell <jmb202@ecs.soton.ac.uk> - * Copyright 2005 Adrian Lees <adrianl@users.sourceforge.net> - * Copyright 2009 Mark Benjamin <netsurf-browser.org.MarkBenjamin@dfgh.net> - * - * 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 <http://www.gnu.org/licenses/>. - */ - -/** - * \file - * Free text search (core) - */ - -#include <ctype.h> -#include <string.h> -#include <dom/dom.h> - -#include "utils/config.h" -#include "utils/log.h" -#include "utils/messages.h" -#include "utils/utils.h" -#include "content/content.h" -#include "content/hlcache.h" -#include "desktop/selection.h" -#include "netsurf/search.h" -#include "netsurf/misc.h" -#include "desktop/gui_internal.h" - -#include "render/box.h" -#include "render/html.h" -#include "render/html_internal.h" -#include "render/search.h" -#include "render/textplain.h" - -#ifndef NOF_ELEMENTS -#define NOF_ELEMENTS(array) (sizeof(array)/sizeof(*(array))) -#endif - - -struct list_entry { - unsigned start_idx; /* start position of match */ - unsigned end_idx; /* end of match */ - - struct box *start_box; /* used only for html contents */ - struct box *end_box; - - struct selection *sel; - - struct list_entry *prev; - struct list_entry *next; -}; - -struct search_context { - void *gui_p; - struct content *c; - struct list_entry *found; - struct list_entry *current; /* first for select all */ - char *string; - bool prev_case_sens; - bool newsearch; - bool is_html; -}; - - -/* Exported function documented in search.h */ -struct search_context * search_create_context(struct content *c, - content_type type, void *gui_data) -{ - struct search_context *context; - struct list_entry *search_head; - - if (type != CONTENT_HTML && type != CONTENT_TEXTPLAIN) { - return NULL; - } - - context = malloc(sizeof(struct search_context)); - if (context == NULL) { - guit->misc->warning("NoMemory", 0); - return NULL; - } - - search_head = malloc(sizeof(struct list_entry)); - if (search_head == NULL) { - guit->misc->warning("NoMemory", 0); - free(context); - return NULL; - } - - search_head->start_idx = 0; - search_head->end_idx = 0; - search_head->start_box = NULL; - search_head->end_box = NULL; - search_head->sel = NULL; - search_head->prev = NULL; - search_head->next = NULL; - - context->found = search_head; - context->current = NULL; - context->string = NULL; - context->prev_case_sens = false; - context->newsearch = true; - context->c = c; - context->is_html = (type == CONTENT_HTML) ? true : false; - context->gui_p = gui_data; - - return context; -} - - -/** - * Release the memory used by the list of matches, - * deleting selection objects too - */ - -static void free_matches(struct search_context *context) -{ - struct list_entry *a; - struct list_entry *b; - - a = context->found->next; - - /* empty the list before clearing and deleting the - * selections because the the clearing updates the - * screen immediately, causing nested accesses to the list */ - - context->found->prev = NULL; - context->found->next = NULL; - - for (; a; a = b) { - b = a->next; - if (a->sel) { - selection_clear(a->sel, true); - selection_destroy(a->sel); - } - free(a); - } -} - - -/** - * Find the first occurrence of 'match' in 'string' and return its index - * - * \param string the string to be searched (unterminated) - * \param s_len length of the string to be searched - * \param pattern the pattern for which we are searching (unterminated) - * \param p_len length of pattern - * \param case_sens true iff case sensitive match required - * \param m_len accepts length of match in bytes - * \return pointer to first match, NULL if none - */ - -static const char *find_pattern(const char *string, int s_len, - const char *pattern, int p_len, bool case_sens, - unsigned int *m_len) -{ - struct { const char *ss, *s, *p; bool first; } context[16]; - const char *ep = pattern + p_len; - const char *es = string + s_len; - const char *p = pattern - 1; /* a virtual '*' before the pattern */ - const char *ss = string; - const char *s = string; - bool first = true; - int top = 0; - - while (p < ep) { - bool matches; - if (p < pattern || *p == '*') { - char ch; - - /* skip any further asterisks; one is the same as many - */ - do p++; while (p < ep && *p == '*'); - - /* if we're at the end of the pattern, yes, it matches - */ - if (p >= ep) break; - - /* anything matches a # so continue matching from - here, and stack a context that will try to match - the wildcard against the next character */ - - ch = *p; - if (ch != '#') { - /* scan forwards until we find a match for - this char */ - if (!case_sens) ch = toupper(ch); - while (s < es) { - if (case_sens) { - if (*s == ch) break; - } else if (toupper(*s) == ch) - break; - s++; - } - } - - if (s < es) { - /* remember where we are in case the match - fails; we may then resume */ - if (top < (int)NOF_ELEMENTS(context)) { - context[top].ss = ss; - context[top].s = s + 1; - context[top].p = p - 1; - /* ptr to last asterisk */ - context[top].first = first; - top++; - } - - if (first) { - ss = s; - /* remember first non-'*' char */ - first = false; - } - - matches = true; - } else { - matches = false; - } - - } else if (s < es) { - char ch = *p; - if (ch == '#') - matches = true; - else { - if (case_sens) - matches = (*s == ch); - else - matches = (toupper(*s) == toupper(ch)); - } - if (matches && first) { - ss = s; /* remember first non-'*' char */ - first = false; - } - } else { - matches = false; - } - - if (matches) { - p++; s++; - } else { - /* doesn't match, - * resume with stacked context if we have one */ - if (--top < 0) - return NULL; /* no match, give up */ - - ss = context[top].ss; - s = context[top].s; - p = context[top].p; - first = context[top].first; - } - } - - /* end of pattern reached */ - *m_len = max(s - ss, 1); - return ss; -} - - -/** - * Add a new entry to the list of matches - * - * \param start_idx Offset of match start within textual representation - * \param end_idx Offset of match end - * \param context The search context to add the entry to. - * \return Pointer to added entry, NULL iff failed. - */ - -static struct list_entry *add_entry(unsigned start_idx, unsigned end_idx, - struct search_context *context) -{ - struct list_entry *entry; - - /* found string in box => add to list */ - entry = calloc(1, sizeof(*entry)); - if (!entry) { - guit->misc->warning("NoMemory", 0); - return NULL; - } - - entry->start_idx = start_idx; - entry->end_idx = end_idx; - entry->sel = NULL; - - entry->next = 0; - entry->prev = context->found->prev; - - if (context->found->prev == NULL) - context->found->next = entry; - else - context->found->prev->next = entry; - - context->found->prev = entry; - - return entry; -} - - -/** - * Finds all occurrences of a given string in the html box tree - * - * \param pattern the string pattern to search for - * \param p_len pattern length - * \param cur pointer to the current box - * \param case_sens whether to perform a case sensitive search - * \param context The search context to add the entry to. - * \return true on success, false on memory allocation failure - */ -static bool find_occurrences_html(const char *pattern, int p_len, - struct box *cur, bool case_sens, - struct search_context *context) -{ - struct box *a; - - /* ignore this box, if there's no visible text */ - if (!cur->object && cur->text) { - const char *text = cur->text; - unsigned length = cur->length; - - while (length > 0) { - struct list_entry *entry; - unsigned match_length; - unsigned match_offset; - const char *new_text; - const char *pos = find_pattern(text, length, - pattern, p_len, case_sens, - &match_length); - if (!pos) - break; - - /* found string in box => add to list */ - match_offset = pos - cur->text; - - entry = add_entry(cur->byte_offset + match_offset, - cur->byte_offset + - match_offset + - match_length, context); - if (!entry) - return false; - - entry->start_box = cur; - entry->end_box = cur; - - new_text = pos + match_length; - length -= (new_text - text); - text = new_text; - } - } - - /* and recurse */ - for (a = cur->children; a; a = a->next) { - if (!find_occurrences_html(pattern, p_len, a, case_sens, - context)) - return false; - } - - return true; -} - - -/** - * Finds all occurrences of a given string in a textplain content - * - * \param pattern the string pattern to search for - * \param p_len pattern length - * \param c the content to be searched - * \param case_sens whether to perform a case sensitive search - * \param context The search context to add the entry to. - * \return true on success, false on memory allocation failure - */ - -static bool find_occurrences_text(const char *pattern, int p_len, - struct content *c, bool case_sens, - struct search_context *context) -{ - int nlines = textplain_line_count(c); - int line; - - for(line = 0; line < nlines; line++) { - size_t offset, length; - const char *text = textplain_get_line(c, line, - &offset, &length); - if (text) { - while (length > 0) { - struct list_entry *entry; - unsigned match_length; - size_t start_idx; - const char *new_text; - const char *pos = find_pattern(text, length, - pattern, p_len, case_sens, - &match_length); - if (!pos) - break; - - /* found string in line => add to list */ - start_idx = offset + (pos - text); - entry = add_entry(start_idx, start_idx + - match_length, context); - if (!entry) - return false; - - new_text = pos + match_length; - offset += (new_text - text); - length -= (new_text - text); - text = new_text; - } - } - } - - return true; -} - - -/** - * Search for a string in the box tree - * - * \param string the string to search for - * \param string_len length of search string - * \param context The search context to add the entry to. - * \param flags flags to control the search. - */ -static void search_text(const char *string, int string_len, - struct search_context *context, search_flags_t flags) -{ - struct rect bounds; - struct box *box = NULL; - union content_msg_data msg_data; - bool case_sensitive, forwards, showall; - - case_sensitive = ((flags & SEARCH_FLAG_CASE_SENSITIVE) != 0) ? - true : false; - forwards = ((flags & SEARCH_FLAG_FORWARDS) != 0) ? true : false; - showall = ((flags & SEARCH_FLAG_SHOWALL) != 0) ? true : false; - - if (context->c == NULL) - return; - - if (context->is_html == true) { - html_content *html = (html_content *)context->c; - - box = html->layout; - - if (!box) - return; - } - - - /* check if we need to start a new search or continue an old one */ - if (context->newsearch) { - bool res; - - if (context->string != NULL) - free(context->string); - - context->current = NULL; - free_matches(context); - - context->string = malloc(string_len + 1); - if (context->string != NULL) { - memcpy(context->string, string, string_len); - context->string[string_len] = '\0'; - } - - guit->search->hourglass(true, context->gui_p); - - if (context->is_html == true) { - res = find_occurrences_html(string, string_len, - box, case_sensitive, context); - } else { - res = find_occurrences_text(string, string_len, - context->c, case_sensitive, context); - } - - if (!res) { - free_matches(context); - guit->search->hourglass(false, context->gui_p); - return; - } - guit->search->hourglass(false, context->gui_p); - - context->prev_case_sens = case_sensitive; - - /* new search, beginning at the top of the page */ - context->current = context->found->next; - context->newsearch = false; - - } else if (context->current != NULL) { - /* continued search in the direction specified */ - if (forwards) { - if (context->current->next) - context->current = context->current->next; - } else { - if (context->current->prev) - context->current = context->current->prev; - } - } - - guit->search->status((context->current != NULL), context->gui_p); - - search_show_all(showall, context); - - guit->search->back_state((context->current != NULL) && - (context->current->prev != NULL), - context->gui_p); - guit->search->forward_state((context->current != NULL) && - (context->current->next != NULL), - context->gui_p); - - if (context->current == NULL) - return; - - if (context->is_html == true) { - /* get box position and jump to it */ - box_coords(context->current->start_box, &bounds.x0, &bounds.y0); - /* \todo: move x0 in by correct idx */ - box_coords(context->current->end_box, &bounds.x1, &bounds.y1); - /* \todo: move x1 in by correct idx */ - bounds.x1 += context->current->end_box->width; - bounds.y1 += context->current->end_box->height; - } else { - textplain_coords_from_range(context->c, - context->current->start_idx, - context->current->end_idx, &bounds); - } - - msg_data.scroll.area = true; - msg_data.scroll.x0 = bounds.x0; - msg_data.scroll.y0 = bounds.y0; - msg_data.scroll.x1 = bounds.x1; - msg_data.scroll.y1 = bounds.y1; - content_broadcast(context->c, CONTENT_MSG_SCROLL, &msg_data); -} - - -/* Exported function documented in search.h */ -void search_step(struct search_context *context, search_flags_t flags, - const char *string) -{ - int string_len; - int i = 0; - - if (context == NULL) { - guit->misc->warning("SearchError", 0); - return; - } - - guit->search->add_recent(string, context->gui_p); - - string_len = strlen(string); - for (i = 0; i < string_len; i++) - if (string[i] != '#' && string[i] != '*') - break; - if (i >= string_len) { - union content_msg_data msg_data; - free_matches(context); - - guit->search->status(true, context->gui_p); - guit->search->back_state(false, context->gui_p); - guit->search->forward_state(false, context->gui_p); - - msg_data.scroll.area = false; - msg_data.scroll.x0 = 0; - msg_data.scroll.y0 = 0; - content_broadcast(context->c, CONTENT_MSG_SCROLL, &msg_data); - return; - } - search_text(string, string_len, context, flags); -} - - -/* Exported function documented in search.h */ -bool search_term_highlighted(struct content *c, - unsigned start_offset, unsigned end_offset, - unsigned *start_idx, unsigned *end_idx, - struct search_context *context) -{ - if (c == context->c) { - struct list_entry *a; - for (a = context->found->next; a; a = a->next) - if (a->sel && selection_defined(a->sel) && - selection_highlighted(a->sel, - start_offset, end_offset, - start_idx, end_idx)) - return true; - } - - return false; -} - - -/* Exported function documented in search.h */ -void search_show_all(bool all, struct search_context *context) -{ - struct list_entry *a; - - for (a = context->found->next; a; a = a->next) { - bool add = true; - if (!all && a != context->current) { - add = false; - if (a->sel) { - selection_clear(a->sel, true); - selection_destroy(a->sel); - a->sel = NULL; - } - } - if (add && !a->sel) { - - if (context->is_html == true) { - html_content *html = (html_content *)context->c; - a->sel = selection_create(context->c, true); - if (!a->sel) - continue; - - selection_init(a->sel, html->layout, - &html->len_ctx); - } else { - a->sel = selection_create(context->c, false); - if (!a->sel) - continue; - - selection_init(a->sel, NULL, NULL); - } - - selection_set_start(a->sel, a->start_idx); - selection_set_end(a->sel, a->end_idx); - } - } -} - - -/* Exported function documented in search.h */ -void search_destroy_context(struct search_context *context) -{ - assert(context != NULL); - - if (context->string != NULL) { - guit->search->add_recent(context->string, context->gui_p); - free(context->string); - } - - guit->search->forward_state(true, context->gui_p); - guit->search->back_state(true, context->gui_p); - - free_matches(context); - free(context); -} diff --git a/render/search.h b/render/search.h deleted file mode 100644 index 79d1ee3d3..000000000 --- a/render/search.h +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright 2009 Mark Benjamin <netsurf-browser.org.MarkBenjamin@dfgh.net> - * - * 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 <http://www.gnu.org/licenses/>. - */ - -#ifndef _NETSURF_RENDER_SEARCH_H_ -#define _NETSURF_RENDER_SEARCH_H_ - -#include <ctype.h> -#include <string.h> - -#include "desktop/search.h" - -struct search_context; - -/** - * create a search_context - * - * \param c The content the search_context is connected to - * \param type The content type of c - * \param context A context pointer passed to the provider routines. - * \return A new search context or NULL on error. - */ -struct search_context *search_create_context(struct content *c, - content_type type, void *context); - -/** - * Ends the search process, invalidating all state - * freeing the list of found boxes - */ -void search_destroy_context(struct search_context *context); - -/** - * Begins/continues the search process - * - * \note that this may be called many times for a single search. - * - * \param context The search context in use. - * \param flags The flags forward/back etc - * \param string The string to match - */ -void search_step(struct search_context *context, search_flags_t flags, - const char * string); - -/** - * Specifies whether all matches or just the current match should - * be highlighted in the search text. - */ -void search_show_all(bool all, struct search_context *context); - -/** - * Determines whether any portion of the given text box should be - * selected because it matches the current search string. - * - * \param c The content to hilight within. - * \param start_offset byte offset within text of string to be checked - * \param end_offset byte offset within text - * \param start_idx byte offset within string of highlight start - * \param end_idx byte offset of highlight end - * \param context The search context to hilight entries from. - * \return true iff part of the box should be highlighted - */ -bool search_term_highlighted(struct content *c, - unsigned start_offset, unsigned end_offset, - unsigned *start_idx, unsigned *end_idx, - struct search_context *context); - -#endif diff --git a/render/table.c b/render/table.c deleted file mode 100644 index 08a2e805c..000000000 --- a/render/table.c +++ /dev/null @@ -1,1080 +0,0 @@ -/* - * Copyright 2005 James Bursa <bursa@users.sourceforge.net> - * Copyright 2005 Richard Wilson <info@tinct.net> - * - * 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 <http://www.gnu.org/licenses/>. - */ - -/** \file - * Table processing and layout (implementation). - */ - -#include <assert.h> -#include <dom/dom.h> - -#include "utils/log.h" -#include "utils/talloc.h" -#include "css/utils.h" - -#include "render/box.h" -#include "render/table.h" - -/* Define to enable verbose table debug */ -#undef TABLE_DEBUG - -/** - * Container for border values during table border calculations - */ -struct border { - enum css_border_style_e style; /**< border-style */ - enum css_border_color_e color; /**< border-color type */ - css_color c; /**< border-color value */ - css_fixed width; /**< border-width length */ - css_unit unit; /**< border-width units */ -}; - -static void table_used_left_border_for_cell( - const nscss_len_ctx *len_ctx, - struct box *cell); -static void table_used_top_border_for_cell( - const nscss_len_ctx *len_ctx, - struct box *cell); -static void table_used_right_border_for_cell( - const nscss_len_ctx *len_ctx, - struct box *cell); -static void table_used_bottom_border_for_cell( - const nscss_len_ctx *len_ctx, - struct box *cell); -static bool table_border_is_more_eyecatching( - const nscss_len_ctx *len_ctx, - const struct border *a, - box_type a_src, - const struct border *b, - box_type b_src); -static void table_cell_top_process_table( - const nscss_len_ctx *len_ctx, - struct box *table, - struct border *a, - box_type *a_src); -static bool table_cell_top_process_group( - const nscss_len_ctx *len_ctx, - struct box *cell, - struct box *group, - struct border *a, - box_type *a_src); -static bool table_cell_top_process_row( - const nscss_len_ctx *len_ctx, - struct box *cell, - struct box *row, - struct border *a, - box_type *a_src); - - -/** - * Determine the column width types for a table. - * - * \param len_ctx Length conversion context - * \param table box of type BOX_TABLE - * \return true on success, false on memory exhaustion - * - * The table->col array is allocated and type and width are filled in for each - * column. - */ - -bool table_calculate_column_types( - const nscss_len_ctx *len_ctx, - struct box *table) -{ - unsigned int i, j; - struct column *col; - struct box *row_group, *row, *cell; - - if (table->col) - /* table->col already constructed, for example frameset table */ - return true; - - table->col = col = talloc_array(table, struct column, table->columns); - if (!col) - return false; - - for (i = 0; i != table->columns; i++) { - col[i].type = COLUMN_WIDTH_UNKNOWN; - col[i].width = 0; - col[i].positioned = true; - } - - /* 1st pass: cells with colspan 1 only */ - for (row_group = table->children; row_group; row_group =row_group->next) - for (row = row_group->children; row; row = row->next) - for (cell = row->children; cell; cell = cell->next) { - enum css_width_e type; - css_fixed value = 0; - css_unit unit = CSS_UNIT_PX; - - assert(cell->type == BOX_TABLE_CELL); - assert(cell->style); - - if (cell->columns != 1) - continue; - i = cell->start_column; - - if (css_computed_position(cell->style) != - CSS_POSITION_ABSOLUTE && - css_computed_position(cell->style) != - CSS_POSITION_FIXED) { - col[i].positioned = false; - } - - type = css_computed_width(cell->style, &value, &unit); - - /* fixed width takes priority over any other width type */ - if (col[i].type != COLUMN_WIDTH_FIXED && - type == CSS_WIDTH_SET && unit != CSS_UNIT_PCT) { - col[i].type = COLUMN_WIDTH_FIXED; - col[i].width = FIXTOINT(nscss_len2px(len_ctx, - value, unit, cell->style)); - if (col[i].width < 0) - col[i].width = 0; - continue; - } - - if (col[i].type != COLUMN_WIDTH_UNKNOWN) - continue; - - if (type == CSS_WIDTH_SET && unit == CSS_UNIT_PCT) { - col[i].type = COLUMN_WIDTH_PERCENT; - col[i].width = FIXTOINT(value); - if (col[i].width < 0) - col[i].width = 0; - } else if (type == CSS_WIDTH_AUTO) { - col[i].type = COLUMN_WIDTH_AUTO; - } - } - - /* 2nd pass: cells which span multiple columns */ - for (row_group = table->children; row_group; row_group =row_group->next) - for (row = row_group->children; row; row = row->next) - for (cell = row->children; cell; cell = cell->next) { - unsigned int fixed_columns = 0, percent_columns = 0, - auto_columns = 0, unknown_columns = 0; - int fixed_width = 0, percent_width = 0; - enum css_width_e type; - css_fixed value = 0; - css_unit unit = CSS_UNIT_PX; - - if (cell->columns == 1) - continue; - i = cell->start_column; - - for (j = i; j < i + cell->columns; j++) { - col[j].positioned = false; - } - - /* count column types in spanned cells */ - for (j = 0; j != cell->columns; j++) { - if (col[i + j].type == COLUMN_WIDTH_FIXED) { - fixed_width += col[i + j].width; - fixed_columns++; - } else if (col[i + j].type == COLUMN_WIDTH_PERCENT) { - percent_width += col[i + j].width; - percent_columns++; - } else if (col[i + j].type == COLUMN_WIDTH_AUTO) { - auto_columns++; - } else { - unknown_columns++; - } - } - - if (!unknown_columns) - continue; - - type = css_computed_width(cell->style, &value, &unit); - - /* if cell is fixed width, and all spanned columns are fixed - * or unknown width, split extra width among unknown columns */ - if (type == CSS_WIDTH_SET && unit != CSS_UNIT_PCT && - fixed_columns + unknown_columns == - cell->columns) { - int width = (FIXTOFLT(nscss_len2px(len_ctx, value, unit, - cell->style)) - fixed_width) / - unknown_columns; - if (width < 0) - width = 0; - for (j = 0; j != cell->columns; j++) { - if (col[i + j].type == COLUMN_WIDTH_UNKNOWN) { - col[i + j].type = COLUMN_WIDTH_FIXED; - col[i + j].width = width; - } - } - } - - /* as above for percentage width */ - if (type == CSS_WIDTH_SET && unit == CSS_UNIT_PCT && - percent_columns + unknown_columns == - cell->columns) { - int width = (FIXTOFLT(value) - - percent_width) / unknown_columns; - if (width < 0) - width = 0; - for (j = 0; j != cell->columns; j++) { - if (col[i + j].type == COLUMN_WIDTH_UNKNOWN) { - col[i + j].type = COLUMN_WIDTH_PERCENT; - col[i + j].width = width; - } - } - } - } - - /* use AUTO if no width type was specified */ - for (i = 0; i != table->columns; i++) { - if (col[i].type == COLUMN_WIDTH_UNKNOWN) - col[i].type = COLUMN_WIDTH_AUTO; - } - -#ifdef TABLE_DEBUG - for (i = 0; i != table->columns; i++) - NSLOG(netsurf, INFO, - "table %p, column %u: type %s, width %i", table, i, ((const char *[]){ - "UNKNOWN", - "FIXED", - "AUTO", - "PERCENT", - "RELATIVE", - })[col[i].type], col[i].width); -#endif - - return true; -} - -/** - * Calculate used values of border-{trbl}-{style,color,width} for table cells. - * - * \param len_ctx Length conversion context - * \param cell Table cell to consider - * - * \post \a cell's border array is populated - */ -void table_used_border_for_cell( - const nscss_len_ctx *len_ctx, - struct box *cell) -{ - int side; - - assert(cell->type == BOX_TABLE_CELL); - - if (css_computed_border_collapse(cell->style) == - CSS_BORDER_COLLAPSE_SEPARATE) { - css_fixed width = 0; - css_unit unit = CSS_UNIT_PX; - - /* Left border */ - cell->border[LEFT].style = - css_computed_border_left_style(cell->style); - css_computed_border_left_color(cell->style, - &cell->border[LEFT].c); - css_computed_border_left_width(cell->style, &width, &unit); - cell->border[LEFT].width = - FIXTOINT(nscss_len2px(len_ctx, - width, unit, cell->style)); - - /* Top border */ - cell->border[TOP].style = - css_computed_border_top_style(cell->style); - css_computed_border_top_color(cell->style, - &cell->border[TOP].c); - css_computed_border_top_width(cell->style, &width, &unit); - cell->border[TOP].width = - FIXTOINT(nscss_len2px(len_ctx, - width, unit, cell->style)); - - /* Right border */ - cell->border[RIGHT].style = - css_computed_border_right_style(cell->style); - css_computed_border_right_color(cell->style, - &cell->border[RIGHT].c); - css_computed_border_right_width(cell->style, &width, &unit); - cell->border[RIGHT].width = - FIXTOINT(nscss_len2px(len_ctx, - width, unit, cell->style)); - - /* Bottom border */ - cell->border[BOTTOM].style = - css_computed_border_bottom_style(cell->style); - css_computed_border_bottom_color(cell->style, - &cell->border[BOTTOM].c); - css_computed_border_bottom_width(cell->style, &width, &unit); - cell->border[BOTTOM].width = - FIXTOINT(nscss_len2px(len_ctx, - width, unit, cell->style)); - } else { - /* Left border */ - table_used_left_border_for_cell(len_ctx, cell); - - /* Top border */ - table_used_top_border_for_cell(len_ctx, cell); - - /* Right border */ - table_used_right_border_for_cell(len_ctx, cell); - - /* Bottom border */ - table_used_bottom_border_for_cell(len_ctx, cell); - } - - /* Finally, ensure that any borders configured as - * hidden or none have zero width. (c.f. layout_find_dimensions) */ - for (side = 0; side != 4; side++) { - if (cell->border[side].style == CSS_BORDER_STYLE_HIDDEN || - cell->border[side].style == - CSS_BORDER_STYLE_NONE) - cell->border[side].width = 0; - } -} - -/****************************************************************************** - * Helpers for used border calculations * - ******************************************************************************/ - -/** - * Calculate used values of border-left-{style,color,width} - * - * \param len_ctx Length conversion context - * \param cell Table cell to consider - */ -void table_used_left_border_for_cell( - const nscss_len_ctx *len_ctx, - struct box *cell) -{ - struct border a, b; - box_type a_src, b_src; - - /** \todo Need column and column_group, too */ - - /* Initialise to computed left border for cell */ - a.style = css_computed_border_left_style(cell->style); - a.color = css_computed_border_left_color(cell->style, &a.c); - css_computed_border_left_width(cell->style, &a.width, &a.unit); - a.width = nscss_len2px(len_ctx, a.width, a.unit, cell->style); - a.unit = CSS_UNIT_PX; - a_src = BOX_TABLE_CELL; - - if (cell->prev != NULL || cell->start_column != 0) { - /* Cell to the left -- consider its right border */ - struct box *prev = NULL; - - if (cell->prev == NULL) { - struct box *row; - - /* Spanned from a previous row in current row group */ - for (row = cell->parent; row != NULL; row = row->prev) { - for (prev = row->children; prev != NULL; - prev = prev->next) { - if (prev->start_column + - prev->columns == - cell->start_column) - break; - } - - if (prev != NULL) - break; - } - - assert(prev != NULL); - } else { - prev = cell->prev; - } - - b.style = css_computed_border_right_style(prev->style); - b.color = css_computed_border_right_color(prev->style, &b.c); - css_computed_border_right_width(prev->style, &b.width, &b.unit); - b.width = nscss_len2px(len_ctx, b.width, b.unit, prev->style); - b.unit = CSS_UNIT_PX; - b_src = BOX_TABLE_CELL; - - if (table_border_is_more_eyecatching(len_ctx, - &a, a_src, &b, b_src)) { - a = b; - a_src = b_src; - } - } else { - /* First cell in row, so consider rows and row group */ - struct box *row = cell->parent; - struct box *group = row->parent; - struct box *table = group->parent; - unsigned int rows = cell->rows; - - while (rows-- > 0 && row != NULL) { - /* Spanned rows -- consider their left border */ - b.style = css_computed_border_left_style(row->style); - b.color = css_computed_border_left_color( - row->style, &b.c); - css_computed_border_left_width( - row->style, &b.width, &b.unit); - b.width = nscss_len2px(len_ctx, - b.width, b.unit, row->style); - b.unit = CSS_UNIT_PX; - b_src = BOX_TABLE_ROW; - - if (table_border_is_more_eyecatching(len_ctx, - &a, a_src, &b, b_src)) { - a = b; - a_src = b_src; - } - - row = row->next; - } - - /** \todo can cells span row groups? */ - - /* Row group -- consider its left border */ - b.style = css_computed_border_left_style(group->style); - b.color = css_computed_border_left_color(group->style, &b.c); - css_computed_border_left_width(group->style, &b.width, &b.unit); - b.width = nscss_len2px(len_ctx, b.width, b.unit, group->style); - b.unit = CSS_UNIT_PX; - b_src = BOX_TABLE_ROW_GROUP; - - if (table_border_is_more_eyecatching(len_ctx, - &a, a_src, &b, b_src)) { - a = b; - a_src = b_src; - } - - /* The table itself -- consider its left border */ - b.style = css_computed_border_left_style(table->style); - b.color = css_computed_border_left_color(table->style, &b.c); - css_computed_border_left_width(table->style, &b.width, &b.unit); - b.width = nscss_len2px(len_ctx, b.width, b.unit, table->style); - b.unit = CSS_UNIT_PX; - b_src = BOX_TABLE; - - if (table_border_is_more_eyecatching(len_ctx, - &a, a_src, &b, b_src)) { - a = b; - a_src = b_src; - } - } - - /* a now contains the used left border for the cell */ - cell->border[LEFT].style = a.style; - cell->border[LEFT].c = a.c; - cell->border[LEFT].width = FIXTOINT(nscss_len2px(len_ctx, - a.width, a.unit, cell->style)); -} - -/** - * Calculate used values of border-top-{style,color,width} - * - * \param len_ctx Length conversion context - * \param cell Table cell to consider - */ -void table_used_top_border_for_cell( - const nscss_len_ctx *len_ctx, - struct box *cell) -{ - struct border a, b; - box_type a_src, b_src; - struct box *row = cell->parent; - bool process_group = false; - - /* Initialise to computed top border for cell */ - a.style = css_computed_border_top_style(cell->style); - css_computed_border_top_color(cell->style, &a.c); - css_computed_border_top_width(cell->style, &a.width, &a.unit); - a.width = nscss_len2px(len_ctx, a.width, a.unit, cell->style); - a.unit = CSS_UNIT_PX; - a_src = BOX_TABLE_CELL; - - /* Top border of row */ - b.style = css_computed_border_top_style(row->style); - css_computed_border_top_color(row->style, &b.c); - css_computed_border_top_width(row->style, &b.width, &b.unit); - b.width = nscss_len2px(len_ctx, b.width, b.unit, row->style); - b.unit = CSS_UNIT_PX; - b_src = BOX_TABLE_ROW; - - if (table_border_is_more_eyecatching(len_ctx, &a, a_src, &b, b_src)) { - a = b; - a_src = b_src; - } - - if (row->prev != NULL) { - /* Consider row(s) above */ - while (table_cell_top_process_row(len_ctx, cell, row->prev, - &a, &a_src) == false) { - if (row->prev->prev == NULL) { - /* Consider row group */ - process_group = true; - break; - } else { - row = row->prev; - } - } - } else { - process_group = true; - } - - if (process_group) { - struct box *group = row->parent; - - /* Top border of row group */ - b.style = css_computed_border_top_style(group->style); - b.color = css_computed_border_top_color(group->style, &b.c); - css_computed_border_top_width(group->style, &b.width, &b.unit); - b.width = nscss_len2px(len_ctx, b.width, b.unit, group->style); - b.unit = CSS_UNIT_PX; - b_src = BOX_TABLE_ROW_GROUP; - - if (table_border_is_more_eyecatching(len_ctx, - &a, a_src, &b, b_src)) { - a = b; - a_src = b_src; - } - - if (group->prev == NULL) { - /* Top border of table */ - table_cell_top_process_table(len_ctx, - group->parent, &a, &a_src); - } else { - /* Process previous group(s) */ - while (table_cell_top_process_group(len_ctx, - cell, group->prev, - &a, &a_src) == false) { - if (group->prev->prev == NULL) { - /* Top border of table */ - table_cell_top_process_table(len_ctx, - group->parent, - &a, &a_src); - break; - } else { - group = group->prev; - } - } - } - } - - /* a now contains the used top border for the cell */ - cell->border[TOP].style = a.style; - cell->border[TOP].c = a.c; - cell->border[TOP].width = FIXTOINT(nscss_len2px(len_ctx, - a.width, a.unit, cell->style)); -} - -/** - * Calculate used values of border-right-{style,color,width} - * - * \param len_ctx Length conversion context - * \param cell Table cell to consider - */ -void table_used_right_border_for_cell( - const nscss_len_ctx *len_ctx, - struct box *cell) -{ - struct border a, b; - box_type a_src, b_src; - - /** \todo Need column and column_group, too */ - - /* Initialise to computed right border for cell */ - a.style = css_computed_border_right_style(cell->style); - css_computed_border_right_color(cell->style, &a.c); - css_computed_border_right_width(cell->style, &a.width, &a.unit); - a.width = nscss_len2px(len_ctx, a.width, a.unit, cell->style); - a.unit = CSS_UNIT_PX; - a_src = BOX_TABLE_CELL; - - if (cell->next != NULL || cell->start_column + cell->columns != - cell->parent->parent->parent->columns) { - /* Cell is not at right edge of table -- no right border */ - a.style = CSS_BORDER_STYLE_NONE; - a.width = 0; - a.unit = CSS_UNIT_PX; - } else { - /* Last cell in row, so consider rows and row group */ - struct box *row = cell->parent; - struct box *group = row->parent; - struct box *table = group->parent; - unsigned int rows = cell->rows; - - while (rows-- > 0 && row != NULL) { - /* Spanned rows -- consider their right border */ - b.style = css_computed_border_right_style(row->style); - b.color = css_computed_border_right_color( - row->style, &b.c); - css_computed_border_right_width( - row->style, &b.width, &b.unit); - b.width = nscss_len2px(len_ctx, - b.width, b.unit, row->style); - b.unit = CSS_UNIT_PX; - b_src = BOX_TABLE_ROW; - - if (table_border_is_more_eyecatching(len_ctx, - &a, a_src, &b, b_src)) { - a = b; - a_src = b_src; - } - - row = row->next; - } - - /** \todo can cells span row groups? */ - - /* Row group -- consider its right border */ - b.style = css_computed_border_right_style(group->style); - b.color = css_computed_border_right_color(group->style, &b.c); - css_computed_border_right_width(group->style, - &b.width, &b.unit); - b.width = nscss_len2px(len_ctx, b.width, b.unit, group->style); - b.unit = CSS_UNIT_PX; - b_src = BOX_TABLE_ROW_GROUP; - - if (table_border_is_more_eyecatching(len_ctx, - &a, a_src, &b, b_src)) { - a = b; - a_src = b_src; - } - - /* The table itself -- consider its right border */ - b.style = css_computed_border_right_style(table->style); - b.color = css_computed_border_right_color(table->style, &b.c); - css_computed_border_right_width(table->style, - &b.width, &b.unit); - b.width = nscss_len2px(len_ctx, b.width, b.unit, table->style); - b.unit = CSS_UNIT_PX; - b_src = BOX_TABLE; - - if (table_border_is_more_eyecatching(len_ctx, - &a, a_src, &b, b_src)) { - a = b; - a_src = b_src; - } - } - - /* a now contains the used right border for the cell */ - cell->border[RIGHT].style = a.style; - cell->border[RIGHT].c = a.c; - cell->border[RIGHT].width = FIXTOINT(nscss_len2px(len_ctx, - a.width, a.unit, cell->style)); -} - -/** - * Calculate used values of border-bottom-{style,color,width} - * - * \param len_ctx Length conversion context - * \param cell Table cell to consider - */ -void table_used_bottom_border_for_cell( - const nscss_len_ctx *len_ctx, - struct box *cell) -{ - struct border a, b; - box_type a_src, b_src; - struct box *row = cell->parent; - unsigned int rows = cell->rows; - - /* Initialise to computed bottom border for cell */ - a.style = css_computed_border_bottom_style(cell->style); - css_computed_border_bottom_color(cell->style, &a.c); - css_computed_border_bottom_width(cell->style, &a.width, &a.unit); - a.width = nscss_len2px(len_ctx, a.width, a.unit, cell->style); - a.unit = CSS_UNIT_PX; - a_src = BOX_TABLE_CELL; - - while (rows-- > 0 && row != NULL) - row = row->next; - - /** \todo Can cells span row groups? */ - - if (row != NULL) { - /* Cell is not at bottom edge of table -- no bottom border */ - a.style = CSS_BORDER_STYLE_NONE; - a.width = 0; - a.unit = CSS_UNIT_PX; - } else { - /* Cell at bottom of table, so consider row and row group */ - struct box *row = cell->parent; - struct box *group = row->parent; - struct box *table = group->parent; - - /* Bottom border of row */ - b.style = css_computed_border_bottom_style(row->style); - b.color = css_computed_border_bottom_color(row->style, &b.c); - css_computed_border_bottom_width(row->style, &b.width, &b.unit); - b.width = nscss_len2px(len_ctx, b.width, b.unit, row->style); - b.unit = CSS_UNIT_PX; - b_src = BOX_TABLE_ROW; - - if (table_border_is_more_eyecatching(len_ctx, - &a, a_src, &b, b_src)) { - a = b; - a_src = b_src; - } - - /* Row group -- consider its bottom border */ - b.style = css_computed_border_bottom_style(group->style); - b.color = css_computed_border_bottom_color(group->style, &b.c); - css_computed_border_bottom_width(group->style, - &b.width, &b.unit); - b.width = nscss_len2px(len_ctx, b.width, b.unit, group->style); - b.unit = CSS_UNIT_PX; - b_src = BOX_TABLE_ROW_GROUP; - - if (table_border_is_more_eyecatching(len_ctx, - &a, a_src, &b, b_src)) { - a = b; - a_src = b_src; - } - - /* The table itself -- consider its bottom border */ - b.style = css_computed_border_bottom_style(table->style); - b.color = css_computed_border_bottom_color(table->style, &b.c); - css_computed_border_bottom_width(table->style, - &b.width, &b.unit); - b.width = nscss_len2px(len_ctx, b.width, b.unit, table->style); - b.unit = CSS_UNIT_PX; - b_src = BOX_TABLE; - - if (table_border_is_more_eyecatching(len_ctx, - &a, a_src, &b, b_src)) { - a = b; - } - } - - /* a now contains the used bottom border for the cell */ - cell->border[BOTTOM].style = a.style; - cell->border[BOTTOM].c = a.c; - cell->border[BOTTOM].width = FIXTOINT(nscss_len2px(len_ctx, - a.width, a.unit, cell->style)); -} - -/** - * Determine if a border style is more eyecatching than another - * - * \param len_ctx Length conversion context - * \param a Reference border style - * \param a_src Source of \a a - * \param b Candidate border style - * \param b_src Source of \a b - * \return True if \a b is more eyecatching than \a a - */ -bool table_border_is_more_eyecatching( - const nscss_len_ctx *len_ctx, - const struct border *a, - box_type a_src, - const struct border *b, - box_type b_src) -{ - css_fixed awidth, bwidth; - int impact = 0; - - /* See CSS 2.1 $17.6.2.1 */ - - /* 1 + 2 -- hidden beats everything, none beats nothing */ - if (a->style == CSS_BORDER_STYLE_HIDDEN || - b->style == CSS_BORDER_STYLE_NONE) - return false; - - if (b->style == CSS_BORDER_STYLE_HIDDEN || - a->style == CSS_BORDER_STYLE_NONE) - return true; - - /* 3a -- wider borders beat narrow ones */ - /* The widths must be absolute, which will be the case - * if they've come from a computed style. */ - assert(a->unit != CSS_UNIT_EM && a->unit != CSS_UNIT_EX); - assert(b->unit != CSS_UNIT_EM && b->unit != CSS_UNIT_EX); - awidth = nscss_len2px(len_ctx, a->width, a->unit, NULL); - bwidth = nscss_len2px(len_ctx, b->width, b->unit, NULL); - - if (awidth < bwidth) - return true; - else if (bwidth < awidth) - return false; - - /* 3b -- sort by style */ - switch (a->style) { - case CSS_BORDER_STYLE_DOUBLE: impact++; /* Fall through */ - case CSS_BORDER_STYLE_SOLID: impact++; /* Fall through */ - case CSS_BORDER_STYLE_DASHED: impact++; /* Fall through */ - case CSS_BORDER_STYLE_DOTTED: impact++; /* Fall through */ - case CSS_BORDER_STYLE_RIDGE: impact++; /* Fall through */ - case CSS_BORDER_STYLE_OUTSET: impact++; /* Fall through */ - case CSS_BORDER_STYLE_GROOVE: impact++; /* Fall through */ - case CSS_BORDER_STYLE_INSET: impact++; /* Fall through */ - default: - break; - } - - switch (b->style) { - case CSS_BORDER_STYLE_DOUBLE: impact--; /* Fall through */ - case CSS_BORDER_STYLE_SOLID: impact--; /* Fall through */ - case CSS_BORDER_STYLE_DASHED: impact--; /* Fall through */ - case CSS_BORDER_STYLE_DOTTED: impact--; /* Fall through */ - case CSS_BORDER_STYLE_RIDGE: impact--; /* Fall through */ - case CSS_BORDER_STYLE_OUTSET: impact--; /* Fall through */ - case CSS_BORDER_STYLE_GROOVE: impact--; /* Fall through */ - case CSS_BORDER_STYLE_INSET: impact--; /* Fall through */ - default: - break; - } - - if (impact < 0) - return true; - else if (impact > 0) - return false; - - /* 4a -- sort by origin */ - impact = 0; - - /** \todo COL/COL_GROUP */ - switch (a_src) { - case BOX_TABLE_CELL: impact++; /* Fall through */ - case BOX_TABLE_ROW: impact++; /* Fall through */ - case BOX_TABLE_ROW_GROUP: impact++; /* Fall through */ - case BOX_TABLE: impact++; /* Fall through */ - default: - break; - } - - /** \todo COL/COL_GROUP */ - switch (b_src) { - case BOX_TABLE_CELL: impact--; /* Fall through */ - case BOX_TABLE_ROW: impact--; /* Fall through */ - case BOX_TABLE_ROW_GROUP: impact--; /* Fall through */ - case BOX_TABLE: impact--; /* Fall through */ - default: - break; - } - - if (impact < 0) - return true; - else if (impact > 0) - return false; - - /* 4b -- furthest left (if direction: ltr) and towards top wins */ - /** \todo Currently assumes b satisifies this */ - return true; -} - -/****************************************************************************** - * Helpers for top border collapsing * - ******************************************************************************/ - -/** - * Process a table - * - * \param len_ctx Length conversion context - * \param table Table to process - * \param a Current border style for cell - * \param a_src Source of \a a - * - * \post \a a will be updated with most eyecatching style - * \post \a a_src will be updated also - */ -void table_cell_top_process_table( - const nscss_len_ctx *len_ctx, - struct box *table, - struct border *a, - box_type *a_src) -{ - struct border b; - box_type b_src; - - /* Top border of table */ - b.style = css_computed_border_top_style(table->style); - b.color = css_computed_border_top_color(table->style, &b.c); - css_computed_border_top_width(table->style, &b.width, &b.unit); - b.width = nscss_len2px(len_ctx, b.width, b.unit, table->style); - b.unit = CSS_UNIT_PX; - b_src = BOX_TABLE; - - if (table_border_is_more_eyecatching(len_ctx, a, *a_src, &b, b_src)) { - *a = b; - *a_src = b_src; - } -} - -/** - * Process a group - * - * \param len_ctx Length conversion context - * \param cell Cell being considered - * \param group Group to process - * \param a Current border style for cell - * \param a_src Source of \a a - * \return true if group has non-empty rows, false otherwise - * - * \post \a a will be updated with most eyecatching style - * \post \a a_src will be updated also - */ -bool table_cell_top_process_group( - const nscss_len_ctx *len_ctx, - struct box *cell, - struct box *group, - struct border *a, - box_type *a_src) -{ - struct border b; - box_type b_src; - - /* Bottom border of group */ - b.style = css_computed_border_bottom_style(group->style); - b.color = css_computed_border_bottom_color(group->style, &b.c); - css_computed_border_bottom_width(group->style, &b.width, &b.unit); - b.width = nscss_len2px(len_ctx, b.width, b.unit, group->style); - b.unit = CSS_UNIT_PX; - b_src = BOX_TABLE_ROW_GROUP; - - if (table_border_is_more_eyecatching(len_ctx, a, *a_src, &b, b_src)) { - *a = b; - *a_src = b_src; - } - - if (group->last != NULL) { - /* Process rows in group, starting with last */ - struct box *row = group->last; - - while (table_cell_top_process_row(len_ctx, cell, row, - a, a_src) == false) { - if (row->prev == NULL) { - return false; - } else { - row = row->prev; - } - } - } else { - /* Group is empty, so consider its top border */ - b.style = css_computed_border_top_style(group->style); - b.color = css_computed_border_top_color(group->style, &b.c); - css_computed_border_top_width(group->style, &b.width, &b.unit); - b.width = nscss_len2px(len_ctx, b.width, b.unit, group->style); - b.unit = CSS_UNIT_PX; - b_src = BOX_TABLE_ROW_GROUP; - - if (table_border_is_more_eyecatching(len_ctx, - a, *a_src, &b, b_src)) { - *a = b; - *a_src = b_src; - } - - return false; - } - - return true; -} - -/** - * Process a row - * - * \param len_ctx Length conversion context - * \param cell Cell being considered - * \param row Row to process - * \param a Current border style for cell - * \param a_src Source of \a a - * \return true if row has cells, false otherwise - * - * \post \a a will be updated with most eyecatching style - * \post \a a_src will be updated also - */ -bool table_cell_top_process_row( - const nscss_len_ctx *len_ctx, - struct box *cell, - struct box *row, - struct border *a, - box_type *a_src) -{ - struct border b; - box_type b_src; - - /* Bottom border of row */ - b.style = css_computed_border_bottom_style(row->style); - b.color = css_computed_border_bottom_color(row->style, &b.c); - css_computed_border_bottom_width(row->style, &b.width, &b.unit); - b.width = nscss_len2px(len_ctx, b.width, b.unit, row->style); - b.unit = CSS_UNIT_PX; - b_src = BOX_TABLE_ROW; - - if (table_border_is_more_eyecatching(len_ctx, a, *a_src, &b, b_src)) { - *a = b; - *a_src = b_src; - } - - if (row->children == NULL) { - /* Row is empty, so consider its top border */ - b.style = css_computed_border_top_style(row->style); - b.color = css_computed_border_top_color(row->style, &b.c); - css_computed_border_top_width(row->style, &b.width, &b.unit); - b.width = nscss_len2px(len_ctx, b.width, b.unit, row->style); - b.unit = CSS_UNIT_PX; - b_src = BOX_TABLE_ROW; - - if (table_border_is_more_eyecatching(len_ctx, - a, *a_src, &b, b_src)) { - *a = b; - *a_src = b_src; - } - - return false; - } else { - /* Process cells that are directly above the cell being - * considered. They may not be in this row, but in one of the - * rows above it in the case where rowspan > 1. */ - struct box *c; - bool processed = false; - - while (processed == false) { - for (c = row->children; c != NULL; c = c->next) { - /* Ignore cells to the left */ - if (c->start_column + c->columns - 1 < - cell->start_column) - continue; - /* Ignore cells to the right */ - if (c->start_column > cell->start_column + - cell->columns - 1) - continue; - - /* Flag that we've processed a cell */ - processed = true; - - /* Consider bottom border */ - b.style = css_computed_border_bottom_style( - c->style); - b.color = css_computed_border_bottom_color( - c->style, &b.c); - css_computed_border_bottom_width(c->style, - &b.width, &b.unit); - b.width = nscss_len2px(len_ctx, - b.width, b.unit, c->style); - b.unit = CSS_UNIT_PX; - b_src = BOX_TABLE_CELL; - - if (table_border_is_more_eyecatching(len_ctx, - a, *a_src, &b, b_src)) { - *a = b; - *a_src = b_src; - } - } - - if (processed == false) { - /* There must be a preceding row */ - assert(row->prev != NULL); - - row = row->prev; - } - } - } - - return true; -} - diff --git a/render/table.h b/render/table.h deleted file mode 100644 index 2eeffe699..000000000 --- a/render/table.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2005 James Bursa <bursa@users.sourceforge.net> - * Copyright 2005 Richard Wilson <info@tinct.net> - * - * 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 <http://www.gnu.org/licenses/>. - */ - -/** \file - * Table processing and layout (interface). - */ - -#ifndef _NETSURF_RENDER_TABLE_H_ -#define _NETSURF_RENDER_TABLE_H_ - -#include <stdbool.h> - -struct box; - -bool table_calculate_column_types( - const nscss_len_ctx *len_ctx, - struct box *table); -void table_used_border_for_cell( - const nscss_len_ctx *len_ctx, - struct box *cell); - -#endif diff --git a/render/textplain.c b/render/textplain.c deleted file mode 100644 index 0036eb5c0..000000000 --- a/render/textplain.c +++ /dev/null @@ -1,1351 +0,0 @@ -/* - * Copyright 2006 James Bursa <bursa@users.sourceforge.net> - * Copyright 2006 Adrian Lees <adrianl@users.sourceforge.net> - * - * 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 <http://www.gnu.org/licenses/>. - */ - -/** - * \file - * - * plain text content handling implementation. - */ - -#include <assert.h> -#include <errno.h> -#include <stddef.h> -#include <string.h> -#include <strings.h> -#include <math.h> - -#include <parserutils/input/inputstream.h> -#include <parserutils/charset/utf8.h> - -#include "utils/corestrings.h" -#include "utils/http.h" -#include "utils/log.h" -#include "utils/messages.h" -#include "utils/utils.h" -#include "utils/utf8.h" -#include "netsurf/content.h" -#include "netsurf/keypress.h" -#include "netsurf/browser_window.h" -#include "netsurf/plotters.h" -#include "netsurf/layout.h" -#include "content/content_protected.h" -#include "content/hlcache.h" -#include "css/utils.h" -#include "utils/nsoption.h" -#include "desktop/search.h" -#include "desktop/selection.h" -#include "desktop/gui_internal.h" - -#include "render/search.h" -#include "render/textplain.h" -#include "render/html.h" -#include "render/search.h" - -struct textplain_line { - size_t start; - size_t length; -}; - -typedef struct textplain_content { - struct content base; - - lwc_string *encoding; - void *inputstream; - char *utf8_data; - size_t utf8_data_size; - size_t utf8_data_allocated; - unsigned long physical_line_count; - struct textplain_line *physical_line; - int formatted_width; - struct browser_window *bw; - - struct selection sel; /** Selection state */ - - /** Context for free text search, or NULL if none */ - struct search_context *search; - /** Current search string, or NULL if none */ - char *search_string; -} textplain_content; - - -#define CHUNK 32768 /* Must be a power of 2 */ -#define MARGIN 4 - -#define TAB_WIDTH 8 /* must be power of 2 currently */ -#define TEXT_SIZE 10 * FONT_SIZE_SCALE /* Unscaled text size in pt */ - -static plot_font_style_t textplain_style = { - .family = PLOT_FONT_FAMILY_MONOSPACE, - .size = TEXT_SIZE, - .weight = 400, - .flags = FONTF_NONE, - .background = 0xffffff, - .foreground = 0x000000, -}; - -static int textplain_tab_width = 256; /* try for a sensible default */ - -static lwc_string *textplain_default_charset; - - -/** - * Clean up after the text content handler - */ -static void textplain_fini(void) -{ - if (textplain_default_charset != NULL) { - lwc_string_unref(textplain_default_charset); - textplain_default_charset = NULL; - } -} - - -/** - * Work around feature in libparserutils - * - * if the client provides an encoding up front, but does not provide a - * charset detection callback, then libparserutils will replace the - * provided encoding with UTF-8. This breaks our input handling. - * - * Avoid this by providing a callback that does precisely nothing, - * thus preserving whatever charset information we decided on in - * textplain_create. - */ -static parserutils_error -textplain_charset_hack(const uint8_t *data, - size_t len, - uint16_t *mibenum, - uint32_t *source) -{ - return PARSERUTILS_OK; -} - - -/** - * setup plain text render. - * - * \param[in] c content object. - * \param[in] encoding the encoding of the content. - * \return NSERROR_OK else appropriate error code. - */ -static nserror -textplain_create_internal(textplain_content *c, lwc_string *encoding) -{ - char *utf8_data; - parserutils_inputstream *stream; - parserutils_error error; - - textplain_style.size = (nsoption_int(font_size) * FONT_SIZE_SCALE) / 10; - - utf8_data = malloc(CHUNK); - if (utf8_data == NULL) - goto no_memory; - - error = parserutils_inputstream_create(lwc_string_data(encoding), 0, - textplain_charset_hack, &stream); - if (error == PARSERUTILS_BADENCODING) { - /* Fall back to Windows-1252 */ - error = parserutils_inputstream_create("Windows-1252", 0, - textplain_charset_hack, &stream); - } - if (error != PARSERUTILS_OK) { - free(utf8_data); - goto no_memory; - } - - c->encoding = lwc_string_ref(encoding); - c->inputstream = stream; - c->utf8_data = utf8_data; - c->utf8_data_size = 0; - c->utf8_data_allocated = CHUNK; - c->physical_line = 0; - c->physical_line_count = 0; - c->formatted_width = 0; - c->bw = NULL; - - selection_prepare(&c->sel, (struct content *)c, false); - - return NSERROR_OK; - -no_memory: - content_broadcast_errorcode(&c->base, NSERROR_NOMEM); - - return NSERROR_NOMEM; -} - - -/** - * Create a CONTENT_TEXTPLAIN. - */ -static nserror -textplain_create(const content_handler *handler, - lwc_string *imime_type, - const http_parameter *params, - llcache_handle *llcache, - const char *fallback_charset, - bool quirks, - struct content **c) -{ - textplain_content *text; - nserror error; - lwc_string *encoding; - - text = calloc(1, sizeof(textplain_content)); - if (text == NULL) { - return NSERROR_NOMEM; - } - - error = content__init(&text->base, handler, imime_type, params, - llcache, fallback_charset, quirks); - if (error != NSERROR_OK) { - free(text); - return error; - } - - error = http_parameter_list_find_item(params, corestring_lwc_charset, - &encoding); - if (error != NSERROR_OK) { - encoding = lwc_string_ref(textplain_default_charset); - } - - error = textplain_create_internal(text, encoding); - if (error != NSERROR_OK) { - lwc_string_unref(encoding); - free(text); - return error; - } - - lwc_string_unref(encoding); - - *c = (struct content *) text; - - return NSERROR_OK; -} - - -/** - * copy utf8 encoded data - */ -static bool -textplain_copy_utf8_data(textplain_content *c, const uint8_t *buf, size_t len) -{ - if (c->utf8_data_size + len >= c->utf8_data_allocated) { - /* Compute next multiple of chunk above the required space */ - size_t allocated; - char *utf8_data; - - allocated = (c->utf8_data_size + len + CHUNK - 1) & ~(CHUNK - 1); - utf8_data = realloc(c->utf8_data, allocated); - if (utf8_data == NULL) - return false; - - c->utf8_data = utf8_data; - c->utf8_data_allocated = allocated; - } - - memcpy(c->utf8_data + c->utf8_data_size, buf, len); - c->utf8_data_size += len; - - return true; -} - - -/** - * drain input - */ -static bool -textplain_drain_input(textplain_content *c, - parserutils_inputstream *stream, - parserutils_error terminator) -{ - static const uint8_t *u_fffd = (const uint8_t *) "\xef\xbf\xfd"; - const uint8_t *ch; - size_t chlen, offset = 0; - - while (parserutils_inputstream_peek(stream, offset, &ch, &chlen) != - terminator) { - /* Replace all instances of NUL with U+FFFD */ - if (chlen == 1 && *ch == 0) { - if (offset > 0) { - /* Obtain pointer to start of input data */ - parserutils_inputstream_peek(stream, 0, - &ch, &chlen); - /* Copy from it up to the start of the NUL */ - if (textplain_copy_utf8_data(c, ch, - offset) == false) - return false; - } - - /* Emit U+FFFD */ - if (textplain_copy_utf8_data(c, u_fffd, 3) == false) - return false; - - /* Advance inputstream past the NUL we just read */ - parserutils_inputstream_advance(stream, offset + 1); - /* Reset the read offset */ - offset = 0; - } else { - /* Accumulate input */ - offset += chlen; - - if (offset > CHUNK) { - /* Obtain pointer to start of input data */ - parserutils_inputstream_peek(stream, 0, - &ch, &chlen); - - /* Emit the data we've read */ - if (textplain_copy_utf8_data(c, ch, - offset) == false) - return false; - - /* Advance the inputstream */ - parserutils_inputstream_advance(stream, offset); - /* Reset the read offset */ - offset = 0; - } - } - } - - if (offset > 0) { - /* Obtain pointer to start of input data */ - parserutils_inputstream_peek(stream, 0, &ch, &chlen); - /* Emit any data remaining */ - if (textplain_copy_utf8_data(c, ch, offset) == false) - return false; - - /* Advance the inputstream past the data we've read */ - parserutils_inputstream_advance(stream, offset); - } - - return true; -} - - -/** - * Process data for CONTENT_TEXTPLAIN. - */ -static bool -textplain_process_data(struct content *c, const char *data, unsigned int size) -{ - textplain_content *text = (textplain_content *) c; - parserutils_inputstream *stream = text->inputstream; - parserutils_error error; - - error = parserutils_inputstream_append(stream, - (const uint8_t *) data, size); - if (error != PARSERUTILS_OK) { - goto no_memory; - } - - if (textplain_drain_input(text, stream, PARSERUTILS_NEEDDATA) == false) - goto no_memory; - - return true; - -no_memory: - content_broadcast_errorcode(c, NSERROR_NOMEM); - return false; -} - - -/** - * Convert a CONTENT_TEXTPLAIN for display. - */ -static bool textplain_convert(struct content *c) -{ - textplain_content *text = (textplain_content *) c; - parserutils_inputstream *stream = text->inputstream; - parserutils_error error; - - error = parserutils_inputstream_append(stream, NULL, 0); - if (error != PARSERUTILS_OK) { - return false; - } - - if (textplain_drain_input(text, stream, PARSERUTILS_EOF) == false) - return false; - - parserutils_inputstream_destroy(stream); - text->inputstream = NULL; - - content_set_ready(c); - content_set_done(c); - content_set_status(c, messages_get("Done")); - - return true; -} - - -/** - * Calculate the line height, in pixels - * - * \return Line height, in pixels - */ -static float textplain_line_height(void) -{ - /* Size is in points, so convert to pixels. - * Then use a constant line height of 1.2 x font size. - */ - return FIXTOFLT(FDIV((FMUL(FLTTOFIX(1.2), FMUL(nscss_screen_dpi, INTTOFIX((textplain_style.size / FONT_SIZE_SCALE))))), F_72)); -} - - -/** - * Reformat a CONTENT_TEXTPLAIN to a new width. - */ -static void textplain_reformat(struct content *c, int width, int height) -{ - textplain_content *text = (textplain_content *) c; - char *utf8_data = text->utf8_data; - size_t utf8_data_size = text->utf8_data_size; - unsigned long line_count = 0; - struct textplain_line *line = text->physical_line; - struct textplain_line *line1; - size_t i, space, col; - size_t columns = 80; - int character_width; - size_t line_start; - nserror res; - - NSLOG(netsurf, INFO, "content %p w:%d h:%d", c, width, height); - - /* compute available columns (assuming monospaced font) - use 8 - * characters for better accuracy - */ - res = guit->layout->width(&textplain_style, - "ABCDEFGH", 8, - &character_width); - if (res != NSERROR_OK) { - return; - } - - columns = (width - MARGIN - MARGIN) * 8 / character_width; - textplain_tab_width = (TAB_WIDTH * character_width) / 8; - - text->formatted_width = width; - - text->physical_line_count = 0; - - if (!line) { - text->physical_line = line = - malloc(sizeof(struct textplain_line) * (1024 + 3)); - if (!line) - goto no_memory; - } - - line[line_count++].start = line_start = 0; - space = 0; - i = 0; - col = 0; - while (i < utf8_data_size) { - size_t csize; /* number of bytes in character */ - uint32_t chr; - bool term; - size_t next_col; - parserutils_error perror; - - perror = parserutils_charset_utf8_to_ucs4((const uint8_t *)utf8_data + i, utf8_data_size - i, &chr, &csize); - if (perror != PARSERUTILS_OK) { - chr = 0xfffd; - } - - term = (chr == '\n' || chr == '\r'); - - next_col = col + 1; - - if (chr == '\t') { - next_col = (next_col + TAB_WIDTH - 1) & ~(TAB_WIDTH - 1); - } - - if (term || next_col >= columns) { - if (line_count % 1024 == 0) { - line1 = realloc(line, - sizeof(struct textplain_line) * - (line_count + 1024 + 3)); - if (!line1) - goto no_memory; - text->physical_line = line = line1; - } - - if (term) { - line[line_count-1].length = i - line_start; - - /* skip second char of CR/LF or LF/CR pair */ - if (i + 1 < utf8_data_size && - utf8_data[i+1] != utf8_data[i] && - (utf8_data[i+1] == '\n' || - utf8_data[i+1] == '\r')) { - i++; - } - } else { - if (space) { - /* break at last space in line */ - i = space; - line[line_count-1].length = (i + 1) - line_start; - } else - line[line_count-1].length = i - line_start; - } - - line[line_count++].start = line_start = i + 1; - col = 0; - space = 0; - } else { - col++; - if (chr == ' ') - space = i; - } - i += csize; - } - line[line_count-1].length = i - line[line_count-1].start; - line[line_count].start = utf8_data_size; - - text->physical_line_count = line_count; - c->width = width; - c->height = line_count * textplain_line_height() + MARGIN + MARGIN; - - return; - -no_memory: - NSLOG(netsurf, INFO, "out of memory (line_count %lu)", line_count); - return; -} - - -/** - * Destroy a CONTENT_TEXTPLAIN and free all resources it owns. - */ - -static void textplain_destroy(struct content *c) -{ - textplain_content *text = (textplain_content *) c; - - lwc_string_unref(text->encoding); - - if (text->inputstream != NULL) { - parserutils_inputstream_destroy(text->inputstream); - } - - if (text->physical_line != NULL) { - free(text->physical_line); - } - - if (text->utf8_data != NULL) { - free(text->utf8_data); - } -} - - -static nserror textplain_clone(const struct content *old, struct content **newc) -{ - const textplain_content *old_text = (textplain_content *) old; - textplain_content *text; - nserror error; - const char *data; - unsigned long size; - - text = calloc(1, sizeof(textplain_content)); - if (text == NULL) - return NSERROR_NOMEM; - - error = content__clone(old, &text->base); - if (error != NSERROR_OK) { - content_destroy(&text->base); - return error; - } - - /* Simply replay create/process/convert */ - error = textplain_create_internal(text, old_text->encoding); - if (error != NSERROR_OK) { - content_destroy(&text->base); - return error; - } - - data = content__get_source_data(&text->base, &size); - if (size > 0) { - if (textplain_process_data(&text->base, data, size) == false) { - content_destroy(&text->base); - return NSERROR_NOMEM; - } - } - - if (old->status == CONTENT_STATUS_READY || - old->status == CONTENT_STATUS_DONE) { - if (textplain_convert(&text->base) == false) { - content_destroy(&text->base); - return NSERROR_CLONE_FAILED; - } - } - - return NSERROR_OK; -} - - -static content_type textplain_content_type(void) -{ - return CONTENT_TEXTPLAIN; -} - - -/** - * Handle mouse clicks and movements in a TEXTPLAIN content window. - * - * \param c content of type textplain - * \param bw browser window - * \param mouse mouse state on action - * \param x coordinate of mouse - * \param y coordinate of mouse - */ -static void -textplain_mouse_action(struct content *c, - struct browser_window *bw, - browser_mouse_state mouse, - int x, int y) -{ - textplain_content *text = (textplain_content *) c; - browser_pointer_shape pointer = BROWSER_POINTER_DEFAULT; - union content_msg_data msg_data; - const char *status = 0; - size_t idx; - int dir = 0; - - browser_window_set_drag_type(bw, DRAGGING_NONE, NULL); - - idx = textplain_offset_from_coords(c, x, y, dir); - if (selection_click(&text->sel, mouse, idx)) { - - if (selection_dragging(&text->sel)) { - browser_window_set_drag_type(bw, - DRAGGING_SELECTION, NULL); - status = messages_get("Selecting"); - } - - } else { - if (mouse & (BROWSER_MOUSE_DRAG_1 | BROWSER_MOUSE_DRAG_2)) { - browser_window_page_drag_start(bw, x, y); - pointer = BROWSER_POINTER_MOVE; - } - } - - msg_data.explicit_status_text = status; - content_broadcast(c, CONTENT_MSG_STATUS, &msg_data); - - msg_data.pointer = pointer; - content_broadcast(c, CONTENT_MSG_POINTER, &msg_data); -} - - -/** - * Handle mouse tracking (including drags) in a TEXTPLAIN content window. - * - * \param c content of type textplain - * \param bw browser window - * \param mouse state of mouse buttons and modifier keys - * \param x coordinate of mouse - * \param y coordinate of mouse - */ -static void -textplain_mouse_track(struct content *c, - struct browser_window *bw, - browser_mouse_state mouse, - int x, int y) -{ - textplain_content *text = (textplain_content *) c; - - if (browser_window_get_drag_type(bw) == DRAGGING_SELECTION && !mouse) { - int dir = -1; - size_t idx; - - if (selection_dragging_start(&text->sel)) - dir = 1; - - idx = textplain_offset_from_coords(c, x, y, dir); - selection_track(&text->sel, mouse, idx); - - browser_window_set_drag_type(bw, DRAGGING_NONE, NULL); - } - - switch (browser_window_get_drag_type(bw)) { - - case DRAGGING_SELECTION: { - int dir = -1; - size_t idx; - - if (selection_dragging_start(&text->sel)) dir = 1; - - idx = textplain_offset_from_coords(c, x, y, dir); - selection_track(&text->sel, mouse, idx); - } - break; - - default: - textplain_mouse_action(c, bw, mouse, x, y); - break; - } -} - - -/** - * Handle keypresses. - * - * \param c content of type CONTENT_TEXTPLAIN - * \param key The UCS4 character codepoint - * \return true if key handled, false otherwise - */ -static bool textplain_keypress(struct content *c, uint32_t key) -{ - textplain_content *text = (textplain_content *) c; - struct selection *sel = &text->sel; - - switch (key) { - case NS_KEY_COPY_SELECTION: - selection_copy_to_clipboard(sel); - return true; - - case NS_KEY_CLEAR_SELECTION: - selection_clear(sel, true); - return true; - - case NS_KEY_SELECT_ALL: - selection_select_all(sel); - return true; - - case NS_KEY_ESCAPE: - if (selection_defined(sel)) { - selection_clear(sel, true); - return true; - } - - /* if there's no selection, leave Escape for the caller */ - return false; - } - - return false; -} - - -/** - * Terminate a search. - * - * \param c content of type text - */ -static void textplain_search_clear(struct content *c) -{ - textplain_content *text = (textplain_content *) c; - - assert(c != NULL); - - free(text->search_string); - text->search_string = NULL; - - if (text->search != NULL) { - search_destroy_context(text->search); - } - text->search = NULL; -} - - -/** - * Handle search. - * - * \param c content of type text - * \param gui_data front end private data - * \param flags search flags - * \param string search string - */ -static void textplain_search(struct content *c, void *gui_data, - search_flags_t flags, const char *string) -{ - textplain_content *text = (textplain_content *) c; - - assert(c != NULL); - - if (string != NULL && text->search_string != NULL && - strcmp(string, text->search_string) == 0 && - text->search != NULL) { - /* Continue prev. search */ - search_step(text->search, flags, string); - - } else if (string != NULL) { - /* New search */ - free(text->search_string); - text->search_string = strdup(string); - if (text->search_string == NULL) - return; - - if (text->search != NULL) { - search_destroy_context(text->search); - text->search = NULL; - } - - text->search = search_create_context(c, CONTENT_TEXTPLAIN, - gui_data); - - if (text->search == NULL) - return; - - search_step(text->search, flags, string); - - } else { - /* Clear search */ - textplain_search_clear(c); - - free(text->search_string); - text->search_string = NULL; - } -} - - -/** - * Draw a CONTENT_TEXTPLAIN using the current set of plotters (plot). - * - * x, y, clip_[xy][01] are in target coordinates. - * - * \param c content of type CONTENT_TEXTPLAIN - * \param data redraw data for this content redraw - * \param clip current clip region - * \param ctx current redraw context - * \return true if successful, false otherwise - */ -static bool -textplain_redraw(struct content *c, - struct content_redraw_data *data, - const struct rect *clip, - const struct redraw_context *ctx) -{ - textplain_content *text = (textplain_content *) c; - struct browser_window *bw = text->bw; - char *utf8_data = text->utf8_data; - long lineno; - int x = data->x; - int y = data->y; - unsigned long line_count = text->physical_line_count; - float line_height = textplain_line_height(); - float scaled_line_height = line_height * data->scale; - long line0 = (clip->y0 - y * data->scale) / scaled_line_height - 1; - long line1 = (clip->y1 - y * data->scale) / scaled_line_height + 1; - struct textplain_line *line = text->physical_line; - size_t length; - plot_style_t *plot_style_highlight; - nserror res; - - if (line0 < 0) - line0 = 0; - if (line1 < 0) - line1 = 0; - if (line_count < (unsigned long) line0) - line0 = line_count; - if (line_count < (unsigned long) line1) - line1 = line_count; - if (line1 < line0) - line1 = line0; - - res = ctx->plot->rectangle(ctx, plot_style_fill_white, clip); - if (res != NSERROR_OK) { - return false; - } - - if (!line) - return true; - - /* choose a suitable background colour for any highlighted text */ - if ((data->background_colour & 0x808080) == 0x808080) - plot_style_highlight = plot_style_fill_black; - else - plot_style_highlight = plot_style_fill_white; - - /* Set up font plot style */ - textplain_style.background = data->background_colour; - - x = (x + MARGIN) * data->scale; - y = (y + MARGIN) * data->scale; - for (lineno = line0; lineno != line1; lineno++) { - const char *text_d = utf8_data + line[lineno].start; - int tab_width = textplain_tab_width * data->scale; - size_t offset = 0; - int tx = x; - - if (!tab_width) tab_width = 1; - - length = line[lineno].length; - if (!length) - continue; - - while (offset < length) { - size_t next_offset = offset; - int width; - int ntx; - nserror res; - - while (next_offset < length && text_d[next_offset] != '\t') - next_offset = utf8_next(text_d, length, next_offset); - - if (!text_redraw(text_d + offset, next_offset - offset, - line[lineno].start + offset, 0, - &textplain_style, - tx, y + (lineno * scaled_line_height), - clip, line_height, data->scale, false, - (struct content *)text, &text->sel, - text->search, ctx)) - return false; - - if (next_offset >= length) - break; - - res = guit->layout->width(&textplain_style, - &text_d[offset], - next_offset - offset, - &width); - /* locate end of string and align to next tab position */ - if (res == NSERROR_OK) { - tx += (int)(width * data->scale); - } - - ntx = x + ((1 + (tx - x) / tab_width) * tab_width); - - /* if the tab character lies within the - * selection, if any, then we must draw it as - * a filled rectangle so that it's consistent - * with background of the selected text - */ - - if (bw) { - unsigned tab_ofst = line[lineno].start + next_offset; - struct selection *sel = &text->sel; - bool highlighted = false; - - if (selection_defined(sel)) { - unsigned start_idx, end_idx; - if (selection_highlighted(sel, - tab_ofst, - tab_ofst + 1, - &start_idx, - &end_idx)) - highlighted = true; - } - - if (!highlighted && (text->search != NULL)) { - unsigned start_idx, end_idx; - if (search_term_highlighted(c, - tab_ofst, - tab_ofst + 1, - &start_idx, - &end_idx, - text->search)) - highlighted = true; - } - - if (highlighted) { - struct rect rect; - rect.x0 = tx; - rect.y0 = y + (lineno * scaled_line_height); - rect.x1 = ntx; - rect.y1 = rect.y0 + scaled_line_height; - res = ctx->plot->rectangle(ctx, - plot_style_highlight, - &rect); - if (res != NSERROR_OK) { - return false; - } - } - } - - offset = next_offset + 1; - tx = ntx; - } - } - - return true; -} - - -/** - * Handle a window containing a CONTENT_TEXTPLAIN being opened. - */ -static void -textplain_open(struct content *c, - struct browser_window *bw, - struct content *page, - struct object_params *params) -{ - textplain_content *text = (textplain_content *) c; - - text->bw = bw; - - /* text selection */ - selection_init(&text->sel, NULL, NULL); -} - - -/** - * Handle a window containing a CONTENT_TEXTPLAIN being closed. - */ -static void textplain_close(struct content *c) -{ - textplain_content *text = (textplain_content *) c; - - if (text->search != NULL) { - search_destroy_context(text->search); - } - - text->bw = NULL; -} - - -/** - * Return an textplain content's selection context - */ -static char *textplain_get_selection(struct content *c) -{ - textplain_content *text = (textplain_content *) c; - - return selection_get_copy(&text->sel); -} - - -/** - * Convert a character offset within a line of text into the - * horizontal co-ordinate - * - * The conversion takes into account the font being used and any tabs - * in the text - * - * \param text line of text - * \param offset char offset within text - * \param length line length - * \return x ordinate - */ -static int -textplain_coord_from_offset(const char *text, size_t offset, size_t length) -{ - int x = 0; - - while (offset > 0) { - size_t next_offset = 0; - int tx; - - while (next_offset < offset && text[next_offset] != '\t') { - next_offset = utf8_next(text, length, next_offset); - } - - guit->layout->width(&textplain_style, text, next_offset, &tx); - - x += tx; - - if (next_offset >= offset) - break; - - /* align to next tab boundary */ - next_offset++; - x = (1 + (x / textplain_tab_width)) * textplain_tab_width; - offset -= next_offset; - text += next_offset; - length -= next_offset; - } - - return x; -} - - -/** - * plain text content handler table - */ -static const content_handler textplain_content_handler = { - .fini = textplain_fini, - .create = textplain_create, - .process_data = textplain_process_data, - .data_complete = textplain_convert, - .reformat = textplain_reformat, - .destroy = textplain_destroy, - .mouse_track = textplain_mouse_track, - .mouse_action = textplain_mouse_action, - .keypress = textplain_keypress, - .search = textplain_search, - .search_clear = textplain_search_clear, - .redraw = textplain_redraw, - .open = textplain_open, - .close = textplain_close, - .get_selection = textplain_get_selection, - .clone = textplain_clone, - .type = textplain_content_type, - .no_share = true, -}; - - -/* exported interface documented in render/textplain.h */ -nserror textplain_init(void) -{ - lwc_error lerror; - nserror error; - - lerror = lwc_intern_string("Windows-1252", - SLEN("Windows-1252"), - &textplain_default_charset); - if (lerror != lwc_error_ok) { - return NSERROR_NOMEM; - } - - error = content_factory_register_handler("text/plain", - &textplain_content_handler); - if (error != NSERROR_OK) { - lwc_string_unref(textplain_default_charset); - } - - return error; -} - - -/* exported interface documented in render/textplain.h */ -unsigned long textplain_line_count(struct content *c) -{ - textplain_content *text = (textplain_content *) c; - - assert(c != NULL); - - return text->physical_line_count; -} - - -/* exported interface documented in render/textplain.h */ -size_t textplain_size(struct content *c) -{ - textplain_content *text = (textplain_content *) c; - - assert(c != NULL); - - return text->utf8_data_size; -} - - -/* exported interface documented in render/textplain.h */ -size_t textplain_offset_from_coords(struct content *c, int x, int y, int dir) -{ - textplain_content *textc = (textplain_content *) c; - float line_height = textplain_line_height(); - struct textplain_line *line; - const char *text; - unsigned nlines; - size_t length; - int idx; - - assert(c != NULL); - - y = (int)((float)(y - MARGIN) / line_height); - x -= MARGIN; - - nlines = textc->physical_line_count; - if (!nlines) - return 0; - - if (y <= 0) y = 0; - else if ((unsigned)y >= nlines) - y = nlines - 1; - - line = &textc->physical_line[y]; - text = textc->utf8_data + line->start; - length = line->length; - idx = 0; - - while (x > 0) { - size_t next_offset = 0; - int width = INT_MAX; - - while (next_offset < length && text[next_offset] != '\t') { - next_offset = utf8_next(text, length, next_offset); - } - - if (next_offset < length) { - guit->layout->width(&textplain_style, - text, - next_offset, - &width); - } - - if (x <= width) { - int pixel_offset; - size_t char_offset; - - guit->layout->position(&textplain_style, - text, next_offset, x, - &char_offset, &pixel_offset); - - idx += char_offset; - break; - } - - x -= width; - length -= next_offset; - text += next_offset; - idx += next_offset; - - /* check if it's within the tab */ - width = textplain_tab_width - (width % textplain_tab_width); - if (x <= width) break; - - x -= width; - length--; - text++; - idx++; - } - - return line->start + idx; -} - - -/* exported interface documented in render/textplain.h */ -void -textplain_coords_from_range(struct content *c, - unsigned start, - unsigned end, - struct rect *r) -{ - textplain_content *text = (textplain_content *) c; - float line_height = textplain_line_height(); - char *utf8_data; - struct textplain_line *line; - unsigned lineno = 0; - unsigned nlines; - - assert(c != NULL); - assert(start <= end); - assert(end <= text->utf8_data_size); - - utf8_data = text->utf8_data; - nlines = text->physical_line_count; - line = text->physical_line; - - /* find start */ - lineno = textplain_find_line(c, start); - - r->y0 = (int)(MARGIN + lineno * line_height); - - if (lineno + 1 <= nlines || line[lineno + 1].start >= end) { - /* \todo - it may actually be more efficient just to - * run forwards most of the time - */ - - /* find end */ - lineno = textplain_find_line(c, end); - - r->x0 = 0; - r->x1 = text->formatted_width; - } else { - /* single line */ - const char *text = utf8_data + line[lineno].start; - - r->x0 = textplain_coord_from_offset(text, - start - line[lineno].start, - line[lineno].length); - - r->x1 = textplain_coord_from_offset(text, - end - line[lineno].start, - line[lineno].length); - } - - r->y1 = (int)(MARGIN + (lineno + 1) * line_height); -} - - -/* exported interface documented in render/textplain.h */ -char * -textplain_get_line(struct content *c, - unsigned lineno, - size_t *poffset, - size_t *plen) -{ - textplain_content *text = (textplain_content *) c; - struct textplain_line *line; - - assert(c != NULL); - - if (lineno >= text->physical_line_count) - return NULL; - line = &text->physical_line[lineno]; - - *poffset = line->start; - *plen = line->length; - return text->utf8_data + line->start; -} - - -/* exported interface documented in render/textplain.h */ -int textplain_find_line(struct content *c, unsigned offset) -{ - textplain_content *text = (textplain_content *) c; - struct textplain_line *line; - int nlines; - int lineno = 0; - - assert(c != NULL); - - line = text->physical_line; - nlines = text->physical_line_count; - - if (offset > text->utf8_data_size) { - return -1; - } - -/* \todo - implement binary search here */ - while (lineno < nlines && line[lineno].start < offset) { - lineno++; - } - if (line[lineno].start > offset) { - lineno--; - } - - return lineno; -} - - -/* exported interface documented in render/textplain.h */ -char * -textplain_get_raw_data(struct content *c, - unsigned start, - unsigned end, - size_t *plen) -{ - textplain_content *text = (textplain_content *) c; - size_t utf8_size; - - assert(c != NULL); - - utf8_size = text->utf8_data_size; - - /* any text at all? */ - if (!utf8_size) return NULL; - - /* clamp to valid offset range */ - if (start >= utf8_size) start = utf8_size; - if (end >= utf8_size) end = utf8_size; - - *plen = end - start; - - return text->utf8_data + start; -} - - -/* exported interface documented in render/textplain.h */ -struct browser_window *textplain_get_browser_window(struct content *c) -{ - textplain_content *text = (textplain_content *) c; - - assert(c != NULL); - assert(c->handler == &textplain_content_handler); - - return text->bw; -} diff --git a/render/textplain.h b/render/textplain.h deleted file mode 100644 index 0f0128e56..000000000 --- a/render/textplain.h +++ /dev/null @@ -1,139 +0,0 @@ -/* - * Copyright 2006 James Bursa <bursa@users.sourceforge.net> - * Copyright 2006 Adrian Lees <adrianl@users.sourceforge.net> - * - * 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 <http://www.gnu.org/licenses/>. - */ - -/** - * \file - * - * Interface to content handler for plain text. - */ - -#ifndef NETSURF_RENDER_TEXTPLAIN_H -#define NETSURF_RENDER_TEXTPLAIN_H - -#include <stddef.h> -#include "netsurf/mouse.h" - -struct content; -struct hlcache_handle; -struct http_parameter; -struct rect; - -/** - * Initialise the text content handler - * - * \return NSERROR_OK on success else appropriate error code. - */ -nserror textplain_init(void); - - -/** - * Retrieve number of lines in content - * - * \param[in] c Content to retrieve line count from - * \return Number of lines - */ -unsigned long textplain_line_count(struct content *c); - - -/** - * Retrieve the size (in bytes) of text data - * - * \param[in] c Content to retrieve size of - * \return Size, in bytes, of data - */ -size_t textplain_size(struct content *c); - - -/** - * Return byte offset within UTF8 textplain content. - * - * given the co-ordinates of a point within a textplain content. 'dir' - * specifies the direction in which to search (-1 = above-left, +1 = - * below-right) if the co-ordinates are not contained within a line. - * - * \param[in] c content of type CONTENT_TEXTPLAIN - * \param[in] x x ordinate of point - * \param[in] y y ordinate of point - * \param[in] dir direction of search if not within line - * \return byte offset of character containing (or nearest to) point - */ -size_t textplain_offset_from_coords(struct content *c, int x, int y, int dir); - - -/** - * Given a range of byte offsets within a UTF8 textplain content, - * return a box that fully encloses the text - * - * \param[in] c content of type CONTENT_TEXTPLAIN - * \param[in] start byte offset of start of text range - * \param[in] end byte offset of end - * \param[out] r rectangle to be completed - */ -void textplain_coords_from_range(struct content *c, - unsigned start, unsigned end, struct rect *r); - -/** - * Return a pointer to the requested line of text. - * - * \param[in] c content of type CONTENT_TEXTPLAIN - * \param[in] lineno line number - * \param[out] poffset receives byte offset of line start within text - * \param[out] plen receives length of returned line - * \return pointer to text, or NULL if invalid line number - */ -char *textplain_get_line(struct content *c, unsigned lineno, - size_t *poffset, size_t *plen); - - -/** - * Find line number of byte in text - * - * Given a byte offset within the text, return the line number - * of the line containing that offset. - * - * \param[in] c content of type CONTENT_TEXTPLAIN - * \param[in] offset byte offset within textual representation - * \return line number, or -1 if offset invalid (larger than size) - */ -int textplain_find_line(struct content *c, unsigned offset); - - -/** - * Return a pointer to the raw UTF-8 data, as opposed to the reformatted - * text to fit the window width. Thus only hard newlines are preserved - * in the saved/copied text of a selection. - * - * \param[in] c content of type CONTENT_TEXTPLAIN - * \param[in] start starting byte offset within UTF-8 text - * \param[in] end ending byte offset - * \param[out] plen receives validated length - * \return pointer to text, or NULL if no text - */ -char *textplain_get_raw_data(struct content *c, unsigned start, unsigned end, size_t *plen); - - -/** - * Get the browser window containing a textplain content - * - * \param[in] c text/plain content - * \return the browser window - */ -struct browser_window *textplain_get_browser_window(struct content *c); - -#endif |