File: | content/handlers/html/table.c |
Warning: | line 753, column 23 Access to field 'parent' results in a dereference of a null pointer (loaded from variable 'row') |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* | |||
2 | * Copyright 2005 James Bursa <bursa@users.sourceforge.net> | |||
3 | * Copyright 2005 Richard Wilson <info@tinct.net> | |||
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 HTML table processing and layout. | |||
23 | */ | |||
24 | ||||
25 | #include <assert.h> | |||
26 | #include <dom/dom.h> | |||
27 | ||||
28 | #include "utils/log.h" | |||
29 | #include "utils/utils.h" | |||
30 | #include "utils/talloc.h" | |||
31 | #include "css/utils.h" | |||
32 | ||||
33 | #include "html/box.h" | |||
34 | #include "html/table.h" | |||
35 | ||||
36 | /* Define to enable verbose table debug */ | |||
37 | #undef TABLE_DEBUG | |||
38 | ||||
39 | /** | |||
40 | * Container for border values during table border calculations | |||
41 | */ | |||
42 | struct border { | |||
43 | enum css_border_style_e style; /**< border-style */ | |||
44 | enum css_border_color_e color; /**< border-color type */ | |||
45 | css_color c; /**< border-color value */ | |||
46 | css_fixed width; /**< border-width length */ | |||
47 | css_unit unit; /**< border-width units */ | |||
48 | }; | |||
49 | ||||
50 | ||||
51 | /** | |||
52 | * Determine if a border style is more eyecatching than another | |||
53 | * | |||
54 | * \param unit_len_ctx Length conversion context | |||
55 | * \param a Reference border style | |||
56 | * \param a_src Source of \a a | |||
57 | * \param b Candidate border style | |||
58 | * \param b_src Source of \a b | |||
59 | * \return True if \a b is more eyecatching than \a a | |||
60 | */ | |||
61 | static bool_Bool | |||
62 | table_border_is_more_eyecatching(const css_unit_ctx *unit_len_ctx, | |||
63 | const struct border *a, | |||
64 | box_type a_src, | |||
65 | const struct border *b, | |||
66 | box_type b_src) | |||
67 | { | |||
68 | css_fixed awidth, bwidth; | |||
69 | int impact = 0; | |||
70 | ||||
71 | /* See CSS 2.1 $17.6.2.1 */ | |||
72 | ||||
73 | /* 1 + 2 -- hidden beats everything, none beats nothing */ | |||
74 | if (a->style == CSS_BORDER_STYLE_HIDDEN || | |||
75 | b->style == CSS_BORDER_STYLE_NONE) | |||
76 | return false0; | |||
77 | ||||
78 | if (b->style == CSS_BORDER_STYLE_HIDDEN || | |||
79 | a->style == CSS_BORDER_STYLE_NONE) | |||
80 | return true1; | |||
81 | ||||
82 | /* 3a -- wider borders beat narrow ones */ | |||
83 | /* The widths must be absolute, which will be the case | |||
84 | * if they've come from a computed style. */ | |||
85 | assert(a->unit != CSS_UNIT_EM && a->unit != CSS_UNIT_EX)((a->unit != CSS_UNIT_EM && a->unit != CSS_UNIT_EX ) ? (void) (0) : __assert_fail ("a->unit != CSS_UNIT_EM && a->unit != CSS_UNIT_EX" , "content/handlers/html/table.c", 85, __extension__ __PRETTY_FUNCTION__ )); | |||
86 | assert(b->unit != CSS_UNIT_EM && b->unit != CSS_UNIT_EX)((b->unit != CSS_UNIT_EM && b->unit != CSS_UNIT_EX ) ? (void) (0) : __assert_fail ("b->unit != CSS_UNIT_EM && b->unit != CSS_UNIT_EX" , "content/handlers/html/table.c", 86, __extension__ __PRETTY_FUNCTION__ )); | |||
87 | awidth = css_unit_len2device_px(NULL((void*)0), unit_len_ctx, a->width, a->unit); | |||
88 | bwidth = css_unit_len2device_px(NULL((void*)0), unit_len_ctx, b->width, b->unit); | |||
89 | ||||
90 | if (awidth < bwidth) | |||
91 | return true1; | |||
92 | else if (bwidth < awidth) | |||
93 | return false0; | |||
94 | ||||
95 | /* 3b -- sort by style */ | |||
96 | switch (a->style) { | |||
97 | case CSS_BORDER_STYLE_DOUBLE: impact++; fallthrough__attribute__((__fallthrough__)); | |||
98 | case CSS_BORDER_STYLE_SOLID: impact++; fallthrough__attribute__((__fallthrough__)); | |||
99 | case CSS_BORDER_STYLE_DASHED: impact++; fallthrough__attribute__((__fallthrough__)); | |||
100 | case CSS_BORDER_STYLE_DOTTED: impact++; fallthrough__attribute__((__fallthrough__)); | |||
101 | case CSS_BORDER_STYLE_RIDGE: impact++; fallthrough__attribute__((__fallthrough__)); | |||
102 | case CSS_BORDER_STYLE_OUTSET: impact++; fallthrough__attribute__((__fallthrough__)); | |||
103 | case CSS_BORDER_STYLE_GROOVE: impact++; fallthrough__attribute__((__fallthrough__)); | |||
104 | case CSS_BORDER_STYLE_INSET: impact++; fallthrough__attribute__((__fallthrough__)); | |||
105 | default: | |||
106 | break; | |||
107 | } | |||
108 | ||||
109 | switch (b->style) { | |||
110 | case CSS_BORDER_STYLE_DOUBLE: impact--; fallthrough__attribute__((__fallthrough__)); | |||
111 | case CSS_BORDER_STYLE_SOLID: impact--; fallthrough__attribute__((__fallthrough__)); | |||
112 | case CSS_BORDER_STYLE_DASHED: impact--; fallthrough__attribute__((__fallthrough__)); | |||
113 | case CSS_BORDER_STYLE_DOTTED: impact--; fallthrough__attribute__((__fallthrough__)); | |||
114 | case CSS_BORDER_STYLE_RIDGE: impact--; fallthrough__attribute__((__fallthrough__)); | |||
115 | case CSS_BORDER_STYLE_OUTSET: impact--; fallthrough__attribute__((__fallthrough__)); | |||
116 | case CSS_BORDER_STYLE_GROOVE: impact--; fallthrough__attribute__((__fallthrough__)); | |||
117 | case CSS_BORDER_STYLE_INSET: impact--; fallthrough__attribute__((__fallthrough__)); | |||
118 | default: | |||
119 | break; | |||
120 | } | |||
121 | ||||
122 | if (impact < 0) | |||
123 | return true1; | |||
124 | else if (impact > 0) | |||
125 | return false0; | |||
126 | ||||
127 | /* 4a -- sort by origin */ | |||
128 | impact = 0; | |||
129 | ||||
130 | /** \todo COL/COL_GROUP */ | |||
131 | switch (a_src) { | |||
132 | case BOX_TABLE_CELL: impact++; fallthrough__attribute__((__fallthrough__)); | |||
133 | case BOX_TABLE_ROW: impact++; fallthrough__attribute__((__fallthrough__)); | |||
134 | case BOX_TABLE_ROW_GROUP: impact++; fallthrough__attribute__((__fallthrough__)); | |||
135 | case BOX_TABLE: impact++; fallthrough__attribute__((__fallthrough__)); | |||
136 | default: | |||
137 | break; | |||
138 | } | |||
139 | ||||
140 | /** \todo COL/COL_GROUP */ | |||
141 | switch (b_src) { | |||
142 | case BOX_TABLE_CELL: impact--; fallthrough__attribute__((__fallthrough__)); | |||
143 | case BOX_TABLE_ROW: impact--; fallthrough__attribute__((__fallthrough__)); | |||
144 | case BOX_TABLE_ROW_GROUP: impact--; fallthrough__attribute__((__fallthrough__)); | |||
145 | case BOX_TABLE: impact--; fallthrough__attribute__((__fallthrough__)); | |||
146 | default: | |||
147 | break; | |||
148 | } | |||
149 | ||||
150 | if (impact < 0) | |||
151 | return true1; | |||
152 | else if (impact > 0) | |||
153 | return false0; | |||
154 | ||||
155 | /* 4b -- furthest left (if direction: ltr) and towards top wins */ | |||
156 | /** \todo Currently assumes b satisifies this */ | |||
157 | return true1; | |||
158 | } | |||
159 | ||||
160 | ||||
161 | /** | |||
162 | * Process a table | |||
163 | * | |||
164 | * \param unit_len_ctx Length conversion context | |||
165 | * \param table Table to process | |||
166 | * \param a Current border style for cell | |||
167 | * \param a_src Source of \a a | |||
168 | * | |||
169 | * \post \a a will be updated with most eyecatching style | |||
170 | * \post \a a_src will be updated also | |||
171 | */ | |||
172 | static void | |||
173 | table_cell_top_process_table(const css_unit_ctx *unit_len_ctx, | |||
174 | struct box *table, | |||
175 | struct border *a, | |||
176 | box_type *a_src) | |||
177 | { | |||
178 | struct border b; | |||
179 | box_type b_src; | |||
180 | ||||
181 | /* Top border of table */ | |||
182 | b.style = css_computed_border_top_style(table->style); | |||
183 | b.color = css_computed_border_top_color(table->style, &b.c); | |||
184 | css_computed_border_top_width(table->style, &b.width, &b.unit); | |||
185 | b.width = css_unit_len2device_px(table->style, unit_len_ctx, | |||
186 | b.width, b.unit); | |||
187 | b.unit = CSS_UNIT_PX; | |||
188 | b_src = BOX_TABLE; | |||
189 | ||||
190 | if (table_border_is_more_eyecatching(unit_len_ctx, a, *a_src, &b, b_src)) { | |||
191 | *a = b; | |||
192 | *a_src = b_src; | |||
193 | } | |||
194 | } | |||
195 | ||||
196 | ||||
197 | /** | |||
198 | * Process a row | |||
199 | * | |||
200 | * \param unit_len_ctx Length conversion context | |||
201 | * \param cell Cell being considered | |||
202 | * \param row Row to process | |||
203 | * \param a Current border style for cell | |||
204 | * \param a_src Source of \a a | |||
205 | * \return true if row has cells, false otherwise | |||
206 | * | |||
207 | * \post \a a will be updated with most eyecatching style | |||
208 | * \post \a a_src will be updated also | |||
209 | */ | |||
210 | static bool_Bool | |||
211 | table_cell_top_process_row(const css_unit_ctx *unit_len_ctx, | |||
212 | struct box *cell, | |||
213 | struct box *row, | |||
214 | struct border *a, | |||
215 | box_type *a_src) | |||
216 | { | |||
217 | struct border b; | |||
218 | box_type b_src; | |||
219 | ||||
220 | /* Bottom border of row */ | |||
221 | b.style = css_computed_border_bottom_style(row->style); | |||
222 | b.color = css_computed_border_bottom_color(row->style, &b.c); | |||
223 | css_computed_border_bottom_width(row->style, &b.width, &b.unit); | |||
224 | b.width = css_unit_len2device_px(row->style, unit_len_ctx, | |||
225 | b.width, b.unit); | |||
226 | b.unit = CSS_UNIT_PX; | |||
227 | b_src = BOX_TABLE_ROW; | |||
228 | ||||
229 | if (table_border_is_more_eyecatching(unit_len_ctx, a, *a_src, &b, b_src)) { | |||
230 | *a = b; | |||
231 | *a_src = b_src; | |||
232 | } | |||
233 | ||||
234 | if (row->children == NULL((void*)0)) { | |||
235 | /* Row is empty, so consider its top border */ | |||
236 | b.style = css_computed_border_top_style(row->style); | |||
237 | b.color = css_computed_border_top_color(row->style, &b.c); | |||
238 | css_computed_border_top_width(row->style, &b.width, &b.unit); | |||
239 | b.width = css_unit_len2device_px(row->style, unit_len_ctx, | |||
240 | b.width, b.unit); | |||
241 | b.unit = CSS_UNIT_PX; | |||
242 | b_src = BOX_TABLE_ROW; | |||
243 | ||||
244 | if (table_border_is_more_eyecatching(unit_len_ctx, | |||
245 | a, *a_src, &b, b_src)) { | |||
246 | *a = b; | |||
247 | *a_src = b_src; | |||
248 | } | |||
249 | ||||
250 | return false0; | |||
251 | } else { | |||
252 | /* Process cells that are directly above the cell being | |||
253 | * considered. They may not be in this row, but in one of the | |||
254 | * rows above it in the case where rowspan > 1. */ | |||
255 | struct box *c; | |||
256 | bool_Bool processed = false0; | |||
257 | ||||
258 | while (processed == false0) { | |||
259 | for (c = row->children; c != NULL((void*)0); c = c->next) { | |||
260 | /* Ignore cells to the left */ | |||
261 | if (c->start_column + c->columns - 1 < | |||
262 | cell->start_column) | |||
263 | continue; | |||
264 | /* Ignore cells to the right */ | |||
265 | if (c->start_column > cell->start_column + | |||
266 | cell->columns - 1) | |||
267 | continue; | |||
268 | ||||
269 | /* Flag that we've processed a cell */ | |||
270 | processed = true1; | |||
271 | ||||
272 | /* Consider bottom border */ | |||
273 | b.style = css_computed_border_bottom_style( | |||
274 | c->style); | |||
275 | b.color = css_computed_border_bottom_color( | |||
276 | c->style, &b.c); | |||
277 | css_computed_border_bottom_width(c->style, | |||
278 | &b.width, &b.unit); | |||
279 | b.width = css_unit_len2device_px( | |||
280 | c->style, unit_len_ctx, | |||
281 | b.width, b.unit); | |||
282 | b.unit = CSS_UNIT_PX; | |||
283 | b_src = BOX_TABLE_CELL; | |||
284 | ||||
285 | if (table_border_is_more_eyecatching(unit_len_ctx, | |||
286 | a, | |||
287 | *a_src, | |||
288 | &b, | |||
289 | b_src)) { | |||
290 | *a = b; | |||
291 | *a_src = b_src; | |||
292 | } | |||
293 | } | |||
294 | ||||
295 | if (processed == false0) { | |||
296 | /* There must be a preceding row */ | |||
297 | assert(row->prev != NULL)((row->prev != ((void*)0)) ? (void) (0) : __assert_fail ("row->prev != NULL" , "content/handlers/html/table.c", 297, __extension__ __PRETTY_FUNCTION__ )); | |||
298 | ||||
299 | row = row->prev; | |||
300 | } | |||
301 | } | |||
302 | } | |||
303 | ||||
304 | return true1; | |||
305 | } | |||
306 | ||||
307 | ||||
308 | /** | |||
309 | * Process a group | |||
310 | * | |||
311 | * \param unit_len_ctx Length conversion context | |||
312 | * \param cell Cell being considered | |||
313 | * \param group Group to process | |||
314 | * \param a Current border style for cell | |||
315 | * \param a_src Source of \a a | |||
316 | * \return true if group has non-empty rows, false otherwise | |||
317 | * | |||
318 | * \post \a a will be updated with most eyecatching style | |||
319 | * \post \a a_src will be updated also | |||
320 | */ | |||
321 | static bool_Bool | |||
322 | table_cell_top_process_group(const css_unit_ctx *unit_len_ctx, | |||
323 | struct box *cell, | |||
324 | struct box *group, | |||
325 | struct border *a, | |||
326 | box_type *a_src) | |||
327 | { | |||
328 | struct border b; | |||
329 | box_type b_src; | |||
330 | ||||
331 | /* Bottom border of group */ | |||
332 | b.style = css_computed_border_bottom_style(group->style); | |||
333 | b.color = css_computed_border_bottom_color(group->style, &b.c); | |||
334 | css_computed_border_bottom_width(group->style, &b.width, &b.unit); | |||
335 | b.width = css_unit_len2device_px(group->style, unit_len_ctx, | |||
336 | b.width, b.unit); | |||
337 | b.unit = CSS_UNIT_PX; | |||
338 | b_src = BOX_TABLE_ROW_GROUP; | |||
339 | ||||
340 | if (table_border_is_more_eyecatching(unit_len_ctx, a, *a_src, &b, b_src)) { | |||
341 | *a = b; | |||
342 | *a_src = b_src; | |||
343 | } | |||
344 | ||||
345 | if (group->last != NULL((void*)0)) { | |||
346 | /* Process rows in group, starting with last */ | |||
347 | struct box *row = group->last; | |||
348 | ||||
349 | while (table_cell_top_process_row(unit_len_ctx, cell, row, | |||
350 | a, a_src) == false0) { | |||
351 | if (row->prev == NULL((void*)0)) { | |||
352 | return false0; | |||
353 | } else { | |||
354 | row = row->prev; | |||
355 | } | |||
356 | } | |||
357 | } else { | |||
358 | /* Group is empty, so consider its top border */ | |||
359 | b.style = css_computed_border_top_style(group->style); | |||
360 | b.color = css_computed_border_top_color(group->style, &b.c); | |||
361 | css_computed_border_top_width(group->style, &b.width, &b.unit); | |||
362 | b.width = css_unit_len2device_px(group->style, unit_len_ctx, | |||
363 | b.width, b.unit); | |||
364 | b.unit = CSS_UNIT_PX; | |||
365 | b_src = BOX_TABLE_ROW_GROUP; | |||
366 | ||||
367 | if (table_border_is_more_eyecatching(unit_len_ctx, | |||
368 | a, *a_src, &b, b_src)) { | |||
369 | *a = b; | |||
370 | *a_src = b_src; | |||
371 | } | |||
372 | ||||
373 | return false0; | |||
374 | } | |||
375 | ||||
376 | return true1; | |||
377 | } | |||
378 | ||||
379 | ||||
380 | /** | |||
381 | * Calculate used values of border-left-{style,color,width} | |||
382 | * | |||
383 | * \param unit_len_ctx Length conversion context | |||
384 | * \param cell Table cell to consider | |||
385 | */ | |||
386 | static void | |||
387 | table_used_left_border_for_cell(const css_unit_ctx *unit_len_ctx, struct box *cell) | |||
388 | { | |||
389 | struct border a, b; | |||
390 | box_type a_src, b_src; | |||
391 | ||||
392 | /** \todo Need column and column_group, too */ | |||
393 | ||||
394 | /* Initialise to computed left border for cell */ | |||
395 | a.style = css_computed_border_left_style(cell->style); | |||
396 | a.color = css_computed_border_left_color(cell->style, &a.c); | |||
397 | css_computed_border_left_width(cell->style, &a.width, &a.unit); | |||
398 | a.width = css_unit_len2device_px(cell->style, unit_len_ctx, | |||
399 | a.width, a.unit); | |||
400 | a.unit = CSS_UNIT_PX; | |||
401 | a_src = BOX_TABLE_CELL; | |||
402 | ||||
403 | if (cell->prev != NULL((void*)0) || cell->start_column != 0) { | |||
404 | /* Cell to the left -- consider its right border */ | |||
405 | struct box *prev = NULL((void*)0); | |||
406 | ||||
407 | if (cell->prev == NULL((void*)0)) { | |||
408 | struct box *row; | |||
409 | ||||
410 | /* Spanned from a previous row in current row group */ | |||
411 | for (row = cell->parent; row != NULL((void*)0); row = row->prev) { | |||
412 | for (prev = row->children; prev != NULL((void*)0); | |||
413 | prev = prev->next) { | |||
414 | if (prev->start_column + | |||
415 | prev->columns == | |||
416 | cell->start_column) | |||
417 | break; | |||
418 | } | |||
419 | ||||
420 | if (prev != NULL((void*)0)) | |||
421 | break; | |||
422 | } | |||
423 | ||||
424 | assert(prev != NULL)((prev != ((void*)0)) ? (void) (0) : __assert_fail ("prev != NULL" , "content/handlers/html/table.c", 424, __extension__ __PRETTY_FUNCTION__ )); | |||
425 | } else { | |||
426 | prev = cell->prev; | |||
427 | } | |||
428 | ||||
429 | b.style = css_computed_border_right_style(prev->style); | |||
430 | b.color = css_computed_border_right_color(prev->style, &b.c); | |||
431 | css_computed_border_right_width(prev->style, &b.width, &b.unit); | |||
432 | b.width = css_unit_len2device_px(prev->style, unit_len_ctx, | |||
433 | b.width, b.unit); | |||
434 | b.unit = CSS_UNIT_PX; | |||
435 | b_src = BOX_TABLE_CELL; | |||
436 | ||||
437 | if (table_border_is_more_eyecatching(unit_len_ctx, | |||
438 | &a, a_src, &b, b_src)) { | |||
439 | a = b; | |||
440 | a_src = b_src; | |||
441 | } | |||
442 | } else { | |||
443 | /* First cell in row, so consider rows and row group */ | |||
444 | struct box *row = cell->parent; | |||
445 | struct box *group = row->parent; | |||
446 | struct box *table = group->parent; | |||
447 | unsigned int rows = cell->rows; | |||
448 | ||||
449 | while (rows-- > 0 && row != NULL((void*)0)) { | |||
450 | /* Spanned rows -- consider their left border */ | |||
451 | b.style = css_computed_border_left_style(row->style); | |||
452 | b.color = css_computed_border_left_color( | |||
453 | row->style, &b.c); | |||
454 | css_computed_border_left_width( | |||
455 | row->style, &b.width, &b.unit); | |||
456 | b.width = css_unit_len2device_px( | |||
457 | row->style, unit_len_ctx, | |||
458 | b.width, b.unit); | |||
459 | b.unit = CSS_UNIT_PX; | |||
460 | b_src = BOX_TABLE_ROW; | |||
461 | ||||
462 | if (table_border_is_more_eyecatching(unit_len_ctx, | |||
463 | &a, a_src, &b, b_src)) { | |||
464 | a = b; | |||
465 | a_src = b_src; | |||
466 | } | |||
467 | ||||
468 | row = row->next; | |||
469 | } | |||
470 | ||||
471 | /** \todo can cells span row groups? */ | |||
472 | ||||
473 | /* Row group -- consider its left border */ | |||
474 | b.style = css_computed_border_left_style(group->style); | |||
475 | b.color = css_computed_border_left_color(group->style, &b.c); | |||
476 | css_computed_border_left_width(group->style, &b.width, &b.unit); | |||
477 | b.width = css_unit_len2device_px(group->style, unit_len_ctx, | |||
478 | b.width, b.unit); | |||
479 | b.unit = CSS_UNIT_PX; | |||
480 | b_src = BOX_TABLE_ROW_GROUP; | |||
481 | ||||
482 | if (table_border_is_more_eyecatching(unit_len_ctx, | |||
483 | &a, a_src, &b, b_src)) { | |||
484 | a = b; | |||
485 | a_src = b_src; | |||
486 | } | |||
487 | ||||
488 | /* The table itself -- consider its left border */ | |||
489 | b.style = css_computed_border_left_style(table->style); | |||
490 | b.color = css_computed_border_left_color(table->style, &b.c); | |||
491 | css_computed_border_left_width(table->style, &b.width, &b.unit); | |||
492 | b.width = css_unit_len2device_px(table->style, unit_len_ctx, | |||
493 | b.width, b.unit); | |||
494 | b.unit = CSS_UNIT_PX; | |||
495 | b_src = BOX_TABLE; | |||
496 | ||||
497 | if (table_border_is_more_eyecatching(unit_len_ctx, | |||
498 | &a, a_src, &b, b_src)) { | |||
499 | a = b; | |||
500 | a_src = b_src; | |||
501 | } | |||
502 | } | |||
503 | ||||
504 | /* a now contains the used left border for the cell */ | |||
505 | cell->border[LEFT].style = a.style; | |||
506 | cell->border[LEFT].c = a.c; | |||
507 | cell->border[LEFT].width = FIXTOINT(css_unit_len2device_px(((css_unit_len2device_px( cell->style, unit_len_ctx, a.width , a.unit)) >> 10) | |||
508 | cell->style, unit_len_ctx, a.width, a.unit))((css_unit_len2device_px( cell->style, unit_len_ctx, a.width , a.unit)) >> 10); | |||
509 | } | |||
510 | ||||
511 | ||||
512 | /** | |||
513 | * Calculate used values of border-top-{style,color,width} | |||
514 | * | |||
515 | * \param unit_len_ctx Length conversion context | |||
516 | * \param cell Table cell to consider | |||
517 | */ | |||
518 | static void | |||
519 | table_used_top_border_for_cell(const css_unit_ctx *unit_len_ctx, struct box *cell) | |||
520 | { | |||
521 | struct border a, b; | |||
522 | box_type a_src, b_src; | |||
523 | struct box *row = cell->parent; | |||
524 | bool_Bool process_group = false0; | |||
525 | ||||
526 | /* Initialise to computed top border for cell */ | |||
527 | a.style = css_computed_border_top_style(cell->style); | |||
528 | css_computed_border_top_color(cell->style, &a.c); | |||
529 | css_computed_border_top_width(cell->style, &a.width, &a.unit); | |||
530 | a.width = css_unit_len2device_px(cell->style, unit_len_ctx, | |||
531 | a.width, a.unit); | |||
532 | a.unit = CSS_UNIT_PX; | |||
533 | a_src = BOX_TABLE_CELL; | |||
534 | ||||
535 | /* Top border of row */ | |||
536 | b.style = css_computed_border_top_style(row->style); | |||
537 | css_computed_border_top_color(row->style, &b.c); | |||
538 | css_computed_border_top_width(row->style, &b.width, &b.unit); | |||
539 | b.width = css_unit_len2device_px(row->style, unit_len_ctx, | |||
540 | b.width, b.unit); | |||
541 | b.unit = CSS_UNIT_PX; | |||
542 | b_src = BOX_TABLE_ROW; | |||
543 | ||||
544 | if (table_border_is_more_eyecatching(unit_len_ctx, &a, a_src, &b, b_src)) { | |||
545 | a = b; | |||
546 | a_src = b_src; | |||
547 | } | |||
548 | ||||
549 | if (row->prev != NULL((void*)0)) { | |||
550 | /* Consider row(s) above */ | |||
551 | while (table_cell_top_process_row(unit_len_ctx, cell, row->prev, | |||
552 | &a, &a_src) == false0) { | |||
553 | if (row->prev->prev == NULL((void*)0)) { | |||
554 | /* Consider row group */ | |||
555 | process_group = true1; | |||
556 | break; | |||
557 | } else { | |||
558 | row = row->prev; | |||
559 | } | |||
560 | } | |||
561 | } else { | |||
562 | process_group = true1; | |||
563 | } | |||
564 | ||||
565 | if (process_group) { | |||
566 | struct box *group = row->parent; | |||
567 | ||||
568 | /* Top border of row group */ | |||
569 | b.style = css_computed_border_top_style(group->style); | |||
570 | b.color = css_computed_border_top_color(group->style, &b.c); | |||
571 | css_computed_border_top_width(group->style, &b.width, &b.unit); | |||
572 | b.width = css_unit_len2device_px(group->style, unit_len_ctx, | |||
573 | b.width, b.unit); | |||
574 | b.unit = CSS_UNIT_PX; | |||
575 | b_src = BOX_TABLE_ROW_GROUP; | |||
576 | ||||
577 | if (table_border_is_more_eyecatching(unit_len_ctx, | |||
578 | &a, a_src, &b, b_src)) { | |||
579 | a = b; | |||
580 | a_src = b_src; | |||
581 | } | |||
582 | ||||
583 | if (group->prev == NULL((void*)0)) { | |||
584 | /* Top border of table */ | |||
585 | table_cell_top_process_table(unit_len_ctx, | |||
586 | group->parent, &a, &a_src); | |||
587 | } else { | |||
588 | /* Process previous group(s) */ | |||
589 | while (table_cell_top_process_group(unit_len_ctx, | |||
590 | cell, group->prev, | |||
591 | &a, &a_src) == false0) { | |||
592 | if (group->prev->prev == NULL((void*)0)) { | |||
593 | /* Top border of table */ | |||
594 | table_cell_top_process_table(unit_len_ctx, | |||
595 | group->parent, | |||
596 | &a, &a_src); | |||
597 | break; | |||
598 | } else { | |||
599 | group = group->prev; | |||
600 | } | |||
601 | } | |||
602 | } | |||
603 | } | |||
604 | ||||
605 | /* a now contains the used top border for the cell */ | |||
606 | cell->border[TOP].style = a.style; | |||
607 | cell->border[TOP].c = a.c; | |||
608 | cell->border[TOP].width = FIXTOINT(css_unit_len2device_px(((css_unit_len2device_px( cell->style, unit_len_ctx, a.width , a.unit)) >> 10) | |||
609 | cell->style, unit_len_ctx, a.width, a.unit))((css_unit_len2device_px( cell->style, unit_len_ctx, a.width , a.unit)) >> 10); | |||
610 | } | |||
611 | ||||
612 | /** | |||
613 | * Calculate used values of border-right-{style,color,width} | |||
614 | * | |||
615 | * \param unit_len_ctx Length conversion context | |||
616 | * \param cell Table cell to consider | |||
617 | */ | |||
618 | static void | |||
619 | table_used_right_border_for_cell(const css_unit_ctx *unit_len_ctx, struct box *cell) | |||
620 | { | |||
621 | struct border a, b; | |||
622 | box_type a_src, b_src; | |||
623 | ||||
624 | /** \todo Need column and column_group, too */ | |||
625 | ||||
626 | /* Initialise to computed right border for cell */ | |||
627 | a.style = css_computed_border_right_style(cell->style); | |||
628 | css_computed_border_right_color(cell->style, &a.c); | |||
629 | css_computed_border_right_width(cell->style, &a.width, &a.unit); | |||
630 | a.width = css_unit_len2device_px(cell->style, unit_len_ctx, | |||
631 | a.width, a.unit); | |||
632 | a.unit = CSS_UNIT_PX; | |||
633 | a_src = BOX_TABLE_CELL; | |||
634 | ||||
635 | if (cell->next != NULL((void*)0) || cell->start_column + cell->columns != | |||
636 | cell->parent->parent->parent->columns) { | |||
637 | /* Cell is not at right edge of table -- no right border */ | |||
638 | a.style = CSS_BORDER_STYLE_NONE; | |||
639 | a.width = 0; | |||
640 | a.unit = CSS_UNIT_PX; | |||
641 | } else { | |||
642 | /* Last cell in row, so consider rows and row group */ | |||
643 | struct box *row = cell->parent; | |||
644 | struct box *group = row->parent; | |||
645 | struct box *table = group->parent; | |||
646 | unsigned int rows = cell->rows; | |||
647 | ||||
648 | while (rows-- > 0 && row != NULL((void*)0)) { | |||
649 | /* Spanned rows -- consider their right border */ | |||
650 | b.style = css_computed_border_right_style(row->style); | |||
651 | b.color = css_computed_border_right_color(row->style, | |||
652 | &b.c); | |||
653 | css_computed_border_right_width(row->style, | |||
654 | &b.width, | |||
655 | &b.unit); | |||
656 | b.width = css_unit_len2device_px( | |||
657 | row->style, unit_len_ctx, | |||
658 | b.width, b.unit); | |||
659 | b.unit = CSS_UNIT_PX; | |||
660 | b_src = BOX_TABLE_ROW; | |||
661 | ||||
662 | if (table_border_is_more_eyecatching(unit_len_ctx, | |||
663 | &a, a_src, | |||
664 | &b, b_src)) { | |||
665 | a = b; | |||
666 | a_src = b_src; | |||
667 | } | |||
668 | ||||
669 | row = row->next; | |||
670 | } | |||
671 | ||||
672 | /** \todo can cells span row groups? */ | |||
673 | ||||
674 | /* Row group -- consider its right border */ | |||
675 | b.style = css_computed_border_right_style(group->style); | |||
676 | b.color = css_computed_border_right_color(group->style, &b.c); | |||
677 | css_computed_border_right_width(group->style, | |||
678 | &b.width, &b.unit); | |||
679 | b.width = css_unit_len2device_px(group->style, unit_len_ctx, | |||
680 | b.width, b.unit); | |||
681 | b.unit = CSS_UNIT_PX; | |||
682 | b_src = BOX_TABLE_ROW_GROUP; | |||
683 | ||||
684 | if (table_border_is_more_eyecatching(unit_len_ctx, | |||
685 | &a, a_src, &b, b_src)) { | |||
686 | a = b; | |||
687 | a_src = b_src; | |||
688 | } | |||
689 | ||||
690 | /* The table itself -- consider its right border */ | |||
691 | b.style = css_computed_border_right_style(table->style); | |||
692 | b.color = css_computed_border_right_color(table->style, &b.c); | |||
693 | css_computed_border_right_width(table->style, | |||
694 | &b.width, &b.unit); | |||
695 | b.width = css_unit_len2device_px(table->style, unit_len_ctx, | |||
696 | b.width, b.unit); | |||
697 | b.unit = CSS_UNIT_PX; | |||
698 | b_src = BOX_TABLE; | |||
699 | ||||
700 | if (table_border_is_more_eyecatching(unit_len_ctx, | |||
701 | &a, a_src, | |||
702 | &b, b_src)) { | |||
703 | a = b; | |||
704 | a_src = b_src; | |||
705 | } | |||
706 | } | |||
707 | ||||
708 | /* a now contains the used right border for the cell */ | |||
709 | cell->border[RIGHT].style = a.style; | |||
710 | cell->border[RIGHT].c = a.c; | |||
711 | cell->border[RIGHT].width = FIXTOINT(css_unit_len2device_px(((css_unit_len2device_px( cell->style, unit_len_ctx, a.width , a.unit)) >> 10) | |||
712 | cell->style, unit_len_ctx, a.width, a.unit))((css_unit_len2device_px( cell->style, unit_len_ctx, a.width , a.unit)) >> 10); | |||
713 | } | |||
714 | ||||
715 | ||||
716 | /** | |||
717 | * Calculate used values of border-bottom-{style,color,width} | |||
718 | * | |||
719 | * \param unit_len_ctx Length conversion context | |||
720 | * \param cell Table cell to consider | |||
721 | */ | |||
722 | static void | |||
723 | table_used_bottom_border_for_cell(const css_unit_ctx *unit_len_ctx, | |||
724 | struct box *cell) | |||
725 | { | |||
726 | struct border a, b; | |||
727 | box_type a_src, b_src; | |||
728 | struct box *row = cell->parent; | |||
729 | unsigned int rows = cell->rows; | |||
730 | ||||
731 | /* Initialise to computed bottom border for cell */ | |||
732 | a.style = css_computed_border_bottom_style(cell->style); | |||
733 | css_computed_border_bottom_color(cell->style, &a.c); | |||
734 | css_computed_border_bottom_width(cell->style, &a.width, &a.unit); | |||
735 | a.width = css_unit_len2device_px(cell->style, unit_len_ctx, | |||
736 | a.width, a.unit); | |||
737 | a.unit = CSS_UNIT_PX; | |||
738 | a_src = BOX_TABLE_CELL; | |||
739 | ||||
740 | while (rows-- > 0 && row != NULL((void*)0)) | |||
741 | row = row->next; | |||
742 | ||||
743 | /** \todo Can cells span row groups? */ | |||
744 | ||||
745 | if (row != NULL((void*)0)) { | |||
746 | /* Cell is not at bottom edge of table -- no bottom border */ | |||
747 | a.style = CSS_BORDER_STYLE_NONE; | |||
748 | a.width = 0; | |||
749 | a.unit = CSS_UNIT_PX; | |||
750 | } else { | |||
751 | /* Cell at bottom of table, so consider row and row group */ | |||
752 | struct box *row = cell->parent; | |||
753 | struct box *group = row->parent; | |||
| ||||
754 | struct box *table = group->parent; | |||
755 | ||||
756 | /* Bottom border of row */ | |||
757 | b.style = css_computed_border_bottom_style(row->style); | |||
758 | b.color = css_computed_border_bottom_color(row->style, &b.c); | |||
759 | css_computed_border_bottom_width(row->style, &b.width, &b.unit); | |||
760 | b.width = css_unit_len2device_px(row->style, unit_len_ctx, | |||
761 | b.width, b.unit); | |||
762 | b.unit = CSS_UNIT_PX; | |||
763 | b_src = BOX_TABLE_ROW; | |||
764 | ||||
765 | if (table_border_is_more_eyecatching(unit_len_ctx, | |||
766 | &a, a_src, &b, b_src)) { | |||
767 | a = b; | |||
768 | a_src = b_src; | |||
769 | } | |||
770 | ||||
771 | /* Row group -- consider its bottom border */ | |||
772 | b.style = css_computed_border_bottom_style(group->style); | |||
773 | b.color = css_computed_border_bottom_color(group->style, &b.c); | |||
774 | css_computed_border_bottom_width(group->style, | |||
775 | &b.width, &b.unit); | |||
776 | b.width = css_unit_len2device_px(group->style, unit_len_ctx, | |||
777 | b.width, b.unit); | |||
778 | b.unit = CSS_UNIT_PX; | |||
779 | b_src = BOX_TABLE_ROW_GROUP; | |||
780 | ||||
781 | if (table_border_is_more_eyecatching(unit_len_ctx, | |||
782 | &a, a_src, &b, b_src)) { | |||
783 | a = b; | |||
784 | a_src = b_src; | |||
785 | } | |||
786 | ||||
787 | /* The table itself -- consider its bottom border */ | |||
788 | b.style = css_computed_border_bottom_style(table->style); | |||
789 | b.color = css_computed_border_bottom_color(table->style, &b.c); | |||
790 | css_computed_border_bottom_width(table->style, | |||
791 | &b.width, &b.unit); | |||
792 | b.width = css_unit_len2device_px(table->style, unit_len_ctx, | |||
793 | b.width, b.unit); | |||
794 | b.unit = CSS_UNIT_PX; | |||
795 | b_src = BOX_TABLE; | |||
796 | ||||
797 | if (table_border_is_more_eyecatching(unit_len_ctx, | |||
798 | &a, a_src, &b, b_src)) { | |||
799 | a = b; | |||
800 | } | |||
801 | } | |||
802 | ||||
803 | /* a now contains the used bottom border for the cell */ | |||
804 | cell->border[BOTTOM].style = a.style; | |||
805 | cell->border[BOTTOM].c = a.c; | |||
806 | cell->border[BOTTOM].width = FIXTOINT(css_unit_len2device_px(((css_unit_len2device_px( cell->style, unit_len_ctx, a.width , a.unit)) >> 10) | |||
807 | cell->style, unit_len_ctx, a.width, a.unit))((css_unit_len2device_px( cell->style, unit_len_ctx, a.width , a.unit)) >> 10); | |||
808 | } | |||
809 | ||||
810 | ||||
811 | /* exported interface documented in html/table.h */ | |||
812 | bool_Bool | |||
813 | table_calculate_column_types(const css_unit_ctx *unit_len_ctx, struct box *table) | |||
814 | { | |||
815 | unsigned int i, j; | |||
816 | struct column *col; | |||
817 | struct box *row_group, *row, *cell; | |||
818 | ||||
819 | if (table->col) | |||
820 | /* table->col already constructed, for example frameset table */ | |||
821 | return true1; | |||
822 | ||||
823 | table->col = col = talloc_array(table, struct column, table->columns)(struct column *)_talloc_array(table, sizeof(struct column), table ->columns, "struct column"); | |||
824 | if (!col) | |||
825 | return false0; | |||
826 | ||||
827 | for (i = 0; i != table->columns; i++) { | |||
828 | col[i].type = COLUMN_WIDTH_UNKNOWN; | |||
829 | col[i].width = 0; | |||
830 | col[i].positioned = true1; | |||
831 | } | |||
832 | ||||
833 | /* 1st pass: cells with colspan 1 only */ | |||
834 | for (row_group = table->children; row_group; row_group =row_group->next) | |||
835 | for (row = row_group->children; row; row = row->next) | |||
836 | for (cell = row->children; cell; cell = cell->next) { | |||
837 | enum css_width_e type; | |||
838 | css_fixed value = 0; | |||
839 | css_unit unit = CSS_UNIT_PX; | |||
840 | ||||
841 | assert(cell->type == BOX_TABLE_CELL)((cell->type == BOX_TABLE_CELL) ? (void) (0) : __assert_fail ("cell->type == BOX_TABLE_CELL", "content/handlers/html/table.c" , 841, __extension__ __PRETTY_FUNCTION__)); | |||
842 | assert(cell->style)((cell->style) ? (void) (0) : __assert_fail ("cell->style" , "content/handlers/html/table.c", 842, __extension__ __PRETTY_FUNCTION__ )); | |||
843 | ||||
844 | if (cell->columns != 1) | |||
845 | continue; | |||
846 | i = cell->start_column; | |||
847 | ||||
848 | if (css_computed_position(cell->style) != | |||
849 | CSS_POSITION_ABSOLUTE && | |||
850 | css_computed_position(cell->style) != | |||
851 | CSS_POSITION_FIXED) { | |||
852 | col[i].positioned = false0; | |||
853 | } | |||
854 | ||||
855 | type = css_computed_width(cell->style, &value, &unit); | |||
856 | ||||
857 | /* fixed width takes priority over any other width type */ | |||
858 | if (col[i].type != COLUMN_WIDTH_FIXED && | |||
859 | type == CSS_WIDTH_SET && unit != CSS_UNIT_PCT) { | |||
860 | col[i].type = COLUMN_WIDTH_FIXED; | |||
861 | col[i].width = FIXTOINT(css_unit_len2device_px(((css_unit_len2device_px( cell->style, unit_len_ctx, value , unit)) >> 10) | |||
862 | cell->style,((css_unit_len2device_px( cell->style, unit_len_ctx, value , unit)) >> 10) | |||
863 | unit_len_ctx,((css_unit_len2device_px( cell->style, unit_len_ctx, value , unit)) >> 10) | |||
864 | value, unit))((css_unit_len2device_px( cell->style, unit_len_ctx, value , unit)) >> 10); | |||
865 | if (col[i].width < 0) | |||
866 | col[i].width = 0; | |||
867 | continue; | |||
868 | } | |||
869 | ||||
870 | if (col[i].type != COLUMN_WIDTH_UNKNOWN) | |||
871 | continue; | |||
872 | ||||
873 | if (type == CSS_WIDTH_SET && unit == CSS_UNIT_PCT) { | |||
874 | col[i].type = COLUMN_WIDTH_PERCENT; | |||
875 | col[i].width = FIXTOINT(value)((value) >> 10); | |||
876 | if (col[i].width < 0) | |||
877 | col[i].width = 0; | |||
878 | } else if (type == CSS_WIDTH_AUTO) { | |||
879 | col[i].type = COLUMN_WIDTH_AUTO; | |||
880 | } | |||
881 | } | |||
882 | ||||
883 | /* 2nd pass: cells which span multiple columns */ | |||
884 | for (row_group = table->children; row_group; row_group =row_group->next) | |||
885 | for (row = row_group->children; row; row = row->next) | |||
886 | for (cell = row->children; cell; cell = cell->next) { | |||
887 | unsigned int fixed_columns = 0, | |||
888 | percent_columns = 0, | |||
889 | auto_columns = 0, | |||
890 | unknown_columns = 0; | |||
891 | int fixed_width = 0, percent_width = 0; | |||
892 | enum css_width_e type; | |||
893 | css_fixed value = 0; | |||
894 | css_unit unit = CSS_UNIT_PX; | |||
895 | ||||
896 | if (cell->columns == 1) | |||
897 | continue; | |||
898 | i = cell->start_column; | |||
899 | ||||
900 | for (j = i; j < i + cell->columns; j++) { | |||
901 | col[j].positioned = false0; | |||
902 | } | |||
903 | ||||
904 | /* count column types in spanned cells */ | |||
905 | for (j = 0; j != cell->columns; j++) { | |||
906 | if (col[i + j].type == COLUMN_WIDTH_FIXED) { | |||
907 | fixed_width += col[i + j].width; | |||
908 | fixed_columns++; | |||
909 | } else if (col[i + j].type == COLUMN_WIDTH_PERCENT) { | |||
910 | percent_width += col[i + j].width; | |||
911 | percent_columns++; | |||
912 | } else if (col[i + j].type == COLUMN_WIDTH_AUTO) { | |||
913 | auto_columns++; | |||
914 | } else { | |||
915 | unknown_columns++; | |||
916 | } | |||
917 | } | |||
918 | ||||
919 | if (!unknown_columns) | |||
920 | continue; | |||
921 | ||||
922 | type = css_computed_width(cell->style, &value, &unit); | |||
923 | ||||
924 | /* if cell is fixed width, and all spanned columns are fixed | |||
925 | * or unknown width, split extra width among unknown columns */ | |||
926 | if (type == CSS_WIDTH_SET && unit != CSS_UNIT_PCT && | |||
927 | fixed_columns + unknown_columns == | |||
928 | cell->columns) { | |||
929 | int width = (FIXTOFLT(css_unit_len2device_px(((float) (css_unit_len2device_px( cell->style, unit_len_ctx , value, unit)) / (float) (1 << 10)) | |||
930 | cell->style,((float) (css_unit_len2device_px( cell->style, unit_len_ctx , value, unit)) / (float) (1 << 10)) | |||
931 | unit_len_ctx,((float) (css_unit_len2device_px( cell->style, unit_len_ctx , value, unit)) / (float) (1 << 10)) | |||
932 | value, unit))((float) (css_unit_len2device_px( cell->style, unit_len_ctx , value, unit)) / (float) (1 << 10)) - | |||
933 | fixed_width) / unknown_columns; | |||
934 | if (width < 0) | |||
935 | width = 0; | |||
936 | for (j = 0; j != cell->columns; j++) { | |||
937 | if (col[i + j].type == COLUMN_WIDTH_UNKNOWN) { | |||
938 | col[i + j].type = COLUMN_WIDTH_FIXED; | |||
939 | col[i + j].width = width; | |||
940 | } | |||
941 | } | |||
942 | } | |||
943 | ||||
944 | /* as above for percentage width */ | |||
945 | if (type == CSS_WIDTH_SET && unit == CSS_UNIT_PCT && | |||
946 | percent_columns + unknown_columns == | |||
947 | cell->columns) { | |||
948 | int width = (FIXTOFLT(value)((float) (value) / (float) (1 << 10)) - | |||
949 | percent_width) / unknown_columns; | |||
950 | if (width < 0) | |||
951 | width = 0; | |||
952 | for (j = 0; j != cell->columns; j++) { | |||
953 | if (col[i + j].type == COLUMN_WIDTH_UNKNOWN) { | |||
954 | col[i + j].type = COLUMN_WIDTH_PERCENT; | |||
955 | col[i + j].width = width; | |||
956 | } | |||
957 | } | |||
958 | } | |||
959 | } | |||
960 | ||||
961 | /* use AUTO if no width type was specified */ | |||
962 | for (i = 0; i != table->columns; i++) { | |||
963 | if (col[i].type == COLUMN_WIDTH_UNKNOWN) | |||
964 | col[i].type = COLUMN_WIDTH_AUTO; | |||
965 | } | |||
966 | ||||
967 | #ifdef TABLE_DEBUG | |||
968 | for (i = 0; i != table->columns; i++) | |||
969 | NSLOG(netsurf, INFO,do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf , NSLOG_LEVEL_INFO, "content/handlers/html/table.c", sizeof("content/handlers/html/table.c" ) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 980 , }; nslog__log(&_nslog_ctx, "table %p, column %u: type %s, width %i" , table, i, ((const char *[]){ "UNKNOWN", "FIXED", "AUTO", "PERCENT" , "RELATIVE", })[col[i].type], col[i].width); } } while(0) | |||
970 | "table %p, column %u: type %s, width %i",do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf , NSLOG_LEVEL_INFO, "content/handlers/html/table.c", sizeof("content/handlers/html/table.c" ) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 980 , }; nslog__log(&_nslog_ctx, "table %p, column %u: type %s, width %i" , table, i, ((const char *[]){ "UNKNOWN", "FIXED", "AUTO", "PERCENT" , "RELATIVE", })[col[i].type], col[i].width); } } while(0) | |||
971 | table,do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf , NSLOG_LEVEL_INFO, "content/handlers/html/table.c", sizeof("content/handlers/html/table.c" ) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 980 , }; nslog__log(&_nslog_ctx, "table %p, column %u: type %s, width %i" , table, i, ((const char *[]){ "UNKNOWN", "FIXED", "AUTO", "PERCENT" , "RELATIVE", })[col[i].type], col[i].width); } } while(0) | |||
972 | i,do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf , NSLOG_LEVEL_INFO, "content/handlers/html/table.c", sizeof("content/handlers/html/table.c" ) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 980 , }; nslog__log(&_nslog_ctx, "table %p, column %u: type %s, width %i" , table, i, ((const char *[]){ "UNKNOWN", "FIXED", "AUTO", "PERCENT" , "RELATIVE", })[col[i].type], col[i].width); } } while(0) | |||
973 | ((const char *[]){do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf , NSLOG_LEVEL_INFO, "content/handlers/html/table.c", sizeof("content/handlers/html/table.c" ) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 980 , }; nslog__log(&_nslog_ctx, "table %p, column %u: type %s, width %i" , table, i, ((const char *[]){ "UNKNOWN", "FIXED", "AUTO", "PERCENT" , "RELATIVE", })[col[i].type], col[i].width); } } while(0) | |||
974 | "UNKNOWN",do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf , NSLOG_LEVEL_INFO, "content/handlers/html/table.c", sizeof("content/handlers/html/table.c" ) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 980 , }; nslog__log(&_nslog_ctx, "table %p, column %u: type %s, width %i" , table, i, ((const char *[]){ "UNKNOWN", "FIXED", "AUTO", "PERCENT" , "RELATIVE", })[col[i].type], col[i].width); } } while(0) | |||
975 | "FIXED",do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf , NSLOG_LEVEL_INFO, "content/handlers/html/table.c", sizeof("content/handlers/html/table.c" ) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 980 , }; nslog__log(&_nslog_ctx, "table %p, column %u: type %s, width %i" , table, i, ((const char *[]){ "UNKNOWN", "FIXED", "AUTO", "PERCENT" , "RELATIVE", })[col[i].type], col[i].width); } } while(0) | |||
976 | "AUTO",do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf , NSLOG_LEVEL_INFO, "content/handlers/html/table.c", sizeof("content/handlers/html/table.c" ) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 980 , }; nslog__log(&_nslog_ctx, "table %p, column %u: type %s, width %i" , table, i, ((const char *[]){ "UNKNOWN", "FIXED", "AUTO", "PERCENT" , "RELATIVE", })[col[i].type], col[i].width); } } while(0) | |||
977 | "PERCENT",do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf , NSLOG_LEVEL_INFO, "content/handlers/html/table.c", sizeof("content/handlers/html/table.c" ) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 980 , }; nslog__log(&_nslog_ctx, "table %p, column %u: type %s, width %i" , table, i, ((const char *[]){ "UNKNOWN", "FIXED", "AUTO", "PERCENT" , "RELATIVE", })[col[i].type], col[i].width); } } while(0) | |||
978 | "RELATIVE",do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf , NSLOG_LEVEL_INFO, "content/handlers/html/table.c", sizeof("content/handlers/html/table.c" ) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 980 , }; nslog__log(&_nslog_ctx, "table %p, column %u: type %s, width %i" , table, i, ((const char *[]){ "UNKNOWN", "FIXED", "AUTO", "PERCENT" , "RELATIVE", })[col[i].type], col[i].width); } } while(0) | |||
979 | })[col[i].type],do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf , NSLOG_LEVEL_INFO, "content/handlers/html/table.c", sizeof("content/handlers/html/table.c" ) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 980 , }; nslog__log(&_nslog_ctx, "table %p, column %u: type %s, width %i" , table, i, ((const char *[]){ "UNKNOWN", "FIXED", "AUTO", "PERCENT" , "RELATIVE", })[col[i].type], col[i].width); } } while(0) | |||
980 | col[i].width)do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf , NSLOG_LEVEL_INFO, "content/handlers/html/table.c", sizeof("content/handlers/html/table.c" ) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 980 , }; nslog__log(&_nslog_ctx, "table %p, column %u: type %s, width %i" , table, i, ((const char *[]){ "UNKNOWN", "FIXED", "AUTO", "PERCENT" , "RELATIVE", })[col[i].type], col[i].width); } } while(0); | |||
981 | #endif | |||
982 | ||||
983 | return true1; | |||
984 | } | |||
985 | ||||
986 | ||||
987 | /* exported interface documented in html/table.h */ | |||
988 | void table_used_border_for_cell(const css_unit_ctx *unit_len_ctx, struct box *cell) | |||
989 | { | |||
990 | int side; | |||
991 | ||||
992 | assert(cell->type == BOX_TABLE_CELL)((cell->type == BOX_TABLE_CELL) ? (void) (0) : __assert_fail ("cell->type == BOX_TABLE_CELL", "content/handlers/html/table.c" , 992, __extension__ __PRETTY_FUNCTION__)); | |||
| ||||
993 | ||||
994 | if (css_computed_border_collapse(cell->style) == | |||
995 | CSS_BORDER_COLLAPSE_SEPARATE) { | |||
996 | css_fixed width = 0; | |||
997 | css_unit unit = CSS_UNIT_PX; | |||
998 | ||||
999 | /* Left border */ | |||
1000 | cell->border[LEFT].style = | |||
1001 | css_computed_border_left_style(cell->style); | |||
1002 | css_computed_border_left_color(cell->style, | |||
1003 | &cell->border[LEFT].c); | |||
1004 | css_computed_border_left_width(cell->style, &width, &unit); | |||
1005 | cell->border[LEFT].width = | |||
1006 | FIXTOINT(css_unit_len2device_px(((css_unit_len2device_px( cell->style, unit_len_ctx, width , unit)) >> 10) | |||
1007 | cell->style, unit_len_ctx,((css_unit_len2device_px( cell->style, unit_len_ctx, width , unit)) >> 10) | |||
1008 | width, unit))((css_unit_len2device_px( cell->style, unit_len_ctx, width , unit)) >> 10); | |||
1009 | ||||
1010 | /* Top border */ | |||
1011 | cell->border[TOP].style = | |||
1012 | css_computed_border_top_style(cell->style); | |||
1013 | css_computed_border_top_color(cell->style, | |||
1014 | &cell->border[TOP].c); | |||
1015 | css_computed_border_top_width(cell->style, &width, &unit); | |||
1016 | cell->border[TOP].width = | |||
1017 | FIXTOINT(css_unit_len2device_px(((css_unit_len2device_px( cell->style, unit_len_ctx, width , unit)) >> 10) | |||
1018 | cell->style, unit_len_ctx,((css_unit_len2device_px( cell->style, unit_len_ctx, width , unit)) >> 10) | |||
1019 | width, unit))((css_unit_len2device_px( cell->style, unit_len_ctx, width , unit)) >> 10); | |||
1020 | ||||
1021 | /* Right border */ | |||
1022 | cell->border[RIGHT].style = | |||
1023 | css_computed_border_right_style(cell->style); | |||
1024 | css_computed_border_right_color(cell->style, | |||
1025 | &cell->border[RIGHT].c); | |||
1026 | css_computed_border_right_width(cell->style, &width, &unit); | |||
1027 | cell->border[RIGHT].width = | |||
1028 | FIXTOINT(css_unit_len2device_px(((css_unit_len2device_px( cell->style, unit_len_ctx, width , unit)) >> 10) | |||
1029 | cell->style, unit_len_ctx,((css_unit_len2device_px( cell->style, unit_len_ctx, width , unit)) >> 10) | |||
1030 | width, unit))((css_unit_len2device_px( cell->style, unit_len_ctx, width , unit)) >> 10); | |||
1031 | ||||
1032 | /* Bottom border */ | |||
1033 | cell->border[BOTTOM].style = | |||
1034 | css_computed_border_bottom_style(cell->style); | |||
1035 | css_computed_border_bottom_color(cell->style, | |||
1036 | &cell->border[BOTTOM].c); | |||
1037 | css_computed_border_bottom_width(cell->style, &width, &unit); | |||
1038 | cell->border[BOTTOM].width = | |||
1039 | FIXTOINT(css_unit_len2device_px(((css_unit_len2device_px( cell->style, unit_len_ctx, width , unit)) >> 10) | |||
1040 | cell->style, unit_len_ctx,((css_unit_len2device_px( cell->style, unit_len_ctx, width , unit)) >> 10) | |||
1041 | width, unit))((css_unit_len2device_px( cell->style, unit_len_ctx, width , unit)) >> 10); | |||
1042 | } else { | |||
1043 | /* Left border */ | |||
1044 | table_used_left_border_for_cell(unit_len_ctx, cell); | |||
1045 | ||||
1046 | /* Top border */ | |||
1047 | table_used_top_border_for_cell(unit_len_ctx, cell); | |||
1048 | ||||
1049 | /* Right border */ | |||
1050 | table_used_right_border_for_cell(unit_len_ctx, cell); | |||
1051 | ||||
1052 | /* Bottom border */ | |||
1053 | table_used_bottom_border_for_cell(unit_len_ctx, cell); | |||
1054 | } | |||
1055 | ||||
1056 | /* Finally, ensure that any borders configured as | |||
1057 | * hidden or none have zero width. (c.f. layout_find_dimensions) */ | |||
1058 | for (side = 0; side != 4; side++) { | |||
1059 | if (cell->border[side].style == CSS_BORDER_STYLE_HIDDEN || | |||
1060 | cell->border[side].style == | |||
1061 | CSS_BORDER_STYLE_NONE) | |||
1062 | cell->border[side].width = 0; | |||
1063 | } | |||
1064 | } |