File: | content/handlers/html/box_construct.c |
Warning: | line 600, column 7 Access to field 'type' results in a dereference of a null pointer (loaded from field 'containing_block') |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
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" | |||
41 | #include "desktop/gui_internal.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 | */ | |||
55 | struct box_construct_ctx { | |||
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 | */ | |||
70 | struct box_construct_props { | |||
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 */ | |||
80 | struct box *containing_block; | |||
81 | /** Current container for inlines, or NULL if none | |||
82 | * \note If non-NULL, will be the last child of containing_block */ | |||
83 | struct box *inline_container; | |||
84 | /** Whether the current node is the root of the DOM tree */ | |||
85 | bool_Bool node_is_root; | |||
86 | }; | |||
87 | ||||
88 | static const content_type image_types = CONTENT_IMAGE; | |||
89 | ||||
90 | /* mapping from CSS display to box type | |||
91 | * this table must be in sync with libcss' css_display enum */ | |||
92 | static 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 | */ | |||
123 | static inline bool_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)dom_node_get_parent_node( (dom_node *) (n), (dom_node **) (& parent)); | |||
130 | if (err != DOM_NO_ERR) | |||
131 | return false0; | |||
132 | ||||
133 | if (parent != NULL((void*)0)) { | |||
134 | err = dom_node_get_node_type(parent, &type)dom_node_get_node_type( (dom_node *) (parent), (dom_node_type *) (&type)); | |||
135 | ||||
136 | dom_node_unref(parent)dom_node_unref((dom_node *) (parent)); | |||
137 | ||||
138 | if (err != DOM_NO_ERR) | |||
139 | return false0; | |||
140 | ||||
141 | if (type != DOM_DOCUMENT_NODE) | |||
142 | return false0; | |||
143 | } | |||
144 | ||||
145 | return true1; | |||
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 | */ | |||
154 | static void | |||
155 | box_extract_properties(dom_node *n, struct box_construct_props *props) | |||
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 == false0) { | |||
163 | dom_node *current_node = n; | |||
164 | dom_node *parent_node = NULL((void*)0); | |||
165 | struct box *parent_box; | |||
166 | dom_exception err; | |||
167 | ||||
168 | /* Find ancestor node containing parent box */ | |||
169 | while (true1) { | |||
170 | err = dom_node_get_parent_node(current_node,dom_node_get_parent_node( (dom_node *) (current_node), (dom_node **) (&parent_node)) | |||
171 | &parent_node)dom_node_get_parent_node( (dom_node *) (current_node), (dom_node **) (&parent_node)); | |||
172 | if (err != DOM_NO_ERR || parent_node == NULL((void*)0)) | |||
173 | break; | |||
174 | ||||
175 | parent_box = box_for_node(parent_node); | |||
176 | ||||
177 | if (parent_box != NULL((void*)0)) { | |||
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)dom_node_unref((dom_node *) (parent_node)); | |||
184 | break; | |||
185 | } else { | |||
186 | if (current_node != n) | |||
187 | dom_node_unref(current_node)dom_node_unref((dom_node *) (current_node)); | |||
188 | current_node = parent_node; | |||
189 | parent_node = NULL((void*)0); | |||
190 | } | |||
191 | } | |||
192 | ||||
193 | /* Find containing block (may be parent) */ | |||
194 | while (true1) { | |||
195 | struct box *b; | |||
196 | ||||
197 | err = dom_node_get_parent_node(current_node,dom_node_get_parent_node( (dom_node *) (current_node), (dom_node **) (&parent_node)) | |||
198 | &parent_node)dom_node_get_parent_node( (dom_node *) (current_node), (dom_node **) (&parent_node)); | |||
199 | if (err != DOM_NO_ERR || parent_node == NULL((void*)0)) { | |||
200 | if (current_node != n) | |||
201 | dom_node_unref(current_node)dom_node_unref((dom_node *) (current_node)); | |||
202 | break; | |||
203 | } | |||
204 | ||||
205 | if (current_node != n) | |||
206 | dom_node_unref(current_node)dom_node_unref((dom_node *) (current_node)); | |||
207 | ||||
208 | b = box_for_node(parent_node); | |||
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((void*)0) && b->type != BOX_INLINE && | |||
217 | b->type != BOX_BR) { | |||
218 | props->containing_block = b; | |||
219 | ||||
220 | dom_node_unref(parent_node)dom_node_unref((dom_node *) (parent_node)); | |||
221 | break; | |||
222 | } else { | |||
223 | current_node = parent_node; | |||
224 | parent_node = NULL((void*)0); | |||
225 | } | |||
226 | } | |||
227 | } | |||
228 | ||||
229 | /* Compute current inline container, if any */ | |||
230 | if (props->containing_block != NULL((void*)0) && | |||
231 | props->containing_block->last != NULL((void*)0) && | |||
232 | props->containing_block->last->type == | |||
233 | BOX_INLINE_CONTAINER) | |||
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 | */ | |||
247 | static css_select_results * | |||
248 | box_get_style(html_content *c, | |||
249 | const css_computed_style *parent_style, | |||
250 | const css_computed_style *root_style, | |||
251 | dom_node *n) | |||
252 | { | |||
253 | dom_string *s = NULL((void*)0); | |||
254 | css_stylesheet *inline_style = NULL((void*)0); | |||
255 | css_select_results *styles; | |||
256 | nscss_select_ctx ctx; | |||
257 | ||||
258 | /* Firstly, construct inline stylesheet, if any */ | |||
259 | if (nsoption_bool(author_level_css)(nsoptions[NSOPTION_author_level_css].value.b)) { | |||
260 | dom_exception err; | |||
261 | err = dom_element_get_attribute(n, corestring_dom_style, &s)dom_element_get_attribute( (dom_element *) (n), (corestring_dom_style ), (&s)); | |||
262 | if (err != DOM_NO_ERR) { | |||
263 | return NULL((void*)0); | |||
264 | } | |||
265 | } | |||
266 | ||||
267 | if (s != NULL((void*)0)) { | |||
268 | inline_style = nscss_create_inline_style( | |||
269 | (const uint8_t *) dom_string_data(s), | |||
270 | dom_string_byte_length(s), | |||
271 | c->encoding, | |||
272 | nsurl_access(c->base_url), | |||
273 | c->quirks != DOM_DOCUMENT_QUIRKS_MODE_NONE); | |||
274 | ||||
275 | dom_string_unref(s); | |||
276 | ||||
277 | if (inline_style == NULL((void*)0)) | |||
278 | return NULL((void*)0); | |||
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((void*)0)) | |||
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 | */ | |||
312 | static void | |||
313 | box_construct_generate(dom_node *n, | |||
314 | html_content *content, | |||
315 | struct box *box, | |||
316 | const css_computed_style *style) | |||
317 | { | |||
318 | struct box *gen = NULL((void*)0); | |||
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((void*)0) || | |||
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((void*)0), (css_computed_style *) style, | |||
344 | false0, NULL((void*)0), NULL((void*)0), NULL((void*)0), NULL((void*)0), content->bctx); | |||
345 | if (gen == NULL((void*)0)) { | |||
346 | return; | |||
347 | } | |||
348 | ||||
349 | /* set box type from computed display */ | |||
350 | gen->type = box_map[ns_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 | */ | |||
367 | static bool_Bool | |||
368 | box_construct_marker(struct box *box, | |||
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((void*)0), box->style, false0, NULL((void*)0), NULL((void*)0), title, | |||
378 | NULL((void*)0), ctx->bctx); | |||
379 | if (marker == false0) | |||
380 | return false0; | |||
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((void*)0); | |||
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((void*)0)) && | |||
417 | (nsoption_bool(foreground_images)(nsoptions[NSOPTION_foreground_images].value.b) == true1)) { | |||
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)({((image_uri != ((void*)0)) ? (void) (0) : __assert_fail ("image_uri != NULL" , "content/handlers/html/box_construct.c", 425, __extension__ __PRETTY_FUNCTION__)); (const char *)((image_uri)+1);}), &url); | |||
426 | if (error != NSERROR_OK) | |||
427 | return false0; | |||
428 | ||||
429 | if (html_fetch_object(ctx->content, | |||
430 | url, | |||
431 | marker, | |||
432 | image_types, | |||
433 | false0) == false0) { | |||
434 | nsurl_unref(url); | |||
435 | return false0; | |||
436 | } | |||
437 | nsurl_unref(url); | |||
438 | } | |||
439 | ||||
440 | box->list_marker = marker; | |||
441 | marker->parent = box; | |||
442 | ||||
443 | return true1; | |||
444 | } | |||
445 | ||||
446 | static inline bool_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 | ||||
452 | static inline bool_Bool box__is_flex(const struct box *box) | |||
453 | { | |||
454 | return box->type == BOX_FLEX || box->type == BOX_INLINE_FLEX; | |||
455 | } | |||
456 | ||||
457 | static inline bool_Bool box__containing_block_is_flex( | |||
458 | const struct box_construct_props *props) | |||
459 | { | |||
460 | return props->containing_block != NULL((void*)0) && | |||
461 | box__is_flex(props->containing_block); | |||
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 | */ | |||
471 | static bool_Bool | |||
472 | box_construct_element(struct box_construct_ctx *ctx, bool_Bool *convert_children) | |||
473 | { | |||
474 | dom_string *title0, *s; | |||
475 | lwc_string *id = NULL((void*)0); | |||
476 | enum css_display_e css_display; | |||
477 | struct box *box = NULL((void*)0), *old_box; | |||
478 | css_select_results *styles = NULL((void*)0); | |||
479 | lwc_string *bgimage_uri; | |||
480 | dom_exception err; | |||
481 | struct box_construct_props props; | |||
482 | const css_computed_style *root_style = NULL((void*)0); | |||
483 | ||||
484 | assert(ctx->n != NULL)((ctx->n != ((void*)0)) ? (void) (0) : __assert_fail ("ctx->n != NULL" , "content/handlers/html/box_construct.c", 484, __extension__ __PRETTY_FUNCTION__)); | |||
| ||||
485 | ||||
486 | box_extract_properties(ctx->n, &props); | |||
487 | ||||
488 | if (props.containing_block != NULL((void*)0)) { | |||
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 == false0) { | |||
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((void*)0)) | |||
502 | return false0; | |||
503 | ||||
504 | /* Extract title attribute, if present */ | |||
505 | err = dom_element_get_attribute(ctx->n, corestring_dom_title, &title0)dom_element_get_attribute( (dom_element *) (ctx->n), (corestring_dom_title ), (&title0)); | |||
506 | if (err != DOM_NO_ERR) | |||
507 | return false0; | |||
508 | ||||
509 | if (title0 != NULL((void*)0)) { | |||
510 | char *t = squash_whitespace(dom_string_data(title0)); | |||
511 | ||||
512 | dom_string_unref(title0); | |||
513 | ||||
514 | if (t == NULL((void*)0)) | |||
515 | return false0; | |||
516 | ||||
517 | props.title = talloc_strdup(ctx->bctx, t); | |||
518 | ||||
519 | free(t); | |||
520 | ||||
521 | if (props.title == NULL((void*)0)) | |||
522 | return false0; | |||
523 | } | |||
524 | ||||
525 | /* Extract id attribute, if present */ | |||
526 | err = dom_element_get_attribute(ctx->n, corestring_dom_id, &s)dom_element_get_attribute( (dom_element *) (ctx->n), (corestring_dom_id ), (&s)); | |||
527 | if (err != DOM_NO_ERR) | |||
528 | return false0; | |||
529 | ||||
530 | if (s != NULL((void*)0)) { | |||
531 | err = dom_string_intern(s, &id); | |||
532 | if (err != DOM_NO_ERR) | |||
533 | id = NULL((void*)0); | |||
534 | ||||
535 | dom_string_unref(s); | |||
536 | } | |||
537 | ||||
538 | box = box_create(styles, styles->styles[CSS_PSEUDO_ELEMENT_NONE], false0, | |||
539 | props.href, props.target, props.title, id, | |||
540 | ctx->bctx); | |||
541 | if (box == NULL((void*)0)) | |||
542 | return false0; | |||
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)dom_element_get_attribute( (dom_element *) (ctx->n), (corestring_dom_colspan ), (&s)); | |||
550 | if (err != DOM_NO_ERR) | |||
551 | return false0; | |||
552 | ||||
553 | if (s != NULL((void*)0)) { | |||
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)((((((((strtol(val, ((void*)0), 10)))>((1)))?((strtol(val, ((void*)0), 10))):((1))))<((1000)))?(((((strtol(val, ((void *)0), 10)))>((1)))?((strtol(val, ((void*)0), 10))):((1)))) :((1000)))); | |||
559 | ||||
560 | dom_string_unref(s); | |||
561 | } | |||
562 | ||||
563 | err = dom_element_get_attribute(ctx->n, corestring_dom_rowspan, &s)dom_element_get_attribute( (dom_element *) (ctx->n), (corestring_dom_rowspan ), (&s)); | |||
564 | if (err != DOM_NO_ERR) | |||
565 | return false0; | |||
566 | ||||
567 | if (s != NULL((void*)0)) { | |||
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)((((((((strtol(val, ((void*)0), 10)))>((0)))?((strtol(val, ((void*)0), 10))):((0))))<((65534)))?(((((strtol(val, ((void *)0), 10)))>((0)))?((strtol(val, ((void*)0), 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 */ | |||
597 | box->type = box_map[ns_computed_display(box->style, | |||
598 | props.node_is_root)]; | |||
599 | ||||
600 | if (props.containing_block->type == BOX_FLEX || | |||
| ||||
601 | props.containing_block->type == BOX_INLINE_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 | ||||
616 | if (convert_special_elements(ctx->n, | |||
617 | ctx->content, | |||
618 | box, | |||
619 | convert_children) == false0) { | |||
620 | return false0; | |||
621 | } | |||
622 | ||||
623 | /* Handle the :before pseudo element */ | |||
624 | if (!(box->flags & IS_REPLACED)) { | |||
625 | box_construct_generate(ctx->n, ctx->content, box, | |||
626 | box->styles->styles[CSS_PSEUDO_ELEMENT_BEFORE]); | |||
627 | } | |||
628 | ||||
629 | if (box->type == BOX_NONE || (ns_computed_display(box->style, | |||
630 | props.node_is_root) == CSS_DISPLAY_NONE && | |||
631 | props.node_is_root == false0)) { | |||
632 | css_select_results_destroy(styles); | |||
633 | box->styles = NULL((void*)0); | |||
634 | box->style = NULL((void*)0); | |||
635 | ||||
636 | /* Invalidate associated gadget, if any */ | |||
637 | if (box->gadget != NULL((void*)0)) { | |||
638 | box->gadget->box = NULL((void*)0); | |||
639 | box->gadget = NULL((void*)0); | |||
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 = false0; | |||
648 | ||||
649 | return true1; | |||
650 | } | |||
651 | ||||
652 | /* Attach DOM node to box */ | |||
653 | err = dom_node_set_user_data(ctx->n,dom_node_set_user_data( (dom_node *) (ctx->n), (corestring_dom___ns_key_box_node_data ), (void *) (box), (dom_user_data_handler) ((void*)0), (void * *) ((void *) &old_box)) | |||
654 | corestring_dom___ns_key_box_node_data, box, NULL,dom_node_set_user_data( (dom_node *) (ctx->n), (corestring_dom___ns_key_box_node_data ), (void *) (box), (dom_user_data_handler) ((void*)0), (void * *) ((void *) &old_box)) | |||
655 | (void *) &old_box)dom_node_set_user_data( (dom_node *) (ctx->n), (corestring_dom___ns_key_box_node_data ), (void *) (box), (dom_user_data_handler) ((void*)0), (void * *) ((void *) &old_box)); | |||
656 | if (err != DOM_NO_ERR) | |||
657 | return false0; | |||
658 | ||||
659 | /* Attach box to DOM node */ | |||
660 | box->node = dom_node_ref(ctx->n)dom_node_ref((dom_node *) (ctx->n)); | |||
661 | ||||
662 | if (props.inline_container == NULL((void*)0) && | |||
663 | (box->type == BOX_INLINE || | |||
664 | box->type == BOX_BR || | |||
665 | box->type == BOX_INLINE_BLOCK || | |||
666 | box->type == BOX_INLINE_FLEX || | |||
667 | (box__style_is_float(box) && | |||
668 | !box__containing_block_is_flex(&props))) && | |||
669 | props.node_is_root == false0) { | |||
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 &&((props.containing_block != ((void*)0) && "Box must have containing block." ) ? (void) (0) : __assert_fail ("props.containing_block != NULL && \"Box must have containing block.\"" , "content/handlers/html/box_construct.c", 674, __extension__ __PRETTY_FUNCTION__)) | |||
674 | "Box must have containing block.")((props.containing_block != ((void*)0) && "Box must have containing block." ) ? (void) (0) : __assert_fail ("props.containing_block != NULL && \"Box must have containing block.\"" , "content/handlers/html/box_construct.c", 674, __extension__ __PRETTY_FUNCTION__)); | |||
675 | ||||
676 | props.inline_container = box_create(NULL((void*)0), NULL((void*)0), false0, NULL((void*)0), | |||
677 | NULL((void*)0), NULL((void*)0), NULL((void*)0), ctx->bctx); | |||
678 | if (props.inline_container == NULL((void*)0)) | |||
679 | return false0; | |||
680 | ||||
681 | props.inline_container->type = BOX_INLINE_CONTAINER; | |||
682 | ||||
683 | box_add_child(props.containing_block, props.inline_container); | |||
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((void*)0) && | |||
689 | nsoption_bool(background_images)(nsoptions[NSOPTION_background_images].value.b) == true1) { | |||
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)({((bgimage_uri != ((void*)0)) ? (void) (0) : __assert_fail ( "bgimage_uri != NULL", "content/handlers/html/box_construct.c" , 697, __extension__ __PRETTY_FUNCTION__)); (const char *)((bgimage_uri )+1);}), &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, | |||
703 | image_types, | |||
704 | true1) == false0) { | |||
705 | nsurl_unref(url); | |||
706 | return false0; | |||
707 | } | |||
708 | nsurl_unref(url); | |||
709 | } | |||
710 | } | |||
711 | ||||
712 | if (*convert_children) | |||
713 | box->flags |= CONVERT_CHILDREN; | |||
714 | ||||
715 | if (box->type == BOX_INLINE || box->type == BOX_BR || | |||
716 | box->type == BOX_INLINE_FLEX || | |||
717 | box->type == BOX_INLINE_BLOCK) { | |||
718 | /* Inline container must exist, as we'll have | |||
719 | * created it above if it didn't */ | |||
720 | assert(props.inline_container != NULL)((props.inline_container != ((void*)0)) ? (void) (0) : __assert_fail ("props.inline_container != NULL", "content/handlers/html/box_construct.c" , 720, __extension__ __PRETTY_FUNCTION__)); | |||
721 | ||||
722 | box_add_child(props.inline_container, box); | |||
723 | } else { | |||
724 | if (ns_computed_display(box->style, props.node_is_root) == | |||
725 | CSS_DISPLAY_LIST_ITEM) { | |||
726 | /* List item: compute marker */ | |||
727 | if (box_construct_marker(box, props.title, ctx, | |||
728 | props.containing_block) == false0) | |||
729 | return false0; | |||
730 | } | |||
731 | ||||
732 | if (props.node_is_root == false0 && | |||
733 | box__containing_block_is_flex(&props) == false0 && | |||
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((void*)0), NULL((void*)0), false0, | |||
740 | props.href, props.target, props.title, | |||
741 | NULL((void*)0), ctx->bctx); | |||
742 | if (flt == NULL((void*)0)) | |||
743 | return false0; | |||
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 | ||||
750 | box_add_child(props.inline_container, flt); | |||
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((void*)0)) | |||
757 | box_add_child(props.containing_block, box); | |||
758 | } | |||
759 | } | |||
760 | ||||
761 | return true1; | |||
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 | */ | |||
773 | static void box_construct_element_after(dom_node *n, html_content *content) | |||
774 | { | |||
775 | struct box_construct_props props; | |||
776 | struct box *box = box_for_node(n); | |||
777 | ||||
778 | assert(box != NULL)((box != ((void*)0)) ? (void) (0) : __assert_fail ("box != NULL" , "content/handlers/html/box_construct.c", 778, __extension__ __PRETTY_FUNCTION__)); | |||
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_Bool has_children; | |||
786 | dom_exception err; | |||
787 | ||||
788 | err = dom_node_has_child_nodes(n, &has_children)dom_node_has_child_nodes( (dom_node *) (n), (_Bool *) (&has_children )); | |||
789 | if (err != DOM_NO_ERR) | |||
790 | return; | |||
791 | ||||
792 | if (has_children == false0 || | |||
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((void*)0)) { | |||
799 | /* Create inline container if we don't have one */ | |||
800 | props.inline_container = box_create(NULL((void*)0), NULL((void*)0), false0, | |||
801 | NULL((void*)0), NULL((void*)0), NULL((void*)0), NULL((void*)0), content->bctx); | |||
802 | if (props.inline_container == NULL((void*)0)) | |||
803 | return; | |||
804 | ||||
805 | props.inline_container->type = BOX_INLINE_CONTAINER; | |||
806 | ||||
807 | box_add_child(props.containing_block, | |||
808 | props.inline_container); | |||
809 | } | |||
810 | ||||
811 | inline_end = box_create(NULL((void*)0), box->style, false0, | |||
812 | box->href, box->target, box->title, | |||
813 | box->id == NULL((void*)0) ? NULL((void*)0) : | |||
814 | lwc_string_ref(box->id)({lwc_string *__lwc_s = (box->id); ((__lwc_s != ((void*)0) ) ? (void) (0) : __assert_fail ("__lwc_s != NULL", "content/handlers/html/box_construct.c" , 814, __extension__ __PRETTY_FUNCTION__)); __lwc_s->refcnt ++; __lwc_s;}), content->bctx); | |||
815 | if (inline_end != NULL((void*)0)) { | |||
816 | inline_end->type = BOX_INLINE_END; | |||
817 | ||||
818 | assert(props.inline_container != NULL)((props.inline_container != ((void*)0)) ? (void) (0) : __assert_fail ("props.inline_container != NULL", "content/handlers/html/box_construct.c" , 818, __extension__ __PRETTY_FUNCTION__)); | |||
819 | ||||
820 | box_add_child(props.inline_container, inline_end); | |||
821 | ||||
822 | box->inline_end = inline_end; | |||
823 | inline_end->inline_end = box; | |||
824 | } | |||
825 | } else if (!(box->flags & IS_REPLACED)) { | |||
826 | /* Handle the :after pseudo element */ | |||
827 | box_construct_generate(n, content, box, | |||
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 | */ | |||
844 | static dom_node * | |||
845 | next_node(dom_node *n, html_content *content, bool_Bool convert_children) | |||
846 | { | |||
847 | dom_node *next = NULL((void*)0); | |||
848 | bool_Bool has_children; | |||
849 | dom_exception err; | |||
850 | ||||
851 | err = dom_node_has_child_nodes(n, &has_children)dom_node_has_child_nodes( (dom_node *) (n), (_Bool *) (&has_children )); | |||
852 | if (err != DOM_NO_ERR) { | |||
853 | dom_node_unref(n)dom_node_unref((dom_node *) (n)); | |||
854 | return NULL((void*)0); | |||
855 | } | |||
856 | ||||
857 | if (convert_children && has_children) { | |||
858 | err = dom_node_get_first_child(n, &next)dom_node_get_first_child( (dom_node *) (n), (dom_node **) (& next)); | |||
859 | if (err != DOM_NO_ERR) { | |||
860 | dom_node_unref(n)dom_node_unref((dom_node *) (n)); | |||
861 | return NULL((void*)0); | |||
862 | } | |||
863 | dom_node_unref(n)dom_node_unref((dom_node *) (n)); | |||
864 | } else { | |||
865 | err = dom_node_get_next_sibling(n, &next)dom_node_get_next_sibling( (dom_node *) (n), (dom_node **) (& next)); | |||
866 | if (err != DOM_NO_ERR) { | |||
867 | dom_node_unref(n)dom_node_unref((dom_node *) (n)); | |||
868 | return NULL((void*)0); | |||
869 | } | |||
870 | ||||
871 | if (next != NULL((void*)0)) { | |||
872 | if (box_for_node(n) != NULL((void*)0)) | |||
873 | box_construct_element_after(n, content); | |||
874 | dom_node_unref(n)dom_node_unref((dom_node *) (n)); | |||
875 | } else { | |||
876 | if (box_for_node(n) != NULL((void*)0)) | |||
877 | box_construct_element_after(n, content); | |||
878 | ||||
879 | while (box_is_root(n) == false0) { | |||
880 | dom_node *parent = NULL((void*)0); | |||
881 | dom_node *parent_next = NULL((void*)0); | |||
882 | ||||
883 | err = dom_node_get_parent_node(n, &parent)dom_node_get_parent_node( (dom_node *) (n), (dom_node **) (& parent)); | |||
884 | if (err != DOM_NO_ERR) { | |||
885 | dom_node_unref(n)dom_node_unref((dom_node *) (n)); | |||
886 | return NULL((void*)0); | |||
887 | } | |||
888 | ||||
889 | assert(parent != NULL)((parent != ((void*)0)) ? (void) (0) : __assert_fail ("parent != NULL" , "content/handlers/html/box_construct.c", 889, __extension__ __PRETTY_FUNCTION__)); | |||
890 | ||||
891 | err = dom_node_get_next_sibling(parent,dom_node_get_next_sibling( (dom_node *) (parent), (dom_node * *) (&parent_next)) | |||
892 | &parent_next)dom_node_get_next_sibling( (dom_node *) (parent), (dom_node * *) (&parent_next)); | |||
893 | if (err != DOM_NO_ERR) { | |||
894 | dom_node_unref(parent)dom_node_unref((dom_node *) (parent)); | |||
895 | dom_node_unref(n)dom_node_unref((dom_node *) (n)); | |||
896 | return NULL((void*)0); | |||
897 | } | |||
898 | ||||
899 | if (parent_next != NULL((void*)0)) { | |||
900 | dom_node_unref(parent_next)dom_node_unref((dom_node *) (parent_next)); | |||
901 | dom_node_unref(parent)dom_node_unref((dom_node *) (parent)); | |||
902 | break; | |||
903 | } | |||
904 | ||||
905 | dom_node_unref(n)dom_node_unref((dom_node *) (n)); | |||
906 | n = parent; | |||
907 | parent = NULL((void*)0); | |||
908 | ||||
909 | if (box_for_node(n) != NULL((void*)0)) { | |||
910 | box_construct_element_after( | |||
911 | n, content); | |||
912 | } | |||
913 | } | |||
914 | ||||
915 | if (box_is_root(n) == false0) { | |||
916 | dom_node *parent = NULL((void*)0); | |||
917 | ||||
918 | err = dom_node_get_parent_node(n, &parent)dom_node_get_parent_node( (dom_node *) (n), (dom_node **) (& parent)); | |||
919 | if (err != DOM_NO_ERR) { | |||
920 | dom_node_unref(n)dom_node_unref((dom_node *) (n)); | |||
921 | return NULL((void*)0); | |||
922 | } | |||
923 | ||||
924 | assert(parent != NULL)((parent != ((void*)0)) ? (void) (0) : __assert_fail ("parent != NULL" , "content/handlers/html/box_construct.c", 924, __extension__ __PRETTY_FUNCTION__)); | |||
925 | ||||
926 | err = dom_node_get_next_sibling(parent, &next)dom_node_get_next_sibling( (dom_node *) (parent), (dom_node * *) (&next)); | |||
927 | if (err != DOM_NO_ERR) { | |||
928 | dom_node_unref(parent)dom_node_unref((dom_node *) (parent)); | |||
929 | dom_node_unref(n)dom_node_unref((dom_node *) (n)); | |||
930 | return NULL((void*)0); | |||
931 | } | |||
932 | ||||
933 | if (box_for_node(parent) != NULL((void*)0)) { | |||
934 | box_construct_element_after(parent, | |||
935 | content); | |||
936 | } | |||
937 | ||||
938 | dom_node_unref(parent)dom_node_unref((dom_node *) (parent)); | |||
939 | } | |||
940 | ||||
941 | dom_node_unref(n)dom_node_unref((dom_node *) (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 | */ | |||
956 | static void | |||
957 | box_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 | */ | |||
993 | static bool_Bool box_construct_text(struct box_construct_ctx *ctx) | |||
994 | { | |||
995 | struct box_construct_props props; | |||
996 | struct box *box = NULL((void*)0); | |||
997 | dom_string *content; | |||
998 | dom_exception err; | |||
999 | ||||
1000 | assert(ctx->n != NULL)((ctx->n != ((void*)0)) ? (void) (0) : __assert_fail ("ctx->n != NULL" , "content/handlers/html/box_construct.c", 1000, __extension__ __PRETTY_FUNCTION__)); | |||
1001 | ||||
1002 | box_extract_properties(ctx->n, &props); | |||
1003 | ||||
1004 | assert(props.containing_block != NULL)((props.containing_block != ((void*)0)) ? (void) (0) : __assert_fail ("props.containing_block != NULL", "content/handlers/html/box_construct.c" , 1004, __extension__ __PRETTY_FUNCTION__)); | |||
1005 | ||||
1006 | err = dom_characterdata_get_data(ctx->n, &content)dom_characterdata_get_data( (struct dom_characterdata *) (ctx ->n), (&content)); | |||
1007 | if (err != DOM_NO_ERR || content == NULL((void*)0)) | |||
1008 | return false0; | |||
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((void*)0)) | |||
1021 | return false0; | |||
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((void*)0)) { | |||
1027 | assert(props.inline_container->last != NULL)((props.inline_container->last != ((void*)0)) ? (void) (0) : __assert_fail ("props.inline_container->last != NULL", "content/handlers/html/box_construct.c" , 1027, __extension__ __PRETTY_FUNCTION__)); | |||
1028 | ||||
1029 | props.inline_container->last->space = | |||
1030 | UNKNOWN_WIDTH2147483647; | |||
1031 | } | |||
1032 | ||||
1033 | free(text); | |||
1034 | ||||
1035 | return true1; | |||
1036 | } | |||
1037 | ||||
1038 | if (props.inline_container == NULL((void*)0)) { | |||
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((void*)0), NULL((void*)0), false0, | |||
1043 | NULL((void*)0), NULL((void*)0), NULL((void*)0), NULL((void*)0), ctx->bctx); | |||
1044 | if (props.inline_container == NULL((void*)0)) { | |||
1045 | free(text); | |||
1046 | return false0; | |||
1047 | } | |||
1048 | ||||
1049 | props.inline_container->type = BOX_INLINE_CONTAINER; | |||
1050 | ||||
1051 | box_add_child(props.containing_block, | |||
1052 | props.inline_container); | |||
1053 | } | |||
1054 | ||||
1055 | /** \todo Dropping const here is not clever */ | |||
1056 | box = box_create(NULL((void*)0), | |||
1057 | (css_computed_style *) props.parent_style, | |||
1058 | false0, props.href, props.target, props.title, | |||
1059 | NULL((void*)0), ctx->bctx); | |||
1060 | if (box == NULL((void*)0)) { | |||
1061 | free(text); | |||
1062 | return false0; | |||
1063 | } | |||
1064 | ||||
1065 | box->type = BOX_TEXT; | |||
1066 | ||||
1067 | box->text = talloc_strdup(ctx->bctx, text); | |||
1068 | free(text); | |||
1069 | if (box->text == NULL((void*)0)) | |||
1070 | return false0; | |||
1071 | ||||
1072 | box->length = strlen(box->text); | |||
1073 | ||||
1074 | /* strip ending space char off */ | |||
1075 | if (box->length > 1 && box->text[box->length - 1] == ' ') { | |||
1076 | box->space = UNKNOWN_WIDTH2147483647; | |||
1077 | box->length--; | |||
1078 | } | |||
1079 | ||||
1080 | if (css_computed_text_transform(props.parent_style) != | |||
1081 | CSS_TEXT_TRANSFORM_NONE) | |||
1082 | box_text_transform(box->text, box->length, | |||
1083 | css_computed_text_transform( | |||
1084 | props.parent_style)); | |||
1085 | ||||
1086 | box_add_child(props.inline_container, box); | |||
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((void*)0)) | |||
1094 | box->prev->space = UNKNOWN_WIDTH2147483647; | |||
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 ||((white_space == CSS_WHITE_SPACE_PRE || white_space == CSS_WHITE_SPACE_PRE_LINE || white_space == CSS_WHITE_SPACE_PRE_WRAP) ? (void) (0) : __assert_fail ("white_space == CSS_WHITE_SPACE_PRE || white_space == CSS_WHITE_SPACE_PRE_LINE || white_space == CSS_WHITE_SPACE_PRE_WRAP" , "content/handlers/html/box_construct.c", 1108, __extension__ __PRETTY_FUNCTION__)) | |||
1107 | white_space == CSS_WHITE_SPACE_PRE_LINE ||((white_space == CSS_WHITE_SPACE_PRE || white_space == CSS_WHITE_SPACE_PRE_LINE || white_space == CSS_WHITE_SPACE_PRE_WRAP) ? (void) (0) : __assert_fail ("white_space == CSS_WHITE_SPACE_PRE || white_space == CSS_WHITE_SPACE_PRE_LINE || white_space == CSS_WHITE_SPACE_PRE_WRAP" , "content/handlers/html/box_construct.c", 1108, __extension__ __PRETTY_FUNCTION__)) | |||
1108 | white_space == CSS_WHITE_SPACE_PRE_WRAP)((white_space == CSS_WHITE_SPACE_PRE || white_space == CSS_WHITE_SPACE_PRE_LINE || white_space == CSS_WHITE_SPACE_PRE_WRAP) ? (void) (0) : __assert_fail ("white_space == CSS_WHITE_SPACE_PRE || white_space == CSS_WHITE_SPACE_PRE_LINE || white_space == CSS_WHITE_SPACE_PRE_WRAP" , "content/handlers/html/box_construct.c", 1108, __extension__ __PRETTY_FUNCTION__)); | |||
1109 | ||||
1110 | text = malloc(text_len + 1); | |||
1111 | dom_string_unref(content); | |||
1112 | ||||
1113 | if (text == NULL((void*)0)) | |||
1114 | return false0; | |||
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((void*)0)) { | |||
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((void*)0), NULL((void*)0), | |||
1160 | false0, NULL((void*)0), NULL((void*)0), NULL((void*)0), NULL((void*)0), | |||
1161 | ctx->bctx); | |||
1162 | if (props.inline_container == NULL((void*)0)) { | |||
1163 | free(text); | |||
1164 | return false0; | |||
1165 | } | |||
1166 | ||||
1167 | props.inline_container->type = | |||
1168 | BOX_INLINE_CONTAINER; | |||
1169 | ||||
1170 | box_add_child(props.containing_block, | |||
1171 | props.inline_container); | |||
1172 | } | |||
1173 | ||||
1174 | /** \todo Dropping const isn't clever */ | |||
1175 | box = box_create(NULL((void*)0), | |||
1176 | (css_computed_style *) props.parent_style, | |||
1177 | false0, props.href, props.target, props.title, | |||
1178 | NULL((void*)0), ctx->bctx); | |||
1179 | if (box == NULL((void*)0)) { | |||
1180 | free(text); | |||
1181 | return false0; | |||
1182 | } | |||
1183 | ||||
1184 | box->type = BOX_TEXT; | |||
1185 | ||||
1186 | box->text = talloc_strdup(ctx->bctx, current); | |||
1187 | if (box->text == NULL((void*)0)) { | |||
1188 | free(text); | |||
1189 | return false0; | |||
1190 | } | |||
1191 | ||||
1192 | box->length = strlen(box->text); | |||
1193 | ||||
1194 | box_add_child(props.inline_container, box); | |||
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((void*)0), NULL((void*)0), | |||
1203 | false0, NULL((void*)0), NULL((void*)0), NULL((void*)0), NULL((void*)0), | |||
1204 | ctx->bctx); | |||
1205 | if (props.inline_container == NULL((void*)0)) { | |||
1206 | free(text); | |||
1207 | return false0; | |||
1208 | } | |||
1209 | ||||
1210 | props.inline_container->type = | |||
1211 | BOX_INLINE_CONTAINER; | |||
1212 | ||||
1213 | box_add_child(props.containing_block, | |||
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 true1; | |||
1227 | } | |||
1228 | ||||
1229 | ||||
1230 | /** | |||
1231 | * Convert an ELEMENT node to a box tree fragment, | |||
1232 | * then schedule conversion of the next ELEMENT node | |||
1233 | */ | |||
1234 | static void convert_xml_to_box(struct box_construct_ctx *ctx) | |||
1235 | { | |||
1236 | dom_node *next; | |||
1237 | bool_Bool convert_children; | |||
1238 | uint32_t num_processed = 0; | |||
1239 | const uint32_t max_processed_before_yield = 10; | |||
1240 | ||||
1241 | do { | |||
1242 | convert_children = true1; | |||
1243 | ||||
1244 | assert(ctx->n != NULL)((ctx->n != ((void*)0)) ? (void) (0) : __assert_fail ("ctx->n != NULL" , "content/handlers/html/box_construct.c", 1244, __extension__ __PRETTY_FUNCTION__)); | |||
1245 | ||||
1246 | if (box_construct_element(ctx, &convert_children) == false0) { | |||
1247 | ctx->cb(ctx->content, false0); | |||
1248 | dom_node_unref(ctx->n)dom_node_unref((dom_node *) (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((void*)0)) { | |||
1256 | dom_node_type type; | |||
1257 | dom_exception err; | |||
1258 | ||||
1259 | err = dom_node_get_node_type(next, &type)dom_node_get_node_type( (dom_node *) (next), (dom_node_type * ) (&type)); | |||
1260 | if (err != DOM_NO_ERR) { | |||
1261 | ctx->cb(ctx->content, false0); | |||
1262 | dom_node_unref(next)dom_node_unref((dom_node *) (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) == false0) { | |||
1273 | ctx->cb(ctx->content, false0); | |||
1274 | dom_node_unref(ctx->n)dom_node_unref((dom_node *) (ctx->n)); | |||
1275 | free(ctx); | |||
1276 | return; | |||
1277 | } | |||
1278 | } | |||
1279 | ||||
1280 | next = next_node(next, ctx->content, true1); | |||
1281 | } | |||
1282 | ||||
1283 | ctx->n = next; | |||
1284 | ||||
1285 | if (next == NULL((void*)0)) { | |||
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 */ | |||
1296 | if (box_normalise_block(&root, ctx->root_box, | |||
1297 | ctx->content) == false0) { | |||
1298 | ctx->cb(ctx->content, false0); | |||
1299 | } else { | |||
1300 | ctx->content->layout = root.children; | |||
1301 | ctx->content->layout->parent = NULL((void*)0); | |||
1302 | ||||
1303 | ctx->cb(ctx->content, true1); | |||
1304 | } | |||
1305 | ||||
1306 | assert(ctx->n == NULL)((ctx->n == ((void*)0)) ? (void) (0) : __assert_fail ("ctx->n == NULL" , "content/handlers/html/box_construct.c", 1306, __extension__ __PRETTY_FUNCTION__)); | |||
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 */ | |||
1319 | nserror | |||
1320 | dom_to_box(dom_node *n, | |||
1321 | html_content *c, | |||
1322 | box_construct_complete_cb cb, | |||
1323 | void **box_conversion_context) | |||
1324 | { | |||
1325 | struct box_construct_ctx *ctx; | |||
1326 | ||||
1327 | assert(box_conversion_context != NULL)((box_conversion_context != ((void*)0)) ? (void) (0) : __assert_fail ("box_conversion_context != NULL", "content/handlers/html/box_construct.c" , 1327, __extension__ __PRETTY_FUNCTION__)); | |||
1328 | ||||
1329 | if (c->bctx == NULL((void*)0)) { | |||
1330 | /* create a context allocation for this box tree */ | |||
1331 | c->bctx = talloc_zero(0, int)(int *)_talloc_zero(0, sizeof(int), "int"); | |||
1332 | if (c->bctx == NULL((void*)0)) { | |||
1333 | return NSERROR_NOMEM; | |||
1334 | } | |||
1335 | } | |||
1336 | ||||
1337 | ctx = malloc(sizeof(*ctx)); | |||
1338 | if (ctx == NULL((void*)0)) { | |||
1339 | return NSERROR_NOMEM; | |||
1340 | } | |||
1341 | ||||
1342 | ctx->content = c; | |||
1343 | ctx->n = dom_node_ref(n)dom_node_ref((dom_node *) (n)); | |||
1344 | ctx->root_box = NULL((void*)0); | |||
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 */ | |||
1355 | nserror 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)dom_node_unref((dom_node *) (ctx->n)); | |||
1366 | free(ctx); | |||
1367 | ||||
1368 | return NSERROR_OK; | |||
1369 | } | |||
1370 | ||||
1371 | ||||
1372 | /* exported function documented in html/box_construct.h */ | |||
1373 | struct box *box_for_node(dom_node *n) | |||
1374 | { | |||
1375 | struct box *box = NULL((void*)0); | |||
1376 | dom_exception err; | |||
1377 | ||||
1378 | err = dom_node_get_user_data(n, corestring_dom___ns_key_box_node_data,dom_node_get_user_data( (dom_node *) (n), (corestring_dom___ns_key_box_node_data ), (void **) ((void *) &box)) | |||
1379 | (void *) &box)dom_node_get_user_data( (dom_node *) (n), (corestring_dom___ns_key_box_node_data ), (void **) ((void *) &box)); | |||
1380 | if (err != DOM_NO_ERR) | |||
1381 | return NULL((void*)0); | |||
1382 | ||||
1383 | return box; | |||
1384 | } | |||
1385 | ||||
1386 | /* exported function documented in html/box_construct.h */ | |||
1387 | bool_Bool | |||
1388 | box_extract_link(const html_content *content, | |||
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 false0; | |||
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 == false0) { | |||
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((void*)0); | |||
1449 | return false0; | |||
1450 | } | |||
1451 | ||||
1452 | return true1; | |||
1453 | } |