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 <body>. 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 ¤t_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 ¤t_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, ¤t_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