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