1 /*
2  * Copyright 2004-2008 James Bursa <bursa@users.sourceforge.net>
3  * Copyright 2004-2007 John M Bell <jmb202@ecs.soton.ac.uk>
4  * Copyright 2004-2007 Richard Wilson <info@tinct.net>
5  * Copyright 2005-2006 Adrian Lees <adrianl@users.sourceforge.net>
6  * Copyright 2006 Rob Kendrick <rjek@netsurf-browser.org>
7  * Copyright 2008 Michael Drake <tlsa@netsurf-browser.org>
8  * Copyright 2009 Paul Blokus <paul_pl@users.sourceforge.net>
9  *
10  * This file is part of NetSurf, http://www.netsurf-browser.org/
11  *
12  * NetSurf is free software; you can redistribute it and/or modify
13  * it under the terms of the GNU General Public License as published by
14  * the Free Software Foundation; version 2 of the License.
15  *
16  * NetSurf is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
23  */
24 
25 /**
26  * \file
27  *
28  * Redrawing CONTENT_HTML implementation.
29  */
30 
31 #include "utils/config.h"
32 #include <assert.h>
33 #include <stdbool.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <math.h>
37 #include <dom/dom.h>
38 
39 #include "utils/log.h"
40 #include "utils/messages.h"
41 #include "utils/utils.h"
42 #include "utils/nsoption.h"
43 #include "utils/corestrings.h"
44 #include "netsurf/content.h"
45 #include "netsurf/browser_window.h"
46 #include "netsurf/plotters.h"
47 #include "netsurf/bitmap.h"
48 #include "netsurf/layout.h"
49 #include "content/content.h"
50 #include "content/content_protected.h"
51 #include "content/textsearch.h"
52 #include "css/utils.h"
53 #include "desktop/selection.h"
54 #include "desktop/print.h"
55 #include "desktop/scrollbar.h"
56 #include "desktop/textarea.h"
57 #include "desktop/gui_internal.h"
58 
59 #include "html/box.h"
60 #include "html/box_inspect.h"
61 #include "html/box_manipulate.h"
62 #include "html/font.h"
63 #include "html/form_internal.h"
64 #include "html/private.h"
65 #include "html/layout.h"
66 
67 
68 bool html_redraw_debug = false;
69 
70 /**
71  * Determine if a box has a background that needs drawing
72  *
73  * \param box  Box to consider
74  * \return True if box has a background, false otherwise.
75  */
html_redraw_box_has_background(struct box * box)76 static bool html_redraw_box_has_background(struct box *box)
77 {
78 	if (box->background != NULL)
79 		return true;
80 
81 	if (box->style != NULL) {
82 		css_color colour;
83 
84 		css_computed_background_color(box->style, &colour);
85 
86 		if (nscss_color_is_transparent(colour) == false)
87 			return true;
88 	}
89 
90 	return false;
91 }
92 
93 /**
94  * Find the background box for a box
95  *
96  * \param box  Box to find background box for
97  * \return Pointer to background box, or NULL if there is none
98  */
html_redraw_find_bg_box(struct box * box)99 static struct box *html_redraw_find_bg_box(struct box *box)
100 {
101 	/* Thanks to backwards compatibility, CSS defines the following:
102 	 *
103 	 * + If the box is for the root element and it has a background,
104 	 *   use that (and then process the body box with no special case)
105 	 * + If the box is for the root element and it has no background,
106 	 *   then use the background (if any) from the body element as if
107 	 *   it were specified on the root. Then, when the box for the body
108 	 *   element is processed, ignore the background.
109 	 * + For any other box, just use its own styling.
110 	 */
111 	if (box->parent == NULL) {
112 		/* Root box */
113 		if (html_redraw_box_has_background(box))
114 			return box;
115 
116 		/* No background on root box: consider body box, if any */
117 		if (box->children != NULL) {
118 			if (html_redraw_box_has_background(box->children))
119 				return box->children;
120 		}
121 	} else if (box->parent != NULL && box->parent->parent == NULL) {
122 		/* Body box: only render background if root has its own */
123 		if (html_redraw_box_has_background(box) &&
124 				html_redraw_box_has_background(box->parent))
125 			return box;
126 	} else {
127 		/* Any other box */
128 		if (html_redraw_box_has_background(box))
129 			return box;
130 	}
131 
132 	return NULL;
133 }
134 
135 /**
136  * Redraw a short text string, complete with highlighting
137  * (for selection/search)
138  *
139  * \param utf8_text pointer to UTF-8 text string
140  * \param utf8_len  length of string, in bytes
141  * \param offset    byte offset within textual representation
142  * \param space     width of space that follows string (0 = no space)
143  * \param fstyle    text style to use (pass text size unscaled)
144  * \param x         x ordinate at which to plot text
145  * \param y         y ordinate at which to plot text
146  * \param clip      pointer to current clip rectangle
147  * \param height    height of text string
148  * \param scale     current display scale (1.0 = 100%)
149  * \param excluded  exclude this text string from the selection
150  * \param c         Content being redrawn.
151  * \param sel       Selection context
152  * \param search    Search context
153  * \param ctx	    current redraw context
154  * \return true iff successful and redraw should proceed
155  */
156 
157 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,const struct redraw_context * ctx)158 text_redraw(const char *utf8_text,
159 	    size_t utf8_len,
160 	    size_t offset,
161 	    int space,
162 	    const plot_font_style_t *fstyle,
163 	    int x,
164 	    int y,
165 	    const struct rect *clip,
166 	    int height,
167 	    float scale,
168 	    bool excluded,
169 	    struct content *c,
170 	    const struct selection *sel,
171 	    const struct redraw_context *ctx)
172 {
173 	bool highlighted = false;
174 	plot_font_style_t plot_fstyle = *fstyle;
175 	nserror res;
176 
177 	/* Need scaled text size to pass to plotters */
178 	plot_fstyle.size *= scale;
179 
180 	/* is this box part of a selection? */
181 	if (!excluded && ctx->interactive == true) {
182 		unsigned len = utf8_len + (space ? 1 : 0);
183 		unsigned start_idx;
184 		unsigned end_idx;
185 
186 		/* first try the browser window's current selection */
187 		if (selection_highlighted(sel,
188 					  offset,
189 					  offset + len,
190 					  &start_idx,
191 					  &end_idx)) {
192 			highlighted = true;
193 		}
194 
195 		/* what about the current search operation, if any? */
196 		if (!highlighted &&
197 		    (c->textsearch.context != NULL) &&
198 		    content_textsearch_ishighlighted(c->textsearch.context,
199 						     offset,
200 						     offset + len,
201 						     &start_idx,
202 						     &end_idx)) {
203 			highlighted = true;
204 		}
205 
206 		/* \todo make search terms visible within selected text */
207 		if (highlighted) {
208 			struct rect r;
209 			unsigned endtxt_idx = end_idx;
210 			bool clip_changed = false;
211 			bool text_visible = true;
212 			int startx, endx;
213 			plot_style_t pstyle_fill_hback = *plot_style_fill_white;
214 			plot_font_style_t fstyle_hback = plot_fstyle;
215 
216 			if (end_idx > utf8_len) {
217 				/* adjust for trailing space, not present in
218 				 * utf8_text */
219 				assert(end_idx == utf8_len + 1);
220 				endtxt_idx = utf8_len;
221 			}
222 
223 			res = guit->layout->width(fstyle,
224 						  utf8_text, start_idx,
225 						  &startx);
226 			if (res != NSERROR_OK) {
227 				startx = 0;
228 			}
229 
230 			res = guit->layout->width(fstyle,
231 						  utf8_text, endtxt_idx,
232 						  &endx);
233 			if (res != NSERROR_OK) {
234 				endx = 0;
235 			}
236 
237 			/* is there a trailing space that should be highlighted
238 			 * as well? */
239 			if (end_idx > utf8_len) {
240 					endx += space;
241 			}
242 
243 			if (scale != 1.0) {
244 				startx *= scale;
245 				endx *= scale;
246 			}
247 
248 			/* draw any text preceding highlighted portion */
249 			if ((start_idx > 0) &&
250 			    (ctx->plot->text(ctx,
251 					     &plot_fstyle,
252 					     x,
253 					     y + (int)(height * 0.75 * scale),
254 					     utf8_text,
255 					     start_idx) != NSERROR_OK))
256 				return false;
257 
258 			pstyle_fill_hback.fill_colour = fstyle->foreground;
259 
260 			/* highlighted portion */
261 			r.x0 = x + startx;
262 			r.y0 = y;
263 			r.x1 = x + endx;
264 			r.y1 = y + height * scale;
265 			res = ctx->plot->rectangle(ctx, &pstyle_fill_hback, &r);
266 			if (res != NSERROR_OK) {
267 				return false;
268 			}
269 
270 			if (start_idx > 0) {
271 				int px0 = max(x + startx, clip->x0);
272 				int px1 = min(x + endx, clip->x1);
273 
274 				if (px0 < px1) {
275 					r.x0 = px0;
276 					r.y0 = clip->y0;
277 					r.x1 = px1;
278 					r.y1 = clip->y1;
279 					res = ctx->plot->clip(ctx, &r);
280 					if (res != NSERROR_OK) {
281 						return false;
282 					}
283 
284 					clip_changed = true;
285 				} else {
286 					text_visible = false;
287 				}
288 			}
289 
290 			fstyle_hback.background =
291 				pstyle_fill_hback.fill_colour;
292 			fstyle_hback.foreground = colour_to_bw_furthest(
293 				pstyle_fill_hback.fill_colour);
294 
295 			if (text_visible &&
296 			    (ctx->plot->text(ctx,
297 					     &fstyle_hback,
298 					     x,
299 					     y + (int)(height * 0.75 * scale),
300 					     utf8_text,
301 					     endtxt_idx) != NSERROR_OK)) {
302 				return false;
303 			}
304 
305 			/* draw any text succeeding highlighted portion */
306 			if (endtxt_idx < utf8_len) {
307 				int px0 = max(x + endx, clip->x0);
308 				if (px0 < clip->x1) {
309 
310 					r.x0 = px0;
311 					r.y0 = clip->y0;
312 					r.x1 = clip->x1;
313 					r.y1 = clip->y1;
314 					res = ctx->plot->clip(ctx, &r);
315 					if (res != NSERROR_OK) {
316 						return false;
317 					}
318 
319 					clip_changed = true;
320 
321 					res = ctx->plot->text(ctx,
322 							      &plot_fstyle,
323 							      x,
324 							      y + (int)(height * 0.75 * scale),
325 							      utf8_text,
326 							      utf8_len);
327 					if (res != NSERROR_OK) {
328 						return false;
329 					}
330 				}
331 			}
332 
333 			if (clip_changed &&
334 			    (ctx->plot->clip(ctx, clip) != NSERROR_OK)) {
335 				return false;
336 			}
337 		}
338 	}
339 
340 	if (!highlighted) {
341 		res = ctx->plot->text(ctx,
342 				      &plot_fstyle,
343 				      x,
344 				      y + (int) (height * 0.75 * scale),
345 				      utf8_text,
346 				      utf8_len);
347 		if (res != NSERROR_OK) {
348 			return false;
349 		}
350 	}
351 	return true;
352 }
353 
354 
355 /**
356  * Plot a checkbox.
357  *
358  * \param  x	     left coordinate
359  * \param  y	     top coordinate
360  * \param  width     dimensions of checkbox
361  * \param  height    dimensions of checkbox
362  * \param  selected  the checkbox is selected
363  * \param  ctx	     current redraw context
364  * \return true if successful, false otherwise
365  */
366 
html_redraw_checkbox(int x,int y,int width,int height,bool selected,const struct redraw_context * ctx)367 static bool html_redraw_checkbox(int x, int y, int width, int height,
368 		bool selected, const struct redraw_context *ctx)
369 {
370 	double z;
371 	nserror res;
372 	struct rect rect;
373 
374 	z = width * 0.15;
375 	if (z == 0) {
376 		z = 1;
377 	}
378 
379 	rect.x0 = x;
380 	rect.y0 = y ;
381 	rect.x1 = x + width;
382 	rect.y1 = y + height;
383 	res = ctx->plot->rectangle(ctx, plot_style_fill_wbasec, &rect);
384 	if (res != NSERROR_OK) {
385 		return false;
386 	}
387 
388 	/* dark line across top */
389 	rect.y1 = y;
390 	res = ctx->plot->line(ctx, plot_style_stroke_darkwbasec, &rect);
391 	if (res != NSERROR_OK) {
392 		return false;
393 	}
394 
395 	/* dark line across left */
396 	rect.x1 = x;
397 	rect.y1 = y + height;
398 	res = ctx->plot->line(ctx, plot_style_stroke_darkwbasec, &rect);
399 	if (res != NSERROR_OK) {
400 		return false;
401 	}
402 
403 	/* light line across right */
404 	rect.x0 = x + width;
405 	rect.x1 = x + width;
406 	res = ctx->plot->line(ctx, plot_style_stroke_lightwbasec, &rect);
407 	if (res != NSERROR_OK) {
408 		return false;
409 	}
410 
411 	/* light line across bottom */
412 	rect.x0 = x;
413 	rect.y0 = y + height;
414 	res = ctx->plot->line(ctx, plot_style_stroke_lightwbasec, &rect);
415 	if (res != NSERROR_OK) {
416 		return false;
417 	}
418 
419 	if (selected) {
420 		if (width < 12 || height < 12) {
421 			/* render a solid box instead of a tick */
422 			rect.x0 = x + z + z;
423 			rect.y0 = y + z + z;
424 			rect.x1 = x + width - z;
425 			rect.y1 = y + height - z;
426 			res = ctx->plot->rectangle(ctx, plot_style_fill_wblobc, &rect);
427 			if (res != NSERROR_OK) {
428 				return false;
429 			}
430 		} else {
431 			/* render a tick, as it'll fit comfortably */
432 			rect.x0 = x + width - z;
433 			rect.y0 = y + z;
434 			rect.x1 = x + (z * 3);
435 			rect.y1 = y + height - z;
436 			res = ctx->plot->line(ctx, plot_style_stroke_wblobc, &rect);
437 			if (res != NSERROR_OK) {
438 				return false;
439 			}
440 
441 			rect.x0 = x + (z * 3);
442 			rect.y0 = y + height - z;
443 			rect.x1 = x + z + z;
444 			rect.y1 = y + (height / 2);
445 			res = ctx->plot->line(ctx, plot_style_stroke_wblobc, &rect);
446 			if (res != NSERROR_OK) {
447 				return false;
448 			}
449 		}
450 	}
451 	return true;
452 }
453 
454 
455 /**
456  * Plot a radio icon.
457  *
458  * \param  x	     left coordinate
459  * \param  y	     top coordinate
460  * \param  width     dimensions of radio icon
461  * \param  height    dimensions of radio icon
462  * \param  selected  the radio icon is selected
463  * \param  ctx	     current redraw context
464  * \return true if successful, false otherwise
465  */
html_redraw_radio(int x,int y,int width,int height,bool selected,const struct redraw_context * ctx)466 static bool html_redraw_radio(int x, int y, int width, int height,
467 		bool selected, const struct redraw_context *ctx)
468 {
469 	nserror res;
470 
471 	/* plot background of radio button */
472 	res = ctx->plot->disc(ctx,
473 			      plot_style_fill_wbasec,
474 			      x + width * 0.5,
475 			      y + height * 0.5,
476 			      width * 0.5 - 1);
477 	if (res != NSERROR_OK) {
478 		return false;
479 	}
480 
481 	/* plot dark arc */
482 	res = ctx->plot->arc(ctx,
483 			     plot_style_fill_darkwbasec,
484 			     x + width * 0.5,
485 			     y + height * 0.5,
486 			     width * 0.5 - 1,
487 			     45,
488 			     225);
489 	if (res != NSERROR_OK) {
490 		return false;
491 	}
492 
493 	/* plot light arc */
494 	res = ctx->plot->arc(ctx,
495 			     plot_style_fill_lightwbasec,
496 			     x + width * 0.5,
497 			     y + height * 0.5,
498 			     width * 0.5 - 1,
499 			     225,
500 			     45);
501 	if (res != NSERROR_OK) {
502 		return false;
503 	}
504 
505 	if (selected) {
506 		/* plot selection blob */
507 		res = ctx->plot->disc(ctx,
508 				      plot_style_fill_wblobc,
509 				      x + width * 0.5,
510 				      y + height * 0.5,
511 				      width * 0.3 - 1);
512 		if (res != NSERROR_OK) {
513 			return false;
514 		}
515 	}
516 
517 	return true;
518 }
519 
520 
521 /**
522  * Plot a file upload input.
523  *
524  * \param  x	     left coordinate
525  * \param  y	     top coordinate
526  * \param  width     dimensions of input
527  * \param  height    dimensions of input
528  * \param  box	     box of input
529  * \param  scale     scale for redraw
530  * \param  background_colour  current background colour
531  * \param  len_ctx   Length conversion context
532  * \param  ctx	     current redraw context
533  * \return true if successful, false otherwise
534  */
535 
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)536 static bool html_redraw_file(int x, int y, int width, int height,
537 		struct box *box, float scale, colour background_colour,
538 		const nscss_len_ctx *len_ctx,
539 		const struct redraw_context *ctx)
540 {
541 	int text_width;
542 	const char *text;
543 	size_t length;
544 	plot_font_style_t fstyle;
545 	nserror res;
546 
547 	font_plot_style_from_css(len_ctx, box->style, &fstyle);
548 	fstyle.background = background_colour;
549 
550 	if (box->gadget->value) {
551 		text = box->gadget->value;
552 	} else {
553 		text = messages_get("Form_Drop");
554 	}
555 	length = strlen(text);
556 
557 	res = guit->layout->width(&fstyle, text, length, &text_width);
558 	if (res != NSERROR_OK) {
559 		return false;
560 	}
561 	text_width *= scale;
562 	if (width < text_width + 8) {
563 		x = x + width - text_width - 4;
564 	} else {
565 		x = x + 4;
566 	}
567 
568 	res = ctx->plot->text(ctx, &fstyle, x, y + height * 0.75, text, length);
569 	if (res != NSERROR_OK) {
570 		return false;
571 	}
572 	return true;
573 }
574 
575 
576 /**
577  * Plot background images.
578  *
579  * The reason for the presence of \a background is the backwards compatibility
580  * mess that is backgrounds on &lt;body&gt;. The background will be drawn relative
581  * to \a box, using the background information contained within \a background.
582  *
583  * \param  x	  coordinate of box
584  * \param  y	  coordinate of box
585  * \param  box	  box to draw background image of
586  * \param  scale  scale for redraw
587  * \param  clip   current clip rectangle
588  * \param  background_colour  current background colour
589  * \param  background  box containing background details (usually \a box)
590  * \param  len_ctx  Length conversion context
591  * \param  ctx      current redraw context
592  * \return true if successful, false otherwise
593  */
594 
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)595 static bool html_redraw_background(int x, int y, struct box *box, float scale,
596 		const struct rect *clip, colour *background_colour,
597 		struct box *background,
598 		const nscss_len_ctx *len_ctx,
599 		const struct redraw_context *ctx)
600 {
601 	bool repeat_x = false;
602 	bool repeat_y = false;
603 	bool plot_colour = true;
604 	bool plot_content;
605 	bool clip_to_children = false;
606 	struct box *clip_box = box;
607 	int ox = x, oy = y;
608 	int width, height;
609 	css_fixed hpos = 0, vpos = 0;
610 	css_unit hunit = CSS_UNIT_PX, vunit = CSS_UNIT_PX;
611 	struct box *parent;
612 	struct rect r = *clip;
613 	css_color bgcol;
614 	plot_style_t pstyle_fill_bg = {
615 		.fill_type = PLOT_OP_TYPE_SOLID,
616 		.fill_colour = *background_colour,
617 	};
618 	nserror res;
619 
620 	if (ctx->background_images == false)
621 		return true;
622 
623 	plot_content = (background->background != NULL);
624 
625 	if (plot_content) {
626 		if (!box->parent) {
627 			/* Root element, special case:
628 			 * background origin calc. is based on margin box */
629 			x -= box->margin[LEFT] * scale;
630 			y -= box->margin[TOP] * scale;
631 			width = box->margin[LEFT] + box->padding[LEFT] +
632 					box->width + box->padding[RIGHT] +
633 					box->margin[RIGHT];
634 			height = box->margin[TOP] + box->padding[TOP] +
635 					box->height + box->padding[BOTTOM] +
636 					box->margin[BOTTOM];
637 		} else {
638 			width = box->padding[LEFT] + box->width +
639 					box->padding[RIGHT];
640 			height = box->padding[TOP] + box->height +
641 					box->padding[BOTTOM];
642 		}
643 		/* handle background-repeat */
644 		switch (css_computed_background_repeat(background->style)) {
645 		case CSS_BACKGROUND_REPEAT_REPEAT:
646 			repeat_x = repeat_y = true;
647 			/* optimisation: only plot the colour if
648 			 * bitmap is not opaque */
649 			plot_colour = !content_get_opaque(background->background);
650 			break;
651 
652 		case CSS_BACKGROUND_REPEAT_REPEAT_X:
653 			repeat_x = true;
654 			break;
655 
656 		case CSS_BACKGROUND_REPEAT_REPEAT_Y:
657 			repeat_y = true;
658 			break;
659 
660 		case CSS_BACKGROUND_REPEAT_NO_REPEAT:
661 			break;
662 
663 		default:
664 			break;
665 		}
666 
667 		/* handle background-position */
668 		css_computed_background_position(background->style,
669 				&hpos, &hunit, &vpos, &vunit);
670 		if (hunit == CSS_UNIT_PCT) {
671 			x += (width -
672 				content_get_width(background->background)) *
673 				scale * FIXTOFLT(hpos) / 100.;
674 		} else {
675 			x += (int) (FIXTOFLT(nscss_len2px(len_ctx, hpos, hunit,
676 					background->style)) * scale);
677 		}
678 
679 		if (vunit == CSS_UNIT_PCT) {
680 			y += (height -
681 				content_get_height(background->background)) *
682 				scale * FIXTOFLT(vpos) / 100.;
683 		} else {
684 			y += (int) (FIXTOFLT(nscss_len2px(len_ctx, vpos, vunit,
685 					background->style)) * scale);
686 		}
687 	}
688 
689 	/* special case for table rows as their background needs
690 	 * to be clipped to all the cells */
691 	if (box->type == BOX_TABLE_ROW) {
692 		css_fixed h = 0, v = 0;
693 		css_unit hu = CSS_UNIT_PX, vu = CSS_UNIT_PX;
694 
695 		for (parent = box->parent;
696 			((parent) && (parent->type != BOX_TABLE));
697 				parent = parent->parent);
698 		assert(parent && (parent->style));
699 
700 		css_computed_border_spacing(parent->style, &h, &hu, &v, &vu);
701 
702 		clip_to_children = (h > 0) || (v > 0);
703 
704 		if (clip_to_children)
705 			clip_box = box->children;
706 	}
707 
708 	for (; clip_box; clip_box = clip_box->next) {
709 		/* clip to child boxes if needed */
710 		if (clip_to_children) {
711 			assert(clip_box->type == BOX_TABLE_CELL);
712 
713 			/* update clip.* to the child cell */
714 			r.x0 = ox + (clip_box->x * scale);
715 			r.y0 = oy + (clip_box->y * scale);
716 			r.x1 = r.x0 + (clip_box->padding[LEFT] +
717 					clip_box->width +
718 					clip_box->padding[RIGHT]) * scale;
719 			r.y1 = r.y0 + (clip_box->padding[TOP] +
720 					clip_box->height +
721 					clip_box->padding[BOTTOM]) * scale;
722 
723 			if (r.x0 < clip->x0) r.x0 = clip->x0;
724 			if (r.y0 < clip->y0) r.y0 = clip->y0;
725 			if (r.x1 > clip->x1) r.x1 = clip->x1;
726 			if (r.y1 > clip->y1) r.y1 = clip->y1;
727 
728 			css_computed_background_color(clip_box->style, &bgcol);
729 
730 			/* <td> attributes override <tr> */
731 			/* if the background content is opaque there
732 			 * is no need to plot underneath it.
733 			 */
734 			if ((r.x0 >= r.x1) ||
735 			    (r.y0 >= r.y1) ||
736 			    (nscss_color_is_transparent(bgcol) == false) ||
737 			    ((clip_box->background != NULL) &&
738 			     content_get_opaque(clip_box->background)))
739 				continue;
740 		}
741 
742 		/* plot the background colour */
743 		css_computed_background_color(background->style, &bgcol);
744 
745 		if (nscss_color_is_transparent(bgcol) == false) {
746 			*background_colour = nscss_color_to_ns(bgcol);
747 			pstyle_fill_bg.fill_colour = *background_colour;
748 			if (plot_colour) {
749 				res = ctx->plot->rectangle(ctx, &pstyle_fill_bg, &r);
750 				if (res != NSERROR_OK) {
751 					return false;
752 				}
753 			}
754 		}
755 		/* and plot the image */
756 		if (plot_content) {
757 			width = content_get_width(background->background);
758 			height = content_get_height(background->background);
759 
760 			/* ensure clip area only as large as required */
761 			if (!repeat_x) {
762 				if (r.x0 < x)
763 					r.x0 = x;
764 				if (r.x1 > x + width * scale)
765 					r.x1 = x + width * scale;
766 			}
767 			if (!repeat_y) {
768 				if (r.y0 < y)
769 					r.y0 = y;
770 				if (r.y1 > y + height * scale)
771 					r.y1 = y + height * scale;
772 			}
773 			/* valid clipping rectangles only */
774 			if ((r.x0 < r.x1) && (r.y0 < r.y1)) {
775 				struct content_redraw_data bg_data;
776 
777 				res = ctx->plot->clip(ctx, &r);
778 				if (res != NSERROR_OK) {
779 					return false;
780 				}
781 
782 				bg_data.x = x;
783 				bg_data.y = y;
784 				bg_data.width = ceilf(width * scale);
785 				bg_data.height = ceilf(height * scale);
786 				bg_data.background_colour = *background_colour;
787 				bg_data.scale = scale;
788 				bg_data.repeat_x = repeat_x;
789 				bg_data.repeat_y = repeat_y;
790 
791 				/* We just continue if redraw fails */
792 				content_redraw(background->background,
793 						&bg_data, &r, ctx);
794 			}
795 		}
796 
797 		/* only <tr> rows being clipped to child boxes loop */
798 		if (!clip_to_children)
799 			return true;
800 	}
801 	return true;
802 }
803 
804 
805 /**
806  * Plot an inline's background and/or background image.
807  *
808  * \param  x	  coordinate of box
809  * \param  y	  coordinate of box
810  * \param  box	  BOX_INLINE which created the background
811  * \param  scale  scale for redraw
812  * \param  clip	  coordinates of clip rectangle
813  * \param  b	  coordinates of border edge rectangle
814  * \param  first  true if this is the first rectangle associated with the inline
815  * \param  last   true if this is the last rectangle associated with the inline
816  * \param  background_colour  updated to current background colour if plotted
817  * \param  len_ctx  Length conversion context
818  * \param  ctx      current redraw context
819  * \return true if successful, false otherwise
820  */
821 
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)822 static bool html_redraw_inline_background(int x, int y, struct box *box,
823 		float scale, const struct rect *clip, struct rect b,
824 		bool first, bool last, colour *background_colour,
825 		const nscss_len_ctx *len_ctx,
826 		const struct redraw_context *ctx)
827 {
828 	struct rect r = *clip;
829 	bool repeat_x = false;
830 	bool repeat_y = false;
831 	bool plot_colour = true;
832 	bool plot_content;
833 	css_fixed hpos = 0, vpos = 0;
834 	css_unit hunit = CSS_UNIT_PX, vunit = CSS_UNIT_PX;
835 	css_color bgcol;
836 	plot_style_t pstyle_fill_bg = {
837 		.fill_type = PLOT_OP_TYPE_SOLID,
838 		.fill_colour = *background_colour,
839 	};
840 	nserror res;
841 
842 	plot_content = (box->background != NULL);
843 
844 	if (html_redraw_printing && nsoption_bool(remove_backgrounds))
845 		return true;
846 
847 	if (plot_content) {
848 		/* handle background-repeat */
849 		switch (css_computed_background_repeat(box->style)) {
850 		case CSS_BACKGROUND_REPEAT_REPEAT:
851 			repeat_x = repeat_y = true;
852 			/* optimisation: only plot the colour if
853 			 * bitmap is not opaque
854 			 */
855 			plot_colour = !content_get_opaque(box->background);
856 			break;
857 
858 		case CSS_BACKGROUND_REPEAT_REPEAT_X:
859 			repeat_x = true;
860 			break;
861 
862 		case CSS_BACKGROUND_REPEAT_REPEAT_Y:
863 			repeat_y = true;
864 			break;
865 
866 		case CSS_BACKGROUND_REPEAT_NO_REPEAT:
867 			break;
868 
869 		default:
870 			break;
871 		}
872 
873 		/* handle background-position */
874 		css_computed_background_position(box->style,
875 				&hpos, &hunit, &vpos, &vunit);
876 		if (hunit == CSS_UNIT_PCT) {
877 			x += (b.x1 - b.x0 -
878 					content_get_width(box->background) *
879 					scale) * FIXTOFLT(hpos) / 100.;
880 
881 			if (!repeat_x && ((hpos < 2 && !first) ||
882 					(hpos > 98 && !last))){
883 				plot_content = false;
884 			}
885 		} else {
886 			x += (int) (FIXTOFLT(nscss_len2px(len_ctx, hpos, hunit,
887 					box->style)) * scale);
888 		}
889 
890 		if (vunit == CSS_UNIT_PCT) {
891 			y += (b.y1 - b.y0 -
892 					content_get_height(box->background) *
893 					scale) * FIXTOFLT(vpos) / 100.;
894 		} else {
895 			y += (int) (FIXTOFLT(nscss_len2px(len_ctx, vpos, vunit,
896 					box->style)) * scale);
897 		}
898 	}
899 
900 	/* plot the background colour */
901 	css_computed_background_color(box->style, &bgcol);
902 
903 	if (nscss_color_is_transparent(bgcol) == false) {
904 		*background_colour = nscss_color_to_ns(bgcol);
905 		pstyle_fill_bg.fill_colour = *background_colour;
906 
907 		if (plot_colour) {
908 			res = ctx->plot->rectangle(ctx, &pstyle_fill_bg, &r);
909 			if (res != NSERROR_OK) {
910 				return false;
911 			}
912 		}
913 	}
914 	/* and plot the image */
915 	if (plot_content) {
916 		int width = content_get_width(box->background);
917 		int height = content_get_height(box->background);
918 
919 		if (!repeat_x) {
920 			if (r.x0 < x)
921 				r.x0 = x;
922 			if (r.x1 > x + width * scale)
923 				r.x1 = x + width * scale;
924 		}
925 		if (!repeat_y) {
926 			if (r.y0 < y)
927 				r.y0 = y;
928 			if (r.y1 > y + height * scale)
929 				r.y1 = y + height * scale;
930 		}
931 		/* valid clipping rectangles only */
932 		if ((r.x0 < r.x1) && (r.y0 < r.y1)) {
933 			struct content_redraw_data bg_data;
934 
935 			res = ctx->plot->clip(ctx, &r);
936 			if (res != NSERROR_OK) {
937 				return false;
938 			}
939 
940 			bg_data.x = x;
941 			bg_data.y = y;
942 			bg_data.width = ceilf(width * scale);
943 			bg_data.height = ceilf(height * scale);
944 			bg_data.background_colour = *background_colour;
945 			bg_data.scale = scale;
946 			bg_data.repeat_x = repeat_x;
947 			bg_data.repeat_y = repeat_y;
948 
949 			/* We just continue if redraw fails */
950 			content_redraw(box->background, &bg_data, &r, ctx);
951 		}
952 	}
953 
954 	return true;
955 }
956 
957 
958 /**
959  * Plot text decoration for an inline box.
960  *
961  * \param  box     box to plot decorations for, of type BOX_INLINE
962  * \param  x       x coordinate of parent of box
963  * \param  y       y coordinate of parent of box
964  * \param  scale   scale for redraw
965  * \param  colour  colour for decorations
966  * \param  ratio   position of line as a ratio of line height
967  * \param  ctx	   current redraw context
968  * \return true if successful, false otherwise
969  */
970 
971 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)972 html_redraw_text_decoration_inline(struct box *box,
973 				   int x, int y,
974 				   float scale,
975 				   colour colour,
976 				   float ratio,
977 				   const struct redraw_context *ctx)
978 {
979 	struct box *c;
980 	plot_style_t plot_style_box = {
981 		.stroke_type = PLOT_OP_TYPE_SOLID,
982 		.stroke_colour = colour,
983 	};
984 	nserror res;
985 	struct rect rect;
986 
987 	for (c = box->next;
988 	     c && c != box->inline_end;
989 	     c = c->next) {
990 		if (c->type != BOX_TEXT) {
991 			continue;
992 		}
993 		rect.x0 = (x + c->x) * scale;
994 		rect.y0 = (y + c->y + c->height * ratio) * scale;
995 		rect.x1 = (x + c->x + c->width) * scale;
996 		rect.y1 = (y + c->y + c->height * ratio) * scale;
997 		res = ctx->plot->line(ctx, &plot_style_box, &rect);
998 		if (res != NSERROR_OK) {
999 			return false;
1000 		}
1001 	}
1002 	return true;
1003 }
1004 
1005 
1006 /**
1007  * Plot text decoration for an non-inline box.
1008  *
1009  * \param  box     box to plot decorations for, of type other than BOX_INLINE
1010  * \param  x       x coordinate of box
1011  * \param  y       y coordinate of box
1012  * \param  scale   scale for redraw
1013  * \param  colour  colour for decorations
1014  * \param  ratio   position of line as a ratio of line height
1015  * \param  ctx	   current redraw context
1016  * \return true if successful, false otherwise
1017  */
1018 
1019 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)1020 html_redraw_text_decoration_block(struct box *box,
1021 				  int x, int y,
1022 				  float scale,
1023 				  colour colour,
1024 				  float ratio,
1025 				  const struct redraw_context *ctx)
1026 {
1027 	struct box *c;
1028 	plot_style_t plot_style_box = {
1029 		.stroke_type = PLOT_OP_TYPE_SOLID,
1030 		.stroke_colour = colour,
1031 	};
1032 	nserror res;
1033 	struct rect rect;
1034 
1035 	/* draw through text descendants */
1036 	for (c = box->children; c; c = c->next) {
1037 		if (c->type == BOX_TEXT) {
1038 			rect.x0 = (x + c->x) * scale;
1039 			rect.y0 = (y + c->y + c->height * ratio) * scale;
1040 			rect.x1 = (x + c->x + c->width) * scale;
1041 			rect.y1 = (y + c->y + c->height * ratio) * scale;
1042 			res = ctx->plot->line(ctx, &plot_style_box, &rect);
1043 			if (res != NSERROR_OK) {
1044 				return false;
1045 			}
1046 		} else if ((c->type == BOX_INLINE_CONTAINER) || (c->type == BOX_BLOCK)) {
1047 			if (!html_redraw_text_decoration_block(c,
1048 					x + c->x, y + c->y,
1049 					scale, colour, ratio, ctx))
1050 				return false;
1051 		}
1052 	}
1053 	return true;
1054 }
1055 
1056 
1057 /**
1058  * Plot text decoration for a box.
1059  *
1060  * \param  box       box to plot decorations for
1061  * \param  x_parent  x coordinate of parent of box
1062  * \param  y_parent  y coordinate of parent of box
1063  * \param  scale     scale for redraw
1064  * \param  background_colour  current background colour
1065  * \param  ctx	     current redraw context
1066  * \return true if successful, false otherwise
1067  */
1068 
html_redraw_text_decoration(struct box * box,int x_parent,int y_parent,float scale,colour background_colour,const struct redraw_context * ctx)1069 static bool html_redraw_text_decoration(struct box *box,
1070 		int x_parent, int y_parent, float scale,
1071 		colour background_colour, const struct redraw_context *ctx)
1072 {
1073 	static const enum css_text_decoration_e decoration[] = {
1074 		CSS_TEXT_DECORATION_UNDERLINE, CSS_TEXT_DECORATION_OVERLINE,
1075 		CSS_TEXT_DECORATION_LINE_THROUGH };
1076 	static const float line_ratio[] = { 0.9, 0.1, 0.5 };
1077 	colour fgcol;
1078 	unsigned int i;
1079 	css_color col;
1080 
1081 	css_computed_color(box->style, &col);
1082 	fgcol = nscss_color_to_ns(col);
1083 
1084 	/* antialias colour for under/overline */
1085 	if (html_redraw_printing == false)
1086 		fgcol = blend_colour(background_colour, fgcol);
1087 
1088 	if (box->type == BOX_INLINE) {
1089 		if (!box->inline_end)
1090 			return true;
1091 		for (i = 0; i != NOF_ELEMENTS(decoration); i++)
1092 			if (css_computed_text_decoration(box->style) &
1093 					decoration[i])
1094 				if (!html_redraw_text_decoration_inline(box,
1095 						x_parent, y_parent, scale,
1096 						fgcol, line_ratio[i], ctx))
1097 					return false;
1098 	} else {
1099 		for (i = 0; i != NOF_ELEMENTS(decoration); i++)
1100 			if (css_computed_text_decoration(box->style) &
1101 					decoration[i])
1102 				if (!html_redraw_text_decoration_block(box,
1103 						x_parent + box->x,
1104 						y_parent + box->y,
1105 						scale,
1106 						fgcol, line_ratio[i], ctx))
1107 					return false;
1108 	}
1109 
1110 	return true;
1111 }
1112 
1113 
1114 /**
1115  * Redraw the text content of a box, possibly partially highlighted
1116  * because the text has been selected, or matches a search operation.
1117  *
1118  * \param html The html content to redraw text within.
1119  * \param  box      box with text content
1120  * \param  x        x co-ord of box
1121  * \param  y        y co-ord of box
1122  * \param  clip     current clip rectangle
1123  * \param  scale    current scale setting (1.0 = 100%)
1124  * \param  current_background_color
1125  * \param  ctx	    current redraw context
1126  * \return true iff successful and redraw should proceed
1127  */
1128 
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)1129 static bool html_redraw_text_box(const html_content *html, struct box *box,
1130 		int x, int y, const struct rect *clip, float scale,
1131 		colour current_background_color,
1132 		const struct redraw_context *ctx)
1133 {
1134 	bool excluded = (box->object != NULL);
1135 	plot_font_style_t fstyle;
1136 
1137 	font_plot_style_from_css(&html->len_ctx, box->style, &fstyle);
1138 	fstyle.background = current_background_color;
1139 
1140 	if (!text_redraw(box->text,
1141 			 box->length,
1142 			 box->byte_offset,
1143 			 box->space,
1144 			 &fstyle,
1145 			 x, y,
1146 			 clip,
1147 			 box->height,
1148 			 scale,
1149 			 excluded,
1150 			 (struct content *)html,
1151 			 html->sel,
1152 			 ctx))
1153 		return false;
1154 
1155 	return true;
1156 }
1157 
1158 bool html_redraw_box(const html_content *html, struct box *box,
1159 		int x_parent, int y_parent,
1160 		const struct rect *clip, float scale,
1161 		colour current_background_color,
1162 		const struct redraw_context *ctx);
1163 
1164 /**
1165  * Draw the various children of a box.
1166  *
1167  * \param  html	     html content
1168  * \param  box	     box to draw children of
1169  * \param  x_parent  coordinate of parent box
1170  * \param  y_parent  coordinate of parent box
1171  * \param  clip      clip rectangle
1172  * \param  scale     scale for redraw
1173  * \param  current_background_color  background colour under this box
1174  * \param  ctx	     current redraw context
1175  * \return true if successful, false otherwise
1176  */
1177 
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)1178 static bool html_redraw_box_children(const html_content *html, struct box *box,
1179 		int x_parent, int y_parent,
1180 		const struct rect *clip, float scale,
1181 		colour current_background_color,
1182 		const struct redraw_context *ctx)
1183 {
1184 	struct box *c;
1185 
1186 	for (c = box->children; c; c = c->next) {
1187 
1188 		if (c->type != BOX_FLOAT_LEFT && c->type != BOX_FLOAT_RIGHT)
1189 			if (!html_redraw_box(html, c,
1190 					x_parent + box->x -
1191 					scrollbar_get_offset(box->scroll_x),
1192 					y_parent + box->y -
1193 					scrollbar_get_offset(box->scroll_y),
1194 					clip, scale, current_background_color,
1195 					ctx))
1196 				return false;
1197 	}
1198 	for (c = box->float_children; c; c = c->next_float)
1199 		if (!html_redraw_box(html, c,
1200 				x_parent + box->x -
1201 				scrollbar_get_offset(box->scroll_x),
1202 				y_parent + box->y -
1203 				scrollbar_get_offset(box->scroll_y),
1204 				clip, scale, current_background_color,
1205 				ctx))
1206 			return false;
1207 
1208 	return true;
1209 }
1210 
1211 /**
1212  * Recursively draw a box.
1213  *
1214  * \param  html	     html content
1215  * \param  box	     box to draw
1216  * \param  x_parent  coordinate of parent box
1217  * \param  y_parent  coordinate of parent box
1218  * \param  clip      clip rectangle
1219  * \param  scale     scale for redraw
1220  * \param  current_background_color  background colour under this box
1221  * \param  ctx	     current redraw context
1222  * \return true if successful, false otherwise
1223  *
1224  * x, y, clip_[xy][01] are in target coordinates.
1225  */
1226 
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)1227 bool html_redraw_box(const html_content *html, struct box *box,
1228 		int x_parent, int y_parent,
1229 		const struct rect *clip, const float scale,
1230 		colour current_background_color,
1231 		const struct redraw_context *ctx)
1232 {
1233 	const struct plotter_table *plot = ctx->plot;
1234 	int x, y;
1235 	int width, height;
1236 	int padding_left, padding_top, padding_width, padding_height;
1237 	int border_left, border_top, border_right, border_bottom;
1238 	struct rect r;
1239 	struct rect rect;
1240 	int x_scrolled, y_scrolled;
1241 	struct box *bg_box = NULL;
1242 	css_computed_clip_rect css_rect;
1243 	enum css_overflow_e overflow_x = CSS_OVERFLOW_VISIBLE;
1244 	enum css_overflow_e overflow_y = CSS_OVERFLOW_VISIBLE;
1245 	dom_exception exc;
1246 	dom_html_element_type tag_type;
1247 
1248 
1249 	if (html_redraw_printing && (box->flags & PRINTED))
1250 		return true;
1251 
1252 	if (box->style != NULL) {
1253 		overflow_x = css_computed_overflow_x(box->style);
1254 		overflow_y = css_computed_overflow_y(box->style);
1255 	}
1256 
1257 	/* avoid trivial FP maths */
1258 	if (scale == 1.0) {
1259 		x = x_parent + box->x;
1260 		y = y_parent + box->y;
1261 		width = box->width;
1262 		height = box->height;
1263 		padding_left = box->padding[LEFT];
1264 		padding_top = box->padding[TOP];
1265 		padding_width = padding_left + box->width + box->padding[RIGHT];
1266 		padding_height = padding_top + box->height +
1267 				box->padding[BOTTOM];
1268 		border_left = box->border[LEFT].width;
1269 		border_top = box->border[TOP].width;
1270 		border_right = box->border[RIGHT].width;
1271 		border_bottom = box->border[BOTTOM].width;
1272 	} else {
1273 		x = (x_parent + box->x) * scale;
1274 		y = (y_parent + box->y) * scale;
1275 		width = box->width * scale;
1276 		height = box->height * scale;
1277 		/* left and top padding values are normally zero,
1278 		 * so avoid trivial FP maths */
1279 		padding_left = box->padding[LEFT] ? box->padding[LEFT] * scale
1280 				: 0;
1281 		padding_top = box->padding[TOP] ? box->padding[TOP] * scale
1282 				: 0;
1283 		padding_width = (box->padding[LEFT] + box->width +
1284 				box->padding[RIGHT]) * scale;
1285 		padding_height = (box->padding[TOP] + box->height +
1286 				box->padding[BOTTOM]) * scale;
1287 		border_left = box->border[LEFT].width * scale;
1288 		border_top = box->border[TOP].width * scale;
1289 		border_right = box->border[RIGHT].width * scale;
1290 		border_bottom = box->border[BOTTOM].width * scale;
1291 	}
1292 
1293 	/* calculate rectangle covering this box and descendants */
1294 	if (box->style && overflow_x != CSS_OVERFLOW_VISIBLE &&
1295 			box->parent != NULL) {
1296 		/* box contents clipped to box size */
1297 		r.x0 = x - border_left;
1298 		r.x1 = x + padding_width + border_right;
1299 	} else {
1300 		/* box contents can hang out of the box; use descendant box */
1301 		if (scale == 1.0) {
1302 			r.x0 = x + box->descendant_x0;
1303 			r.x1 = x + box->descendant_x1 + 1;
1304 		} else {
1305 			r.x0 = x + box->descendant_x0 * scale;
1306 			r.x1 = x + box->descendant_x1 * scale + 1;
1307 		}
1308 		if (!box->parent) {
1309 			/* root element */
1310 			int margin_left, margin_right;
1311 			if (scale == 1.0) {
1312 				margin_left = box->margin[LEFT];
1313 				margin_right = box->margin[RIGHT];
1314 			} else {
1315 				margin_left = box->margin[LEFT] * scale;
1316 				margin_right = box->margin[RIGHT] * scale;
1317 			}
1318 			r.x0 = x - border_left - margin_left < r.x0 ?
1319 					x - border_left - margin_left : r.x0;
1320 			r.x1 = x + padding_width + border_right +
1321 					margin_right > r.x1 ?
1322 					x + padding_width + border_right +
1323 					margin_right : r.x1;
1324 		}
1325 	}
1326 
1327 	/* calculate rectangle covering this box and descendants */
1328 	if (box->style && overflow_y != CSS_OVERFLOW_VISIBLE &&
1329 			box->parent != NULL) {
1330 		/* box contents clipped to box size */
1331 		r.y0 = y - border_top;
1332 		r.y1 = y + padding_height + border_bottom;
1333 	} else {
1334 		/* box contents can hang out of the box; use descendant box */
1335 		if (scale == 1.0) {
1336 			r.y0 = y + box->descendant_y0;
1337 			r.y1 = y + box->descendant_y1 + 1;
1338 		} else {
1339 			r.y0 = y + box->descendant_y0 * scale;
1340 			r.y1 = y + box->descendant_y1 * scale + 1;
1341 		}
1342 		if (!box->parent) {
1343 			/* root element */
1344 			int margin_top, margin_bottom;
1345 			if (scale == 1.0) {
1346 				margin_top = box->margin[TOP];
1347 				margin_bottom = box->margin[BOTTOM];
1348 			} else {
1349 				margin_top = box->margin[TOP] * scale;
1350 				margin_bottom = box->margin[BOTTOM] * scale;
1351 			}
1352 			r.y0 = y - border_top - margin_top < r.y0 ?
1353 					y - border_top - margin_top : r.y0;
1354 			r.y1 = y + padding_height + border_bottom +
1355 					margin_bottom > r.y1 ?
1356 					y + padding_height + border_bottom +
1357 					margin_bottom : r.y1;
1358 		}
1359 	}
1360 
1361 	/* return if the rectangle is completely outside the clip rectangle */
1362 	if (clip->y1 < r.y0 || r.y1 < clip->y0 ||
1363 			clip->x1 < r.x0 || r.x1 < clip->x0)
1364 		return true;
1365 
1366 	/*if the rectangle is under the page bottom but it can fit in a page,
1367 	don't print it now*/
1368 	if (html_redraw_printing) {
1369 		if (r.y1 > html_redraw_printing_border) {
1370 			if (r.y1 - r.y0 <= html_redraw_printing_border &&
1371 					(box->type == BOX_TEXT ||
1372 					box->type == BOX_TABLE_CELL
1373 					|| box->object || box->gadget)) {
1374 				/*remember the highest of all points from the
1375 				not printed elements*/
1376 				if (r.y0 < html_redraw_printing_top_cropped)
1377 					html_redraw_printing_top_cropped = r.y0;
1378 				return true;
1379 			}
1380 		}
1381 		else box->flags |= PRINTED; /*it won't be printed anymore*/
1382 	}
1383 
1384 	/* if visibility is hidden render children only */
1385 	if (box->style && css_computed_visibility(box->style) ==
1386 			CSS_VISIBILITY_HIDDEN) {
1387 		if ((ctx->plot->group_start) &&
1388 		    (ctx->plot->group_start(ctx, "hidden box") != NSERROR_OK))
1389 			return false;
1390 		if (!html_redraw_box_children(html, box, x_parent, y_parent,
1391 				&r, scale, current_background_color, ctx))
1392 			return false;
1393 		return ((!ctx->plot->group_end) || (ctx->plot->group_end(ctx) == NSERROR_OK));
1394 	}
1395 
1396 	if ((ctx->plot->group_start) &&
1397 	    (ctx->plot->group_start(ctx,"vis box") != NSERROR_OK)) {
1398 		return false;
1399 	}
1400 
1401 	if (box->style != NULL &&
1402 			css_computed_position(box->style) ==
1403 					CSS_POSITION_ABSOLUTE &&
1404 			css_computed_clip(box->style, &css_rect) ==
1405 					CSS_CLIP_RECT) {
1406 		/* We have an absolutly positioned box with a clip rect */
1407 		if (css_rect.left_auto == false)
1408 			r.x0 = x - border_left + FIXTOINT(nscss_len2px(
1409 					&html->len_ctx,
1410 					css_rect.left, css_rect.lunit,
1411 					box->style));
1412 
1413 		if (css_rect.top_auto == false)
1414 			r.y0 = y - border_top + FIXTOINT(nscss_len2px(
1415 					&html->len_ctx,
1416 					css_rect.top, css_rect.tunit,
1417 					box->style));
1418 
1419 		if (css_rect.right_auto == false)
1420 			r.x1 = x - border_left + FIXTOINT(nscss_len2px(
1421 					&html->len_ctx,
1422 					css_rect.right, css_rect.runit,
1423 					box->style));
1424 
1425 		if (css_rect.bottom_auto == false)
1426 			r.y1 = y - border_top + FIXTOINT(nscss_len2px(
1427 					&html->len_ctx,
1428 					css_rect.bottom, css_rect.bunit,
1429 					box->style));
1430 
1431 		/* find intersection of clip rectangle and box */
1432 		if (r.x0 < clip->x0) r.x0 = clip->x0;
1433 		if (r.y0 < clip->y0) r.y0 = clip->y0;
1434 		if (clip->x1 < r.x1) r.x1 = clip->x1;
1435 		if (clip->y1 < r.y1) r.y1 = clip->y1;
1436 		/* Nothing to do for invalid rectangles */
1437 		if (r.x0 >= r.x1 || r.y0 >= r.y1)
1438 			/* not an error */
1439 			return ((!ctx->plot->group_end) ||
1440 				(ctx->plot->group_end(ctx) == NSERROR_OK));
1441 		/* clip to it */
1442 		if (ctx->plot->clip(ctx, &r) != NSERROR_OK)
1443 			return false;
1444 
1445 	} else if (box->type == BOX_BLOCK || box->type == BOX_INLINE_BLOCK ||
1446 			box->type == BOX_TABLE_CELL || box->object) {
1447 		/* find intersection of clip rectangle and box */
1448 		if (r.x0 < clip->x0) r.x0 = clip->x0;
1449 		if (r.y0 < clip->y0) r.y0 = clip->y0;
1450 		if (clip->x1 < r.x1) r.x1 = clip->x1;
1451 		if (clip->y1 < r.y1) r.y1 = clip->y1;
1452 		/* no point trying to draw 0-width/height boxes */
1453 		if (r.x0 == r.x1 || r.y0 == r.y1)
1454 			/* not an error */
1455 			return ((!ctx->plot->group_end) ||
1456 				(ctx->plot->group_end(ctx) == NSERROR_OK));
1457 		/* clip to it */
1458 		if (ctx->plot->clip(ctx, &r) != NSERROR_OK)
1459 			return false;
1460 	} else {
1461 		/* clip box is fine, clip to it */
1462 		r = *clip;
1463 		if (ctx->plot->clip(ctx, &r) != NSERROR_OK)
1464 			return false;
1465 	}
1466 
1467 	/* background colour and image for block level content and replaced
1468 	 * inlines */
1469 
1470 	bg_box = html_redraw_find_bg_box(box);
1471 
1472 	/* bg_box == NULL implies that this box should not have
1473 	* its background rendered. Otherwise filter out linebreaks,
1474 	* optimize away non-differing inlines, only plot background
1475 	* for BOX_TEXT it's in an inline */
1476 	if (bg_box && bg_box->type != BOX_BR &&
1477 			bg_box->type != BOX_TEXT &&
1478 			bg_box->type != BOX_INLINE_END &&
1479 			(bg_box->type != BOX_INLINE || bg_box->object ||
1480 			bg_box->flags & IFRAME || box->flags & REPLACE_DIM ||
1481 			(bg_box->gadget != NULL &&
1482 			(bg_box->gadget->type == GADGET_TEXTAREA ||
1483 			bg_box->gadget->type == GADGET_TEXTBOX ||
1484 			bg_box->gadget->type == GADGET_PASSWORD)))) {
1485 		/* find intersection of clip box and border edge */
1486 		struct rect p;
1487 		p.x0 = x - border_left < r.x0 ? r.x0 : x - border_left;
1488 		p.y0 = y - border_top < r.y0 ? r.y0 : y - border_top;
1489 		p.x1 = x + padding_width + border_right < r.x1 ?
1490 				x + padding_width + border_right : r.x1;
1491 		p.y1 = y + padding_height + border_bottom < r.y1 ?
1492 				y + padding_height + border_bottom : r.y1;
1493 		if (!box->parent) {
1494 			/* Root element, special case:
1495 			 * background covers margins too */
1496 			int m_left, m_top, m_right, m_bottom;
1497 			if (scale == 1.0) {
1498 				m_left = box->margin[LEFT];
1499 				m_top = box->margin[TOP];
1500 				m_right = box->margin[RIGHT];
1501 				m_bottom = box->margin[BOTTOM];
1502 			} else {
1503 				m_left = box->margin[LEFT] * scale;
1504 				m_top = box->margin[TOP] * scale;
1505 				m_right = box->margin[RIGHT] * scale;
1506 				m_bottom = box->margin[BOTTOM] * scale;
1507 			}
1508 			p.x0 = p.x0 - m_left < r.x0 ? r.x0 : p.x0 - m_left;
1509 			p.y0 = p.y0 - m_top < r.y0 ? r.y0 : p.y0 - m_top;
1510 			p.x1 = p.x1 + m_right < r.x1 ? p.x1 + m_right : r.x1;
1511 			p.y1 = p.y1 + m_bottom < r.y1 ? p.y1 + m_bottom : r.y1;
1512 		}
1513 		/* valid clipping rectangles only */
1514 		if ((p.x0 < p.x1) && (p.y0 < p.y1)) {
1515 			/* plot background */
1516 			if (!html_redraw_background(x, y, box, scale, &p,
1517 					&current_background_color, bg_box,
1518 					&html->len_ctx, ctx))
1519 				return false;
1520 			/* restore previous graphics window */
1521 			if (ctx->plot->clip(ctx, &r) != NSERROR_OK)
1522 				return false;
1523 		}
1524 	}
1525 
1526 	/* borders for block level content and replaced inlines */
1527 	if (box->style &&
1528 	    box->type != BOX_TEXT &&
1529 	    box->type != BOX_INLINE_END &&
1530 	    (box->type != BOX_INLINE || box->object ||
1531 	     box->flags & IFRAME || box->flags & REPLACE_DIM ||
1532 	     (box->gadget != NULL &&
1533 	      (box->gadget->type == GADGET_TEXTAREA ||
1534 	       box->gadget->type == GADGET_TEXTBOX ||
1535 	       box->gadget->type == GADGET_PASSWORD))) &&
1536 	    (border_top || border_right || border_bottom || border_left)) {
1537 		if (!html_redraw_borders(box, x_parent, y_parent,
1538 				padding_width, padding_height, &r,
1539 				scale, ctx))
1540 			return false;
1541 	}
1542 
1543 	/* backgrounds and borders for non-replaced inlines */
1544 	if (box->style && box->type == BOX_INLINE && box->inline_end &&
1545 			(html_redraw_box_has_background(box) ||
1546 			border_top || border_right ||
1547 			border_bottom || border_left)) {
1548 		/* inline backgrounds and borders span other boxes and may
1549 		 * wrap onto separate lines */
1550 		struct box *ib;
1551 		struct rect b; /* border edge rectangle */
1552 		struct rect p; /* clipped rect */
1553 		bool first = true;
1554 		int ib_x;
1555 		int ib_y = y;
1556 		int ib_p_width;
1557 		int ib_b_left, ib_b_right;
1558 
1559 		b.x0 = x - border_left;
1560 		b.x1 = x + padding_width + border_right;
1561 		b.y0 = y - border_top;
1562 		b.y1 = y + padding_height + border_bottom;
1563 
1564 		p.x0 = b.x0 < r.x0 ? r.x0 : b.x0;
1565 		p.x1 = b.x1 < r.x1 ? b.x1 : r.x1;
1566 		p.y0 = b.y0 < r.y0 ? r.y0 : b.y0;
1567 		p.y1 = b.y1 < r.y1 ? b.y1 : r.y1;
1568 		for (ib = box; ib; ib = ib->next) {
1569 			/* to get extents of rectangle(s) associated with
1570 			 * inline, cycle though all boxes in inline, skipping
1571 			 * over floats */
1572 			if (ib->type == BOX_FLOAT_LEFT ||
1573 					ib->type == BOX_FLOAT_RIGHT)
1574 				continue;
1575 			if (scale == 1.0) {
1576 				ib_x = x_parent + ib->x;
1577 				ib_y = y_parent + ib->y;
1578 				ib_p_width = ib->padding[LEFT] + ib->width +
1579 						ib->padding[RIGHT];
1580 				ib_b_left = ib->border[LEFT].width;
1581 				ib_b_right = ib->border[RIGHT].width;
1582 			} else {
1583 				ib_x = (x_parent + ib->x) * scale;
1584 				ib_y = (y_parent + ib->y) * scale;
1585 				ib_p_width = (ib->padding[LEFT] + ib->width +
1586 						ib->padding[RIGHT]) * scale;
1587 				ib_b_left = ib->border[LEFT].width * scale;
1588 				ib_b_right = ib->border[RIGHT].width * scale;
1589 			}
1590 
1591 			if ((ib->flags & NEW_LINE) && ib != box) {
1592 				/* inline element has wrapped, plot background
1593 				 * and borders */
1594 				if (!html_redraw_inline_background(
1595 						x, y, box, scale, &p, b,
1596 						first, false,
1597 						&current_background_color,
1598 						&html->len_ctx, ctx))
1599 					return false;
1600 				/* restore previous graphics window */
1601 				if (ctx->plot->clip(ctx, &r) != NSERROR_OK)
1602 					return false;
1603 				if (!html_redraw_inline_borders(box, b, &r,
1604 						scale, first, false, ctx))
1605 					return false;
1606 				/* reset coords */
1607 				b.x0 = ib_x - ib_b_left;
1608 				b.y0 = ib_y - border_top - padding_top;
1609 				b.y1 = ib_y + padding_height - padding_top +
1610 						border_bottom;
1611 
1612 				p.x0 = b.x0 < r.x0 ? r.x0 : b.x0;
1613 				p.y0 = b.y0 < r.y0 ? r.y0 : b.y0;
1614 				p.y1 = b.y1 < r.y1 ? b.y1 : r.y1;
1615 
1616 				first = false;
1617 			}
1618 
1619 			/* increase width for current box */
1620 			b.x1 = ib_x + ib_p_width + ib_b_right;
1621 			p.x1 = b.x1 < r.x1 ? b.x1 : r.x1;
1622 
1623 			if (ib == box->inline_end)
1624 				/* reached end of BOX_INLINE span */
1625 				break;
1626 		}
1627 		/* plot background and borders for last rectangle of
1628 		 * the inline */
1629 		if (!html_redraw_inline_background(x, ib_y, box, scale, &p, b,
1630 				first, true, &current_background_color,
1631 				&html->len_ctx, ctx))
1632 			return false;
1633 		/* restore previous graphics window */
1634 		if (ctx->plot->clip(ctx, &r) != NSERROR_OK)
1635 			return false;
1636 		if (!html_redraw_inline_borders(box, b, &r, scale, first, true,
1637 				ctx))
1638 			return false;
1639 
1640 	}
1641 
1642 	/* Debug outlines */
1643 	if (html_redraw_debug) {
1644 		int margin_left, margin_right;
1645 		int margin_top, margin_bottom;
1646 		if (scale == 1.0) {
1647 			/* avoid trivial fp maths */
1648 			margin_left = box->margin[LEFT];
1649 			margin_top = box->margin[TOP];
1650 			margin_right = box->margin[RIGHT];
1651 			margin_bottom = box->margin[BOTTOM];
1652 		} else {
1653 			margin_left = box->margin[LEFT] * scale;
1654 			margin_top = box->margin[TOP] * scale;
1655 			margin_right = box->margin[RIGHT] * scale;
1656 			margin_bottom = box->margin[BOTTOM] * scale;
1657 		}
1658 		/* Content edge -- blue */
1659 		rect.x0 = x + padding_left;
1660 		rect.y0 = y + padding_top;
1661 		rect.x1 = x + padding_left + width;
1662 		rect.y1 = y + padding_top + height;
1663 		if (ctx->plot->rectangle(ctx, plot_style_content_edge, &rect) != NSERROR_OK)
1664 			return false;
1665 
1666 		/* Padding edge -- red */
1667 		rect.x0 = x;
1668 		rect.y0 = y;
1669 		rect.x1 = x + padding_width;
1670 		rect.y1 = y + padding_height;
1671 		if (ctx->plot->rectangle(ctx, plot_style_padding_edge, &rect) != NSERROR_OK)
1672 			return false;
1673 
1674 		/* Margin edge -- yellow */
1675 		rect.x0 = x - border_left - margin_left;
1676 		rect.y0 = y - border_top - margin_top;
1677 		rect.x1 = x + padding_width + border_right + margin_right;
1678 		rect.y1 = y + padding_height + border_bottom + margin_bottom;
1679 		if (ctx->plot->rectangle(ctx, plot_style_margin_edge, &rect) != NSERROR_OK)
1680 			return false;
1681 	}
1682 
1683 	/* clip to the padding edge for objects, or boxes with overflow hidden
1684 	 * or scroll, unless it's the root element */
1685 	if (box->parent != NULL) {
1686 		bool need_clip = false;
1687 		if (box->object || box->flags & IFRAME ||
1688 				(overflow_x != CSS_OVERFLOW_VISIBLE &&
1689 				 overflow_y != CSS_OVERFLOW_VISIBLE)) {
1690 			r.x0 = x;
1691 			r.y0 = y;
1692 			r.x1 = x + padding_width;
1693 			r.y1 = y + padding_height;
1694 			if (r.x0 < clip->x0) r.x0 = clip->x0;
1695 			if (r.y0 < clip->y0) r.y0 = clip->y0;
1696 			if (clip->x1 < r.x1) r.x1 = clip->x1;
1697 			if (clip->y1 < r.y1) r.y1 = clip->y1;
1698 			if (r.x1 <= r.x0 || r.y1 <= r.y0) {
1699 				return (!ctx->plot->group_end ||
1700 					(ctx->plot->group_end(ctx) == NSERROR_OK));
1701 			}
1702 			need_clip = true;
1703 
1704 		} else if (overflow_x != CSS_OVERFLOW_VISIBLE) {
1705 			r.x0 = x;
1706 			r.y0 = clip->y0;
1707 			r.x1 = x + padding_width;
1708 			r.y1 = clip->y1;
1709 			if (r.x0 < clip->x0) r.x0 = clip->x0;
1710 			if (clip->x1 < r.x1) r.x1 = clip->x1;
1711 			if (r.x1 <= r.x0) {
1712 				return (!ctx->plot->group_end ||
1713 					(ctx->plot->group_end(ctx) == NSERROR_OK));
1714 			}
1715 			need_clip = true;
1716 
1717 		} else if (overflow_y != CSS_OVERFLOW_VISIBLE) {
1718 			r.x0 = clip->x0;
1719 			r.y0 = y;
1720 			r.x1 = clip->x1;
1721 			r.y1 = y + padding_height;
1722 			if (r.y0 < clip->y0) r.y0 = clip->y0;
1723 			if (clip->y1 < r.y1) r.y1 = clip->y1;
1724 			if (r.y1 <= r.y0) {
1725 				return (!ctx->plot->group_end ||
1726 					(ctx->plot->group_end(ctx) == NSERROR_OK));
1727 			}
1728 			need_clip = true;
1729 		}
1730 
1731 		if (need_clip &&
1732 		    (box->type == BOX_BLOCK ||
1733 		     box->type == BOX_INLINE_BLOCK ||
1734 		     box->type == BOX_TABLE_CELL || box->object)) {
1735 			if (ctx->plot->clip(ctx, &r) != NSERROR_OK)
1736 				return false;
1737 		}
1738 	}
1739 
1740 	/* text decoration */
1741 	if ((box->type != BOX_TEXT) &&
1742 	    box->style &&
1743 	    css_computed_text_decoration(box->style) !=	CSS_TEXT_DECORATION_NONE) {
1744 		if (!html_redraw_text_decoration(box, x_parent, y_parent,
1745 				scale, current_background_color, ctx))
1746 			return false;
1747 	}
1748 
1749 	if (box->node != NULL) {
1750 		exc = dom_html_element_get_tag_type(box->node, &tag_type);
1751 		if (exc != DOM_NO_ERR) {
1752 			tag_type = DOM_HTML_ELEMENT_TYPE__UNKNOWN;
1753 		}
1754 	} else {
1755 		tag_type = DOM_HTML_ELEMENT_TYPE__UNKNOWN;
1756 	}
1757 
1758 	if (box->object && width != 0 && height != 0) {
1759 		struct content_redraw_data obj_data;
1760 
1761 		x_scrolled = x - scrollbar_get_offset(box->scroll_x) * scale;
1762 		y_scrolled = y - scrollbar_get_offset(box->scroll_y) * scale;
1763 
1764 		obj_data.x = x_scrolled + padding_left;
1765 		obj_data.y = y_scrolled + padding_top;
1766 		obj_data.width = width;
1767 		obj_data.height = height;
1768 		obj_data.background_colour = current_background_color;
1769 		obj_data.scale = scale;
1770 		obj_data.repeat_x = false;
1771 		obj_data.repeat_y = false;
1772 
1773 		if (content_get_type(box->object) == CONTENT_HTML) {
1774 			obj_data.x /= scale;
1775 			obj_data.y /= scale;
1776 		}
1777 
1778 		if (!content_redraw(box->object, &obj_data, &r, ctx)) {
1779 			/* Show image fail */
1780 			/* Unicode (U+FFFC) 'OBJECT REPLACEMENT CHARACTER' */
1781 			const char *obj = "\xef\xbf\xbc";
1782 			int obj_width;
1783 			int obj_x = x + padding_left;
1784 			nserror res;
1785 
1786 			rect.x0 = x + padding_left;
1787 			rect.y0 = y + padding_top;
1788 			rect.x1 = x + padding_left + width - 1;
1789 			rect.y1 = y + padding_top + height - 1;
1790 			res = ctx->plot->rectangle(ctx, plot_style_broken_object, &rect);
1791 			if (res != NSERROR_OK) {
1792 				return false;
1793 			}
1794 
1795 			res = guit->layout->width(plot_fstyle_broken_object,
1796 						  obj,
1797 						  sizeof(obj) - 1,
1798 						  &obj_width);
1799 			if (res != NSERROR_OK) {
1800 				obj_x += 1;
1801 			} else {
1802 				obj_x += width / 2 - obj_width / 2;
1803 			}
1804 
1805 			if (ctx->plot->text(ctx,
1806 					    plot_fstyle_broken_object,
1807 					    obj_x, y + padding_top + (int)(height * 0.75),
1808 					    obj, sizeof(obj) - 1) != NSERROR_OK)
1809 				return false;
1810 		}
1811 	} else if (tag_type == DOM_HTML_ELEMENT_TYPE_CANVAS &&
1812 		   box->node != NULL &&
1813 		   box->flags & REPLACE_DIM) {
1814 		/* Canvas to draw */
1815 		struct bitmap *bitmap = NULL;
1816 		exc = dom_node_get_user_data(box->node,
1817 					     corestring_dom___ns_key_canvas_node_data,
1818 					     &bitmap);
1819 		if (exc != DOM_NO_ERR) {
1820 			bitmap = NULL;
1821 		}
1822 		if (bitmap != NULL &&
1823 		    ctx->plot->bitmap(ctx, bitmap, x + padding_left, y + padding_top,
1824 				      width, height, current_background_color,
1825 				      BITMAPF_NONE) != NSERROR_OK)
1826 			return false;
1827 	} else if (box->iframe) {
1828 		/* Offset is passed to browser window redraw unscaled */
1829 		browser_window_redraw(box->iframe,
1830 				x + padding_left,
1831 				y + padding_top, &r, ctx);
1832 
1833 	} else if (box->gadget && box->gadget->type == GADGET_CHECKBOX) {
1834 		if (!html_redraw_checkbox(x + padding_left, y + padding_top,
1835 				width, height, box->gadget->selected, ctx))
1836 			return false;
1837 
1838 	} else if (box->gadget && box->gadget->type == GADGET_RADIO) {
1839 		if (!html_redraw_radio(x + padding_left, y + padding_top,
1840 				width, height, box->gadget->selected, ctx))
1841 			return false;
1842 
1843 	} else if (box->gadget && box->gadget->type == GADGET_FILE) {
1844 		if (!html_redraw_file(x + padding_left, y + padding_top,
1845 				width, height, box, scale,
1846 				current_background_color, &html->len_ctx, ctx))
1847 			return false;
1848 
1849 	} else if (box->gadget &&
1850 			(box->gadget->type == GADGET_TEXTAREA ||
1851 			box->gadget->type == GADGET_PASSWORD ||
1852 			box->gadget->type == GADGET_TEXTBOX)) {
1853 		textarea_redraw(box->gadget->data.text.ta, x, y,
1854 				current_background_color, scale, &r, ctx);
1855 
1856 	} else if (box->text) {
1857 		if (!html_redraw_text_box(html, box, x, y, &r, scale,
1858 				current_background_color, ctx))
1859 			return false;
1860 
1861 	} else {
1862 		if (!html_redraw_box_children(html, box, x_parent, y_parent, &r,
1863 				scale, current_background_color, ctx))
1864 			return false;
1865 	}
1866 
1867 	if (box->type == BOX_BLOCK || box->type == BOX_INLINE_BLOCK ||
1868 			box->type == BOX_TABLE_CELL || box->type == BOX_INLINE)
1869 		if (ctx->plot->clip(ctx, clip) != NSERROR_OK)
1870 			return false;
1871 
1872 	/* list marker */
1873 	if (box->list_marker) {
1874 		if (!html_redraw_box(html, box->list_marker,
1875 				x_parent + box->x -
1876 				scrollbar_get_offset(box->scroll_x),
1877 				y_parent + box->y -
1878 				scrollbar_get_offset(box->scroll_y),
1879 				clip, scale, current_background_color, ctx))
1880 			return false;
1881 	}
1882 
1883 	/* scrollbars */
1884 	if (((box->style && box->type != BOX_BR &&
1885 	      box->type != BOX_TABLE && box->type != BOX_INLINE &&
1886 	      (box->gadget == NULL || box->gadget->type != GADGET_TEXTAREA) &&
1887 	      (overflow_x == CSS_OVERFLOW_SCROLL ||
1888 	       overflow_x == CSS_OVERFLOW_AUTO ||
1889 	       overflow_y == CSS_OVERFLOW_SCROLL ||
1890 	       overflow_y == CSS_OVERFLOW_AUTO)) ||
1891 	     (box->object && content_get_type(box->object) ==
1892 	      CONTENT_HTML)) && box->parent != NULL) {
1893 		nserror res;
1894 		bool has_x_scroll = (overflow_x == CSS_OVERFLOW_SCROLL);
1895 		bool has_y_scroll = (overflow_y == CSS_OVERFLOW_SCROLL);
1896 
1897 		has_x_scroll |= (overflow_x == CSS_OVERFLOW_AUTO) &&
1898 				box_hscrollbar_present(box);
1899 		has_y_scroll |= (overflow_y == CSS_OVERFLOW_AUTO) &&
1900 				box_vscrollbar_present(box);
1901 
1902 		res = box_handle_scrollbars((struct content *)html,
1903 					    box, has_x_scroll, has_y_scroll);
1904 		if (res != NSERROR_OK) {
1905 			NSLOG(netsurf, INFO, "%s", messages_get_errorcode(res));
1906 			return false;
1907 		}
1908 
1909 		if (box->scroll_x != NULL)
1910 			scrollbar_redraw(box->scroll_x,
1911 					x_parent + box->x,
1912 					y_parent + box->y + box->padding[TOP] +
1913 					box->height + box->padding[BOTTOM] -
1914 					SCROLLBAR_WIDTH, clip, scale, ctx);
1915 		if (box->scroll_y != NULL)
1916 			scrollbar_redraw(box->scroll_y,
1917 					x_parent + box->x + box->padding[LEFT] +
1918 					box->width + box->padding[RIGHT] -
1919 					SCROLLBAR_WIDTH,
1920 					y_parent + box->y, clip, scale, ctx);
1921 	}
1922 
1923 	if (box->type == BOX_BLOCK || box->type == BOX_INLINE_BLOCK ||
1924 	    box->type == BOX_TABLE_CELL || box->type == BOX_INLINE) {
1925 		if (ctx->plot->clip(ctx, clip) != NSERROR_OK)
1926 			return false;
1927 	}
1928 
1929 	return ((!plot->group_end) || (ctx->plot->group_end(ctx) == NSERROR_OK));
1930 }
1931 
1932 /**
1933  * Draw a CONTENT_HTML using the current set of plotters (plot).
1934  *
1935  * \param  c	 content of type CONTENT_HTML
1936  * \param  data	 redraw data for this content redraw
1937  * \param  clip	 current clip region
1938  * \param  ctx	 current redraw context
1939  * \return true if successful, false otherwise
1940  *
1941  * x, y, clip_[xy][01] are in target coordinates.
1942  */
1943 
html_redraw(struct content * c,struct content_redraw_data * data,const struct rect * clip,const struct redraw_context * ctx)1944 bool html_redraw(struct content *c, struct content_redraw_data *data,
1945 		const struct rect *clip, const struct redraw_context *ctx)
1946 {
1947 	html_content *html = (html_content *) c;
1948 	struct box *box;
1949 	bool result = true;
1950 	bool select, select_only;
1951 	plot_style_t pstyle_fill_bg = {
1952 		.fill_type = PLOT_OP_TYPE_SOLID,
1953 		.fill_colour = data->background_colour,
1954 	};
1955 
1956 	box = html->layout;
1957 	assert(box);
1958 
1959 	/* The select menu needs special treating because, when opened, it
1960 	 * reaches beyond its layout box.
1961 	 */
1962 	select = false;
1963 	select_only = false;
1964 	if (ctx->interactive && html->visible_select_menu != NULL) {
1965 		struct form_control *control = html->visible_select_menu;
1966 		select = true;
1967 		/* check if the redraw rectangle is completely inside of the
1968 		   select menu */
1969 		select_only = form_clip_inside_select_menu(control,
1970 				data->scale, clip);
1971 	}
1972 
1973 	if (!select_only) {
1974 		/* clear to background colour */
1975 		result = (ctx->plot->clip(ctx, clip) == NSERROR_OK);
1976 
1977 		if (html->background_colour != NS_TRANSPARENT)
1978 			pstyle_fill_bg.fill_colour = html->background_colour;
1979 
1980 		result &= (ctx->plot->rectangle(ctx, &pstyle_fill_bg, clip) == NSERROR_OK);
1981 
1982 		result &= html_redraw_box(html, box, data->x, data->y, clip,
1983 				data->scale, pstyle_fill_bg.fill_colour, ctx);
1984 	}
1985 
1986 	if (select) {
1987 		int menu_x, menu_y;
1988 		box = html->visible_select_menu->box;
1989 		box_coords(box, &menu_x, &menu_y);
1990 
1991 		menu_x -= box->border[LEFT].width;
1992 		menu_y += box->height + box->border[BOTTOM].width +
1993 				box->padding[BOTTOM] + box->padding[TOP];
1994 		result &= form_redraw_select_menu(html->visible_select_menu,
1995 				data->x + menu_x, data->y + menu_y,
1996 				data->scale, clip, ctx);
1997 	}
1998 
1999 	return result;
2000 
2001 }
2002