NetSurf
box_construct.c
Go to the documentation of this file.
1/*
2 * Copyright 2005 James Bursa <bursa@users.sourceforge.net>
3 * Copyright 2003 Phil Mellor <monkeyson@users.sourceforge.net>
4 * Copyright 2005 John M Bell <jmb202@ecs.soton.ac.uk>
5 * Copyright 2006 Richard Wilson <info@tinct.net>
6 * Copyright 2008 Michael Drake <tlsa@netsurf-browser.org>
7 *
8 * This file is part of NetSurf, http://www.netsurf-browser.org/
9 *
10 * NetSurf is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; version 2 of the License.
13 *
14 * NetSurf is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program. If not, see <http://www.gnu.org/licenses/>.
21 */
22
23/**
24 * \file
25 * Implementation of conversion from DOM tree to box tree.
26 */
27
28#include <string.h>
29#include <dom/dom.h>
30
31#include "utils/errors.h"
32#include "utils/nsoption.h"
33#include "utils/corestrings.h"
34#include "utils/talloc.h"
35#include "utils/string.h"
36#include "utils/ascii.h"
37#include "utils/nsurl.h"
38#include "utils/utils.h"
39#include "netsurf/misc.h"
40#include "css/select.h"
42
43#include "html/private.h"
44#include "html/object.h"
45#include "html/box.h"
46#include "html/box_manipulate.h"
47#include "html/box_construct.h"
48#include "html/box_special.h"
49#include "html/box_normalise.h"
50#include "html/form_internal.h"
51
52/**
53 * Context for box tree construction
54 */
56 html_content *content; /**< Content we're constructing for */
57
58 dom_node *n; /**< Current node to process */
59
60 struct box *root_box; /**< Root box in the tree */
61
62 box_construct_complete_cb cb; /**< Callback to invoke on completion */
63
64 int *bctx; /**< talloc context */
65};
66
67/**
68 * Transient properties for construction of current node
69 */
71 /** Style from which to inherit, or NULL if none */
72 const css_computed_style *parent_style;
73 /** Current link target, or NULL if none */
74 struct nsurl *href;
75 /** Current frame target, or NULL if none */
76 const char *target;
77 /** Current title attribute, or NULL if none */
78 const char *title;
79 /** Identity of the current block-level container */
81 /** Current container for inlines, or NULL if none
82 * \note If non-NULL, will be the last child of containing_block */
84 /** Whether the current node is the root of the DOM tree */
86};
87
89
90/* mapping from CSS display to box type
91 * this table must be in sync with libcss' css_display enum */
92static const box_type box_map[] = {
93 BOX_BLOCK, /* CSS_DISPLAY_INHERIT */
94 BOX_INLINE, /* CSS_DISPLAY_INLINE */
95 BOX_BLOCK, /* CSS_DISPLAY_BLOCK */
96 BOX_BLOCK, /* CSS_DISPLAY_LIST_ITEM */
97 BOX_INLINE, /* CSS_DISPLAY_RUN_IN */
98 BOX_INLINE_BLOCK, /* CSS_DISPLAY_INLINE_BLOCK */
99 BOX_TABLE, /* CSS_DISPLAY_TABLE */
100 BOX_TABLE, /* CSS_DISPLAY_INLINE_TABLE */
101 BOX_TABLE_ROW_GROUP, /* CSS_DISPLAY_TABLE_ROW_GROUP */
102 BOX_TABLE_ROW_GROUP, /* CSS_DISPLAY_TABLE_HEADER_GROUP */
103 BOX_TABLE_ROW_GROUP, /* CSS_DISPLAY_TABLE_FOOTER_GROUP */
104 BOX_TABLE_ROW, /* CSS_DISPLAY_TABLE_ROW */
105 BOX_NONE, /* CSS_DISPLAY_TABLE_COLUMN_GROUP */
106 BOX_NONE, /* CSS_DISPLAY_TABLE_COLUMN */
107 BOX_TABLE_CELL, /* CSS_DISPLAY_TABLE_CELL */
108 BOX_INLINE, /* CSS_DISPLAY_TABLE_CAPTION */
109 BOX_NONE, /* CSS_DISPLAY_NONE */
110 BOX_FLEX, /* CSS_DISPLAY_FLEX */
111 BOX_INLINE_FLEX, /* CSS_DISPLAY_INLINE_FLEX */
112 BOX_BLOCK, /* CSS_DISPLAY_GRID */
113 BOX_INLINE_BLOCK, /* CSS_DISPLAY_INLINE_GRID */
114};
115
116
117/**
118 * determine if a box is the root node
119 *
120 * \param n node to check
121 * \return true if node is root else false.
122 */
123static inline bool box_is_root(dom_node *n)
124{
125 dom_node *parent;
126 dom_node_type type;
127 dom_exception err;
128
129 err = dom_node_get_parent_node(n, &parent);
130 if (err != DOM_NO_ERR)
131 return false;
132
133 if (parent != NULL) {
134 err = dom_node_get_node_type(parent, &type);
135
136 dom_node_unref(parent);
137
138 if (err != DOM_NO_ERR)
139 return false;
140
141 if (type != DOM_DOCUMENT_NODE)
142 return false;
143 }
144
145 return true;
146}
147
148/**
149 * Extract transient construction properties
150 *
151 * \param n Current DOM node to convert
152 * \param props Property object to populate
153 */
154static void
156{
157 memset(props, 0, sizeof(*props));
158
159 props->node_is_root = box_is_root(n);
160
161 /* Extract properties from containing DOM node */
162 if (props->node_is_root == false) {
163 dom_node *current_node = n;
164 dom_node *parent_node = NULL;
165 struct box *parent_box;
166 dom_exception err;
167
168 /* Find ancestor node containing parent box */
169 while (true) {
170 err = dom_node_get_parent_node(current_node,
171 &parent_node);
172 if (err != DOM_NO_ERR || parent_node == NULL)
173 break;
174
175 parent_box = box_for_node(parent_node);
176
177 if (parent_box != NULL) {
178 props->parent_style = parent_box->style;
179 props->href = parent_box->href;
180 props->target = parent_box->target;
181 props->title = parent_box->title;
182
183 dom_node_unref(parent_node);
184 break;
185 } else {
186 if (current_node != n)
187 dom_node_unref(current_node);
188 current_node = parent_node;
189 parent_node = NULL;
190 }
191 }
192
193 /* Find containing block (may be parent) */
194 while (true) {
195 struct box *b;
196
197 err = dom_node_get_parent_node(current_node,
198 &parent_node);
199 if (err != DOM_NO_ERR || parent_node == NULL) {
200 if (current_node != n)
201 dom_node_unref(current_node);
202 break;
203 }
204
205 if (current_node != n)
206 dom_node_unref(current_node);
207
209
210 /* Children of nodes that created an inline box
211 * will generate boxes which are attached as
212 * _siblings_ of the box generated for their
213 * parent node. Note, however, that we'll still
214 * use the parent node's styling as the parent
215 * style, above. */
216 if (b != NULL && b->type != BOX_INLINE &&
217 b->type != BOX_BR) {
218 props->containing_block = b;
219
220 dom_node_unref(parent_node);
221 break;
222 } else {
223 current_node = parent_node;
224 parent_node = NULL;
225 }
226 }
227 }
228
229 /* Compute current inline container, if any */
230 if (props->containing_block != NULL &&
231 props->containing_block->last != NULL &&
232 props->containing_block->last->type ==
234 props->inline_container = props->containing_block->last;
235}
236
237
238/**
239 * Get the style for an element.
240 *
241 * \param c content of type CONTENT_HTML that is being processed
242 * \param parent_style style at this point in xml tree, or NULL for root
243 * \param root_style root node's style, or NULL for root
244 * \param n node in xml tree
245 * \return the new style, or NULL on memory exhaustion
246 */
247static css_select_results *
249 const css_computed_style *parent_style,
250 const css_computed_style *root_style,
251 dom_node *n)
252{
253 dom_string *s = NULL;
254 css_stylesheet *inline_style = NULL;
255 css_select_results *styles;
257
258 /* Firstly, construct inline stylesheet, if any */
259 if (nsoption_bool(author_level_css)) {
260 dom_exception err;
261 err = dom_element_get_attribute(n, corestring_dom_style, &s);
262 if (err != DOM_NO_ERR) {
263 return NULL;
264 }
265 }
266
267 if (s != NULL) {
268 inline_style = nscss_create_inline_style(
269 (const uint8_t *) dom_string_data(s),
270 dom_string_byte_length(s),
271 c->encoding,
273 c->quirks != DOM_DOCUMENT_QUIRKS_MODE_NONE);
274
275 dom_string_unref(s);
276
277 if (inline_style == NULL)
278 return NULL;
279 }
280
281 /* Populate selection context */
282 ctx.ctx = c->select_ctx;
283 ctx.quirks = (c->quirks == DOM_DOCUMENT_QUIRKS_MODE_FULL);
284 ctx.base_url = c->base_url;
285 ctx.universal = c->universal;
286 ctx.root_style = root_style;
287 ctx.parent_style = parent_style;
288
289 /* Select style for element */
290 styles = nscss_get_style(&ctx, n, &c->media, &c->unit_len_ctx,
291 inline_style);
292
293 /* No longer need inline style */
294 if (inline_style != NULL)
295 css_stylesheet_destroy(inline_style);
296
297 return styles;
298}
299
300
301/**
302 * Construct the box required for a generated element.
303 *
304 * \param n XML node of type XML_ELEMENT_NODE
305 * \param content Content of type CONTENT_HTML that is being processed
306 * \param box Box which may have generated content
307 * \param style Complete computed style for pseudo element, or NULL
308 *
309 * \todo This is currently incomplete. It just does enough to support
310 * the clearfix hack. (http://www.positioniseverything.net/easyclearing.html )
311 */
312static void
315 struct box *box,
316 const css_computed_style *style)
317{
318 struct box *gen = NULL;
319 enum css_display_e computed_display;
320 const css_computed_content_item *c_item;
321
322 /* Nothing to generate if the parent box is not a block */
323 if (box->type != BOX_BLOCK)
324 return;
325
326 /* To determine if an element has a pseudo element, we select
327 * for it and test to see if the returned style's content
328 * property is set to normal. */
329 if (style == NULL ||
330 css_computed_content(style, &c_item) ==
331 CSS_CONTENT_NORMAL) {
332 /* No pseudo element */
333 return;
334 }
335
336 /* create box for this element */
337 computed_display = ns_computed_display(style, box_is_root(n));
338 if (computed_display == CSS_DISPLAY_BLOCK ||
339 computed_display == CSS_DISPLAY_TABLE) {
340 /* currently only support block level boxes */
341
342 /** \todo Not wise to drop const from the computed style */
343 gen = box_create(NULL, (css_computed_style *) style,
344 false, NULL, NULL, NULL, NULL, content->bctx);
345 if (gen == NULL) {
346 return;
347 }
348
349 /* set box type from computed display */
351 style, box_is_root(n))];
352
353 box_add_child(box, gen);
354 }
355}
356
357
358/**
359 * Construct a list marker box
360 *
361 * \param box Box to attach marker to
362 * \param title Current title attribute
363 * \param ctx Box construction context
364 * \param parent Current block-level container
365 * \return true on success, false on memory exhaustion
366 */
367static bool
369 const char *title,
370 struct box_construct_ctx *ctx,
371 struct box *parent)
372{
373 lwc_string *image_uri;
374 struct box *marker;
375 enum css_list_style_type_e list_style_type;
376
377 marker = box_create(NULL, box->style, false, NULL, NULL, title,
378 NULL, ctx->bctx);
379 if (marker == false)
380 return false;
381
382 marker->type = BOX_BLOCK;
383
384 list_style_type = css_computed_list_style_type(box->style);
385
386 /** \todo marker content (list-style-type) */
387 switch (list_style_type) {
388 case CSS_LIST_STYLE_TYPE_DISC:
389 /* 2022 BULLET */
390 marker->text = (char *) "\342\200\242";
391 marker->length = 3;
392 break;
393
394 case CSS_LIST_STYLE_TYPE_CIRCLE:
395 /* 25CB WHITE CIRCLE */
396 marker->text = (char *) "\342\227\213";
397 marker->length = 3;
398 break;
399
400 case CSS_LIST_STYLE_TYPE_SQUARE:
401 /* 25AA BLACK SMALL SQUARE */
402 marker->text = (char *) "\342\226\252";
403 marker->length = 3;
404 break;
405
406 default:
407 /* Numerical list counters get handled in layout. */
408 /* Fall through. */
409 case CSS_LIST_STYLE_TYPE_NONE:
410 marker->text = NULL;
411 marker->length = 0;
412 break;
413 }
414
415 if (css_computed_list_style_image(box->style, &image_uri) == CSS_LIST_STYLE_IMAGE_URI &&
416 (image_uri != NULL) &&
417 (nsoption_bool(foreground_images) == true)) {
418 nsurl *url;
419 nserror error;
420
421 /* TODO: we get a url out of libcss as a lwc string, but
422 * earlier we already had it as a nsurl after we
423 * nsurl_joined it. Can this be improved?
424 * For now, just making another nsurl. */
425 error = nsurl_create(lwc_string_data(image_uri), &url);
426 if (error != NSERROR_OK)
427 return false;
428
429 if (html_fetch_object(ctx->content,
430 url,
431 marker,
433 false) == false) {
434 nsurl_unref(url);
435 return false;
436 }
437 nsurl_unref(url);
438 }
439
440 box->list_marker = marker;
441 marker->parent = box;
442
443 return true;
444}
445
446static inline bool box__style_is_float(const struct box *box)
447{
448 return css_computed_float(box->style) == CSS_FLOAT_LEFT ||
449 css_computed_float(box->style) == CSS_FLOAT_RIGHT;
450}
451
452static inline bool box__is_flex(const struct box *box)
453{
454 return box->type == BOX_FLEX || box->type == BOX_INLINE_FLEX;
455}
456
458 const struct box_construct_props *props)
459{
460 return props->containing_block != NULL &&
462}
463
464/**
465 * Construct the box tree for an XML element.
466 *
467 * \param ctx Tree construction context
468 * \param convert_children Whether to convert children
469 * \return true on success, false on memory exhaustion
470 */
471static bool
472box_construct_element(struct box_construct_ctx *ctx, bool *convert_children)
473{
474 dom_string *title0, *s;
475 lwc_string *id = NULL;
476 enum css_display_e css_display;
477 struct box *box = NULL, *old_box;
478 css_select_results *styles = NULL;
479 lwc_string *bgimage_uri;
480 dom_exception err;
481 struct box_construct_props props;
482 const css_computed_style *root_style = NULL;
483
484 assert(ctx->n != NULL);
485
486 box_extract_properties(ctx->n, &props);
487
488 if (props.containing_block != NULL) {
489 /* In case the containing block is a pre block, we clear
490 * the PRE_STRIP flag since it is not used if we follow
491 * the pre with a tag */
492 props.containing_block->flags &= ~PRE_STRIP;
493 }
494
495 if (props.node_is_root == false) {
496 root_style = ctx->root_box->style;
497 }
498
499 styles = box_get_style(ctx->content, props.parent_style, root_style,
500 ctx->n);
501 if (styles == NULL)
502 return false;
503
504 /* Extract title attribute, if present */
505 err = dom_element_get_attribute(ctx->n, corestring_dom_title, &title0);
506 if (err != DOM_NO_ERR)
507 return false;
508
509 if (title0 != NULL) {
510 char *t = squash_whitespace(dom_string_data(title0));
511
512 dom_string_unref(title0);
513
514 if (t == NULL)
515 return false;
516
517 props.title = talloc_strdup(ctx->bctx, t);
518
519 free(t);
520
521 if (props.title == NULL)
522 return false;
523 }
524
525 /* Extract id attribute, if present */
526 err = dom_element_get_attribute(ctx->n, corestring_dom_id, &s);
527 if (err != DOM_NO_ERR)
528 return false;
529
530 if (s != NULL) {
531 err = dom_string_intern(s, &id);
532 if (err != DOM_NO_ERR)
533 id = NULL;
534
535 dom_string_unref(s);
536 }
537
538 box = box_create(styles, styles->styles[CSS_PSEUDO_ELEMENT_NONE], false,
539 props.href, props.target, props.title, id,
540 ctx->bctx);
541 if (box == NULL)
542 return false;
543
544 /* If this is the root box, add it to the context */
545 if (props.node_is_root)
546 ctx->root_box = box;
547
548 /* Deal with colspan/rowspan */
549 err = dom_element_get_attribute(ctx->n, corestring_dom_colspan, &s);
550 if (err != DOM_NO_ERR)
551 return false;
552
553 if (s != NULL) {
554 const char *val = dom_string_data(s);
555
556 /* Convert to a number, clamping to [1,1000] according to 4.9.11 */
557 if ('0' <= val[0] && val[0] <= '9')
558 box->columns = clamp(strtol(val, NULL, 10), 1, 1000);
559
560 dom_string_unref(s);
561 }
562
563 err = dom_element_get_attribute(ctx->n, corestring_dom_rowspan, &s);
564 if (err != DOM_NO_ERR)
565 return false;
566
567 if (s != NULL) {
568 const char *val = dom_string_data(s);
569
570 /* Convert to a number, clamping to [0,65534] according to 4.9.11 */
571 if ('0' <= val[0] && val[0] <= '9')
572 box->rows = clamp(strtol(val, NULL, 10), 0, 65534);
573
574 dom_string_unref(s);
575 }
576
577 css_display = ns_computed_display_static(box->style);
578
579 /* Set box type from computed display */
580 if ((css_computed_position(box->style) == CSS_POSITION_ABSOLUTE ||
581 css_computed_position(box->style) == CSS_POSITION_FIXED) &&
582 (css_display == CSS_DISPLAY_INLINE ||
583 css_display == CSS_DISPLAY_INLINE_BLOCK ||
584 css_display == CSS_DISPLAY_INLINE_TABLE ||
585 css_display == CSS_DISPLAY_INLINE_FLEX)) {
586 /* Special case for absolute positioning: make absolute inlines
587 * into inline block so that the boxes are constructed in an
588 * inline container as if they were not absolutely positioned.
589 * Layout expects and handles this. */
590 box->type = box_map[CSS_DISPLAY_INLINE_BLOCK];
591 } else if (props.node_is_root) {
592 /* Special case for root element: force it to BLOCK, or the
593 * rest of the layout will break. */
594 box->type = BOX_BLOCK;
595 } else {
596 /* Normal mapping */
598 props.node_is_root)];
599
600 if (props.containing_block->type == BOX_FLEX ||
602 /* Blockification */
603 switch (box->type) {
604 case BOX_INLINE_FLEX:
605 box->type = BOX_FLEX;
606 break;
607 case BOX_INLINE_BLOCK:
608 box->type = BOX_BLOCK;
609 break;
610 default:
611 break;
612 }
613 }
614 }
615
617 ctx->content,
618 box,
619 convert_children) == false) {
620 return false;
621 }
622
623 /* Handle the :before pseudo element */
624 if (!(box->flags & IS_REPLACED)) {
626 box->styles->styles[CSS_PSEUDO_ELEMENT_BEFORE]);
627 }
628
630 props.node_is_root) == CSS_DISPLAY_NONE &&
631 props.node_is_root == false)) {
632 css_select_results_destroy(styles);
633 box->styles = NULL;
634 box->style = NULL;
635
636 /* Invalidate associated gadget, if any */
637 if (box->gadget != NULL) {
638 box->gadget->box = NULL;
639 box->gadget = NULL;
640 }
641
642 /* Can't do this, because the lifetimes of boxes and gadgets
643 * are inextricably linked. Fortunately, talloc will save us
644 * (for now) */
645 /* box_free_box(box); */
646
647 *convert_children = false;
648
649 return true;
650 }
651
652 /* Attach DOM node to box */
653 err = dom_node_set_user_data(ctx->n,
654 corestring_dom___ns_key_box_node_data, box, NULL,
655 (void *) &old_box);
656 if (err != DOM_NO_ERR)
657 return false;
658
659 /* Attach box to DOM node */
660 box->node = dom_node_ref(ctx->n);
661
662 if (props.inline_container == NULL &&
663 (box->type == BOX_INLINE ||
664 box->type == BOX_BR ||
669 props.node_is_root == false) {
670 /* Found an inline child of a block without a current container
671 * (i.e. this box is the first child of its parent, or was
672 * preceded by block-level siblings) */
673 assert(props.containing_block != NULL &&
674 "Box must have containing block.");
675
676 props.inline_container = box_create(NULL, NULL, false, NULL,
677 NULL, NULL, NULL, ctx->bctx);
678 if (props.inline_container == NULL)
679 return false;
680
682
684 }
685
686 /* Kick off fetch for any background image */
687 if (css_computed_background_image(box->style, &bgimage_uri) ==
688 CSS_BACKGROUND_IMAGE_IMAGE && bgimage_uri != NULL &&
689 nsoption_bool(background_images) == true) {
690 nsurl *url;
691 nserror error;
692
693 /* TODO: we get a url out of libcss as a lwc string, but
694 * earlier we already had it as a nsurl after we
695 * nsurl_joined it. Can this be improved?
696 * For now, just making another nsurl. */
697 error = nsurl_create(lwc_string_data(bgimage_uri), &url);
698 if (error == NSERROR_OK) {
699 /* Fetch image if we got a valid URL */
700 if (html_fetch_object(ctx->content,
701 url,
702 box,
704 true) == false) {
705 nsurl_unref(url);
706 return false;
707 }
708 nsurl_unref(url);
709 }
710 }
711
712 if (*convert_children)
714
715 if (box->type == BOX_INLINE || box->type == BOX_BR ||
718 /* Inline container must exist, as we'll have
719 * created it above if it didn't */
720 assert(props.inline_container != NULL);
721
723 } else {
725 CSS_DISPLAY_LIST_ITEM) {
726 /* List item: compute marker */
727 if (box_construct_marker(box, props.title, ctx,
728 props.containing_block) == false)
729 return false;
730 }
731
732 if (props.node_is_root == false &&
733 box__containing_block_is_flex(&props) == false &&
734 (css_computed_float(box->style) ==
735 CSS_FLOAT_LEFT ||
736 css_computed_float(box->style) ==
737 CSS_FLOAT_RIGHT)) {
738 /* Float: insert a float between the parent and box. */
739 struct box *flt = box_create(NULL, NULL, false,
740 props.href, props.target, props.title,
741 NULL, ctx->bctx);
742 if (flt == NULL)
743 return false;
744
745 if (css_computed_float(box->style) == CSS_FLOAT_LEFT)
746 flt->type = BOX_FLOAT_LEFT;
747 else
748 flt->type = BOX_FLOAT_RIGHT;
749
751 box_add_child(flt, box);
752 } else {
753 /* Non-floated block-level box: add to containing block
754 * if there is one. If we're the root box, then there
755 * won't be. */
756 if (props.containing_block != NULL)
758 }
759 }
760
761 return true;
762}
763
764
765/**
766 * Complete construction of the box tree for an element.
767 *
768 * \param n DOM node to construct for
769 * \param content Containing document
770 *
771 * This will be called after all children of an element have been processed
772 */
774{
775 struct box_construct_props props;
776 struct box *box = box_for_node(n);
777
778 assert(box != NULL);
779
780 box_extract_properties(n, &props);
781
782 if (box->type == BOX_INLINE || box->type == BOX_BR) {
783 /* Insert INLINE_END into containing block */
784 struct box *inline_end;
785 bool has_children;
786 dom_exception err;
787
788 err = dom_node_has_child_nodes(n, &has_children);
789 if (err != DOM_NO_ERR)
790 return;
791
792 if (has_children == false ||
793 (box->flags & CONVERT_CHILDREN) == 0) {
794 /* No children, or didn't want children converted */
795 return;
796 }
797
798 if (props.inline_container == NULL) {
799 /* Create inline container if we don't have one */
800 props.inline_container = box_create(NULL, NULL, false,
801 NULL, NULL, NULL, NULL, content->bctx);
802 if (props.inline_container == NULL)
803 return;
804
806
808 props.inline_container);
809 }
810
811 inline_end = box_create(NULL, box->style, false,
812 box->href, box->target, box->title,
813 box->id == NULL ? NULL :
814 lwc_string_ref(box->id), content->bctx);
815 if (inline_end != NULL) {
817
818 assert(props.inline_container != NULL);
819
821
824 }
825 } else if (!(box->flags & IS_REPLACED)) {
826 /* Handle the :after pseudo element */
828 box->styles->styles[CSS_PSEUDO_ELEMENT_AFTER]);
829 }
830}
831
832
833/**
834 * Find the next node in the DOM tree, completing element construction
835 * where appropriate.
836 *
837 * \param n Current node
838 * \param content Containing content
839 * \param convert_children Whether to consider children of \a n
840 * \return Next node to process, or NULL if complete
841 *
842 * \note \a n will be unreferenced
843 */
844static dom_node *
845next_node(dom_node *n, html_content *content, bool convert_children)
846{
847 dom_node *next = NULL;
848 bool has_children;
849 dom_exception err;
850
851 err = dom_node_has_child_nodes(n, &has_children);
852 if (err != DOM_NO_ERR) {
853 dom_node_unref(n);
854 return NULL;
855 }
856
857 if (convert_children && has_children) {
858 err = dom_node_get_first_child(n, &next);
859 if (err != DOM_NO_ERR) {
860 dom_node_unref(n);
861 return NULL;
862 }
863 dom_node_unref(n);
864 } else {
865 err = dom_node_get_next_sibling(n, &next);
866 if (err != DOM_NO_ERR) {
867 dom_node_unref(n);
868 return NULL;
869 }
870
871 if (next != NULL) {
872 if (box_for_node(n) != NULL)
874 dom_node_unref(n);
875 } else {
876 if (box_for_node(n) != NULL)
878
879 while (box_is_root(n) == false) {
880 dom_node *parent = NULL;
881 dom_node *parent_next = NULL;
882
883 err = dom_node_get_parent_node(n, &parent);
884 if (err != DOM_NO_ERR) {
885 dom_node_unref(n);
886 return NULL;
887 }
888
889 assert(parent != NULL);
890
891 err = dom_node_get_next_sibling(parent,
892 &parent_next);
893 if (err != DOM_NO_ERR) {
894 dom_node_unref(parent);
895 dom_node_unref(n);
896 return NULL;
897 }
898
899 if (parent_next != NULL) {
900 dom_node_unref(parent_next);
901 dom_node_unref(parent);
902 break;
903 }
904
905 dom_node_unref(n);
906 n = parent;
907 parent = NULL;
908
909 if (box_for_node(n) != NULL) {
911 n, content);
912 }
913 }
914
915 if (box_is_root(n) == false) {
916 dom_node *parent = NULL;
917
918 err = dom_node_get_parent_node(n, &parent);
919 if (err != DOM_NO_ERR) {
920 dom_node_unref(n);
921 return NULL;
922 }
923
924 assert(parent != NULL);
925
926 err = dom_node_get_next_sibling(parent, &next);
927 if (err != DOM_NO_ERR) {
928 dom_node_unref(parent);
929 dom_node_unref(n);
930 return NULL;
931 }
932
933 if (box_for_node(parent) != NULL) {
935 content);
936 }
937
938 dom_node_unref(parent);
939 }
940
941 dom_node_unref(n);
942 }
943 }
944
945 return next;
946}
947
948
949/**
950 * Apply the CSS text-transform property to given text for its ASCII chars.
951 *
952 * \param s string to transform
953 * \param len length of s
954 * \param tt transform type
955 */
956static void
957box_text_transform(char *s, unsigned int len, enum css_text_transform_e tt)
958{
959 unsigned int i;
960 if (len == 0)
961 return;
962 switch (tt) {
963 case CSS_TEXT_TRANSFORM_UPPERCASE:
964 for (i = 0; i < len; ++i)
965 if ((unsigned char) s[i] < 0x80)
966 s[i] = ascii_to_upper(s[i]);
967 break;
968 case CSS_TEXT_TRANSFORM_LOWERCASE:
969 for (i = 0; i < len; ++i)
970 if ((unsigned char) s[i] < 0x80)
971 s[i] = ascii_to_lower(s[i]);
972 break;
973 case CSS_TEXT_TRANSFORM_CAPITALIZE:
974 if ((unsigned char) s[0] < 0x80)
975 s[0] = ascii_to_upper(s[0]);
976 for (i = 1; i < len; ++i)
977 if ((unsigned char) s[i] < 0x80 &&
978 ascii_is_space(s[i - 1]))
979 s[i] = ascii_to_upper(s[i]);
980 break;
981 default:
982 break;
983 }
984}
985
986
987/**
988 * Construct the box tree for an XML text node.
989 *
990 * \param ctx Tree construction context
991 * \return true on success, false on memory exhaustion
992 */
993static bool box_construct_text(struct box_construct_ctx *ctx)
994{
995 struct box_construct_props props;
996 struct box *box = NULL;
997 dom_string *content;
998 dom_exception err;
999
1000 assert(ctx->n != NULL);
1001
1002 box_extract_properties(ctx->n, &props);
1003
1004 assert(props.containing_block != NULL);
1005
1006 err = dom_characterdata_get_data(ctx->n, &content);
1007 if (err != DOM_NO_ERR || content == NULL)
1008 return false;
1009
1010 if (css_computed_white_space(props.parent_style) ==
1011 CSS_WHITE_SPACE_NORMAL ||
1012 css_computed_white_space(props.parent_style) ==
1013 CSS_WHITE_SPACE_NOWRAP) {
1014 char *text;
1015
1016 text = squash_whitespace(dom_string_data(content));
1017
1018 dom_string_unref(content);
1019
1020 if (text == NULL)
1021 return false;
1022
1023 /* if the text is just a space, combine it with the preceding
1024 * text node, if any */
1025 if (text[0] == ' ' && text[1] == 0) {
1026 if (props.inline_container != NULL) {
1027 assert(props.inline_container->last != NULL);
1028
1029 props.inline_container->last->space =
1031 }
1032
1033 free(text);
1034
1035 return true;
1036 }
1037
1038 if (props.inline_container == NULL) {
1039 /* Child of a block without a current container
1040 * (i.e. this box is the first child of its parent, or
1041 * was preceded by block-level siblings) */
1042 props.inline_container = box_create(NULL, NULL, false,
1043 NULL, NULL, NULL, NULL, ctx->bctx);
1044 if (props.inline_container == NULL) {
1045 free(text);
1046 return false;
1047 }
1048
1050
1052 props.inline_container);
1053 }
1054
1055 /** \todo Dropping const here is not clever */
1056 box = box_create(NULL,
1057 (css_computed_style *) props.parent_style,
1058 false, props.href, props.target, props.title,
1059 NULL, ctx->bctx);
1060 if (box == NULL) {
1061 free(text);
1062 return false;
1063 }
1064
1065 box->type = BOX_TEXT;
1066
1067 box->text = talloc_strdup(ctx->bctx, text);
1068 free(text);
1069 if (box->text == NULL)
1070 return false;
1071
1072 box->length = strlen(box->text);
1073
1074 /* strip ending space char off */
1075 if (box->length > 1 && box->text[box->length - 1] == ' ') {
1077 box->length--;
1078 }
1079
1080 if (css_computed_text_transform(props.parent_style) !=
1081 CSS_TEXT_TRANSFORM_NONE)
1083 css_computed_text_transform(
1084 props.parent_style));
1085
1087
1088 if (box->text[0] == ' ') {
1089 box->length--;
1090
1091 memmove(box->text, &box->text[1], box->length);
1092
1093 if (box->prev != NULL)
1095 }
1096 } else {
1097 /* white-space: pre */
1098 char *text;
1099 size_t text_len = dom_string_byte_length(content);
1100 size_t i;
1101 char *current;
1102 enum css_white_space_e white_space =
1103 css_computed_white_space(props.parent_style);
1104
1105 /* note: pre-wrap/pre-line are unimplemented */
1106 assert(white_space == CSS_WHITE_SPACE_PRE ||
1107 white_space == CSS_WHITE_SPACE_PRE_LINE ||
1108 white_space == CSS_WHITE_SPACE_PRE_WRAP);
1109
1110 text = malloc(text_len + 1);
1111 dom_string_unref(content);
1112
1113 if (text == NULL)
1114 return false;
1115
1116 memcpy(text, dom_string_data(content), text_len);
1117 text[text_len] = '\0';
1118
1119 /* TODO: Handle tabs properly */
1120 for (i = 0; i < text_len; i++)
1121 if (text[i] == '\t')
1122 text[i] = ' ';
1123
1124 if (css_computed_text_transform(props.parent_style) !=
1125 CSS_TEXT_TRANSFORM_NONE)
1126 box_text_transform(text, strlen(text),
1127 css_computed_text_transform(
1128 props.parent_style));
1129
1130 current = text;
1131
1132 /* swallow a single leading new line */
1133 if (props.containing_block->flags & PRE_STRIP) {
1134 switch (*current) {
1135 case '\n':
1136 current++;
1137 break;
1138 case '\r':
1139 current++;
1140 if (*current == '\n')
1141 current++;
1142 break;
1143 }
1144 props.containing_block->flags &= ~PRE_STRIP;
1145 }
1146
1147 do {
1148 size_t len = strcspn(current, "\r\n");
1149
1150 char old = current[len];
1151
1152 current[len] = 0;
1153
1154 if (props.inline_container == NULL) {
1155 /* Child of a block without a current container
1156 * (i.e. this box is the first child of its
1157 * parent, or was preceded by block-level
1158 * siblings) */
1159 props.inline_container = box_create(NULL, NULL,
1160 false, NULL, NULL, NULL, NULL,
1161 ctx->bctx);
1162 if (props.inline_container == NULL) {
1163 free(text);
1164 return false;
1165 }
1166
1167 props.inline_container->type =
1169
1171 props.inline_container);
1172 }
1173
1174 /** \todo Dropping const isn't clever */
1175 box = box_create(NULL,
1176 (css_computed_style *) props.parent_style,
1177 false, props.href, props.target, props.title,
1178 NULL, ctx->bctx);
1179 if (box == NULL) {
1180 free(text);
1181 return false;
1182 }
1183
1184 box->type = BOX_TEXT;
1185
1186 box->text = talloc_strdup(ctx->bctx, current);
1187 if (box->text == NULL) {
1188 free(text);
1189 return false;
1190 }
1191
1192 box->length = strlen(box->text);
1193
1195
1196 current[len] = old;
1197
1198 current += len;
1199
1200 if (current[0] != '\0') {
1201 /* Linebreak: create new inline container */
1202 props.inline_container = box_create(NULL, NULL,
1203 false, NULL, NULL, NULL, NULL,
1204 ctx->bctx);
1205 if (props.inline_container == NULL) {
1206 free(text);
1207 return false;
1208 }
1209
1210 props.inline_container->type =
1212
1214 props.inline_container);
1215
1216 if (current[0] == '\r' && current[1] == '\n')
1217 current += 2;
1218 else
1219 current++;
1220 }
1221 } while (*current);
1222
1223 free(text);
1224 }
1225
1226 return true;
1227}
1228
1229
1230/**
1231 * Convert an ELEMENT node to a box tree fragment,
1232 * then schedule conversion of the next ELEMENT node
1233 */
1235{
1236 dom_node *next;
1237 bool convert_children;
1238 uint32_t num_processed = 0;
1239 const uint32_t max_processed_before_yield = 10;
1240
1241 do {
1242 convert_children = true;
1243
1244 assert(ctx->n != NULL);
1245
1246 if (box_construct_element(ctx, &convert_children) == false) {
1247 ctx->cb(ctx->content, false);
1248 dom_node_unref(ctx->n);
1249 free(ctx);
1250 return;
1251 }
1252
1253 /* Find next element to process, converting text nodes as we go */
1254 next = next_node(ctx->n, ctx->content, convert_children);
1255 while (next != NULL) {
1256 dom_node_type type;
1257 dom_exception err;
1258
1259 err = dom_node_get_node_type(next, &type);
1260 if (err != DOM_NO_ERR) {
1261 ctx->cb(ctx->content, false);
1262 dom_node_unref(next);
1263 free(ctx);
1264 return;
1265 }
1266
1267 if (type == DOM_ELEMENT_NODE)
1268 break;
1269
1270 if (type == DOM_TEXT_NODE) {
1271 ctx->n = next;
1272 if (box_construct_text(ctx) == false) {
1273 ctx->cb(ctx->content, false);
1274 dom_node_unref(ctx->n);
1275 free(ctx);
1276 return;
1277 }
1278 }
1279
1280 next = next_node(next, ctx->content, true);
1281 }
1282
1283 ctx->n = next;
1284
1285 if (next == NULL) {
1286 /* Conversion complete */
1287 struct box root;
1288
1289 memset(&root, 0, sizeof(root));
1290
1291 root.type = BOX_BLOCK;
1292 root.children = root.last = ctx->root_box;
1293 root.children->parent = &root;
1294
1295 /** \todo Remove box_normalise_block */
1297 ctx->content) == false) {
1298 ctx->cb(ctx->content, false);
1299 } else {
1300 ctx->content->layout = root.children;
1301 ctx->content->layout->parent = NULL;
1302
1303 ctx->cb(ctx->content, true);
1304 }
1305
1306 assert(ctx->n == NULL);
1307
1308 free(ctx);
1309 return;
1310 }
1311 } while (++num_processed < max_processed_before_yield);
1312
1313 /* More work to do: schedule a continuation */
1314 guit->misc->schedule(0, (void *)convert_xml_to_box, ctx);
1315}
1316
1317
1318/* exported function documented in html/box_construct.h */
1319nserror
1320dom_to_box(dom_node *n,
1321 html_content *c,
1323 void **box_conversion_context)
1324{
1325 struct box_construct_ctx *ctx;
1326
1327 assert(box_conversion_context != NULL);
1328
1329 if (c->bctx == NULL) {
1330 /* create a context allocation for this box tree */
1331 c->bctx = talloc_zero(0, int);
1332 if (c->bctx == NULL) {
1333 return NSERROR_NOMEM;
1334 }
1335 }
1336
1337 ctx = malloc(sizeof(*ctx));
1338 if (ctx == NULL) {
1339 return NSERROR_NOMEM;
1340 }
1341
1342 ctx->content = c;
1343 ctx->n = dom_node_ref(n);
1344 ctx->root_box = NULL;
1345 ctx->cb = cb;
1346 ctx->bctx = c->bctx;
1347
1348 *box_conversion_context = ctx;
1349
1350 return guit->misc->schedule(0, (void *)convert_xml_to_box, ctx);
1351}
1352
1353
1354/* exported function documented in html/box_construct.h */
1355nserror cancel_dom_to_box(void *box_conversion_context)
1356{
1357 struct box_construct_ctx *ctx = box_conversion_context;
1358 nserror err;
1359
1360 err = guit->misc->schedule(-1, (void *)convert_xml_to_box, ctx);
1361 if (err != NSERROR_OK) {
1362 return err;
1363 }
1364
1365 dom_node_unref(ctx->n);
1366 free(ctx);
1367
1368 return NSERROR_OK;
1369}
1370
1371
1372/* exported function documented in html/box_construct.h */
1373struct box *box_for_node(dom_node *n)
1374{
1375 struct box *box = NULL;
1376 dom_exception err;
1377
1378 err = dom_node_get_user_data(n, corestring_dom___ns_key_box_node_data,
1379 (void *) &box);
1380 if (err != DOM_NO_ERR)
1381 return NULL;
1382
1383 return box;
1384}
1385
1386/* exported function documented in html/box_construct.h */
1387bool
1389 const dom_string *dsrel,
1390 nsurl *base,
1391 nsurl **result)
1392{
1393 char *s, *s1, *apos0 = 0, *apos1 = 0, *quot0 = 0, *quot1 = 0;
1394 unsigned int i, j, end;
1395 nserror error;
1396 const char *rel;
1397
1398 rel = dom_string_data(dsrel);
1399
1400 s1 = s = malloc(3 * strlen(rel) + 1);
1401 if (!s)
1402 return false;
1403
1404 /* copy to s, removing white space and control characters */
1405 for (i = 0; rel[i] && ascii_is_space(rel[i]); i++)
1406 ;
1407 for (end = strlen(rel);
1408 (end != i) && ascii_is_space(rel[end - 1]);
1409 end--)
1410 ;
1411 for (j = 0; i != end; i++) {
1412 if ((unsigned char) rel[i] < 0x20) {
1413 ; /* skip control characters */
1414 } else if (rel[i] == ' ') {
1415 s[j++] = '%';
1416 s[j++] = '2';
1417 s[j++] = '0';
1418 } else {
1419 s[j++] = rel[i];
1420 }
1421 }
1422 s[j] = 0;
1423
1424 if (content->enable_scripting == false) {
1425 /* extract first quoted string out of "javascript:" link */
1426 if (strncmp(s, "javascript:", 11) == 0) {
1427 apos0 = strchr(s, '\'');
1428 if (apos0)
1429 apos1 = strchr(apos0 + 1, '\'');
1430 quot0 = strchr(s, '"');
1431 if (quot0)
1432 quot1 = strchr(quot0 + 1, '"');
1433 if (apos0 && apos1 &&
1434 (!quot0 || !quot1 || apos0 < quot0)) {
1435 *apos1 = 0;
1436 s1 = apos0 + 1;
1437 } else if (quot0 && quot1) {
1438 *quot1 = 0;
1439 s1 = quot0 + 1;
1440 }
1441 }
1442 }
1443
1444 /* construct absolute URL */
1445 error = nsurl_join(base, s1, result);
1446 free(s);
1447 if (error != NSERROR_OK) {
1448 *result = NULL;
1449 return false;
1450 }
1451
1452 return true;
1453}
STATIC char result[100]
Definition: arexx.c:77
Helpers for ASCII string handling.
static char ascii_to_lower(char c)
Convert an upper case character to lower case.
Definition: ascii.h:212
static char ascii_to_upper(char c)
Convert a lower case character to upper case.
Definition: ascii.h:226
static bool ascii_is_space(char c)
Test whether a character is a whitespace character.
Definition: ascii.h:40
Box interface.
void(* box_construct_complete_cb)(struct html_content *c, bool success)
Definition: box.h:49
#define UNKNOWN_WIDTH
Definition: box.h:45
box_type
Type of a struct box.
Definition: box.h:55
@ BOX_BLOCK
Definition: box.h:56
@ BOX_FLOAT_LEFT
Definition: box.h:63
@ BOX_INLINE_BLOCK
Definition: box.h:65
@ BOX_FLOAT_RIGHT
Definition: box.h:64
@ BOX_INLINE_CONTAINER
Definition: box.h:57
@ BOX_TABLE_CELL
Definition: box.h:61
@ BOX_TABLE_ROW_GROUP
Definition: box.h:62
@ BOX_INLINE_END
Definition: box.h:68
@ BOX_TABLE
Definition: box.h:59
@ BOX_INLINE
Definition: box.h:58
@ BOX_TABLE_ROW
Definition: box.h:60
@ BOX_TEXT
Definition: box.h:67
@ BOX_INLINE_FLEX
Definition: box.h:71
@ BOX_BR
Definition: box.h:66
@ BOX_FLEX
Definition: box.h:70
@ BOX_NONE
Definition: box.h:69
@ CONVERT_CHILDREN
Definition: box.h:90
@ IS_REPLACED
Definition: box.h:91
@ PRE_STRIP
Definition: box.h:82
static void box_construct_element_after(dom_node *n, html_content *content)
Complete construction of the box tree for an element.
static bool box__is_flex(const struct box *box)
static const box_type box_map[]
Definition: box_construct.c:92
static bool box_construct_text(struct box_construct_ctx *ctx)
Construct the box tree for an XML text node.
static bool box__style_is_float(const struct box *box)
nserror dom_to_box(dom_node *n, html_content *c, box_construct_complete_cb cb, void **box_conversion_context)
Construct a box tree from a dom and html content.
static void box_extract_properties(dom_node *n, struct box_construct_props *props)
Extract transient construction properties.
static dom_node * next_node(dom_node *n, html_content *content, bool convert_children)
Find the next node in the DOM tree, completing element construction where appropriate.
struct box * box_for_node(dom_node *n)
Retrieve the box for a dom node, if there is one.
static css_select_results * box_get_style(html_content *c, const css_computed_style *parent_style, const css_computed_style *root_style, dom_node *n)
Get the style for an element.
static bool box_is_root(dom_node *n)
determine if a box is the root node
static void box_text_transform(char *s, unsigned int len, enum css_text_transform_e tt)
Apply the CSS text-transform property to given text for its ASCII chars.
bool box_extract_link(const html_content *content, const dom_string *dsrel, nsurl *base, nsurl **result)
static bool box__containing_block_is_flex(const struct box_construct_props *props)
static bool box_construct_marker(struct box *box, const char *title, struct box_construct_ctx *ctx, struct box *parent)
Construct a list marker box.
nserror cancel_dom_to_box(void *box_conversion_context)
aborts any ongoing box construction
static void convert_xml_to_box(struct box_construct_ctx *ctx)
Convert an ELEMENT node to a box tree fragment, then schedule conversion of the next ELEMENT node.
static void box_construct_generate(dom_node *n, html_content *content, struct box *box, const css_computed_style *style)
Construct the box required for a generated element.
static bool box_construct_element(struct box_construct_ctx *ctx, bool *convert_children)
Construct the box tree for an XML element.
static const content_type image_types
Definition: box_construct.c:88
HTML Box tree construction interface.
struct box * box_create(css_select_results *styles, css_computed_style *style, bool style_owned, nsurl *href, const char *target, const char *title, lwc_string *id, void *context)
Create a box tree node.
void box_add_child(struct box *parent, struct box *child)
Add a child to a box tree node.
Box tree manipulation interface.
bool box_normalise_block(struct box *block, const struct box *root, html_content *c)
Ensure the box tree is correctly nested by adding and removing nodes.
HTML Box tree normalise interface.
bool convert_special_elements(dom_node *node, html_content *content, struct box *box, bool *convert_children)
call an elements special conversion handler
Definition: box_special.c:1855
HTML Box tree construction special element conversion interface.
static uint8_t ns_computed_display(const css_computed_style *style, bool root)
Temporary helper wrappers for for libcss computed style getter, while we don't support all values of ...
Definition: utils.h:33
static uint8_t ns_computed_display_static(const css_computed_style *style)
Temporary helper wrappers for for libcss computed style getter, while we don't support all values of ...
Definition: utils.h:56
bool html_fetch_object(html_content *c, nsurl *url, struct box *box, content_type permitted_types, bool background)
Start a fetch for an object required by a page.
Definition: object.c:710
HTML content object interface.
content_type
The type of a content.
Definition: content_type.h:53
@ CONTENT_IMAGE
All images.
Definition: content_type.h:67
Useful interned string pointers (interface).
wimp_w parent
Definition: dialog.c:88
Error codes.
nserror
Enumeration of error codes.
Definition: errors.h:29
@ NSERROR_NOMEM
Memory exhaustion.
Definition: errors.h:32
@ NSERROR_OK
No error.
Definition: errors.h:30
static struct directory * root
Definition: filename.c:55
const char * type
Definition: filetype.cpp:44
Interface to form handling functions internal to HTML content handler.
struct netsurf_table * guit
The global interface table.
Definition: gui_factory.c:50
Interface to core interface table.
Interface to platform-specific miscellaneous browser operation table.
NetSurf URL handling (interface).
nserror nsurl_create(const char *const url_s, nsurl **url)
Create a NetSurf URL object from a URL string.
void nsurl_unref(nsurl *url)
Drop a reference to a NetSurf URL object.
const char * nsurl_access(const nsurl *url)
Access a NetSurf URL object as a string.
nserror nsurl_join(const nsurl *base, const char *rel, nsurl **joined)
Join a base url to a relative link part, creating a new NetSurf URL object.
struct nsurl nsurl
NetSurf URL object.
Definition: nsurl.h:31
Private data for text/html content.
@ base
Definition: punycode.c:19
css_select_results * nscss_get_style(nscss_select_ctx *ctx, dom_node *n, const css_media *media, const css_unit_ctx *unit_len_ctx, const css_stylesheet *inline_style)
Get style selection results for an element.
Definition: select.c:253
css_stylesheet * nscss_create_inline_style(const uint8_t *data, size_t len, const char *charset, const char *url, bool allow_quirks)
Create an inline style.
Definition: select.c:148
static css_error parent_node(void *pw, void *node, void **parent)
Callback to retrieve the parent of a node.
Definition: select.c:644
Interface to utility string handling.
char * squash_whitespace(const char *s)
Replace consecutive whitespace with a single space.
Definition: utils.c:38
Context for box tree construction.
Definition: box_construct.c:55
box_construct_complete_cb cb
Callback to invoke on completion.
Definition: box_construct.c:62
struct box * root_box
Root box in the tree.
Definition: box_construct.c:60
int * bctx
talloc context
Definition: box_construct.c:64
dom_node * n
Current node to process.
Definition: box_construct.c:58
html_content * content
Content we're constructing for.
Definition: box_construct.c:56
Transient properties for construction of current node.
Definition: box_construct.c:70
struct nsurl * href
Current link target, or NULL if none.
Definition: box_construct.c:74
bool node_is_root
Whether the current node is the root of the DOM tree.
Definition: box_construct.c:85
struct box * containing_block
Identity of the current block-level container.
Definition: box_construct.c:80
const char * target
Current frame target, or NULL if none.
Definition: box_construct.c:76
struct box * inline_container
Current container for inlines, or NULL if none.
Definition: box_construct.c:83
const css_computed_style * parent_style
Style from which to inherit, or NULL if none.
Definition: box_construct.c:72
const char * title
Current title attribute, or NULL if none.
Definition: box_construct.c:78
Node in box tree.
Definition: box.h:177
struct box * parent
Parent box, or NULL.
Definition: box.h:236
lwc_string * id
value of id attribute (or name for anchors)
Definition: box.h:210
const char * title
Title, or NULL.
Definition: box.h:386
struct box * inline_end
INLINE_END box corresponding to this INLINE box, or INLINE box corresponding to this INLINE_END box.
Definition: box.h:242
struct box * prev
Previous sibling box, or NULL.
Definition: box.h:221
struct box * list_marker
List marker box if this is a list-item, or NULL.
Definition: box.h:417
const char * target
Link target, or NULL.
Definition: box.h:381
css_select_results * styles
Computed styles for elements and their pseudo elements.
Definition: box.h:197
struct box * last
Last child box, or NULL.
Definition: box.h:231
struct box * next
Next sibling box, or NULL.
Definition: box.h:216
box_type type
Type of box.
Definition: box.h:181
struct nsurl * href
Link, or NULL.
Definition: box.h:376
css_computed_style * style
Style for this box.
Definition: box.h:205
size_t length
Length of text.
Definition: box.h:360
char * text
Text, or NULL if none.
Definition: box.h:355
box_flags flags
Box flags.
Definition: box.h:186
int space
Width of space after current text (depends on font and size).
Definition: box.h:365
struct form_control * gadget
Form control data, or NULL if not a form control.
Definition: box.h:423
unsigned int rows
Number of rows for TABLE only.
Definition: box.h:397
struct dom_node * node
DOM node that generated this box or NULL.
Definition: box.h:191
unsigned int columns
Number of columns for TABLE / TABLE_CELL.
Definition: box.h:392
Content which corresponds to a single URL.
struct box * box
Box for control.
Definition: form_internal.h:89
nserror(* schedule)(int t, void(*callback)(void *p), void *p)
Schedule a callback.
Definition: misc.h:58
Data specific to CONTENT_HTML.
Definition: private.h:93
dom_document_quirks_mode quirks
Quirkyness of document.
Definition: private.h:103
int * bctx
A talloc context purely for the render box tree.
Definition: private.h:134
char * encoding
Encoding of source, NULL if unknown.
Definition: private.h:106
struct nsurl * base_url
Base URL (may be a copy of content->url).
Definition: private.h:111
css_select_ctx * select_ctx
Style selection media specification.
Definition: private.h:159
css_media media
Definition: private.h:161
css_unit_ctx unit_len_ctx
CSS length conversion context for document.
Definition: private.h:163
struct box * layout
Box tree, or NULL.
Definition: private.h:140
lwc_string * universal
Definition: private.h:165
struct gui_misc_table * misc
Browser table.
Definition: gui_table.h:57
Selection context.
Definition: select.h:35
struct nsurl * base_url
Definition: select.h:38
css_select_ctx * ctx
Definition: select.h:36
lwc_string * universal
Definition: select.h:39
const css_computed_style * parent_style
Definition: select.h:41
const css_computed_style * root_style
Definition: select.h:40
char * talloc_strdup(const void *t, const char *p)
Definition: talloc.c:1118
#define talloc_zero(ctx, type)
Definition: talloc.h:91
Option reading and saving interface.
#define nsoption_bool(OPTION)
Get the value of a boolean option.
Definition: nsoption.h:304
Interface to a number of general purpose functionality.
#define clamp(x, low, high)
Definition: utils.h:54
static nserror text(const struct redraw_context *ctx, const struct plot_font_style *fstyle, int x, int y, const char *text, size_t length)
Text plotting.
Definition: plot.c:978