| File: | content/handlers/html/box_inspect.c |
| Warning: | line 894, column 10 The right operand of '-' is a garbage value |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
| 1 | /* | |||
| 2 | * Copyright 2008 Michael Drake <tlsa@netsurf-browser.org> | |||
| 3 | * Copyright 2020 Vincent Sanders <vince@netsurf-browser.org> | |||
| 4 | * | |||
| 5 | * This file is part of NetSurf, http://www.netsurf-browser.org/ | |||
| 6 | * | |||
| 7 | * NetSurf is free software; you can redistribute it and/or modify | |||
| 8 | * it under the terms of the GNU General Public License as published by | |||
| 9 | * the Free Software Foundation; version 2 of the License. | |||
| 10 | * | |||
| 11 | * NetSurf is distributed in the hope that it will be useful, | |||
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
| 14 | * GNU General Public License for more details. | |||
| 15 | * | |||
| 16 | * You should have received a copy of the GNU General Public License | |||
| 17 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | |||
| 18 | */ | |||
| 19 | ||||
| 20 | /** | |||
| 21 | * \file | |||
| 22 | * implementation of box tree inspection. | |||
| 23 | */ | |||
| 24 | ||||
| 25 | #include <stdio.h> | |||
| 26 | #include <dom/dom.h> | |||
| 27 | ||||
| 28 | #include "utils/utils.h" | |||
| 29 | #include "utils/nsurl.h" | |||
| 30 | #include "utils/errors.h" | |||
| 31 | #include "netsurf/types.h" | |||
| 32 | #include "netsurf/content.h" | |||
| 33 | #include "netsurf/mouse.h" | |||
| 34 | #include "css/utils.h" | |||
| 35 | #include "css/dump.h" | |||
| 36 | #include "desktop/scrollbar.h" | |||
| 37 | ||||
| 38 | #include "html/private.h" | |||
| 39 | #include "html/box.h" | |||
| 40 | #include "html/box_inspect.h" | |||
| 41 | ||||
| 42 | /** | |||
| 43 | * Direction to move in a box-tree walk | |||
| 44 | */ | |||
| 45 | enum box_walk_dir { | |||
| 46 | BOX_WALK_CHILDREN, | |||
| 47 | BOX_WALK_PARENT, | |||
| 48 | BOX_WALK_NEXT_SIBLING, | |||
| 49 | BOX_WALK_FLOAT_CHILDREN, | |||
| 50 | BOX_WALK_NEXT_FLOAT_SIBLING, | |||
| 51 | BOX_WALK_FLOAT_CONTAINER | |||
| 52 | }; | |||
| 53 | ||||
| 54 | #define box_is_float(box)(box->type == BOX_FLOAT_LEFT || box->type == BOX_FLOAT_RIGHT ) (box->type == BOX_FLOAT_LEFT || \ | |||
| 55 | box->type == BOX_FLOAT_RIGHT) | |||
| 56 | ||||
| 57 | /** | |||
| 58 | * Determine if a point lies within a box. | |||
| 59 | * | |||
| 60 | * \param[in] unit_len_ctx CSS length conversion context to use. | |||
| 61 | * \param[in] box Box to consider | |||
| 62 | * \param[in] x Coordinate relative to box | |||
| 63 | * \param[in] y Coordinate relative to box | |||
| 64 | * \param[out] physically If function returning true, physically is set true | |||
| 65 | * iff point is within the box's physical dimensions and | |||
| 66 | * false if the point is not within the box's physical | |||
| 67 | * dimensions but is in the area defined by the box's | |||
| 68 | * descendants. If function returns false, physically | |||
| 69 | * is undefined. | |||
| 70 | * \return true if the point is within the box or a descendant box | |||
| 71 | * | |||
| 72 | * This is a helper function for box_at_point(). | |||
| 73 | */ | |||
| 74 | static bool_Bool | |||
| 75 | box_contains_point(const css_unit_ctx *unit_len_ctx, | |||
| 76 | const struct box *box, | |||
| 77 | int x, | |||
| 78 | int y, | |||
| 79 | bool_Bool *physically) | |||
| 80 | { | |||
| 81 | css_computed_clip_rect css_rect; | |||
| 82 | ||||
| 83 | if (box->style != NULL((void*)0) && | |||
| 84 | css_computed_position(box->style) == CSS_POSITION_ABSOLUTE && | |||
| 85 | css_computed_clip(box->style, &css_rect) == CSS_CLIP_RECT) { | |||
| 86 | /* We have an absolutly positioned box with a clip rect */ | |||
| 87 | struct rect r = { | |||
| 88 | .x0 = box->border[LEFT].width, | |||
| 89 | .y0 = box->border[TOP].width, | |||
| 90 | .x1 = box->padding[LEFT] + box->width + | |||
| 91 | box->border[RIGHT].width + | |||
| 92 | box->padding[RIGHT], | |||
| 93 | .y1 = box->padding[TOP] + box->height + | |||
| 94 | box->border[BOTTOM].width + | |||
| 95 | box->padding[BOTTOM] | |||
| 96 | }; | |||
| 97 | if (x >= r.x0 && x < r.x1 && y >= r.y0 && y < r.y1) { | |||
| 98 | *physically = true1; | |||
| 99 | } else { | |||
| 100 | *physically = false0; | |||
| 101 | } | |||
| 102 | ||||
| 103 | /* Adjust rect to css clip region */ | |||
| 104 | if (css_rect.left_auto == false0) { | |||
| 105 | r.x0 += FIXTOINT(css_unit_len2device_px(((css_unit_len2device_px( box->style, unit_len_ctx, css_rect .left, css_rect.lunit)) >> 10) | |||
| 106 | box->style,((css_unit_len2device_px( box->style, unit_len_ctx, css_rect .left, css_rect.lunit)) >> 10) | |||
| 107 | unit_len_ctx,((css_unit_len2device_px( box->style, unit_len_ctx, css_rect .left, css_rect.lunit)) >> 10) | |||
| 108 | css_rect.left,((css_unit_len2device_px( box->style, unit_len_ctx, css_rect .left, css_rect.lunit)) >> 10) | |||
| 109 | css_rect.lunit))((css_unit_len2device_px( box->style, unit_len_ctx, css_rect .left, css_rect.lunit)) >> 10); | |||
| 110 | } | |||
| 111 | if (css_rect.top_auto == false0) { | |||
| 112 | r.y0 += FIXTOINT(css_unit_len2device_px(((css_unit_len2device_px( box->style, unit_len_ctx, css_rect .top, css_rect.tunit)) >> 10) | |||
| 113 | box->style,((css_unit_len2device_px( box->style, unit_len_ctx, css_rect .top, css_rect.tunit)) >> 10) | |||
| 114 | unit_len_ctx,((css_unit_len2device_px( box->style, unit_len_ctx, css_rect .top, css_rect.tunit)) >> 10) | |||
| 115 | css_rect.top,((css_unit_len2device_px( box->style, unit_len_ctx, css_rect .top, css_rect.tunit)) >> 10) | |||
| 116 | css_rect.tunit))((css_unit_len2device_px( box->style, unit_len_ctx, css_rect .top, css_rect.tunit)) >> 10); | |||
| 117 | } | |||
| 118 | if (css_rect.right_auto == false0) { | |||
| 119 | r.x1 = box->border[LEFT].width + | |||
| 120 | FIXTOINT(css_unit_len2device_px(((css_unit_len2device_px( box->style, unit_len_ctx, css_rect .right, css_rect.runit)) >> 10) | |||
| 121 | box->style,((css_unit_len2device_px( box->style, unit_len_ctx, css_rect .right, css_rect.runit)) >> 10) | |||
| 122 | unit_len_ctx,((css_unit_len2device_px( box->style, unit_len_ctx, css_rect .right, css_rect.runit)) >> 10) | |||
| 123 | css_rect.right,((css_unit_len2device_px( box->style, unit_len_ctx, css_rect .right, css_rect.runit)) >> 10) | |||
| 124 | css_rect.runit))((css_unit_len2device_px( box->style, unit_len_ctx, css_rect .right, css_rect.runit)) >> 10); | |||
| 125 | } | |||
| 126 | if (css_rect.bottom_auto == false0) { | |||
| 127 | r.y1 = box->border[TOP].width + | |||
| 128 | FIXTOINT(css_unit_len2device_px(((css_unit_len2device_px( box->style, unit_len_ctx, css_rect .bottom, css_rect.bunit)) >> 10) | |||
| 129 | box->style,((css_unit_len2device_px( box->style, unit_len_ctx, css_rect .bottom, css_rect.bunit)) >> 10) | |||
| 130 | unit_len_ctx,((css_unit_len2device_px( box->style, unit_len_ctx, css_rect .bottom, css_rect.bunit)) >> 10) | |||
| 131 | css_rect.bottom,((css_unit_len2device_px( box->style, unit_len_ctx, css_rect .bottom, css_rect.bunit)) >> 10) | |||
| 132 | css_rect.bunit))((css_unit_len2device_px( box->style, unit_len_ctx, css_rect .bottom, css_rect.bunit)) >> 10); | |||
| 133 | } | |||
| 134 | ||||
| 135 | /* Test if point is in clipped box */ | |||
| 136 | if (x >= r.x0 && x < r.x1 && y >= r.y0 && y < r.y1) { | |||
| 137 | /* inside clip area */ | |||
| 138 | return true1; | |||
| 139 | } | |||
| 140 | ||||
| 141 | /* Not inside clip area */ | |||
| 142 | return false0; | |||
| 143 | } | |||
| 144 | if (x >= -box->border[LEFT].width && | |||
| 145 | x < box->padding[LEFT] + box->width + | |||
| 146 | box->padding[RIGHT] + box->border[RIGHT].width && | |||
| 147 | y >= -box->border[TOP].width && | |||
| 148 | y < box->padding[TOP] + box->height + | |||
| 149 | box->padding[BOTTOM] + box->border[BOTTOM].width) { | |||
| 150 | *physically = true1; | |||
| 151 | return true1; | |||
| 152 | } | |||
| 153 | if (box->list_marker && box->list_marker->x - box->x <= x + | |||
| 154 | box->list_marker->border[LEFT].width && | |||
| 155 | x < box->list_marker->x - box->x + | |||
| 156 | box->list_marker->padding[LEFT] + | |||
| 157 | box->list_marker->width + | |||
| 158 | box->list_marker->border[RIGHT].width + | |||
| 159 | box->list_marker->padding[RIGHT] && | |||
| 160 | box->list_marker->y - box->y <= y + | |||
| 161 | box->list_marker->border[TOP].width && | |||
| 162 | y < box->list_marker->y - box->y + | |||
| 163 | box->list_marker->padding[TOP] + | |||
| 164 | box->list_marker->height + | |||
| 165 | box->list_marker->border[BOTTOM].width + | |||
| 166 | box->list_marker->padding[BOTTOM]) { | |||
| 167 | *physically = true1; | |||
| 168 | return true1; | |||
| 169 | } | |||
| 170 | if ((box->style && css_computed_overflow_x(box->style) == | |||
| 171 | CSS_OVERFLOW_VISIBLE) || !box->style) { | |||
| 172 | if (box->descendant_x0 <= x && | |||
| 173 | x < box->descendant_x1) { | |||
| 174 | *physically = false0; | |||
| 175 | return true1; | |||
| 176 | } | |||
| 177 | } | |||
| 178 | if ((box->style && css_computed_overflow_y(box->style) == | |||
| 179 | CSS_OVERFLOW_VISIBLE) || !box->style) { | |||
| 180 | if (box->descendant_y0 <= y && | |||
| 181 | y < box->descendant_y1) { | |||
| 182 | *physically = false0; | |||
| 183 | return true1; | |||
| 184 | } | |||
| 185 | } | |||
| 186 | return false0; | |||
| 187 | } | |||
| 188 | ||||
| 189 | ||||
| 190 | /** | |||
| 191 | * Move from box to next box in given direction, adjusting for box coord change | |||
| 192 | * | |||
| 193 | * \param b box to move from from | |||
| 194 | * \param dir direction to move in | |||
| 195 | * \param x box's global x-coord, updated to position of next box | |||
| 196 | * \param y box's global y-coord, updated to position of next box | |||
| 197 | * | |||
| 198 | * If no box can be found in given direction, NULL is returned. | |||
| 199 | */ | |||
| 200 | static inline struct box * | |||
| 201 | box_move_xy(struct box *b, enum box_walk_dir dir, int *x, int *y) | |||
| 202 | { | |||
| 203 | struct box *rb = NULL((void*)0); | |||
| 204 | ||||
| 205 | switch (dir) { | |||
| 206 | case BOX_WALK_CHILDREN: | |||
| 207 | b = b->children; | |||
| 208 | if (b == NULL((void*)0)) | |||
| 209 | break; | |||
| 210 | *x += b->x; | |||
| 211 | *y += b->y; | |||
| 212 | if (!box_is_float(b)(b->type == BOX_FLOAT_LEFT || b->type == BOX_FLOAT_RIGHT )) { | |||
| 213 | rb = b; | |||
| 214 | break; | |||
| 215 | } | |||
| 216 | fallthrough__attribute__((__fallthrough__)); | |||
| 217 | ||||
| 218 | case BOX_WALK_NEXT_SIBLING: | |||
| 219 | do { | |||
| 220 | *x -= b->x; | |||
| 221 | *y -= b->y; | |||
| 222 | b = b->next; | |||
| 223 | if (b == NULL((void*)0)) | |||
| 224 | break; | |||
| 225 | *x += b->x; | |||
| 226 | *y += b->y; | |||
| 227 | } while (box_is_float(b)(b->type == BOX_FLOAT_LEFT || b->type == BOX_FLOAT_RIGHT )); | |||
| 228 | rb = b; | |||
| 229 | break; | |||
| 230 | ||||
| 231 | case BOX_WALK_PARENT: | |||
| 232 | *x -= b->x; | |||
| 233 | *y -= b->y; | |||
| 234 | rb = b->parent; | |||
| 235 | break; | |||
| 236 | ||||
| 237 | case BOX_WALK_FLOAT_CHILDREN: | |||
| 238 | b = b->float_children; | |||
| 239 | if (b == NULL((void*)0)) | |||
| 240 | break; | |||
| 241 | *x += b->x; | |||
| 242 | *y += b->y; | |||
| 243 | rb = b; | |||
| 244 | break; | |||
| 245 | ||||
| 246 | case BOX_WALK_NEXT_FLOAT_SIBLING: | |||
| 247 | *x -= b->x; | |||
| 248 | *y -= b->y; | |||
| 249 | b = b->next_float; | |||
| 250 | if (b == NULL((void*)0)) | |||
| 251 | break; | |||
| 252 | *x += b->x; | |||
| 253 | *y += b->y; | |||
| 254 | rb = b; | |||
| 255 | break; | |||
| 256 | ||||
| 257 | case BOX_WALK_FLOAT_CONTAINER: | |||
| 258 | *x -= b->x; | |||
| 259 | *y -= b->y; | |||
| 260 | rb = b->float_container; | |||
| 261 | break; | |||
| 262 | ||||
| 263 | default: | |||
| 264 | assert(0 && "Bad box walk type.")((0 && "Bad box walk type.") ? (void) (0) : __assert_fail ("0 && \"Bad box walk type.\"", "content/handlers/html/box_inspect.c" , 264, __extension__ __PRETTY_FUNCTION__)); | |||
| 265 | } | |||
| 266 | ||||
| 267 | return rb; | |||
| 268 | } | |||
| 269 | ||||
| 270 | ||||
| 271 | /** | |||
| 272 | * Itterator for walking to next box in interaction order | |||
| 273 | * | |||
| 274 | * \param b box to find next box from | |||
| 275 | * \param x box's global x-coord, updated to position of next box | |||
| 276 | * \param y box's global y-coord, updated to position of next box | |||
| 277 | * \param skip_children whether to skip box's children | |||
| 278 | * | |||
| 279 | * This walks to a boxes float children before its children. When walking | |||
| 280 | * children, floating boxes are skipped. | |||
| 281 | */ | |||
| 282 | static inline struct box * | |||
| 283 | box_next_xy(struct box *b, int *x, int *y, bool_Bool skip_children) | |||
| 284 | { | |||
| 285 | struct box *n; | |||
| 286 | int tx, ty; | |||
| 287 | ||||
| 288 | assert(b != NULL)((b != ((void*)0)) ? (void) (0) : __assert_fail ("b != NULL", "content/handlers/html/box_inspect.c", 288, __extension__ __PRETTY_FUNCTION__ )); | |||
| 289 | ||||
| 290 | if (skip_children) { | |||
| 291 | /* Caller is not interested in any kind of children */ | |||
| 292 | goto skip_children; | |||
| 293 | } | |||
| 294 | ||||
| 295 | tx = *x; ty = *y; | |||
| 296 | n = box_move_xy(b, BOX_WALK_FLOAT_CHILDREN, &tx, &ty); | |||
| 297 | if (n) { | |||
| 298 | /* Next node is float child */ | |||
| 299 | *x = tx; | |||
| 300 | *y = ty; | |||
| 301 | return n; | |||
| 302 | } | |||
| 303 | done_float_children: | |||
| 304 | ||||
| 305 | tx = *x; ty = *y; | |||
| 306 | n = box_move_xy(b, BOX_WALK_CHILDREN, &tx, &ty); | |||
| 307 | if (n) { | |||
| 308 | /* Next node is child */ | |||
| 309 | *x = tx; | |||
| 310 | *y = ty; | |||
| 311 | return n; | |||
| 312 | } | |||
| 313 | ||||
| 314 | skip_children: | |||
| 315 | tx = *x; ty = *y; | |||
| 316 | n = box_move_xy(b, BOX_WALK_NEXT_FLOAT_SIBLING, &tx, &ty); | |||
| 317 | if (n) { | |||
| 318 | /* Go to next float sibling */ | |||
| 319 | *x = tx; | |||
| 320 | *y = ty; | |||
| 321 | return n; | |||
| 322 | } | |||
| 323 | ||||
| 324 | if (box_is_float(b)(b->type == BOX_FLOAT_LEFT || b->type == BOX_FLOAT_RIGHT )) { | |||
| 325 | /* Done floats, but the float container may have children, | |||
| 326 | * or siblings, or ansestors with siblings. Change to | |||
| 327 | * float container and move past handling its float children. | |||
| 328 | */ | |||
| 329 | b = box_move_xy(b, BOX_WALK_FLOAT_CONTAINER, x, y); | |||
| 330 | goto done_float_children; | |||
| 331 | } | |||
| 332 | ||||
| 333 | /* Go to next sibling, or nearest ancestor with next sibling. */ | |||
| 334 | while (b) { | |||
| 335 | while (!b->next && b->parent) { | |||
| 336 | b = box_move_xy(b, BOX_WALK_PARENT, x, y); | |||
| 337 | if (box_is_float(b)(b->type == BOX_FLOAT_LEFT || b->type == BOX_FLOAT_RIGHT )) { | |||
| 338 | /* Go on to next float, if there is one */ | |||
| 339 | goto skip_children; | |||
| 340 | } | |||
| 341 | } | |||
| 342 | if (!b->next) { | |||
| 343 | /* No more boxes */ | |||
| 344 | return NULL((void*)0); | |||
| 345 | } | |||
| 346 | ||||
| 347 | tx = *x; ty = *y; | |||
| 348 | n = box_move_xy(b, BOX_WALK_NEXT_SIBLING, &tx, &ty); | |||
| 349 | if (n) { | |||
| 350 | /* Go to non-float (ancestor) sibling */ | |||
| 351 | *x = tx; | |||
| 352 | *y = ty; | |||
| 353 | return n; | |||
| 354 | ||||
| 355 | } else if (b->parent) { | |||
| 356 | b = box_move_xy(b, BOX_WALK_PARENT, x, y); | |||
| 357 | if (box_is_float(b)(b->type == BOX_FLOAT_LEFT || b->type == BOX_FLOAT_RIGHT )) { | |||
| 358 | /* Go on to next float, if there is one */ | |||
| 359 | goto skip_children; | |||
| 360 | } | |||
| 361 | ||||
| 362 | } else { | |||
| 363 | /* No more boxes */ | |||
| 364 | return NULL((void*)0); | |||
| 365 | } | |||
| 366 | } | |||
| 367 | ||||
| 368 | assert(b != NULL)((b != ((void*)0)) ? (void) (0) : __assert_fail ("b != NULL", "content/handlers/html/box_inspect.c", 368, __extension__ __PRETTY_FUNCTION__ )); | |||
| 369 | return NULL((void*)0); | |||
| 370 | } | |||
| 371 | ||||
| 372 | ||||
| 373 | /** | |||
| 374 | * Check whether box is nearer mouse coordinates than current nearest box | |||
| 375 | * | |||
| 376 | * \param box box to test | |||
| 377 | * \param bx position of box, in global document coordinates | |||
| 378 | * \param by position of box, in global document coordinates | |||
| 379 | * \param x mouse point, in global document coordinates | |||
| 380 | * \param y mouse point, in global document coordinates | |||
| 381 | * \param dir direction in which to search (-1 = above-left, | |||
| 382 | * +1 = below-right) | |||
| 383 | * \param nearest nearest text box found, or NULL if none | |||
| 384 | * updated if box is nearer than existing nearest | |||
| 385 | * \param tx position of text_box, in global document coordinates | |||
| 386 | * updated if box is nearer than existing nearest | |||
| 387 | * \param ty position of text_box, in global document coordinates | |||
| 388 | * updated if box is nearer than existing nearest | |||
| 389 | * \param nr_xd distance to nearest text box found | |||
| 390 | * updated if box is nearer than existing nearest | |||
| 391 | * \param nr_yd distance to nearest text box found | |||
| 392 | * updated if box is nearer than existing nearest | |||
| 393 | * \return true if mouse point is inside box | |||
| 394 | */ | |||
| 395 | static bool_Bool | |||
| 396 | box_nearer_text_box(struct box *box, | |||
| 397 | int bx, int by, | |||
| 398 | int x, int y, | |||
| 399 | int dir, | |||
| 400 | struct box **nearest, | |||
| 401 | int *tx, int *ty, | |||
| 402 | int *nr_xd, int *nr_yd) | |||
| 403 | { | |||
| 404 | int w = box->padding[LEFT] + box->width + box->padding[RIGHT]; | |||
| 405 | int h = box->padding[TOP] + box->height + box->padding[BOTTOM]; | |||
| 406 | int y1 = by + h; | |||
| 407 | int x1 = bx + w; | |||
| 408 | int yd = INT_MAX2147483647; | |||
| 409 | int xd = INT_MAX2147483647; | |||
| 410 | ||||
| 411 | if (x >= bx && x1 > x && y >= by && y1 > y) { | |||
| 412 | *nearest = box; | |||
| 413 | *tx = bx; | |||
| 414 | *ty = by; | |||
| 415 | return true1; | |||
| 416 | } | |||
| 417 | ||||
| 418 | if (box->parent->list_marker != box) { | |||
| 419 | if (dir < 0) { | |||
| 420 | /* consider only those children (partly) above-left */ | |||
| 421 | if (by <= y && bx < x) { | |||
| 422 | yd = y <= y1 ? 0 : y - y1; | |||
| 423 | xd = x <= x1 ? 0 : x - x1; | |||
| 424 | } | |||
| 425 | } else { | |||
| 426 | /* consider only those children (partly) below-right */ | |||
| 427 | if (y1 > y && x1 > x) { | |||
| 428 | yd = y > by ? 0 : by - y; | |||
| 429 | xd = x > bx ? 0 : bx - x; | |||
| 430 | } | |||
| 431 | } | |||
| 432 | ||||
| 433 | /* give y displacement precedence over x */ | |||
| 434 | if (yd < *nr_yd || (yd == *nr_yd && xd <= *nr_xd)) { | |||
| 435 | *nr_yd = yd; | |||
| 436 | *nr_xd = xd; | |||
| 437 | *nearest = box; | |||
| 438 | *tx = bx; | |||
| 439 | *ty = by; | |||
| 440 | } | |||
| 441 | } | |||
| 442 | return false0; | |||
| 443 | } | |||
| 444 | ||||
| 445 | ||||
| 446 | /** | |||
| 447 | * Pick the text box child of 'box' that is closest to and above-left | |||
| 448 | * (dir -ve) or below-right (dir +ve) of the point 'x,y' | |||
| 449 | * | |||
| 450 | * \param box parent box | |||
| 451 | * \param bx position of box, in global document coordinates | |||
| 452 | * \param by position of box, in global document coordinates | |||
| 453 | * \param fx position of float parent, in global document coordinates | |||
| 454 | * \param fy position of float parent, in global document coordinates | |||
| 455 | * \param x mouse point, in global document coordinates | |||
| 456 | * \param y mouse point, in global document coordinates | |||
| 457 | * \param dir direction in which to search (-1 = above-left, | |||
| 458 | * +1 = below-right) | |||
| 459 | * \param nearest nearest text box found, or NULL if none | |||
| 460 | * updated if a descendant of box is nearer than old nearest | |||
| 461 | * \param tx position of nearest, in global document coordinates | |||
| 462 | * updated if a descendant of box is nearer than old nearest | |||
| 463 | * \param ty position of nearest, in global document coordinates | |||
| 464 | * updated if a descendant of box is nearer than old nearest | |||
| 465 | * \param nr_xd distance to nearest text box found | |||
| 466 | * updated if a descendant of box is nearer than old nearest | |||
| 467 | * \param nr_yd distance to nearest text box found | |||
| 468 | * updated if a descendant of box is nearer than old nearest | |||
| 469 | * \return true if mouse point is inside text_box | |||
| 470 | */ | |||
| 471 | static bool_Bool | |||
| 472 | box_nearest_text_box(struct box *box, | |||
| 473 | int bx, int by, | |||
| 474 | int fx, int fy, | |||
| 475 | int x, int y, | |||
| 476 | int dir, | |||
| 477 | struct box **nearest, | |||
| 478 | int *tx, int *ty, | |||
| 479 | int *nr_xd, int *nr_yd) | |||
| 480 | { | |||
| 481 | struct box *child = box->children; | |||
| 482 | int c_bx, c_by; | |||
| 483 | int c_fx, c_fy; | |||
| 484 | bool_Bool in_box = false0; | |||
| 485 | ||||
| 486 | if (*nearest == NULL((void*)0)) { | |||
| 487 | *nr_xd = INT_MAX2147483647 / 2; /* displacement of 'nearest so far' */ | |||
| 488 | *nr_yd = INT_MAX2147483647 / 2; | |||
| 489 | } | |||
| 490 | if (box->type == BOX_INLINE_CONTAINER) { | |||
| 491 | int bw = box->padding[LEFT] + box->width + box->padding[RIGHT]; | |||
| 492 | int bh = box->padding[TOP] + box->height + box->padding[BOTTOM]; | |||
| 493 | int b_y1 = by + bh; | |||
| 494 | int b_x1 = bx + bw; | |||
| 495 | if (x >= bx && b_x1 > x && y >= by && b_y1 > y) { | |||
| 496 | in_box = true1; | |||
| 497 | } | |||
| 498 | } | |||
| 499 | ||||
| 500 | while (child) { | |||
| 501 | if (child->type == BOX_FLOAT_LEFT || | |||
| 502 | child->type == BOX_FLOAT_RIGHT) { | |||
| 503 | c_bx = fx + child->x - | |||
| 504 | scrollbar_get_offset(child->scroll_x); | |||
| 505 | c_by = fy + child->y - | |||
| 506 | scrollbar_get_offset(child->scroll_y); | |||
| 507 | } else { | |||
| 508 | c_bx = bx + child->x - | |||
| 509 | scrollbar_get_offset(child->scroll_x); | |||
| 510 | c_by = by + child->y - | |||
| 511 | scrollbar_get_offset(child->scroll_y); | |||
| 512 | } | |||
| 513 | if (child->float_children) { | |||
| 514 | c_fx = c_bx; | |||
| 515 | c_fy = c_by; | |||
| 516 | } else { | |||
| 517 | c_fx = fx; | |||
| 518 | c_fy = fy; | |||
| 519 | } | |||
| 520 | if (in_box && child->text && !child->object) { | |||
| 521 | if (box_nearer_text_box(child, | |||
| 522 | c_bx, c_by, x, y, dir, nearest, | |||
| 523 | tx, ty, nr_xd, nr_yd)) | |||
| 524 | return true1; | |||
| 525 | } else { | |||
| 526 | if (child->list_marker) { | |||
| 527 | if (box_nearer_text_box( | |||
| 528 | child->list_marker, | |||
| 529 | c_bx + child->list_marker->x, | |||
| 530 | c_by + child->list_marker->y, | |||
| 531 | x, y, dir, nearest, | |||
| 532 | tx, ty, nr_xd, nr_yd)) | |||
| 533 | return true1; | |||
| 534 | } | |||
| 535 | if (box_nearest_text_box(child, c_bx, c_by, | |||
| 536 | c_fx, c_fy, | |||
| 537 | x, y, dir, nearest, tx, ty, | |||
| 538 | nr_xd, nr_yd)) | |||
| 539 | return true1; | |||
| 540 | } | |||
| 541 | child = child->next; | |||
| 542 | } | |||
| 543 | ||||
| 544 | return false0; | |||
| 545 | } | |||
| 546 | ||||
| 547 | ||||
| 548 | /* Exported function documented in html/box.h */ | |||
| 549 | void box_coords(struct box *box, int *x, int *y) | |||
| 550 | { | |||
| 551 | *x = box->x; | |||
| 552 | *y = box->y; | |||
| 553 | while (box->parent) { | |||
| 554 | if (box_is_float(box)(box->type == BOX_FLOAT_LEFT || box->type == BOX_FLOAT_RIGHT )) { | |||
| 555 | assert(box->float_container)((box->float_container) ? (void) (0) : __assert_fail ("box->float_container" , "content/handlers/html/box_inspect.c", 555, __extension__ __PRETTY_FUNCTION__ )); | |||
| 556 | box = box->float_container; | |||
| 557 | } else { | |||
| 558 | box = box->parent; | |||
| 559 | } | |||
| 560 | *x += box->x - scrollbar_get_offset(box->scroll_x); | |||
| 561 | *y += box->y - scrollbar_get_offset(box->scroll_y); | |||
| 562 | } | |||
| 563 | } | |||
| 564 | ||||
| 565 | ||||
| 566 | /* Exported function documented in html/box.h */ | |||
| 567 | void box_bounds(struct box *box, struct rect *r) | |||
| 568 | { | |||
| 569 | int width, height; | |||
| 570 | ||||
| 571 | box_coords(box, &r->x0, &r->y0); | |||
| 572 | ||||
| 573 | width = box->padding[LEFT] + box->width + box->padding[RIGHT]; | |||
| 574 | height = box->padding[TOP] + box->height + box->padding[BOTTOM]; | |||
| 575 | ||||
| 576 | r->x1 = r->x0 + width; | |||
| 577 | r->y1 = r->y0 + height; | |||
| 578 | } | |||
| 579 | ||||
| 580 | ||||
| 581 | /* Exported function documented in html/box.h */ | |||
| 582 | struct box * | |||
| 583 | box_at_point(const css_unit_ctx *unit_len_ctx, | |||
| 584 | struct box *box, | |||
| 585 | const int x, const int y, | |||
| 586 | int *box_x, int *box_y) | |||
| 587 | { | |||
| 588 | bool_Bool skip_children; | |||
| 589 | bool_Bool physically; | |||
| 590 | ||||
| 591 | assert(box)((box) ? (void) (0) : __assert_fail ("box", "content/handlers/html/box_inspect.c" , 591, __extension__ __PRETTY_FUNCTION__)); | |||
| 592 | ||||
| 593 | skip_children = false0; | |||
| 594 | while ((box = box_next_xy(box, box_x, box_y, skip_children))) { | |||
| 595 | if (box_contains_point(unit_len_ctx, box, x - *box_x, y - *box_y, | |||
| 596 | &physically)) { | |||
| 597 | *box_x -= scrollbar_get_offset(box->scroll_x); | |||
| 598 | *box_y -= scrollbar_get_offset(box->scroll_y); | |||
| 599 | ||||
| 600 | if (physically) | |||
| 601 | return box; | |||
| 602 | ||||
| 603 | skip_children = false0; | |||
| 604 | } else { | |||
| 605 | skip_children = true1; | |||
| 606 | } | |||
| 607 | } | |||
| 608 | ||||
| 609 | return NULL((void*)0); | |||
| 610 | } | |||
| 611 | ||||
| 612 | ||||
| 613 | /* Exported function documented in html/box.h */ | |||
| 614 | struct box *box_find_by_id(struct box *box, lwc_string *id) | |||
| 615 | { | |||
| 616 | struct box *a, *b; | |||
| 617 | bool_Bool m; | |||
| 618 | ||||
| 619 | if (box->id != NULL((void*)0) && | |||
| 620 | lwc_string_isequal(id, box->id, &m)((*(&m) = ((id) == (box->id))), lwc_error_ok) == lwc_error_ok && | |||
| 621 | m == true1) { | |||
| 622 | return box; | |||
| 623 | } | |||
| 624 | ||||
| 625 | for (a = box->children; a; a = a->next) { | |||
| 626 | if ((b = box_find_by_id(a, id)) != NULL((void*)0)) { | |||
| 627 | return b; | |||
| 628 | } | |||
| 629 | } | |||
| 630 | ||||
| 631 | return NULL((void*)0); | |||
| 632 | } | |||
| 633 | ||||
| 634 | ||||
| 635 | /* Exported function documented in html/box.h */ | |||
| 636 | bool_Bool box_visible(struct box *box) | |||
| 637 | { | |||
| 638 | /* visibility: hidden */ | |||
| 639 | if (box->style && | |||
| 640 | css_computed_visibility(box->style) == CSS_VISIBILITY_HIDDEN) { | |||
| 641 | return false0; | |||
| 642 | } | |||
| 643 | ||||
| 644 | return true1; | |||
| 645 | } | |||
| 646 | ||||
| 647 | ||||
| 648 | /* Exported function documented in html/box.h */ | |||
| 649 | void box_dump(FILE *stream, struct box *box, unsigned int depth, bool_Bool style) | |||
| 650 | { | |||
| 651 | unsigned int i; | |||
| 652 | struct box *c, *prev; | |||
| 653 | ||||
| 654 | for (i = 0; i != depth; i++) { | |||
| 655 | fprintf(stream, " "); | |||
| 656 | } | |||
| 657 | ||||
| 658 | fprintf(stream, "%p ", box); | |||
| 659 | fprintf(stream, "x%i y%i w%i h%i ", | |||
| 660 | box->x, box->y, box->width, box->height); | |||
| 661 | if (box->max_width != UNKNOWN_MAX_WIDTH2147483647) { | |||
| 662 | fprintf(stream, "min%i max%i ", box->min_width, box->max_width); | |||
| 663 | } | |||
| 664 | fprintf(stream, "desc(%i %i %i %i) ", | |||
| 665 | box->descendant_x0, box->descendant_y0, | |||
| 666 | box->descendant_x1, box->descendant_y1); | |||
| 667 | ||||
| 668 | fprintf(stream, "m(%i %i %i %i) ", | |||
| 669 | box->margin[TOP], box->margin[LEFT], | |||
| 670 | box->margin[BOTTOM], box->margin[RIGHT]); | |||
| 671 | ||||
| 672 | switch (box->type) { | |||
| 673 | case BOX_BLOCK: | |||
| 674 | fprintf(stream, "BLOCK "); | |||
| 675 | break; | |||
| 676 | ||||
| 677 | case BOX_INLINE_CONTAINER: | |||
| 678 | fprintf(stream, "INLINE_CONTAINER "); | |||
| 679 | break; | |||
| 680 | ||||
| 681 | case BOX_INLINE: | |||
| 682 | fprintf(stream, "INLINE "); | |||
| 683 | break; | |||
| 684 | ||||
| 685 | case BOX_INLINE_END: | |||
| 686 | fprintf(stream, "INLINE_END "); | |||
| 687 | break; | |||
| 688 | ||||
| 689 | case BOX_INLINE_BLOCK: | |||
| 690 | fprintf(stream, "INLINE_BLOCK "); | |||
| 691 | break; | |||
| 692 | ||||
| 693 | case BOX_TABLE: | |||
| 694 | fprintf(stream, "TABLE [columns %i] ", box->columns); | |||
| 695 | break; | |||
| 696 | ||||
| 697 | case BOX_TABLE_ROW: | |||
| 698 | fprintf(stream, "TABLE_ROW "); | |||
| 699 | break; | |||
| 700 | ||||
| 701 | case BOX_TABLE_CELL: | |||
| 702 | fprintf(stream, "TABLE_CELL [columns %i, start %i, rows %i] ", | |||
| 703 | box->columns, | |||
| 704 | box->start_column, | |||
| 705 | box->rows); | |||
| 706 | break; | |||
| 707 | ||||
| 708 | case BOX_TABLE_ROW_GROUP: | |||
| 709 | fprintf(stream, "TABLE_ROW_GROUP "); | |||
| 710 | break; | |||
| 711 | ||||
| 712 | case BOX_FLOAT_LEFT: | |||
| 713 | fprintf(stream, "FLOAT_LEFT "); | |||
| 714 | break; | |||
| 715 | ||||
| 716 | case BOX_FLOAT_RIGHT: | |||
| 717 | fprintf(stream, "FLOAT_RIGHT "); | |||
| 718 | break; | |||
| 719 | ||||
| 720 | case BOX_BR: | |||
| 721 | fprintf(stream, "BR "); | |||
| 722 | break; | |||
| 723 | ||||
| 724 | case BOX_TEXT: | |||
| 725 | fprintf(stream, "TEXT "); | |||
| 726 | break; | |||
| 727 | ||||
| 728 | case BOX_FLEX: | |||
| 729 | fprintf(stream, "FLEX "); | |||
| 730 | break; | |||
| 731 | ||||
| 732 | case BOX_INLINE_FLEX: | |||
| 733 | fprintf(stream, "INLINE_FLEX "); | |||
| 734 | break; | |||
| 735 | ||||
| 736 | default: | |||
| 737 | fprintf(stream, "Unknown box type "); | |||
| 738 | } | |||
| 739 | ||||
| 740 | if (box->text) | |||
| 741 | fprintf(stream, "%li '%.*s' ", (unsigned long) box->byte_offset, | |||
| 742 | (int) box->length, box->text); | |||
| 743 | if (box->space) | |||
| 744 | fprintf(stream, "space "); | |||
| 745 | if (box->object) { | |||
| 746 | fprintf(stream, "(object '%s') ", | |||
| 747 | nsurl_access(hlcache_handle_get_url(box->object))); | |||
| 748 | } | |||
| 749 | if (box->iframe) { | |||
| 750 | fprintf(stream, "(iframe) "); | |||
| 751 | } | |||
| 752 | if (box->gadget) | |||
| 753 | fprintf(stream, "(gadget) "); | |||
| 754 | if (style && box->style) | |||
| 755 | nscss_dump_computed_style(stream, box->style); | |||
| 756 | if (box->href) | |||
| 757 | fprintf(stream, " -> '%s'", nsurl_access(box->href)); | |||
| 758 | if (box->target) | |||
| 759 | fprintf(stream, " |%s|", box->target); | |||
| 760 | if (box->title) | |||
| 761 | fprintf(stream, " [%s]", box->title); | |||
| 762 | if (box->id) | |||
| 763 | fprintf(stream, " ID:%s", lwc_string_data(box->id)({((box->id != ((void*)0)) ? (void) (0) : __assert_fail ("box->id != NULL" , "content/handlers/html/box_inspect.c", 763, __extension__ __PRETTY_FUNCTION__ )); (const char *)((box->id)+1);})); | |||
| 764 | if (box->type == BOX_INLINE || box->type == BOX_INLINE_END) | |||
| 765 | fprintf(stream, " inline_end %p", box->inline_end); | |||
| 766 | if (box->float_children) | |||
| 767 | fprintf(stream, " float_children %p", box->float_children); | |||
| 768 | if (box->next_float) | |||
| 769 | fprintf(stream, " next_float %p", box->next_float); | |||
| 770 | if (box->float_container) | |||
| 771 | fprintf(stream, " float_container %p", box->float_container); | |||
| 772 | if (box->col) { | |||
| 773 | fprintf(stream, " (columns"); | |||
| 774 | for (i = 0; i != box->columns; i++) { | |||
| 775 | fprintf(stream, " (%s %s %i %i %i)", | |||
| 776 | ((const char *[]) { | |||
| 777 | "UNKNOWN", | |||
| 778 | "FIXED", | |||
| 779 | "AUTO", | |||
| 780 | "PERCENT", | |||
| 781 | "RELATIVE" | |||
| 782 | }) | |||
| 783 | [box->col[i].type], | |||
| 784 | ((const char *[]) { | |||
| 785 | "normal", | |||
| 786 | "positioned"}) | |||
| 787 | [box->col[i].positioned], | |||
| 788 | box->col[i].width, | |||
| 789 | box->col[i].min, box->col[i].max); | |||
| 790 | } | |||
| 791 | fprintf(stream, ")"); | |||
| 792 | } | |||
| 793 | if (box->node != NULL((void*)0)) { | |||
| 794 | dom_string *name; | |||
| 795 | if (dom_node_get_node_name(box->node, &name)dom_node_get_node_name((dom_node *) (box->node), (&name )) == DOM_NO_ERR) { | |||
| 796 | fprintf(stream, " <%s>", dom_string_data(name)); | |||
| 797 | dom_string_unref(name); | |||
| 798 | } | |||
| 799 | } | |||
| 800 | fprintf(stream, "\n"); | |||
| 801 | ||||
| 802 | if (box->list_marker) { | |||
| 803 | for (i = 0; i != depth; i++) | |||
| 804 | fprintf(stream, " "); | |||
| 805 | fprintf(stream, "list_marker:\n"); | |||
| 806 | box_dump(stream, box->list_marker, depth + 1, style); | |||
| 807 | } | |||
| 808 | ||||
| 809 | for (c = box->children; c && c->next; c = c->next) | |||
| 810 | ; | |||
| 811 | if (box->last != c) | |||
| 812 | fprintf(stream, "warning: box->last %p (should be %p) " | |||
| 813 | "(box %p)\n", box->last, c, box); | |||
| 814 | for (prev = 0, c = box->children; c; prev = c, c = c->next) { | |||
| 815 | if (c->parent != box) | |||
| 816 | fprintf(stream, "warning: box->parent %p (should be " | |||
| 817 | "%p) (box on next line)\n", | |||
| 818 | c->parent, box); | |||
| 819 | if (c->prev != prev) | |||
| 820 | fprintf(stream, "warning: box->prev %p (should be " | |||
| 821 | "%p) (box on next line)\n", | |||
| 822 | c->prev, prev); | |||
| 823 | box_dump(stream, c, depth + 1, style); | |||
| 824 | } | |||
| 825 | } | |||
| 826 | ||||
| 827 | ||||
| 828 | /* exported interface documented in html/box.h */ | |||
| 829 | bool_Bool box_vscrollbar_present(const struct box * const box) | |||
| 830 | { | |||
| 831 | return box->padding[TOP] + | |||
| 832 | box->height + | |||
| 833 | box->padding[BOTTOM] + | |||
| 834 | box->border[BOTTOM].width < box->descendant_y1; | |||
| 835 | } | |||
| 836 | ||||
| 837 | ||||
| 838 | /* exported interface documented in html/box.h */ | |||
| 839 | bool_Bool box_hscrollbar_present(const struct box * const box) | |||
| 840 | { | |||
| 841 | return box->padding[LEFT] + | |||
| 842 | box->width + | |||
| 843 | box->padding[RIGHT] + | |||
| 844 | box->border[RIGHT].width < box->descendant_x1; | |||
| 845 | } | |||
| 846 | ||||
| 847 | ||||
| 848 | /* Exported function documented in html/box.h */ | |||
| 849 | struct box * | |||
| 850 | box_pick_text_box(struct html_content *html, | |||
| ||||
| 851 | int x, int y, | |||
| 852 | int dir, | |||
| 853 | int *dx, int *dy) | |||
| 854 | { | |||
| 855 | struct box *text_box = NULL((void*)0); | |||
| 856 | struct box *box; | |||
| 857 | int nr_xd, nr_yd; | |||
| 858 | int bx, by; | |||
| 859 | int fx, fy; | |||
| 860 | int tx, ty; | |||
| 861 | ||||
| 862 | if (html == NULL((void*)0)) | |||
| 863 | return NULL((void*)0); | |||
| 864 | ||||
| 865 | box = html->layout; | |||
| 866 | bx = box->margin[LEFT]; | |||
| 867 | by = box->margin[TOP]; | |||
| 868 | fx = bx; | |||
| 869 | fy = by; | |||
| 870 | ||||
| 871 | if (!box_nearest_text_box(box, bx, by, fx, fy, x, y, | |||
| 872 | dir, &text_box, &tx, &ty, &nr_xd, &nr_yd)) { | |||
| 873 | if (text_box
| |||
| 874 | int w = (text_box->padding[LEFT] + | |||
| 875 | text_box->width + | |||
| 876 | text_box->padding[RIGHT]); | |||
| 877 | int h = (text_box->padding[TOP] + | |||
| 878 | text_box->height + | |||
| 879 | text_box->padding[BOTTOM]); | |||
| 880 | int x1, y1; | |||
| 881 | ||||
| 882 | y1 = ty + h; | |||
| 883 | x1 = tx + w; | |||
| 884 | ||||
| 885 | /* ensure point lies within the text box */ | |||
| 886 | if (x < tx) x = tx; | |||
| 887 | if (y < ty) y = ty; | |||
| 888 | if (y > y1) y = y1; | |||
| 889 | if (x > x1) x = x1; | |||
| 890 | } | |||
| 891 | } | |||
| 892 | ||||
| 893 | /* return coordinates relative to box */ | |||
| 894 | *dx = x - tx; | |||
| ||||
| 895 | *dy = y - ty; | |||
| 896 | ||||
| 897 | return text_box; | |||
| 898 | } |