From 2a03ea30490892ac52b3da325ab78e1aa888f83e Mon Sep 17 00:00:00 2001 From: Vincent Sanders Date: Thu, 10 May 2018 11:34:26 +0100 Subject: move html and text content handlers where they belong --- content/handlers/html/html_redraw.c | 1951 +++++++++++++++++++++++++++++++++++ 1 file changed, 1951 insertions(+) create mode 100644 content/handlers/html/html_redraw.c (limited to 'content/handlers/html/html_redraw.c') diff --git a/content/handlers/html/html_redraw.c b/content/handlers/html/html_redraw.c new file mode 100644 index 000000000..d05df8753 --- /dev/null +++ b/content/handlers/html/html_redraw.c @@ -0,0 +1,1951 @@ +/* + * Copyright 2004-2008 James Bursa + * Copyright 2004-2007 John M Bell + * Copyright 2004-2007 Richard Wilson + * Copyright 2005-2006 Adrian Lees + * Copyright 2006 Rob Kendrick + * Copyright 2008 Michael Drake + * Copyright 2009 Paul Blokus + * + * This file is part of NetSurf, http://www.netsurf-browser.org/ + * + * NetSurf is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * NetSurf is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** + * \file + * + * Redrawing CONTENT_HTML implementation. + */ + +#include "utils/config.h" +#include +#include +#include +#include +#include +#include + +#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 "html/box.h" +#include "html/font.h" +#include "html/form_internal.h" +#include "html/html_internal.h" +#include "html/layout.h" +#include "html/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 + */ + +static 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); + + /* attributes override */ + /* 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 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; + +} -- cgit v1.2.3