| 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 | } |