| File: | content/handlers/html/layout_internal.h |
| Warning: | line 163, column 31 Access to field 'style' results in a dereference of a null pointer (loaded from variable 'b') |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
| 1 | /* | ||||
| 2 | * Copyright 2022 Michael Drake <tlsa@netsurf-browser.org> | ||||
| 3 | * | ||||
| 4 | * This file is part of NetSurf, http://www.netsurf-browser.org/ | ||||
| 5 | * | ||||
| 6 | * NetSurf is free software; you can redistribute it and/or modify | ||||
| 7 | * it under the terms of the GNU General Public License as published by | ||||
| 8 | * the Free Software Foundation; version 2 of the License. | ||||
| 9 | * | ||||
| 10 | * NetSurf is distributed in the hope that it will be useful, | ||||
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||
| 13 | * GNU General Public License for more details. | ||||
| 14 | * | ||||
| 15 | * You should have received a copy of the GNU General Public License | ||||
| 16 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||||
| 17 | */ | ||||
| 18 | |||||
| 19 | /** | ||||
| 20 | * \file | ||||
| 21 | * HTML layout implementation: display: flex. | ||||
| 22 | * | ||||
| 23 | * Layout is carried out in two stages: | ||||
| 24 | * | ||||
| 25 | * 1. + calculation of minimum / maximum box widths, and | ||||
| 26 | * + determination of whether block level boxes will have >zero height | ||||
| 27 | * | ||||
| 28 | * 2. + layout (position and dimensions) | ||||
| 29 | * | ||||
| 30 | * In most cases the functions for the two stages are a corresponding pair | ||||
| 31 | * layout_minmax_X() and layout_X(). | ||||
| 32 | */ | ||||
| 33 | |||||
| 34 | #include <string.h> | ||||
| 35 | |||||
| 36 | #include "utils/log.h" | ||||
| 37 | #include "utils/utils.h" | ||||
| 38 | |||||
| 39 | #include "html/box.h" | ||||
| 40 | #include "html/html.h" | ||||
| 41 | #include "html/private.h" | ||||
| 42 | #include "html/box_inspect.h" | ||||
| 43 | #include "html/layout_internal.h" | ||||
| 44 | |||||
| 45 | /** | ||||
| 46 | * Flex item data | ||||
| 47 | */ | ||||
| 48 | struct flex_item_data { | ||||
| 49 | enum css_flex_basis_e basis; | ||||
| 50 | css_fixed basis_length; | ||||
| 51 | css_unit basis_unit; | ||||
| 52 | struct box *box; | ||||
| 53 | |||||
| 54 | css_fixed shrink; | ||||
| 55 | css_fixed grow; | ||||
| 56 | |||||
| 57 | int min_main; | ||||
| 58 | int max_main; | ||||
| 59 | int min_cross; | ||||
| 60 | int max_cross; | ||||
| 61 | |||||
| 62 | int target_main_size; | ||||
| 63 | int base_size; | ||||
| 64 | int main_size; | ||||
| 65 | size_t line; | ||||
| 66 | |||||
| 67 | bool_Bool freeze; | ||||
| 68 | bool_Bool min_violation; | ||||
| 69 | bool_Bool max_violation; | ||||
| 70 | }; | ||||
| 71 | |||||
| 72 | /** | ||||
| 73 | * Flex line data | ||||
| 74 | */ | ||||
| 75 | struct flex_line_data { | ||||
| 76 | int main_size; | ||||
| 77 | int cross_size; | ||||
| 78 | |||||
| 79 | int used_main_size; | ||||
| 80 | int main_auto_margin_count; | ||||
| 81 | |||||
| 82 | int pos; | ||||
| 83 | |||||
| 84 | size_t first; | ||||
| 85 | size_t count; | ||||
| 86 | size_t frozen; | ||||
| 87 | }; | ||||
| 88 | |||||
| 89 | /** | ||||
| 90 | * Flex layout context | ||||
| 91 | */ | ||||
| 92 | struct flex_ctx { | ||||
| 93 | html_content *content; | ||||
| 94 | const struct box *flex; | ||||
| 95 | const css_unit_ctx *unit_len_ctx; | ||||
| 96 | |||||
| 97 | int main_size; | ||||
| 98 | int cross_size; | ||||
| 99 | |||||
| 100 | int available_main; | ||||
| 101 | int available_cross; | ||||
| 102 | |||||
| 103 | bool_Bool horizontal; | ||||
| 104 | bool_Bool main_reversed; | ||||
| 105 | enum css_flex_wrap_e wrap; | ||||
| 106 | |||||
| 107 | struct flex_items { | ||||
| 108 | size_t count; | ||||
| 109 | struct flex_item_data *data; | ||||
| 110 | } item; | ||||
| 111 | |||||
| 112 | struct flex_lines { | ||||
| 113 | size_t count; | ||||
| 114 | size_t alloc; | ||||
| 115 | struct flex_line_data *data; | ||||
| 116 | } line; | ||||
| 117 | }; | ||||
| 118 | |||||
| 119 | /** | ||||
| 120 | * Destroy a flex layout context | ||||
| 121 | * | ||||
| 122 | * \param[in] ctx Flex layout context | ||||
| 123 | */ | ||||
| 124 | static void layout_flex_ctx__destroy(struct flex_ctx *ctx) | ||||
| 125 | { | ||||
| 126 | if (ctx != NULL((void*)0)) { | ||||
| 127 | free(ctx->item.data); | ||||
| 128 | free(ctx->line.data); | ||||
| 129 | free(ctx); | ||||
| 130 | } | ||||
| 131 | } | ||||
| 132 | |||||
| 133 | /** | ||||
| 134 | * Create a flex layout context | ||||
| 135 | * | ||||
| 136 | * \param[in] content HTML content containing flex box | ||||
| 137 | * \param[in] flex Box to create layout context for | ||||
| 138 | * \return flex layout context or NULL on error | ||||
| 139 | */ | ||||
| 140 | static struct flex_ctx *layout_flex_ctx__create( | ||||
| 141 | html_content *content, | ||||
| 142 | const struct box *flex) | ||||
| 143 | { | ||||
| 144 | struct flex_ctx *ctx; | ||||
| 145 | |||||
| 146 | ctx = calloc(1, sizeof(*ctx)); | ||||
| 147 | if (ctx == NULL((void*)0)) { | ||||
| 148 | return NULL((void*)0); | ||||
| 149 | } | ||||
| 150 | ctx->line.alloc = 1; | ||||
| 151 | |||||
| 152 | ctx->item.count = box_count_children(flex); | ||||
| 153 | ctx->item.data = calloc(ctx->item.count, sizeof(*ctx->item.data)); | ||||
| 154 | if (ctx->item.data == NULL((void*)0)) { | ||||
| 155 | layout_flex_ctx__destroy(ctx); | ||||
| 156 | return NULL((void*)0); | ||||
| 157 | } | ||||
| 158 | |||||
| 159 | ctx->line.alloc = 1; | ||||
| 160 | ctx->line.data = calloc(ctx->line.alloc, sizeof(*ctx->line.data)); | ||||
| 161 | if (ctx->line.data == NULL((void*)0)) { | ||||
| 162 | layout_flex_ctx__destroy(ctx); | ||||
| 163 | return NULL((void*)0); | ||||
| 164 | } | ||||
| 165 | |||||
| 166 | ctx->flex = flex; | ||||
| 167 | ctx->content = content; | ||||
| 168 | ctx->unit_len_ctx = &content->unit_len_ctx; | ||||
| 169 | |||||
| 170 | ctx->wrap = css_computed_flex_wrap(flex->style); | ||||
| 171 | ctx->horizontal = lh__flex_main_is_horizontal(flex); | ||||
| 172 | ctx->main_reversed = lh__flex_direction_reversed(flex); | ||||
| 173 | |||||
| 174 | return ctx; | ||||
| 175 | } | ||||
| 176 | |||||
| 177 | /** | ||||
| 178 | * Find box side representing the start of flex container in main direction. | ||||
| 179 | * | ||||
| 180 | * \param[in] ctx Flex layout context. | ||||
| 181 | * \return the start side. | ||||
| 182 | */ | ||||
| 183 | static enum box_side layout_flex__main_start_side( | ||||
| 184 | const struct flex_ctx *ctx) | ||||
| 185 | { | ||||
| 186 | if (ctx->horizontal) { | ||||
| 187 | return (ctx->main_reversed) ? RIGHT : LEFT; | ||||
| 188 | } else { | ||||
| 189 | return (ctx->main_reversed) ? BOTTOM : TOP; | ||||
| 190 | } | ||||
| 191 | } | ||||
| 192 | |||||
| 193 | /** | ||||
| 194 | * Find box side representing the end of flex container in main direction. | ||||
| 195 | * | ||||
| 196 | * \param[in] ctx Flex layout context. | ||||
| 197 | * \return the end side. | ||||
| 198 | */ | ||||
| 199 | static enum box_side layout_flex__main_end_side( | ||||
| 200 | const struct flex_ctx *ctx) | ||||
| 201 | { | ||||
| 202 | if (ctx->horizontal) { | ||||
| 203 | return (ctx->main_reversed) ? LEFT : RIGHT; | ||||
| 204 | } else { | ||||
| 205 | return (ctx->main_reversed) ? TOP : BOTTOM; | ||||
| 206 | } | ||||
| 207 | } | ||||
| 208 | |||||
| 209 | /** | ||||
| 210 | * Perform layout on a flex item | ||||
| 211 | * | ||||
| 212 | * \param[in] ctx Flex layout context | ||||
| 213 | * \param[in] item Item to lay out | ||||
| 214 | * \param[in] available_width Available width for item in pixels | ||||
| 215 | * \return true on success false on failure | ||||
| 216 | */ | ||||
| 217 | static bool_Bool layout_flex_item( | ||||
| |||||
| 218 | const struct flex_ctx *ctx, | ||||
| 219 | const struct flex_item_data *item, | ||||
| 220 | int available_width) | ||||
| 221 | { | ||||
| 222 | bool_Bool success; | ||||
| 223 | struct box *b = item->box; | ||||
| 224 | |||||
| 225 | switch (b->type) { | ||||
| 226 | case BOX_BLOCK: | ||||
| 227 | success = layout_block_context(b, -1, ctx->content); | ||||
| 228 | break; | ||||
| 229 | case BOX_TABLE: | ||||
| 230 | b->float_container = b->parent; | ||||
| 231 | success = layout_table(b, available_width, ctx->content); | ||||
| 232 | b->float_container = NULL((void*)0); | ||||
| 233 | break; | ||||
| 234 | case BOX_FLEX: | ||||
| 235 | b->float_container = b->parent; | ||||
| 236 | success = layout_flex(b, available_width, ctx->content); | ||||
| 237 | b->float_container = NULL((void*)0); | ||||
| 238 | break; | ||||
| 239 | default: | ||||
| 240 | assert(0 && "Bad flex item back type")((0 && "Bad flex item back type") ? (void) (0) : __assert_fail ("0 && \"Bad flex item back type\"", "content/handlers/html/layout_flex.c" , 240, __extension__ __PRETTY_FUNCTION__)); | ||||
| 241 | success = false0; | ||||
| 242 | break; | ||||
| 243 | } | ||||
| 244 | |||||
| 245 | if (!success) { | ||||
| 246 | NSLOG(flex, ERROR, "box %p: layout failed", b)do { if (NSLOG_LEVEL_ERROR >= NSLOG_LEVEL_VERBOSE) { static nslog_entry_context_t _nslog_ctx = { &__nslog_category_flex , NSLOG_LEVEL_ERROR, "content/handlers/html/layout_flex.c", sizeof ("content/handlers/html/layout_flex.c") - 1, __PRETTY_FUNCTION__ , sizeof(__PRETTY_FUNCTION__) - 1, 246, }; nslog__log(&_nslog_ctx , "box %p: layout failed", b); } } while(0); | ||||
| 247 | } | ||||
| 248 | |||||
| 249 | return success; | ||||
| 250 | } | ||||
| 251 | |||||
| 252 | /** | ||||
| 253 | * Calculate an item's base and target main sizes. | ||||
| 254 | * | ||||
| 255 | * \param[in] ctx Flex layout context | ||||
| 256 | * \param[in] item Item to get sizes of | ||||
| 257 | * \param[in] available_width Available width in pixels | ||||
| 258 | * \return true on success false on failure | ||||
| 259 | */ | ||||
| 260 | static inline bool_Bool layout_flex__base_and_main_sizes( | ||||
| 261 | const struct flex_ctx *ctx, | ||||
| 262 | struct flex_item_data *item, | ||||
| 263 | int available_width) | ||||
| 264 | { | ||||
| 265 | struct box *b = item->box; | ||||
| 266 | int content_min_width = b->min_width; | ||||
| 267 | int content_max_width = b->max_width; | ||||
| 268 | int delta_outer_main = lh__delta_outer_main(ctx->flex, b); | ||||
| 269 | |||||
| 270 | NSLOG(flex, DEEPDEBUG, "box %p: delta_outer_main: %i",do { if (NSLOG_LEVEL_DEEPDEBUG >= NSLOG_LEVEL_VERBOSE) { static nslog_entry_context_t _nslog_ctx = { &__nslog_category_flex , NSLOG_LEVEL_DEEPDEBUG, "content/handlers/html/layout_flex.c" , sizeof("content/handlers/html/layout_flex.c") - 1, __PRETTY_FUNCTION__ , sizeof(__PRETTY_FUNCTION__) - 1, 271, }; nslog__log(&_nslog_ctx , "box %p: delta_outer_main: %i", b, delta_outer_main); } } while (0) | ||||
| 271 | b, delta_outer_main)do { if (NSLOG_LEVEL_DEEPDEBUG >= NSLOG_LEVEL_VERBOSE) { static nslog_entry_context_t _nslog_ctx = { &__nslog_category_flex , NSLOG_LEVEL_DEEPDEBUG, "content/handlers/html/layout_flex.c" , sizeof("content/handlers/html/layout_flex.c") - 1, __PRETTY_FUNCTION__ , sizeof(__PRETTY_FUNCTION__) - 1, 271, }; nslog__log(&_nslog_ctx , "box %p: delta_outer_main: %i", b, delta_outer_main); } } while (0); | ||||
| 272 | |||||
| 273 | if (item->basis == CSS_FLEX_BASIS_SET) { | ||||
| 274 | if (item->basis_unit == CSS_UNIT_PCT) { | ||||
| 275 | item->base_size = FPCT_OF_INT_TOINT(((((css_divide_fixed(((item->basis_length * available_width )), (0x00019000)))) >> 10)) | ||||
| 276 | item->basis_length,((((css_divide_fixed(((item->basis_length * available_width )), (0x00019000)))) >> 10)) | ||||
| 277 | available_width)((((css_divide_fixed(((item->basis_length * available_width )), (0x00019000)))) >> 10)); | ||||
| 278 | } else { | ||||
| 279 | item->base_size = FIXTOINT(css_unit_len2device_px(((css_unit_len2device_px( b->style, ctx->unit_len_ctx, item ->basis_length, item->basis_unit)) >> 10) | ||||
| 280 | b->style, ctx->unit_len_ctx,((css_unit_len2device_px( b->style, ctx->unit_len_ctx, item ->basis_length, item->basis_unit)) >> 10) | ||||
| 281 | item->basis_length,((css_unit_len2device_px( b->style, ctx->unit_len_ctx, item ->basis_length, item->basis_unit)) >> 10) | ||||
| 282 | item->basis_unit))((css_unit_len2device_px( b->style, ctx->unit_len_ctx, item ->basis_length, item->basis_unit)) >> 10); | ||||
| 283 | } | ||||
| 284 | |||||
| 285 | } else if (item->basis == CSS_FLEX_BASIS_AUTO) { | ||||
| 286 | item->base_size = ctx->horizontal ? b->width : b->height; | ||||
| 287 | } else { | ||||
| 288 | item->base_size = AUTO(-2147483647 -1); | ||||
| 289 | } | ||||
| 290 | |||||
| 291 | if (ctx->horizontal == false0) { | ||||
| 292 | if (b->width == AUTO(-2147483647 -1)) { | ||||
| 293 | b->width = min(max(content_min_width, available_width),((((((content_min_width)>(available_width))?(content_min_width ):(available_width)))<(content_max_width))?((((content_min_width )>(available_width))?(content_min_width):(available_width) )):(content_max_width)) | ||||
| 294 | content_max_width)((((((content_min_width)>(available_width))?(content_min_width ):(available_width)))<(content_max_width))?((((content_min_width )>(available_width))?(content_min_width):(available_width) )):(content_max_width)); | ||||
| 295 | b->width -= lh__delta_outer_width(b); | ||||
| 296 | } | ||||
| 297 | |||||
| 298 | if (!layout_flex_item(ctx, item, b->width)) { | ||||
| 299 | return false0; | ||||
| 300 | } | ||||
| 301 | } | ||||
| 302 | |||||
| 303 | if (item->base_size == AUTO(-2147483647 -1)) { | ||||
| 304 | if (ctx->horizontal == false0) { | ||||
| 305 | item->base_size = b->height; | ||||
| 306 | } else { | ||||
| 307 | item->base_size = content_max_width - delta_outer_main; | ||||
| 308 | } | ||||
| 309 | } | ||||
| 310 | |||||
| 311 | item->base_size += delta_outer_main; | ||||
| 312 | |||||
| 313 | if (ctx->horizontal) { | ||||
| 314 | item->base_size = min(item->base_size, available_width)(((item->base_size)<(available_width))?(item->base_size ):(available_width)); | ||||
| 315 | item->base_size = max(item->base_size, content_min_width)(((item->base_size)>(content_min_width))?(item->base_size ):(content_min_width)); | ||||
| 316 | } | ||||
| 317 | |||||
| 318 | item->target_main_size = item->base_size; | ||||
| 319 | item->main_size = item->base_size; | ||||
| 320 | |||||
| 321 | if (item->max_main > 0 && | ||||
| 322 | item->main_size > item->max_main + delta_outer_main) { | ||||
| 323 | item->main_size = item->max_main + delta_outer_main; | ||||
| 324 | } | ||||
| 325 | |||||
| 326 | if (item->main_size < item->min_main + delta_outer_main) { | ||||
| 327 | item->main_size = item->min_main + delta_outer_main; | ||||
| 328 | } | ||||
| 329 | |||||
| 330 | NSLOG(flex, DEEPDEBUG, "flex-item box: %p: base_size: %i, main_size %i",do { if (NSLOG_LEVEL_DEEPDEBUG >= NSLOG_LEVEL_VERBOSE) { static nslog_entry_context_t _nslog_ctx = { &__nslog_category_flex , NSLOG_LEVEL_DEEPDEBUG, "content/handlers/html/layout_flex.c" , sizeof("content/handlers/html/layout_flex.c") - 1, __PRETTY_FUNCTION__ , sizeof(__PRETTY_FUNCTION__) - 1, 331, }; nslog__log(&_nslog_ctx , "flex-item box: %p: base_size: %i, main_size %i", b, item-> base_size, item->main_size); } } while(0) | ||||
| 331 | b, item->base_size, item->main_size)do { if (NSLOG_LEVEL_DEEPDEBUG >= NSLOG_LEVEL_VERBOSE) { static nslog_entry_context_t _nslog_ctx = { &__nslog_category_flex , NSLOG_LEVEL_DEEPDEBUG, "content/handlers/html/layout_flex.c" , sizeof("content/handlers/html/layout_flex.c") - 1, __PRETTY_FUNCTION__ , sizeof(__PRETTY_FUNCTION__) - 1, 331, }; nslog__log(&_nslog_ctx , "flex-item box: %p: base_size: %i, main_size %i", b, item-> base_size, item->main_size); } } while(0); | ||||
| 332 | |||||
| 333 | return true1; | ||||
| 334 | } | ||||
| 335 | |||||
| 336 | /** | ||||
| 337 | * Fill out all item's data in a flex container. | ||||
| 338 | * | ||||
| 339 | * \param[in] ctx Flex layout context | ||||
| 340 | * \param[in] flex Flex box | ||||
| 341 | * \param[in] available_width Available width in pixels | ||||
| 342 | */ | ||||
| 343 | static void layout_flex_ctx__populate_item_data( | ||||
| 344 | const struct flex_ctx *ctx, | ||||
| 345 | const struct box *flex, | ||||
| 346 | int available_width) | ||||
| 347 | { | ||||
| 348 | size_t i = 0; | ||||
| 349 | bool_Bool horizontal = ctx->horizontal; | ||||
| 350 | |||||
| 351 | for (struct box *b = flex->children; b != NULL((void*)0); b = b->next) { | ||||
| 352 | struct flex_item_data *item = &ctx->item.data[i++]; | ||||
| 353 | |||||
| 354 | b->float_container = b->parent; | ||||
| 355 | layout_find_dimensions(ctx->unit_len_ctx, available_width, -1, | ||||
| 356 | b, b->style, &b->width, &b->height, | ||||
| 357 | horizontal ? &item->max_main : &item->max_cross, | ||||
| 358 | horizontal ? &item->min_main : &item->min_cross, | ||||
| 359 | horizontal ? &item->max_cross : &item->max_main, | ||||
| 360 | horizontal ? &item->min_cross : &item->min_main, | ||||
| 361 | b->margin, b->padding, b->border); | ||||
| 362 | b->float_container = NULL((void*)0); | ||||
| 363 | |||||
| 364 | NSLOG(flex, DEEPDEBUG, "flex-item box: %p: width: %i",do { if (NSLOG_LEVEL_DEEPDEBUG >= NSLOG_LEVEL_VERBOSE) { static nslog_entry_context_t _nslog_ctx = { &__nslog_category_flex , NSLOG_LEVEL_DEEPDEBUG, "content/handlers/html/layout_flex.c" , sizeof("content/handlers/html/layout_flex.c") - 1, __PRETTY_FUNCTION__ , sizeof(__PRETTY_FUNCTION__) - 1, 365, }; nslog__log(&_nslog_ctx , "flex-item box: %p: width: %i", b, b->width); } } while( 0) | ||||
| 365 | b, b->width)do { if (NSLOG_LEVEL_DEEPDEBUG >= NSLOG_LEVEL_VERBOSE) { static nslog_entry_context_t _nslog_ctx = { &__nslog_category_flex , NSLOG_LEVEL_DEEPDEBUG, "content/handlers/html/layout_flex.c" , sizeof("content/handlers/html/layout_flex.c") - 1, __PRETTY_FUNCTION__ , sizeof(__PRETTY_FUNCTION__) - 1, 365, }; nslog__log(&_nslog_ctx , "flex-item box: %p: width: %i", b, b->width); } } while( 0); | ||||
| 366 | |||||
| 367 | item->box = b; | ||||
| 368 | item->basis = css_computed_flex_basis(b->style, | ||||
| 369 | &item->basis_length, &item->basis_unit); | ||||
| 370 | |||||
| 371 | css_computed_flex_shrink(b->style, &item->shrink); | ||||
| 372 | css_computed_flex_grow(b->style, &item->grow); | ||||
| 373 | |||||
| 374 | layout_flex__base_and_main_sizes(ctx, item, available_width); | ||||
| 375 | } | ||||
| 376 | } | ||||
| 377 | |||||
| 378 | /** | ||||
| 379 | * Ensure context's lines array has a free space | ||||
| 380 | * | ||||
| 381 | * \param[in] ctx Flex layout context | ||||
| 382 | * \return true on success false on out of memory | ||||
| 383 | */ | ||||
| 384 | static bool_Bool layout_flex_ctx__ensure_line(struct flex_ctx *ctx) | ||||
| 385 | { | ||||
| 386 | struct flex_line_data *temp; | ||||
| 387 | size_t line_alloc = ctx->line.alloc * 2; | ||||
| 388 | |||||
| 389 | if (ctx->line.alloc > ctx->line.count) { | ||||
| 390 | return true1; | ||||
| 391 | } | ||||
| 392 | |||||
| 393 | temp = realloc(ctx->line.data, sizeof(*ctx->line.data) * line_alloc); | ||||
| 394 | if (temp == NULL((void*)0)) { | ||||
| 395 | return false0; | ||||
| 396 | } | ||||
| 397 | ctx->line.data = temp; | ||||
| 398 | |||||
| 399 | memset(ctx->line.data + ctx->line.alloc, 0, | ||||
| 400 | sizeof(*ctx->line.data) * (line_alloc - ctx->line.alloc)); | ||||
| 401 | ctx->line.alloc = line_alloc; | ||||
| 402 | |||||
| 403 | return true1; | ||||
| 404 | } | ||||
| 405 | |||||
| 406 | /** | ||||
| 407 | * Assigns flex items to the line and returns the line | ||||
| 408 | * | ||||
| 409 | * \param[in] ctx Flex layout context | ||||
| 410 | * \param[in] item_index Index to first item to assign to this line | ||||
| 411 | * \return Pointer to the new line, or NULL on error. | ||||
| 412 | */ | ||||
| 413 | static struct flex_line_data *layout_flex__build_line(struct flex_ctx *ctx, | ||||
| 414 | size_t item_index) | ||||
| 415 | { | ||||
| 416 | enum box_side start_side = layout_flex__main_start_side(ctx); | ||||
| 417 | enum box_side end_side = layout_flex__main_end_side(ctx); | ||||
| 418 | struct flex_line_data *line; | ||||
| 419 | int used_main = 0; | ||||
| 420 | |||||
| 421 | if (!layout_flex_ctx__ensure_line(ctx)) { | ||||
| 422 | return NULL((void*)0); | ||||
| 423 | } | ||||
| 424 | |||||
| 425 | line = &ctx->line.data[ctx->line.count]; | ||||
| 426 | line->first = item_index; | ||||
| 427 | |||||
| 428 | NSLOG(flex, DEEPDEBUG, "flex container %p: available main: %i",do { if (NSLOG_LEVEL_DEEPDEBUG >= NSLOG_LEVEL_VERBOSE) { static nslog_entry_context_t _nslog_ctx = { &__nslog_category_flex , NSLOG_LEVEL_DEEPDEBUG, "content/handlers/html/layout_flex.c" , sizeof("content/handlers/html/layout_flex.c") - 1, __PRETTY_FUNCTION__ , sizeof(__PRETTY_FUNCTION__) - 1, 429, }; nslog__log(&_nslog_ctx , "flex container %p: available main: %i", ctx->flex, ctx-> available_main); } } while(0) | ||||
| 429 | ctx->flex, ctx->available_main)do { if (NSLOG_LEVEL_DEEPDEBUG >= NSLOG_LEVEL_VERBOSE) { static nslog_entry_context_t _nslog_ctx = { &__nslog_category_flex , NSLOG_LEVEL_DEEPDEBUG, "content/handlers/html/layout_flex.c" , sizeof("content/handlers/html/layout_flex.c") - 1, __PRETTY_FUNCTION__ , sizeof(__PRETTY_FUNCTION__) - 1, 429, }; nslog__log(&_nslog_ctx , "flex container %p: available main: %i", ctx->flex, ctx-> available_main); } } while(0); | ||||
| 430 | |||||
| 431 | while (item_index < ctx->item.count) { | ||||
| 432 | struct flex_item_data *item = &ctx->item.data[item_index]; | ||||
| 433 | struct box *b = item->box; | ||||
| 434 | int pos_main; | ||||
| 435 | |||||
| 436 | pos_main = ctx->horizontal
| ||||
| 437 | item->main_size : | ||||
| 438 | b->height + lh__delta_outer_main(ctx->flex, b); | ||||
| 439 | |||||
| 440 | if (ctx->wrap == CSS_FLEX_WRAP_NOWRAP || | ||||
| 441 | pos_main + used_main <= ctx->available_main || | ||||
| 442 | lh__box_is_absolute(item->box) || | ||||
| 443 | ctx->available_main == AUTO(-2147483647 -1) || | ||||
| 444 | line->count == 0 || | ||||
| 445 | pos_main == 0) { | ||||
| 446 | if (lh__box_is_absolute(item->box) == false0) { | ||||
| 447 | line->main_size += item->main_size; | ||||
| 448 | used_main += pos_main; | ||||
| 449 | |||||
| 450 | if (b->margin[start_side] == AUTO(-2147483647 -1)) { | ||||
| 451 | line->main_auto_margin_count++; | ||||
| 452 | } | ||||
| 453 | if (b->margin[end_side] == AUTO(-2147483647 -1)) { | ||||
| 454 | line->main_auto_margin_count++; | ||||
| 455 | } | ||||
| 456 | } | ||||
| 457 | item->line = ctx->line.count; | ||||
| 458 | line->count++; | ||||
| 459 | item_index++; | ||||
| 460 | } else { | ||||
| 461 | break; | ||||
| 462 | } | ||||
| 463 | } | ||||
| 464 | |||||
| 465 | if (line->count > 0) { | ||||
| 466 | ctx->line.count++; | ||||
| 467 | } else { | ||||
| 468 | NSLOG(layout, ERROR, "Failed to fit any flex items")do { if (NSLOG_LEVEL_ERROR >= NSLOG_LEVEL_VERBOSE) { static nslog_entry_context_t _nslog_ctx = { &__nslog_category_layout , NSLOG_LEVEL_ERROR, "content/handlers/html/layout_flex.c", sizeof ("content/handlers/html/layout_flex.c") - 1, __PRETTY_FUNCTION__ , sizeof(__PRETTY_FUNCTION__) - 1, 468, }; nslog__log(&_nslog_ctx , "Failed to fit any flex items"); } } while(0); | ||||
| 469 | } | ||||
| 470 | |||||
| 471 | return line; | ||||
| 472 | } | ||||
| 473 | |||||
| 474 | /** | ||||
| 475 | * Freeze an item on a line | ||||
| 476 | * | ||||
| 477 | * \param[in] line Line to containing item | ||||
| 478 | * \param[in] item Item to freeze | ||||
| 479 | */ | ||||
| 480 | static inline void layout_flex__item_freeze( | ||||
| 481 | struct flex_line_data *line, | ||||
| 482 | struct flex_item_data *item) | ||||
| 483 | { | ||||
| 484 | item->freeze = true1; | ||||
| 485 | line->frozen++; | ||||
| 486 | |||||
| 487 | if (!lh__box_is_absolute(item->box)){ | ||||
| 488 | line->used_main_size += item->target_main_size; | ||||
| 489 | } | ||||
| 490 | |||||
| 491 | NSLOG(flex, DEEPDEBUG, "flex-item box: %p: "do { if (NSLOG_LEVEL_DEEPDEBUG >= NSLOG_LEVEL_VERBOSE) { static nslog_entry_context_t _nslog_ctx = { &__nslog_category_flex , NSLOG_LEVEL_DEEPDEBUG, "content/handlers/html/layout_flex.c" , sizeof("content/handlers/html/layout_flex.c") - 1, __PRETTY_FUNCTION__ , sizeof(__PRETTY_FUNCTION__) - 1, 493, }; nslog__log(&_nslog_ctx , "flex-item box: %p: " "Frozen at target_main_size: %i", item ->box, item->target_main_size); } } while(0) | ||||
| 492 | "Frozen at target_main_size: %i",do { if (NSLOG_LEVEL_DEEPDEBUG >= NSLOG_LEVEL_VERBOSE) { static nslog_entry_context_t _nslog_ctx = { &__nslog_category_flex , NSLOG_LEVEL_DEEPDEBUG, "content/handlers/html/layout_flex.c" , sizeof("content/handlers/html/layout_flex.c") - 1, __PRETTY_FUNCTION__ , sizeof(__PRETTY_FUNCTION__) - 1, 493, }; nslog__log(&_nslog_ctx , "flex-item box: %p: " "Frozen at target_main_size: %i", item ->box, item->target_main_size); } } while(0) | ||||
| 493 | item->box, item->target_main_size)do { if (NSLOG_LEVEL_DEEPDEBUG >= NSLOG_LEVEL_VERBOSE) { static nslog_entry_context_t _nslog_ctx = { &__nslog_category_flex , NSLOG_LEVEL_DEEPDEBUG, "content/handlers/html/layout_flex.c" , sizeof("content/handlers/html/layout_flex.c") - 1, __PRETTY_FUNCTION__ , sizeof(__PRETTY_FUNCTION__) - 1, 493, }; nslog__log(&_nslog_ctx , "flex-item box: %p: " "Frozen at target_main_size: %i", item ->box, item->target_main_size); } } while(0); | ||||
| 494 | } | ||||
| 495 | |||||
| 496 | /** | ||||
| 497 | * Calculate remaining free space and unfrozen item factor sum | ||||
| 498 | * | ||||
| 499 | * \param[in] ctx Flex layout context | ||||
| 500 | * \param[in] line Line to calculate free space on | ||||
| 501 | * \param[out] unfrozen_factor_sum Returns sum of unfrozen item's flex factors | ||||
| 502 | * \param[in] initial_free_main Initial free space in main direction | ||||
| 503 | * \param[in] available_main Available space in main direction | ||||
| 504 | * \param[in] grow Whether to grow or shrink | ||||
| 505 | * return remaining free space on line | ||||
| 506 | */ | ||||
| 507 | static inline int layout_flex__remaining_free_main( | ||||
| 508 | struct flex_ctx *ctx, | ||||
| 509 | struct flex_line_data *line, | ||||
| 510 | css_fixed *unfrozen_factor_sum, | ||||
| 511 | int initial_free_main, | ||||
| 512 | int available_main, | ||||
| 513 | bool_Bool grow) | ||||
| 514 | { | ||||
| 515 | int remaining_free_main = available_main; | ||||
| 516 | size_t item_count = line->first + line->count; | ||||
| 517 | |||||
| 518 | *unfrozen_factor_sum = 0; | ||||
| 519 | |||||
| 520 | for (size_t i = line->first; i < item_count; i++) { | ||||
| 521 | struct flex_item_data *item = &ctx->item.data[i]; | ||||
| 522 | |||||
| 523 | if (item->freeze) { | ||||
| 524 | remaining_free_main -= item->target_main_size; | ||||
| 525 | } else { | ||||
| 526 | remaining_free_main -= item->base_size; | ||||
| 527 | |||||
| 528 | *unfrozen_factor_sum += grow ? | ||||
| 529 | item->grow : item->shrink; | ||||
| 530 | } | ||||
| 531 | } | ||||
| 532 | |||||
| 533 | if (*unfrozen_factor_sum < F_10x00000400) { | ||||
| 534 | int free_space = FIXTOINT(FMUL(INTTOFIX(initial_free_main),(((css_multiply_fixed(((css_int_to_fixed(initial_free_main))) , (*unfrozen_factor_sum)))) >> 10) | ||||
| 535 | *unfrozen_factor_sum))(((css_multiply_fixed(((css_int_to_fixed(initial_free_main))) , (*unfrozen_factor_sum)))) >> 10); | ||||
| 536 | |||||
| 537 | if (free_space < remaining_free_main) { | ||||
| 538 | remaining_free_main = free_space; | ||||
| 539 | } | ||||
| 540 | } | ||||
| 541 | |||||
| 542 | NSLOG(flex, DEEPDEBUG, "Remaining free space: %i",do { if (NSLOG_LEVEL_DEEPDEBUG >= NSLOG_LEVEL_VERBOSE) { static nslog_entry_context_t _nslog_ctx = { &__nslog_category_flex , NSLOG_LEVEL_DEEPDEBUG, "content/handlers/html/layout_flex.c" , sizeof("content/handlers/html/layout_flex.c") - 1, __PRETTY_FUNCTION__ , sizeof(__PRETTY_FUNCTION__) - 1, 543, }; nslog__log(&_nslog_ctx , "Remaining free space: %i", remaining_free_main); } } while (0) | ||||
| 543 | remaining_free_main)do { if (NSLOG_LEVEL_DEEPDEBUG >= NSLOG_LEVEL_VERBOSE) { static nslog_entry_context_t _nslog_ctx = { &__nslog_category_flex , NSLOG_LEVEL_DEEPDEBUG, "content/handlers/html/layout_flex.c" , sizeof("content/handlers/html/layout_flex.c") - 1, __PRETTY_FUNCTION__ , sizeof(__PRETTY_FUNCTION__) - 1, 543, }; nslog__log(&_nslog_ctx , "Remaining free space: %i", remaining_free_main); } } while (0); | ||||
| 544 | |||||
| 545 | return remaining_free_main; | ||||
| 546 | } | ||||
| 547 | |||||
| 548 | /** | ||||
| 549 | * Clamp flex item target main size and get min/max violations | ||||
| 550 | * | ||||
| 551 | * \param[in] ctx Flex layout context | ||||
| 552 | * \param[in] line Line to align items on | ||||
| 553 | * return total violation in pixels | ||||
| 554 | */ | ||||
| 555 | static inline int layout_flex__get_min_max_violations( | ||||
| 556 | struct flex_ctx *ctx, | ||||
| 557 | struct flex_line_data *line) | ||||
| 558 | { | ||||
| 559 | |||||
| 560 | int total_violation = 0; | ||||
| 561 | size_t item_count = line->first + line->count; | ||||
| 562 | |||||
| 563 | for (size_t i = line->first; i < item_count; i++) { | ||||
| 564 | struct flex_item_data *item = &ctx->item.data[i]; | ||||
| 565 | int target_main_size = item->target_main_size; | ||||
| 566 | |||||
| 567 | NSLOG(flex, DEEPDEBUG, "item %p: target_main_size: %i",do { if (NSLOG_LEVEL_DEEPDEBUG >= NSLOG_LEVEL_VERBOSE) { static nslog_entry_context_t _nslog_ctx = { &__nslog_category_flex , NSLOG_LEVEL_DEEPDEBUG, "content/handlers/html/layout_flex.c" , sizeof("content/handlers/html/layout_flex.c") - 1, __PRETTY_FUNCTION__ , sizeof(__PRETTY_FUNCTION__) - 1, 568, }; nslog__log(&_nslog_ctx , "item %p: target_main_size: %i", item->box, target_main_size ); } } while(0) | ||||
| 568 | item->box, target_main_size)do { if (NSLOG_LEVEL_DEEPDEBUG >= NSLOG_LEVEL_VERBOSE) { static nslog_entry_context_t _nslog_ctx = { &__nslog_category_flex , NSLOG_LEVEL_DEEPDEBUG, "content/handlers/html/layout_flex.c" , sizeof("content/handlers/html/layout_flex.c") - 1, __PRETTY_FUNCTION__ , sizeof(__PRETTY_FUNCTION__) - 1, 568, }; nslog__log(&_nslog_ctx , "item %p: target_main_size: %i", item->box, target_main_size ); } } while(0); | ||||
| 569 | |||||
| 570 | if (item->freeze) { | ||||
| 571 | continue; | ||||
| 572 | } | ||||
| 573 | |||||
| 574 | if (item->max_main > 0 && | ||||
| 575 | target_main_size > item->max_main) { | ||||
| 576 | target_main_size = item->max_main; | ||||
| 577 | item->max_violation = true1; | ||||
| 578 | NSLOG(flex, DEEPDEBUG, "Violation: max_main: %i",do { if (NSLOG_LEVEL_DEEPDEBUG >= NSLOG_LEVEL_VERBOSE) { static nslog_entry_context_t _nslog_ctx = { &__nslog_category_flex , NSLOG_LEVEL_DEEPDEBUG, "content/handlers/html/layout_flex.c" , sizeof("content/handlers/html/layout_flex.c") - 1, __PRETTY_FUNCTION__ , sizeof(__PRETTY_FUNCTION__) - 1, 579, }; nslog__log(&_nslog_ctx , "Violation: max_main: %i", item->max_main); } } while(0) | ||||
| 579 | item->max_main)do { if (NSLOG_LEVEL_DEEPDEBUG >= NSLOG_LEVEL_VERBOSE) { static nslog_entry_context_t _nslog_ctx = { &__nslog_category_flex , NSLOG_LEVEL_DEEPDEBUG, "content/handlers/html/layout_flex.c" , sizeof("content/handlers/html/layout_flex.c") - 1, __PRETTY_FUNCTION__ , sizeof(__PRETTY_FUNCTION__) - 1, 579, }; nslog__log(&_nslog_ctx , "Violation: max_main: %i", item->max_main); } } while(0); | ||||
| 580 | } | ||||
| 581 | |||||
| 582 | if (target_main_size < item->min_main) { | ||||
| 583 | target_main_size = item->min_main; | ||||
| 584 | item->min_violation = true1; | ||||
| 585 | NSLOG(flex, DEEPDEBUG, "Violation: min_main: %i",do { if (NSLOG_LEVEL_DEEPDEBUG >= NSLOG_LEVEL_VERBOSE) { static nslog_entry_context_t _nslog_ctx = { &__nslog_category_flex , NSLOG_LEVEL_DEEPDEBUG, "content/handlers/html/layout_flex.c" , sizeof("content/handlers/html/layout_flex.c") - 1, __PRETTY_FUNCTION__ , sizeof(__PRETTY_FUNCTION__) - 1, 586, }; nslog__log(&_nslog_ctx , "Violation: min_main: %i", item->min_main); } } while(0) | ||||
| 586 | item->min_main)do { if (NSLOG_LEVEL_DEEPDEBUG >= NSLOG_LEVEL_VERBOSE) { static nslog_entry_context_t _nslog_ctx = { &__nslog_category_flex , NSLOG_LEVEL_DEEPDEBUG, "content/handlers/html/layout_flex.c" , sizeof("content/handlers/html/layout_flex.c") - 1, __PRETTY_FUNCTION__ , sizeof(__PRETTY_FUNCTION__) - 1, 586, }; nslog__log(&_nslog_ctx , "Violation: min_main: %i", item->min_main); } } while(0); | ||||
| 587 | } | ||||
| 588 | |||||
| 589 | if (target_main_size < item->box->min_width) { | ||||
| 590 | target_main_size = item->box->min_width; | ||||
| 591 | item->min_violation = true1; | ||||
| 592 | NSLOG(flex, DEEPDEBUG, "Violation: box min_width: %i",do { if (NSLOG_LEVEL_DEEPDEBUG >= NSLOG_LEVEL_VERBOSE) { static nslog_entry_context_t _nslog_ctx = { &__nslog_category_flex , NSLOG_LEVEL_DEEPDEBUG, "content/handlers/html/layout_flex.c" , sizeof("content/handlers/html/layout_flex.c") - 1, __PRETTY_FUNCTION__ , sizeof(__PRETTY_FUNCTION__) - 1, 593, }; nslog__log(&_nslog_ctx , "Violation: box min_width: %i", item->box->min_width) ; } } while(0) | ||||
| 593 | item->box->min_width)do { if (NSLOG_LEVEL_DEEPDEBUG >= NSLOG_LEVEL_VERBOSE) { static nslog_entry_context_t _nslog_ctx = { &__nslog_category_flex , NSLOG_LEVEL_DEEPDEBUG, "content/handlers/html/layout_flex.c" , sizeof("content/handlers/html/layout_flex.c") - 1, __PRETTY_FUNCTION__ , sizeof(__PRETTY_FUNCTION__) - 1, 593, }; nslog__log(&_nslog_ctx , "Violation: box min_width: %i", item->box->min_width) ; } } while(0); | ||||
| 594 | } | ||||
| 595 | |||||
| 596 | if (target_main_size < 0) { | ||||
| 597 | target_main_size = 0; | ||||
| 598 | item->min_violation = true1; | ||||
| 599 | NSLOG(flex, DEEPDEBUG, "Violation: less than 0")do { if (NSLOG_LEVEL_DEEPDEBUG >= NSLOG_LEVEL_VERBOSE) { static nslog_entry_context_t _nslog_ctx = { &__nslog_category_flex , NSLOG_LEVEL_DEEPDEBUG, "content/handlers/html/layout_flex.c" , sizeof("content/handlers/html/layout_flex.c") - 1, __PRETTY_FUNCTION__ , sizeof(__PRETTY_FUNCTION__) - 1, 599, }; nslog__log(&_nslog_ctx , "Violation: less than 0"); } } while(0); | ||||
| 600 | } | ||||
| 601 | |||||
| 602 | total_violation += target_main_size - item->target_main_size; | ||||
| 603 | item->target_main_size = target_main_size; | ||||
| 604 | } | ||||
| 605 | |||||
| 606 | NSLOG(flex, DEEPDEBUG, "Total violation: %i", total_violation)do { if (NSLOG_LEVEL_DEEPDEBUG >= NSLOG_LEVEL_VERBOSE) { static nslog_entry_context_t _nslog_ctx = { &__nslog_category_flex , NSLOG_LEVEL_DEEPDEBUG, "content/handlers/html/layout_flex.c" , sizeof("content/handlers/html/layout_flex.c") - 1, __PRETTY_FUNCTION__ , sizeof(__PRETTY_FUNCTION__) - 1, 606, }; nslog__log(&_nslog_ctx , "Total violation: %i", total_violation); } } while(0); | ||||
| 607 | |||||
| 608 | return total_violation; | ||||
| 609 | } | ||||
| 610 | |||||
| 611 | /** | ||||
| 612 | * Distribute remaining free space proportional to the flex factors. | ||||
| 613 | * | ||||
| 614 | * Remaining free space may be negative. | ||||
| 615 | * | ||||
| 616 | * \param[in] ctx Flex layout context | ||||
| 617 | * \param[in] line Line to distribute free space on | ||||
| 618 | * \param[in] unfrozen_factor_sum Sum of unfrozen item's flex factors | ||||
| 619 | * \param[in] remaining_free_main Remaining free space in main direction | ||||
| 620 | * \param[in] grow Whether to grow or shrink | ||||
| 621 | */ | ||||
| 622 | static inline void layout_flex__distribute_free_main( | ||||
| 623 | struct flex_ctx *ctx, | ||||
| 624 | struct flex_line_data *line, | ||||
| 625 | css_fixed unfrozen_factor_sum, | ||||
| 626 | int remaining_free_main, | ||||
| 627 | bool_Bool grow) | ||||
| 628 | { | ||||
| 629 | size_t item_count = line->first + line->count; | ||||
| 630 | |||||
| 631 | if (grow) { | ||||
| 632 | css_fixed remainder = 0; | ||||
| 633 | for (size_t i = line->first; i < item_count; i++) { | ||||
| 634 | struct flex_item_data *item = &ctx->item.data[i]; | ||||
| 635 | css_fixed result; | ||||
| 636 | css_fixed ratio; | ||||
| 637 | |||||
| 638 | if (item->freeze) { | ||||
| 639 | continue; | ||||
| 640 | } | ||||
| 641 | |||||
| 642 | ratio = FDIV(item->grow, unfrozen_factor_sum)(css_divide_fixed((item->grow), (unfrozen_factor_sum))); | ||||
| 643 | result = FMUL(INTTOFIX(remaining_free_main), ratio)(css_multiply_fixed(((css_int_to_fixed(remaining_free_main))) , (ratio))) + | ||||
| 644 | remainder; | ||||
| 645 | |||||
| 646 | item->target_main_size = item->base_size + | ||||
| 647 | FIXTOINT(result)((result) >> 10); | ||||
| 648 | remainder = FIXFRAC(result)(result & ((1 << 10)- 1 )); | ||||
| 649 | } | ||||
| 650 | } else { | ||||
| 651 | css_fixed scaled_shrink_factor_sum = 0; | ||||
| 652 | css_fixed remainder = 0; | ||||
| 653 | |||||
| 654 | for (size_t i = line->first; i < item_count; i++) { | ||||
| 655 | struct flex_item_data *item = &ctx->item.data[i]; | ||||
| 656 | css_fixed scaled_shrink_factor; | ||||
| 657 | |||||
| 658 | if (item->freeze) { | ||||
| 659 | continue; | ||||
| 660 | } | ||||
| 661 | |||||
| 662 | scaled_shrink_factor = FMUL((css_multiply_fixed((item->shrink), ((css_int_to_fixed(item ->base_size))))) | ||||
| 663 | item->shrink,(css_multiply_fixed((item->shrink), ((css_int_to_fixed(item ->base_size))))) | ||||
| 664 | INTTOFIX(item->base_size))(css_multiply_fixed((item->shrink), ((css_int_to_fixed(item ->base_size))))); | ||||
| 665 | scaled_shrink_factor_sum += scaled_shrink_factor; | ||||
| 666 | } | ||||
| 667 | |||||
| 668 | for (size_t i = line->first; i < item_count; i++) { | ||||
| 669 | struct flex_item_data *item = &ctx->item.data[i]; | ||||
| 670 | css_fixed scaled_shrink_factor; | ||||
| 671 | css_fixed result; | ||||
| 672 | css_fixed ratio; | ||||
| 673 | |||||
| 674 | if (item->freeze) { | ||||
| 675 | continue; | ||||
| 676 | } else if (scaled_shrink_factor_sum == 0) { | ||||
| 677 | item->target_main_size = item->main_size; | ||||
| 678 | layout_flex__item_freeze(line, item); | ||||
| 679 | continue; | ||||
| 680 | } | ||||
| 681 | |||||
| 682 | scaled_shrink_factor = FMUL((css_multiply_fixed((item->shrink), ((css_int_to_fixed(item ->base_size))))) | ||||
| 683 | item->shrink,(css_multiply_fixed((item->shrink), ((css_int_to_fixed(item ->base_size))))) | ||||
| 684 | INTTOFIX(item->base_size))(css_multiply_fixed((item->shrink), ((css_int_to_fixed(item ->base_size))))); | ||||
| 685 | ratio = FDIV(scaled_shrink_factor,(css_divide_fixed((scaled_shrink_factor), (scaled_shrink_factor_sum ))) | ||||
| 686 | scaled_shrink_factor_sum)(css_divide_fixed((scaled_shrink_factor), (scaled_shrink_factor_sum ))); | ||||
| 687 | result = FMUL(INTTOFIX(abs(remaining_free_main)),(css_multiply_fixed(((css_int_to_fixed(abs(remaining_free_main )))), (ratio))) | ||||
| 688 | ratio)(css_multiply_fixed(((css_int_to_fixed(abs(remaining_free_main )))), (ratio))) + remainder; | ||||
| 689 | |||||
| 690 | item->target_main_size = item->base_size - | ||||
| 691 | FIXTOINT(result)((result) >> 10); | ||||
| 692 | remainder = FIXFRAC(result)(result & ((1 << 10)- 1 )); | ||||
| 693 | } | ||||
| 694 | } | ||||
| 695 | } | ||||
| 696 | |||||
| 697 | /** | ||||
| 698 | * Resolve flexible item lengths along a line. | ||||
| 699 | * | ||||
| 700 | * See 9.7 of Tests CSS Flexible Box Layout Module Level 1. | ||||
| 701 | * | ||||
| 702 | * \param[in] ctx Flex layout context | ||||
| 703 | * \param[in] line Line to resolve | ||||
| 704 | * \return true on success, false on failure. | ||||
| 705 | */ | ||||
| 706 | static bool_Bool layout_flex__resolve_line( | ||||
| 707 | struct flex_ctx *ctx, | ||||
| 708 | struct flex_line_data *line) | ||||
| 709 | { | ||||
| 710 | size_t item_count = line->first + line->count; | ||||
| 711 | int available_main = ctx->available_main; | ||||
| 712 | int initial_free_main; | ||||
| 713 | bool_Bool grow; | ||||
| 714 | |||||
| 715 | if (available_main == AUTO(-2147483647 -1)) { | ||||
| 716 | available_main = INT_MAX2147483647; | ||||
| 717 | } | ||||
| 718 | |||||
| 719 | grow = (line->main_size < available_main); | ||||
| 720 | initial_free_main = available_main; | ||||
| 721 | |||||
| 722 | NSLOG(flex, DEEPDEBUG, "box %p: line %zu: first: %zu, count: %zu",do { if (NSLOG_LEVEL_DEEPDEBUG >= NSLOG_LEVEL_VERBOSE) { static nslog_entry_context_t _nslog_ctx = { &__nslog_category_flex , NSLOG_LEVEL_DEEPDEBUG, "content/handlers/html/layout_flex.c" , sizeof("content/handlers/html/layout_flex.c") - 1, __PRETTY_FUNCTION__ , sizeof(__PRETTY_FUNCTION__) - 1, 724, }; nslog__log(&_nslog_ctx , "box %p: line %zu: first: %zu, count: %zu", ctx->flex, line - ctx->line.data, line->first, line->count); } } while (0) | ||||
| 723 | ctx->flex, line - ctx->line.data,do { if (NSLOG_LEVEL_DEEPDEBUG >= NSLOG_LEVEL_VERBOSE) { static nslog_entry_context_t _nslog_ctx = { &__nslog_category_flex , NSLOG_LEVEL_DEEPDEBUG, "content/handlers/html/layout_flex.c" , sizeof("content/handlers/html/layout_flex.c") - 1, __PRETTY_FUNCTION__ , sizeof(__PRETTY_FUNCTION__) - 1, 724, }; nslog__log(&_nslog_ctx , "box %p: line %zu: first: %zu, count: %zu", ctx->flex, line - ctx->line.data, line->first, line->count); } } while (0) | ||||
| 724 | line->first, line->count)do { if (NSLOG_LEVEL_DEEPDEBUG >= NSLOG_LEVEL_VERBOSE) { static nslog_entry_context_t _nslog_ctx = { &__nslog_category_flex , NSLOG_LEVEL_DEEPDEBUG, "content/handlers/html/layout_flex.c" , sizeof("content/handlers/html/layout_flex.c") - 1, __PRETTY_FUNCTION__ , sizeof(__PRETTY_FUNCTION__) - 1, 724, }; nslog__log(&_nslog_ctx , "box %p: line %zu: first: %zu, count: %zu", ctx->flex, line - ctx->line.data, line->first, line->count); } } while (0); | ||||
| 725 | NSLOG(flex, DEEPDEBUG, "Line main_size: %i, available_main: %i",do { if (NSLOG_LEVEL_DEEPDEBUG >= NSLOG_LEVEL_VERBOSE) { static nslog_entry_context_t _nslog_ctx = { &__nslog_category_flex , NSLOG_LEVEL_DEEPDEBUG, "content/handlers/html/layout_flex.c" , sizeof("content/handlers/html/layout_flex.c") - 1, __PRETTY_FUNCTION__ , sizeof(__PRETTY_FUNCTION__) - 1, 726, }; nslog__log(&_nslog_ctx , "Line main_size: %i, available_main: %i", line->main_size , available_main); } } while(0) | ||||
| 726 | line->main_size, available_main)do { if (NSLOG_LEVEL_DEEPDEBUG >= NSLOG_LEVEL_VERBOSE) { static nslog_entry_context_t _nslog_ctx = { &__nslog_category_flex , NSLOG_LEVEL_DEEPDEBUG, "content/handlers/html/layout_flex.c" , sizeof("content/handlers/html/layout_flex.c") - 1, __PRETTY_FUNCTION__ , sizeof(__PRETTY_FUNCTION__) - 1, 726, }; nslog__log(&_nslog_ctx , "Line main_size: %i, available_main: %i", line->main_size , available_main); } } while(0); | ||||
| 727 | |||||
| 728 | for (size_t i = line->first; i < item_count; i++) { | ||||
| 729 | struct flex_item_data *item = &ctx->item.data[i]; | ||||
| 730 | |||||
| 731 | /* 3. Size inflexible items */ | ||||
| 732 | if (grow) { | ||||
| 733 | if (item->grow == 0 || | ||||
| 734 | item->base_size > item->main_size) { | ||||
| 735 | item->target_main_size = item->main_size; | ||||
| 736 | layout_flex__item_freeze(line, item); | ||||
| 737 | } | ||||
| 738 | } else { | ||||
| 739 | if (item->shrink == 0 || | ||||
| 740 | item->base_size < item->main_size) { | ||||
| 741 | item->target_main_size = item->main_size; | ||||
| 742 | layout_flex__item_freeze(line, item); | ||||
| 743 | } | ||||
| 744 | } | ||||
| 745 | |||||
| 746 | /* 4. Calculate initial free space */ | ||||
| 747 | if (item->freeze) { | ||||
| 748 | initial_free_main -= item->target_main_size; | ||||
| 749 | } else { | ||||
| 750 | initial_free_main -= item->base_size; | ||||
| 751 | } | ||||
| 752 | } | ||||
| 753 | |||||
| 754 | /* 5. Loop */ | ||||
| 755 | while (line->frozen < line->count) { | ||||
| 756 | css_fixed unfrozen_factor_sum; | ||||
| 757 | int remaining_free_main; | ||||
| 758 | int total_violation; | ||||
| 759 | |||||
| 760 | NSLOG(flex, DEEPDEBUG, "flex-container: %p: Resolver pass",do { if (NSLOG_LEVEL_DEEPDEBUG >= NSLOG_LEVEL_VERBOSE) { static nslog_entry_context_t _nslog_ctx = { &__nslog_category_flex , NSLOG_LEVEL_DEEPDEBUG, "content/handlers/html/layout_flex.c" , sizeof("content/handlers/html/layout_flex.c") - 1, __PRETTY_FUNCTION__ , sizeof(__PRETTY_FUNCTION__) - 1, 761, }; nslog__log(&_nslog_ctx , "flex-container: %p: Resolver pass", ctx->flex); } } while (0) | ||||
| 761 | ctx->flex)do { if (NSLOG_LEVEL_DEEPDEBUG >= NSLOG_LEVEL_VERBOSE) { static nslog_entry_context_t _nslog_ctx = { &__nslog_category_flex , NSLOG_LEVEL_DEEPDEBUG, "content/handlers/html/layout_flex.c" , sizeof("content/handlers/html/layout_flex.c") - 1, __PRETTY_FUNCTION__ , sizeof(__PRETTY_FUNCTION__) - 1, 761, }; nslog__log(&_nslog_ctx , "flex-container: %p: Resolver pass", ctx->flex); } } while (0); | ||||
| 762 | |||||
| 763 | /* b */ | ||||
| 764 | remaining_free_main = layout_flex__remaining_free_main(ctx, | ||||
| 765 | line, &unfrozen_factor_sum, initial_free_main, | ||||
| 766 | available_main, grow); | ||||
| 767 | |||||
| 768 | /* c */ | ||||
| 769 | if (remaining_free_main != 0) { | ||||
| 770 | layout_flex__distribute_free_main(ctx, | ||||
| 771 | line, unfrozen_factor_sum, | ||||
| 772 | remaining_free_main, grow); | ||||
| 773 | } | ||||
| 774 | |||||
| 775 | /* d */ | ||||
| 776 | total_violation = layout_flex__get_min_max_violations( | ||||
| 777 | ctx, line); | ||||
| 778 | |||||
| 779 | /* e */ | ||||
| 780 | for (size_t i = line->first; i < item_count; i++) { | ||||
| 781 | struct flex_item_data *item = &ctx->item.data[i]; | ||||
| 782 | |||||
| 783 | if (item->freeze) { | ||||
| 784 | continue; | ||||
| 785 | } | ||||
| 786 | |||||
| 787 | if (total_violation == 0 || | ||||
| 788 | (total_violation > 0 && item->min_violation) || | ||||
| 789 | (total_violation < 0 && item->max_violation)) { | ||||
| 790 | layout_flex__item_freeze(line, item); | ||||
| 791 | } | ||||
| 792 | } | ||||
| 793 | } | ||||
| 794 | |||||
| 795 | return true1; | ||||
| 796 | } | ||||
| 797 | |||||
| 798 | /** | ||||
| 799 | * Position items along a line | ||||
| 800 | * | ||||
| 801 | * \param[in] ctx Flex layout context | ||||
| 802 | * \param[in] line Line to resolve | ||||
| 803 | * \return true on success, false on failure. | ||||
| 804 | */ | ||||
| 805 | static bool_Bool layout_flex__place_line_items_main( | ||||
| 806 | struct flex_ctx *ctx, | ||||
| 807 | struct flex_line_data *line) | ||||
| 808 | { | ||||
| 809 | int main_pos = ctx->flex->padding[layout_flex__main_start_side(ctx)]; | ||||
| 810 | int post_multiplier = ctx->main_reversed ? 0 : 1; | ||||
| 811 | int pre_multiplier = ctx->main_reversed ? -1 : 0; | ||||
| 812 | size_t item_count = line->first + line->count; | ||||
| 813 | int extra_remainder = 0; | ||||
| 814 | int extra = 0; | ||||
| 815 | |||||
| 816 | if (ctx->main_reversed) { | ||||
| 817 | main_pos = lh__box_size_main(ctx->horizontal, ctx->flex) - | ||||
| 818 | main_pos; | ||||
| 819 | } | ||||
| 820 | |||||
| 821 | if (ctx->available_main != AUTO(-2147483647 -1) && | ||||
| 822 | ctx->available_main != UNKNOWN_WIDTH2147483647 && | ||||
| 823 | ctx->available_main > line->used_main_size) { | ||||
| 824 | if (line->main_auto_margin_count > 0) { | ||||
| 825 | extra = ctx->available_main - line->used_main_size; | ||||
| 826 | |||||
| 827 | extra_remainder = extra % line->main_auto_margin_count; | ||||
| 828 | extra /= line->main_auto_margin_count; | ||||
| 829 | } | ||||
| 830 | } | ||||
| 831 | |||||
| 832 | for (size_t i = line->first; i < item_count; i++) { | ||||
| 833 | enum box_side main_end = ctx->horizontal ? RIGHT : BOTTOM; | ||||
| 834 | enum box_side main_start = ctx->horizontal ? LEFT : TOP; | ||||
| 835 | struct flex_item_data *item = &ctx->item.data[i]; | ||||
| 836 | struct box *b = item->box; | ||||
| 837 | int extra_total = 0; | ||||
| 838 | int extra_post = 0; | ||||
| 839 | int extra_pre = 0; | ||||
| 840 | int box_size_main; | ||||
| 841 | int *box_pos_main; | ||||
| 842 | |||||
| 843 | if (ctx->horizontal) { | ||||
| 844 | b->width = item->target_main_size - | ||||
| 845 | lh__delta_outer_width(b); | ||||
| 846 | |||||
| 847 | if (!layout_flex_item(ctx, item, b->width)) { | ||||
| 848 | return false0; | ||||
| 849 | } | ||||
| 850 | } | ||||
| 851 | |||||
| 852 | box_size_main = lh__box_size_main(ctx->horizontal, b); | ||||
| 853 | box_pos_main = ctx->horizontal ? &b->x : &b->y; | ||||
| 854 | |||||
| 855 | if (!lh__box_is_absolute(b)) { | ||||
| 856 | if (b->margin[main_start] == AUTO(-2147483647 -1)) { | ||||
| 857 | extra_pre = extra + extra_remainder; | ||||
| 858 | } | ||||
| 859 | if (b->margin[main_end] == AUTO(-2147483647 -1)) { | ||||
| 860 | extra_post = extra + extra_remainder; | ||||
| 861 | } | ||||
| 862 | extra_total = extra_pre + extra_post; | ||||
| 863 | |||||
| 864 | main_pos += pre_multiplier * | ||||
| 865 | (extra_total + box_size_main + | ||||
| 866 | lh__delta_outer_main(ctx->flex, b)); | ||||
| 867 | } | ||||
| 868 | |||||
| 869 | *box_pos_main = main_pos + lh__non_auto_margin(b, main_start) + | ||||
| 870 | extra_pre + b->border[main_start].width; | ||||
| 871 | |||||
| 872 | if (!lh__box_is_absolute(b)) { | ||||
| 873 | int cross_size; | ||||
| 874 | int box_size_cross = lh__box_size_cross( | ||||
| 875 | ctx->horizontal, b); | ||||
| 876 | |||||
| 877 | main_pos += post_multiplier * | ||||
| 878 | (extra_total + box_size_main + | ||||
| 879 | lh__delta_outer_main(ctx->flex, b)); | ||||
| 880 | |||||
| 881 | cross_size = box_size_cross + lh__delta_outer_cross( | ||||
| 882 | ctx->flex, b); | ||||
| 883 | if (line->cross_size < cross_size) { | ||||
| 884 | line->cross_size = cross_size; | ||||
| 885 | } | ||||
| 886 | } | ||||
| 887 | } | ||||
| 888 | |||||
| 889 | return true1; | ||||
| 890 | } | ||||
| 891 | |||||
| 892 | /** | ||||
| 893 | * Collect items onto lines and place items along the lines | ||||
| 894 | * | ||||
| 895 | * \param[in] ctx Flex layout context | ||||
| 896 | * \return true on success, false on failure. | ||||
| 897 | */ | ||||
| 898 | static bool_Bool layout_flex__collect_items_into_lines( | ||||
| 899 | struct flex_ctx *ctx) | ||||
| 900 | { | ||||
| 901 | size_t pos = 0; | ||||
| 902 | |||||
| 903 | while (pos < ctx->item.count) { | ||||
| 904 | struct flex_line_data *line; | ||||
| 905 | |||||
| 906 | line = layout_flex__build_line(ctx, pos); | ||||
| 907 | if (line == NULL((void*)0)) { | ||||
| 908 | return false0; | ||||
| 909 | } | ||||
| 910 | |||||
| 911 | pos += line->count; | ||||
| 912 | |||||
| 913 | NSLOG(flex, DEEPDEBUG, "flex-container: %p: "do { if (NSLOG_LEVEL_DEEPDEBUG >= NSLOG_LEVEL_VERBOSE) { static nslog_entry_context_t _nslog_ctx = { &__nslog_category_flex , NSLOG_LEVEL_DEEPDEBUG, "content/handlers/html/layout_flex.c" , sizeof("content/handlers/html/layout_flex.c") - 1, __PRETTY_FUNCTION__ , sizeof(__PRETTY_FUNCTION__) - 1, 916, }; nslog__log(&_nslog_ctx , "flex-container: %p: " "fitted: %zu (total: %zu/%zu)", ctx-> flex, line->count, pos, ctx->item.count); } } while(0) | ||||
| 914 | "fitted: %zu (total: %zu/%zu)",do { if (NSLOG_LEVEL_DEEPDEBUG >= NSLOG_LEVEL_VERBOSE) { static nslog_entry_context_t _nslog_ctx = { &__nslog_category_flex , NSLOG_LEVEL_DEEPDEBUG, "content/handlers/html/layout_flex.c" , sizeof("content/handlers/html/layout_flex.c") - 1, __PRETTY_FUNCTION__ , sizeof(__PRETTY_FUNCTION__) - 1, 916, }; nslog__log(&_nslog_ctx , "flex-container: %p: " "fitted: %zu (total: %zu/%zu)", ctx-> flex, line->count, pos, ctx->item.count); } } while(0) | ||||
| 915 | ctx->flex, line->count,do { if (NSLOG_LEVEL_DEEPDEBUG >= NSLOG_LEVEL_VERBOSE) { static nslog_entry_context_t _nslog_ctx = { &__nslog_category_flex , NSLOG_LEVEL_DEEPDEBUG, "content/handlers/html/layout_flex.c" , sizeof("content/handlers/html/layout_flex.c") - 1, __PRETTY_FUNCTION__ , sizeof(__PRETTY_FUNCTION__) - 1, 916, }; nslog__log(&_nslog_ctx , "flex-container: %p: " "fitted: %zu (total: %zu/%zu)", ctx-> flex, line->count, pos, ctx->item.count); } } while(0) | ||||
| 916 | pos, ctx->item.count)do { if (NSLOG_LEVEL_DEEPDEBUG >= NSLOG_LEVEL_VERBOSE) { static nslog_entry_context_t _nslog_ctx = { &__nslog_category_flex , NSLOG_LEVEL_DEEPDEBUG, "content/handlers/html/layout_flex.c" , sizeof("content/handlers/html/layout_flex.c") - 1, __PRETTY_FUNCTION__ , sizeof(__PRETTY_FUNCTION__) - 1, 916, }; nslog__log(&_nslog_ctx , "flex-container: %p: " "fitted: %zu (total: %zu/%zu)", ctx-> flex, line->count, pos, ctx->item.count); } } while(0); | ||||
| 917 | |||||
| 918 | if (!layout_flex__resolve_line(ctx, line)) { | ||||
| 919 | return false0; | ||||
| 920 | } | ||||
| 921 | |||||
| 922 | if (!layout_flex__place_line_items_main(ctx, line)) { | ||||
| 923 | return false0; | ||||
| 924 | } | ||||
| 925 | |||||
| 926 | ctx->cross_size += line->cross_size; | ||||
| 927 | if (ctx->main_size < line->main_size) { | ||||
| 928 | ctx->main_size = line->main_size; | ||||
| 929 | } | ||||
| 930 | } | ||||
| 931 | |||||
| 932 | return true1; | ||||
| 933 | } | ||||
| 934 | |||||
| 935 | /** | ||||
| 936 | * Align items on a line. | ||||
| 937 | * | ||||
| 938 | * \param[in] ctx Flex layout context | ||||
| 939 | * \param[in] line Line to align items on | ||||
| 940 | * \param[in] extra Extra line width in pixels | ||||
| 941 | */ | ||||
| 942 | static void layout_flex__place_line_items_cross(struct flex_ctx *ctx, | ||||
| 943 | struct flex_line_data *line, int extra) | ||||
| 944 | { | ||||
| 945 | enum box_side cross_start = ctx->horizontal ? TOP : LEFT; | ||||
| 946 | size_t item_count = line->first + line->count; | ||||
| 947 | |||||
| 948 | for (size_t i = line->first; i < item_count; i++) { | ||||
| 949 | struct flex_item_data *item = &ctx->item.data[i]; | ||||
| 950 | struct box *b = item->box; | ||||
| 951 | int cross_free_space; | ||||
| 952 | int *box_size_cross; | ||||
| 953 | int *box_pos_cross; | ||||
| 954 | |||||
| 955 | box_pos_cross = ctx->horizontal ? &b->y : &b->x; | ||||
| 956 | box_size_cross = lh__box_size_cross_ptr(ctx->horizontal, b); | ||||
| 957 | |||||
| 958 | cross_free_space = line->cross_size + extra - *box_size_cross - | ||||
| 959 | lh__delta_outer_cross(ctx->flex, b); | ||||
| 960 | |||||
| 961 | switch (lh__box_align_self(ctx->flex, b)) { | ||||
| 962 | default: | ||||
| 963 | case CSS_ALIGN_SELF_STRETCH: | ||||
| 964 | if (lh__box_size_cross_is_auto(ctx->horizontal, b)) { | ||||
| 965 | *box_size_cross += cross_free_space; | ||||
| 966 | |||||
| 967 | /* Relayout children for stretch. */ | ||||
| 968 | if (!layout_flex_item(ctx, item, b->width)) { | ||||
| 969 | return; | ||||
| 970 | } | ||||
| 971 | } | ||||
| 972 | fallthrough__attribute__((__fallthrough__)); | ||||
| 973 | case CSS_ALIGN_SELF_FLEX_START: | ||||
| 974 | *box_pos_cross = ctx->flex->padding[cross_start] + | ||||
| 975 | line->pos + | ||||
| 976 | lh__non_auto_margin(b, cross_start) + | ||||
| 977 | b->border[cross_start].width; | ||||
| 978 | break; | ||||
| 979 | |||||
| 980 | case CSS_ALIGN_SELF_FLEX_END: | ||||
| 981 | *box_pos_cross = ctx->flex->padding[cross_start] + | ||||
| 982 | line->pos + cross_free_space + | ||||
| 983 | lh__non_auto_margin(b, cross_start) + | ||||
| 984 | b->border[cross_start].width; | ||||
| 985 | break; | ||||
| 986 | |||||
| 987 | case CSS_ALIGN_SELF_BASELINE: | ||||
| 988 | case CSS_ALIGN_SELF_CENTER: | ||||
| 989 | *box_pos_cross = ctx->flex->padding[cross_start] + | ||||
| 990 | line->pos + cross_free_space / 2 + | ||||
| 991 | lh__non_auto_margin(b, cross_start) + | ||||
| 992 | b->border[cross_start].width; | ||||
| 993 | break; | ||||
| 994 | } | ||||
| 995 | } | ||||
| 996 | } | ||||
| 997 | |||||
| 998 | /** | ||||
| 999 | * Place the lines and align the items on the line. | ||||
| 1000 | * | ||||
| 1001 | * \param[in] ctx Flex layout context | ||||
| 1002 | */ | ||||
| 1003 | static void layout_flex__place_lines(struct flex_ctx *ctx) | ||||
| 1004 | { | ||||
| 1005 | bool_Bool reversed = ctx->wrap == CSS_FLEX_WRAP_WRAP_REVERSE; | ||||
| 1006 | int line_pos = reversed ? ctx->cross_size : 0; | ||||
| 1007 | int post_multiplier = reversed ? 0 : 1; | ||||
| 1008 | int pre_multiplier = reversed ? -1 : 0; | ||||
| 1009 | int extra_remainder = 0; | ||||
| 1010 | int extra = 0; | ||||
| 1011 | |||||
| 1012 | if (ctx->available_cross != AUTO(-2147483647 -1) && | ||||
| 1013 | ctx->available_cross > ctx->cross_size && | ||||
| 1014 | ctx->line.count > 0) { | ||||
| 1015 | extra = ctx->available_cross - ctx->cross_size; | ||||
| 1016 | |||||
| 1017 | extra_remainder = extra % ctx->line.count; | ||||
| 1018 | extra /= ctx->line.count; | ||||
| 1019 | } | ||||
| 1020 | |||||
| 1021 | for (size_t i = 0; i < ctx->line.count; i++) { | ||||
| 1022 | struct flex_line_data *line = &ctx->line.data[i]; | ||||
| 1023 | |||||
| 1024 | line_pos += pre_multiplier * line->cross_size; | ||||
| 1025 | line->pos = line_pos; | ||||
| 1026 | line_pos += post_multiplier * line->cross_size + | ||||
| 1027 | extra + extra_remainder; | ||||
| 1028 | |||||
| 1029 | layout_flex__place_line_items_cross(ctx, line, | ||||
| 1030 | extra + extra_remainder); | ||||
| 1031 | |||||
| 1032 | if (extra_remainder > 0) { | ||||
| 1033 | extra_remainder--; | ||||
| 1034 | } | ||||
| 1035 | } | ||||
| 1036 | } | ||||
| 1037 | |||||
| 1038 | /** | ||||
| 1039 | * Layout a flex container. | ||||
| 1040 | * | ||||
| 1041 | * \param[in] flex table to layout | ||||
| 1042 | * \param[in] available_width width of containing block | ||||
| 1043 | * \param[in] content memory pool for any new boxes | ||||
| 1044 | * \return true on success, false on memory exhaustion | ||||
| 1045 | */ | ||||
| 1046 | bool_Bool layout_flex(struct box *flex, int available_width, | ||||
| 1047 | html_content *content) | ||||
| 1048 | { | ||||
| 1049 | int max_height, min_height; | ||||
| 1050 | struct flex_ctx *ctx; | ||||
| 1051 | bool_Bool success = false0; | ||||
| 1052 | |||||
| 1053 | ctx = layout_flex_ctx__create(content, flex); | ||||
| 1054 | if (ctx
| ||||
| 1055 | return false0; | ||||
| 1056 | } | ||||
| 1057 | |||||
| 1058 | NSLOG(flex, DEEPDEBUG, "box %p: %s, available_width %i, width: %i",do { if (NSLOG_LEVEL_DEEPDEBUG >= NSLOG_LEVEL_VERBOSE) { static nslog_entry_context_t _nslog_ctx = { &__nslog_category_flex , NSLOG_LEVEL_DEEPDEBUG, "content/handlers/html/layout_flex.c" , sizeof("content/handlers/html/layout_flex.c") - 1, __PRETTY_FUNCTION__ , sizeof(__PRETTY_FUNCTION__) - 1, 1060, }; nslog__log(&_nslog_ctx , "box %p: %s, available_width %i, width: %i", flex, ctx-> horizontal ? "horizontal" : "vertical", available_width, flex ->width); } } while(0) | ||||
| 1059 | flex, ctx->horizontal ? "horizontal" : "vertical",do { if (NSLOG_LEVEL_DEEPDEBUG >= NSLOG_LEVEL_VERBOSE) { static nslog_entry_context_t _nslog_ctx = { &__nslog_category_flex , NSLOG_LEVEL_DEEPDEBUG, "content/handlers/html/layout_flex.c" , sizeof("content/handlers/html/layout_flex.c") - 1, __PRETTY_FUNCTION__ , sizeof(__PRETTY_FUNCTION__) - 1, 1060, }; nslog__log(&_nslog_ctx , "box %p: %s, available_width %i, width: %i", flex, ctx-> horizontal ? "horizontal" : "vertical", available_width, flex ->width); } } while(0) | ||||
| 1060 | available_width, flex->width)do { if (NSLOG_LEVEL_DEEPDEBUG >= NSLOG_LEVEL_VERBOSE) { static nslog_entry_context_t _nslog_ctx = { &__nslog_category_flex , NSLOG_LEVEL_DEEPDEBUG, "content/handlers/html/layout_flex.c" , sizeof("content/handlers/html/layout_flex.c") - 1, __PRETTY_FUNCTION__ , sizeof(__PRETTY_FUNCTION__) - 1, 1060, }; nslog__log(&_nslog_ctx , "box %p: %s, available_width %i, width: %i", flex, ctx-> horizontal ? "horizontal" : "vertical", available_width, flex ->width); } } while(0); | ||||
| 1061 | |||||
| 1062 | layout_find_dimensions( | ||||
| 1063 | ctx->unit_len_ctx, available_width, -1, | ||||
| 1064 | flex, flex->style, NULL((void*)0), &flex->height, | ||||
| 1065 | NULL((void*)0), NULL((void*)0), &max_height, &min_height, | ||||
| 1066 | flex->margin, flex->padding, flex->border); | ||||
| 1067 | |||||
| 1068 | available_width = min(available_width, flex->width)(((available_width)<(flex->width))?(available_width):(flex ->width)); | ||||
| 1069 | |||||
| 1070 | if (ctx->horizontal
| ||||
| 1071 | ctx->available_main = available_width; | ||||
| 1072 | ctx->available_cross = ctx->flex->height; | ||||
| 1073 | } else { | ||||
| 1074 | ctx->available_main = ctx->flex->height; | ||||
| 1075 | ctx->available_cross = available_width; | ||||
| 1076 | } | ||||
| 1077 | |||||
| 1078 | NSLOG(flex, DEEPDEBUG, "box %p: available_main: %i",do { if (NSLOG_LEVEL_DEEPDEBUG >= NSLOG_LEVEL_VERBOSE) { static nslog_entry_context_t _nslog_ctx = { &__nslog_category_flex , NSLOG_LEVEL_DEEPDEBUG, "content/handlers/html/layout_flex.c" , sizeof("content/handlers/html/layout_flex.c") - 1, __PRETTY_FUNCTION__ , sizeof(__PRETTY_FUNCTION__) - 1, 1079, }; nslog__log(&_nslog_ctx , "box %p: available_main: %i", flex, ctx->available_main) ; } } while(0) | ||||
| 1079 | flex, ctx->available_main)do { if (NSLOG_LEVEL_DEEPDEBUG >= NSLOG_LEVEL_VERBOSE) { static nslog_entry_context_t _nslog_ctx = { &__nslog_category_flex , NSLOG_LEVEL_DEEPDEBUG, "content/handlers/html/layout_flex.c" , sizeof("content/handlers/html/layout_flex.c") - 1, __PRETTY_FUNCTION__ , sizeof(__PRETTY_FUNCTION__) - 1, 1079, }; nslog__log(&_nslog_ctx , "box %p: available_main: %i", flex, ctx->available_main) ; } } while(0); | ||||
| 1080 | NSLOG(flex, DEEPDEBUG, "box %p: available_cross: %i",do { if (NSLOG_LEVEL_DEEPDEBUG >= NSLOG_LEVEL_VERBOSE) { static nslog_entry_context_t _nslog_ctx = { &__nslog_category_flex , NSLOG_LEVEL_DEEPDEBUG, "content/handlers/html/layout_flex.c" , sizeof("content/handlers/html/layout_flex.c") - 1, __PRETTY_FUNCTION__ , sizeof(__PRETTY_FUNCTION__) - 1, 1081, }; nslog__log(&_nslog_ctx , "box %p: available_cross: %i", flex, ctx->available_cross ); } } while(0) | ||||
| 1081 | flex, ctx->available_cross)do { if (NSLOG_LEVEL_DEEPDEBUG >= NSLOG_LEVEL_VERBOSE) { static nslog_entry_context_t _nslog_ctx = { &__nslog_category_flex , NSLOG_LEVEL_DEEPDEBUG, "content/handlers/html/layout_flex.c" , sizeof("content/handlers/html/layout_flex.c") - 1, __PRETTY_FUNCTION__ , sizeof(__PRETTY_FUNCTION__) - 1, 1081, }; nslog__log(&_nslog_ctx , "box %p: available_cross: %i", flex, ctx->available_cross ); } } while(0); | ||||
| 1082 | |||||
| 1083 | layout_flex_ctx__populate_item_data(ctx, flex, available_width); | ||||
| 1084 | |||||
| 1085 | /* Place items onto lines. */ | ||||
| 1086 | success = layout_flex__collect_items_into_lines(ctx); | ||||
| 1087 | if (!success) { | ||||
| 1088 | goto cleanup; | ||||
| 1089 | } | ||||
| 1090 | |||||
| 1091 | layout_flex__place_lines(ctx); | ||||
| 1092 | |||||
| 1093 | if (flex->height == AUTO(-2147483647 -1)) { | ||||
| 1094 | flex->height = ctx->horizontal ? | ||||
| 1095 | ctx->cross_size : | ||||
| 1096 | ctx->main_size; | ||||
| 1097 | } | ||||
| 1098 | |||||
| 1099 | if (flex->height != AUTO(-2147483647 -1)) { | ||||
| 1100 | if (max_height >= 0 && flex->height > max_height) { | ||||
| 1101 | flex->height = max_height; | ||||
| 1102 | } | ||||
| 1103 | if (min_height > 0 && flex->height < min_height) { | ||||
| 1104 | flex->height = min_height; | ||||
| 1105 | } | ||||
| 1106 | } | ||||
| 1107 | |||||
| 1108 | success = true1; | ||||
| 1109 | |||||
| 1110 | cleanup: | ||||
| 1111 | layout_flex_ctx__destroy(ctx); | ||||
| 1112 | |||||
| 1113 | NSLOG(flex, DEEPDEBUG, "box %p: %s: w: %i, h: %i", flex,do { if (NSLOG_LEVEL_DEEPDEBUG >= NSLOG_LEVEL_VERBOSE) { static nslog_entry_context_t _nslog_ctx = { &__nslog_category_flex , NSLOG_LEVEL_DEEPDEBUG, "content/handlers/html/layout_flex.c" , sizeof("content/handlers/html/layout_flex.c") - 1, __PRETTY_FUNCTION__ , sizeof(__PRETTY_FUNCTION__) - 1, 1115, }; nslog__log(&_nslog_ctx , "box %p: %s: w: %i, h: %i", flex, success ? "success" : "failure" , flex->width, flex->height); } } while(0) | ||||
| 1114 | success ? "success" : "failure",do { if (NSLOG_LEVEL_DEEPDEBUG >= NSLOG_LEVEL_VERBOSE) { static nslog_entry_context_t _nslog_ctx = { &__nslog_category_flex , NSLOG_LEVEL_DEEPDEBUG, "content/handlers/html/layout_flex.c" , sizeof("content/handlers/html/layout_flex.c") - 1, __PRETTY_FUNCTION__ , sizeof(__PRETTY_FUNCTION__) - 1, 1115, }; nslog__log(&_nslog_ctx , "box %p: %s: w: %i, h: %i", flex, success ? "success" : "failure" , flex->width, flex->height); } } while(0) | ||||
| 1115 | flex->width, flex->height)do { if (NSLOG_LEVEL_DEEPDEBUG >= NSLOG_LEVEL_VERBOSE) { static nslog_entry_context_t _nslog_ctx = { &__nslog_category_flex , NSLOG_LEVEL_DEEPDEBUG, "content/handlers/html/layout_flex.c" , sizeof("content/handlers/html/layout_flex.c") - 1, __PRETTY_FUNCTION__ , sizeof(__PRETTY_FUNCTION__) - 1, 1115, }; nslog__log(&_nslog_ctx , "box %p: %s: w: %i, h: %i", flex, success ? "success" : "failure" , flex->width, flex->height); } } while(0); | ||||
| 1116 | return success; | ||||
| 1117 | } |
| 1 | /* | |||
| 2 | * Copyright 2003 James Bursa <bursa@users.sourceforge.net> | |||
| 3 | * | |||
| 4 | * This file is part of NetSurf, http://www.netsurf-browser.org/ | |||
| 5 | * | |||
| 6 | * NetSurf is free software; you can redistribute it and/or modify | |||
| 7 | * it under the terms of the GNU General Public License as published by | |||
| 8 | * the Free Software Foundation; version 2 of the License. | |||
| 9 | * | |||
| 10 | * NetSurf is distributed in the hope that it will be useful, | |||
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
| 13 | * GNU General Public License for more details. | |||
| 14 | * | |||
| 15 | * You should have received a copy of the GNU General Public License | |||
| 16 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | |||
| 17 | */ | |||
| 18 | ||||
| 19 | /** | |||
| 20 | * \file | |||
| 21 | * HTML layout private interface. | |||
| 22 | */ | |||
| 23 | ||||
| 24 | #ifndef NETSURF_HTML_LAYOUT_INTERNAL_H | |||
| 25 | #define NETSURF_HTML_LAYOUT_INTERNAL_H | |||
| 26 | ||||
| 27 | #define AUTO(-2147483647 -1) INT_MIN(-2147483647 -1) | |||
| 28 | ||||
| 29 | /** | |||
| 30 | * Layout a block formatting context. | |||
| 31 | * | |||
| 32 | * \param block BLOCK, INLINE_BLOCK, or TABLE_CELL to layout | |||
| 33 | * \param viewport_height Height of viewport in pixels or -ve if unknown | |||
| 34 | * \param content Memory pool for any new boxes | |||
| 35 | * \return true on success, false on memory exhaustion | |||
| 36 | * | |||
| 37 | * This function carries out layout of a block and its children, as described | |||
| 38 | * in CSS 2.1 9.4.1. | |||
| 39 | */ | |||
| 40 | bool_Bool layout_block_context( | |||
| 41 | struct box *block, | |||
| 42 | int viewport_height, | |||
| 43 | html_content *content); | |||
| 44 | ||||
| 45 | /** | |||
| 46 | * Layout a table. | |||
| 47 | * | |||
| 48 | * \param table table to layout | |||
| 49 | * \param available_width width of containing block | |||
| 50 | * \param content memory pool for any new boxes | |||
| 51 | * \return true on success, false on memory exhaustion | |||
| 52 | */ | |||
| 53 | bool_Bool layout_table( | |||
| 54 | struct box *table, | |||
| 55 | int available_width, | |||
| 56 | html_content *content); | |||
| 57 | ||||
| 58 | /** | |||
| 59 | * Layout a flex container. | |||
| 60 | * | |||
| 61 | * \param[in] flex table to layout | |||
| 62 | * \param[in] available_width width of containing block | |||
| 63 | * \param[in] content memory pool for any new boxes | |||
| 64 | * \return true on success, false on memory exhaustion | |||
| 65 | */ | |||
| 66 | bool_Bool layout_flex( | |||
| 67 | struct box *flex, | |||
| 68 | int available_width, | |||
| 69 | html_content *content); | |||
| 70 | ||||
| 71 | typedef uint8_t (*css_len_func)( | |||
| 72 | const css_computed_style *style, | |||
| 73 | css_fixed *length, css_unit *unit); | |||
| 74 | typedef uint8_t (*css_border_style_func)( | |||
| 75 | const css_computed_style *style); | |||
| 76 | typedef uint8_t (*css_border_color_func)( | |||
| 77 | const css_computed_style *style, | |||
| 78 | css_color *color); | |||
| 79 | ||||
| 80 | /** Array of per-side access functions for computed style margins. */ | |||
| 81 | extern const css_len_func margin_funcs[4]; | |||
| 82 | ||||
| 83 | /** Array of per-side access functions for computed style paddings. */ | |||
| 84 | extern const css_len_func padding_funcs[4]; | |||
| 85 | ||||
| 86 | /** Array of per-side access functions for computed style border_widths. */ | |||
| 87 | extern const css_len_func border_width_funcs[4]; | |||
| 88 | ||||
| 89 | /** Array of per-side access functions for computed style border styles. */ | |||
| 90 | extern const css_border_style_func border_style_funcs[4]; | |||
| 91 | ||||
| 92 | /** Array of per-side access functions for computed style border colors. */ | |||
| 93 | extern const css_border_color_func border_color_funcs[4]; | |||
| 94 | ||||
| 95 | /** Layout helper: Check whether box is a float. */ | |||
| 96 | static inline bool_Bool lh__box_is_float_box(const struct box *b) | |||
| 97 | { | |||
| 98 | return b->type == BOX_FLOAT_LEFT || | |||
| 99 | b->type == BOX_FLOAT_RIGHT; | |||
| 100 | } | |||
| 101 | ||||
| 102 | /** Layout helper: Check whether box takes part in inline flow. */ | |||
| 103 | static inline bool_Bool lh__box_is_inline_flow(const struct box *b) | |||
| 104 | { | |||
| 105 | return b->type == BOX_INLINE || | |||
| 106 | b->type == BOX_INLINE_FLEX || | |||
| 107 | b->type == BOX_INLINE_BLOCK || | |||
| 108 | b->type == BOX_TEXT || | |||
| 109 | b->type == BOX_INLINE_END; | |||
| 110 | } | |||
| 111 | ||||
| 112 | /** Layout helper: Check whether box takes part in inline flow. */ | |||
| 113 | static inline bool_Bool lh__box_is_flex_container(const struct box *b) | |||
| 114 | { | |||
| 115 | return b->type == BOX_FLEX || | |||
| 116 | b->type == BOX_INLINE_FLEX; | |||
| 117 | } | |||
| 118 | ||||
| 119 | /** Layout helper: Check whether box takes part in inline flow. */ | |||
| 120 | static inline bool_Bool lh__box_is_flex_item(const struct box *b) | |||
| 121 | { | |||
| 122 | return (b->parent != NULL((void*)0)) && lh__box_is_flex_container(b->parent); | |||
| 123 | } | |||
| 124 | ||||
| 125 | /** Layout helper: Check whether box is inline level. (Includes BR.) */ | |||
| 126 | static inline bool_Bool lh__box_is_inline_level(const struct box *b) | |||
| 127 | { | |||
| 128 | return lh__box_is_inline_flow(b) || | |||
| 129 | b->type == BOX_BR; | |||
| 130 | } | |||
| 131 | ||||
| 132 | /** Layout helper: Check whether box is inline level. (Includes BR, floats.) */ | |||
| 133 | static inline bool_Bool lh__box_is_inline_content(const struct box *b) | |||
| 134 | { | |||
| 135 | return lh__box_is_float_box(b) || | |||
| 136 | lh__box_is_inline_level(b); | |||
| 137 | } | |||
| 138 | ||||
| 139 | /** Layout helper: Check whether box is an object. */ | |||
| 140 | static inline bool_Bool lh__box_is_object(const struct box *b) | |||
| 141 | { | |||
| 142 | return b->object || | |||
| 143 | (b->flags & (IFRAME | REPLACE_DIM)); | |||
| 144 | } | |||
| 145 | ||||
| 146 | /** Layout helper: Check whether box is replaced. */ | |||
| 147 | static inline bool_Bool lh__box_is_replace(const struct box *b) | |||
| 148 | { | |||
| 149 | return b->gadget || | |||
| 150 | lh__box_is_object(b); | |||
| 151 | } | |||
| 152 | ||||
| 153 | /** Layout helper: Check for CSS border on given side. */ | |||
| 154 | static inline bool_Bool lh__have_border( | |||
| 155 | enum box_side side, | |||
| 156 | const css_computed_style *style) | |||
| 157 | { | |||
| 158 | return border_style_funcs[side](style) != CSS_BORDER_STYLE_NONE; | |||
| 159 | } | |||
| 160 | ||||
| 161 | static inline bool_Bool lh__box_is_absolute(const struct box *b) | |||
| 162 | { | |||
| 163 | return css_computed_position(b->style) == CSS_POSITION_ABSOLUTE || | |||
| ||||
| 164 | css_computed_position(b->style) == CSS_POSITION_FIXED; | |||
| 165 | } | |||
| 166 | ||||
| 167 | static inline bool_Bool lh__flex_main_is_horizontal(const struct box *flex) | |||
| 168 | { | |||
| 169 | const css_computed_style *style = flex->style; | |||
| 170 | ||||
| 171 | assert(style != NULL)((style != ((void*)0)) ? (void) (0) : __assert_fail ("style != NULL" , "content/handlers/html/layout_internal.h", 171, __extension__ __PRETTY_FUNCTION__)); | |||
| 172 | ||||
| 173 | switch (css_computed_flex_direction(style)) { | |||
| 174 | default: /* Fallthrough. */ | |||
| 175 | case CSS_FLEX_DIRECTION_ROW: /* Fallthrough. */ | |||
| 176 | case CSS_FLEX_DIRECTION_ROW_REVERSE: | |||
| 177 | return true1; | |||
| 178 | case CSS_FLEX_DIRECTION_COLUMN: /* Fallthrough. */ | |||
| 179 | case CSS_FLEX_DIRECTION_COLUMN_REVERSE: | |||
| 180 | return false0; | |||
| 181 | } | |||
| 182 | } | |||
| 183 | ||||
| 184 | static inline bool_Bool lh__flex_direction_reversed(const struct box *flex) | |||
| 185 | { | |||
| 186 | switch (css_computed_flex_direction(flex->style)) { | |||
| 187 | default: /* Fallthrough. */ | |||
| 188 | case CSS_FLEX_DIRECTION_ROW_REVERSE: /* Fallthrough. */ | |||
| 189 | case CSS_FLEX_DIRECTION_COLUMN_REVERSE: | |||
| 190 | return true1; | |||
| 191 | case CSS_FLEX_DIRECTION_ROW: /* Fallthrough. */ | |||
| 192 | case CSS_FLEX_DIRECTION_COLUMN: | |||
| 193 | return false0; | |||
| 194 | } | |||
| 195 | } | |||
| 196 | ||||
| 197 | static inline int lh__non_auto_margin(const struct box *b, enum box_side side) | |||
| 198 | { | |||
| 199 | return (b->margin[side] == AUTO(-2147483647 -1)) ? 0 : b->margin[side]; | |||
| 200 | } | |||
| 201 | ||||
| 202 | static inline int lh__delta_outer_height(const struct box *b) | |||
| 203 | { | |||
| 204 | return b->padding[TOP] + | |||
| 205 | b->padding[BOTTOM] + | |||
| 206 | b->border[TOP].width + | |||
| 207 | b->border[BOTTOM].width + | |||
| 208 | lh__non_auto_margin(b, TOP) + | |||
| 209 | lh__non_auto_margin(b, BOTTOM); | |||
| 210 | } | |||
| 211 | ||||
| 212 | static inline int lh__delta_outer_width(const struct box *b) | |||
| 213 | { | |||
| 214 | return b->padding[LEFT] + | |||
| 215 | b->padding[RIGHT] + | |||
| 216 | b->border[LEFT].width + | |||
| 217 | b->border[RIGHT].width + | |||
| 218 | lh__non_auto_margin(b, LEFT) + | |||
| 219 | lh__non_auto_margin(b, RIGHT); | |||
| 220 | } | |||
| 221 | ||||
| 222 | static inline int lh__delta_outer_main( | |||
| 223 | const struct box *flex, | |||
| 224 | const struct box *b) | |||
| 225 | { | |||
| 226 | if (lh__flex_main_is_horizontal(flex)) { | |||
| 227 | return lh__delta_outer_width(b); | |||
| 228 | } else { | |||
| 229 | return lh__delta_outer_height(b); | |||
| 230 | } | |||
| 231 | } | |||
| 232 | ||||
| 233 | static inline int lh__delta_outer_cross( | |||
| 234 | const struct box *flex, | |||
| 235 | const struct box *b) | |||
| 236 | { | |||
| 237 | if (lh__flex_main_is_horizontal(flex) == false0) { | |||
| 238 | return lh__delta_outer_width(b); | |||
| 239 | } else { | |||
| 240 | return lh__delta_outer_height(b); | |||
| 241 | } | |||
| 242 | } | |||
| 243 | ||||
| 244 | static inline int *lh__box_size_main_ptr( | |||
| 245 | bool_Bool horizontal, | |||
| 246 | struct box *b) | |||
| 247 | { | |||
| 248 | return horizontal ? &b->width : &b->height; | |||
| 249 | } | |||
| 250 | ||||
| 251 | static inline int *lh__box_size_cross_ptr( | |||
| 252 | bool_Bool horizontal, | |||
| 253 | struct box *b) | |||
| 254 | { | |||
| 255 | return horizontal ? &b->height : &b->width; | |||
| 256 | } | |||
| 257 | ||||
| 258 | static inline int lh__box_size_main( | |||
| 259 | bool_Bool horizontal, | |||
| 260 | const struct box *b) | |||
| 261 | { | |||
| 262 | return horizontal ? b->width : b->height; | |||
| 263 | } | |||
| 264 | ||||
| 265 | static inline int lh__box_size_cross( | |||
| 266 | bool_Bool horizontal, | |||
| 267 | const struct box *b) | |||
| 268 | { | |||
| 269 | return horizontal ? b->height : b->width; | |||
| 270 | } | |||
| 271 | ||||
| 272 | static inline bool_Bool lh__box_size_cross_is_auto( | |||
| 273 | bool_Bool horizontal, | |||
| 274 | struct box *b) | |||
| 275 | { | |||
| 276 | css_fixed length; | |||
| 277 | css_unit unit; | |||
| 278 | ||||
| 279 | if (horizontal) { | |||
| 280 | return css_computed_height(b->style, | |||
| 281 | &length, &unit) == CSS_HEIGHT_AUTO; | |||
| 282 | } else { | |||
| 283 | return css_computed_width(b->style, | |||
| 284 | &length, &unit) == CSS_WIDTH_AUTO; | |||
| 285 | } | |||
| 286 | } | |||
| 287 | ||||
| 288 | static inline enum css_align_self_e lh__box_align_self( | |||
| 289 | const struct box *flex, | |||
| 290 | const struct box *item) | |||
| 291 | { | |||
| 292 | enum css_align_self_e align_self = css_computed_align_self(item->style); | |||
| 293 | ||||
| 294 | if (align_self == CSS_ALIGN_SELF_AUTO) { | |||
| 295 | align_self = css_computed_align_items(flex->style); | |||
| 296 | } | |||
| 297 | ||||
| 298 | return align_self; | |||
| 299 | } | |||
| 300 | ||||
| 301 | /** | |||
| 302 | * Determine width of margin, borders, and padding on one side of a box. | |||
| 303 | * | |||
| 304 | * \param unit_len_ctx CSS length conversion context for document | |||
| 305 | * \param style style to measure | |||
| 306 | * \param side side of box to measure | |||
| 307 | * \param margin whether margin width is required | |||
| 308 | * \param border whether border width is required | |||
| 309 | * \param padding whether padding width is required | |||
| 310 | * \param fixed increased by sum of fixed margin, border, and padding | |||
| 311 | * \param frac increased by sum of fractional margin and padding | |||
| 312 | */ | |||
| 313 | static inline void calculate_mbp_width( | |||
| 314 | const css_unit_ctx *unit_len_ctx, | |||
| 315 | const css_computed_style *style, | |||
| 316 | unsigned int side, | |||
| 317 | bool_Bool margin, | |||
| 318 | bool_Bool border, | |||
| 319 | bool_Bool padding, | |||
| 320 | int *fixed, | |||
| 321 | float *frac) | |||
| 322 | { | |||
| 323 | css_fixed value = 0; | |||
| 324 | css_unit unit = CSS_UNIT_PX; | |||
| 325 | ||||
| 326 | assert(style)((style) ? (void) (0) : __assert_fail ("style", "content/handlers/html/layout_internal.h" , 326, __extension__ __PRETTY_FUNCTION__)); | |||
| 327 | ||||
| 328 | /* margin */ | |||
| 329 | if (margin) { | |||
| 330 | enum css_margin_e type; | |||
| 331 | ||||
| 332 | type = margin_funcs[side](style, &value, &unit); | |||
| 333 | if (type == CSS_MARGIN_SET) { | |||
| 334 | if (unit == CSS_UNIT_PCT) { | |||
| 335 | *frac += FIXTOFLT(FDIV(value, F_100))((float) ((css_divide_fixed((value), (0x00019000)))) / (float ) (1 << 10)); | |||
| 336 | } else { | |||
| 337 | *fixed += FIXTOINT(css_unit_len2device_px(((css_unit_len2device_px( style, unit_len_ctx, value, unit)) >> 10) | |||
| 338 | style, unit_len_ctx,((css_unit_len2device_px( style, unit_len_ctx, value, unit)) >> 10) | |||
| 339 | value, unit))((css_unit_len2device_px( style, unit_len_ctx, value, unit)) >> 10); | |||
| 340 | } | |||
| 341 | } | |||
| 342 | } | |||
| 343 | ||||
| 344 | /* border */ | |||
| 345 | if (border) { | |||
| 346 | if (lh__have_border(side, style)) { | |||
| 347 | border_width_funcs[side](style, &value, &unit); | |||
| 348 | ||||
| 349 | *fixed += FIXTOINT(css_unit_len2device_px(((css_unit_len2device_px( style, unit_len_ctx, value, unit)) >> 10) | |||
| 350 | style, unit_len_ctx,((css_unit_len2device_px( style, unit_len_ctx, value, unit)) >> 10) | |||
| 351 | value, unit))((css_unit_len2device_px( style, unit_len_ctx, value, unit)) >> 10); | |||
| 352 | } | |||
| 353 | } | |||
| 354 | ||||
| 355 | /* padding */ | |||
| 356 | if (padding) { | |||
| 357 | padding_funcs[side](style, &value, &unit); | |||
| 358 | if (unit == CSS_UNIT_PCT) { | |||
| 359 | *frac += FIXTOFLT(FDIV(value, F_100))((float) ((css_divide_fixed((value), (0x00019000)))) / (float ) (1 << 10)); | |||
| 360 | } else { | |||
| 361 | *fixed += FIXTOINT(css_unit_len2device_px(((css_unit_len2device_px( style, unit_len_ctx, value, unit)) >> 10) | |||
| 362 | style, unit_len_ctx,((css_unit_len2device_px( style, unit_len_ctx, value, unit)) >> 10) | |||
| 363 | value, unit))((css_unit_len2device_px( style, unit_len_ctx, value, unit)) >> 10); | |||
| 364 | } | |||
| 365 | } | |||
| 366 | } | |||
| 367 | ||||
| 368 | /** | |||
| 369 | * Adjust a specified width or height for the box-sizing property. | |||
| 370 | * | |||
| 371 | * This turns the specified dimension into a content-box dimension. | |||
| 372 | * | |||
| 373 | * \param unit_len_ctx Length conversion context | |||
| 374 | * \param box gadget to adjust dimensions of | |||
| 375 | * \param available_width width of containing block | |||
| 376 | * \param setwidth set true if the dimension to be tweaked is a width, | |||
| 377 | * else set false for a height | |||
| 378 | * \param dimension current value for given width/height dimension. | |||
| 379 | * updated to new value after consideration of | |||
| 380 | * gadget properties. | |||
| 381 | */ | |||
| 382 | static inline void layout_handle_box_sizing( | |||
| 383 | const css_unit_ctx *unit_len_ctx, | |||
| 384 | const struct box *box, | |||
| 385 | int available_width, | |||
| 386 | bool_Bool setwidth, | |||
| 387 | int *dimension) | |||
| 388 | { | |||
| 389 | enum css_box_sizing_e bs; | |||
| 390 | ||||
| 391 | assert(box && box->style)((box && box->style) ? (void) (0) : __assert_fail ( "box && box->style", "content/handlers/html/layout_internal.h" , 391, __extension__ __PRETTY_FUNCTION__)); | |||
| 392 | ||||
| 393 | bs = css_computed_box_sizing(box->style); | |||
| 394 | ||||
| 395 | if (bs == CSS_BOX_SIZING_BORDER_BOX) { | |||
| 396 | int orig = *dimension; | |||
| 397 | int fixed = 0; | |||
| 398 | float frac = 0; | |||
| 399 | ||||
| 400 | calculate_mbp_width(unit_len_ctx, box->style, | |||
| 401 | setwidth ? LEFT : TOP, | |||
| 402 | false0, true1, true1, &fixed, &frac); | |||
| 403 | calculate_mbp_width(unit_len_ctx, box->style, | |||
| 404 | setwidth ? RIGHT : BOTTOM, | |||
| 405 | false0, true1, true1, &fixed, &frac); | |||
| 406 | orig -= frac * available_width + fixed; | |||
| 407 | *dimension = orig > 0 ? orig : 0; | |||
| 408 | } | |||
| 409 | } | |||
| 410 | ||||
| 411 | /** | |||
| 412 | * Calculate width, height, and thickness of margins, paddings, and borders. | |||
| 413 | * | |||
| 414 | * \param unit_len_ctx Length conversion context | |||
| 415 | * \param available_width width of containing block | |||
| 416 | * \param viewport_height height of viewport in pixels or -ve if unknown | |||
| 417 | * \param box current box | |||
| 418 | * \param style style giving width, height, margins, paddings, | |||
| 419 | * and borders | |||
| 420 | * \param width updated to width, may be NULL | |||
| 421 | * \param height updated to height, may be NULL | |||
| 422 | * \param max_width updated to max-width, may be NULL | |||
| 423 | * \param min_width updated to min-width, may be NULL | |||
| 424 | * \param max_height updated to max-height, may be NULL | |||
| 425 | * \param min_height updated to min-height, may be NULL | |||
| 426 | * \param margin filled with margins, may be NULL | |||
| 427 | * \param padding filled with paddings, may be NULL | |||
| 428 | * \param border filled with border widths, may be NULL | |||
| 429 | */ | |||
| 430 | static inline void layout_find_dimensions( | |||
| 431 | const css_unit_ctx *unit_len_ctx, | |||
| 432 | int available_width, | |||
| 433 | int viewport_height, | |||
| 434 | const struct box *box, | |||
| 435 | const css_computed_style *style, | |||
| 436 | int *width, | |||
| 437 | int *height, | |||
| 438 | int *max_width, | |||
| 439 | int *min_width, | |||
| 440 | int *max_height, | |||
| 441 | int *min_height, | |||
| 442 | int margin[4], | |||
| 443 | int padding[4], | |||
| 444 | struct box_border border[4]) | |||
| 445 | { | |||
| 446 | struct box *containing_block = NULL((void*)0); | |||
| 447 | unsigned int i; | |||
| 448 | ||||
| 449 | if (width) { | |||
| 450 | if (css_computed_width_px(style, unit_len_ctx, | |||
| 451 | available_width, width) == CSS_WIDTH_SET) { | |||
| 452 | layout_handle_box_sizing(unit_len_ctx, box, | |||
| 453 | available_width, true1, width); | |||
| 454 | } else { | |||
| 455 | *width = AUTO(-2147483647 -1); | |||
| 456 | } | |||
| 457 | } | |||
| 458 | ||||
| 459 | if (height) { | |||
| 460 | enum css_height_e htype; | |||
| 461 | css_fixed value = 0; | |||
| 462 | css_unit unit = CSS_UNIT_PX; | |||
| 463 | ||||
| 464 | htype = css_computed_height(style, &value, &unit); | |||
| 465 | ||||
| 466 | if (htype == CSS_HEIGHT_SET) { | |||
| 467 | if (unit == CSS_UNIT_PCT) { | |||
| 468 | enum css_height_e cbhtype; | |||
| 469 | ||||
| 470 | if (css_computed_position(box->style) == | |||
| 471 | CSS_POSITION_ABSOLUTE && | |||
| 472 | box->parent) { | |||
| 473 | /* Box is absolutely positioned */ | |||
| 474 | assert(box->float_container)((box->float_container) ? (void) (0) : __assert_fail ("box->float_container" , "content/handlers/html/layout_internal.h", 474, __extension__ __PRETTY_FUNCTION__)); | |||
| 475 | containing_block = box->float_container; | |||
| 476 | } else if (box->float_container && | |||
| 477 | css_computed_position(box->style) != | |||
| 478 | CSS_POSITION_ABSOLUTE && | |||
| 479 | (css_computed_float(box->style) == | |||
| 480 | CSS_FLOAT_LEFT || | |||
| 481 | css_computed_float(box->style) == | |||
| 482 | CSS_FLOAT_RIGHT)) { | |||
| 483 | /* Box is a float */ | |||
| 484 | assert(box->parent &&((box->parent && box->parent->parent && box->parent->parent->parent) ? (void) (0) : __assert_fail ("box->parent && box->parent->parent && box->parent->parent->parent" , "content/handlers/html/layout_internal.h", 486, __extension__ __PRETTY_FUNCTION__)) | |||
| 485 | box->parent->parent &&((box->parent && box->parent->parent && box->parent->parent->parent) ? (void) (0) : __assert_fail ("box->parent && box->parent->parent && box->parent->parent->parent" , "content/handlers/html/layout_internal.h", 486, __extension__ __PRETTY_FUNCTION__)) | |||
| 486 | box->parent->parent->parent)((box->parent && box->parent->parent && box->parent->parent->parent) ? (void) (0) : __assert_fail ("box->parent && box->parent->parent && box->parent->parent->parent" , "content/handlers/html/layout_internal.h", 486, __extension__ __PRETTY_FUNCTION__)); | |||
| 487 | ||||
| 488 | containing_block = | |||
| 489 | box->parent->parent->parent; | |||
| 490 | } else if (box->parent && box->parent->type != | |||
| 491 | BOX_INLINE_CONTAINER) { | |||
| 492 | /* Box is a block level element */ | |||
| 493 | containing_block = box->parent; | |||
| 494 | } else if (box->parent && box->parent->type == | |||
| 495 | BOX_INLINE_CONTAINER) { | |||
| 496 | /* Box is an inline block */ | |||
| 497 | assert(box->parent->parent)((box->parent->parent) ? (void) (0) : __assert_fail ("box->parent->parent" , "content/handlers/html/layout_internal.h", 497, __extension__ __PRETTY_FUNCTION__)); | |||
| 498 | containing_block = box->parent->parent; | |||
| 499 | } | |||
| 500 | ||||
| 501 | if (containing_block) { | |||
| 502 | css_fixed f = 0; | |||
| 503 | css_unit u = CSS_UNIT_PX; | |||
| 504 | ||||
| 505 | cbhtype = css_computed_height( | |||
| 506 | containing_block->style, | |||
| 507 | &f, &u); | |||
| 508 | } | |||
| 509 | ||||
| 510 | if (containing_block && | |||
| 511 | containing_block->height != AUTO(-2147483647 -1) && | |||
| 512 | (css_computed_position(box->style) == | |||
| 513 | CSS_POSITION_ABSOLUTE || | |||
| 514 | cbhtype == CSS_HEIGHT_SET)) { | |||
| 515 | /* Box is absolutely positioned or its | |||
| 516 | * containing block has a valid | |||
| 517 | * specified height. | |||
| 518 | * (CSS 2.1 Section 10.5) */ | |||
| 519 | *height = FPCT_OF_INT_TOINT(value,((((css_divide_fixed(((value * containing_block->height)), (0x00019000)))) >> 10)) | |||
| 520 | containing_block->height)((((css_divide_fixed(((value * containing_block->height)), (0x00019000)))) >> 10)); | |||
| 521 | } else if ((!box->parent || | |||
| 522 | !box->parent->parent) && | |||
| 523 | viewport_height >= 0) { | |||
| 524 | /* If root element or it's child | |||
| 525 | * (HTML or BODY) */ | |||
| 526 | *height = FPCT_OF_INT_TOINT(value,((((css_divide_fixed(((value * viewport_height)), (0x00019000 )))) >> 10)) | |||
| 527 | viewport_height)((((css_divide_fixed(((value * viewport_height)), (0x00019000 )))) >> 10)); | |||
| 528 | } else { | |||
| 529 | /* precentage height not permissible | |||
| 530 | * treat height as auto */ | |||
| 531 | *height = AUTO(-2147483647 -1); | |||
| 532 | } | |||
| 533 | } else { | |||
| 534 | *height = FIXTOINT(css_unit_len2device_px(((css_unit_len2device_px( style, unit_len_ctx, value, unit)) >> 10) | |||
| 535 | style, unit_len_ctx,((css_unit_len2device_px( style, unit_len_ctx, value, unit)) >> 10) | |||
| 536 | value, unit))((css_unit_len2device_px( style, unit_len_ctx, value, unit)) >> 10); | |||
| 537 | } | |||
| 538 | } else { | |||
| 539 | *height = AUTO(-2147483647 -1); | |||
| 540 | } | |||
| 541 | ||||
| 542 | if (*height != AUTO(-2147483647 -1)) { | |||
| 543 | layout_handle_box_sizing(unit_len_ctx, box, | |||
| 544 | available_width, false0, height); | |||
| 545 | } | |||
| 546 | } | |||
| 547 | ||||
| 548 | if (max_width) { | |||
| 549 | enum css_max_width_e type; | |||
| 550 | css_fixed value = 0; | |||
| 551 | css_unit unit = CSS_UNIT_PX; | |||
| 552 | ||||
| 553 | type = css_computed_max_width(style, &value, &unit); | |||
| 554 | ||||
| 555 | if (type == CSS_MAX_WIDTH_SET) { | |||
| 556 | if (unit == CSS_UNIT_PCT) { | |||
| 557 | *max_width = FPCT_OF_INT_TOINT(value,((((css_divide_fixed(((value * available_width)), (0x00019000 )))) >> 10)) | |||
| 558 | available_width)((((css_divide_fixed(((value * available_width)), (0x00019000 )))) >> 10)); | |||
| 559 | } else { | |||
| 560 | *max_width = FIXTOINT(css_unit_len2device_px(((css_unit_len2device_px( style, unit_len_ctx, value, unit)) >> 10) | |||
| 561 | style, unit_len_ctx,((css_unit_len2device_px( style, unit_len_ctx, value, unit)) >> 10) | |||
| 562 | value, unit))((css_unit_len2device_px( style, unit_len_ctx, value, unit)) >> 10); | |||
| 563 | } | |||
| 564 | } else { | |||
| 565 | /* Inadmissible */ | |||
| 566 | *max_width = -1; | |||
| 567 | } | |||
| 568 | ||||
| 569 | if (*max_width != -1) { | |||
| 570 | layout_handle_box_sizing(unit_len_ctx, box, | |||
| 571 | available_width, true1, max_width); | |||
| 572 | } | |||
| 573 | } | |||
| 574 | ||||
| 575 | if (min_width) { | |||
| 576 | enum css_min_width_e type; | |||
| 577 | css_fixed value = 0; | |||
| 578 | css_unit unit = CSS_UNIT_PX; | |||
| 579 | ||||
| 580 | type = ns_computed_min_width(style, &value, &unit); | |||
| 581 | ||||
| 582 | if (type == CSS_MIN_WIDTH_SET) { | |||
| 583 | if (unit == CSS_UNIT_PCT) { | |||
| 584 | *min_width = FPCT_OF_INT_TOINT(value,((((css_divide_fixed(((value * available_width)), (0x00019000 )))) >> 10)) | |||
| 585 | available_width)((((css_divide_fixed(((value * available_width)), (0x00019000 )))) >> 10)); | |||
| 586 | } else { | |||
| 587 | *min_width = FIXTOINT(css_unit_len2device_px(((css_unit_len2device_px( style, unit_len_ctx, value, unit)) >> 10) | |||
| 588 | style, unit_len_ctx,((css_unit_len2device_px( style, unit_len_ctx, value, unit)) >> 10) | |||
| 589 | value, unit))((css_unit_len2device_px( style, unit_len_ctx, value, unit)) >> 10); | |||
| 590 | } | |||
| 591 | } else { | |||
| 592 | /* Inadmissible */ | |||
| 593 | *min_width = 0; | |||
| 594 | } | |||
| 595 | ||||
| 596 | if (*min_width != 0) { | |||
| 597 | layout_handle_box_sizing(unit_len_ctx, box, | |||
| 598 | available_width, true1, min_width); | |||
| 599 | } | |||
| 600 | } | |||
| 601 | ||||
| 602 | if (max_height) { | |||
| 603 | enum css_max_height_e type; | |||
| 604 | css_fixed value = 0; | |||
| 605 | css_unit unit = CSS_UNIT_PX; | |||
| 606 | ||||
| 607 | type = css_computed_max_height(style, &value, &unit); | |||
| 608 | ||||
| 609 | if (type == CSS_MAX_HEIGHT_SET) { | |||
| 610 | if (unit == CSS_UNIT_PCT) { | |||
| 611 | /* TODO: handle percentage */ | |||
| 612 | *max_height = -1; | |||
| 613 | } else { | |||
| 614 | *max_height = FIXTOINT(css_unit_len2device_px(((css_unit_len2device_px( style, unit_len_ctx, value, unit)) >> 10) | |||
| 615 | style, unit_len_ctx,((css_unit_len2device_px( style, unit_len_ctx, value, unit)) >> 10) | |||
| 616 | value, unit))((css_unit_len2device_px( style, unit_len_ctx, value, unit)) >> 10); | |||
| 617 | } | |||
| 618 | } else { | |||
| 619 | /* Inadmissible */ | |||
| 620 | *max_height = -1; | |||
| 621 | } | |||
| 622 | } | |||
| 623 | ||||
| 624 | if (min_height) { | |||
| 625 | enum css_min_height_e type; | |||
| 626 | css_fixed value = 0; | |||
| 627 | css_unit unit = CSS_UNIT_PX; | |||
| 628 | ||||
| 629 | type = ns_computed_min_height(style, &value, &unit); | |||
| 630 | ||||
| 631 | if (type == CSS_MIN_HEIGHT_SET) { | |||
| 632 | if (unit == CSS_UNIT_PCT) { | |||
| 633 | /* TODO: handle percentage */ | |||
| 634 | *min_height = 0; | |||
| 635 | } else { | |||
| 636 | *min_height = FIXTOINT(css_unit_len2device_px(((css_unit_len2device_px( style, unit_len_ctx, value, unit)) >> 10) | |||
| 637 | style, unit_len_ctx,((css_unit_len2device_px( style, unit_len_ctx, value, unit)) >> 10) | |||
| 638 | value, unit))((css_unit_len2device_px( style, unit_len_ctx, value, unit)) >> 10); | |||
| 639 | } | |||
| 640 | } else { | |||
| 641 | /* Inadmissible */ | |||
| 642 | *min_height = 0; | |||
| 643 | } | |||
| 644 | } | |||
| 645 | ||||
| 646 | for (i = 0; i != 4; i++) { | |||
| 647 | if (margin) { | |||
| 648 | enum css_margin_e type = CSS_MARGIN_AUTO; | |||
| 649 | css_fixed value = 0; | |||
| 650 | css_unit unit = CSS_UNIT_PX; | |||
| 651 | ||||
| 652 | type = margin_funcs[i](style, &value, &unit); | |||
| 653 | ||||
| 654 | if (type == CSS_MARGIN_SET) { | |||
| 655 | if (unit == CSS_UNIT_PCT) { | |||
| 656 | margin[i] = FPCT_OF_INT_TOINT(value,((((css_divide_fixed(((value * available_width)), (0x00019000 )))) >> 10)) | |||
| 657 | available_width)((((css_divide_fixed(((value * available_width)), (0x00019000 )))) >> 10)); | |||
| 658 | } else { | |||
| 659 | margin[i] = FIXTOINT(css_unit_len2device_px(((css_unit_len2device_px( style, unit_len_ctx, value, unit)) >> 10) | |||
| 660 | style, unit_len_ctx,((css_unit_len2device_px( style, unit_len_ctx, value, unit)) >> 10) | |||
| 661 | value, unit))((css_unit_len2device_px( style, unit_len_ctx, value, unit)) >> 10); | |||
| 662 | } | |||
| 663 | } else { | |||
| 664 | margin[i] = AUTO(-2147483647 -1); | |||
| 665 | } | |||
| 666 | } | |||
| 667 | ||||
| 668 | if (padding) { | |||
| 669 | css_fixed value = 0; | |||
| 670 | css_unit unit = CSS_UNIT_PX; | |||
| 671 | ||||
| 672 | padding_funcs[i](style, &value, &unit); | |||
| 673 | ||||
| 674 | if (unit == CSS_UNIT_PCT) { | |||
| 675 | padding[i] = FPCT_OF_INT_TOINT(value,((((css_divide_fixed(((value * available_width)), (0x00019000 )))) >> 10)) | |||
| 676 | available_width)((((css_divide_fixed(((value * available_width)), (0x00019000 )))) >> 10)); | |||
| 677 | } else { | |||
| 678 | padding[i] = FIXTOINT(css_unit_len2device_px(((css_unit_len2device_px( style, unit_len_ctx, value, unit)) >> 10) | |||
| 679 | style, unit_len_ctx,((css_unit_len2device_px( style, unit_len_ctx, value, unit)) >> 10) | |||
| 680 | value, unit))((css_unit_len2device_px( style, unit_len_ctx, value, unit)) >> 10); | |||
| 681 | } | |||
| 682 | } | |||
| 683 | ||||
| 684 | /* Table cell borders are populated in table.c */ | |||
| 685 | if (border && box->type != BOX_TABLE_CELL) { | |||
| 686 | enum css_border_style_e bstyle = CSS_BORDER_STYLE_NONE; | |||
| 687 | css_color color = 0; | |||
| 688 | css_fixed value = 0; | |||
| 689 | css_unit unit = CSS_UNIT_PX; | |||
| 690 | ||||
| 691 | border_width_funcs[i](style, &value, &unit); | |||
| 692 | bstyle = border_style_funcs[i](style); | |||
| 693 | border_color_funcs[i](style, &color); | |||
| 694 | ||||
| 695 | border[i].style = bstyle; | |||
| 696 | border[i].c = color; | |||
| 697 | ||||
| 698 | if (bstyle == CSS_BORDER_STYLE_HIDDEN || | |||
| 699 | bstyle == CSS_BORDER_STYLE_NONE) | |||
| 700 | /* spec unclear: following Mozilla */ | |||
| 701 | border[i].width = 0; | |||
| 702 | else | |||
| 703 | border[i].width = FIXTOINT(css_unit_len2device_px(((css_unit_len2device_px( style, unit_len_ctx, value, unit)) >> 10) | |||
| 704 | style, unit_len_ctx,((css_unit_len2device_px( style, unit_len_ctx, value, unit)) >> 10) | |||
| 705 | value, unit))((css_unit_len2device_px( style, unit_len_ctx, value, unit)) >> 10); | |||
| 706 | ||||
| 707 | /* Special case for border-collapse: make all borders | |||
| 708 | * on table/table-row-group/table-row zero width. */ | |||
| 709 | if (css_computed_border_collapse(style) == | |||
| 710 | CSS_BORDER_COLLAPSE_COLLAPSE && | |||
| 711 | (box->type == BOX_TABLE || | |||
| 712 | box->type == BOX_TABLE_ROW_GROUP || | |||
| 713 | box->type == BOX_TABLE_ROW)) | |||
| 714 | border[i].width = 0; | |||
| 715 | } | |||
| 716 | } | |||
| 717 | } | |||
| 718 | ||||
| 719 | #endif |